Skip to content

Commit

Permalink
Make adjustments for new sdk version and custom roles
Browse files Browse the repository at this point in the history
  • Loading branch information
JakobGray committed Jul 30, 2024
1 parent 7049ed3 commit 98bdffe
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 100 deletions.
113 changes: 63 additions & 50 deletions cmd/ocm/gcp/create-wif-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,6 @@ func createWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) {
log.Fatalf("failed to create WIF config: %v", err)
}

poolSpec := gcp.WorkloadIdentityPoolSpec{
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)

Expand All @@ -97,12 +89,12 @@ func createWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) {
return
}

if err = createWorkloadIdentityPool(ctx, gcpClient, poolSpec); err != nil {
if err = createWorkloadIdentityPool(ctx, gcpClient, wifConfig); 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.ID())
}

if err = createWorkloadIdentityProvider(ctx, gcpClient, poolSpec); err != nil {
if err = createWorkloadIdentityProvider(ctx, gcpClient, wifConfig); 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.ID())
}
Expand Down Expand Up @@ -164,7 +156,7 @@ func createWorkloadIdentityConfiguration(displayName, projectId string) (*cmv1.W
return nil, errors.Wrap(err, "failed to build WIF config")
}

response, err := connection.ClustersMgmt().V1().
response, err := connection.ClustersMgmt().V1().GCP().
WifConfigs().
Add().
Body(wifConfigInput).
Expand All @@ -177,82 +169,88 @@ func createWorkloadIdentityConfiguration(displayName, projectId string) (*cmv1.W
}

func createWorkloadIdentityPool(ctx context.Context, client gcp.GcpClient,
spec gcp.WorkloadIdentityPoolSpec) error {
name := spec.PoolName
project := spec.ProjectId
wifConfig *cmv1.WifConfig) error {
poolId := wifConfig.Gcp().WorkloadIdentityPool().PoolId()
project := wifConfig.Gcp().ProjectId()

parentResourceForPool := fmt.Sprintf("projects/%s/locations/global", project)
poolResource := fmt.Sprintf("%s/workloadIdentityPools/%s", parentResourceForPool, name)
poolResource := fmt.Sprintf("%s/workloadIdentityPools/%s", parentResourceForPool, poolId)
resp, err := client.GetWorkloadIdentityPool(ctx, poolResource)
if resp != nil && resp.State == "DELETED" {
log.Printf("Workload identity pool %s was deleted", name)
log.Printf("Workload identity pool %s was deleted", poolId)
_, err := client.UndeleteWorkloadIdentityPool(ctx, poolResource, &iamv1.UndeleteWorkloadIdentityPoolRequest{})
if err != nil {
return errors.Wrapf(err, "failed to undelete workload identity pool %s", name)
return errors.Wrapf(err, "failed to undelete workload identity pool %s", poolId)
}
} else if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 &&
strings.Contains(gerr.Message, "Requested entity was not found") {
pool := &iamv1.WorkloadIdentityPool{
Name: name,
DisplayName: name,
Name: poolId,
DisplayName: poolId,
Description: poolDescription,
State: "ACTIVE",
Disabled: false,
}

_, err := client.CreateWorkloadIdentityPool(ctx, parentResourceForPool, name, pool)
_, err := client.CreateWorkloadIdentityPool(ctx, parentResourceForPool, poolId, pool)
if err != nil {
return errors.Wrapf(err, "failed to create workload identity pool %s", name)
return errors.Wrapf(err, "failed to create workload identity pool %s", poolId)
}
log.Printf("Workload identity pool created with name %s", name)
log.Printf("Workload identity pool created with name %s", poolId)
} else {
return errors.Wrapf(err, "failed to check if there is existing workload identity pool %s", name)
return errors.Wrapf(err, "failed to check if there is existing workload identity pool %s", poolId)
}
} else {
log.Printf("Workload identity pool %s already exists", name)
log.Printf("Workload identity pool %s already exists", poolId)
}

return nil
}

