sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/scope/managednodegroup.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 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 ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1" 31 expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/exp/api/v1beta1" 32 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud" 33 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/throttle" 34 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 35 expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 36 "sigs.k8s.io/cluster-api/util/conditions" 37 "sigs.k8s.io/cluster-api/util/patch" 38 ) 39 40 // ManagedMachinePoolScopeParams defines the input parameters used to create a new Scope. 41 type ManagedMachinePoolScopeParams struct { 42 Client client.Client 43 Logger *logr.Logger 44 Cluster *clusterv1.Cluster 45 ControlPlane *ekscontrolplanev1.AWSManagedControlPlane 46 ManagedMachinePool *expinfrav1.AWSManagedMachinePool 47 MachinePool *expclusterv1.MachinePool 48 ControllerName string 49 Endpoints []ServiceEndpoint 50 Session awsclient.ConfigProvider 51 52 EnableIAM bool 53 AllowAdditionalRoles bool 54 } 55 56 // NewManagedMachinePoolScope creates a new Scope from the supplied parameters. 57 // This is meant to be called for each reconcile iteration. 58 func NewManagedMachinePoolScope(params ManagedMachinePoolScopeParams) (*ManagedMachinePoolScope, error) { 59 if params.ControlPlane == nil { 60 return nil, errors.New("failed to generate new scope from nil AWSManagedMachinePool") 61 } 62 if params.MachinePool == nil { 63 return nil, errors.New("failed to generate new scope from nil MachinePool") 64 } 65 if params.ManagedMachinePool == nil { 66 return nil, errors.New("failed to generate new scope from nil ManagedMachinePool") 67 } 68 if params.Logger == nil { 69 log := klogr.New() 70 params.Logger = &log 71 } 72 73 managedScope := &ManagedControlPlaneScope{ 74 Logger: *params.Logger, 75 Client: params.Client, 76 Cluster: params.Cluster, 77 ControlPlane: params.ControlPlane, 78 controllerName: params.ControllerName, 79 } 80 session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, managedScope, params.ControlPlane.Spec.Region, params.Endpoints, *params.Logger) 81 if err != nil { 82 return nil, errors.Errorf("failed to create aws session: %v", err) 83 } 84 85 helper, err := patch.NewHelper(params.ManagedMachinePool, params.Client) 86 if err != nil { 87 return nil, errors.Wrap(err, "failed to init patch helper") 88 } 89 90 return &ManagedMachinePoolScope{ 91 Logger: *params.Logger, 92 Client: params.Client, 93 Cluster: params.Cluster, 94 ControlPlane: params.ControlPlane, 95 ManagedMachinePool: params.ManagedMachinePool, 96 MachinePool: params.MachinePool, 97 patchHelper: helper, 98 session: session, 99 serviceLimiters: serviceLimiters, 100 controllerName: params.ControllerName, 101 enableIAM: params.EnableIAM, 102 allowAdditionalRoles: params.AllowAdditionalRoles, 103 }, nil 104 } 105 106 // ManagedMachinePoolScope defines the basic context for an actuator to operate upon. 107 type ManagedMachinePoolScope struct { 108 logr.Logger 109 Client client.Client 110 patchHelper *patch.Helper 111 112 Cluster *clusterv1.Cluster 113 ControlPlane *ekscontrolplanev1.AWSManagedControlPlane 114 ManagedMachinePool *expinfrav1.AWSManagedMachinePool 115 MachinePool *expclusterv1.MachinePool 116 117 session awsclient.ConfigProvider 118 serviceLimiters throttle.ServiceLimiters 119 controllerName string 120 121 enableIAM bool 122 allowAdditionalRoles bool 123 } 124 125 // ManagedPoolName returns the managed machine pool name. 126 func (s *ManagedMachinePoolScope) ManagedPoolName() string { 127 return s.ManagedMachinePool.Name 128 } 129 130 // ServiceLimiter returns the AWS SDK session. Used for creating clients. 131 func (s *ManagedMachinePoolScope) ServiceLimiter(service string) *throttle.ServiceLimiter { 132 if sl, ok := s.serviceLimiters[service]; ok { 133 return sl 134 } 135 return nil 136 } 137 138 // ClusterName returns the cluster name. 139 func (s *ManagedMachinePoolScope) ClusterName() string { 140 return s.ControlPlane.Spec.EKSClusterName 141 } 142 143 // EnableIAM indicates that reconciliation should create IAM roles. 144 func (s *ManagedMachinePoolScope) EnableIAM() bool { 145 return s.enableIAM 146 } 147 148 // AllowAdditionalRoles indicates if additional roles can be added to the created IAM roles. 149 func (s *ManagedMachinePoolScope) AllowAdditionalRoles() bool { 150 return s.allowAdditionalRoles 151 } 152 153 // IdentityRef returns the cluster identityRef. 154 func (s *ManagedMachinePoolScope) IdentityRef() *infrav1.AWSIdentityReference { 155 return s.ControlPlane.Spec.IdentityRef 156 } 157 158 // AdditionalTags returns AdditionalTags from the scope's ManagedMachinePool 159 // The returned value will never be nil. 160 func (s *ManagedMachinePoolScope) AdditionalTags() infrav1.Tags { 161 if s.ManagedMachinePool.Spec.AdditionalTags == nil { 162 s.ManagedMachinePool.Spec.AdditionalTags = infrav1.Tags{} 163 } 164 165 return s.ManagedMachinePool.Spec.AdditionalTags.DeepCopy() 166 } 167 168 // RoleName returns the node group role name. 169 func (s *ManagedMachinePoolScope) RoleName() string { 170 return s.ManagedMachinePool.Spec.RoleName 171 } 172 173 // Version returns the nodegroup Kubernetes version. 174 func (s *ManagedMachinePoolScope) Version() *string { 175 return s.MachinePool.Spec.Template.Spec.Version 176 } 177 178 // ControlPlaneSubnets returns the control plane subnets. 179 func (s *ManagedMachinePoolScope) ControlPlaneSubnets() infrav1.Subnets { 180 return s.ControlPlane.Spec.NetworkSpec.Subnets 181 } 182 183 // SubnetIDs returns the machine pool subnet IDs. 184 func (s *ManagedMachinePoolScope) SubnetIDs() ([]string, error) { 185 strategy, err := newDefaultSubnetPlacementStrategy(&s.Logger) 186 if err != nil { 187 return []string{}, fmt.Errorf("getting subnet placement strategy: %w", err) 188 } 189 190 return strategy.Place(&placementInput{ 191 SpecSubnetIDs: s.ManagedMachinePool.Spec.SubnetIDs, 192 SpecAvailabilityZones: s.ManagedMachinePool.Spec.AvailabilityZones, 193 ParentAvailabilityZones: s.MachinePool.Spec.FailureDomains, 194 ControlplaneSubnets: s.ControlPlaneSubnets(), 195 }) 196 } 197 198 // NodegroupReadyFalse marks the ready condition false using warning if error isn't 199 // empty. 200 func (s *ManagedMachinePoolScope) NodegroupReadyFalse(reason string, err string) error { 201 severity := clusterv1.ConditionSeverityWarning 202 if err == "" { 203 severity = clusterv1.ConditionSeverityInfo 204 } 205 conditions.MarkFalse( 206 s.ManagedMachinePool, 207 expinfrav1.EKSNodegroupReadyCondition, 208 reason, 209 severity, 210 err, 211 ) 212 if err := s.PatchObject(); err != nil { 213 return errors.Wrap(err, "failed to mark nodegroup not ready") 214 } 215 return nil 216 } 217 218 // IAMReadyFalse marks the ready condition false using warning if error isn't 219 // empty. 220 func (s *ManagedMachinePoolScope) IAMReadyFalse(reason string, err string) error { 221 severity := clusterv1.ConditionSeverityWarning 222 if err == "" { 223 severity = clusterv1.ConditionSeverityInfo 224 } 225 conditions.MarkFalse( 226 s.ManagedMachinePool, 227 expinfrav1.IAMNodegroupRolesReadyCondition, 228 reason, 229 severity, 230 err, 231 ) 232 if err := s.PatchObject(); err != nil { 233 return errors.Wrap(err, "failed to mark nodegroup role not ready") 234 } 235 return nil 236 } 237 238 // PatchObject persists the control plane configuration and status. 239 func (s *ManagedMachinePoolScope) PatchObject() error { 240 return s.patchHelper.Patch( 241 context.TODO(), 242 s.ManagedMachinePool, 243 patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{ 244 expinfrav1.EKSNodegroupReadyCondition, 245 expinfrav1.IAMNodegroupRolesReadyCondition, 246 }}) 247 } 248 249 // Close closes the current scope persisting the control plane configuration and status. 250 func (s *ManagedMachinePoolScope) Close() error { 251 return s.PatchObject() 252 } 253 254 // InfraCluster returns the AWS infrastructure cluster or control plane object. 255 func (s *ManagedMachinePoolScope) InfraCluster() cloud.ClusterObject { 256 return s.ControlPlane 257 } 258 259 // ClusterObj returns the cluster object. 260 func (s *ManagedMachinePoolScope) ClusterObj() cloud.ClusterObject { 261 return s.Cluster 262 } 263 264 // Session returns the AWS SDK session. Used for creating clients. 265 func (s *ManagedMachinePoolScope) Session() awsclient.ConfigProvider { 266 return s.session 267 } 268 269 // ControllerName returns the name of the controller that 270 // created the ManagedMachinePool. 271 func (s *ManagedMachinePoolScope) ControllerName() string { 272 return s.controllerName 273 } 274 275 // KubernetesClusterName is the name of the EKS cluster name. 276 func (s *ManagedMachinePoolScope) KubernetesClusterName() string { 277 return s.ControlPlane.Spec.EKSClusterName 278 } 279 280 // NodegroupName is the name of the EKS nodegroup. 281 func (s *ManagedMachinePoolScope) NodegroupName() string { 282 return s.ManagedMachinePool.Spec.EKSNodegroupName 283 }