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

     1  /*
     2  Copyright 2018 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 scope
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	awsclient "github.com/aws/aws-sdk-go/aws/client"
    24  	"github.com/go-logr/logr"
    25  	"github.com/pkg/errors"
    26  	"k8s.io/klog/v2/klogr"
    27  	"sigs.k8s.io/controller-runtime/pkg/client"
    28  
    29  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    30  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud"
    31  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/throttle"
    32  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    33  	"sigs.k8s.io/cluster-api/util/conditions"
    34  	"sigs.k8s.io/cluster-api/util/patch"
    35  )
    36  
    37  // ClusterScopeParams defines the input parameters used to create a new Scope.
    38  type ClusterScopeParams struct {
    39  	Client         client.Client
    40  	Logger         *logr.Logger
    41  	Cluster        *clusterv1.Cluster
    42  	AWSCluster     *infrav1.AWSCluster
    43  	ControllerName string
    44  	Endpoints      []ServiceEndpoint
    45  	Session        awsclient.ConfigProvider
    46  }
    47  
    48  // NewClusterScope creates a new Scope from the supplied parameters.
    49  // This is meant to be called for each reconcile iteration.
    50  func NewClusterScope(params ClusterScopeParams) (*ClusterScope, error) {
    51  	if params.Cluster == nil {
    52  		return nil, errors.New("failed to generate new scope from nil Cluster")
    53  	}
    54  	if params.AWSCluster == nil {
    55  		return nil, errors.New("failed to generate new scope from nil AWSCluster")
    56  	}
    57  
    58  	if params.Logger == nil {
    59  		log := klogr.New()
    60  		params.Logger = &log
    61  	}
    62  
    63  	clusterScope := &ClusterScope{
    64  		Logger:         *params.Logger,
    65  		client:         params.Client,
    66  		Cluster:        params.Cluster,
    67  		AWSCluster:     params.AWSCluster,
    68  		controllerName: params.ControllerName,
    69  	}
    70  
    71  	session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, clusterScope, params.AWSCluster.Spec.Region, params.Endpoints, *params.Logger)
    72  	if err != nil {
    73  		return nil, errors.Errorf("failed to create aws session: %v", err)
    74  	}
    75  
    76  	helper, err := patch.NewHelper(params.AWSCluster, params.Client)
    77  	if err != nil {
    78  		return nil, errors.Wrap(err, "failed to init patch helper")
    79  	}
    80  
    81  	clusterScope.patchHelper = helper
    82  	clusterScope.session = session
    83  	clusterScope.serviceLimiters = serviceLimiters
    84  
    85  	return clusterScope, nil
    86  }
    87  
    88  // ClusterScope defines the basic context for an actuator to operate upon.
    89  type ClusterScope struct {
    90  	logr.Logger
    91  	client      client.Client
    92  	patchHelper *patch.Helper
    93  
    94  	Cluster    *clusterv1.Cluster
    95  	AWSCluster *infrav1.AWSCluster
    96  
    97  	session         awsclient.ConfigProvider
    98  	serviceLimiters throttle.ServiceLimiters
    99  	controllerName  string
   100  }
   101  
   102  // Network returns the cluster network object.
   103  func (s *ClusterScope) Network() *infrav1.NetworkStatus {
   104  	return &s.AWSCluster.Status.Network
   105  }
   106  
   107  // VPC returns the cluster VPC.
   108  func (s *ClusterScope) VPC() *infrav1.VPCSpec {
   109  	return &s.AWSCluster.Spec.NetworkSpec.VPC
   110  }
   111  
   112  // Subnets returns the cluster subnets.
   113  func (s *ClusterScope) Subnets() infrav1.Subnets {
   114  	return s.AWSCluster.Spec.NetworkSpec.Subnets
   115  }
   116  
   117  // IdentityRef returns the cluster identityRef.
   118  func (s *ClusterScope) IdentityRef() *infrav1.AWSIdentityReference {
   119  	return s.AWSCluster.Spec.IdentityRef
   120  }
   121  
   122  // SetSubnets updates the clusters subnets.
   123  func (s *ClusterScope) SetSubnets(subnets infrav1.Subnets) {
   124  	s.AWSCluster.Spec.NetworkSpec.Subnets = subnets
   125  }
   126  
   127  // CNIIngressRules returns the CNI spec ingress rules.
   128  func (s *ClusterScope) CNIIngressRules() infrav1.CNIIngressRules {
   129  	if s.AWSCluster.Spec.NetworkSpec.CNI != nil {
   130  		return s.AWSCluster.Spec.NetworkSpec.CNI.CNIIngressRules
   131  	}
   132  	return infrav1.CNIIngressRules{}
   133  }
   134  
   135  // SecurityGroupOverrides returns the cluster security group overrides.
   136  func (s *ClusterScope) SecurityGroupOverrides() map[infrav1.SecurityGroupRole]string {
   137  	return s.AWSCluster.Spec.NetworkSpec.SecurityGroupOverrides
   138  }
   139  
   140  // SecurityGroups returns the cluster security groups as a map, it creates the map if empty.
   141  func (s *ClusterScope) SecurityGroups() map[infrav1.SecurityGroupRole]infrav1.SecurityGroup {
   142  	return s.AWSCluster.Status.Network.SecurityGroups
   143  }
   144  
   145  // SecondaryCidrBlock is currently unimplemented for non-managed clusters.
   146  func (s *ClusterScope) SecondaryCidrBlock() *string {
   147  	return nil
   148  }
   149  
   150  // Name returns the CAPI cluster name.
   151  func (s *ClusterScope) Name() string {
   152  	return s.Cluster.Name
   153  }
   154  
   155  // Namespace returns the cluster namespace.
   156  func (s *ClusterScope) Namespace() string {
   157  	return s.Cluster.Namespace
   158  }
   159  
   160  // InfraClusterName returns the AWS cluster name.
   161  func (s *ClusterScope) InfraClusterName() string {
   162  	return s.AWSCluster.Name
   163  }
   164  
   165  // Region returns the cluster region.
   166  func (s *ClusterScope) Region() string {
   167  	return s.AWSCluster.Spec.Region
   168  }
   169  
   170  // KubernetesClusterName is the name of the Kubernetes cluster. For the cluster
   171  // scope this is the same as the CAPI cluster name.
   172  func (s *ClusterScope) KubernetesClusterName() string {
   173  	return s.Cluster.Name
   174  }
   175  
   176  // ControlPlaneLoadBalancer returns the AWSLoadBalancerSpec.
   177  func (s *ClusterScope) ControlPlaneLoadBalancer() *infrav1.AWSLoadBalancerSpec {
   178  	return s.AWSCluster.Spec.ControlPlaneLoadBalancer
   179  }
   180  
   181  // ControlPlaneLoadBalancerScheme returns the Classic ELB scheme (public or internal facing).
   182  func (s *ClusterScope) ControlPlaneLoadBalancerScheme() infrav1.ClassicELBScheme {
   183  	if s.ControlPlaneLoadBalancer() != nil && s.ControlPlaneLoadBalancer().Scheme != nil {
   184  		return *s.ControlPlaneLoadBalancer().Scheme
   185  	}
   186  	return infrav1.ClassicELBSchemeInternetFacing
   187  }
   188  
   189  func (s *ClusterScope) ControlPlaneLoadBalancerName() *string {
   190  	if s.AWSCluster.Spec.ControlPlaneLoadBalancer != nil {
   191  		return s.AWSCluster.Spec.ControlPlaneLoadBalancer.Name
   192  	}
   193  	return nil
   194  }
   195  
   196  func (s *ClusterScope) ControlPlaneEndpoint() clusterv1.APIEndpoint {
   197  	return s.AWSCluster.Spec.ControlPlaneEndpoint
   198  }
   199  
   200  func (s *ClusterScope) Bucket() *infrav1.S3Bucket {
   201  	return s.AWSCluster.Spec.S3Bucket
   202  }
   203  
   204  // ControlPlaneConfigMapName returns the name of the ConfigMap used to
   205  // coordinate the bootstrapping of control plane nodes.
   206  func (s *ClusterScope) ControlPlaneConfigMapName() string {
   207  	return fmt.Sprintf("%s-controlplane", s.Cluster.UID)
   208  }
   209  
   210  // ListOptionsLabelSelector returns a ListOptions with a label selector for clusterName.
   211  func (s *ClusterScope) ListOptionsLabelSelector() client.ListOption {
   212  	return client.MatchingLabels(map[string]string{
   213  		clusterv1.ClusterLabelName: s.Cluster.Name,
   214  	})
   215  }
   216  
   217  // PatchObject persists the cluster configuration and status.
   218  func (s *ClusterScope) PatchObject() error {
   219  	// Always update the readyCondition by summarizing the state of other conditions.
   220  	// A step counter is added to represent progress during the provisioning process (instead we are hiding during the deletion process).
   221  	applicableConditions := []clusterv1.ConditionType{
   222  		infrav1.VpcReadyCondition,
   223  		infrav1.SubnetsReadyCondition,
   224  		infrav1.ClusterSecurityGroupsReadyCondition,
   225  		infrav1.LoadBalancerReadyCondition,
   226  	}
   227  
   228  	if s.VPC().IsManaged(s.Name()) {
   229  		applicableConditions = append(applicableConditions,
   230  			infrav1.InternetGatewayReadyCondition,
   231  			infrav1.NatGatewaysReadyCondition,
   232  			infrav1.RouteTablesReadyCondition)
   233  
   234  		if s.AWSCluster.Spec.Bastion.Enabled {
   235  			applicableConditions = append(applicableConditions, infrav1.BastionHostReadyCondition)
   236  		}
   237  	}
   238  
   239  	conditions.SetSummary(s.AWSCluster,
   240  		conditions.WithConditions(applicableConditions...),
   241  		conditions.WithStepCounterIf(s.AWSCluster.ObjectMeta.DeletionTimestamp.IsZero()),
   242  		conditions.WithStepCounter(),
   243  	)
   244  
   245  	return s.patchHelper.Patch(
   246  		context.TODO(),
   247  		s.AWSCluster,
   248  		patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{
   249  			clusterv1.ReadyCondition,
   250  			infrav1.VpcReadyCondition,
   251  			infrav1.SubnetsReadyCondition,
   252  			infrav1.InternetGatewayReadyCondition,
   253  			infrav1.NatGatewaysReadyCondition,
   254  			infrav1.RouteTablesReadyCondition,
   255  			infrav1.ClusterSecurityGroupsReadyCondition,
   256  			infrav1.BastionHostReadyCondition,
   257  			infrav1.LoadBalancerReadyCondition,
   258  			infrav1.PrincipalUsageAllowedCondition,
   259  		}})
   260  }
   261  
   262  // Close closes the current scope persisting the cluster configuration and status.
   263  func (s *ClusterScope) Close() error {
   264  	return s.PatchObject()
   265  }
   266  
   267  // AdditionalTags returns AdditionalTags from the scope's AWSCluster. The returned value will never be nil.
   268  func (s *ClusterScope) AdditionalTags() infrav1.Tags {
   269  	if s.AWSCluster.Spec.AdditionalTags == nil {
   270  		s.AWSCluster.Spec.AdditionalTags = infrav1.Tags{}
   271  	}
   272  
   273  	return s.AWSCluster.Spec.AdditionalTags.DeepCopy()
   274  }
   275  
   276  // APIServerPort returns the APIServerPort to use when creating the load balancer.
   277  func (s *ClusterScope) APIServerPort() int32 {
   278  	if s.Cluster.Spec.ClusterNetwork != nil && s.Cluster.Spec.ClusterNetwork.APIServerPort != nil {
   279  		return *s.Cluster.Spec.ClusterNetwork.APIServerPort
   280  	}
   281  	return 6443
   282  }
   283  
   284  // SetFailureDomain sets the infrastructure provider failure domain key to the spec given as input.
   285  func (s *ClusterScope) SetFailureDomain(id string, spec clusterv1.FailureDomainSpec) {
   286  	if s.AWSCluster.Status.FailureDomains == nil {
   287  		s.AWSCluster.Status.FailureDomains = make(clusterv1.FailureDomains)
   288  	}
   289  	s.AWSCluster.Status.FailureDomains[id] = spec
   290  }
   291  
   292  // InfraCluster returns the AWS infrastructure cluster or control plane object.
   293  func (s *ClusterScope) InfraCluster() cloud.ClusterObject {
   294  	return s.AWSCluster
   295  }
   296  
   297  // ClusterObj returns the cluster object.
   298  func (s *ClusterScope) ClusterObj() cloud.ClusterObject {
   299  	return s.Cluster
   300  }
   301  
   302  // Session returns the AWS SDK session. Used for creating clients.
   303  func (s *ClusterScope) Session() awsclient.ConfigProvider {
   304  	return s.session
   305  }
   306  
   307  // ServiceLimiter returns the AWS SDK session. Used for creating clients.
   308  func (s *ClusterScope) ServiceLimiter(service string) *throttle.ServiceLimiter {
   309  	if sl, ok := s.serviceLimiters[service]; ok {
   310  		return sl
   311  	}
   312  	return nil
   313  }
   314  
   315  // Bastion returns the bastion details.
   316  func (s *ClusterScope) Bastion() *infrav1.Bastion {
   317  	return &s.AWSCluster.Spec.Bastion
   318  }
   319  
   320  // SetBastionInstance sets the bastion instance in the status of the cluster.
   321  func (s *ClusterScope) SetBastionInstance(instance *infrav1.Instance) {
   322  	s.AWSCluster.Status.Bastion = instance
   323  }
   324  
   325  // SSHKeyName returns the SSH key name to use for instances.
   326  func (s *ClusterScope) SSHKeyName() *string {
   327  	return s.AWSCluster.Spec.SSHKeyName
   328  }
   329  
   330  // ControllerName returns the name of the controller that
   331  // created the ClusterScope.
   332  func (s *ClusterScope) ControllerName() string {
   333  	return s.controllerName
   334  }
   335  
   336  // ImageLookupFormat returns the format string to use when looking up AMIs.
   337  func (s *ClusterScope) ImageLookupFormat() string {
   338  	return s.AWSCluster.Spec.ImageLookupFormat
   339  }
   340  
   341  // ImageLookupOrg returns the organization name to use when looking up AMIs.
   342  func (s *ClusterScope) ImageLookupOrg() string {
   343  	return s.AWSCluster.Spec.ImageLookupOrg
   344  }
   345  
   346  // ImageLookupBaseOS returns the base operating system name to use when looking up AMIs.
   347  func (s *ClusterScope) ImageLookupBaseOS() string {
   348  	return s.AWSCluster.Spec.ImageLookupBaseOS
   349  }