From 046c5e71ee8e9e363f9a6e4250f05c78a5577a02 Mon Sep 17 00:00:00 2001 From: CHIKAMATSU Naohiro Date: Thu, 11 Jan 2024 23:30:48 +0900 Subject: [PATCH] add CloudFormationStackLister method --- app/domain/model/cloudformation.go | 86 ++++++++++++++++++++++++++ app/domain/service/cloudformation.go | 21 +++++++ app/external/cloudformation.go | 90 ++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 app/domain/model/cloudformation.go create mode 100644 app/domain/service/cloudformation.go create mode 100644 app/external/cloudformation.go diff --git a/app/domain/model/cloudformation.go b/app/domain/model/cloudformation.go new file mode 100644 index 0000000..ed3e9e6 --- /dev/null +++ b/app/domain/model/cloudformation.go @@ -0,0 +1,86 @@ +package model + +import "time" + +const ( + // CloudFormationRetryMaxAttempts is the maximum number of retries for CloudFormation. + CloudFormationRetryMaxAttempts int = 2 +) + +// StackStatus is the status of a CloudFormation stack. +type StackStatus string + +// StackDriftInformationSummary contains information about whether the stack's +// actual configuration differs, or has drifted, from its expected configuration, +// as defined in the stack template and any values specified as template parameters. +// A stack is considered to have drifted if one or more of its resources have drifted. +type StackDriftInformationSummary struct { + // StackDriftStatus is status of the stack's actual configuration compared to its expected template + // configuration. + StackDriftStatus StackDriftStatus + // LastCheckTimestamp is most recent time when a drift detection operation was + // initiated on the stack, or any of its individual resources that support drift detection. + LastCheckTimestamp *time.Time +} + +// StackDriftStatus is the status of a stack's actual configuration compared to +// its expected template configuration. +type StackDriftStatus string + +const ( + // StackDriftStatusDrifted is the stack differs from its expected template configuration. + // A stack is considered to have drifted if one or more of its resources have drifted. + StackDriftStatusDrifted StackDriftStatus = "DRIFTED" + // StackDriftStatusInSync is the stack's actual configuration matches its expected template + // configuration. + StackDriftStatusInSync StackDriftStatus = "IN_SYNC" + // StackDriftStatusNotChecked is CloudFormation hasn't checked if the stack differs from its + // expected template configuration. + StackDriftStatusNotChecked StackDriftStatus = "NOT_CHECKED" + // StackDriftStatusUnknown is this value is reserved for future use. + StackDriftStatusUnknown StackDriftStatus = "UNKNOWN" +) + +// Values returns all known values for StackDriftStatus. Note that this can be +// expanded in the future, and so it is only as up to date as the client. The +// ordering of this slice is not guaranteed to be stable across updates. +func (StackDriftStatus) Values() []StackDriftStatus { + return []StackDriftStatus{ + "DRIFTED", + "IN_SYNC", + "UNKNOWN", + "NOT_CHECKED", + } +} + +// Stack is a CloudFormation stack. It is same as types.StackSummary. +type Stack struct { + // CreationTime is the time the stack was created. + CreationTime *time.Time + // StackName is the name associated with the stack. + StackName *string + // StackStatus is the current status of the stack. + StackStatus StackStatus + // DeletionTime is the time the stack was deleted. + DeletionTime *time.Time + // DriftInformation is summarizes information about whether a stack's actual + // configuration differs, or has drifted, from its expected configuration, + // as defined in the stack template and any values specified as template parameters. + DriftInformation *StackDriftInformationSummary + // LastUpdatedTime is the time the stack was last updated. + LastUpdatedTime *time.Time + // ParentID is used for nested stacks --stacks created as resources for + // another stack-- the stack ID of the direct parent of this stack. + // For the first level of nested stacks, the root stack is also the parent stack. + ParentID *string + // RootID id used for nested stacks --stacks created as resources for + // another stack--the stack ID of the top-level stack to which the nested stack + // ultimately belongs. + RootID *string + // StackID is unique stack identifier. + StackID *string + // StackStatusReason is Success/Failure message associated with the stack status. + StackStatusReason *string + // TemplateDescription is the template description of the template used to create the stack. + TemplateDescription *string +} diff --git a/app/domain/service/cloudformation.go b/app/domain/service/cloudformation.go new file mode 100644 index 0000000..eef86f5 --- /dev/null +++ b/app/domain/service/cloudformation.go @@ -0,0 +1,21 @@ +package service + +import ( + "context" + + "github.com/nao1215/rainbow/app/domain/model" +) + +// CloudFormationStackListerInput is the input of the CloudFormationStackLister method. +type CloudFormationStackListerInput struct{} + +// CloudFormationStackListerOutput is the output of the CloudFormationStackLister method. +type CloudFormationStackListerOutput struct { + // Stacks is a list of CloudFormation stacks. + Stacks []*model.Stack +} + +// CloudFormationStackLister is the interface that wraps the basic CloudFormationStackLister method. +type CloudFormationStackLister interface { + CloudFormationStackLister(ctx context.Context, input *CloudFormationStackListerInput) (*CloudFormationStackListerOutput, error) +} diff --git a/app/external/cloudformation.go b/app/external/cloudformation.go new file mode 100644 index 0000000..68b49b0 --- /dev/null +++ b/app/external/cloudformation.go @@ -0,0 +1,90 @@ +package external + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudformation" + "github.com/google/wire" + "github.com/nao1215/rainbow/app/domain/model" + "github.com/nao1215/rainbow/app/domain/service" +) + +// NewCloudFormationClient returns a new CloudFormation client. +func NewCloudFormationClient(cfg *model.AWSConfig) *cloudformation.Client { + return cloudformation.NewFromConfig( + *cfg.Config, + func(o *cloudformation.Options) { + o.RetryMaxAttempts = model.CloudFormationRetryMaxAttempts + o.RetryMode = aws.RetryModeStandard + }) +} + +// CloudFormationStackLister implements the CloudFormationStackLister interface. +type CloudFormationStackLister struct { + client *cloudformation.Client +} + +// CloudFormationStackListerSet is a set of CloudFormationStackLister. +// +//nolint:gochecknoglobals +var CloudFormationStackListerSet = wire.NewSet( + NewCloudFormationStackLister, + wire.Bind(new(service.CloudFormationStackLister), new(*CloudFormationStackLister)), +) + +var _ service.CloudFormationStackLister = (*CloudFormationStackLister)(nil) + +// NewCloudFormationStackLister returns a new CloudFormationStackLister. +func NewCloudFormationStackLister(client *cloudformation.Client) *CloudFormationStackLister { + return &CloudFormationStackLister{client: client} +} + +// CloudFormationStackLister returns a list of CloudFormation stacks. +func (l *CloudFormationStackLister) CloudFormationStackLister(ctx context.Context, input *service.CloudFormationStackListerInput) (*service.CloudFormationStackListerOutput, error) { + in := &cloudformation.ListStacksInput{} + stacks := make([]*model.Stack, 0, 100) + + for { + select { + case <-ctx.Done(): + return &service.CloudFormationStackListerOutput{ + Stacks: stacks, + }, ctx.Err() + default: + } + + out, err := l.client.ListStacks(ctx, in) + if err != nil { + return nil, err + } + + for _, stack := range out.StackSummaries { + stacks = append(stacks, &model.Stack{ + CreationTime: stack.CreationTime, + StackName: stack.StackName, + StackStatus: model.StackStatus(stack.StackStatus), + DeletionTime: stack.DeletionTime, + DriftInformation: &model.StackDriftInformationSummary{ + StackDriftStatus: model.StackDriftStatus(stack.DriftInformation.StackDriftStatus), + LastCheckTimestamp: stack.DriftInformation.LastCheckTimestamp, + }, + LastUpdatedTime: stack.LastUpdatedTime, + ParentID: stack.ParentId, + RootID: stack.RootId, + StackID: stack.StackId, + StackStatusReason: stack.StackStatusReason, + TemplateDescription: stack.TemplateDescription, + }) + } + + if out.NextToken == nil { + break + } + in.NextToken = out.NextToken + } + + return &service.CloudFormationStackListerOutput{ + Stacks: stacks, + }, nil +}