Skip to content

Commit 54a8707

Browse files
committed
Onboarding: Remove additional aggregates
If the offboarding failed for any reason, we might be have left-over associations with other aggregates. For robustness reasons, let's handle that case here as well.
1 parent 58f4775 commit 54a8707

File tree

2 files changed

+128
-32
lines changed

2 files changed

+128
-32
lines changed

internal/controller/onboarding_controller.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"errors"
2323
"fmt"
2424
"net/http"
25+
"slices"
2526
"strings"
2627
"time"
2728

@@ -205,6 +206,22 @@ func (r *OnboardingController) initialOnboarding(ctx context.Context, hv *kvmv1.
205206
return fmt.Errorf("failed to agg to test aggregate %w", err)
206207
}
207208

209+
var errs []error
210+
for aggregateName, aggregate := range aggs {
211+
if aggregateName == testAggregateName || aggregateName == zone {
212+
continue
213+
}
214+
if slices.Contains(aggregate.Hosts, host) {
215+
if err := removeFromAggregate(ctx, r.computeClient, aggs, host, aggregateName); err != nil {
216+
errs = append(errs, err)
217+
}
218+
}
219+
}
220+
221+
if len(errs) > 0 {
222+
return fmt.Errorf("failed to remove host %v from aggregates due to %w", host, errors.Join(errs...))
223+
}
224+
208225
// The service may be forced down previously due to an HA event,
209226
// so we need to ensure it not only enabled, but also not forced to be down.
210227
falseVal := false

internal/controller/onboarding_controller_test.go

