diff --git a/cmd/ocm/gcp/create-wif-config.go b/cmd/ocm/gcp/create-wif-config.go index 3e6a8300..17d0052c 100644 --- a/cmd/ocm/gcp/create-wif-config.go +++ b/cmd/ocm/gcp/create-wif-config.go @@ -10,11 +10,11 @@ import ( "strings" "github.com/googleapis/gax-go/v2/apierror" - alphaocm "github.com/openshift-online/ocm-cli/pkg/alpha_ocm" "cloud.google.com/go/iam/admin/apiv1/adminpb" "github.com/openshift-online/ocm-cli/pkg/gcp" - "github.com/openshift-online/ocm-cli/pkg/models" + "github.com/openshift-online/ocm-cli/pkg/ocm" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -76,26 +76,27 @@ func createWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) { } log.Println("Creating workload identity configuration...") - wifConfig, err := createWorkloadIdentityConfiguration(models.WifConfigInput{ - DisplayName: CreateWifConfigOpts.Name, - ProjectId: CreateWifConfigOpts.Project, - }) + wifConfig, err := createWorkloadIdentityConfiguration(CreateWifConfigOpts.Name, CreateWifConfigOpts.Project) if err != nil { log.Fatalf("failed to create WIF config: %v", err) } poolSpec := gcp.WorkloadIdentityPoolSpec{ - PoolName: wifConfig.Status.WorkloadIdentityPoolData.PoolId, - ProjectId: wifConfig.Status.WorkloadIdentityPoolData.ProjectId, - Jwks: wifConfig.Status.WorkloadIdentityPoolData.Jwks, - IssuerUrl: wifConfig.Status.WorkloadIdentityPoolData.IssuerUrl, - PoolIdentityProviderId: wifConfig.Status.WorkloadIdentityPoolData.IdentityProviderId, + PoolName: wifConfig.Gcp().WorkloadIdentityPool().PoolId(), + ProjectId: wifConfig.Gcp().ProjectId(), + Jwks: wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().Jwks(), + IssuerUrl: wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().IssuerUrl(), + PoolIdentityProviderId: wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().IdentityProviderId(), } if CreateWifConfigOpts.DryRun { log.Printf("Writing script files to %s", CreateWifConfigOpts.TargetDir) - err := createScript(CreateWifConfigOpts.TargetDir, wifConfig) + projectNum, err := gcpClient.ProjectNumberFromId(wifConfig.Gcp().ProjectId()) + if err != nil { + log.Fatalf("failed to get project number from id: %v", err) + } + err = createScript(CreateWifConfigOpts.TargetDir, wifConfig, projectNum) if err != nil { log.Fatalf("Failed to create script files: %s", err) } @@ -104,17 +105,17 @@ func createWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) { if err = createWorkloadIdentityPool(ctx, gcpClient, poolSpec); err != nil { log.Printf("Failed to create workload identity pool: %s", err) - log.Fatalf("To clean up, run the following command: ocm gcp delete wif-config %s", wifConfig.Metadata.Id) + log.Fatalf("To clean up, run the following command: ocm gcp delete wif-config %s", wifConfig.ID()) } if err = createWorkloadIdentityProvider(ctx, gcpClient, poolSpec); err != nil { log.Printf("Failed to create workload identity provider: %s", err) - log.Fatalf("To clean up, run the following command: ocm gcp delete wif-config %s", wifConfig.Metadata.Id) + log.Fatalf("To clean up, run the following command: ocm gcp delete wif-config %s", wifConfig.ID()) } if err = createServiceAccounts(ctx, gcpClient, wifConfig); err != nil { log.Printf("Failed to create IAM service accounts: %s", err) - log.Fatalf("To clean up, run the following command: ocm gcp delete wif-config %s", wifConfig.Metadata.Id) + log.Fatalf("To clean up, run the following command: ocm gcp delete wif-config %s", wifConfig.ID()) } } @@ -151,16 +152,34 @@ func validationForCreateWorkloadIdentityConfigurationCmd(cmd *cobra.Command, arg } -func createWorkloadIdentityConfiguration(input models.WifConfigInput) (*models.WifConfigOutput, error) { - ocmClient, err := alphaocm.NewOcmClient() +func createWorkloadIdentityConfiguration(displayName, projectId string) (*cmv1.WifConfig, error) { + connection, err := ocm.NewConnection().Build() if err != nil { - return nil, errors.Wrap(err, "failed to create backend client") + log.Fatal(err) } - output, err := ocmClient.CreateWifConfig(input) + defer connection.Close() + + wifBuilder := cmv1.NewWifConfig() + gcpBuilder := cmv1.NewWifGcp().ProjectId(projectId) + + wifBuilder.DisplayName(displayName) + wifBuilder.Gcp(gcpBuilder) + + wifConfigInput, err := wifBuilder.Build() if err != nil { - return nil, errors.Wrap(err, "failed to create wif config") + return nil, errors.Wrap(err, "failed to build WIF config") } - return &output, nil + + response, err := connection.ClustersMgmt().V1(). + WifConfigs(). + Add(). + Body(wifConfigInput). + Send() + if err != nil { + return nil, errors.Wrap(err, "failed to create WIF config") + } + + return response.Body(), nil } func createWorkloadIdentityPool(ctx context.Context, client gcp.GcpClient, @@ -245,17 +264,17 @@ func createWorkloadIdentityProvider(ctx context.Context, client gcp.GcpClient, return nil } -func createServiceAccounts(ctx context.Context, gcpClient gcp.GcpClient, wifOutput *models.WifConfigOutput) error { - projectId := wifOutput.Spec.ProjectId - fmtRoleResourceId := func(role models.Role) string { - return fmt.Sprintf("roles/%s", role.Id) +func createServiceAccounts(ctx context.Context, gcpClient gcp.GcpClient, wifConfig *cmv1.WifConfig) error { + projectId := wifConfig.Gcp().ProjectId() + fmtRoleResourceId := func(role *cmv1.WifRole) string { + return fmt.Sprintf("roles/%s", role.RoleId()) } // Create service accounts - for _, serviceAccount := range wifOutput.Status.ServiceAccounts { - serviceAccountID := serviceAccount.Id - serviceAccountName := wifOutput.Spec.DisplayName + "-" + serviceAccountID - serviceAccountDesc := poolDescription + " for WIF config " + wifOutput.Spec.DisplayName + for _, serviceAccount := range wifConfig.Gcp().ServiceAccounts() { + serviceAccountID := serviceAccount.ServiceAccountId() + serviceAccountName := wifConfig.DisplayName() + "-" + serviceAccountID + serviceAccountDesc := poolDescription + " for WIF config " + wifConfig.DisplayName() _, err := createServiceAccount(gcpClient, serviceAccountID, serviceAccountName, serviceAccountDesc, projectId, true) if err != nil { @@ -265,14 +284,14 @@ func createServiceAccounts(ctx context.Context, gcpClient gcp.GcpClient, wifOutp } // Create roles that aren't predefined - for _, serviceAccount := range wifOutput.Status.ServiceAccounts { - for _, role := range serviceAccount.Roles { - if role.Predefined { + for _, serviceAccount := range wifConfig.Gcp().ServiceAccounts() { + for _, role := range serviceAccount.Roles() { + if role.Predefined() { continue } - roleID := role.Id - roleName := role.Id - permissions := role.Permissions + roleID := role.RoleId() + roleName := role.RoleId() + permissions := role.Permissions() existingRole, err := GetRole(gcpClient, roleID, projectId) if err != nil { if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 && @@ -300,11 +319,11 @@ func createServiceAccounts(ctx context.Context, gcpClient gcp.GcpClient, wifOutp } // Bind roles and grant access - for _, serviceAccount := range wifOutput.Status.ServiceAccounts { - serviceAccountID := serviceAccount.Id + for _, serviceAccount := range wifConfig.Gcp().ServiceAccounts() { + serviceAccountID := serviceAccount.ServiceAccountId() - roles := make([]string, 0, len(serviceAccount.Roles)) - for _, role := range serviceAccount.Roles { + roles := make([]string, 0, len(serviceAccount.Roles())) + for _, role := range serviceAccount.Roles() { roles = append(roles, fmtRoleResourceId(role)) } member := fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", serviceAccountID, projectId) @@ -313,19 +332,21 @@ func createServiceAccounts(ctx context.Context, gcpClient gcp.GcpClient, wifOutp return errors.Errorf("Failed to bind roles to service account %s: %s", serviceAccountID, err) } - switch serviceAccount.AccessMethod { - case "impersonate": - if err := gcpClient.AttachImpersonator(serviceAccount.Id, projectId, + switch serviceAccount.AccessMethod() { + case cmv1.WifAccessMethodImpersonate: + if err := gcpClient.AttachImpersonator(serviceAccount.ServiceAccountId(), projectId, impersonatorServiceAccount); err != nil { - return errors.Wrapf(err, "Failed to attach impersonator to service account %s", serviceAccount.Id) + return errors.Wrapf(err, "Failed to attach impersonator to service account %s", + serviceAccount.ServiceAccountId()) } - case "wif": + case cmv1.WifAccessMethodWif: if err := gcpClient.AttachWorkloadIdentityPool(serviceAccount, - wifOutput.Status.WorkloadIdentityPoolData.PoolId, projectId); err != nil { - return errors.Wrapf(err, "Failed to attach workload identity pool to service account %s", serviceAccount.Id) + wifConfig.Gcp().WorkloadIdentityPool().PoolId(), projectId); err != nil { + return errors.Wrapf(err, "Failed to attach workload identity pool to service account %s", + serviceAccount.ServiceAccountId()) } default: - log.Printf("Warning: %s is not a supported access type\n", serviceAccount.AccessMethod) + log.Printf("Warning: %s is not a supported access type\n", serviceAccount.AccessMethod()) } } diff --git a/cmd/ocm/gcp/delete-wif-config.go b/cmd/ocm/gcp/delete-wif-config.go index 86cae760..4cebbe5b 100644 --- a/cmd/ocm/gcp/delete-wif-config.go +++ b/cmd/ocm/gcp/delete-wif-config.go @@ -9,9 +9,9 @@ import ( "github.com/googleapis/gax-go/v2/apierror" "google.golang.org/grpc/codes" - alphaocm "github.com/openshift-online/ocm-cli/pkg/alpha_ocm" "github.com/openshift-online/ocm-cli/pkg/gcp" - "github.com/openshift-online/ocm-cli/pkg/models" + "github.com/openshift-online/ocm-cli/pkg/ocm" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -59,21 +59,23 @@ func deleteWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) { log.Fatal("WIF config ID is required") } - // Create clients - ocmClient, err := alphaocm.NewOcmClient() + // Create the client for the OCM API: + connection, err := ocm.NewConnection().Build() if err != nil { - log.Fatalf("failed to create backend client: %v", err) + log.Fatal(err) } + defer connection.Close() - wifConfig, err := ocmClient.GetWifConfig(wifConfigId) + response, err := connection.ClustersMgmt().V1().WifConfigs().WifConfig(wifConfigId).Get().Send() if err != nil { - log.Fatal(err) + log.Fatalf("failed to get wif-config: %v", err) } + wifConfig := response.Body() if DeleteWifConfigOpts.DryRun { log.Printf("Writing script files to %s", DeleteWifConfigOpts.TargetDir) - err := createDeleteScript(DeleteWifConfigOpts.TargetDir, &wifConfig) + err := createDeleteScript(DeleteWifConfigOpts.TargetDir, wifConfig) if err != nil { log.Fatalf("Failed to create script files: %s", err) } @@ -85,27 +87,30 @@ func deleteWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) { log.Fatal(err) } - if err := deleteServiceAccounts(ctx, gcpClient, &wifConfig, true); err != nil { + if err := deleteServiceAccounts(ctx, gcpClient, wifConfig, true); err != nil { log.Fatal(err) } - if err := deleteWorkloadIdentityPool(ctx, gcpClient, &wifConfig, true); err != nil { + if err := deleteWorkloadIdentityPool(ctx, gcpClient, wifConfig, true); err != nil { log.Fatal(err) } - err = ocmClient.DeleteWifConfig(wifConfigId) + _, err = connection.ClustersMgmt().V1().WifConfigs(). + WifConfig(wifConfigId). + Delete(). + Send() if err != nil { - log.Fatal(err) + log.Fatalf("failed to delete wif config %q: %v", wifConfigId, err) } } func deleteServiceAccounts(ctx context.Context, gcpClient gcp.GcpClient, - wifConfig *models.WifConfigOutput, allowMissing bool) error { + wifConfig *cmv1.WifConfig, allowMissing bool) error { log.Println("Deleting service accounts...") - projectId := wifConfig.Spec.ProjectId + projectId := wifConfig.Gcp().ProjectId() - for _, serviceAccount := range wifConfig.Status.ServiceAccounts { - serviceAccountID := serviceAccount.Id + for _, serviceAccount := range wifConfig.Gcp().ServiceAccounts() { + serviceAccountID := serviceAccount.ServiceAccountId() log.Println("Deleting service account", serviceAccountID) err := gcpClient.DeleteServiceAccount(serviceAccountID, projectId, allowMissing) if err != nil { @@ -117,10 +122,10 @@ func deleteServiceAccounts(ctx context.Context, gcpClient gcp.GcpClient, } func deleteWorkloadIdentityPool(ctx context.Context, gcpClient gcp.GcpClient, - wifConfig *models.WifConfigOutput, allowMissing bool) error { + wifConfig *cmv1.WifConfig, allowMissing bool) error { log.Println("Deleting workload identity pool...") - projectId := wifConfig.Spec.ProjectId - poolName := wifConfig.Status.WorkloadIdentityPoolData.PoolId + projectId := wifConfig.Gcp().ProjectId() + poolName := wifConfig.Gcp().WorkloadIdentityPool().PoolId() poolResource := fmt.Sprintf("projects/%s/locations/global/workloadIdentityPools/%s", projectId, poolName) _, err := gcpClient.DeleteWorkloadIdentityPool(ctx, poolResource) diff --git a/cmd/ocm/gcp/describe-wif-config.go b/cmd/ocm/gcp/describe-wif-config.go index 687b1720..5c38285a 100644 --- a/cmd/ocm/gcp/describe-wif-config.go +++ b/cmd/ocm/gcp/describe-wif-config.go @@ -6,7 +6,7 @@ import ( "os" "text/tabwriter" - alphaocm "github.com/openshift-online/ocm-cli/pkg/alpha_ocm" + "github.com/openshift-online/ocm-cli/pkg/ocm" "github.com/openshift-online/ocm-cli/pkg/urls" "github.com/spf13/cobra" ) @@ -30,25 +30,25 @@ func describeWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) } // Create the client for the OCM API: - ocmClient, err := alphaocm.NewOcmClient() + connection, err := ocm.NewConnection().Build() if err != nil { - log.Fatalf("failed to create backend client: %v", err) + log.Fatal(err) } + defer connection.Close() - wifconfig, err := ocmClient.GetWifConfig(id) + response, err := connection.ClustersMgmt().V1().WifConfigs().WifConfig(id).Get().Send() if err != nil { log.Fatalf("failed to get wif-config: %v", err) } + wifConfig := response.Body() // Print output w := tabwriter.NewWriter(os.Stdout, 8, 0, 2, ' ', 0) - fmt.Fprintf(w, "ID:\t%s\n", wifconfig.Metadata.Id) - fmt.Fprintf(w, "Display Name:\t%s\n", wifconfig.Metadata.DisplayName) - fmt.Fprintf(w, "Project:\t%s\n", wifconfig.Spec.ProjectId) - fmt.Fprintf(w, "State:\t%s\n", wifconfig.Status.State) - fmt.Fprintf(w, "Summary:\t%s\n", wifconfig.Status.Summary) - fmt.Fprintf(w, "Issuer URL:\t%s\n", wifconfig.Status.WorkloadIdentityPoolData.IssuerUrl) + fmt.Fprintf(w, "ID:\t%s\n", wifConfig.ID()) + fmt.Fprintf(w, "Display Name:\t%s\n", wifConfig.DisplayName()) + fmt.Fprintf(w, "Project:\t%s\n", wifConfig.Gcp().ProjectId()) + fmt.Fprintf(w, "Issuer URL:\t%s\n", wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().IssuerUrl()) w.Flush() } diff --git a/cmd/ocm/gcp/generate-wif-script.go b/cmd/ocm/gcp/generate-wif-script.go index 13e1b981..399969b9 100644 --- a/cmd/ocm/gcp/generate-wif-script.go +++ b/cmd/ocm/gcp/generate-wif-script.go @@ -1,9 +1,11 @@ package gcp import ( + "context" "log" - alphaocm "github.com/openshift-online/ocm-cli/pkg/alpha_ocm" + "github.com/openshift-online/ocm-cli/pkg/gcp" + "github.com/openshift-online/ocm-cli/pkg/ocm" "github.com/spf13/cobra" ) @@ -39,27 +41,40 @@ func validationForGenerateCreateScriptCmd(cmd *cobra.Command, argv []string) { } func generateCreateScriptCmd(cmd *cobra.Command, argv []string) { - // Create the client for the OCM API: - ocmClient, err := alphaocm.NewOcmClient() + ctx := context.Background() + + gcpClient, err := gcp.NewGcpClient(ctx) + if err != nil { + log.Fatalf("failed to initiate GCP client: %v", err) + } + + connection, err := ocm.NewConnection().Build() if err != nil { - log.Fatalf("failed to create backend client: %v", err) + log.Fatal(err) } + defer connection.Close() wifConfigId := argv[0] if wifConfigId == "" { log.Fatal("WIF config ID is required") } - wifConfig, err := ocmClient.GetWifConfig(wifConfigId) + response, err := connection.ClustersMgmt().V1().WifConfigs().WifConfig(wifConfigId).Get().Send() if err != nil { log.Fatalf("failed to get wif-config: %v", err) } + wifConfig := response.Body() + + projectNum, err := gcpClient.ProjectNumberFromId(wifConfig.Gcp().ProjectId()) + if err != nil { + log.Fatalf("failed to get project number from id: %v", err) + } log.Printf("Writing script files to %s", GenerateScriptOpts.TargetDir) - if err := createScript(GenerateScriptOpts.TargetDir, &wifConfig); err != nil { + if err := createScript(GenerateScriptOpts.TargetDir, wifConfig, projectNum); err != nil { log.Fatalf("failed to generate create script: %v", err) } - if err := createDeleteScript(GenerateScriptOpts.TargetDir, &wifConfig); err != nil { + if err := createDeleteScript(GenerateScriptOpts.TargetDir, wifConfig); err != nil { log.Fatalf("failed to generate delete script: %v", err) } } diff --git a/cmd/ocm/gcp/list-wif-config.go b/cmd/ocm/gcp/list-wif-config.go index 7e1f8211..0ee9a868 100644 --- a/cmd/ocm/gcp/list-wif-config.go +++ b/cmd/ocm/gcp/list-wif-config.go @@ -5,9 +5,10 @@ import ( "log" "os" - alphaocm "github.com/openshift-online/ocm-cli/pkg/alpha_ocm" "github.com/openshift-online/ocm-cli/pkg/config" + "github.com/openshift-online/ocm-cli/pkg/ocm" "github.com/openshift-online/ocm-cli/pkg/output" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" "github.com/spf13/cobra" ) @@ -58,10 +59,11 @@ func listWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) { } // Create the client for the OCM API: - ocmClient, err := alphaocm.NewOcmClient() + connection, err := ocm.NewConnection().Build() if err != nil { - log.Fatalf("failed to create backend client: %v", err) + log.Fatal(err) } + defer connection.Close() // Create the output printer: printer, err := output.NewPrinter(). @@ -73,12 +75,6 @@ func listWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) { } defer printer.Close() - // Get the wif-configs: - wifconfigs, err := ocmClient.ListWifConfigs() - if err != nil { - log.Fatalf("failed to get wif-configs: %v", err) - } - // Create the output table: table, err := printer.NewTable(). Name("wifconfigs"). @@ -94,14 +90,34 @@ func listWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) { table.WriteHeaders() } - // Write the rows: - for _, wc := range wifconfigs { - err = table.WriteObject(wc) + // Create the request + request := connection.ClustersMgmt().V1().WifConfigs().List() + + size := 100 + index := 1 + for { + // Fetch the next page: + request.Size(size) + request.Page(index) + response, err := request.Send() if err != nil { + log.Fatalf("Can't retrieve wif configs: %v", err) + } + + // Display the items of the fetched page: + response.Items().Each(func(wc *cmv1.WifConfig) bool { + err = table.WriteObject(wc) + return err == nil + }) + if err != nil { + log.Fatal(err) + } + + // If the number of fetched items is less than requested, then this was the last + // page, otherwise process the next one: + if response.Size() < size { break } - } - if err != nil { - log.Fatal(err) + index++ } } diff --git a/cmd/ocm/gcp/scripting.go b/cmd/ocm/gcp/scripting.go index 893004a0..691a1857 100644 --- a/cmd/ocm/gcp/scripting.go +++ b/cmd/ocm/gcp/scripting.go @@ -7,26 +7,26 @@ import ( "strings" "github.com/openshift-online/ocm-cli/pkg/gcp" - "github.com/openshift-online/ocm-cli/pkg/models" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" ) -func createScript(targetDir string, wifConfig *models.WifConfigOutput) error { +func createScript(targetDir string, wifConfig *cmv1.WifConfig, projectNum int64) error { // Write the script content to the path - scriptContent := generateScriptContent(wifConfig) + scriptContent := generateScriptContent(wifConfig, projectNum) err := os.WriteFile(filepath.Join(targetDir, "script.sh"), []byte(scriptContent), 0600) if err != nil { return err } // Write jwk json file to the path jwkPath := filepath.Join(targetDir, "jwk.json") - err = os.WriteFile(jwkPath, []byte(wifConfig.Status.WorkloadIdentityPoolData.Jwks), 0600) + err = os.WriteFile(jwkPath, []byte(wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().Jwks()), 0600) if err != nil { return err } return nil } -func createDeleteScript(targetDir string, wifConfig *models.WifConfigOutput) error { +func createDeleteScript(targetDir string, wifConfig *cmv1.WifConfig) error { // Write the script content to the path scriptContent := generateDeleteScriptContent(wifConfig) err := os.WriteFile(filepath.Join(targetDir, "delete.sh"), []byte(scriptContent), 0600) @@ -36,7 +36,7 @@ func createDeleteScript(targetDir string, wifConfig *models.WifConfigOutput) err return nil } -func generateDeleteScriptContent(wifConfig *models.WifConfigOutput) string { +func generateDeleteScriptContent(wifConfig *cmv1.WifConfig) string { scriptContent := "#!/bin/bash\n" // Append the script to delete the service accounts @@ -47,34 +47,34 @@ func generateDeleteScriptContent(wifConfig *models.WifConfigOutput) string { return scriptContent } -func deleteServiceAccountScriptContent(wifConfig *models.WifConfigOutput) string { +func deleteServiceAccountScriptContent(wifConfig *cmv1.WifConfig) string { var sb strings.Builder sb.WriteString("\n# Delete service accounts:\n") - for _, sa := range wifConfig.Status.ServiceAccounts { - project := wifConfig.Spec.ProjectId - serviceAccountID := sa.Id + for _, sa := range wifConfig.Gcp().ServiceAccounts() { + project := wifConfig.Gcp().ProjectId() + serviceAccountID := sa.ServiceAccountId() sb.WriteString(fmt.Sprintf("gcloud iam service-accounts delete %s --project=%s\n", serviceAccountID, project)) } return sb.String() } -func deleteIdentityPoolScriptContent(wifConfig *models.WifConfigOutput) string { - pool := wifConfig.Status.WorkloadIdentityPoolData +func deleteIdentityPoolScriptContent(wifConfig *cmv1.WifConfig) string { + pool := wifConfig.Gcp().WorkloadIdentityPool() // Delete the workload identity pool return fmt.Sprintf(` # Delete the workload identity pool gcloud iam workload-identity-pools delete %s --project=%s --location=global -`, pool.PoolId, pool.ProjectId) +`, pool.PoolId(), wifConfig.Gcp().ProjectId()) } -func generateScriptContent(wifConfig *models.WifConfigOutput) string { +func generateScriptContent(wifConfig *cmv1.WifConfig, projectNum int64) string { poolSpec := gcp.WorkloadIdentityPoolSpec{ - PoolName: wifConfig.Status.WorkloadIdentityPoolData.PoolId, - ProjectId: wifConfig.Status.WorkloadIdentityPoolData.ProjectId, - Jwks: wifConfig.Status.WorkloadIdentityPoolData.Jwks, - IssuerUrl: wifConfig.Status.WorkloadIdentityPoolData.IssuerUrl, - PoolIdentityProviderId: wifConfig.Status.WorkloadIdentityPoolData.IdentityProviderId, + PoolName: wifConfig.Gcp().WorkloadIdentityPool().PoolId(), + ProjectId: wifConfig.Gcp().ProjectId(), + Jwks: wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().Jwks(), + IssuerUrl: wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().IssuerUrl(), + PoolIdentityProviderId: wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().IdentityProviderId(), } scriptContent := "#!/bin/bash\n" @@ -86,7 +86,7 @@ func generateScriptContent(wifConfig *models.WifConfigOutput) string { scriptContent += createIdentityProviderScriptContent(poolSpec) // Append the script to create the service accounts - scriptContent += createServiceAccountScriptContent(wifConfig) + scriptContent += createServiceAccountScriptContent(wifConfig, projectNum) return scriptContent } @@ -122,29 +122,29 @@ gcloud iam workload-identity-pools providers create-oidc %s \ // This returns the gcloud commands to create a service account, bind roles, and grant access // to the workload identity pool -func createServiceAccountScriptContent(wifConfig *models.WifConfigOutput) string { +func createServiceAccountScriptContent(wifConfig *cmv1.WifConfig, projectNum int64) string { // For each service account, create a service account and bind it to the workload identity pool var sb strings.Builder sb.WriteString("\n# Create service accounts:\n") - for _, sa := range wifConfig.Status.ServiceAccounts { - project := wifConfig.Spec.ProjectId - serviceAccountID := sa.Id - serviceAccountName := wifConfig.Spec.DisplayName + "-" + serviceAccountID - serviceAccountDesc := poolDescription + " for WIF config " + wifConfig.Spec.DisplayName + for _, sa := range wifConfig.Gcp().ServiceAccounts() { + project := wifConfig.Gcp().ProjectId() + serviceAccountID := sa.ServiceAccountId() + serviceAccountName := wifConfig.DisplayName() + "-" + serviceAccountID + serviceAccountDesc := poolDescription + " for WIF config " + wifConfig.DisplayName() //nolint:lll sb.WriteString(fmt.Sprintf("gcloud iam service-accounts create %s --display-name=%s --description=\"%s\" --project=%s\n", serviceAccountID, serviceAccountName, serviceAccountDesc, project)) } sb.WriteString("\n# Create roles:\n") - for _, sa := range wifConfig.Status.ServiceAccounts { - for _, role := range sa.Roles { - if !role.Predefined { - roleId := strings.ReplaceAll(role.Id, "-", "_") - project := wifConfig.Spec.ProjectId - permissions := strings.Join(role.Permissions, ",") + for _, sa := range wifConfig.Gcp().ServiceAccounts() { + for _, role := range sa.Roles() { + if !role.Predefined() { + roleId := strings.ReplaceAll(role.RoleId(), "-", "_") + project := wifConfig.Gcp().ProjectId() + permissions := strings.Join(role.Permissions(), ",") roleName := roleId - serviceAccountDesc := roleDescription + " for WIF config " + wifConfig.Spec.DisplayName + serviceAccountDesc := roleDescription + " for WIF config " + wifConfig.DisplayName() //nolint:lll sb.WriteString(fmt.Sprintf("gcloud iam roles create %s --project=%s --title=%s --description=\"%s\" --stage=GA --permissions=%s\n", roleId, project, roleName, serviceAccountDesc, permissions)) @@ -152,45 +152,45 @@ func createServiceAccountScriptContent(wifConfig *models.WifConfigOutput) string } } sb.WriteString("\n# Bind service account roles:\n") - for _, sa := range wifConfig.Status.ServiceAccounts { - for _, role := range sa.Roles { - project := wifConfig.Spec.ProjectId - member := fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", sa.Id, project) + for _, sa := range wifConfig.Gcp().ServiceAccounts() { + for _, role := range sa.Roles() { + project := wifConfig.Gcp().ProjectId() + member := fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", sa.ServiceAccountId(), project) sb.WriteString(fmt.Sprintf("gcloud projects add-iam-policy-binding %s --member=%s --role=roles/%s\n", - project, member, role.Id)) + project, member, role.RoleId())) } } sb.WriteString("\n# Grant access:\n") - for _, sa := range wifConfig.Status.ServiceAccounts { - if sa.AccessMethod == "wif" { - project := wifConfig.Spec.ProjectId - serviceAccount := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", sa.Id, project) - members := fmtMembers(sa, wifConfig.Status.WorkloadIdentityPoolData.ProjectNumber, - wifConfig.Status.WorkloadIdentityPoolData.PoolId) + for _, sa := range wifConfig.Gcp().ServiceAccounts() { + if sa.AccessMethod() == "wif" { + project := wifConfig.Gcp().ProjectId() + serviceAccount := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", sa.ServiceAccountId(), project) + members := fmtMembers(sa, projectNum, + wifConfig.Gcp().WorkloadIdentityPool().PoolId()) for _, member := range members { //nolint:lll sb.WriteString(fmt.Sprintf("gcloud iam service-accounts add-iam-policy-binding %s --member=%s --role=roles/iam.workloadIdentityUser --project=%s\n", serviceAccount, member, project)) } - } else if sa.AccessMethod == "impersonate" { - project := wifConfig.Spec.ProjectId - serviceAccount := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", sa.Id, project) + } else if sa.AccessMethod() == "impersonate" { + project := wifConfig.Gcp().ProjectId() + serviceAccount := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", sa.ServiceAccountId(), project) impersonator := fmt.Sprintf("serviceAccount:%s", impersonatorEmail) //nolint:lll sb.WriteString(fmt.Sprintf("gcloud iam service-accounts add-iam-policy-binding %s --member=%s --role=roles/iam.serviceAccountTokenCreator --project=%s\n", - serviceAccount, impersonator, wifConfig.Spec.ProjectId)) + serviceAccount, impersonator, wifConfig.Gcp().ProjectId())) } } return sb.String() } -func fmtMembers(sa models.ServiceAccount, projectNum int64, poolId string) []string { +func fmtMembers(sa *cmv1.WifServiceAccount, projectNum int64, poolId string) []string { members := []string{} - for _, saName := range sa.GetServiceAccountNames() { + for _, saName := range sa.CredentialRequest().ServiceAccountNames() { //nolint:lll members = append(members, fmt.Sprintf( "principal://iam.googleapis.com/projects/%d/locations/global/workloadIdentityPools/%s/subject/system:serviceaccount:%s:%s", - projectNum, poolId, sa.GetSecretNamespace(), saName)) + projectNum, poolId, sa.CredentialRequest().SecretRef().Namespace(), saName)) } return members } diff --git a/go.mod b/go.mod index bb6772c8..f8a9ea1a 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/nwidger/jsoncolor v0.3.2 github.com/onsi/ginkgo/v2 v2.11.0 github.com/onsi/gomega v1.27.8 - github.com/openshift-online/ocm-sdk-go v0.1.422 + github.com/openshift-online/ocm-sdk-go v0.1.431 github.com/openshift/rosa v1.2.24 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index f0ce8af2..a41ec5ed 100644 --- a/go.sum +++ b/go.sum @@ -361,8 +361,8 @@ github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/openshift-online/ocm-sdk-go v0.1.422 h1:NWXLNTg7sLgUJRM3tyuk/QuVbUCRuMH+aLlbCKNzXWc= -github.com/openshift-online/ocm-sdk-go v0.1.422/go.mod h1:CiAu2jwl3ITKOxkeV0Qnhzv4gs35AmpIzVABQLtcI2Y= +github.com/openshift-online/ocm-sdk-go v0.1.431 h1:jEWVqkf2EyXV+ocIBAAO+Htl0jXFShenKM/h5aSP46Q= +github.com/openshift-online/ocm-sdk-go v0.1.431/go.mod h1:CiAu2jwl3ITKOxkeV0Qnhzv4gs35AmpIzVABQLtcI2Y= github.com/openshift/rosa v1.2.24 h1:vv0yYnWHx6CCPEAau/0rS54P2ksaf+uWXb1TQPWxiYE= github.com/openshift/rosa v1.2.24/go.mod h1:MVXB27O3PF8WoOic23I03mmq6/9kVxpFx6FKyLMCyrQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= diff --git a/pkg/alpha_ocm/client.go b/pkg/alpha_ocm/client.go deleted file mode 100644 index b284c986..00000000 --- a/pkg/alpha_ocm/client.go +++ /dev/null @@ -1,125 +0,0 @@ -package alphaocm - -/* This package will be replaced with calls to the sdk once available */ - -import ( - "encoding/json" - "fmt" - "os" - - sdk "github.com/openshift-online/ocm-sdk-go" - - "github.com/openshift-online/ocm-cli/pkg/config" - "github.com/openshift-online/ocm-cli/pkg/dump" - "github.com/openshift-online/ocm-cli/pkg/models" -) - -type OcmClient interface { - CreateWifConfig(models.WifConfigInput) (models.WifConfigOutput, error) - GetWifConfig(string) (models.WifConfigOutput, error) - ListWifConfigs() ([]models.WifConfigOutput, error) - DeleteWifConfig(string) error -} - -type ocmClient struct { - connection *sdk.Connection -} - -func NewOcmClient() (OcmClient, error) { - // Load the configuration file: - cfg, err := config.Load() - if err != nil { - return nil, fmt.Errorf("can't load config file: %v", err) - } - if cfg == nil { - return nil, fmt.Errorf("not logged in, run the 'login' command") - } - - // Check that the configuration has credentials or tokens that don't have expired: - armed, reason, err := cfg.Armed() - if err != nil { - return nil, fmt.Errorf(err.Error()) - } - if !armed { - return nil, fmt.Errorf("not logged in, %s, run the 'login' command", reason) - } - - // Create the connection: - connection, err := cfg.Connection() - if err != nil { - return nil, fmt.Errorf("can't create connection: %v", err) - } - return &ocmClient{ - connection: connection, - }, nil -} - -func (c *ocmClient) CreateWifConfig(input models.WifConfigInput) (models.WifConfigOutput, error) { - var wifConfigOutput models.WifConfigOutput - - inputJson, err := json.Marshal(input) - if err != nil { - return wifConfigOutput, fmt.Errorf("failed to marshal wif input: %v", err) - } - - resp, err := c.connection.Post().Path("/api/clusters_mgmt/v1/gcp/wif_configs").Bytes(inputJson).Send() - if err != nil { - return wifConfigOutput, fmt.Errorf("can't send request: %v", err) - } - - status := resp.Status() - body := resp.Bytes() - - if status >= 400 { - dump.Pretty(os.Stderr, body) - return wifConfigOutput, fmt.Errorf("failed to create WIF config: %s", string(body)) - } - - wifConfigOutput, err = models.WifConfigOutputFromJson(body) - return wifConfigOutput, err -} - -func (c *ocmClient) GetWifConfig(id string) (models.WifConfigOutput, error) { - var wifConfigOutput models.WifConfigOutput - resp, err := c.connection.Get().Path(fmt.Sprintf("/api/clusters_mgmt/v1/gcp/wif_configs/%s", id)).Send() - if err != nil { - return wifConfigOutput, fmt.Errorf("can't send request: %v", err) - } - if resp.Status() >= 400 { - body := resp.Bytes() - dump.Pretty(os.Stderr, body) - return wifConfigOutput, fmt.Errorf("failed to list WIF configs: %s", string(body)) - } - - wifConfigOutput, err = models.WifConfigOutputFromJson(resp.Bytes()) - return wifConfigOutput, err -} - -func (c *ocmClient) ListWifConfigs() ([]models.WifConfigOutput, error) { - var wifConfigs []models.WifConfigOutput - resp, err := c.connection.Get().Path("/api/clusters_mgmt/v1/gcp/wif_configs").Send() - if err != nil { - return wifConfigs, fmt.Errorf("can't send request: %v", err) - } - if resp.Status() >= 400 { - body := resp.Bytes() - dump.Pretty(os.Stderr, body) - return wifConfigs, fmt.Errorf("failed to list WIF configs: %s", string(body)) - } - - wifConfigsList, err := models.WifConfigOutputListFromJson(resp.Bytes()) - return wifConfigsList.Items, err -} - -func (c *ocmClient) DeleteWifConfig(id string) error { - resp, err := c.connection.Delete().Path(fmt.Sprintf("/api/clusters_mgmt/v1/gcp/wif_configs/%s", id)).Send() - if err != nil { - return fmt.Errorf("can't send request: %v", err) - } - if resp.Status() >= 400 { - body := resp.Bytes() - dump.Pretty(os.Stderr, body) - return fmt.Errorf("failed to delete WIF config: %s", string(body)) - } - return nil -} diff --git a/pkg/gcp/client.go b/pkg/gcp/client.go index 77df428a..7335b47d 100644 --- a/pkg/gcp/client.go +++ b/pkg/gcp/client.go @@ -10,7 +10,9 @@ import ( "cloud.google.com/go/iam/admin/apiv1/adminpb" "cloud.google.com/go/iam/apiv1/iampb" "cloud.google.com/go/storage" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1" + iamv1 "google.golang.org/api/iam/v1" "google.golang.org/api/iterator" secretmanager "google.golang.org/api/secretmanager/v1" @@ -35,7 +37,7 @@ type GcpClient interface { SetProjectIamPolicy(svcAcctResource string, request *cloudresourcemanager.SetIamPolicyRequest) (*cloudresourcemanager.Policy, error) //nolint:lll AttachImpersonator(saId, projectId, impersonatorResourceId string) error - AttachWorkloadIdentityPool(sa ServiceAccount, poolId, projectId string) error + AttachWorkloadIdentityPool(sa *cmv1.WifServiceAccount, poolId, projectId string) error SaveSecret(secretId, projectId string, secretData []byte) error RetreiveSecret(secretId string, projectId string) ([]byte, error) @@ -50,12 +52,6 @@ type GcpClient interface { ListRoles(context.Context, *adminpb.ListRolesRequest) (*adminpb.ListRolesResponse, error) } -type ServiceAccount interface { - GetId() string - GetSecretNamespace() string - GetServiceAccountNames() []string -} - type gcpClient struct { ctx context.Context iamClient *iamadmin.IamClient @@ -161,8 +157,8 @@ func (c *gcpClient) AttachImpersonator(saId, projectId string, impersonatorId st return nil } -func (c *gcpClient) AttachWorkloadIdentityPool(sa ServiceAccount, poolId, projectId string) error { - saResourceId := c.fmtSaResourceId(sa.GetId(), projectId) +func (c *gcpClient) AttachWorkloadIdentityPool(sa *cmv1.WifServiceAccount, poolId, projectId string) error { + saResourceId := c.fmtSaResourceId(sa.ServiceAccountId(), projectId) projectNum, err := c.ProjectNumberFromId(projectId) if err != nil { @@ -175,12 +171,12 @@ func (c *gcpClient) AttachWorkloadIdentityPool(sa ServiceAccount, poolId, projec if err != nil { return c.handleAttachWorkloadIdentityPoolError(err) } - for _, openshiftServiceAccount := range sa.GetServiceAccountNames() { + for _, openshiftServiceAccount := range sa.CredentialRequest().ServiceAccountNames() { policy.Add( //nolint:lll fmt.Sprintf( "principal://iam.googleapis.com/projects/%d/locations/global/workloadIdentityPools/%s/subject/system:serviceaccount:%s:%s", - projectNum, poolId, sa.GetSecretNamespace(), openshiftServiceAccount, + projectNum, poolId, sa.CredentialRequest().SecretRef().Namespace(), openshiftServiceAccount, ), iam.RoleName("roles/iam.workloadIdentityUser")) } diff --git a/pkg/models/role.go b/pkg/models/role.go deleted file mode 100644 index 654967c0..00000000 --- a/pkg/models/role.go +++ /dev/null @@ -1,8 +0,0 @@ -package models - -type Role struct { - Id string `json:"id,omitempty"` - Kind string `json:"kind,omitempty"` - Permissions []string `json:"permissions,omitempty"` - Predefined bool `json:"predefined,omitempty"` -} diff --git a/pkg/models/service_account.go b/pkg/models/service_account.go deleted file mode 100644 index 12d6adaa..00000000 --- a/pkg/models/service_account.go +++ /dev/null @@ -1,36 +0,0 @@ -package models - -type ServiceAccount struct { - AccessMethod string `json:"access_method,omitempty"` - CredentialRequest CredentialRequest `json:"credential_request,omitempty"` - Id string `json:"id,omitempty"` - Kind string `json:"kind,omitempty"` - OsdRole string `json:"osd_role,omitempty"` - Roles []Role `json:"roles,omitempty"` -} - -type CredentialRequest struct { - SecretRef SecretRef - ServiceAccountNames []string -} - -type SecretRef struct { - Name string - Namespace string -} - -func (s ServiceAccount) GetId() string { - return s.Id -} - -func (s ServiceAccount) GetSecretName() string { - return s.CredentialRequest.SecretRef.Name -} - -func (s ServiceAccount) GetSecretNamespace() string { - return s.CredentialRequest.SecretRef.Namespace -} - -func (s ServiceAccount) GetServiceAccountNames() []string { - return s.CredentialRequest.ServiceAccountNames -} diff --git a/pkg/models/wif_config_input.go b/pkg/models/wif_config_input.go deleted file mode 100644 index 00da13d9..00000000 --- a/pkg/models/wif_config_input.go +++ /dev/null @@ -1,6 +0,0 @@ -package models - -type WifConfigInput struct { - DisplayName string `json:"display_name"` - ProjectId string `json:"project_id"` -} diff --git a/pkg/models/wif_config_list.go b/pkg/models/wif_config_list.go deleted file mode 100644 index 28b6518d..00000000 --- a/pkg/models/wif_config_list.go +++ /dev/null @@ -1,7 +0,0 @@ -package models - -type WifConfigList struct { - Items []WifConfigOutput `json:"items,omitempty"` - Page int32 `json:"page,omitempty"` - Total int32 `json:"total,omitempty"` -} diff --git a/pkg/models/wif_config_metadata.go b/pkg/models/wif_config_metadata.go deleted file mode 100644 index a9321a3a..00000000 --- a/pkg/models/wif_config_metadata.go +++ /dev/null @@ -1,7 +0,0 @@ -package models - -type WifConfigMetadata struct { - DisplayName string `json:"display_name,omitempty"` - Id string `json:"id,omitempty"` - Organization *WifConfigMetadataOrganization `json:"organization,omitempty"` -} diff --git a/pkg/models/wif_config_metadata_organization.go b/pkg/models/wif_config_metadata_organization.go deleted file mode 100644 index b64deece..00000000 --- a/pkg/models/wif_config_metadata_organization.go +++ /dev/null @@ -1,6 +0,0 @@ -package models - -type WifConfigMetadataOrganization struct { - Href string `json:"href,omitempty"` - Kind string `json:"kind,omitempty"` -} diff --git a/pkg/models/wif_config_output.go b/pkg/models/wif_config_output.go deleted file mode 100644 index 820ac233..00000000 --- a/pkg/models/wif_config_output.go +++ /dev/null @@ -1,32 +0,0 @@ -package models - -import ( - "encoding/json" - "fmt" -) - -type WifConfigOutput struct { - Metadata *WifConfigMetadata `json:"metadata,omitempty"` - Spec *WifConfigInput `json:"spec,omitempty"` - Status *WifConfigStatus `json:"status,omitempty"` -} - -type WifConfigOutputList struct { - Items []WifConfigOutput `json:"items"` -} - -func WifConfigOutputFromJson(rawWifConfigOutput []byte) (WifConfigOutput, error) { - var output WifConfigOutput - err := json.Unmarshal(rawWifConfigOutput, &output) - return output, err -} - -func WifConfigOutputListFromJson(data []byte) (*WifConfigOutputList, error) { - var wifConfigListOutput WifConfigOutputList - err := json.Unmarshal(data, &wifConfigListOutput) - if err != nil { - return nil, fmt.Errorf("error unmarshalling data: %v", err) - } - return &wifConfigListOutput, nil - -} diff --git a/pkg/models/wif_config_status.go b/pkg/models/wif_config_status.go deleted file mode 100644 index 0e1c2ace..00000000 --- a/pkg/models/wif_config_status.go +++ /dev/null @@ -1,23 +0,0 @@ -package models - -type WifConfigStatus struct { - ServiceAccounts []ServiceAccount `json:"service_accounts,omitempty"` - State string `json:"state,omitempty"` - Summary string `json:"summary,omitempty"` - TimeData WifTimeData `json:"time_data,omitempty"` - WorkloadIdentityPoolData WifWorkloadIdentityPoolData `json:"workload_identity_pool,omitempty"` -} - -type WifWorkloadIdentityPoolData struct { - IdentityProviderId string `json:"identity_provider_id,omitempty"` - IssuerUrl string `json:"issuer_url,omitempty"` - Jwks string `json:"jwks,omitempty"` - PoolId string `json:"pool_id,omitempty"` - ProjectId string `json:"gcp_project_name,omitempty"` - ProjectNumber int64 `json:"gcp_project_num,omitempty"` -} - -type WifTimeData struct { - LastChecked string `json:"last_checked,omitempty"` - CreatedAt string `json:"created_at,omitempty"` -} diff --git a/pkg/models/wif_template.go b/pkg/models/wif_template.go deleted file mode 100644 index bb22986e..00000000 --- a/pkg/models/wif_template.go +++ /dev/null @@ -1,7 +0,0 @@ -package models - -type WifTemplate struct { - Id string `json:"id,omitempty"` - Kind string `json:"kind,omitempty"` - ServiceAccounts []ServiceAccount `json:"service_accounts,omitempty"` -}