Skip to content

Commit d515275

Browse files
AndiDogcnmcavoy
andcommitted
Add AWSMachines to back the EC2 instances in AWSMachinePools and AWSManagedMachinePools
Co-authored-by: Cameron McAvoy <[email protected]>
1 parent 95b1622 commit d515275

18 files changed

+679
-82
lines changed

config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinepools.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,10 @@ spec:
11441144
can be added as events to the Machine object and/or logged in the
11451145
controller's output.
11461146
type: string
1147+
infrastructureMachineKind:
1148+
description: InfrastructureMachineKind is the kind of the infrastructure
1149+
resources behind MachinePool Machines.
1150+
type: string
11471151
instances:
11481152
description: Instances contains the status for each instance in the
11491153
pool

config/crd/bases/infrastructure.cluster.x-k8s.io_awsmanagedmachinepools.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,10 @@ spec:
10721072
can be added as events to the MachinePool object and/or logged in the
10731073
controller's output.
10741074
type: string
1075+
infrastructureMachineKind:
1076+
description: InfrastructureMachineKind is the kind of the infrastructure
1077+
resources behind MachinePool Machines.
1078+
type: string
10751079
launchTemplateID:
10761080
description: The ID of the launch template
10771081
type: string

config/rbac/role.yaml

+21-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ rules:
7272
- clusters
7373
- clusters/status
7474
- machinedeployments
75-
- machines
7675
- machines/status
7776
verbs:
7877
- get
@@ -88,6 +87,15 @@ rules:
8887
- list
8988
- patch
9089
- watch
90+
- apiGroups:
91+
- cluster.x-k8s.io
92+
resources:
93+
- machines
94+
verbs:
95+
- delete
96+
- get
97+
- list
98+
- watch
9199
- apiGroups:
92100
- controlplane.cluster.x-k8s.io
93101
resources:
@@ -150,7 +158,6 @@ rules:
150158
- awsclusters
151159
- awsfargateprofiles
152160
- awsmachinepools
153-
- awsmachines
154161
- awsmanagedclusters
155162
- awsmanagedmachinepools
156163
- rosaclusters
@@ -186,6 +193,18 @@ rules:
186193
- patch
187194
- update
188195
- watch
196+
- apiGroups:
197+
- infrastructure.cluster.x-k8s.io
198+
resources:
199+
- awsmachines
200+
verbs:
201+
- create
202+
- delete
203+
- get
204+
- list
205+
- patch
206+
- update
207+
- watch
189208
- apiGroups:
190209
- infrastructure.cluster.x-k8s.io
191210
resources:

controllers/awsmachine_controller.go

+42-17
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,11 @@ func (r *AWSMachineReconciler) getObjectStoreService(scope scope.S3Scope) servic
143143
return s3.NewService(scope)
144144
}
145145

146-
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmachines,verbs=get;list;watch;update;patch;delete
147-
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmachines/status,verbs=get;update;patch
148146
// +kubebuilder:rbac:groups=controlplane.cluster.x-k8s.io,resources=*,verbs=get;list;watch
149-
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machines;machines/status,verbs=get;list;watch
147+
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmachines,verbs=create;get;list;watch;update;patch;delete
148+
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmachines/status,verbs=get;update;patch
149+
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machines,verbs=get;list;watch;delete
150+
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machines/status,verbs=get;list;watch
150151
// +kubebuilder:rbac:groups="",resources=secrets;,verbs=get;list;watch
151152
// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch
152153
// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch
@@ -459,6 +460,7 @@ func (r *AWSMachineReconciler) findInstance(machineScope *scope.MachineScope, ec
459460
return instance, nil
460461
}
461462

