Repository: metal3-io/baremetal-operator
Pull request #1433 (2023-11-07 16:42:31)

🌱 Refactor handling the provisioner's ready status
In preparation for the future microversion negotiation handling:
1) Remove the unnecessary ironicDependenciesChecker abstraction.
   It probably made more sense when Inspector was used explicitly.
2) Change tests to make Ready the default state (and NotReady
   an explicit override for the few tests that need it).


Patch:
diff --git a/pkg/provisioner/ironic/dependencies.go b/pkg/provisioner/ironic/dependencies.go
index 06d3662f..757cfd4f 100644
--- a/pkg/provisioner/ironic/dependencies.go
+++ b/pkg/provisioner/ironic/dependencies.go
@@ -1,57 +1,31 @@
 package ironic
 
 import (
-	"strings"
-	"time"
-
-	"github.com/go-logr/logr"
-	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/openstack/baremetal/v1/drivers"
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-const (
-	checkRequeueDelay = time.Second * 10
-)
-
-type ironicDependenciesChecker struct {
-	client *gophercloud.ServiceClient
-	log    logr.Logger
-}
-
-func newIronicDependenciesChecker(client *gophercloud.ServiceClient, log logr.Logger) *ironicDependenciesChecker {
-	return &ironicDependenciesChecker{
-		client: client,
-		log:    log,
-	}
-}
-
-func (i *ironicDependenciesChecker) IsReady() (result bool, err error) {
-	ready := i.checkEndpoint(i.client)
+// IsReady checks if the provisioning backend is available
+func (p *ironicProvisioner) IsReady() (result bool, err error) {
+	p.debugLog.Info("verifying ironic provisioner dependencies")
+	ready := p.checkEndpoint()
 	if ready {
-		ready, err = i.checkIronicConductor()
+		ready, err = p.checkIronicConductor()
 	}
 	return ready, err
 }
 
-func (i *ironicDependenciesChecker) checkEndpoint(client *gophercloud.ServiceClient) (ready bool) {
-
-	// NOTE: Some versions of Ironic inspector returns 404 for /v1/ but 200 for /v1,
-	// which seems to be the default behavior for Flask. Remove the trailing slash
-	// from the client endpoint.
-	endpoint := strings.TrimSuffix(client.Endpoint, "/")
-
-	_, err := client.Get(endpoint, nil, nil)
+func (p *ironicProvisioner) checkEndpoint() (ready bool) {
+	_, err := p.client.Get(p.client.Endpoint, nil, nil)
 	if err != nil {
-		i.log.Info("error caught while checking endpoint", "endpoint", client.Endpoint, "error", err)
+		p.log.Info("error caught while checking endpoint", "endpoint", p.client.Endpoint, "error", err)
 	}
 
 	return err == nil
 }
 
-func (i *ironicDependenciesChecker) checkIronicConductor() (ready bool, err error) {
-
-	pager := drivers.ListDrivers(i.client, drivers.ListDriversOpts{
+func (p *ironicProvisioner) checkIronicConductor() (ready bool, err error) {
+	pager := drivers.ListDrivers(p.client, drivers.ListDriversOpts{
 		Detail: false,
 	})
 	err = pager.Err
diff --git a/pkg/provisioner/ironic/ironic.go b/pkg/provisioner/ironic/ironic.go
index 2dc7e6d1..84049089 100644
--- a/pkg/provisioner/ironic/ironic.go
+++ b/pkg/provisioner/ironic/ironic.go
@@ -1979,14 +1979,6 @@ func ironicNodeName(objMeta metav1.ObjectMeta) string {
 	return objMeta.Namespace + nameSeparator + objMeta.Name
 }
 
-// IsReady checks if the provisioning backend is available
-func (p *ironicProvisioner) IsReady() (result bool, err error) {
-	p.debugLog.Info("verifying ironic provisioner dependencies")
-
-	checker := newIronicDependenciesChecker(p.client, p.log)
-	return checker.IsReady()
-}
-
 func (p *ironicProvisioner) HasCapacity() (result bool, err error) {
 	bmcAccess, err := p.bmcAccess()
 	if err != nil {


Test patch:
diff --git a/pkg/provisioner/ironic/adopt_test.go b/pkg/provisioner/ironic/adopt_test.go
index d203528f..0fcd5d08 100644
--- a/pkg/provisioner/ironic/adopt_test.go
+++ b/pkg/provisioner/ironic/adopt_test.go
@@ -27,7 +27,7 @@ func TestAdopt(t *testing.T) {
 	}{
 		{
 			name: "node-in-enroll",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Enroll),
 				UUID:           nodeUUID,
 			}),
@@ -47,7 +47,7 @@ func TestAdopt(t *testing.T) {
 		},
 		{
 			name: "node-in-adopting",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Adopting),
 				UUID:           nodeUUID,
 			}),
@@ -57,7 +57,7 @@ func TestAdopt(t *testing.T) {
 		},
 		{
 			name: "node-in-verifying",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Verifying),
 				UUID:           nodeUUID,
 			}),
@@ -67,7 +67,7 @@ func TestAdopt(t *testing.T) {
 		},
 		{
 			name: "node-in-AdoptFail",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.AdoptFail),
 				UUID:           nodeUUID,
 			}),
@@ -88,7 +88,7 @@ func TestAdopt(t *testing.T) {
 		},
 		{
 			name: "node-in-Active",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Active),
 				UUID:           nodeUUID,
 			}),