func createWorkloadIdentityProvider(ctx context.Context, client gcp.GcpClient,
spec gcp.WorkloadIdentityPoolSpec) error {
//nolint:lll
providerResource := fmt.Sprintf("projects/%s/locations/global/workloadIdentityPools/%s/providers/%s", spec.ProjectId, spec.PoolName, spec.PoolName)
wifConfig *cmv1.WifConfig) error {
projectId := wifConfig.Gcp().ProjectId()
poolId := wifConfig.Gcp().WorkloadIdentityPool().PoolId()
jwks := wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().Jwks()
audiences := wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().AllowedAudiences()
issuerUrl := wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().IssuerUrl()
providerId := wifConfig.Gcp().WorkloadIdentityPool().IdentityProvider().IdentityProviderId()

parent := fmt.Sprintf("projects/%s/locations/global/workloadIdentityPools/%s", projectId, poolId)
providerResource := fmt.Sprintf("%s/providers/%s", parent, providerId)

_, err := client.GetWorkloadIdentityProvider(ctx, providerResource)
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 &&
strings.Contains(gerr.Message, "Requested entity was not found") {
provider := &iamv1.WorkloadIdentityPoolProvider{
Name: spec.PoolName,
DisplayName: spec.PoolName,
Name: providerId,
DisplayName: providerId,
Description: poolDescription,
State: "ACTIVE",
Disabled: false,
Oidc: &iamv1.Oidc{
AllowedAudiences: spec.Audience,
IssuerUri: spec.IssuerUrl,
JwksJson: spec.Jwks,
AllowedAudiences: audiences,
IssuerUri: issuerUrl,
JwksJson: jwks,
},
AttributeMapping: map[string]string{
"google.subject": "assertion.sub",
},
}

parent := fmt.Sprintf("projects/%s/locations/global/workloadIdentityPools/%s",
spec.ProjectId, spec.PoolName)
_, err := client.CreateWorkloadIdentityProvider(ctx, parent, spec.PoolName, provider)
_, err := client.CreateWorkloadIdentityProvider(ctx, parent, providerId, provider)
if err != nil {
return errors.Wrapf(err, "failed to create workload identity provider %s", spec.PoolName)
return errors.Wrapf(err, "failed to create workload identity provider %s", providerId)
}
log.Printf("workload identity provider created with name %s", spec.PoolName)
log.Printf("workload identity provider created with name %s", providerId)
} else {
return errors.Wrapf(err, "failed to check if there is existing workload identity provider %s in pool %s",
spec.PoolName, spec.PoolName)
providerId, poolId)
}
} else {
return errors.Errorf("workload identity provider %s already exists in pool %s", spec.PoolName, spec.PoolName)
return errors.Errorf("workload identity provider %s already exists in pool %s", providerId, poolId)
}

return nil
Expand All @@ -261,7 +259,11 @@ func createWorkloadIdentityProvider(ctx context.Context, client gcp.GcpClient,
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())
if role.Predefined() {
return fmt.Sprintf("roles/%s", role.RoleId())
} else {
return fmt.Sprintf("projects/%s/roles/%s", projectId, role.RoleId())
}
}

// Create service accounts
Expand All @@ -284,30 +286,41 @@ func createServiceAccounts(ctx context.Context, gcpClient gcp.GcpClient, wifConf
continue
}
roleID := role.RoleId()
roleName := role.RoleId()
roleTitle := role.RoleId()
permissions := role.Permissions()
existingRole, err := GetRole(gcpClient, roleID, projectId)
existingRole, err := GetRole(gcpClient, fmtRoleResourceId(role))
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 &&
strings.Contains(gerr.Message, "Requested entity was not found") {
existingRole, err = CreateRole(gcpClient, permissions, roleName,
if gerr, ok := err.(*apierror.APIError); ok && gerr.GRPCStatus().Code() == codes.NotFound {
_, err = CreateRole(gcpClient, permissions, roleTitle,
roleID, roleDescription, projectId)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Failed to create %s", roleName))
return errors.Wrap(err, fmt.Sprintf("Failed to create %s", roleID))
}
log.Printf("Role %s created", roleID)
log.Printf("Role %q created", roleID)
continue
} else {
return errors.Wrap(err, "Failed to check if role exists")
}
}

