@@ -16,15 +16,24 @@ package instance
1616import (
1717 "context"
1818 "errors"
19+ "fmt"
20+ "time"
1921
2022 ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
23+ ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue"
2124 ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
25+ "github.com/aws/aws-sdk-go-v2/aws"
2226 svcsdk "github.com/aws/aws-sdk-go-v2/service/ec2"
2327 svcsdktypes "github.com/aws/aws-sdk-go-v2/service/ec2/types"
2428
29+ "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1"
2530 "github.com/aws-controllers-k8s/ec2-controller/pkg/tags"
2631)
2732
33+ const (
34+ requeueUntilReadyDuration = 10 * time .Second
35+ )
36+
2837// addInstanceIDsToTerminateRequest populates the list of InstanceIDs
2938// in the TerminateInstances request with the resource's InstanceID
3039// Return error to indicate to callers that the resource is not yet created.
@@ -45,27 +54,123 @@ func (rm *resourceManager) customUpdateInstance(
4554) (updated * resource , err error ) {
4655 rlog := ackrtlog .FromContext (ctx )
4756 exit := rlog .Trace ("rm.customUpdateInstance" )
48- defer exit (err )
57+ defer func () { exit (err ) }( )
4958
5059 // Default `updated` to `desired` because it is likely
5160 // EC2 `modify` APIs do NOT return output, only errors.
5261 // If the `modify` calls (i.e. `sync`) do NOT return
5362 // an error, then the update was successful and desired.Spec
5463 // (now updated.Spec) reflects the latest resource state.
5564 updated = rm .concreteResource (desired .DeepCopy ())
65+ updated .SetStatus (latest )
5666
5767 if delta .DifferentAt ("Spec.Tags" ) {
5868 if err := tags .Sync (
5969 ctx , rm .sdkapi , rm .metrics , * latest .ko .Status .InstanceID ,
6070 desired .ko .Spec .Tags , latest .ko .Spec .Tags ,
6171 ); err != nil {
62- return nil , err
72+ return updated , err
6373 }
6474 }
6575
76+ if ! delta .DifferentExcept ("Spec.Tags" ) {
77+ return updated , nil
78+ }
79+
80+ if ! isRunning (updated .ko ) {
81+ return updated , ackrequeue .NeededAfter (
82+ fmt .Errorf ("requeuing until state is %s or %s" , svcsdktypes .InstanceStateNameRunning , svcsdktypes .InstanceStateNameStopped ),
83+ requeueUntilReadyDuration ,
84+ )
85+ }
86+
87+ err = rm .modifyInstanceAttributes (ctx , delta , desired , latest )
88+ if err != nil {
89+ return updated , err
90+ }
91+
6692 return updated , nil
6793}
6894
95+ func (rm * resourceManager ) modifyInstanceAttributes (ctx context.Context , delta * ackcompare.Delta , desired , latest * resource ) (err error ) {
96+ rlog := ackrtlog .FromContext (ctx )
97+ exit := rlog .Trace ("rm.modifyInstanceAttributes" )
98+ defer func () { exit (err ) }()
99+ input := & svcsdk.ModifyInstanceAttributeInput {
100+ InstanceId : latest .ko .Status .InstanceID ,
101+ }
102+ // we can only update one attribute at a time
103+ if delta .DifferentAt ("Spec.DisableAPITermination" ) {
104+ input .DisableApiTermination = & svcsdktypes.AttributeBooleanValue {Value : desired .ko .Spec .DisableAPITermination }
105+ } else if delta .DifferentAt ("Spec.InstanceType" ) {
106+ input .InstanceType = & svcsdktypes.AttributeValue {Value : desired .ko .Spec .InstanceType }
107+ } else if delta .DifferentAt ("Spec.KernelID" ) {
108+ input .Kernel = & svcsdktypes.AttributeValue {Value : desired .ko .Spec .KernelID }
109+ } else if delta .DifferentAt ("Spec.RAMDiskID" ) {
110+ input .Ramdisk = & svcsdktypes.AttributeValue {Value : desired .ko .Spec .RAMDiskID }
111+ } else if delta .DifferentAt ("Spec.InstanceInitiatedShutdownBehavior" ) {
112+ input .InstanceInitiatedShutdownBehavior = & svcsdktypes.AttributeValue {Value : desired .ko .Spec .InstanceInitiatedShutdownBehavior }
113+ } else if delta .DifferentAt ("Spec.UserData" ) {
114+ input .UserData = & svcsdktypes.BlobAttributeValue {Value : []byte (aws .ToString (desired .ko .Spec .UserData ))}
115+ } else if delta .DifferentAt ("Spec.EBSOptimized" ) {
116+ input .EbsOptimized = & svcsdktypes.AttributeBooleanValue {Value : desired .ko .Spec .EBSOptimized }
117+ } else if delta .DifferentAt ("Spec.DisableAPIStop" ) {
118+ input .DisableApiStop = & svcsdktypes.AttributeBooleanValue {Value : desired .ko .Spec .DisableAPIStop }
119+ } else if delta .DifferentAt ("Spec.SecurityGroupIDs" ) {
120+ input .Groups = aws .ToStringSlice (desired .ko .Spec .SecurityGroupIDs )
121+ } else {
122+ input = nil
123+ }
124+
125+ if input != nil {
126+ _ , err = rm .sdkapi .ModifyInstanceAttribute (ctx , input )
127+ rm .metrics .RecordAPICall ("UPDATE" , "ModifyInstanceAttribute" , err )
128+ if err != nil {
129+ return err
130+ }
131+ return fmt .Errorf ("requeuing until all fields are updated" )
132+ }
133+ return nil
134+ }
135+
136+ func isRunning (ko * v1alpha1.Instance ) bool {
137+ if ko .Status .State == nil || ko .Status .State .Name == nil {
138+ return false
139+ }
140+
141+ // NOTE: (michaelhtm) We will count `stopped` as running for now.
142+ // TODO: expose annotation to allow users to start/stop instances
143+ return * ko .Status .State .Name == string (svcsdktypes .InstanceStateNameRunning ) ||
144+ * ko .Status .State .Name == string (svcsdktypes .InstanceStateNameStopped )
145+ }
146+
147+ // needsRestart checks if the Instance is terminated (deleted)
148+ func needsRestart (ko * v1alpha1.Instance ) bool {
149+ if ko .Status .State == nil || ko .Status .State .Name == nil {
150+ return false
151+ }
152+
153+ return * ko .Status .State .Name == string (svcsdktypes .InstanceStateNameTerminated )
154+ }
155+
156+
157+ func setAdditionalFields (instance svcsdktypes.Instance , ko * v1alpha1.Instance ) {
158+ ko .Spec .SecurityGroupIDs = []* string {}
159+ for _ , group := range instance .SecurityGroups {
160+ ko .Spec .SecurityGroupIDs = append (ko .Spec .SecurityGroupIDs , group .GroupId )
161+ }
162+
163+ if monitoring := instance .Monitoring ; monitoring != nil {
164+ switch monitoring .State {
165+ case svcsdktypes .MonitoringStateDisabled , svcsdktypes .MonitoringStateDisabling :
166+ ko .Spec .Monitoring = & v1alpha1.RunInstancesMonitoringEnabled {Enabled : aws .Bool (false )}
167+
168+ case svcsdktypes .MonitoringStateEnabled , svcsdktypes .MonitoringStatePending :
169+ ko .Spec .Monitoring = & v1alpha1.RunInstancesMonitoringEnabled {Enabled : aws .Bool (true )}
170+ }
171+ }
172+ }
173+
69174var computeTagsDelta = tags .ComputeTagsDelta
70175
71176// updateTagSpecificationsInCreateRequest adds
0 commit comments