@@ -97,7 +97,7 @@ func TestAdopt(t *testing.T) {
 		},
 		{
 			name: "node-in-Maintenance",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Active),
 				UUID:           nodeUUID,
 				Maintenance:    true,
@@ -109,7 +109,7 @@ func TestAdopt(t *testing.T) {
 		},
 		{
 			name: "node-in-Fault",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Active),
 				UUID:           nodeUUID,
 				Maintenance:    true,
diff --git a/pkg/provisioner/ironic/configdrive_test.go b/pkg/provisioner/ironic/configdrive_test.go
index d2f6a1d0..9bb8d472 100644
--- a/pkg/provisioner/ironic/configdrive_test.go
+++ b/pkg/provisioner/ironic/configdrive_test.go
@@ -122,7 +122,7 @@ func TestEmpty(t *testing.T) {
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
-			ironic := testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic := testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Active),
 				UUID:           nodeUUID,
 			})
diff --git a/pkg/provisioner/ironic/delete_test.go b/pkg/provisioner/ironic/delete_test.go
index 84ee54c1..82d4c1f0 100644
--- a/pkg/provisioner/ironic/delete_test.go
+++ b/pkg/provisioner/ironic/delete_test.go
@@ -90,13 +90,13 @@ func deleteTest(t *testing.T, detach bool) {
 			name: "host-not-found",
 
 			hostName: "worker-0",
-			ironic:   testserver.NewIronic(t).Ready().NodeError(nodeUUID, http.StatusGatewayTimeout),
+			ironic:   testserver.NewIronic(t).NodeError(nodeUUID, http.StatusGatewayTimeout),
 
 			expectedError: "failed to find node by ID 33ce8659-7400-4c68-9535-d10766f07a58: Gateway Timeout.*",
 		},
 		{
 			name:   "not-ironic-node",
-			ironic: testserver.NewIronic(t).Ready().NoNode(nodeUUID).NoNode("myns" + nameSeparator + "myhost").NoNode("myhost"),
+			ironic: testserver.NewIronic(t).NoNode(nodeUUID).NoNode("myns" + nameSeparator + "myhost").NoNode("myhost"),
 		},
 		{
 			name: "available-node",
diff --git a/pkg/provisioner/ironic/inspecthardware_test.go b/pkg/provisioner/ironic/inspecthardware_test.go
index ab3d4db7..f643e057 100644
--- a/pkg/provisioner/ironic/inspecthardware_test.go
+++ b/pkg/provisioner/ironic/inspecthardware_test.go
@@ -89,7 +89,7 @@ func TestInspectHardware(t *testing.T) {
 		},
 		{
 			name: "introspection-status-retry-on-wait",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:           nodeUUID,
 				ProvisionState: "inspect wait",
 			}),
