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

     1  /*
     2  Copyright 2020 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 eks
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net"
    23  	"time"
    24  
    25  	"github.com/aws/aws-sdk-go/aws"
    26  	"github.com/aws/aws-sdk-go/aws/awserr"
    27  	"github.com/aws/aws-sdk-go/aws/request"
    28  	"github.com/aws/aws-sdk-go/service/eks"
    29  	"github.com/pkg/errors"
    30  	"k8s.io/apimachinery/pkg/util/sets"
    31  	"k8s.io/apimachinery/pkg/util/version"
    32  
    33  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    34  	ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1"
    35  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud"
    36  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/awserrors"
    37  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/wait"
    38  	"sigs.k8s.io/cluster-api-provider-aws/pkg/internal/cidr"
    39  	"sigs.k8s.io/cluster-api-provider-aws/pkg/internal/cmp"
    40  	"sigs.k8s.io/cluster-api-provider-aws/pkg/internal/tristate"
    41  	"sigs.k8s.io/cluster-api-provider-aws/pkg/record"
    42  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    43  	"sigs.k8s.io/cluster-api/util/conditions"
    44  )
    45  
    46  func (s *Service) reconcileCluster(ctx context.Context) error {
    47  	s.scope.V(2).Info("Reconciling EKS cluster")
    48  
    49  	eksClusterName := s.scope.KubernetesClusterName()
    50  
    51  	cluster, err := s.describeEKSCluster(eksClusterName)
    52  	if err != nil {
    53  		return errors.Wrap(err, "failed to describe eks clusters")
    54  	}
    55  
    56  	if cluster == nil {
    57  		cluster, err = s.createCluster(eksClusterName)
    58  		if err != nil {
    59  			return errors.Wrap(err, "failed to create cluster")
    60  		}
    61  	} else {
    62  		tagKey := infrav1.ClusterAWSCloudProviderTagKey(s.scope.KubernetesClusterName())
    63  		ownedTag := cluster.Tags[tagKey]
    64  		// Prior to https://github.com/kubernetes-sigs/cluster-api-provider-aws/pull/3573,
    65  		// Clusters were tagged using s.scope.Name()
    66  		// To support upgrading older clusters, check for both tags
    67  		oldTagKey := infrav1.ClusterAWSCloudProviderTagKey(s.scope.Name())
    68  		oldOwnedTag := cluster.Tags[oldTagKey]
    69  
    70  		if ownedTag == nil && oldOwnedTag == nil {
    71  			return fmt.Errorf("EKS cluster resource %q must have a tag with key %q or %q", eksClusterName, oldTagKey, tagKey)
    72  		}
    73  
    74  		s.scope.V(2).Info("Found owned EKS cluster in AWS", "cluster-name", eksClusterName)
    75  	}
    76  
    77  	if err := s.setStatus(cluster); err != nil {
    78  		return errors.Wrap(err, "failed to set status")
    79  	}
    80  
    81  	// Wait for our cluster to be ready if necessary
    82  	switch *cluster.Status {
    83  	case eks.ClusterStatusUpdating, eks.ClusterStatusCreating:
    84  		cluster, err = s.waitForClusterActive()
    85  	default:
    86  		break
    87  	}
    88  	if err != nil {
    89  		return errors.Wrap(err, "failed to wait for cluster to be active")
    90  	}
    91  
    92  	if !s.scope.ControlPlane.Status.Ready {
    93  		return nil
    94  	}
    95  
    96  	s.scope.V(2).Info("EKS Control Plane active", "endpoint", *cluster.Endpoint)
    97  
    98  	s.scope.ControlPlane.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
    99  		Host: *cluster.Endpoint,
   100  		Port: 443,
   101  	}
   102  
   103  	if err := s.reconcileSecurityGroups(cluster); err != nil {
   104  		return errors.Wrap(err, "failed reconciling security groups")
   105  	}
   106  
   107  	if err := s.reconcileKubeconfig(ctx, cluster); err != nil {
   108  		return errors.Wrap(err, "failed reconciling kubeconfig")
   109  	}
   110  
   111  	if err := s.reconcileAdditionalKubeconfigs(ctx, cluster); err != nil {
   112  		return errors.Wrap(err, "failed reconciling additional kubeconfigs")
   113  	}
   114  
   115  	if err := s.reconcileClusterVersion(cluster); err != nil {
   116  		return errors.Wrap(err, "failed reconciling cluster version")
   117  	}
   118  
   119  	if err := s.reconcileClusterConfig(cluster); err != nil {
   120  		return errors.Wrap(err, "failed reconciling cluster config")
   121  	}
   122  
   123  	if err := s.reconcileEKSEncryptionConfig(cluster.EncryptionConfig); err != nil {
   124  		return errors.Wrap(err, "failed reconciling eks encryption config")
   125  	}
   126  
   127  	if err := s.reconcileTags(cluster); err != nil {
   128  		return errors.Wrap(err, "failed updating cluster tags")
   129  	}
   130  
   131  	if err := s.reconcileOIDCProvider(cluster); err != nil {
   132  		return errors.Wrap(err, "failed reconciling OIDC provider for cluster")
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  func (s *Service) setStatus(cluster *eks.Cluster) error {
   139  	switch *cluster.Status {
   140  	case eks.ClusterStatusDeleting:
   141  		s.scope.ControlPlane.Status.Ready = false
   142  	case eks.ClusterStatusFailed:
   143  		s.scope.ControlPlane.Status.Ready = false
   144  		// TODO FailureReason
   145  		failureMsg := fmt.Sprintf("EKS cluster in unexpected %s state", *cluster.Status)
   146  		s.scope.ControlPlane.Status.FailureMessage = &failureMsg
   147  	case eks.ClusterStatusActive:
   148  		s.scope.ControlPlane.Status.Ready = true
   149  		s.scope.ControlPlane.Status.FailureMessage = nil
   150  		if conditions.IsTrue(s.scope.ControlPlane, ekscontrolplanev1.EKSControlPlaneCreatingCondition) {
   151  			record.Eventf(s.scope.ControlPlane, "SuccessfulCreateEKSControlPlane", "Created new EKS control plane %s", s.scope.KubernetesClusterName())
   152  			conditions.MarkFalse(s.scope.ControlPlane, ekscontrolplanev1.EKSControlPlaneCreatingCondition, "created", clusterv1.ConditionSeverityInfo, "")
   153  		}
   154  		if conditions.IsTrue(s.scope.ControlPlane, ekscontrolplanev1.EKSControlPlaneUpdatingCondition) {
   155  			conditions.MarkFalse(s.scope.ControlPlane, ekscontrolplanev1.EKSControlPlaneUpdatingCondition, "updated", clusterv1.ConditionSeverityInfo, "")
   156  			record.Eventf(s.scope.ControlPlane, "SuccessfulUpdateEKSControlPlane", "Updated EKS control plane %s", s.scope.KubernetesClusterName())
   157  		}
   158  		// TODO FailureReason
   159  	case eks.ClusterStatusCreating:
   160  		s.scope.ControlPlane.Status.Ready = false
   161  	case eks.ClusterStatusUpdating:
   162  		s.scope.ControlPlane.Status.Ready = true
   163  	default:
   164  		return errors.Errorf("unexpected EKS cluster status %s", *cluster.Status)
   165  	}
   166  	if err := s.scope.PatchObject(); err != nil {
   167  		return errors.Wrap(err, "failed to update control plane")
   168  	}
   169  	return nil
   170  }
   171  
   172  // deleteCluster deletes an EKS cluster.
   173  func (s *Service) deleteCluster() error {
   174  	eksClusterName := s.scope.KubernetesClusterName()
   175  
   176  	if eksClusterName == "" {
   177  		s.scope.V(2).Info("no EKS cluster name, skipping EKS cluster deletion")
   178  		return nil
   179  	}
   180  
   181  	cluster, err := s.describeEKSCluster(eksClusterName)
   182  	if err != nil {
   183  		if awserrors.IsNotFound(err) {
   184  			s.scope.V(4).Info("eks cluster does not exist")
   185  			return nil
   186  		}
   187  		return errors.Wrap(err, "unable to describe eks cluster")
   188  	}
   189  	if cluster == nil {
   190  		return nil
   191  	}
   192  
   193  	err = s.deleteClusterAndWait(cluster)
   194  	if err != nil {
   195  		record.Warnf(s.scope.ControlPlane, "FailedDeleteEKSCluster", "Failed to delete EKS cluster %s: %v", s.scope.KubernetesClusterName(), err)
   196  		return errors.Wrap(err, "unable to delete EKS cluster")
   197  	}
   198  	record.Eventf(s.scope.ControlPlane, "SuccessfulDeleteEKSCluster", "Deleted EKS Cluster %s", s.scope.KubernetesClusterName())
   199  
   200  	return nil
   201  }
   202  
   203  func (s *Service) deleteClusterAndWait(cluster *eks.Cluster) error {
   204  	s.scope.Info("Deleting EKS cluster", "cluster-name", s.scope.KubernetesClusterName())
   205  
   206  	input := &eks.DeleteClusterInput{
   207  		Name: cluster.Name,
   208  	}
   209  	_, err := s.EKSClient.DeleteCluster(input)
   210  	if err != nil {
   211  		return errors.Wrapf(err, "failed to request delete of eks cluster %s", *cluster.Name)
   212  	}
   213  
   214  	waitInput := &eks.DescribeClusterInput{
   215  		Name: cluster.Name,
   216  	}
   217  
   218  	err = s.EKSClient.WaitUntilClusterDeleted(waitInput)
   219  	if err != nil {
   220  		return errors.Wrapf(err, "failed waiting for eks cluster %s to delete", *cluster.Name)
   221  	}
   222  
   223  	return nil
   224  }
   225  
   226  func makeEksEncryptionConfigs(encryptionConfig *ekscontrolplanev1.EncryptionConfig) []*eks.EncryptionConfig {
   227  	cfg := []*eks.EncryptionConfig{}
   228  
   229  	if encryptionConfig == nil {
   230  		return cfg
   231  	}
   232  	// TODO: change EncryptionConfig so that provider and resources are required  if encruptionConfig is specified
   233  	if encryptionConfig.Provider == nil || len(*encryptionConfig.Provider) == 0 {
   234  		return cfg
   235  	}
   236  	if len(encryptionConfig.Resources) == 0 {
   237  		return cfg
   238  	}
   239  
   240  	return append(cfg, &eks.EncryptionConfig{
   241  		Provider: &eks.Provider{
   242  			KeyArn: encryptionConfig.Provider,
   243  		},
   244  		Resources: encryptionConfig.Resources,
   245  	})
   246  }
   247  
   248  func makeKubernetesNetworkConfig(serviceCidrs *clusterv1.NetworkRanges) (*eks.KubernetesNetworkConfigRequest, error) {
   249  	if serviceCidrs == nil || len(serviceCidrs.CIDRBlocks) == 0 {
   250  		return nil, nil
   251  	}
   252  
   253  	ipv4cidrs, err := cidr.GetIPv4Cidrs(serviceCidrs.CIDRBlocks)
   254  	if err != nil {
   255  		return nil, fmt.Errorf("filtering service cidr blocks to IPv4: %w", err)
   256  	}
   257  
   258  	if len(ipv4cidrs) == 0 {
   259  		return nil, nil
   260  	}
   261  
   262  	return &eks.KubernetesNetworkConfigRequest{
   263  		ServiceIpv4Cidr: &ipv4cidrs[0],
   264  	}, nil
   265  }
   266  
   267  func makeVpcConfig(subnets infrav1.Subnets, endpointAccess ekscontrolplanev1.EndpointAccess, securityGroups map[infrav1.SecurityGroupRole]infrav1.SecurityGroup) (*eks.VpcConfigRequest, error) {
   268  	// TODO: Do we need to just add the private subnets?
   269  	if len(subnets) < 2 {
   270  		return nil, awserrors.NewFailedDependency("at least 2 subnets is required")
   271  	}
   272  
   273  	if zones := subnets.GetUniqueZones(); len(zones) < 2 {
   274  		return nil, awserrors.NewFailedDependency("subnets in at least 2 different az's are required")
   275  	}
   276  
   277  	subnetIds := make([]*string, 0)
   278  	for i := range subnets {
   279  		subnet := subnets[i]
   280  		subnetIds = append(subnetIds, &subnet.ID)
   281  	}
   282  
   283  	cidrs := make([]*string, 0)
   284  	for _, cidr := range endpointAccess.PublicCIDRs {
   285  		_, ipNet, err := net.ParseCIDR(*cidr)
   286  		if err != nil {
   287  			return nil, errors.Wrap(err, "couldn't parse PublicCIDRs")
   288  		}
   289  		parsedCIDR := ipNet.String()
   290  		cidrs = append(cidrs, &parsedCIDR)
   291  	}
   292  
   293  	vpcConfig := &eks.VpcConfigRequest{
   294  		EndpointPublicAccess:  endpointAccess.Public,
   295  		EndpointPrivateAccess: endpointAccess.Private,
   296  		SubnetIds:             subnetIds,
   297  	}
   298  
   299  	if len(cidrs) > 0 {
   300  		vpcConfig.PublicAccessCidrs = cidrs
   301  	}
   302  	sg, ok := securityGroups[infrav1.SecurityGroupEKSNodeAdditional]
   303  	if ok {
   304  		vpcConfig.SecurityGroupIds = append(vpcConfig.SecurityGroupIds, &sg.ID)
   305  	}
   306  	return vpcConfig, nil
   307  }
   308  
   309  func makeEksLogging(loggingSpec *ekscontrolplanev1.ControlPlaneLoggingSpec) *eks.Logging {
   310  	if loggingSpec == nil {
   311  		return nil
   312  	}
   313  	on := true
   314  	off := false
   315  	var enabledTypes []string
   316  	var disabledTypes []string
   317  
   318  	appendToTypes := func(logType string, field bool) {
   319  		if field {
   320  			enabledTypes = append(enabledTypes, logType)
   321  		} else {
   322  			disabledTypes = append(disabledTypes, logType)
   323  		}
   324  	}
   325  
   326  	appendToTypes(eks.LogTypeApi, loggingSpec.APIServer)
   327  	appendToTypes(eks.LogTypeAudit, loggingSpec.Audit)
   328  	appendToTypes(eks.LogTypeAuthenticator, loggingSpec.Authenticator)
   329  	appendToTypes(eks.LogTypeControllerManager, loggingSpec.ControllerManager)
   330  	appendToTypes(eks.LogTypeScheduler, loggingSpec.Scheduler)
   331  
   332  	var clusterLogging []*eks.LogSetup
   333  	if len(enabledTypes) > 0 {
   334  		enabled := eks.LogSetup{
   335  			Enabled: &on,
   336  			Types:   aws.StringSlice(enabledTypes),
   337  		}
   338  		clusterLogging = append(clusterLogging, &enabled)
   339  	}
   340  	if len(disabledTypes) > 0 {
   341  		disabled := eks.LogSetup{
   342  			Enabled: &off,
   343  			Types:   aws.StringSlice(disabledTypes),
   344  		}
   345  		clusterLogging = append(clusterLogging, &disabled)
   346  	}
   347  	if len(clusterLogging) > 0 {
   348  		return &eks.Logging{
   349  			ClusterLogging: clusterLogging,
   350  		}
   351  	}
   352  	return nil
   353  }
   354  
   355  func (s *Service) createCluster(eksClusterName string) (*eks.Cluster, error) {
   356  	logging := makeEksLogging(s.scope.ControlPlane.Spec.Logging)
   357  	encryptionConfigs := makeEksEncryptionConfigs(s.scope.ControlPlane.Spec.EncryptionConfig)
   358  	vpcConfig, err := makeVpcConfig(s.scope.Subnets(), s.scope.ControlPlane.Spec.EndpointAccess, s.scope.SecurityGroups())
   359  	if err != nil {
   360  		return nil, errors.Wrap(err, "couldn't create vpc config for cluster")
   361  	}
   362  	netConfig, err := makeKubernetesNetworkConfig(s.scope.ServiceCidrs())
   363  	if err != nil {
   364  		return nil, errors.Wrap(err, "couldn't create Kubernetes network config for cluster")
   365  	}
   366  
   367  	// Make sure to use the MachineScope here to get the merger of AWSCluster and AWSMachine tags
   368  	additionalTags := s.scope.AdditionalTags()
   369  
   370  	// Set the cloud provider tag
   371  	additionalTags[infrav1.ClusterAWSCloudProviderTagKey(s.scope.KubernetesClusterName())] = string(infrav1.ResourceLifecycleOwned)
   372  	tags := make(map[string]*string)
   373  	for k, v := range additionalTags {
   374  		tagValue := v
   375  		tags[k] = &tagValue
   376  	}
   377  
   378  	role, err := s.GetIAMRole(*s.scope.ControlPlane.Spec.RoleName)
   379  	if err != nil {
   380  		return nil, errors.Wrapf(err, "error getting control plane iam role: %s", *s.scope.ControlPlane.Spec.RoleName)
   381  	}
   382  
   383  	v := versionToEKS(parseEKSVersion(*s.scope.ControlPlane.Spec.Version))
   384  	input := &eks.CreateClusterInput{
   385  		Name:                    aws.String(eksClusterName),
   386  		Version:                 aws.String(v),
   387  		Logging:                 logging,
   388  		EncryptionConfig:        encryptionConfigs,
   389  		ResourcesVpcConfig:      vpcConfig,
   390  		RoleArn:                 role.Arn,
   391  		Tags:                    tags,
   392  		KubernetesNetworkConfig: netConfig,
   393  	}
   394  
   395  	var out *eks.CreateClusterOutput
   396  	if err := wait.WaitForWithRetryable(wait.NewBackoff(), func() (bool, error) {
   397  		if out, err = s.EKSClient.CreateCluster(input); err != nil {
   398  			if aerr, ok := err.(awserr.Error); ok {
   399  				return false, aerr
   400  			}
   401  			return false, err
   402  		}
   403  		conditions.MarkTrue(s.scope.ControlPlane, ekscontrolplanev1.EKSControlPlaneCreatingCondition)
   404  		record.Eventf(s.scope.ControlPlane, "InitiatedCreateEKSControlPlane", "Initiated creation of a new EKS control plane %s", s.scope.KubernetesClusterName())
   405  		return true, nil
   406  	}, awserrors.ResourceNotFound); err != nil { // TODO: change the error that can be retried
   407  		record.Warnf(s.scope.ControlPlane, "FailedCreateEKSControlPlane", "Failed to initiate creation of a new EKS control plane: %v", err)
   408  		return nil, errors.Wrapf(err, "failed to create EKS cluster")
   409  	}
   410  
   411  	s.scope.Info("Created EKS cluster in AWS", "cluster-name", eksClusterName)
   412  	return out.Cluster, nil
   413  }
   414  
   415  func (s *Service) waitForClusterActive() (*eks.Cluster, error) {
   416  	eksClusterName := s.scope.KubernetesClusterName()
   417  	req := eks.DescribeClusterInput{
   418  		Name: aws.String(eksClusterName),
   419  	}
   420  	if err := s.EKSClient.WaitUntilClusterActive(&req); err != nil {
   421  		return nil, errors.Wrapf(err, "failed to wait for eks control plane %q", *req.Name)
   422  	}
   423  
   424  	s.scope.Info("EKS control plane is now active", "cluster-name", eksClusterName)
   425  
   426  	cluster, err := s.describeEKSCluster(eksClusterName)
   427  	if err != nil {
   428  		return nil, errors.Wrap(err, "failed to describe eks clusters")
   429  	}
   430  
   431  	if err := s.setStatus(cluster); err != nil {
   432  		return nil, errors.Wrap(err, "failed to set status")
   433  	}
   434  
   435  	return cluster, nil
   436  }
   437  
   438  func (s *Service) reconcileClusterConfig(cluster *eks.Cluster) error {
   439  	var needsUpdate bool
   440  	input := eks.UpdateClusterConfigInput{Name: aws.String(s.scope.KubernetesClusterName())}
   441  
   442  	if updateLogging := s.reconcileLogging(cluster.Logging); updateLogging != nil {
   443  		needsUpdate = true
   444  		input.Logging = updateLogging
   445  	}
   446  
   447  	updateVpcConfig, err := s.reconcileVpcConfig(cluster.ResourcesVpcConfig)
   448  	if err != nil {
   449  		return errors.Wrap(err, "couldn't create vpc config for cluster")
   450  	}
   451  	if updateVpcConfig != nil {
   452  		needsUpdate = true
   453  		input.ResourcesVpcConfig = updateVpcConfig
   454  	}
   455  
   456  	if needsUpdate {
   457  		if err := input.Validate(); err != nil {
   458  			return errors.Wrap(err, "created invalid UpdateClusterConfigInput")
   459  		}
   460  		if err := wait.WaitForWithRetryable(wait.NewBackoff(), func() (bool, error) {
   461  			if _, err := s.EKSClient.UpdateClusterConfig(&input); err != nil {
   462  				if aerr, ok := err.(awserr.Error); ok {
   463  					return false, aerr
   464  				}
   465  				return false, err
   466  			}
   467  			conditions.MarkTrue(s.scope.ControlPlane, ekscontrolplanev1.EKSControlPlaneUpdatingCondition)
   468  			record.Eventf(s.scope.ControlPlane, "InitiatedUpdateEKSControlPlane", "Initiated update of a new EKS control plane %s", s.scope.KubernetesClusterName())
   469  			return true, nil
   470  		}); err != nil {
   471  			record.Warnf(s.scope.ControlPlane, "FailedUpdateEKSControlPlane", "Failed to update the EKS control plane: %v", err)
   472  			return errors.Wrapf(err, "failed to update EKS cluster")
   473  		}
   474  	}
   475  	return nil
   476  }
   477  
   478  func (s *Service) reconcileLogging(logging *eks.Logging) *eks.Logging {
   479  	for _, logSetup := range logging.ClusterLogging {
   480  		for _, l := range logSetup.Types {
   481  			enabled := s.scope.ControlPlane.Spec.Logging.IsLogEnabled(*l)
   482  			if enabled != *logSetup.Enabled {
   483  				return makeEksLogging(s.scope.ControlPlane.Spec.Logging)
   484  			}
   485  		}
   486  	}
   487  	return nil
   488  }
   489  
   490  func publicAccessCIDRsEqual(as []*string, bs []*string) bool {
   491  	all := "0.0.0.0/0"
   492  	if len(as) == 0 {
   493  		as = []*string{&all}
   494  	}
   495  	if len(bs) == 0 {
   496  		bs = []*string{&all}
   497  	}
   498  	return sets.NewString(aws.StringValueSlice(as)...).Equal(
   499  		sets.NewString(aws.StringValueSlice(bs)...),
   500  	)
   501  }
   502  
   503  func (s *Service) reconcileVpcConfig(vpcConfig *eks.VpcConfigResponse) (*eks.VpcConfigRequest, error) {
   504  	endpointAccess := s.scope.ControlPlane.Spec.EndpointAccess
   505  	updatedVpcConfig, err := makeVpcConfig(s.scope.Subnets(), endpointAccess, s.scope.SecurityGroups())
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  	needsUpdate := !tristate.EqualWithDefault(false, vpcConfig.EndpointPrivateAccess, updatedVpcConfig.EndpointPrivateAccess) ||
   510  		!tristate.EqualWithDefault(true, vpcConfig.EndpointPublicAccess, updatedVpcConfig.EndpointPublicAccess) ||
   511  		!publicAccessCIDRsEqual(vpcConfig.PublicAccessCidrs, updatedVpcConfig.PublicAccessCidrs)
   512  	if needsUpdate {
   513  		return &eks.VpcConfigRequest{
   514  			EndpointPublicAccess:  updatedVpcConfig.EndpointPublicAccess,
   515  			EndpointPrivateAccess: updatedVpcConfig.EndpointPrivateAccess,
   516  			PublicAccessCidrs:     updatedVpcConfig.PublicAccessCidrs,
   517  		}, nil
   518  	}
   519  	return nil, nil
   520  }
   521  
   522  func (s *Service) reconcileEKSEncryptionConfig(currentClusterConfig []*eks.EncryptionConfig) error {
   523  	s.Info("reconciling encryption configuration")
   524  	if currentClusterConfig == nil {
   525  		currentClusterConfig = []*eks.EncryptionConfig{}
   526  	}
   527  
   528  	encryptionConfigs := s.scope.ControlPlane.Spec.EncryptionConfig
   529  	updatedEncryptionConfigs := makeEksEncryptionConfigs(encryptionConfigs)
   530  
   531  	if compareEncryptionConfig(currentClusterConfig, updatedEncryptionConfigs) {
   532  		s.V(2).Info("encryption configuration unchanged, no action")
   533  		return nil
   534  	}
   535  
   536  	if len(currentClusterConfig) == 0 && len(updatedEncryptionConfigs) > 0 {
   537  		s.V(2).Info("enabling encryption for eks cluster", "cluster", s.scope.KubernetesClusterName())
   538  		if err := s.updateEncryptionConfig(updatedEncryptionConfigs); err != nil {
   539  			record.Warnf(s.scope.ControlPlane, "FailedUpdateEKSControlPlane", "failed to update the EKS control plane encryption configuration: %v", err)
   540  			return errors.Wrapf(err, "failed to update EKS cluster")
   541  		}
   542  
   543  		return nil
   544  	}
   545  
   546  	record.Warnf(s.scope.ControlPlane, "FailedUpdateEKSControlPlane", "failed to update the EKS control plane: disabling EKS encryption is not allowed after it has been enabled")
   547  	return errors.Errorf("failed to update the EKS control plane: disabling EKS encryption is not allowed after it has been enabled")
   548  }
   549  
   550  func parseEKSVersion(raw string) *version.Version {
   551  	v := version.MustParseGeneric(raw)
   552  	return version.MustParseGeneric(fmt.Sprintf("%d.%d", v.Major(), v.Minor()))
   553  }
   554  
   555  func versionToEKS(v *version.Version) string {
   556  	return fmt.Sprintf("%d.%d", v.Major(), v.Minor())
   557  }
   558  
   559  func (s *Service) reconcileClusterVersion(cluster *eks.Cluster) error {
   560  	specVersion := parseEKSVersion(*s.scope.ControlPlane.Spec.Version)
   561  	clusterVersion := version.MustParseGeneric(*cluster.Version)
   562  
   563  	if clusterVersion.LessThan(specVersion) {
   564  		// NOTE: you can only upgrade increments of minor versions. If you want to upgrade 1.14 to 1.16 we
   565  		// need to go 1.14-> 1.15 and then 1.15 -> 1.16.
   566  		nextVersionString := versionToEKS(clusterVersion.WithMinor(clusterVersion.Minor() + 1))
   567  
   568  		input := &eks.UpdateClusterVersionInput{
   569  			Name:    aws.String(s.scope.KubernetesClusterName()),
   570  			Version: &nextVersionString,
   571  		}
   572  
   573  		if err := wait.WaitForWithRetryable(wait.NewBackoff(), func() (bool, error) {
   574  			if _, err := s.EKSClient.UpdateClusterVersion(input); err != nil {
   575  				if aerr, ok := err.(awserr.Error); ok {
   576  					return false, aerr
   577  				}
   578  				return false, err
   579  			}
   580  
   581  			// Wait until status transitions to UPDATING because there's a short
   582  			// window after UpdateClusterVersion returns where the cluster
   583  			// status is ACTIVE and the update would be tried again
   584  			if err := s.EKSClient.WaitUntilClusterUpdating(
   585  				&eks.DescribeClusterInput{Name: aws.String(s.scope.KubernetesClusterName())},
   586  				request.WithWaiterLogger(&awslog{s}),
   587  			); err != nil {
   588  				return false, err
   589  			}
   590  
   591  			conditions.MarkTrue(s.scope.ControlPlane, ekscontrolplanev1.EKSControlPlaneUpdatingCondition)
   592  			record.Eventf(s.scope.ControlPlane, "InitiatedUpdateEKSControlPlane", "Initiated update of EKS control plane %s to version %s", s.scope.KubernetesClusterName(), nextVersionString)
   593  
   594  			return true, nil
   595  		}); err != nil {
   596  			record.Warnf(s.scope.ControlPlane, "FailedUpdateEKSControlPlane", "failed to update the EKS control plane: %v", err)
   597  			return errors.Wrapf(err, "failed to update EKS cluster")
   598  		}
   599  	}
   600  	return nil
   601  }
   602  
   603  func (s *Service) describeEKSCluster(eksClusterName string) (*eks.Cluster, error) {
   604  	input := &eks.DescribeClusterInput{
   605  		Name: aws.String(eksClusterName),
   606  	}
   607  
   608  	out, err := s.EKSClient.DescribeCluster(input)
   609  	if err != nil {
   610  		if aerr, ok := err.(awserr.Error); ok {
   611  			switch aerr.Code() {
   612  			case eks.ErrCodeResourceNotFoundException:
   613  				return nil, nil
   614  			default:
   615  				return nil, errors.Wrap(err, "failed to describe cluster")
   616  			}
   617  		} else {
   618  			return nil, errors.Wrap(err, "failed to describe cluster")
   619  		}
   620  	}
   621  
   622  	return out.Cluster, nil
   623  }
   624  
   625  func (s *Service) updateEncryptionConfig(updatedEncryptionConfigs []*eks.EncryptionConfig) error {
   626  	input := &eks.AssociateEncryptionConfigInput{
   627  		ClusterName:      aws.String(s.scope.KubernetesClusterName()),
   628  		EncryptionConfig: updatedEncryptionConfigs,
   629  	}
   630  	if err := wait.WaitForWithRetryable(wait.NewBackoff(), func() (bool, error) {
   631  		if _, err := s.EKSClient.AssociateEncryptionConfig(input); err != nil {
   632  			if aerr, ok := err.(awserr.Error); ok {
   633  				return false, aerr
   634  			}
   635  			return false, err
   636  		}
   637  
   638  		// Wait until status transitions to UPDATING because there's a short
   639  		// window after UpdateClusterVersion returns where the cluster
   640  		// status is ACTIVE and the update would be tried again
   641  		if err := s.EKSClient.WaitUntilClusterUpdating(
   642  			&eks.DescribeClusterInput{Name: aws.String(s.scope.KubernetesClusterName())},
   643  			request.WithWaiterLogger(&awslog{s}),
   644  		); err != nil {
   645  			return false, err
   646  		}
   647  
   648  		conditions.MarkTrue(s.scope.ControlPlane, ekscontrolplanev1.EKSControlPlaneUpdatingCondition)
   649  		record.Eventf(s.scope.ControlPlane, "InitiatedUpdateEncryptionConfig", "Initiated update of encryption config in EKS control plane %s", s.scope.KubernetesClusterName())
   650  
   651  		return true, nil
   652  	}); err != nil {
   653  		return err
   654  	}
   655  	return nil
   656  }
   657  
   658  // An internal type to satisfy aws' log interface.
   659  type awslog struct {
   660  	cloud.Logger
   661  }
   662  
   663  func (a *awslog) Log(args ...interface{}) {
   664  	a.WithName("aws").Info(fmt.Sprintln(args...))
   665  }
   666  
   667  // WaitUntilClusterUpdating is adapted from aws-sdk-go/service/eks/waiters.go.
   668  func (c EKSClient) WaitUntilClusterUpdating(input *eks.DescribeClusterInput, opts ...request.WaiterOption) error {
   669  	ctx := aws.BackgroundContext()
   670  	statusPath := "cluster.status"
   671  	w := request.Waiter{
   672  		Name:        "WaitUntilClusterUpdating",
   673  		MaxAttempts: 40,
   674  		Delay:       request.ConstantWaiterDelay(30 * time.Second),
   675  		Acceptors: []request.WaiterAcceptor{
   676  			{
   677  				State:   request.FailureWaiterState,
   678  				Matcher: request.PathWaiterMatch, Argument: statusPath,
   679  				Expected: eks.ClusterStatusDeleting,
   680  			},
   681  			{
   682  				State:   request.FailureWaiterState,
   683  				Matcher: request.PathWaiterMatch, Argument: statusPath,
   684  				Expected: eks.ClusterStatusFailed,
   685  			},
   686  			{
   687  				State:   request.SuccessWaiterState,
   688  				Matcher: request.PathWaiterMatch, Argument: statusPath,
   689  				Expected: eks.ClusterStatusUpdating,
   690  			},
   691  		},
   692  		NewRequest: func(opts []request.Option) (*request.Request, error) {
   693  			var inCpy *eks.DescribeClusterInput
   694  			if input != nil {
   695  				tmp := *input
   696  				inCpy = &tmp
   697  			}
   698  			req, _ := c.DescribeClusterRequest(inCpy)
   699  			req.SetContext(ctx)
   700  			req.ApplyOptions(opts...)
   701  			return req, nil
   702  		},
   703  	}
   704  	w.ApplyOptions(opts...)
   705  
   706  	return w.WaitWithContext(ctx)
   707  }
   708  
   709  func compareEncryptionConfig(updatedEncryptionConfig, existingEncryptionConfig []*eks.EncryptionConfig) bool {
   710  	if len(updatedEncryptionConfig) != len(existingEncryptionConfig) {
   711  		return false
   712  	}
   713  	for index := range updatedEncryptionConfig {
   714  		encryptionConfig := updatedEncryptionConfig[index]
   715  
   716  		if getKeyArn(encryptionConfig) != getKeyArn(existingEncryptionConfig[index]) {
   717  			return false
   718  		}
   719  		if !cmp.Equals(encryptionConfig.Resources, existingEncryptionConfig[index].Resources) {
   720  			return false
   721  		}
   722  	}
   723  	return true
   724  }
   725  
   726  func getKeyArn(encryptionConfig *eks.EncryptionConfig) string {
   727  	if encryptionConfig.Provider != nil {
   728  		return aws.StringValue(encryptionConfig.Provider.KeyArn)
   729  	}
   730  	return ""
   731  }