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 }