@@ -99,7 +99,7 @@ func TestInspectHardware(t *testing.T) {
 		},
 		{
 			name: "introspection-status-retry-on-inspecting",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:           nodeUUID,
 				ProvisionState: "inspecting",
 			}),
@@ -109,7 +109,7 @@ func TestInspectHardware(t *testing.T) {
 		},
 		{
 			name: "introspection-failed",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:           nodeUUID,
 				ProvisionState: "inspect failed",
 				LastError:      "Timeout",
@@ -119,7 +119,7 @@ func TestInspectHardware(t *testing.T) {
 		},
 		{
 			name: "introspection-aborted",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:           nodeUUID,
 				ProvisionState: "inspect failed",
 				LastError:      "Inspection was aborted by request.",
@@ -135,7 +135,7 @@ func TestInspectHardware(t *testing.T) {
 		},
 		{
 			name: "inspection-in-progress - forceReboot",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:           nodeUUID,
 				ProvisionState: string(nodes.InspectWait),
 			}).WithNodeStatesProvisionUpdate(nodeUUID),
@@ -147,7 +147,7 @@ func TestInspectHardware(t *testing.T) {
 		},
 		{
 			name: "inspection-failed",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:           nodeUUID,
 				ProvisionState: string(nodes.InspectFail),
 			}),
