Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/v1beta2/awscluster_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go-v2/aws"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand Down
2 changes: 1 addition & 1 deletion api/v1beta2/awsmachine_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"strings"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go-v2/aws"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/component-base/featuregate/testing"
Expand Down
2 changes: 1 addition & 1 deletion api/v1beta2/awsmachinetemplate_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"context"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go-v2/aws"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
)
Expand Down
4 changes: 2 additions & 2 deletions api/v1beta2/network_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import (
"sort"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go/aws"
"k8s.io/utils/ptr"
)

Expand Down Expand Up @@ -670,7 +670,7 @@ func (s *SubnetSpec) IsEdgeWavelength() bool {
func (s *SubnetSpec) SetZoneInfo(zones []types.AvailabilityZone) error {
zoneInfo := func(zoneName string) *types.AvailabilityZone {
for _, zone := range zones {
if aws.StringValue(zone.ZoneName) == zoneName {
if aws.ToString(zone.ZoneName) == zoneName {
return &zone
}
}
Expand Down
2 changes: 1 addition & 1 deletion api/v1beta2/sshkeyname_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"context"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go-v2/aws"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
Expand Down
4 changes: 2 additions & 2 deletions cmd/clusterawsadm/ami/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import (
"strconv"
"strings"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go/aws"
"github.com/blang/semver"
"github.com/pkg/errors"

Expand Down Expand Up @@ -218,7 +218,7 @@ func getAllImages(ec2Client common.EC2API, ownerID string) (map[string][]types.I

imagesMap := make(map[string][]types.Image)
for _, image := range out.Images {
arr := strings.Split(aws.StringValue(image.Name), "-")
arr := strings.Split(aws.ToString(image.Name), "-")
if arr[len(arr)-2] == "00" {
arr = arr[:len(arr)-2]
} else {
Expand Down
8 changes: 4 additions & 4 deletions cmd/clusterawsadm/ami/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import (
"fmt"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go/aws"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

amiv1 "sigs.k8s.io/cluster-api-provider-aws/v2/cmd/clusterawsadm/api/ami/v1beta1"
Expand Down Expand Up @@ -103,7 +103,7 @@ func List(input ListInput) (*amiv1.AWSAMIList, error) {
if image == nil {
continue
}
creationTimestamp, err := time.Parse(time.RFC3339, aws.StringValue(image.CreationDate))
creationTimestamp, err := time.Parse(time.RFC3339, aws.ToString(image.CreationDate))
if err != nil {
return nil, err
}
Expand All @@ -114,13 +114,13 @@ func List(input ListInput) (*amiv1.AWSAMIList, error) {
APIVersion: amiv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: aws.StringValue(image.Name),
Name: aws.ToString(image.Name),
CreationTimestamp: metav1.NewTime(creationTimestamp),
},
Spec: amiv1.AWSAMISpec{
OS: os,
Region: region,
ImageID: aws.StringValue(image.ImageId),
ImageID: aws.ToString(image.ImageId),
KubernetesVersion: version,
},
})
Expand Down
132 changes: 92 additions & 40 deletions cmd/clusterawsadm/cloudformation/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,55 +19,80 @@ limitations under the License.
package cloudformation

import (
"context"
"fmt"
"os"
"text/tabwriter"
"time"

"github.com/aws/aws-sdk-go/aws"
cfn "github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface"
"github.com/aws/aws-sdk-go-v2/aws"
cfn "github.com/aws/aws-sdk-go-v2/service/cloudformation"
cfntypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
go_cfn "github.com/awslabs/goformation/v4/cloudformation"
"github.com/pkg/errors"
"k8s.io/klog/v2"
"k8s.io/utils/ptr"

"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/awserrors"
)

const (
// MaxWaitCreateUpdateDelete is the default maximum amount of time to wait for a cfn stack to complete.
MaxWaitCreateUpdateDelete = 30 * time.Minute
)

// CFNAPI defines the CFN API interface.
type CFNAPI interface {
CreateStack(ctx context.Context, params *cfn.CreateStackInput, optFns ...func(*cfn.Options)) (*cfn.CreateStackOutput, error)
DeleteStack(ctx context.Context, params *cfn.DeleteStackInput, optFns ...func(*cfn.Options)) (*cfn.DeleteStackOutput, error)
DescribeStacks(ctx context.Context, params *cfn.DescribeStacksInput, optFns ...func(*cfn.Options)) (*cfn.DescribeStacksOutput, error)
DescribeStackResources(ctx context.Context, params *cfn.DescribeStackResourcesInput, optFns ...func(*cfn.Options)) (*cfn.DescribeStackResourcesOutput, error)
UpdateStack(ctx context.Context, params *cfn.UpdateStackInput, optFns ...func(*cfn.Options)) (*cfn.UpdateStackOutput, error)

// Waiters for CFN stacks
WaitUntilStackCreateComplete(ctx context.Context, input *cfn.DescribeStacksInput, maxWait time.Duration) error
WaitUntilStackUpdateComplete(ctx context.Context, input *cfn.DescribeStacksInput, maxWait time.Duration) error
WaitUntilStackDeleteComplete(ctx context.Context, input *cfn.DescribeStacksInput, maxWait time.Duration) error
}

// CFNClient is a wrapper over cfn.Client for implementing custom methods of CFNAPI.
type CFNClient struct {
*cfn.Client
}

// Service holds a collection of interfaces.
// The interfaces are broken down like this to group functions together.
// One alternative is to have a large list of functions from the ec2 client.
type Service struct {
CFN cloudformationiface.CloudFormationAPI
CFN CFNAPI
}

// NewService returns a new service given the CloudFormation api client.
func NewService(i cloudformationiface.CloudFormationAPI) *Service {
func NewService(i CFNAPI) *Service {
return &Service{
CFN: i,
}
}

// ReconcileBootstrapStack creates or updates bootstrap CloudFormation.
func (s *Service) ReconcileBootstrapStack(stackName string, t go_cfn.Template, tags map[string]string) error {
func (s *Service) ReconcileBootstrapStack(ctx context.Context, stackName string, t go_cfn.Template, tags map[string]string) error {
yaml, err := t.YAML()
processedYaml := string(yaml)
if err != nil {
return errors.Wrap(err, "failed to generate AWS CloudFormation YAML")
}

stackTags := []*cfn.Tag{}
stackTags := []cfntypes.Tag{}
for k, v := range tags {
stackTags = append(stackTags, &cfn.Tag{
Key: ptr.To[string](k),
Value: ptr.To[string](v),
stackTags = append(stackTags, cfntypes.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
//nolint:nestif
if err := s.createStack(stackName, processedYaml, stackTags); err != nil {
if code, _ := awserrors.Code(errors.Cause(err)); code == "AlreadyExistsException" {
if err := s.createStack(ctx, stackName, processedYaml, stackTags); err != nil {
if code, _ := awserrors.Code(errors.Cause(err)); code == (&cfntypes.AlreadyExistsException{}).ErrorCode() {
klog.Infof("AWS Cloudformation stack %q already exists, updating", klog.KRef("", stackName))
updateErr := s.updateStack(stackName, processedYaml, stackTags)
updateErr := s.updateStack(ctx, stackName, processedYaml, stackTags)
if updateErr != nil {
code, ok := awserrors.Code(errors.Cause(updateErr))
message := awserrors.Message(errors.Cause(updateErr))
Expand All @@ -83,25 +108,25 @@ func (s *Service) ReconcileBootstrapStack(stackName string, t go_cfn.Template, t
}

// ReconcileBootstrapNoUpdate creates or updates bootstrap CloudFormation without updating the stack.
func (s *Service) ReconcileBootstrapNoUpdate(stackName string, t go_cfn.Template, tags map[string]string) error {
func (s *Service) ReconcileBootstrapNoUpdate(ctx context.Context, stackName string, t go_cfn.Template, tags map[string]string) error {
yaml, err := t.YAML()
processedYaml := string(yaml)
if err != nil {
return errors.Wrap(err, "failed to generate AWS CloudFormation YAML")
}

stackTags := []*cfn.Tag{}
stackTags := []cfntypes.Tag{}
for k, v := range tags {
stackTags = append(stackTags, &cfn.Tag{
stackTags = append(stackTags, cfntypes.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
//nolint:nestif
if err := s.createStack(stackName, processedYaml, stackTags); err != nil {
if code, _ := awserrors.Code(errors.Cause(err)); code == "AlreadyExistsException" {
if err := s.createStack(ctx, stackName, processedYaml, stackTags); err != nil {
if code, _ := awserrors.Code(errors.Cause(err)); code == (&cfntypes.AlreadyExistsException{}).ErrorCode() {
desInput := &cfn.DescribeStacksInput{StackName: aws.String(stackName)}
if err := s.CFN.WaitUntilStackCreateComplete(desInput); err != nil {
if err := s.CFN.WaitUntilStackCreateComplete(ctx, desInput, MaxWaitCreateUpdateDelete); err != nil {
return errors.Wrap(err, "failed to wait for AWS CloudFormation stack to be CreateComplete")
}
return nil
Expand All @@ -111,42 +136,42 @@ func (s *Service) ReconcileBootstrapNoUpdate(stackName string, t go_cfn.Template
return nil
}

func (s *Service) createStack(stackName, yaml string, tags []*cfn.Tag) error {
func (s *Service) createStack(ctx context.Context, stackName, yaml string, tags []cfntypes.Tag) error {
input := &cfn.CreateStackInput{
Capabilities: aws.StringSlice([]string{cfn.CapabilityCapabilityIam, cfn.CapabilityCapabilityNamedIam}),
Capabilities: []cfntypes.Capability{cfntypes.CapabilityCapabilityIam, cfntypes.CapabilityCapabilityNamedIam},
TemplateBody: aws.String(yaml),
StackName: aws.String(stackName),
Tags: tags,
}
klog.V(2).Infof("creating AWS CloudFormation stack %q", stackName)
if _, err := s.CFN.CreateStack(input); err != nil {
if _, err := s.CFN.CreateStack(ctx, input); err != nil {
return errors.Wrap(err, "failed to create AWS CloudFormation stack")
}

desInput := &cfn.DescribeStacksInput{StackName: aws.String(stackName)}
klog.V(2).Infof("waiting for stack %q to create", stackName)
if err := s.CFN.WaitUntilStackCreateComplete(desInput); err != nil {
if err := s.CFN.WaitUntilStackCreateComplete(ctx, desInput, MaxWaitCreateUpdateDelete); err != nil {
return errors.Wrap(err, "failed to wait for AWS CloudFormation stack to be CreateComplete")
}

klog.V(2).Infof("stack %q created", stackName)
return nil
}

func (s *Service) updateStack(stackName, yaml string, tags []*cfn.Tag) error {
func (s *Service) updateStack(ctx context.Context, stackName, yaml string, tags []cfntypes.Tag) error {
input := &cfn.UpdateStackInput{
Capabilities: aws.StringSlice([]string{cfn.CapabilityCapabilityIam, cfn.CapabilityCapabilityNamedIam}),
Capabilities: []cfntypes.Capability{cfntypes.CapabilityCapabilityIam, cfntypes.CapabilityCapabilityNamedIam},
TemplateBody: aws.String(yaml),
StackName: aws.String(stackName),
Tags: tags,
}
klog.V(2).Infof("updating AWS CloudFormation stack %q", stackName)
if _, err := s.CFN.UpdateStack(input); err != nil {
if _, err := s.CFN.UpdateStack(ctx, input); err != nil {
return errors.Wrap(err, "failed to update AWS CloudFormation stack")
}
desInput := &cfn.DescribeStacksInput{StackName: aws.String(stackName)}
klog.V(2).Infof("waiting for stack %q to update", stackName)
if err := s.CFN.WaitUntilStackUpdateComplete(desInput); err != nil {
if err := s.CFN.WaitUntilStackUpdateComplete(ctx, desInput, MaxWaitCreateUpdateDelete); err != nil {
return errors.Wrap(err, "failed to update AWS CloudFormation stack")
}

Expand All @@ -155,20 +180,20 @@ func (s *Service) updateStack(stackName, yaml string, tags []*cfn.Tag) error {
}

// DeleteStack deletes a cloudformation stack.
func (s *Service) DeleteStack(stackName string, retainResources []*string) error {
func (s *Service) DeleteStack(ctx context.Context, stackName string, retainResources []string) error {
klog.V(2).Infof("deleting AWS CloudFormation stack %q", stackName)
var err error
if retainResources == nil {
_, err = s.CFN.DeleteStack(&cfn.DeleteStackInput{StackName: aws.String(stackName)})
_, err = s.CFN.DeleteStack(ctx, &cfn.DeleteStackInput{StackName: aws.String(stackName)})
} else {
_, err = s.CFN.DeleteStack(&cfn.DeleteStackInput{StackName: aws.String(stackName), RetainResources: retainResources})
_, err = s.CFN.DeleteStack(ctx, &cfn.DeleteStackInput{StackName: aws.String(stackName), RetainResources: retainResources})
}
if err != nil {
return errors.Wrap(err, "failed to delete AWS CloudFormation stack")
}

klog.V(2).Infof("waiting for stack %q to delete", stackName)
if err := s.CFN.WaitUntilStackDeleteComplete(&cfn.DescribeStacksInput{StackName: aws.String(stackName)}); err != nil {
if err := s.CFN.WaitUntilStackDeleteComplete(ctx, &cfn.DescribeStacksInput{StackName: aws.String(stackName)}, MaxWaitCreateUpdateDelete); err != nil {
return errors.Wrap(err, "failed to delete AWS CloudFormation stack")
}

Expand All @@ -178,11 +203,11 @@ func (s *Service) DeleteStack(stackName string, retainResources []*string) error

// ShowStackResources prints out in tabular format the resources in the
// stack.
func (s *Service) ShowStackResources(stackName string) error {
func (s *Service) ShowStackResources(ctx context.Context, stackName string) error {
input := &cfn.DescribeStackResourcesInput{
StackName: aws.String(stackName),
}
out, err := s.CFN.DescribeStackResources(input)
out, err := s.CFN.DescribeStackResources(ctx, input)
if err != nil {
return errors.Wrap(err, "unable to describe stack resources")
}
Expand All @@ -195,15 +220,15 @@ func (s *Service) ShowStackResources(stackName string) error {

for _, r := range out.StackResources {
fmt.Fprintf(w, "%s\t%s\t%s\n",
aws.StringValue(r.ResourceType),
aws.StringValue(r.PhysicalResourceId),
aws.StringValue(r.ResourceStatus))
aws.ToString(r.ResourceType),
aws.ToString(r.PhysicalResourceId),
r.ResourceStatus)

switch aws.StringValue(r.ResourceStatus) {
case cfn.ResourceStatusCreateComplete, cfn.ResourceStatusUpdateComplete:
switch r.ResourceStatus {
case cfntypes.ResourceStatusCreateComplete, cfntypes.ResourceStatusUpdateComplete:
continue
default:
fmt.Println(aws.StringValue(r.ResourceStatusReason))
fmt.Println(aws.ToString(r.ResourceStatusReason))
}
}

Expand All @@ -213,3 +238,30 @@ func (s *Service) ShowStackResources(stackName string) error {

return nil
}

// WaitUntilStackCreateComplete is blocking function to wait until CFN Stack is successfully created.
func (c *CFNClient) WaitUntilStackCreateComplete(ctx context.Context, input *cfn.DescribeStacksInput, maxWait time.Duration) error {
waiter := cfn.NewStackCreateCompleteWaiter(c, func(o *cfn.StackCreateCompleteWaiterOptions) {
o.LogWaitAttempts = true
})

return waiter.Wait(ctx, input, maxWait)
}

// WaitUntilStackUpdateComplete is blocking function to wait until CFN Stack is successfully updated.
func (c *CFNClient) WaitUntilStackUpdateComplete(ctx context.Context, input *cfn.DescribeStacksInput, maxWait time.Duration) error {
waiter := cfn.NewStackUpdateCompleteWaiter(c, func(o *cfn.StackUpdateCompleteWaiterOptions) {
o.LogWaitAttempts = true
})

return waiter.Wait(ctx, input, maxWait)
}

// WaitUntilStackDeleteComplete is blocking function to wait until CFN Stack is successfully deleted.
func (c *CFNClient) WaitUntilStackDeleteComplete(ctx context.Context, input *cfn.DescribeStacksInput, maxWait time.Duration) error {
waiter := cfn.NewStackDeleteCompleteWaiter(c, func(o *cfn.StackDeleteCompleteWaiterOptions) {
o.LogWaitAttempts = true
})

return waiter.Wait(ctx, input, maxWait)
}
Loading