sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/eks/addons/procedures.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package addons
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  
    24  	"github.com/aws/aws-sdk-go/aws"
    25  	"github.com/aws/aws-sdk-go/service/eks"
    26  
    27  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/wait"
    28  )
    29  
    30  var (
    31  	// ErrNilAddon defines an error for when a nil addon is returned.
    32  	ErrNilAddon = errors.New("nil addon returned from create")
    33  	// ErrAddonNotFound defines an error for when an addon is not found.
    34  	ErrAddonNotFound = errors.New("addon not found")
    35  	// ErrAddonAlreadyExists defines an error for when an addon already exists.
    36  	ErrAddonAlreadyExists = errors.New("addon already exists")
    37  )
    38  
    39  // DeleteAddonProcedure is a procedure that will delete an EKS addon.
    40  type DeleteAddonProcedure struct {
    41  	plan *plan
    42  	name string
    43  }
    44  
    45  // Do implements the logic for the procedure.
    46  func (p *DeleteAddonProcedure) Do(ctx context.Context) error {
    47  	input := &eks.DeleteAddonInput{
    48  		AddonName:   aws.String(p.name),
    49  		ClusterName: aws.String(p.plan.clusterName),
    50  	}
    51  
    52  	if _, err := p.plan.eksClient.DeleteAddon(input); err != nil {
    53  		return fmt.Errorf("deleting eks addon %s: %w", p.name, err)
    54  	}
    55  
    56  	return nil
    57  }
    58  
    59  // Name is the name of the procedure.
    60  func (p *DeleteAddonProcedure) Name() string {
    61  	return "addon_delete"
    62  }
    63  
    64  // UpdateAddonProcedure is a procedure that will update an EKS addon.
    65  type UpdateAddonProcedure struct {
    66  	plan *plan
    67  	name string
    68  }
    69  
    70  // Do implements the logic for the procedure.
    71  func (p *UpdateAddonProcedure) Do(ctx context.Context) error {
    72  	desired := p.plan.getDesired(p.name)
    73  
    74  	if desired == nil {
    75  		return fmt.Errorf("getting desired addon %s: %w", p.name, ErrAddonNotFound)
    76  	}
    77  
    78  	input := &eks.UpdateAddonInput{
    79  		AddonName:             desired.Name,
    80  		AddonVersion:          desired.Version,
    81  		ClusterName:           &p.plan.clusterName,
    82  		ResolveConflicts:      desired.ResolveConflict,
    83  		ServiceAccountRoleArn: desired.ServiceAccountRoleARN,
    84  	}
    85  
    86  	if _, err := p.plan.eksClient.UpdateAddon(input); err != nil {
    87  		return fmt.Errorf("updating eks addon %s: %w", p.name, err)
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  // Name is the name of the procedure.
    94  func (p *UpdateAddonProcedure) Name() string {
    95  	return "addon_update"
    96  }
    97  
    98  // UpdateAddonTagsProcedure is a procedure that will update an EKS addon tags.
    99  type UpdateAddonTagsProcedure struct {
   100  	plan *plan
   101  	name string
   102  }
   103  
   104  // Do implements the logic for the procedure.
   105  func (p *UpdateAddonTagsProcedure) Do(ctx context.Context) error {
   106  	desired := p.plan.getDesired(p.name)
   107  	installed := p.plan.getInstalled(p.name)
   108  
   109  	if desired == nil {
   110  		return fmt.Errorf("getting desired addon %s: %w", p.name, ErrAddonNotFound)
   111  	}
   112  	if installed == nil {
   113  		return fmt.Errorf("getting installed addon %s: %w", p.name, ErrAddonNotFound)
   114  	}
   115  
   116  	input := &eks.TagResourceInput{
   117  		ResourceArn: installed.ARN,
   118  		Tags:        convertTags(desired.Tags),
   119  	}
   120  
   121  	if _, err := p.plan.eksClient.TagResource(input); err != nil {
   122  		return fmt.Errorf("updating eks addon tags %s: %w", p.name, err)
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  // Name is the name of the procedure.
   129  func (p *UpdateAddonTagsProcedure) Name() string {
   130  	return "addon_tags_update"
   131  }
   132  
   133  // CreateAddonProcedure is a procedure that will create an EKS addon for a cluster.
   134  type CreateAddonProcedure struct {
   135  	plan *plan
   136  	name string
   137  }
   138  
   139  // Do implements the logic for the procedure.
   140  func (p *CreateAddonProcedure) Do(ctx context.Context) error {
   141  	desired := p.plan.getDesired(p.name)
   142  	if desired == nil {
   143  		return fmt.Errorf("getting desired addon %s: %w", p.name, ErrAddonNotFound)
   144  	}
   145  
   146  	input := &eks.CreateAddonInput{
   147  		AddonName:             desired.Name,
   148  		AddonVersion:          desired.Version,
   149  		ClusterName:           &p.plan.clusterName,
   150  		ServiceAccountRoleArn: desired.ServiceAccountRoleARN,
   151  		ResolveConflicts:      desired.ResolveConflict,
   152  		Tags:                  convertTags(desired.Tags),
   153  	}
   154  
   155  	output, err := p.plan.eksClient.CreateAddon(input)
   156  	if err != nil {
   157  		return fmt.Errorf("creating eks addon %s: %w", p.name, err)
   158  	}
   159  
   160  	if output.Addon == nil {
   161  		return ErrNilAddon
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  // Name is the name of the procedure.
   168  func (p *CreateAddonProcedure) Name() string {
   169  	return "addon_create"
   170  }
   171  
   172  // WaitAddonActiveProcedure is a procedure that will wait for an EKS addon
   173  // to be active in a cluster. Abd optionally include the degraded state.
   174  // Note: addons may be degraded until there are worker nodes.
   175  type WaitAddonActiveProcedure struct {
   176  	plan            *plan
   177  	name            string
   178  	includeDegraded bool
   179  }
   180  
   181  // Do implements the logic for the procedure.
   182  func (p *WaitAddonActiveProcedure) Do(ctx context.Context) error {
   183  	input := &eks.DescribeAddonInput{
   184  		AddonName:   aws.String(p.name),
   185  		ClusterName: aws.String(p.plan.clusterName),
   186  	}
   187  
   188  	if err := wait.WaitForWithRetryable(wait.NewBackoff(), func() (bool, error) {
   189  		out, describeErr := p.plan.eksClient.DescribeAddon(input)
   190  		if describeErr != nil {
   191  			return false, describeErr
   192  		}
   193  
   194  		if *out.Addon.Status == eks.AddonStatusActive {
   195  			return true, nil
   196  		}
   197  
   198  		if p.includeDegraded && *out.Addon.Status == eks.AddonStatusDegraded {
   199  			return true, nil
   200  		}
   201  
   202  		return false, nil
   203  	}); err != nil {
   204  		return fmt.Errorf("failed waiting for addon %s to be ready: %w", p.name, err)
   205  	}
   206  
   207  	return nil
   208  }
   209  
   210  // Name is the name of the procedure.
   211  func (p *WaitAddonActiveProcedure) Name() string {
   212  	return "addon_wait_active"
   213  }
   214  
   215  // WaitAddonDeleteProcedure is a procedure that will wait for an EKS addon
   216  // to be deleted from a cluster.
   217  type WaitAddonDeleteProcedure struct {
   218  	plan *plan
   219  	name string
   220  }
   221  
   222  // Do implements the logic for the procedure.
   223  func (p *WaitAddonDeleteProcedure) Do(ctx context.Context) error {
   224  	input := &eks.DescribeAddonInput{
   225  		AddonName:   aws.String(p.name),
   226  		ClusterName: aws.String(p.plan.clusterName),
   227  	}
   228  
   229  	if err := p.plan.eksClient.WaitUntilAddonDeleted(input); err != nil {
   230  		return fmt.Errorf("waiting for addon %s to be deleted: %w", p.name, err)
   231  	}
   232  
   233  	return nil
   234  }
   235  
   236  // Name is the name of the procedure.
   237  func (p *WaitAddonDeleteProcedure) Name() string {
   238  	return "addon_wait_delete"
   239  }