Lines changed: 111 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,32 @@ var _ = Describe("Onboarding Controller", func() {
6262
]
6363
}`
6464

65+
aggregatesBodyUnexpected = `{
66+
"aggregates": [
67+
{
68+
"name": "test-az",
69+
"availability_zone": "test-az",
70+
"deleted": false,
71+
"id": 100001,
72+
"hosts": []
73+
},
74+
{
75+
"name": "tenant_filter_tests",
76+
"availability_zone": "",
77+
"deleted": false,
78+
"id": 99,
79+
"hosts": []
80+
},
81+
{
82+
"name": "unexpected",
83+
"availability_zone": "",
84+
"deleted": false,
85+
"id": -1,
86+
"hosts": ["test-host"]
87+
}
88+
]
89+
}`
90+
6591
aggregatesBodySetup = `{
6692
"aggregates": [
6793
{
@@ -249,12 +275,6 @@ var _ = Describe("Onboarding Controller", func() {
249275
w.WriteHeader(http.StatusOK)
250276
Expect(fmt.Fprintf(w, HypervisorWithServers, serviceId, "", hypervisorName)).ToNot(BeNil())
251277
})
252-
fakeServer.Mux.HandleFunc("GET /os-aggregates", func(w http.ResponseWriter, r *http.Request) {
253-
w.Header().Add("Content-Type", "application/json")
254-
w.WriteHeader(http.StatusOK)
255-
_, err := fmt.Fprint(w, aggregatesBodyInitial)
256-
Expect(err).NotTo(HaveOccurred())
257-
})
258278

259279
fakeServer.Mux.HandleFunc("POST /os-aggregates/100001/action", func(w http.ResponseWriter, r *http.Request) {
260280
w.Header().Add("Content-Type", "application/json")
@@ -278,34 +298,93 @@ var _ = Describe("Onboarding Controller", func() {
278298
})
279299
})
280300

281-
It("should set the Service- and HypervisorId from Nova", func(ctx SpecContext) {
282-
By("Reconciling the created resource")
283-
err := reconcileLoop(ctx, 1)
284-
Expect(err).NotTo(HaveOccurred())
285-
hv := &kvmv1.Hypervisor{}
286-
Expect(k8sClient.Get(ctx, namespacedName, hv)).To(Succeed())
287-
Expect(hv.Status.ServiceID).To(Equal(serviceId))
288-
Expect(hv.Status.HypervisorID).To(Equal(hypervisorId))
301+
When("it is a clean setup", func() {
302+
BeforeEach(func() {
303+
fakeServer.Mux.HandleFunc("GET /os-aggregates", func(w http.ResponseWriter, r *http.Request) {
304+
w.Header().Add("Content-Type", "application/json")
305+
w.WriteHeader(http.StatusOK)
306+
_, err := fmt.Fprint(w, aggregatesBodyInitial)
307+
Expect(err).NotTo(HaveOccurred())
308+
})
309+
})
310+
311+
It("should set the Service- and HypervisorId from Nova", func(ctx SpecContext) {
312+
By("Reconciling the created resource")
313+
err := reconcileLoop(ctx, 1)
314+
Expect(err).NotTo(HaveOccurred())
315+
hv := &kvmv1.Hypervisor{}
316+
Expect(k8sClient.Get(ctx, namespacedName, hv)).To(Succeed())
317+
Expect(hv.Status.ServiceID).To(Equal(serviceId))
318+
Expect(hv.Status.HypervisorID).To(Equal(hypervisorId))
319+
})
320+
321+
It("should update the status accordingly", func(ctx SpecContext) {
322+
By("Reconciling the created resource")
323+
err := reconcileLoop(ctx, 2)
324+
Expect(err).NotTo(HaveOccurred())
325+
hv := &kvmv1.Hypervisor{}
326+
Expect(k8sClient.Get(ctx, namespacedName, hv)).To(Succeed())
327+
Expect(hv.Status.Conditions).To(ContainElements(
328+
SatisfyAll(
329+
HaveField("Type", kvmv1.ConditionTypeReady),
330+
HaveField("Status", metav1.ConditionFalse),
331+
HaveField("Reason", ConditionReasonOnboarding),
332+
),
333+
SatisfyAll(
334+
HaveField("Type", ConditionTypeOnboarding),
335+
HaveField("Status", metav1.ConditionTrue),
336+
HaveField("Reason", ConditionReasonTesting),
337+
),
338+
))
339+
})
289340
})
290341

291-
It("should update the status accordingly", func(ctx SpecContext) {
292-
By("Reconciling the created resource")
293-
err := reconcileLoop(ctx, 2)
294-
Expect(err).NotTo(HaveOccurred())
295-
hv := &kvmv1.Hypervisor{}
296-
Expect(k8sClient.Get(ctx, namespacedName, hv)).To(Succeed())
297-
Expect(hv.Status.Conditions).To(ContainElements(
298-
SatisfyAll(
299-
HaveField("Type", kvmv1.ConditionTypeReady),
300-
HaveField("Status", metav1.ConditionFalse),
301-
HaveField("Reason", ConditionReasonOnboarding),
302-
),
303-
SatisfyAll(
304-
HaveField("Type", ConditionTypeOnboarding),
305-
HaveField("Status", metav1.ConditionTrue),
306-
HaveField("Reason", ConditionReasonTesting),
307-
),
308-
))
342+
When("it the host is already in an unexpected aggregate", func() {
343+
BeforeEach(func() {
344+
fakeServer.Mux.HandleFunc("GET /os-aggregates", func(w http.ResponseWriter, r *http.Request) {
345+
w.Header().Add("Content-Type", "application/json")
346+
w.WriteHeader(http.StatusOK)
347+
_, err := fmt.Fprint(w, aggregatesBodyUnexpected)
348+
Expect(err).NotTo(HaveOccurred())
349+
})
350+
351+
fakeServer.Mux.HandleFunc("POST /os-aggregates/-1/action", func(w http.ResponseWriter, r *http.Request) {
352+
w.Header().Add("Content-Type", "application/json")
353+
w.WriteHeader(http.StatusOK)
354+
_, err := fmt.Fprint(w, addedHostToTestBody)
355+
Expect(err).NotTo(HaveOccurred())
356+
})
357+
})
358+
359+
It("should set the Service- and HypervisorId from Nova", func(ctx SpecContext) {
360+
By("Reconciling the created resource")
361+
err := reconcileLoop(ctx, 1)
362+
Expect(err).NotTo(HaveOccurred())
363+
hv := &kvmv1.Hypervisor{}
364+
Expect(k8sClient.Get(ctx, namespacedName, hv)).To(Succeed())
365+
Expect(hv.Status.ServiceID).To(Equal(serviceId))
366+
Expect(hv.Status.HypervisorID).To(Equal(hypervisorId))
367+
})
368+
369+
It("should update the status accordingly", func(ctx SpecContext) {
370+
By("Reconciling the created resource")
371+
err := reconcileLoop(ctx, 2)
372+
Expect(err).NotTo(HaveOccurred())
373+
hv := &kvmv1.Hypervisor{}
374+
Expect(k8sClient.Get(ctx, namespacedName, hv)).To(Succeed())
375+
Expect(hv.Status.Conditions).To(ContainElements(
376+
SatisfyAll(
377+
HaveField("Type", kvmv1.ConditionTypeReady),
378+
HaveField("Status", metav1.ConditionFalse),
379+
HaveField("Reason", ConditionReasonOnboarding),
380+
),
381+
SatisfyAll(
382+
HaveField("Type", ConditionTypeOnboarding),
383+
HaveField("Status", metav1.ConditionTrue),
384+
HaveField("Reason", ConditionReasonTesting),
385+
),
386+
))
387+
})
309388
})
310389
})
311390

0 commit comments

Comments
 (0)