Skip to content

Commit 33f438d

Browse files
committed
Allow forced detachment of a host from Ironic
Adds Force as DetachedAnnotationArgument to API. Detach in all states if force is set to true. Add force argument to provisioner's Detach and Delete functions. Signed-off-by: CrystalChun <cchun@redhat.com>
1 parent f07a0f3 commit 33f438d

File tree

9 files changed

+45
-21
lines changed

9 files changed

+45
-21
lines changed

apis/metal3.io/v1alpha1/baremetalhost_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,11 @@ const (
614614
type DetachedAnnotationArguments struct {
615615
// DeleteAction indicates the desired delete logic when the detached annotation is present
616616
DeleteAction DetachedDeleteAction `json:"deleteAction,omitempty"`
617+
618+
// Force indicates if detaching should be forced regardless of the host's state
619+
// +optional
620+
// +kubebuilder:default:=false
621+
Force bool `json:"force,omitempty"`
617622
}
618623

619624
// Match compares the saved status information with the name and

internal/controller/metal3.io/baremetalhost_controller.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ func (r *BareMetalHostReconciler) actionPowerOffBeforeDeleting(prov provisioner.
538538
}
539539

540540
// Manage deletion of the host.
541-
func (r *BareMetalHostReconciler) actionDeleting(prov provisioner.Provisioner, info *reconcileInfo) actionResult {
541+
func (r *BareMetalHostReconciler) actionDeleting(prov provisioner.Provisioner, info *reconcileInfo, force bool) actionResult {
542542
info.log.Info(
543543
"marked to be deleted",
544544
"timestamp", info.host.DeletionTimestamp,
@@ -550,7 +550,7 @@ func (r *BareMetalHostReconciler) actionDeleting(prov provisioner.Provisioner, i
550550
return deleteComplete{}
551551
}
552552

553-
provResult, err := prov.Delete()
553+
provResult, err := prov.Delete(force)
554554
if err != nil {
555555
return actionError{fmt.Errorf("failed to delete: %w", err)}
556556
}
@@ -616,8 +616,8 @@ func hasCustomDeploy(host *metal3api.BareMetalHost) bool {
616616
}
617617

618618
// detachHost() detaches the host from the Provisioner.
619-
func (r *BareMetalHostReconciler) detachHost(prov provisioner.Provisioner, info *reconcileInfo) actionResult {
620-
provResult, err := prov.Detach()
619+
func (r *BareMetalHostReconciler) detachHost(prov provisioner.Provisioner, info *reconcileInfo, force bool) actionResult {
620+
provResult, err := prov.Detach(force)
621621
if err != nil {
622622
return actionError{fmt.Errorf("failed to detach: %w", err)}
623623
}

internal/controller/metal3.io/host_state_machine.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,14 @@ func (hsm *hostStateMachine) checkDetachedHost(info *reconcileInfo) (result acti
316316
// Only allow detaching hosts in Provisioned/ExternallyProvisioned/Ready/Available states
317317
switch info.host.Status.Provisioning.State {
318318
case metal3api.StateProvisioned, metal3api.StateExternallyProvisioned, metal3api.StateReady, metal3api.StateAvailable:
319-
return hsm.Reconciler.detachHost(hsm.Provisioner, info)
319+
return hsm.Reconciler.detachHost(hsm.Provisioner, info, false)
320320
default:
321-
info.log.Info("host cannot be detached yet, waiting for the current operation to finish", "provisioningState", info.host.Status.Provisioning.State)
321+
info.log.Info("host not in allowed detaching state, checking for force annotation")
322+
if hasForceDetachAnnotation(hsm.Host) {
323+
info.log.Info("forcing detach of host", "host", info.host.Name, "annotation", hsm.Host.GetAnnotations()[metal3api.DetachedAnnotation])
324+
return hsm.Reconciler.detachHost(hsm.Provisioner, info, true)
325+
}
326+
info.log.Info("host cannot be detached yet, waiting for the current operation to finish", "provisioningState", info.host.Status.Provisioning.State, "annotation", hsm.Host.GetAnnotations()[metal3api.DetachedAnnotation])
322327
}
323328
}
324329
if info.host.Status.ErrorType == metal3api.DetachError {
@@ -339,6 +344,20 @@ func (hsm *hostStateMachine) checkDetachedHost(info *reconcileInfo) (result acti
339344
return nil
340345
}
341346

347+
func hasForceDetachAnnotation(host *metal3api.BareMetalHost) bool {
348+
annotations := host.GetAnnotations()
349+
if annotations != nil {
350+
if val, ok := annotations[metal3api.DetachedAnnotation]; ok {
351+
args := metal3api.DetachedAnnotationArguments{}
352+
if err := json.Unmarshal([]byte(val), &args); err != nil {
353+
return false
354+
}
355+
return args.Force
356+
}
357+
}
358+
return false
359+
}
360+
342361
func (hsm *hostStateMachine) ensureRegistered(info *reconcileInfo) (result actionResult) {
343362
if !hsm.haveCreds {
344363
// If we are in the process of deletion (which may start with
@@ -631,5 +650,5 @@ func (hsm *hostStateMachine) handlePoweringOffBeforeDelete(info *reconcileInfo)
631650
}
632651

633652
func (hsm *hostStateMachine) handleDeleting(info *reconcileInfo) actionResult {
634-
return hsm.Reconciler.actionDeleting(hsm.Provisioner, info)
653+
return hsm.Reconciler.actionDeleting(hsm.Provisioner, info, false)
635654
}

internal/controller/metal3.io/host_state_machine_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,11 +1378,11 @@ func (m *mockProvisioner) Deprovision(_ bool, _ metal3api.AutomatedCleaningMode)
13781378
return m.getNextResultByMethod("Deprovision"), err
13791379
}
13801380

1381-
func (m *mockProvisioner) Delete() (result provisioner.Result, err error) {
1381+
func (m *mockProvisioner) Delete(_ bool) (result provisioner.Result, err error) {
13821382
return m.getNextResultByMethod("Delete"), err
13831383
}
13841384

1385-
func (m *mockProvisioner) Detach() (result provisioner.Result, err error) {
1385+
func (m *mockProvisioner) Detach(_ bool) (result provisioner.Result, err error) {
13861386
res := m.getNextResultByMethod("Detach")
13871387
return res, err
13881388
}

pkg/provisioner/demo/demo.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ func (p *demoProvisioner) Deprovision(_ bool, _ metal3api.AutomatedCleaningMode)
277277
// Delete removes the host from the provisioning system. It may be
278278
// called multiple times, and should return true for its dirty flag
279279
// until the deprovisioning operation is completed.
280-
func (p *demoProvisioner) Delete() (result provisioner.Result, err error) {
280+
func (p *demoProvisioner) Delete(_ bool) (result provisioner.Result, err error) {
281281
p.log.Info("deleting host")
282282
return result, nil
283283
}
@@ -287,7 +287,7 @@ func (p *demoProvisioner) Delete() (result provisioner.Result, err error) {
287287
// for the target system. It may be called multiple times,
288288
// and should return true for its dirty flag until the
289289
// deletion operation is completed.
290-
func (p *demoProvisioner) Detach() (result provisioner.Result, err error) {
290+
func (p *demoProvisioner) Detach(_ bool) (result provisioner.Result, err error) {
291291
p.log.Info("detaching host")
292292
return result, nil
293293
}

pkg/provisioner/fixture/fixture.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ func (p *fixtureProvisioner) Deprovision(_ bool, _ metal3api.AutomatedCleaningMo
329329
// Delete removes the host from the provisioning system. It may be
330330
// called multiple times, and should return true for its dirty flag
331331
// until the deprovisioning operation is completed.
332-
func (p *fixtureProvisioner) Delete() (result provisioner.Result, err error) {
332+
func (p *fixtureProvisioner) Delete(_ bool) (result provisioner.Result, err error) {
333333
p.log.Info("deleting host")
334334

335335
if !p.state.Deleted {
@@ -347,8 +347,8 @@ func (p *fixtureProvisioner) Delete() (result provisioner.Result, err error) {
347347
// for the target system. It may be called multiple times,
348348
// and should return true for its dirty flag until the
349349
// deletion operation is completed.
350-
func (p *fixtureProvisioner) Detach() (result provisioner.Result, err error) {
351-
return p.Delete()
350+
func (p *fixtureProvisioner) Detach(force bool) (result provisioner.Result, err error) {
351+
return p.Delete(force)
352352
}
353353

354354
// PowerOn ensures the server is powered on independently of any image

pkg/provisioner/ironic/delete_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,9 @@ func deleteTest(t *testing.T, detach bool) {
217217

218218
var result provisioner.Result
219219
if detach {
220-
result, err = prov.Detach()
220+
result, err = prov.Detach(false)
221221
} else {
222-
result, err = prov.Delete()
222+
result, err = prov.Delete(false)
223223
}
224224

225225
assert.Equal(t, tc.expectedDirty, result.Dirty)

pkg/provisioner/ironic/ironic.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,7 @@ func (p *ironicProvisioner) Deprovision(restartOnFailure bool, automatedCleaning
15231523
// Delete removes the host from the provisioning system. It may be
15241524
// called multiple times, and should return true for its dirty flag
15251525
// until the deprovisioning operation is completed.
1526-
func (p *ironicProvisioner) Delete() (result provisioner.Result, err error) {
1526+
func (p *ironicProvisioner) Delete(_ bool) (result provisioner.Result, err error) {
15271527
ironicNode, err := p.getNode()
15281528
if err != nil {
15291529
if errors.Is(err, provisioner.ErrNeedsRegistration) {
@@ -1605,10 +1605,10 @@ func (p *ironicProvisioner) Delete() (result provisioner.Result, err error) {
16051605
// for the target system. It may be called multiple times,
16061606
// and should return true for its dirty flag until the
16071607
// deletion operation is completed.
1608-
func (p *ironicProvisioner) Detach() (result provisioner.Result, err error) {
1608+
func (p *ironicProvisioner) Detach(force bool) (result provisioner.Result, err error) {
16091609
// Currently the same behavior as Delete()
16101610
p.log.Info("removing the node for detachment", "node", p.nodeID)
1611-
return p.Delete()
1611+
return p.Delete(force)
16121612
}
16131613

16141614
// softPowerOffUnsupportedError is returned when the BMC does not

pkg/provisioner/provisioner.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,14 @@ type Provisioner interface {
182182
// Delete removes the host from the provisioning system. It may be
183183
// called multiple times, and should return true for its dirty
184184
// flag until the deletion operation is completed.
185-
Delete() (result Result, err error)
185+
Delete(force bool) (result Result, err error)
186186

187187
// Detach removes the host from the provisioning system.
188188
// Similar to Delete, but ensures non-interruptive behavior
189189
// for the target system. It may be called multiple times,
190190
// and should return true for its dirty flag until the
191191
// deletion operation is completed.
192-
Detach() (result Result, err error)
192+
Detach(force bool) (result Result, err error)
193193

194194
// PowerOn ensures the server is powered on independently of any image
195195
// provisioning operation.

0 commit comments

Comments
 (0)