Skip to content

Commit e086f2b

Browse files
committed
Add an e2e test for forced detachment during provisioning
Exercise forced detachment followed by deletion, new enrollment and finished provisioning. Signed-off-by: Dmitry Tantsur <dtantsur@protonmail.com>
1 parent 0746217 commit e086f2b

File tree

1 file changed

+298
-0
lines changed

1 file changed

+298
-0
lines changed

test/e2e/forced_detachment_test.go

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
//go:build e2e
2+
// +build e2e
3+
4+
package e2e
5+
6+
import (
7+
"context"
8+
"path"
9+
10+
metal3api "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
11+
. "github.com/onsi/ginkgo/v2"
12+
. "github.com/onsi/gomega"
13+
corev1 "k8s.io/api/core/v1"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/apimachinery/pkg/types"
16+
"sigs.k8s.io/cluster-api/test/framework"
17+
"sigs.k8s.io/cluster-api/util/deprecated/v1beta1/patch"
18+
)
19+
20+
var _ = Describe("Start provisioning, force detachment, delete and recreate, provision, detach again and delete", Label("required", "provision", "detach", "force-detach"),
21+
func() {
22+
var (
23+
specName = "force-detach"
24+
secretName = "bmc-credentials"
25+
namespace *corev1.Namespace
26+
cancelWatches context.CancelFunc
27+
)
28+
29+
BeforeEach(func() {
30+
namespace, cancelWatches = framework.CreateNamespaceAndWatchEvents(ctx, framework.CreateNamespaceAndWatchEventsInput{
31+
Creator: clusterProxy.GetClient(),
32+
ClientSet: clusterProxy.GetClientSet(),
33+
Name: specName,
34+
LogFolder: artifactFolder,
35+
IgnoreAlreadyExists: true,
36+
})
37+
})
38+
39+
It("starts provisioning, forces detachment, removes the host", func() {
40+
By("Creating a secret with BMH credentials")
41+
bmcCredentialsData := map[string]string{
42+
"username": bmc.User,
43+
"password": bmc.Password,
44+
}
45+
CreateSecret(ctx, clusterProxy.GetClient(), namespace.Name, secretName, bmcCredentialsData)
46+
47+
By("Creating a BMH with inspection and cleaning disabled")
48+
bmh := metal3api.BareMetalHost{
49+
ObjectMeta: metav1.ObjectMeta{
50+
Name: specName,
51+
Namespace: namespace.Name,
52+
},
53+
Spec: metal3api.BareMetalHostSpec{
54+
Online: true,
55+
BMC: metal3api.BMCDetails{
56+
Address: bmc.Address,
57+
CredentialsName: "bmc-credentials",
58+
DisableCertificateVerification: bmc.DisableCertificateVerification,
59+
},
60+
BootMode: metal3api.BootMode(e2eConfig.GetVariable("BOOT_MODE")),
61+
BootMACAddress: bmc.BootMacAddress,
62+
AutomatedCleaningMode: metal3api.CleaningModeDisabled,
63+
InspectionMode: metal3api.InspectionModeDisabled,
64+
},
65+
}
66+
err := clusterProxy.GetClient().Create(ctx, &bmh)
67+
Expect(err).NotTo(HaveOccurred())
68+
69+
By("Waiting for the BMH to become available")
70+
WaitForBmhInProvisioningState(ctx, WaitForBmhInProvisioningStateInput{
71+
Client: clusterProxy.GetClient(),
72+
Bmh: bmh,
73+
State: metal3api.StateAvailable,
74+
}, e2eConfig.GetIntervals(specName, "wait-available")...)
75+
76+
By("Patching the BMH to test provisioning")
77+
err = PatchBMHForProvisioning(ctx, PatchBMHForProvisioningInput{
78+
client: clusterProxy.GetClient(),
79+
bmh: &bmh,
80+
bmc: bmc,
81+
e2eConfig: e2eConfig,
82+
namespace: namespace.Name,
83+
})
84+
Expect(err).NotTo(HaveOccurred())
85+
86+
By("Waiting for the BMH to be in provisioning state")
87+
WaitForBmhInProvisioningState(ctx, WaitForBmhInProvisioningStateInput{
88+
Client: clusterProxy.GetClient(),
89+
Bmh: bmh,
90+
State: metal3api.StateProvisioning,
91+
}, e2eConfig.GetIntervals(specName, "wait-provisioning")...)
92+
93+
By("Retrieving the latest BMH object")
94+
err = clusterProxy.GetClient().Get(ctx, types.NamespacedName{
95+
Name: bmh.Name,
96+
Namespace: bmh.Namespace,
97+
}, &bmh)
98+
Expect(err).NotTo(HaveOccurred())
99+
100+
By("Adding the detached annotation")
101+
helper, err := patch.NewHelper(&bmh, clusterProxy.GetClient())
102+
Expect(err).NotTo(HaveOccurred())
103+
104+
if bmh.Annotations == nil {
105+
bmh.Annotations = make(map[string]string, 1)
106+
}
107+
bmh.Annotations["baremetalhost.metal3.io/detached"] = "{\"force\": true}"
108+
109+
Expect(helper.Patch(ctx, &bmh)).To(Succeed())
110+
111+
By("Waiting for the BMH to be detached")
112+
WaitForBmhInOperationalStatus(ctx, WaitForBmhInOperationalStatusInput{
113+
Client: clusterProxy.GetClient(),
114+
Bmh: bmh,
115+
State: metal3api.OperationalStatusDetached,
116+
UndesiredStates: []metal3api.OperationalStatus{
117+
metal3api.OperationalStatusError,
118+
},
119+
}, e2eConfig.GetIntervals(specName, "wait-detached")...)
120+
121+
By("Retrieving and checking the latest BMH object")
122+
err = clusterProxy.GetClient().Get(ctx, types.NamespacedName{
123+
Name: bmh.Name,
124+
Namespace: bmh.Namespace,
125+
}, &bmh)
126+
Expect(err).NotTo(HaveOccurred())
127+
Expect(bmh.Status.Provisioning.State).To(Equal(metal3api.StateProvisioning))
128+
129+
By("Deleting the BMH")
130+
err = clusterProxy.GetClient().Delete(ctx, &bmh)
131+
Expect(err).NotTo(HaveOccurred())
132+
133+
By("Waiting for the BMH to be deleted")
134+
WaitForBmhDeleted(ctx, WaitForBmhDeletedInput{
135+
Client: clusterProxy.GetClient(),
136+
BmhName: bmh.Name,
137+
Namespace: bmh.Namespace,
138+
UndesiredStates: []metal3api.ProvisioningState{
139+
metal3api.StateDeprovisioning,
140+
metal3api.StatePoweringOffBeforeDelete,
141+
},
142+
}, e2eConfig.GetIntervals(specName, "wait-bmh-deleted")...)
143+
})
144+
145+
It("starts provisioning, forces detachment, re-attaches and finishes provisioning", func() {
146+
By("Creating a secret with BMH credentials")
147+
bmcCredentialsData := map[string]string{
148+
"username": bmc.User,
149+
"password": bmc.Password,
150+
}
151+
CreateSecret(ctx, clusterProxy.GetClient(), namespace.Name, secretName, bmcCredentialsData)
152+
153+
By("Creating a BMH with inspection and cleaning disabled")
154+
bmh := metal3api.BareMetalHost{
155+
ObjectMeta: metav1.ObjectMeta{
156+
Name: specName,
157+
Namespace: namespace.Name,
158+
},
159+
Spec: metal3api.BareMetalHostSpec{
160+
Online: true,
161+
BMC: metal3api.BMCDetails{
162+
Address: bmc.Address,
163+
CredentialsName: "bmc-credentials",
164+
DisableCertificateVerification: bmc.DisableCertificateVerification,
165+
},
166+
BootMode: metal3api.BootMode(e2eConfig.GetVariable("BOOT_MODE")),
167+
BootMACAddress: bmc.BootMacAddress,
168+
AutomatedCleaningMode: metal3api.CleaningModeDisabled,
169+
InspectionMode: metal3api.InspectionModeDisabled,
170+
},
171+
}
172+
err := clusterProxy.GetClient().Create(ctx, &bmh)
173+
Expect(err).NotTo(HaveOccurred())
174+
175+
By("Waiting for the BMH to become available")
176+
WaitForBmhInProvisioningState(ctx, WaitForBmhInProvisioningStateInput{
177+
Client: clusterProxy.GetClient(),
178+
Bmh: bmh,
179+
State: metal3api.StateAvailable,
180+
}, e2eConfig.GetIntervals(specName, "wait-available")...)
181+
182+
By("Patching the BMH to test provisioning")
183+
err = PatchBMHForProvisioning(ctx, PatchBMHForProvisioningInput{
184+
client: clusterProxy.GetClient(),
185+
bmh: &bmh,
186+
bmc: bmc,
187+
e2eConfig: e2eConfig,
188+
namespace: namespace.Name,
189+
})
190+
Expect(err).NotTo(HaveOccurred())
191+
192+
By("Waiting for the BMH to be in provisioning state")
193+
WaitForBmhInProvisioningState(ctx, WaitForBmhInProvisioningStateInput{
194+
Client: clusterProxy.GetClient(),
195+
Bmh: bmh,
196+
State: metal3api.StateProvisioning,
197+
}, e2eConfig.GetIntervals(specName, "wait-provisioning")...)
198+
199+
By("Retrieving the latest BMH object")
200+
err = clusterProxy.GetClient().Get(ctx, types.NamespacedName{
201+
Name: bmh.Name,
202+
Namespace: bmh.Namespace,
203+
}, &bmh)
204+
Expect(err).NotTo(HaveOccurred())
205+
206+
By("Adding the detached annotation")
207+
helper, err := patch.NewHelper(&bmh, clusterProxy.GetClient())
208+
Expect(err).NotTo(HaveOccurred())
209+
210+
if bmh.Annotations == nil {
211+
bmh.Annotations = make(map[string]string, 1)
212+
}
213+
bmh.Annotations["baremetalhost.metal3.io/detached"] = "{\"force\": true}"
214+
215+
Expect(helper.Patch(ctx, &bmh)).To(Succeed())
216+
217+
By("Waiting for the BMH to be detached")
218+
WaitForBmhInOperationalStatus(ctx, WaitForBmhInOperationalStatusInput{
219+
Client: clusterProxy.GetClient(),
220+
Bmh: bmh,
221+
State: metal3api.OperationalStatusDetached,
222+
UndesiredStates: []metal3api.OperationalStatus{
223+
metal3api.OperationalStatusError,
224+
},
225+
}, e2eConfig.GetIntervals(specName, "wait-detached")...)
226+
227+
By("Removing the detached annotation")
228+
helper, err = patch.NewHelper(&bmh, clusterProxy.GetClient())
229+
Expect(err).NotTo(HaveOccurred())
230+
231+
delete(bmh.Annotations, "baremetalhost.metal3.io/detached")
232+
Expect(helper.Patch(ctx, &bmh)).To(Succeed())
233+
234+
By("Waiting for the BMH to become provisioned")
235+
WaitForBmhInProvisioningState(ctx, WaitForBmhInProvisioningStateInput{
236+
Client: clusterProxy.GetClient(),
237+
Bmh: bmh,
238+
State: metal3api.StateProvisioned,
239+
}, e2eConfig.GetIntervals(specName, "wait-provisioned")...)
240+
241+
// The ssh check is not possible in all situations (e.g. fixture) so it can be skipped
242+
if e2eConfig.GetVariable("SSH_CHECK_PROVISIONED") == "true" {
243+
By("Verifying the node booting from disk")
244+
PerformSSHBootCheck(e2eConfig, "disk", bmc.IPAddress)
245+
} else {
246+
Logf("WARNING: Skipping SSH check since SSH_CHECK_PROVISIONED != true")
247+
}
248+
249+
By("Retrieving the latest BMH object")
250+
err = clusterProxy.GetClient().Get(ctx, types.NamespacedName{
251+
Name: bmh.Name,
252+
Namespace: bmh.Namespace,
253+
}, &bmh)
254+
Expect(err).NotTo(HaveOccurred())
255+
256+
By("Adding the detached annotation")
257+
helper, err = patch.NewHelper(&bmh, clusterProxy.GetClient())
258+
Expect(err).NotTo(HaveOccurred())
259+
260+
if bmh.Annotations == nil {
261+
bmh.Annotations = make(map[string]string, 1)
262+
}
263+
// Making sure that forced detachment works in the normal case too
264+
bmh.Annotations["baremetalhost.metal3.io/detached"] = "{\"force\": true}"
265+
266+
Expect(helper.Patch(ctx, &bmh)).To(Succeed())
267+
268+
By("Waiting for the BMH to be detached")
269+
WaitForBmhInOperationalStatus(ctx, WaitForBmhInOperationalStatusInput{
270+
Client: clusterProxy.GetClient(),
271+
Bmh: bmh,
272+
State: metal3api.OperationalStatusDetached,
273+
UndesiredStates: []metal3api.OperationalStatus{
274+
metal3api.OperationalStatusError,
275+
},
276+
}, e2eConfig.GetIntervals(specName, "wait-detached")...)
277+
278+
By("Delete BMH")
279+
err = clusterProxy.GetClient().Delete(ctx, &bmh)
280+
Expect(err).NotTo(HaveOccurred())
281+
282+
By("Waiting for the BMH to be deleted")
283+
WaitForBmhDeleted(ctx, WaitForBmhDeletedInput{
284+
Client: clusterProxy.GetClient(),
285+
BmhName: bmh.Name,
286+
Namespace: bmh.Namespace,
287+
}, e2eConfig.GetIntervals(specName, "wait-bmh-deleted")...)
288+
})
289+
290+
AfterEach(func() {
291+
CollectSerialLogs(bmc.Name, path.Join(artifactFolder, specName))
292+
DumpResources(ctx, e2eConfig, clusterProxy, path.Join(artifactFolder, specName))
293+
if !skipCleanup {
294+
isNamespaced := e2eConfig.GetBoolVariable("NAMESPACE_SCOPED")
295+
Cleanup(ctx, clusterProxy, namespace, cancelWatches, isNamespaced, e2eConfig.GetIntervals("default", "wait-namespace-deleted")...)
296+
}
297+
})
298+
})

0 commit comments

Comments
 (0)