From e92b2c111154daa78eb464ef377c1bf8f45679d6 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Thu, 19 Jun 2025 09:15:34 +0000 Subject: [PATCH 1/2] Allow usage of friendly names in most commands This change adds the ability to use the repo/org/enterprise names instead of UUID in most garm-cli commands, at the expense of an extra list API call, leveraging the recently added filter options. Signed-off-by: Gabriel Adrian Samfira --- cmd/garm-cli/cmd/enterprise.go | 23 ++++++- cmd/garm-cli/cmd/organization.go | 49 ++++++++++++--- cmd/garm-cli/cmd/pool.go | 24 ++++++++ cmd/garm-cli/cmd/repository.go | 49 ++++++++++++--- cmd/garm-cli/cmd/scalesets.go | 24 ++++++++ cmd/garm-cli/cmd/util.go | 100 +++++++++++++++++++++++++++++++ 6 files changed, 249 insertions(+), 20 deletions(-) create mode 100644 cmd/garm-cli/cmd/util.go diff --git a/cmd/garm-cli/cmd/enterprise.go b/cmd/garm-cli/cmd/enterprise.go index b8850e1b..0f688fe5 100644 --- a/cmd/garm-cli/cmd/enterprise.go +++ b/cmd/garm-cli/cmd/enterprise.go @@ -112,8 +112,14 @@ var enterpriseShowCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } + + enterpriseID, err := resolveEnterprise(args[0]) + if err != nil { + return err + } + showEnterpriseReq := apiClientEnterprises.NewGetEnterpriseParams() - showEnterpriseReq.EnterpriseID = args[0] + showEnterpriseReq.EnterpriseID = enterpriseID response, err := apiCli.Enterprises.GetEnterprise(showEnterpriseReq, authToken) if err != nil { return err @@ -139,8 +145,14 @@ var enterpriseDeleteCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } + + enterpriseID, err := resolveEnterprise(args[0]) + if err != nil { + return err + } + deleteEnterpriseReq := apiClientEnterprises.NewDeleteEnterpriseParams() - deleteEnterpriseReq.EnterpriseID = args[0] + deleteEnterpriseReq.EnterpriseID = enterpriseID if err := apiCli.Enterprises.DeleteEnterprise(deleteEnterpriseReq, authToken); err != nil { return err } @@ -165,13 +177,18 @@ var enterpriseUpdateCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } + enterpriseID, err := resolveEnterprise(args[0]) + if err != nil { + return err + } + updateEnterpriseReq := apiClientEnterprises.NewUpdateEnterpriseParams() updateEnterpriseReq.Body = params.UpdateEntityParams{ WebhookSecret: repoWebhookSecret, CredentialsName: repoCreds, PoolBalancerType: params.PoolBalancerType(poolBalancerType), } - updateEnterpriseReq.EnterpriseID = args[0] + updateEnterpriseReq.EnterpriseID = enterpriseID response, err := apiCli.Enterprises.UpdateEnterprise(updateEnterpriseReq, authToken) if err != nil { return err diff --git a/cmd/garm-cli/cmd/organization.go b/cmd/garm-cli/cmd/organization.go index 9f23888a..4cb7222f 100644 --- a/cmd/garm-cli/cmd/organization.go +++ b/cmd/garm-cli/cmd/organization.go @@ -76,8 +76,13 @@ var orgWebhookInstallCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } + orgID, err := resolveOrganization(args[0]) + if err != nil { + return err + } + installWebhookReq := apiClientOrgs.NewInstallOrgWebhookParams() - installWebhookReq.OrgID = args[0] + installWebhookReq.OrgID = orgID installWebhookReq.Body.InsecureSSL = insecureOrgWebhook installWebhookReq.Body.WebhookEndpointType = params.WebhookEndpointDirect @@ -105,9 +110,12 @@ var orgHookInfoShowCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } - + orgID, err := resolveOrganization(args[0]) + if err != nil { + return err + } showWebhookInfoReq := apiClientOrgs.NewGetOrgWebhookInfoParams() - showWebhookInfoReq.OrgID = args[0] + showWebhookInfoReq.OrgID = orgID response, err := apiCli.Organizations.GetOrgWebhookInfo(showWebhookInfoReq, authToken) if err != nil { @@ -134,10 +142,15 @@ var orgWebhookUninstallCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - uninstallWebhookReq := apiClientOrgs.NewUninstallOrgWebhookParams() - uninstallWebhookReq.OrgID = args[0] + orgID, err := resolveOrganization(args[0]) + if err != nil { + return err + } - err := apiCli.Organizations.UninstallOrgWebhook(uninstallWebhookReq, authToken) + uninstallWebhookReq := apiClientOrgs.NewUninstallOrgWebhookParams() + uninstallWebhookReq.OrgID = orgID + + err = apiCli.Organizations.UninstallOrgWebhook(uninstallWebhookReq, authToken) if err != nil { return err } @@ -216,13 +229,19 @@ var orgUpdateCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } + + orgID, err := resolveOrganization(args[0]) + if err != nil { + return err + } + updateOrgReq := apiClientOrgs.NewUpdateOrgParams() updateOrgReq.Body = params.UpdateEntityParams{ WebhookSecret: orgWebhookSecret, CredentialsName: orgCreds, PoolBalancerType: params.PoolBalancerType(poolBalancerType), } - updateOrgReq.OrgID = args[0] + updateOrgReq.OrgID = orgID response, err := apiCli.Organizations.UpdateOrg(updateOrgReq, authToken) if err != nil { return err @@ -270,8 +289,14 @@ var orgShowCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } + + orgID, err := resolveOrganization(args[0]) + if err != nil { + return err + } + showOrgReq := apiClientOrgs.NewGetOrgParams() - showOrgReq.OrgID = args[0] + showOrgReq.OrgID = orgID response, err := apiCli.Organizations.GetOrg(showOrgReq, authToken) if err != nil { return err @@ -297,8 +322,14 @@ var orgDeleteCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } + + orgID, err := resolveOrganization(args[0]) + if err != nil { + return err + } + deleteOrgReq := apiClientOrgs.NewDeleteOrgParams() - deleteOrgReq.OrgID = args[0] + deleteOrgReq.OrgID = orgID deleteOrgReq.KeepWebhook = &keepOrgWebhook if err := apiCli.Organizations.DeleteOrg(deleteOrgReq, authToken); err != nil { return err diff --git a/cmd/garm-cli/cmd/pool.go b/cmd/garm-cli/cmd/pool.go index b2c324ea..096210fa 100644 --- a/cmd/garm-cli/cmd/pool.go +++ b/cmd/garm-cli/cmd/pool.go @@ -105,14 +105,26 @@ Example: switch len(args) { case 0: if cmd.Flags().Changed("repo") { + poolRepository, err = resolveRepository(poolRepository) + if err != nil { + return err + } listRepoPoolsReq := apiClientRepos.NewListRepoPoolsParams() listRepoPoolsReq.RepoID = poolRepository response, err = apiCli.Repositories.ListRepoPools(listRepoPoolsReq, authToken) } else if cmd.Flags().Changed("org") { + poolOrganization, err = resolveOrganization(poolOrganization) + if err != nil { + return err + } listOrgPoolsReq := apiClientOrgs.NewListOrgPoolsParams() listOrgPoolsReq.OrgID = poolOrganization response, err = apiCli.Organizations.ListOrgPools(listOrgPoolsReq, authToken) } else if cmd.Flags().Changed("enterprise") { + poolEnterprise, err = resolveEnterprise(poolEnterprise) + if err != nil { + return err + } listEnterprisePoolsReq := apiClientEnterprises.NewListEnterprisePoolsParams() listEnterprisePoolsReq.EnterpriseID = poolEnterprise response, err = apiCli.Enterprises.ListEnterprisePools(listEnterprisePoolsReq, authToken) @@ -250,16 +262,28 @@ var poolAddCmd = &cobra.Command{ var err error var response poolPayloadGetter if cmd.Flags().Changed("repo") { + poolRepository, err = resolveRepository(poolRepository) + if err != nil { + return err + } newRepoPoolReq := apiClientRepos.NewCreateRepoPoolParams() newRepoPoolReq.RepoID = poolRepository newRepoPoolReq.Body = newPoolParams response, err = apiCli.Repositories.CreateRepoPool(newRepoPoolReq, authToken) } else if cmd.Flags().Changed("org") { + poolOrganization, err = resolveOrganization(poolOrganization) + if err != nil { + return err + } newOrgPoolReq := apiClientOrgs.NewCreateOrgPoolParams() newOrgPoolReq.OrgID = poolOrganization newOrgPoolReq.Body = newPoolParams response, err = apiCli.Organizations.CreateOrgPool(newOrgPoolReq, authToken) } else if cmd.Flags().Changed("enterprise") { + poolEnterprise, err = resolveEnterprise(poolEnterprise) + if err != nil { + return err + } newEnterprisePoolReq := apiClientEnterprises.NewCreateEnterprisePoolParams() newEnterprisePoolReq.EnterpriseID = poolEnterprise newEnterprisePoolReq.Body = newPoolParams diff --git a/cmd/garm-cli/cmd/repository.go b/cmd/garm-cli/cmd/repository.go index 91db23ea..eef936da 100644 --- a/cmd/garm-cli/cmd/repository.go +++ b/cmd/garm-cli/cmd/repository.go @@ -78,8 +78,13 @@ var repoWebhookInstallCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } + repoID, err := resolveRepository(args[0]) + if err != nil { + return err + } + installWebhookReq := apiClientRepos.NewInstallRepoWebhookParams() - installWebhookReq.RepoID = args[0] + installWebhookReq.RepoID = repoID installWebhookReq.Body.InsecureSSL = insecureRepoWebhook installWebhookReq.Body.WebhookEndpointType = params.WebhookEndpointDirect @@ -108,8 +113,13 @@ var repoHookInfoShowCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } + repoID, err := resolveRepository(args[0]) + if err != nil { + return err + } + showWebhookInfoReq := apiClientRepos.NewGetRepoWebhookInfoParams() - showWebhookInfoReq.RepoID = args[0] + showWebhookInfoReq.RepoID = repoID response, err := apiCli.Repositories.GetRepoWebhookInfo(showWebhookInfoReq, authToken) if err != nil { @@ -136,10 +146,15 @@ var repoWebhookUninstallCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - uninstallWebhookReq := apiClientRepos.NewUninstallRepoWebhookParams() - uninstallWebhookReq.RepoID = args[0] + repoID, err := resolveRepository(args[0]) + if err != nil { + return err + } - err := apiCli.Repositories.UninstallRepoWebhook(uninstallWebhookReq, authToken) + uninstallWebhookReq := apiClientRepos.NewUninstallRepoWebhookParams() + uninstallWebhookReq.RepoID = repoID + + err = apiCli.Repositories.UninstallRepoWebhook(uninstallWebhookReq, authToken) if err != nil { return err } @@ -243,13 +258,19 @@ var repoUpdateCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } + + repoID, err := resolveRepository(args[0]) + if err != nil { + return err + } + updateReposReq := apiClientRepos.NewUpdateRepoParams() updateReposReq.Body = params.UpdateEntityParams{ WebhookSecret: repoWebhookSecret, CredentialsName: repoCreds, PoolBalancerType: params.PoolBalancerType(poolBalancerType), } - updateReposReq.RepoID = args[0] + updateReposReq.RepoID = repoID response, err := apiCli.Repositories.UpdateRepo(updateReposReq, authToken) if err != nil { @@ -275,8 +296,14 @@ var repoShowCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } + + repoID, err := resolveRepository(args[0]) + if err != nil { + return err + } + showRepoReq := apiClientRepos.NewGetRepoParams() - showRepoReq.RepoID = args[0] + showRepoReq.RepoID = repoID response, err := apiCli.Repositories.GetRepo(showRepoReq, authToken) if err != nil { return err @@ -302,8 +329,14 @@ var repoDeleteCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } + + repoID, err := resolveRepository(args[0]) + if err != nil { + return err + } + deleteRepoReq := apiClientRepos.NewDeleteRepoParams() - deleteRepoReq.RepoID = args[0] + deleteRepoReq.RepoID = repoID deleteRepoReq.KeepWebhook = &keepRepoWebhook if err := apiCli.Repositories.DeleteRepo(deleteRepoReq, authToken); err != nil { return err diff --git a/cmd/garm-cli/cmd/scalesets.go b/cmd/garm-cli/cmd/scalesets.go index 920b60cf..ece9b7a2 100644 --- a/cmd/garm-cli/cmd/scalesets.go +++ b/cmd/garm-cli/cmd/scalesets.go @@ -105,14 +105,26 @@ Example: switch len(args) { case 0: if cmd.Flags().Changed("repo") { + scalesetRepository, err = resolveRepository(scalesetRepository) + if err != nil { + return err + } listRepoScaleSetsReq := apiClientRepos.NewListRepoScaleSetsParams() listRepoScaleSetsReq.RepoID = scalesetRepository response, err = apiCli.Repositories.ListRepoScaleSets(listRepoScaleSetsReq, authToken) } else if cmd.Flags().Changed("org") { + scalesetOrganization, err = resolveOrganization(scalesetOrganization) + if err != nil { + return err + } listOrgScaleSetsReq := apiClientOrgs.NewListOrgScaleSetsParams() listOrgScaleSetsReq.OrgID = scalesetOrganization response, err = apiCli.Organizations.ListOrgScaleSets(listOrgScaleSetsReq, authToken) } else if cmd.Flags().Changed("enterprise") { + scalesetEnterprise, err = resolveEnterprise(scalesetEnterprise) + if err != nil { + return err + } listEnterpriseScaleSetsReq := apiClientEnterprises.NewListEnterpriseScaleSetsParams() listEnterpriseScaleSetsReq.EnterpriseID = scalesetEnterprise response, err = apiCli.Enterprises.ListEnterpriseScaleSets(listEnterpriseScaleSetsReq, authToken) @@ -244,16 +256,28 @@ var scaleSetAddCmd = &cobra.Command{ var err error var response scalesetPayloadGetter if cmd.Flags().Changed("repo") { + scalesetRepository, err = resolveRepository(scalesetRepository) + if err != nil { + return err + } newRepoScaleSetReq := apiClientRepos.NewCreateRepoScaleSetParams() newRepoScaleSetReq.RepoID = scalesetRepository newRepoScaleSetReq.Body = newScaleSetParams response, err = apiCli.Repositories.CreateRepoScaleSet(newRepoScaleSetReq, authToken) } else if cmd.Flags().Changed("org") { + scalesetOrganization, err = resolveOrganization(scalesetOrganization) + if err != nil { + return err + } newOrgScaleSetReq := apiClientOrgs.NewCreateOrgScaleSetParams() newOrgScaleSetReq.OrgID = scalesetOrganization newOrgScaleSetReq.Body = newScaleSetParams response, err = apiCli.Organizations.CreateOrgScaleSet(newOrgScaleSetReq, authToken) } else if cmd.Flags().Changed("enterprise") { + scalesetEnterprise, err = resolveEnterprise(scalesetEnterprise) + if err != nil { + return err + } newEnterpriseScaleSetReq := apiClientEnterprises.NewCreateEnterpriseScaleSetParams() newEnterpriseScaleSetReq.EnterpriseID = scalesetEnterprise newEnterpriseScaleSetReq.Body = newScaleSetParams diff --git a/cmd/garm-cli/cmd/util.go b/cmd/garm-cli/cmd/util.go new file mode 100644 index 00000000..584ad9c4 --- /dev/null +++ b/cmd/garm-cli/cmd/util.go @@ -0,0 +1,100 @@ +package cmd + +import ( + "fmt" + "strings" + + "github.com/google/uuid" + + apiClientEnterprises "github.com/cloudbase/garm/client/enterprises" + apiClientOrgs "github.com/cloudbase/garm/client/organizations" + apiClientRepos "github.com/cloudbase/garm/client/repositories" +) + +func resolveRepository(nameOrID string) (string, error) { + if nameOrID == "" { + return "", fmt.Errorf("missing repository name or ID") + } + entityID, err := uuid.Parse(nameOrID) + if err == nil { + return entityID.String(), nil + } + + parts := strings.SplitN(nameOrID, "/", 2) + if len(parts) < 2 { + // format of friendly name is invalid for a repository. + // Return the string as is. + return nameOrID, nil + } + + listReposReq := apiClientRepos.NewListReposParams() + listReposReq.Owner = &parts[0] + listReposReq.Name = &parts[1] + response, err := apiCli.Repositories.ListRepos(listReposReq, authToken) + if err != nil { + return "", err + } + if len(response.Payload) == 0 { + return "", fmt.Errorf("repository %s was not found", nameOrID) + } + + if len(response.Payload) > 1 { + return "", fmt.Errorf("multiple repositories with the name %s exist, please use the repository ID", nameOrID) + } + return response.Payload[0].ID, nil +} + +func resolveOrganization(nameOrID string) (string, error) { + if nameOrID == "" { + return "", fmt.Errorf("missing organization name or ID") + } + entityID, err := uuid.Parse(nameOrID) + if err == nil { + return entityID.String(), nil + } + + listOrgsReq := apiClientOrgs.NewListOrgsParams() + listOrgsReq.Name = &nameOrID + response, err := apiCli.Organizations.ListOrgs(listOrgsReq, authToken) + if err != nil { + return "", err + } + + if len(response.Payload) == 0 { + return "", fmt.Errorf("organization %s was not found", nameOrID) + } + + if len(response.Payload) > 1 { + return "", fmt.Errorf("multiple organizations with the name %s exist, please use the organization ID", nameOrID) + } + + return response.Payload[0].ID, nil +} + +func resolveEnterprise(nameOrID string) (string, error) { + if nameOrID == "" { + return "", fmt.Errorf("missing enterprise name or ID") + } + entityID, err := uuid.Parse(nameOrID) + if err == nil { + return entityID.String(), nil + } + + listEnterprisesReq := apiClientEnterprises.NewListEnterprisesParams() + listEnterprisesReq.Name = &enterpriseName + listEnterprisesReq.Endpoint = &enterpriseEndpoint + response, err := apiCli.Enterprises.ListEnterprises(listEnterprisesReq, authToken) + if err != nil { + return "", err + } + + if len(response.Payload) == 0 { + return "", fmt.Errorf("enterprise %s was not found", nameOrID) + } + + if len(response.Payload) > 1 { + return "", fmt.Errorf("multiple enterprises with the name %s exist, please use the enterprise ID", nameOrID) + } + + return response.Payload[0].ID, nil +} From 808af82e0d6185d543792daf1c7dec82f5c0223d Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Sat, 21 Jun 2025 16:53:41 +0000 Subject: [PATCH 2/2] Add endpoint option to all relevant commands In case of ambiguity when using the name of a repo, org or enterprise, an --endpoint flag can be used to uniquely identify an entity against an endpoint. Signed-off-by: Gabriel Adrian Samfira --- cmd/garm-cli/cmd/enterprise.go | 10 +++++++--- cmd/garm-cli/cmd/organization.go | 22 ++++++++++++++++------ cmd/garm-cli/cmd/pool.go | 16 ++++++++++------ cmd/garm-cli/cmd/repository.go | 21 +++++++++++++++------ cmd/garm-cli/cmd/scalesets.go | 14 ++++++++------ cmd/garm-cli/cmd/util.go | 22 +++++++++++++++------- 6 files changed, 71 insertions(+), 34 deletions(-) diff --git a/cmd/garm-cli/cmd/enterprise.go b/cmd/garm-cli/cmd/enterprise.go index 0f688fe5..5c937b81 100644 --- a/cmd/garm-cli/cmd/enterprise.go +++ b/cmd/garm-cli/cmd/enterprise.go @@ -113,7 +113,7 @@ var enterpriseShowCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - enterpriseID, err := resolveEnterprise(args[0]) + enterpriseID, err := resolveEnterprise(args[0], enterpriseEndpoint) if err != nil { return err } @@ -146,7 +146,7 @@ var enterpriseDeleteCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - enterpriseID, err := resolveEnterprise(args[0]) + enterpriseID, err := resolveEnterprise(args[0], enterpriseEndpoint) if err != nil { return err } @@ -177,7 +177,7 @@ var enterpriseUpdateCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } - enterpriseID, err := resolveEnterprise(args[0]) + enterpriseID, err := resolveEnterprise(args[0], enterpriseEndpoint) if err != nil { return err } @@ -213,6 +213,10 @@ func init() { enterpriseUpdateCmd.Flags().StringVar(&enterpriseWebhookSecret, "webhook-secret", "", "The webhook secret for this enterprise") enterpriseUpdateCmd.Flags().StringVar(&enterpriseCreds, "credentials", "", "Credentials name. See credentials list.") enterpriseUpdateCmd.Flags().StringVar(&poolBalancerType, "pool-balancer-type", "", "The balancing strategy to use when creating runners in pools matching requested labels.") + enterpriseUpdateCmd.Flags().StringVar(&enterpriseEndpoint, "endpoint", "", "When using the name of the enterprise, the endpoint must be specified when multiple enterprises with the same name exist.") + + enterpriseDeleteCmd.Flags().StringVar(&enterpriseEndpoint, "endpoint", "", "When using the name of the enterprise, the endpoint must be specified when multiple enterprises with the same name exist.") + enterpriseShowCmd.Flags().StringVar(&enterpriseEndpoint, "endpoint", "", "When using the name of the enterprise, the endpoint must be specified when multiple enterprises with the same name exist.") enterpriseCmd.AddCommand( enterpriseListCmd, diff --git a/cmd/garm-cli/cmd/organization.go b/cmd/garm-cli/cmd/organization.go index 4cb7222f..b16812fa 100644 --- a/cmd/garm-cli/cmd/organization.go +++ b/cmd/garm-cli/cmd/organization.go @@ -76,7 +76,7 @@ var orgWebhookInstallCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - orgID, err := resolveOrganization(args[0]) + orgID, err := resolveOrganization(args[0], orgEndpoint) if err != nil { return err } @@ -110,7 +110,7 @@ var orgHookInfoShowCmd = &cobra.Command{ if len(args) > 1 { return fmt.Errorf("too many arguments") } - orgID, err := resolveOrganization(args[0]) + orgID, err := resolveOrganization(args[0], orgEndpoint) if err != nil { return err } @@ -142,7 +142,7 @@ var orgWebhookUninstallCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - orgID, err := resolveOrganization(args[0]) + orgID, err := resolveOrganization(args[0], orgEndpoint) if err != nil { return err } @@ -230,7 +230,7 @@ var orgUpdateCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - orgID, err := resolveOrganization(args[0]) + orgID, err := resolveOrganization(args[0], orgEndpoint) if err != nil { return err } @@ -290,7 +290,7 @@ var orgShowCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - orgID, err := resolveOrganization(args[0]) + orgID, err := resolveOrganization(args[0], orgEndpoint) if err != nil { return err } @@ -323,7 +323,7 @@ var orgDeleteCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - orgID, err := resolveOrganization(args[0]) + orgID, err := resolveOrganization(args[0], orgEndpoint) if err != nil { return err } @@ -357,12 +357,22 @@ func init() { orgAddCmd.MarkFlagRequired("name") //nolint orgDeleteCmd.Flags().BoolVar(&keepOrgWebhook, "keep-webhook", false, "Do not delete any existing webhook when removing the organization from GARM.") + orgDeleteCmd.Flags().StringVar(&orgEndpoint, "endpoint", "", "When using the name of the org, the endpoint must be specified when multiple organizations with the same name exist.") + + orgShowCmd.Flags().StringVar(&orgEndpoint, "endpoint", "", "When using the name of the org, the endpoint must be specified when multiple organizations with the same name exist.") orgUpdateCmd.Flags().StringVar(&orgWebhookSecret, "webhook-secret", "", "The webhook secret for this organization") orgUpdateCmd.Flags().StringVar(&orgCreds, "credentials", "", "Credentials name. See credentials list.") orgUpdateCmd.Flags().StringVar(&poolBalancerType, "pool-balancer-type", "", "The balancing strategy to use when creating runners in pools matching requested labels.") + orgUpdateCmd.Flags().StringVar(&orgEndpoint, "endpoint", "", "When using the name of the org, the endpoint must be specified when multiple organizations with the same name exist.") orgWebhookInstallCmd.Flags().BoolVar(&insecureOrgWebhook, "insecure", false, "Ignore self signed certificate errors.") + orgWebhookInstallCmd.Flags().StringVar(&orgEndpoint, "endpoint", "", "When using the name of the org, the endpoint must be specified when multiple organizations with the same name exist.") + + orgWebhookUninstallCmd.Flags().StringVar(&orgEndpoint, "endpoint", "", "When using the name of the org, the endpoint must be specified when multiple organizations with the same name exist.") + + orgHookInfoShowCmd.Flags().StringVar(&orgEndpoint, "endpoint", "", "When using the name of the org, the endpoint must be specified when multiple organizations with the same name exist.") + orgWebhookCmd.AddCommand( orgWebhookInstallCmd, orgWebhookUninstallCmd, diff --git a/cmd/garm-cli/cmd/pool.go b/cmd/garm-cli/cmd/pool.go index 096210fa..0c667c4a 100644 --- a/cmd/garm-cli/cmd/pool.go +++ b/cmd/garm-cli/cmd/pool.go @@ -105,7 +105,7 @@ Example: switch len(args) { case 0: if cmd.Flags().Changed("repo") { - poolRepository, err = resolveRepository(poolRepository) + poolRepository, err = resolveRepository(poolRepository, endpointName) if err != nil { return err } @@ -113,7 +113,7 @@ Example: listRepoPoolsReq.RepoID = poolRepository response, err = apiCli.Repositories.ListRepoPools(listRepoPoolsReq, authToken) } else if cmd.Flags().Changed("org") { - poolOrganization, err = resolveOrganization(poolOrganization) + poolOrganization, err = resolveOrganization(poolOrganization, endpointName) if err != nil { return err } @@ -121,7 +121,7 @@ Example: listOrgPoolsReq.OrgID = poolOrganization response, err = apiCli.Organizations.ListOrgPools(listOrgPoolsReq, authToken) } else if cmd.Flags().Changed("enterprise") { - poolEnterprise, err = resolveEnterprise(poolEnterprise) + poolEnterprise, err = resolveEnterprise(poolEnterprise, endpointName) if err != nil { return err } @@ -262,7 +262,7 @@ var poolAddCmd = &cobra.Command{ var err error var response poolPayloadGetter if cmd.Flags().Changed("repo") { - poolRepository, err = resolveRepository(poolRepository) + poolRepository, err = resolveRepository(poolRepository, endpointName) if err != nil { return err } @@ -271,7 +271,7 @@ var poolAddCmd = &cobra.Command{ newRepoPoolReq.Body = newPoolParams response, err = apiCli.Repositories.CreateRepoPool(newRepoPoolReq, authToken) } else if cmd.Flags().Changed("org") { - poolOrganization, err = resolveOrganization(poolOrganization) + poolOrganization, err = resolveOrganization(poolOrganization, endpointName) if err != nil { return err } @@ -280,7 +280,7 @@ var poolAddCmd = &cobra.Command{ newOrgPoolReq.Body = newPoolParams response, err = apiCli.Organizations.CreateOrgPool(newOrgPoolReq, authToken) } else if cmd.Flags().Changed("enterprise") { - poolEnterprise, err = resolveEnterprise(poolEnterprise) + poolEnterprise, err = resolveEnterprise(poolEnterprise, endpointName) if err != nil { return err } @@ -411,6 +411,8 @@ func init() { poolListCmd.Flags().StringVarP(&poolEnterprise, "enterprise", "e", "", "List all pools within this enterprise.") poolListCmd.Flags().BoolVarP(&poolAll, "all", "a", false, "List all pools, regardless of org or repo.") poolListCmd.Flags().BoolVarP(&long, "long", "l", false, "Include additional info.") + poolListCmd.Flags().StringVar(&endpointName, "endpoint", "", "When using the name of an entity, the endpoint must be specified when multiple entities with the same name exist.") + poolListCmd.MarkFlagsMutuallyExclusive("repo", "org", "all", "enterprise") poolUpdateCmd.Flags().StringVar(&poolImage, "image", "", "The provider-specific image name to use for runners in this pool.") @@ -444,6 +446,8 @@ func init() { poolAddCmd.Flags().UintVar(&poolRunnerBootstrapTimeout, "runner-bootstrap-timeout", 20, "Duration in minutes after which a runner is considered failed if it does not join Github.") poolAddCmd.Flags().UintVar(&poolMinIdleRunners, "min-idle-runners", 1, "Attempt to maintain a minimum of idle self-hosted runners of this type.") poolAddCmd.Flags().BoolVar(&poolEnabled, "enabled", false, "Enable this pool.") + poolAddCmd.Flags().StringVar(&endpointName, "endpoint", "", "When using the name of an entity, the endpoint must be specified when multiple entities with the same name exist.") + poolAddCmd.MarkFlagRequired("provider-name") //nolint poolAddCmd.MarkFlagRequired("image") //nolint poolAddCmd.MarkFlagRequired("flavor") //nolint diff --git a/cmd/garm-cli/cmd/repository.go b/cmd/garm-cli/cmd/repository.go index eef936da..cca1a7fe 100644 --- a/cmd/garm-cli/cmd/repository.go +++ b/cmd/garm-cli/cmd/repository.go @@ -78,7 +78,7 @@ var repoWebhookInstallCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - repoID, err := resolveRepository(args[0]) + repoID, err := resolveRepository(args[0], repoEndpoint) if err != nil { return err } @@ -113,7 +113,7 @@ var repoHookInfoShowCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - repoID, err := resolveRepository(args[0]) + repoID, err := resolveRepository(args[0], repoEndpoint) if err != nil { return err } @@ -146,7 +146,7 @@ var repoWebhookUninstallCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - repoID, err := resolveRepository(args[0]) + repoID, err := resolveRepository(args[0], repoEndpoint) if err != nil { return err } @@ -259,7 +259,7 @@ var repoUpdateCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - repoID, err := resolveRepository(args[0]) + repoID, err := resolveRepository(args[0], repoEndpoint) if err != nil { return err } @@ -297,7 +297,7 @@ var repoShowCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - repoID, err := resolveRepository(args[0]) + repoID, err := resolveRepository(args[0], repoEndpoint) if err != nil { return err } @@ -330,7 +330,7 @@ var repoDeleteCmd = &cobra.Command{ return fmt.Errorf("too many arguments") } - repoID, err := resolveRepository(args[0]) + repoID, err := resolveRepository(args[0], repoEndpoint) if err != nil { return err } @@ -367,12 +367,21 @@ func init() { repoAddCmd.MarkFlagRequired("name") //nolint repoDeleteCmd.Flags().BoolVar(&keepRepoWebhook, "keep-webhook", false, "Do not delete any existing webhook when removing the repo from GARM.") + repoDeleteCmd.Flags().StringVar(&repoEndpoint, "endpoint", "", "When using the name of the repo, the endpoint must be specified when multiple repositories with the same name exist.") + + repoShowCmd.Flags().StringVar(&repoEndpoint, "endpoint", "", "When using the name of the repo, the endpoint must be specified when multiple repositories with the same name exist.") repoUpdateCmd.Flags().StringVar(&repoWebhookSecret, "webhook-secret", "", "The webhook secret for this repository. If you update this secret, you will have to manually update the secret in GitHub as well.") repoUpdateCmd.Flags().StringVar(&repoCreds, "credentials", "", "Credentials name. See credentials list.") repoUpdateCmd.Flags().StringVar(&poolBalancerType, "pool-balancer-type", "", "The balancing strategy to use when creating runners in pools matching requested labels.") + repoUpdateCmd.Flags().StringVar(&repoEndpoint, "endpoint", "", "When using the name of the repo, the endpoint must be specified when multiple repositories with the same name exist.") repoWebhookInstallCmd.Flags().BoolVar(&insecureRepoWebhook, "insecure", false, "Ignore self signed certificate errors.") + repoWebhookInstallCmd.Flags().StringVar(&repoEndpoint, "endpoint", "", "When using the name of the repo, the endpoint must be specified when multiple repositories with the same name exist.") + + repoWebhookUninstallCmd.Flags().StringVar(&repoEndpoint, "endpoint", "", "When using the name of the repo, the endpoint must be specified when multiple repositories with the same name exist.") + + repoHookInfoShowCmd.Flags().StringVar(&repoEndpoint, "endpoint", "", "When using the name of the repo, the endpoint must be specified when multiple repositories with the same name exist.") repoWebhookCmd.AddCommand( repoWebhookInstallCmd, diff --git a/cmd/garm-cli/cmd/scalesets.go b/cmd/garm-cli/cmd/scalesets.go index ece9b7a2..cf64c9fa 100644 --- a/cmd/garm-cli/cmd/scalesets.go +++ b/cmd/garm-cli/cmd/scalesets.go @@ -105,7 +105,7 @@ Example: switch len(args) { case 0: if cmd.Flags().Changed("repo") { - scalesetRepository, err = resolveRepository(scalesetRepository) + scalesetRepository, err = resolveRepository(scalesetRepository, endpointName) if err != nil { return err } @@ -113,7 +113,7 @@ Example: listRepoScaleSetsReq.RepoID = scalesetRepository response, err = apiCli.Repositories.ListRepoScaleSets(listRepoScaleSetsReq, authToken) } else if cmd.Flags().Changed("org") { - scalesetOrganization, err = resolveOrganization(scalesetOrganization) + scalesetOrganization, err = resolveOrganization(scalesetOrganization, endpointName) if err != nil { return err } @@ -121,7 +121,7 @@ Example: listOrgScaleSetsReq.OrgID = scalesetOrganization response, err = apiCli.Organizations.ListOrgScaleSets(listOrgScaleSetsReq, authToken) } else if cmd.Flags().Changed("enterprise") { - scalesetEnterprise, err = resolveEnterprise(scalesetEnterprise) + scalesetEnterprise, err = resolveEnterprise(scalesetEnterprise, endpointName) if err != nil { return err } @@ -256,7 +256,7 @@ var scaleSetAddCmd = &cobra.Command{ var err error var response scalesetPayloadGetter if cmd.Flags().Changed("repo") { - scalesetRepository, err = resolveRepository(scalesetRepository) + scalesetRepository, err = resolveRepository(scalesetRepository, endpointName) if err != nil { return err } @@ -265,7 +265,7 @@ var scaleSetAddCmd = &cobra.Command{ newRepoScaleSetReq.Body = newScaleSetParams response, err = apiCli.Repositories.CreateRepoScaleSet(newRepoScaleSetReq, authToken) } else if cmd.Flags().Changed("org") { - scalesetOrganization, err = resolveOrganization(scalesetOrganization) + scalesetOrganization, err = resolveOrganization(scalesetOrganization, endpointName) if err != nil { return err } @@ -274,7 +274,7 @@ var scaleSetAddCmd = &cobra.Command{ newOrgScaleSetReq.Body = newScaleSetParams response, err = apiCli.Organizations.CreateOrgScaleSet(newOrgScaleSetReq, authToken) } else if cmd.Flags().Changed("enterprise") { - scalesetEnterprise, err = resolveEnterprise(scalesetEnterprise) + scalesetEnterprise, err = resolveEnterprise(scalesetEnterprise, endpointName) if err != nil { return err } @@ -402,6 +402,7 @@ func init() { scalesetListCmd.Flags().StringVarP(&scalesetEnterprise, "enterprise", "e", "", "List all scale sets within this enterprise.") scalesetListCmd.Flags().BoolVarP(&scalesetAll, "all", "a", false, "List all scale sets, regardless of org or repo.") scalesetListCmd.MarkFlagsMutuallyExclusive("repo", "org", "all", "enterprise") + scalesetListCmd.Flags().StringVar(&endpointName, "endpoint", "", "When using the name of an entity, the endpoint must be specified when multiple entities with the same name exist.") scaleSetUpdateCmd.Flags().StringVar(&scalesetImage, "image", "", "The provider-specific image name to use for runners in this scale set.") scaleSetUpdateCmd.Flags().StringVar(&scalesetFlavor, "flavor", "", "The flavor to use for the runners in this scale set.") @@ -432,6 +433,7 @@ func init() { scaleSetAddCmd.Flags().UintVar(&scalesetRunnerBootstrapTimeout, "runner-bootstrap-timeout", 20, "Duration in minutes after which a runner is considered failed if it does not join Github.") scaleSetAddCmd.Flags().UintVar(&scalesetMinIdleRunners, "min-idle-runners", 1, "Attempt to maintain a minimum of idle self-hosted runners of this type.") scaleSetAddCmd.Flags().BoolVar(&scalesetEnabled, "enabled", false, "Enable this scale set.") + scaleSetAddCmd.Flags().StringVar(&endpointName, "endpoint", "", "When using the name of an entity, the endpoint must be specified when multiple entities with the same name exist.") scaleSetAddCmd.MarkFlagRequired("provider-name") //nolint scaleSetAddCmd.MarkFlagRequired("name") //nolint scaleSetAddCmd.MarkFlagRequired("image") //nolint diff --git a/cmd/garm-cli/cmd/util.go b/cmd/garm-cli/cmd/util.go index 584ad9c4..26f57abb 100644 --- a/cmd/garm-cli/cmd/util.go +++ b/cmd/garm-cli/cmd/util.go @@ -11,7 +11,7 @@ import ( apiClientRepos "github.com/cloudbase/garm/client/repositories" ) -func resolveRepository(nameOrID string) (string, error) { +func resolveRepository(nameOrID, endpoint string) (string, error) { if nameOrID == "" { return "", fmt.Errorf("missing repository name or ID") } @@ -30,6 +30,9 @@ func resolveRepository(nameOrID string) (string, error) { listReposReq := apiClientRepos.NewListReposParams() listReposReq.Owner = &parts[0] listReposReq.Name = &parts[1] + if endpoint != "" { + listReposReq.Endpoint = &endpoint + } response, err := apiCli.Repositories.ListRepos(listReposReq, authToken) if err != nil { return "", err @@ -39,12 +42,12 @@ func resolveRepository(nameOrID string) (string, error) { } if len(response.Payload) > 1 { - return "", fmt.Errorf("multiple repositories with the name %s exist, please use the repository ID", nameOrID) + return "", fmt.Errorf("multiple repositories with the name %s exist, please use the repository ID or specify the --endpoint parameter", nameOrID) } return response.Payload[0].ID, nil } -func resolveOrganization(nameOrID string) (string, error) { +func resolveOrganization(nameOrID, endpoint string) (string, error) { if nameOrID == "" { return "", fmt.Errorf("missing organization name or ID") } @@ -55,6 +58,9 @@ func resolveOrganization(nameOrID string) (string, error) { listOrgsReq := apiClientOrgs.NewListOrgsParams() listOrgsReq.Name = &nameOrID + if endpoint != "" { + listOrgsReq.Endpoint = &endpoint + } response, err := apiCli.Organizations.ListOrgs(listOrgsReq, authToken) if err != nil { return "", err @@ -65,13 +71,13 @@ func resolveOrganization(nameOrID string) (string, error) { } if len(response.Payload) > 1 { - return "", fmt.Errorf("multiple organizations with the name %s exist, please use the organization ID", nameOrID) + return "", fmt.Errorf("multiple organizations with the name %s exist, please use the organization ID or specify the --endpoint parameter", nameOrID) } return response.Payload[0].ID, nil } -func resolveEnterprise(nameOrID string) (string, error) { +func resolveEnterprise(nameOrID, endpoint string) (string, error) { if nameOrID == "" { return "", fmt.Errorf("missing enterprise name or ID") } @@ -82,7 +88,9 @@ func resolveEnterprise(nameOrID string) (string, error) { listEnterprisesReq := apiClientEnterprises.NewListEnterprisesParams() listEnterprisesReq.Name = &enterpriseName - listEnterprisesReq.Endpoint = &enterpriseEndpoint + if endpoint != "" { + listEnterprisesReq.Endpoint = &endpoint + } response, err := apiCli.Enterprises.ListEnterprises(listEnterprisesReq, authToken) if err != nil { return "", err @@ -93,7 +101,7 @@ func resolveEnterprise(nameOrID string) (string, error) { } if len(response.Payload) > 1 { - return "", fmt.Errorf("multiple enterprises with the name %s exist, please use the enterprise ID", nameOrID) + return "", fmt.Errorf("multiple enterprises with the name %s exist, please use the enterprise ID or specify the --endpoint parameter", nameOrID) } return response.Payload[0].ID, nil