463+
//nolint:gocyclo
462464
func (r *AWSMachineReconciler) reconcileNormal(_ context.Context, machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper, ec2Scope scope.EC2Scope, elbScope scope.ELBScope, objectStoreScope scope.S3Scope) (ctrl.Result, error) {
463465
machineScope.Trace("Reconciling AWSMachine")
464466

@@ -482,7 +484,7 @@ func (r *AWSMachineReconciler) reconcileNormal(_ context.Context, machineScope *
482484
}
483485

484486
// Make sure bootstrap data is available and populated.
485-
if machineScope.Machine.Spec.Bootstrap.DataSecretName == nil {
487+
if !machineScope.IsMachinePoolMachine() && machineScope.Machine.Spec.Bootstrap.DataSecretName == nil {
486488
machineScope.Info("Bootstrap data secret reference is not yet available")
487489
conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.WaitingForBootstrapDataReason, clusterv1.ConditionSeverityInfo, "")
488490
return ctrl.Result{}, nil
@@ -497,6 +499,12 @@ func (r *AWSMachineReconciler) reconcileNormal(_ context.Context, machineScope *
497499
conditions.MarkUnknown(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceNotFoundReason, err.Error())
498500
return ctrl.Result{}, err
499501
}
502+
if instance == nil && machineScope.IsMachinePoolMachine() {
503+
err = errors.New("no instance found for machine pool")
504+
machineScope.Error(err, "unable to find instance")
505+
conditions.MarkUnknown(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceNotFoundReason, err.Error())
506+
return ctrl.Result{}, err
507+
}
500508

501509
// If the AWSMachine doesn't have our finalizer, add it.
502510
if controllerutil.AddFinalizer(machineScope.AWSMachine, infrav1.MachineFinalizer) {
@@ -586,9 +594,18 @@ func (r *AWSMachineReconciler) reconcileNormal(_ context.Context, machineScope *
586594
conditions.MarkTrue(machineScope.AWSMachine, infrav1.InstanceReadyCondition)
587595
case infrav1.InstanceStateShuttingDown, infrav1.InstanceStateTerminated:
588596
machineScope.SetNotReady()
589-
machineScope.Info("Unexpected EC2 instance termination", "state", instance.State, "instance-id", *machineScope.GetInstanceID())
590-
r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "InstanceUnexpectedTermination", "Unexpected EC2 instance termination")
591-
conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceTerminatedReason, clusterv1.ConditionSeverityError, "")
597+
598+
if machineScope.IsMachinePoolMachine() {
599+
// In an auto-scaling group, instance termination is perfectly normal on scale-down
600+
// and therefore should not be reported as error.
601+
machineScope.Info("EC2 instance of machine pool was terminated", "state", instance.State, "instance-id", *machineScope.GetInstanceID())
602+
r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeNormal, infrav1.InstanceTerminatedReason, "EC2 instance termination")
603+
conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceTerminatedReason, clusterv1.ConditionSeverityInfo, "")
604+
} else {
605+
machineScope.Info("Unexpected EC2 instance termination", "state", instance.State, "instance-id", *machineScope.GetInstanceID())
606+
r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "InstanceUnexpectedTermination", "Unexpected EC2 instance termination")
607+
conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceTerminatedReason, clusterv1.ConditionSeverityError, "")
608+
}
592609
default:
593610
machineScope.SetNotReady()
594611
machineScope.Info("EC2 instance state is undefined", "state", instance.State, "instance-id", *machineScope.GetInstanceID())
@@ -599,14 +616,18 @@ func (r *AWSMachineReconciler) reconcileNormal(_ context.Context, machineScope *
599616
}
600617