// Undelete role if it was deleted
if existingRole.Deleted {
_, err = UndeleteRole(gcpClient, fmtRoleResourceId(role))
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Failed to undelete custom role %q", roleID))
}
existingRole.Deleted = false
log.Printf("Role %q undeleted", roleID)
}

// Update role if permissions have changed
if !reflect.DeepEqual(existingRole.IncludedPermissions, permissions) {
existingRole.IncludedPermissions = permissions
_, err := UpdateRole(gcpClient, existingRole, roleName)
_, err := UpdateRole(gcpClient, existingRole, fmtRoleResourceId(role))
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Failed to update %s", roleName))
return errors.Wrap(err, fmt.Sprintf("Failed to update %s", roleID))
}
log.Printf("Role %s updated", roleID)
log.Printf("Role %q updated", roleID)
}
}
}
Expand Down
19 changes: 8 additions & 11 deletions cmd/ocm/gcp/delete-wif-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ package gcp
import (
"context"
"fmt"
"strings"

"log"

"github.com/googleapis/gax-go/v2/apierror"
"google.golang.org/grpc/codes"

"github.com/openshift-online/ocm-cli/pkg/gcp"
"github.com/openshift-online/ocm-cli/pkg/ocm"
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
"github.com/pkg/errors"
"google.golang.org/api/googleapi"

"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -66,7 +65,7 @@ func deleteWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) {
}
defer connection.Close()

response, err := connection.ClustersMgmt().V1().WifConfigs().WifConfig(wifConfigId).Get().Send()
response, err := connection.ClustersMgmt().V1().GCP().WifConfigs().WifConfig(wifConfigId).Get().Send()
if err != nil {
log.Fatalf("failed to get wif-config: %v", err)
}
Expand Down Expand Up @@ -95,7 +94,7 @@ func deleteWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) {
log.Fatal(err)
}

