@@ -14,10 +14,18 @@ import (
1414 packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
1515)
1616
17+ const (
18+ // DefaultCleanupTimeout is the default maximum time to wait for instance deletion
19+ DefaultCleanupTimeout = 10 * time .Minute
20+ // CleanupPollInterval is how often to check instance deletion status
21+ CleanupPollInterval = 10 * time .Second
22+ )
23+
1724type StepCreateInstance struct {
18- InstanceName string
19- KeyPairName string
20- UserData string
25+ InstanceName string
26+ KeyPairName string
27+ UserData string
28+ CleanupTimeout time.Duration
2129
2230 doCleanup bool
2331}
@@ -114,20 +122,75 @@ func (s *StepCreateInstance) Cleanup(state multistep.StateBag) {
114122 ui .Say ("Deleting the Instance" )
115123 instanceClient := state .Get ("instanceClient" ).(* instance.IBMPIInstanceClient )
116124 i := state .Get ("instance" ).(* models.PVMInstance )
125+
126+ // Initiate deletion
117127 err := instanceClient .Delete (* i .PvmInstanceID )
118128 if err != nil {
119129 ui .Error (fmt .Sprintf (
120- "Error cleaning up instance. Please delete the instance manually: %s" , * i .ServerName ))
130+ "Error initiating instance deletion. Please delete the instance manually: %s (ID: %s)" ,
131+ * i .ServerName , * i .PvmInstanceID ))
132+ ui .Error (fmt .Sprintf ("Deletion error: %v" , err ))
133+ return
134+ }
135+
136+ // Wait for deletion with timeout
137+ timeout := s .CleanupTimeout
138+ if timeout == 0 {
139+ timeout = DefaultCleanupTimeout
121140 }
122- for {
141+
142+ begin := time .Now ()
143+ errorStateCount := 0
144+ maxErrorStateRetries := 5 // Give some retries even in ERROR state
145+
146+ //nolint:staticcheck // SA1015 this disable staticcheck for the next line
147+ err = pollUntil (time .Tick (CleanupPollInterval ), time .After (timeout ), func () (bool , error ) {
123148 in , err := instanceClient .Get (* i .PvmInstanceID )
124- if err == nil {
125- ui .Message (fmt .Sprintf ("VM still exists, state: %s" , * in .Status ))
126- time .Sleep (10 * time .Second )
127- continue
128- } else {
129- ui .Message ("instance deleted successfully" )
130- break
149+
150+ // Instance not found means it was successfully deleted
151+ if err != nil {
152+ ui .Message ("Instance deleted successfully" )
153+ return true , nil
154+ }
155+
156+ // Instance still exists, check its state
157+ currentState := * in .Status
158+ elapsed := time .Since (begin ).Round (time .Second )
159+ ui .Message (fmt .Sprintf ("VM still exists, state: %s (elapsed: %s)" , currentState , elapsed ))
160+
161+ // If instance is in ERROR state, warn but continue for a few retries
162+ // Sometimes instances in ERROR state can still be deleted
163+ if currentState == "ERROR" {
164+ errorStateCount ++
165+ if errorStateCount == 1 {
166+ ui .Message ("Warning: Instance entered ERROR state during deletion" )
167+ ui .Message ("Continuing to wait for deletion (may require manual cleanup)" )
168+ }
169+ if errorStateCount >= maxErrorStateRetries {
170+ ui .Error (fmt .Sprintf (
171+ "Instance has been in ERROR state for %d checks. Deletion may have failed." ,
172+ errorStateCount ))
173+ ui .Error (fmt .Sprintf (
174+ "Please verify instance status manually: %s (ID: %s)" ,
175+ * i .ServerName , * i .PvmInstanceID ))
176+ // Don't return error, let timeout handle it
177+ }
178+ }
179+
180+ return false , nil
181+ })
182+
183+ if err != nil {
184+ // Timeout occurred
185+ ui .Error (fmt .Sprintf (
186+ "Timed out waiting for instance deletion after %s" , timeout ))
187+ ui .Error (fmt .Sprintf (
188+ "Instance may need manual cleanup: %s (ID: %s)" ,
189+ * i .ServerName , * i .PvmInstanceID ))
190+
191+ // Try to get final state
192+ if finalInstance , getErr := instanceClient .Get (* i .PvmInstanceID ); getErr == nil {
193+ ui .Error (fmt .Sprintf ("Final instance state: %s" , * finalInstance .Status ))
131194 }
132195 }
133196}
0 commit comments