@@ -156,7 +156,7 @@ func TestInspectHardware(t *testing.T) {
 		},
 		{
 			name: "inspection-failed force",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:           nodeUUID,
 				ProvisionState: string(nodes.InspectFail),
 			}).NodeUpdate(nodes.Node{
@@ -172,7 +172,7 @@ func TestInspectHardware(t *testing.T) {
 		},
 		{
 			name: "inspection-forceReboot",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:           nodeUUID,
 				ProvisionState: string(nodes.InspectWait),
 			}).WithNodeStatesProvisionUpdate(nodeUUID),
@@ -184,7 +184,7 @@ func TestInspectHardware(t *testing.T) {
 		},
 		{
 			name: "inspection-complete",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:           nodeUUID,
 				ProvisionState: string(nodes.Manageable),
 			}).WithInventory(nodeUUID, nodes.InventoryData{
diff --git a/pkg/provisioner/ironic/power_test.go b/pkg/provisioner/ironic/power_test.go
index 2b097e24..98df3ea3 100644
--- a/pkg/provisioner/ironic/power_test.go
+++ b/pkg/provisioner/ironic/power_test.go
@@ -30,14 +30,14 @@ func TestPowerOn(t *testing.T) {
 	}{
 		{
 			name: "node-already-power-on",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState: powerOn,
 				UUID:       nodeUUID,
 			}),
 		},
 		{
 			name: "waiting-for-target-power-on",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState:       powerOff,
 				TargetPowerState: powerOn,
 				UUID:             nodeUUID,
@@ -57,7 +57,7 @@ func TestPowerOn(t *testing.T) {
 		},
 		{
 			name: "power-on wait for Provisioning state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState:           powerOff,
 				TargetPowerState:     powerOff,
 				TargetProvisionState: string(nodes.TargetDeleted),
@@ -68,7 +68,7 @@ func TestPowerOn(t *testing.T) {
 		},
 		{
 			name: "power-on wait for locked host",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState:           powerOff,
 				TargetPowerState:     powerOff,
 				TargetProvisionState: "",
@@ -79,7 +79,7 @@ func TestPowerOn(t *testing.T) {
 		},
 		{
 			name: "power-on with LastError",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState:       powerOff,
 				TargetPowerState: powerOff,
 				UUID:             nodeUUID,
@@ -92,7 +92,7 @@ func TestPowerOn(t *testing.T) {
 		{
 			name:  "power-on with LastError",
 			force: true,
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState:       powerOff,
 				TargetPowerState: powerOff,
 				UUID:             nodeUUID,
@@ -153,14 +153,14 @@ func TestPowerOff(t *testing.T) {
 	}{
 		{
 			name: "node-already-power-off",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState: powerOff,
 				UUID:       nodeUUID,
 			}),
 		},
 		{
 			name: "waiting-for-target-power-off",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState:       powerOn,
 				TargetPowerState: powerOff,
 				UUID:             nodeUUID,
@@ -192,7 +192,7 @@ func TestPowerOff(t *testing.T) {
 		},
 		{
 			name: "power-off wait for Provisioning state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState:           powerOn,
 				TargetProvisionState: string(nodes.TargetDeleted),
 				UUID:                 nodeUUID,
@@ -202,7 +202,7 @@ func TestPowerOff(t *testing.T) {
 		},
 		{
 			name: "power-off wait for locked host",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				PowerState:           powerOn,
 				TargetProvisionState: "",
 				UUID:                 nodeUUID,
@@ -297,7 +297,7 @@ func TestSoftPowerOffFallback(t *testing.T) {
 		PowerState: powerOn,
 		UUID:       nodeUUID,
 	}
-	ironic := testserver.NewIronic(t).Ready().Node(node).WithNodeStatesPowerUpdate(nodeUUID, http.StatusBadRequest)
+	ironic := testserver.NewIronic(t).Node(node).WithNodeStatesPowerUpdate(nodeUUID, http.StatusBadRequest)
 	ironic.Start()
 	defer ironic.Stop()
 
diff --git a/pkg/provisioner/ironic/provision_test.go b/pkg/provisioner/ironic/provision_test.go
index c39b885e..121b0d99 100644
--- a/pkg/provisioner/ironic/provision_test.go
+++ b/pkg/provisioner/ironic/provision_test.go
@@ -97,7 +97,7 @@ func TestProvision(t *testing.T) {
 		},
 		{
 			name: "active state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Active),
 				UUID:           nodeUUID,
 			}),
@@ -106,7 +106,7 @@ func TestProvision(t *testing.T) {
 		},
 		{
 			name: "other state: Cleaning",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Cleaning),
 				UUID:           nodeUUID,
 			}),
@@ -115,7 +115,7 @@ func TestProvision(t *testing.T) {
 		},
 		{
 			name: "other state: Deploy Wait",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.DeployWait),
 				UUID:           nodeUUID,
 			}),
@@ -243,7 +243,7 @@ func TestDeprovision(t *testing.T) {
 		},
 		{
 			name: "deleting state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Deleting),
 				UUID:           nodeUUID,
 			}),
@@ -252,7 +252,7 @@ func TestDeprovision(t *testing.T) {
 		},
 		{
 			name: "cleaning state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.Cleaning),
 				UUID:           nodeUUID,
 			}),
@@ -261,7 +261,7 @@ func TestDeprovision(t *testing.T) {
 		},
 		{
 			name: "cleanWait state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				ProvisionState: string(nodes.CleanWait),
 				UUID:           nodeUUID,
 			}),
diff --git a/pkg/provisioner/ironic/ready_test.go b/pkg/provisioner/ironic/ready_test.go
index a7007fcf..e1ec1f3f 100644
--- a/pkg/provisioner/ironic/ready_test.go
+++ b/pkg/provisioner/ironic/ready_test.go
@@ -23,14 +23,14 @@ func TestProvisionerIsReady(t *testing.T) {
 	}{
 		{
 			name:                "IsReady",
-			ironic:              testserver.NewIronic(t).Ready().WithDrivers(),
-			expectedIronicCalls: "/v1;/v1/drivers;",
+			ironic:              testserver.NewIronic(t).WithDrivers(),
+			expectedIronicCalls: "/v1/;/v1/drivers;",
 			expectedIsReady:     true,
 		},
 		{
 			name:                "NoDriversLoaded",
-			ironic:              testserver.NewIronic(t).Ready(),
-			expectedIronicCalls: "/v1;/v1/drivers;",
+			ironic:              testserver.NewIronic(t),
+			expectedIronicCalls: "/v1/;/v1/drivers;",
 		},
 		{
 			name:            "IronicDown",
@@ -40,13 +40,13 @@ func TestProvisionerIsReady(t *testing.T) {
 			name:                "IronicNotOk",
 			ironic:              testserver.NewIronic(t).NotReady(http.StatusInternalServerError),
 			expectedIsReady:     false,
-			expectedIronicCalls: "/v1;",
+			expectedIronicCalls: "/v1/;",
 		},
 		{
 			name:                "IronicNotOkAndNotExpected",
 			ironic:              testserver.NewIronic(t).NotReady(http.StatusBadGateway),
 			expectedIsReady:     false,
-			expectedIronicCalls: "/v1;",
+			expectedIronicCalls: "/v1/;",
 		},
 	}
 
diff --git a/pkg/provisioner/ironic/testserver/ironic.go b/pkg/provisioner/ironic/testserver/ironic.go
index a287228c..85a6dcb3 100644
--- a/pkg/provisioner/ironic/testserver/ironic.go
+++ b/pkg/provisioner/ironic/testserver/ironic.go
@@ -20,15 +20,28 @@ type IronicMock struct {
 
 // NewIronic builds an ironic mock server
 func NewIronic(t *testing.T) *IronicMock {
-
+	server := New(t, "ironic").AddDefaultResponse("/v1/?", "", http.StatusOK, versionedRootResult)
 	return &IronicMock{
-		MockServer:   New(t, "ironic"),
+		MockServer:   server,
 		CreatedNodes: 0,
 	}
 }
 
 const validateResult = `{"boot": {"result": true}, "deploy": {"result": true}, "power": {"result": true}}`
 
+// NOTE(dtantsur): the actual result is much longer, but we only potentially care about versions.
+const versionedRootResult = `
+{
+ "id": "v1",
+ "version": {
+    "id": "v1",
+    "links": [ { "href": "/v1/", "rel": "self" } ],
+    "status": "CURRENT",
+    "min_version": "1.1",
+    "version": "1.86"
+  }
+}`
+
 // WithDefaultResponses sets a valid answer for all the API calls
 func (m *IronicMock) WithDefaultResponses() *IronicMock {
 	m.AddDefaultResponseJSON("/v1/nodes/{id}", "", http.StatusOK, nodes.Node{
@@ -38,7 +51,6 @@ func (m *IronicMock) WithDefaultResponses() *IronicMock {
 	m.AddDefaultResponse("/v1/nodes/{id}/states/power", "", http.StatusAccepted, "{}")
 	m.AddDefaultResponse("/v1/nodes/{id}/states/raid", "", http.StatusNoContent, "{}")
 	m.AddDefaultResponse("/v1/nodes/{id}/validate", "", http.StatusOK, validateResult)
-	m.Ready()
 
 	return m
 }
@@ -51,15 +63,9 @@ func (m *IronicMock) Endpoint() string {
 	return m.MockServer.Endpoint()
 }
 
-// Ready configures the server with a valid response for /v1
-func (m *IronicMock) Ready() *IronicMock {
-	m.ResponseWithCode("/v1", "{}", http.StatusOK)
-	return m
-}
-
-// NotReady configures the server with an error response for /v1
+// NotReady configures the server with an error response for /v1/ overriding the default response
 func (m *IronicMock) NotReady(errorCode int) *IronicMock {
-	m.ErrorResponse("/v1", errorCode)
+	m.ErrorResponse("/v1/", errorCode)
 	return m
 }
 
diff --git a/pkg/provisioner/ironic/updatehardwarestate_test.go b/pkg/provisioner/ironic/updatehardwarestate_test.go
index ab2b13b6..c7700821 100644
--- a/pkg/provisioner/ironic/updatehardwarestate_test.go
+++ b/pkg/provisioner/ironic/updatehardwarestate_test.go
@@ -29,14 +29,14 @@ func TestUpdateHardwareState(t *testing.T) {
 	}{
 		{
 			name: "unknown-power-state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID: nodeUUID,
 			}),
 			expectUnreadablePower: true,
 		},
 		{
 			name: "updated-power-on-state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:       nodeUUID,
 				PowerState: "power on",
 			}),
@@ -44,7 +44,7 @@ func TestUpdateHardwareState(t *testing.T) {
 		},
 		{
 			name: "not-updated-power-on-state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:       nodeUUID,
 				PowerState: "power on",
 			}),
@@ -52,7 +52,7 @@ func TestUpdateHardwareState(t *testing.T) {
 		},
 		{
 			name: "updated-power-off-state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:       nodeUUID,
 				PowerState: "power off",
 			}),
@@ -60,7 +60,7 @@ func TestUpdateHardwareState(t *testing.T) {
 		},
 		{
 			name: "not-updated-power-off-state",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:       nodeUUID,
 				PowerState: "power off",
 			}),
@@ -68,7 +68,7 @@ func TestUpdateHardwareState(t *testing.T) {
 		},
 		{
 			name: "no-power",
-			ironic: testserver.NewIronic(t).Ready().Node(nodes.Node{
+			ironic: testserver.NewIronic(t).Node(nodes.Node{
 				UUID:       nodeUUID,
 				PowerState: "None",
 			}),
@@ -79,7 +79,7 @@ func TestUpdateHardwareState(t *testing.T) {
 			name: "node-not-found",
 
 			hostName: "worker-0",
-			ironic:   testserver.NewIronic(t).Ready().NodeError(nodeUUID, http.StatusGatewayTimeout),
+			ironic:   testserver.NewIronic(t).NodeError(nodeUUID, http.StatusGatewayTimeout),
 
 			expectedError: "failed to find node by ID 33ce8659-7400-4c68-9535-d10766f07a58: Gateway Timeout.*",
 
@@ -89,7 +89,7 @@ func TestUpdateHardwareState(t *testing.T) {
 			name: "node-not-found-by-name",
 
 			hostName: "worker-0",
-			ironic:   testserver.NewIronic(t).Ready().NoNode(nodeUUID).NodeError("myns"+nameSeparator+"myhost", http.StatusGatewayTimeout),
+			ironic:   testserver.NewIronic(t).NoNode(nodeUUID).NodeError("myns"+nameSeparator+"myhost", http.StatusGatewayTimeout),
 
 			expectedError: "Host not registered",
 
@@ -97,7 +97,7 @@ func TestUpdateHardwareState(t *testing.T) {
 		},
 		{
 			name:   "not-ironic-node",
-			ironic: testserver.NewIronic(t).Ready().NoNode(nodeUUID).NoNode("myns" + nameSeparator + "myhost").NoNode("myhost"),
+			ironic: testserver.NewIronic(t).NoNode(nodeUUID).NoNode("myns" + nameSeparator + "myhost").NoNode("myhost"),
 
 			expectedError: "Host not registered",
 
diff --git a/pkg/provisioner/ironic/validatemanagementaccess_test.go b/pkg/provisioner/ironic/validatemanagementaccess_test.go
index 819f88ec..5a73c0c3 100644
--- a/pkg/provisioner/ironic/validatemanagementaccess_test.go
+++ b/pkg/provisioner/ironic/validatemanagementaccess_test.go
@@ -25,7 +25,7 @@ func TestValidateManagementAccessNoMAC(t *testing.T) {
 	host.Spec.BootMACAddress = ""
 	host.Status.Provisioning.ID = "" // so we don't lookup by uuid
 
-	ironic := testserver.NewIronic(t).Ready().NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name)
+	ironic := testserver.NewIronic(t).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name)
 	ironic.Start()
 	defer ironic.Stop()
 
@@ -49,7 +49,7 @@ func TestValidateManagementAccessMACOptional(t *testing.T) {
 	host.Spec.BootMACAddress = ""
 
 	// Set up ironic server to return the node
-	ironic := testserver.NewIronic(t).Ready().
+	ironic := testserver.NewIronic(t).
 		Node(nodes.Node{
 			Name: host.Namespace + nameSeparator + host.Name,
 			UUID: host.Status.Provisioning.ID,
@@ -86,7 +86,7 @@ func TestValidateManagementAccessCreateNodeNoImage(t *testing.T) {
 		createdNode = &node
 	}
 
-	ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name)
+	ironic := testserver.NewIronic(t).CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name)
 	ironic.AddDefaultResponse("/v1/nodes/node-0", "PATCH", http.StatusOK, "{}")
 	ironic.Start()
 	defer ironic.Stop()
@@ -120,7 +120,7 @@ func TestValidateManagementAccessCreateWithImage(t *testing.T) {
 		createdNode = &node
 	}
 
-	ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name)
+	ironic := testserver.NewIronic(t).CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name)
 	ironic.AddDefaultResponse("/v1/nodes/node-0", "PATCH", http.StatusOK, "{}")
 	ironic.Start()
 	defer ironic.Stop()
@@ -156,7 +156,7 @@ func TestValidateManagementAccessCreateWithLiveIso(t *testing.T) {
 		createdNode = &node
 	}
 
-	ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name)
+	ironic := testserver.NewIronic(t).CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name)
 	ironic.AddDefaultResponse("/v1/nodes/node-0", "PATCH", http.StatusOK, "{}")
 	ironic.Start()
 	defer ironic.Stop()
@@ -190,7 +190,7 @@ func TestValidateManagementAccessExistingNode(t *testing.T) {
 		t.Fatal("create callback should not be invoked for existing node")
 	}
 
-	ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(nodes.Node{
+	ironic := testserver.NewIronic(t).CreateNodes(createCallback).Node(nodes.Node{
 		Name: host.Namespace + nameSeparator + host.Name,
 		UUID: "uuid",
 	}).NodeUpdate(
@@ -289,7 +289,7 @@ func TestValidateManagementAccessExistingNodeContinue(t *testing.T) {
 				t.Fatal("create callback should not be invoked for existing node")
 			}
 
-			ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(nodes.Node{
+			ironic := testserver.NewIronic(t).CreateNodes(createCallback).Node(nodes.Node{
 				Name:           host.Namespace + nameSeparator + host.Name,
 				UUID:           "uuid", // to match status in host
 				ProvisionState: string(status),
@@ -451,7 +451,7 @@ func TestValidateManagementAccessExistingSteadyStateNoUpdate(t *testing.T) {
 			if provisionState == "" {
 				provisionState = string(nodes.Manageable)
 			}
-			ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(nodes.Node{
+			ironic := testserver.NewIronic(t).CreateNodes(createCallback).Node(nodes.Node{
 				Name:            host.Namespace + nameSeparator + host.Name,
 				UUID:            "uuid", // to match status in host
 				ProvisionState:  provisionState,
@@ -517,7 +517,7 @@ func TestValidateManagementAccessExistingNodeWaiting(t *testing.T) {
 					"test_port":      "42",
 				},
 			}
-			ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(node).NodeUpdate(nodes.Node{
+			ironic := testserver.NewIronic(t).CreateNodes(createCallback).Node(node).NodeUpdate(nodes.Node{
 				UUID: "uuid",
 			}).WithNodeStatesProvisionUpdate(node.UUID)
 			ironic.Start()
@@ -615,7 +615,7 @@ func TestValidateManagementAccessLinkExistingIronicNodeByMAC(t *testing.T) {
 		t.Fatal("create callback should not be invoked for existing node")
 	}
 
-	ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(existingNode).Port(existingNodePort)
+	ironic := testserver.NewIronic(t).CreateNodes(createCallback).Node(existingNode).Port(existingNodePort)
 	ironic.AddDefaultResponse("/v1/nodes/myns"+nameSeparator+"myhost", "GET", http.StatusNotFound, "")
 	ironic.AddDefaultResponse("/v1/nodes/myhost", "GET", http.StatusNotFound, "")
 	ironic.AddDefaultResponse("/v1/nodes/"+existingNode.UUID, "PATCH", http.StatusOK, "{\"uuid\": \"33ce8659-7400-4c68-9535-d10766f07a58\"}")
@@ -657,7 +657,7 @@ func TestValidateManagementAccessExistingPortWithWrongUUID(t *testing.T) {
 		t.Fatal("create callback should not be invoked for existing node")
 	}
 
-	ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(existingNode).Port(existingNodePort)
+	ironic := testserver.NewIronic(t).CreateNodes(createCallback).Node(existingNode).Port(existingNodePort)
 	ironic.AddDefaultResponse("/v1/nodes/myns"+nameSeparator+"myhost", "GET", http.StatusNotFound, "")
 	ironic.AddDefaultResponse("/v1/nodes/myhost", "GET", http.StatusNotFound, "")
 	ironic.AddDefaultResponse("/v1/nodes/random-wrong-id", "GET", http.StatusNotFound, "")
@@ -701,7 +701,7 @@ func TestValidateManagementAccessExistingPortButHasName(t *testing.T) {
 		t.Fatal("create callback should not be invoked for existing node")
 	}
 
-	ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(existingNode).Port(existingNodePort)
+	ironic := testserver.NewIronic(t).CreateNodes(createCallback).Node(existingNode).Port(existingNodePort)
 	ironic.AddDefaultResponse("/v1/nodes/myns"+nameSeparator+"myhost", "GET", http.StatusNotFound, "")
 	ironic.AddDefaultResponse("/v1/nodes/myhost", "GET", http.StatusNotFound, "")
 	ironic.Start()
@@ -738,7 +738,7 @@ func TestValidateManagementAccessAddTwoHostsWithSameMAC(t *testing.T) {
 		t.Fatal("create callback should not be invoked for existing node")
 	}
 
-	ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(existingNode).NodeUpdate(nodes.Node{
+	ironic := testserver.NewIronic(t).CreateNodes(createCallback).Node(existingNode).NodeUpdate(nodes.Node{
 		UUID: "33ce8659-7400-4c68-9535-d10766f07a58",
 	}).Port(existingNodePort)
 	ironic.Start()
@@ -770,7 +770,7 @@ func TestValidateManagementAccessUnsupportedSecureBoot(t *testing.T) {
 	host := makeHost()
 	host.Status.Provisioning.ID = "" // so we don't lookup by uuid
 
-	ironic := testserver.NewIronic(t).Ready().NoNode("myns" + nameSeparator + host.Name).NoNode(host.Name)
+	ironic := testserver.NewIronic(t).NoNode("myns" + nameSeparator + host.Name).NoNode(host.Name)
 	ironic.Start()
 	defer ironic.Stop()
 
@@ -788,7 +788,7 @@ func TestValidateManagementAccessUnsupportedSecureBoot(t *testing.T) {
 }
 
 func TestValidateManagementAccessNoBMCDetails(t *testing.T) {
-	ironic := testserver.NewIronic(t).Ready()
+	ironic := testserver.NewIronic(t)
 	ironic.Start()
 	defer ironic.Stop()
 
@@ -809,7 +809,7 @@ func TestValidateManagementAccessNoBMCDetails(t *testing.T) {
 }
 
 func TestValidateManagementAccessMalformedBMCAddress(t *testing.T) {
-	ironic := testserver.NewIronic(t).Ready()
+	ironic := testserver.NewIronic(t)
 	ironic.Start()
 	defer ironic.Stop()
 
@@ -1160,7 +1160,7 @@ func TestSetExternalURL(t *testing.T) {
 	host := makeHost()
 	host.Spec.BMC.Address = "redfish-virtualmedia://[fe80::fc33:62ff:fe83:8a76]:6233"
 
-	ironic := testserver.NewIronic(t).Ready().
+	ironic := testserver.NewIronic(t).
 		Node(nodes.Node{
 			Name: host.Namespace + nameSeparator + host.Name,
 			UUID: host.Status.Provisioning.ID,
@@ -1190,7 +1190,7 @@ func TestSetExternalURLIPv4(t *testing.T) {
 	host := makeHost()
 	host.Spec.BMC.Address = "redfish-virtualmedia://1.1.1.1:1111"
 
-	ironic := testserver.NewIronic(t).Ready().
+	ironic := testserver.NewIronic(t).
 		Node(nodes.Node{
 			Name: host.Namespace + nameSeparator + host.Name,
 			UUID: host.Status.Provisioning.ID,
@@ -1220,7 +1220,7 @@ func TestSetExternalURLRemoving(t *testing.T) {
 	host := makeHost()
 	host.Spec.BMC.Address = "redfish-virtualmedia://1.1.1.1:1111"
 
-	ironic := testserver.NewIronic(t).Ready().
+	ironic := testserver.NewIronic(t).
 		Node(nodes.Node{
 			Name: host.Namespace + nameSeparator + host.Name,
 			UUID: host.Status.Provisioning.ID,