_, err = connection.ClustersMgmt().V1().WifConfigs().
_, err = connection.ClustersMgmt().V1().GCP().WifConfigs().
WifConfig(wifConfigId).
Delete().
Send()
Expand Down Expand Up @@ -130,12 +129,10 @@ func deleteWorkloadIdentityPool(ctx context.Context, gcpClient gcp.GcpClient,

_, err := gcpClient.DeleteWorkloadIdentityPool(ctx, poolResource)
if err != nil {
pApiError, ok := err.(*apierror.APIError)
if ok {
if pApiError.GRPCStatus().Code() == codes.NotFound && allowMissing {
log.Printf("Workload identity pool %s not found", poolName)
return nil
}
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 &&
strings.Contains(gerr.Message, "Requested entity was not found") && allowMissing {
log.Printf("Workload identity pool %s not found", poolName)
return nil
}
return errors.Wrapf(err, "Failed to delete workload identity pool %s", poolName)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/ocm/gcp/describe-wif-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func describeWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string)
}
defer connection.Close()

response, err := connection.ClustersMgmt().V1().WifConfigs().WifConfig(id).Get().Send()
response, err := connection.ClustersMgmt().V1().GCP().WifConfigs().WifConfig(id).Get().Send()
if err != nil {
log.Fatalf("failed to get wif-config: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/ocm/gcp/generate-wif-script.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func generateCreateScriptCmd(cmd *cobra.Command, argv []string) {
log.Fatal("WIF config ID is required")
}

response, err := connection.ClustersMgmt().V1().WifConfigs().WifConfig(wifConfigId).Get().Send()
response, err := connection.ClustersMgmt().V1().GCP().WifConfigs().WifConfig(wifConfigId).Get().Send()
if err != nil {
log.Fatalf("failed to get wif-config: %v", err)
}
Expand Down
26 changes: 14 additions & 12 deletions cmd/ocm/gcp/iam.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ package gcp
import (
"context"
"fmt"
"log"

"cloud.google.com/go/iam/admin/apiv1/adminpb"
"github.com/openshift-online/ocm-cli/pkg/gcp"

cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1"
)

// EnsurePolicyBindingsForProject ensures that given roles and member, appropriate binding is added to project
// EnsurePolicyBindingsForProject ensures that given roles and member, appropriate binding is added to project.
// Roles should be in the format projects/{project}/roles/{role_id} for custom roles and roles/{role_id}
// for predefined roles.
func EnsurePolicyBindingsForProject(gcpClient gcp.GcpClient, roles []string, member string, projectName string) error {
needPolicyUpdate := false

Expand Down Expand Up @@ -88,44 +89,45 @@ func setProjectIamPolicy(gcpClient gcp.GcpClient, policy *cloudresourcemanager.P

/* Custom Role Creation */

// GetRole fetches the role created to satisfy a credentials request
func GetRole(gcpClient gcp.GcpClient, roleID, projectName string) (*adminpb.Role, error) {
log.Printf("role id %v", roleID)
// GetRole fetches the role created to satisfy a credentials request.
// Custom roles should follow the format projects/{project}/roles/{role_id}.
func GetRole(gcpClient gcp.GcpClient, roleName string) (*adminpb.Role, error) {
role, err := gcpClient.GetRole(context.TODO(), &adminpb.GetRoleRequest{
Name: fmt.Sprintf("projects/%s/roles/%s", projectName, roleID),
Name: roleName,
})
return role, err
}

// CreateRole creates a new role given permissions
func CreateRole(gcpClient gcp.GcpClient, permissions []string, roleName, roleID, roleDescription,
func CreateRole(gcpClient gcp.GcpClient, permissions []string, roleTitle, roleId, roleDescription,
projectName string) (*adminpb.Role, error) {
role, err := gcpClient.CreateRole(context.TODO(), &adminpb.CreateRoleRequest{
Role: &adminpb.Role{
Title: roleName,
Title: roleTitle,
Description: roleDescription,
IncludedPermissions: permissions,
Stage: adminpb.Role_GA,
},
Parent: fmt.Sprintf("projects/%s", projectName),
RoleId: roleID,
RoleId: roleId,
})
if err != nil {
return nil, err
}
return role, nil
}

// UpdateRole updates an existing role given permissions
// UpdateRole updates an existing role given permissions.
// Custom roles should follow the format projects/{project}/roles/{role_id}.
func UpdateRole(gcpClient gcp.GcpClient, role *adminpb.Role, roleName string) (*adminpb.Role, error) {
role, err := gcpClient.UpdateRole(context.TODO(), &adminpb.UpdateRoleRequest{
updated, err := gcpClient.UpdateRole(context.TODO(), &adminpb.UpdateRoleRequest{
Name: roleName,
Role: role,
})
if err != nil {
return nil, err
}
return role, nil
return updated, nil
}

// DeleteRole deletes the role created to satisfy a credentials request
Expand Down
4 changes: 2 additions & 2 deletions cmd/ocm/gcp/list-wif-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func NewListWorkloadIdentityConfiguration() *cobra.Command {
fs.StringVar(
&ListWorkloadIdentityConfigurationOpts.columns,
"columns",
"metadata.id, metadata.displayName",
"id, display_name",
"Specify which columns to display separated by commas, path is based on wif-config struct",
)
fs.BoolVar(
Expand Down Expand Up @@ -91,7 +91,7 @@ func listWorkloadIdentityConfigurationCmd(cmd *cobra.Command, argv []string) {
}

// Create the request
request := connection.ClustersMgmt().V1().WifConfigs().List()
request := connection.ClustersMgmt().V1().GCP().WifConfigs().List()

size := 100
index := 1
Expand Down
Loading

0 comments on commit 98bdffe

Please sign in to comment.