From b4f84920858fc3b8b24bc1980b51849114eebcf4 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Wed, 17 Jul 2024 09:51:39 -0700 Subject: [PATCH] Set up AWS clients before building resource syncer. (#48) * Better names for client getter functions. * Set up AWS clients before building resource syncer. This lets us return errors instead of setting up bad resource syncers which would then panic. --- pkg/connector/connector.go | 119 +++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 19ee917b..7afaf1ce 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -115,33 +115,24 @@ type AWS struct { _identityInstancesCacheMtx sync.Mutex _identityInstancesCacheErr error _identityInstancesCache []*awsSsoAdminTypes.InstanceMetadata -} -func (o *AWS) iamClient(ctx context.Context) (*iam.Client, error) { - callingConfig, err := o.getCallingConfig(ctx, o.globalRegion) - if err != nil { - return nil, err - } - return iam.NewFromConfig(callingConfig), nil + iamClient *iam.Client + orgClient *awsOrgs.Client + ssoAdminClient *awsSsoAdmin.Client + ssoSCIMClient *awsIdentityCenterSCIMClient + identityStoreClient *awsIdentityStore.Client + identityInstance *awsSsoAdminTypes.InstanceMetadata } -func (o *AWS) orgClient(ctx context.Context) (*awsOrgs.Client, error) { +func (o *AWS) getIAMClient(ctx context.Context) (*iam.Client, error) { callingConfig, err := o.getCallingConfig(ctx, o.globalRegion) if err != nil { return nil, err } - return awsOrgs.NewFromConfig(callingConfig), nil -} - -func (o *AWS) ssoAdminClient(ctx context.Context) (*awsSsoAdmin.Client, error) { - callingConfig, err := o.getCallingConfig(ctx, o.ssoRegion) - if err != nil { - return nil, err - } - return awsSsoAdmin.NewFromConfig(callingConfig), nil + return iam.NewFromConfig(callingConfig), nil } -func (o *AWS) ssoSCIMClient(ctx context.Context) (*awsIdentityCenterSCIMClient, error) { +func (o *AWS) getSSOSCIMClient(ctx context.Context) (*awsIdentityCenterSCIMClient, error) { if !o.scimEnabled { return &awsIdentityCenterSCIMClient{scimEnabled: false}, nil } @@ -165,7 +156,7 @@ func (o *AWS) ssoSCIMClient(ctx context.Context) (*awsIdentityCenterSCIMClient, }, nil } -func (o *AWS) stsClient(ctx context.Context) (*sts.Client, error) { +func (o *AWS) getSTSClient(ctx context.Context) (*sts.Client, error) { callingConfig, err := o.getCallingConfig(ctx, o.globalRegion) if err != nil { return nil, err @@ -173,14 +164,6 @@ func (o *AWS) stsClient(ctx context.Context) (*sts.Client, error) { return sts.NewFromConfig(callingConfig), nil } -func (o *AWS) identityStoreClient(ctx context.Context) (*awsIdentityStore.Client, error) { - callingConfig, err := o.getCallingConfig(ctx, o.ssoRegion) - if err != nil { - return nil, err - } - return awsIdentityStore.NewFromConfig(callingConfig), nil -} - func (o *AWS) getCallingConfig(ctx context.Context, region string) (awsSdk.Config, error) { if _, ok := o._onceCallingConfig[region]; !ok { o._onceCallingConfig[region] = new(sync.Once) @@ -286,11 +269,16 @@ func New(ctx context.Context, config Config) (*AWS, error) { return nil, fmt.Errorf("aws-connector: SSO Support requires Org support to also be enabled. Please enable both") } + err = rv.SetupClients(ctx) + if err != nil { + return nil, err + } + return rv, nil } func (c *AWS) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error) { - stsSvc, err := c.stsClient(ctx) + stsSvc, err := c.getSTSClient(ctx) if err != nil { return nil, err } @@ -305,7 +293,7 @@ func (c *AWS) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error) { return nil, fmt.Errorf("aws-connector: failed to validate ARN: %w", err) } - iamClient, err := c.iamClient(ctx) + iamClient, err := c.getIAMClient(ctx) if err != nil { return nil, err } @@ -348,48 +336,68 @@ func (c *AWS) Asset(ctx context.Context, asset *v2.AssetRef) (string, io.ReadClo return "", nil, nil } -func (c *AWS) ResourceSyncers(ctx context.Context) []connectorbuilder.ResourceSyncer { - l := ctxzap.Extract(ctx) - rs := []connectorbuilder.ResourceSyncer{} - iamClient, err := c.iamClient(ctx) - if err == nil { - rs = append(rs, iamUserBuilder(iamClient), iamRoleBuilder(iamClient), iamGroupBuilder(iamClient)) +func (c *AWS) SetupClients(ctx context.Context) error { + globalCallingConfig, err := c.getCallingConfig(ctx, c.globalRegion) + if err != nil { + return err } + + c.iamClient = iam.NewFromConfig(globalCallingConfig) + + // The other clients are only needed if sso or org syncing is enabled if !c.ssoEnabled && !c.orgsEnabled { - return rs + return nil } - ix, err := c.getIdentityInstance(ctx) - if err != nil { - l.Error("getIdentityInstance error", zap.Error(err)) + if c.orgsEnabled { + c.orgClient = awsOrgs.NewFromConfig(globalCallingConfig) } - ssoAdminClient, err := c.ssoAdminClient(ctx) + + ssoCallingConfig, err := c.getCallingConfig(ctx, c.ssoRegion) if err != nil { - l.Error("ssoAdminClient error", zap.Error(err)) + return err } - identityStoreClient, err := c.identityStoreClient(ctx) + c.identityStoreClient = awsIdentityStore.NewFromConfig(ssoCallingConfig) + c.ssoAdminClient = awsSsoAdmin.NewFromConfig(ssoCallingConfig) + + identityInstance, err := c.getIdentityInstance(ctx, c.ssoAdminClient) if err != nil { - l.Error("identityStoreClient error", zap.Error(err)) + return err } + c.identityInstance = identityInstance + if c.ssoEnabled { - scimClient, err := c.ssoSCIMClient(ctx) + scimClient, err := c.getSSOSCIMClient(ctx) if err != nil { - l.Error("scimClient error", zap.Error(err)) + return err } - rs = append(rs, ssoUserBuilder(c.ssoRegion, ssoAdminClient, identityStoreClient, ix, scimClient)) - rs = append(rs, ssoGroupBuilder(c.ssoRegion, ssoAdminClient, identityStoreClient, ix)) + c.ssoSCIMClient = scimClient + } + + return nil +} + +func (c *AWS) ResourceSyncers(ctx context.Context) []connectorbuilder.ResourceSyncer { + l := ctxzap.Extract(ctx) + rs := []connectorbuilder.ResourceSyncer{ + iamUserBuilder(c.iamClient), + iamRoleBuilder(c.iamClient), + iamGroupBuilder(c.iamClient), + } + + if c.ssoEnabled { + l.Debug("ssoEnabled. creating ssoUserBuilder and ssoGroupBuilder") + rs = append(rs, ssoUserBuilder(c.ssoRegion, c.ssoAdminClient, c.identityStoreClient, c.identityInstance, c.ssoSCIMClient)) + rs = append(rs, ssoGroupBuilder(c.ssoRegion, c.ssoAdminClient, c.identityStoreClient, c.identityInstance)) } if c.orgsEnabled { - orgClient, err := c.orgClient(ctx) - if err == nil { - l.Error("accountBuilder error", zap.Error(err)) - } - rs = append(rs, accountBuilder(orgClient, c.roleARN, ssoAdminClient, ix, c.ssoRegion, identityStoreClient)) + l.Debug("orgsEnabled. creating accountBuilder") + rs = append(rs, accountBuilder(c.orgClient, c.roleARN, c.ssoAdminClient, c.identityInstance, c.ssoRegion, c.identityStoreClient)) } return rs } -func (c *AWS) getIdentityInstance(ctx context.Context) (*awsSsoAdminTypes.InstanceMetadata, error) { +func (c *AWS) getIdentityInstance(ctx context.Context, ssoClient *awsSsoAdmin.Client) (*awsSsoAdminTypes.InstanceMetadata, error) { c._identityInstancesCacheMtx.Lock() defer c._identityInstancesCacheMtx.Unlock() if c._identityInstancesCacheErr != nil { @@ -400,11 +408,6 @@ func (c *AWS) getIdentityInstance(ctx context.Context) (*awsSsoAdminTypes.Instan return c._identityInstancesCache[0], nil } - ssoClient, err := c.ssoAdminClient(ctx) - if err != nil { - return nil, err - } - paginator := awsSsoAdmin.NewListInstancesPaginator(ssoClient, &awsSsoAdmin.ListInstancesInput{}) for { resp, err := paginator.NextPage(ctx)