601618
// reconcile the deletion of the bootstrap data secret now that we have updated instance state
602-
if deleteSecretErr := r.deleteBootstrapData(machineScope, clusterScope, objectStoreScope); deleteSecretErr != nil {
603-
r.Log.Error(deleteSecretErr, "unable to delete secrets")
604-
return ctrl.Result{}, deleteSecretErr
605-
}
619+
if !machineScope.IsMachinePoolMachine() {
620+
if deleteSecretErr := r.deleteBootstrapData(machineScope, clusterScope, objectStoreScope); deleteSecretErr != nil {
621+
r.Log.Error(deleteSecretErr, "unable to delete secrets")
622+
return ctrl.Result{}, deleteSecretErr
623+
}
606624

607-
if instance.State == infrav1.InstanceStateTerminated {
608-
machineScope.SetFailureReason(capierrors.UpdateMachineError)
609-
machineScope.SetFailureMessage(errors.Errorf("EC2 instance state %q is unexpected", instance.State))
625+
// For machine pool machines, it is expected that the ASG terminates instances at any time,
626+
// so no error is logged for those.
627+
if instance.State == infrav1.InstanceStateTerminated {
628+
machineScope.SetFailureReason(capierrors.UpdateMachineError)
629+
machineScope.SetFailureMessage(errors.Errorf("EC2 instance state %q is unexpected", instance.State))
630+
}
610631
}
611632

612633
// tasks that can take place during all known instance states
@@ -876,9 +897,13 @@ func getIgnitionVersion(scope *scope.MachineScope) string {
876897
}
877898

878899
func (r *AWSMachineReconciler) deleteBootstrapData(machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper, objectStoreScope scope.S3Scope) error {
879-
_, userDataFormat, err := machineScope.GetRawBootstrapDataWithFormat()
880-
if client.IgnoreNotFound(err) != nil {
881-
return errors.Wrap(err, "failed to get raw userdata")
900+
var userDataFormat string
901+
var err error
902+
if machineScope.Machine.Spec.Bootstrap.DataSecretName != nil {
903+
_, userDataFormat, err = machineScope.GetRawBootstrapDataWithFormat()
904+
if client.IgnoreNotFound(err) != nil {
905+
return errors.Wrap(err, "failed to get raw userdata")
906+
}
882907
}
883908

884909
if machineScope.UseSecretsManager(userDataFormat) {

exp/api/v1beta1/conversion.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func (src *AWSMachinePool) ConvertTo(dstRaw conversion.Hub) error {
5252
if restored.Spec.AvailabilityZoneSubnetType != nil {
5353
dst.Spec.AvailabilityZoneSubnetType = restored.Spec.AvailabilityZoneSubnetType
5454
}
55+
dst.Status.InfrastructureMachineKind = restored.Status.InfrastructureMachineKind
5556

5657
if restored.Spec.AWSLaunchTemplate.PrivateDNSName != nil {
5758
dst.Spec.AWSLaunchTemplate.PrivateDNSName = restored.Spec.AWSLaunchTemplate.PrivateDNSName
@@ -83,7 +84,6 @@ func (src *AWSMachinePoolList) ConvertTo(dstRaw conversion.Hub) error {
8384
// ConvertFrom converts the v1beta2 AWSMachinePoolList receiver to v1beta1 AWSMachinePoolList.
8485
func (r *AWSMachinePoolList) ConvertFrom(srcRaw conversion.Hub) error {
8586
src := srcRaw.(*infrav1exp.AWSMachinePoolList)
86-
8787
return Convert_v1beta2_AWSMachinePoolList_To_v1beta1_AWSMachinePoolList(src, r, nil)
8888
}
8989

@@ -114,6 +114,8 @@ func (src *AWSManagedMachinePool) ConvertTo(dstRaw conversion.Hub) error {
114114
dst.Spec.AvailabilityZoneSubnetType = restored.Spec.AvailabilityZoneSubnetType
115115
}
116116

117+
dst.Status.InfrastructureMachineKind = restored.Status.InfrastructureMachineKind
118+
117119
return nil
118120
}
119121

@@ -133,6 +135,14 @@ func Convert_v1beta2_AWSManagedMachinePoolSpec_To_v1beta1_AWSManagedMachinePoolS
133135
return autoConvert_v1beta2_AWSManagedMachinePoolSpec_To_v1beta1_AWSManagedMachinePoolSpec(in, out, s)
134136
}
135137

138+
func Convert_v1beta2_AWSMachinePoolStatus_To_v1beta1_AWSMachinePoolStatus(in *infrav1exp.AWSMachinePoolStatus, out *AWSMachinePoolStatus, s apiconversion.Scope) error {
139+
return autoConvert_v1beta2_AWSMachinePoolStatus_To_v1beta1_AWSMachinePoolStatus(in, out, s)
140+
}
141+
142+
func Convert_v1beta2_AWSManagedMachinePoolStatus_To_v1beta1_AWSManagedMachinePoolStatus(in *infrav1exp.AWSManagedMachinePoolStatus, out *AWSManagedMachinePoolStatus, s apiconversion.Scope) error {
143+
return autoConvert_v1beta2_AWSManagedMachinePoolStatus_To_v1beta1_AWSManagedMachinePoolStatus(in, out, s)
144+
}
145+
136146
// ConvertTo converts the v1beta1 AWSManagedMachinePoolList receiver to a v1beta2 AWSManagedMachinePoolList.
137147
func (src *AWSManagedMachinePoolList) ConvertTo(dstRaw conversion.Hub) error {
138148
dst := dstRaw.(*infrav1exp.AWSManagedMachinePoolList)

exp/api/v1beta1/zz_generated.conversion.go

+12-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

exp/api/v1beta2/awsmachinepool_types.go

+4
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ type AWSMachinePoolStatus struct {
210210
// +optional
211211
LaunchTemplateVersion *string `json:"launchTemplateVersion,omitempty"`
212212

213+
// InfrastructureMachineKind is the kind of the infrastructure resources behind MachinePool Machines.
214+
// +optional
215+
InfrastructureMachineKind string `json:"infrastructureMachineKind,omitempty"`
216+
213217
// FailureReason will be set in the event that there is a terminal problem
214218
// reconciling the Machine and will contain a succinct value suitable
215219
// for machine interpretation.

exp/api/v1beta2/awsmanagedmachinepool_types.go

+4
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ type AWSManagedMachinePoolStatus struct {
199199
// +optional
200200
LaunchTemplateVersion *string `json:"launchTemplateVersion,omitempty"`
201201

202+
// InfrastructureMachineKind is the kind of the infrastructure resources behind MachinePool Machines.
203+
// +optional
204+
InfrastructureMachineKind string `json:"infrastructureMachineKind,omitempty"`
205+
202206
// FailureReason will be set in the event that there is a terminal problem
203207
// reconciling the MachinePool and will contain a succinct value suitable
204208
// for machine interpretation.

exp/api/v1beta2/conditions_consts.go

+5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ const (
5454
InstanceRefreshNotReadyReason = "InstanceRefreshNotReady"
5555
// InstanceRefreshFailedReason used to report when there instance refresh is not initiated.
5656
InstanceRefreshFailedReason = "InstanceRefreshFailed"
57+
58+
// AWSMachineCreationFailed reports if creating AWSMachines to represent ASG (machine pool) machines failed.
59+
AWSMachineCreationFailed = "AWSMachineCreationFailed"
60+
// AWSMachineDeletionFailed reports if deleting AWSMachines failed.
61+
AWSMachineDeletionFailed = "AWSMachineDeletionFailed"
5762
)
5863

5964
const (

exp/api/v1beta2/types.go

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ import (
2222
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
2323
)
2424

25+
const (
26+
// KindMachinePool is a MachinePool resource Kind
27+
KindMachinePool string = "MachinePool"
28+
)
29+
2530
// EBS can be used to automatically set up EBS volumes when an instance is launched.
2631
type EBS struct {
2732
// Encrypted is whether the volume should be encrypted or not.

0 commit comments

Comments
 (0)