Skip to content

Commit 6bb6c61

Browse files
committed
Aggregates: Only reconcile after onboarding and before decomissioning
Onboarding needs to set its own aggregates for testing purposes, and decomissining needs to remove the aggregates. To avoid these controllers fighting, let's limit this controller to the actual active lifetime of the hypervisor. Unfortunately, that means we have to reconcile on every status update.
1 parent eb8dd3e commit 6bb6c61

File tree

2 files changed

+94
-56
lines changed

2 files changed

+94
-56
lines changed

internal/controller/aggregates_controller.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import (
3030
"sigs.k8s.io/controller-runtime/pkg/builder"
3131
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
3232
logger "sigs.k8s.io/controller-runtime/pkg/log"
33-
"sigs.k8s.io/controller-runtime/pkg/predicate"
3433

3534
"github.com/gophercloud/gophercloud/v2"
3635
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates"
@@ -63,6 +62,12 @@ func (ac *AggregatesController) Reconcile(ctx context.Context, req ctrl.Request)
6362
return ctrl.Result{}, k8sclient.IgnoreNotFound(err)
6463
}
6564

65+
/// On- and off-boarding need to mess with the aggregates, so let's get out of their way
66+
if !meta.IsStatusConditionFalse(hv.Status.Conditions, ConditionTypeOnboarding) ||
67+
meta.IsStatusConditionTrue(hv.Status.Conditions, kvmv1.ConditionTypeTerminating) {
68+
return ctrl.Result{}, nil
69+
}
70+
6671
if slices.Equal(hv.Spec.Aggregates, hv.Status.Aggregates) {
6772
// Nothing to be done
6873
return ctrl.Result{}, nil
@@ -154,7 +159,6 @@ func (ac *AggregatesController) SetupWithManager(mgr ctrl.Manager) error {
154159
return ctrl.NewControllerManagedBy(mgr).
155160
Named(AggregatesControllerName).
156161
For(&kvmv1.Hypervisor{}, builder.WithPredicates(utils.LifecycleEnabledPredicate)).
157-
WithEventFilter(predicate.GenerationChangedPredicate{}).
158162
Complete(ac)
159163
}
160164

internal/controller/aggregates_controller_test.go

Lines changed: 88 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,15 @@ import (
3535
)
3636

3737
var _ = Describe("AggregatesController", func() {
38-
var (
39-
tc *AggregatesController
40-
fakeServer testhelper.FakeServer
41-
)
42-
const EOF = "EOF"
43-
const AggregateListBodyEmpty = `
38+
const (
39+
EOF = "EOF"
40+
AggregateListBodyEmpty = `
4441
{
4542
"aggregates": []
4643
}
4744
`
4845

49-
const AggregateListBodyFull = `
46+
AggregateListBodyFull = `
5047
{
5148
"aggregates": [
5249
{
@@ -67,7 +64,7 @@ var _ = Describe("AggregatesController", func() {
6764
}
6865
`
6966

70-
const AggregatesPostBody = `
67+
AggregatesPostBody = `
7168
{
7269
"aggregate": {
7370
"name": "test-aggregate1",
@@ -77,7 +74,7 @@ var _ = Describe("AggregatesController", func() {
7774
}
7875
}`
7976

80-
const AggregateRemoveHostBody = `
77+
AggregateRemoveHostBody = `
8178
{
8279
"aggregate": {
8380
"name": "test-aggregate3",
@@ -87,7 +84,7 @@ var _ = Describe("AggregatesController", func() {
8784
}
8885
}`
8986

90-
const AggregateAddHostBody = `
87+
AggregateAddHostBody = `
9188
{
9289
"aggregate": {
9390
"name": "test-aggregate1",
@@ -99,47 +96,63 @@ var _ = Describe("AggregatesController", func() {
9996
"id": 42
10097
}
10198
}`
99+
)
102100

101+
var (
102+
tc *AggregatesController
103+
fakeServer testhelper.FakeServer
104+
hypervisorName = types.NamespacedName{Name: "hv-test"}
105+
)
103106
// Setup and teardown
104107

105108
BeforeEach(func(ctx context.Context) {
106109
By("Setting up the OpenStack http mock server")
107110
fakeServer = testhelper.SetupHTTP()
111+
DeferCleanup(fakeServer.Teardown)
112+
113+
hypervisor := &kvmv1.Hypervisor{
114+
ObjectMeta: v1.ObjectMeta{
115+
Name: hypervisorName.Name,
116+
},
117+
Spec: kvmv1.HypervisorSpec{
118+
LifecycleEnabled: true,
119+
},
120+
}
121+
Expect(k8sClient.Create(ctx, hypervisor)).To(Succeed())
122+
Expect(k8sClient.Get(ctx, hypervisorName, hypervisor)).To(Succeed())
123+
meta.SetStatusCondition(&hypervisor.Status.Conditions, v1.Condition{
124+
Type: ConditionTypeOnboarding,
125+
Status: v1.ConditionFalse,
126+
Reason: "dontcare",
127+
Message: "dontcare",
128+
})
129+
Expect(k8sClient.Status().Update(ctx, hypervisor)).To(Succeed())
108130

109131
By("Creating the AggregatesController")
110132
tc = &AggregatesController{
111133
Client: k8sClient,
112134
Scheme: k8sClient.Scheme(),
113135
computeClient: client.ServiceClient(fakeServer),
114136
}
115-
})
116137

117-
AfterEach(func() {
118-
By("Tearing down the OpenStack http mock server")
119-
fakeServer.Teardown()
138+
DeferCleanup(func(ctx context.Context) {
139+
Expect(tc.Client.Delete(ctx, hypervisor)).To(Succeed())
140+
})
141+
})
120142

121-
By("Deleting the Hypervisor resource")
122-
hypervisor := &kvmv1.Hypervisor{}
123-
Expect(tc.Client.Get(ctx, types.NamespacedName{Name: "hv-test", Namespace: "default"}, hypervisor)).To(Succeed())
124-
Expect(tc.Client.Delete(ctx, hypervisor)).To(Succeed())
143+
JustBeforeEach(func(ctx context.Context) {
144+
_, err := tc.Reconcile(ctx, ctrl.Request{NamespacedName: hypervisorName})
145+
Expect(err).NotTo(HaveOccurred())
125146
})
126147

127148
// Tests
128-
129149
Context("Adding new Aggregate", func() {
130150
BeforeEach(func() {
131-
By("Creating a Hypervisor resource")
132-
hypervisor := &kvmv1.Hypervisor{
133-
ObjectMeta: v1.ObjectMeta{
134-
Name: "hv-test",
135-
Namespace: "default",
136-
},
137-
Spec: kvmv1.HypervisorSpec{
138-
LifecycleEnabled: true,
139-
Aggregates: []string{"test-aggregate1"},
140-
},
141-
}
142-
Expect(k8sClient.Create(ctx, hypervisor)).To(Succeed())
151+
By("Setting a missing aggregate")
152+
hypervisor := &kvmv1.Hypervisor{}
153+
Expect(k8sClient.Get(ctx, hypervisorName, hypervisor)).To(Succeed())
154+
hypervisor.Spec.Aggregates = []string{"test-aggregate1"}
155+
Expect(k8sClient.Update(ctx, hypervisor)).To(Succeed())
143156

144157
// Mock resourceproviders.GetAggregates
145158
fakeServer.Mux.HandleFunc("GET /os-aggregates", func(w http.ResponseWriter, r *http.Request) {
@@ -176,32 +189,18 @@ var _ = Describe("AggregatesController", func() {
176189
})
177190
})
178191

179-
It("should update Aggregates and set status condition when Aggregates differ", func() {
180-
req := ctrl.Request{NamespacedName: types.NamespacedName{Name: "hv-test", Namespace: "default"}}
181-
_, err := tc.Reconcile(ctx, req)
182-
Expect(err).NotTo(HaveOccurred())
183-
192+
It("should update Aggregates and set status condition as Aggregates differ", func() {
184193
updated := &kvmv1.Hypervisor{}
185-
Expect(tc.Client.Get(ctx, req.NamespacedName, updated)).To(Succeed())
194+
Expect(tc.Client.Get(ctx, hypervisorName, updated)).To(Succeed())
186195
Expect(updated.Status.Aggregates).To(ContainElements("test-aggregate1"))
187196
Expect(meta.IsStatusConditionTrue(updated.Status.Conditions, ConditionTypeAggregatesUpdated)).To(BeTrue())
188197
})
189198
})
190199

191200
Context("Removing Aggregate", func() {
192201
BeforeEach(func() {
193-
By("Creating a Hypervisor resource")
194-
hypervisor := &kvmv1.Hypervisor{
195-
ObjectMeta: v1.ObjectMeta{
196-
Name: "hv-test",
197-
Namespace: "default",
198-
},
199-
Spec: kvmv1.HypervisorSpec{
200-
LifecycleEnabled: true,
201-
},
202-
}
203-
Expect(k8sClient.Create(ctx, hypervisor)).To(Succeed())
204-
202+
hypervisor := &kvmv1.Hypervisor{}
203+
Expect(k8sClient.Get(ctx, hypervisorName, hypervisor)).To(Succeed())
205204
// update status to have existing aggregate
206205
hypervisor.Status.Aggregates = []string{"test-aggregate2", "test-aggregate3"}
207206
Expect(k8sClient.Status().Update(ctx, hypervisor)).To(Succeed())
@@ -235,14 +234,49 @@ var _ = Describe("AggregatesController", func() {
235234
})
236235

237236
It("should update Aggregates and set status condition when Aggregates differ", func() {
238-
req := ctrl.Request{NamespacedName: types.NamespacedName{Name: "hv-test", Namespace: "default"}}
239-
_, err := tc.Reconcile(ctx, req)
240-
Expect(err).NotTo(HaveOccurred())
241-
242237
updated := &kvmv1.Hypervisor{}
243-
Expect(tc.Client.Get(ctx, req.NamespacedName, updated)).To(Succeed())
238+
Expect(tc.Client.Get(ctx, hypervisorName, updated)).To(Succeed())
244239
Expect(updated.Status.Aggregates).To(BeEmpty())
245240
Expect(meta.IsStatusConditionTrue(updated.Status.Conditions, ConditionTypeAggregatesUpdated)).To(BeTrue())
246241
})
247242
})
243+
244+
Context("before onboarding", func() {
245+
BeforeEach(func() {
246+
hypervisor := &kvmv1.Hypervisor{}
247+
Expect(k8sClient.Get(ctx, hypervisorName, hypervisor)).To(Succeed())
248+
// Remove the onboarding condition
249+
hypervisor.Status.Conditions = []v1.Condition{}
250+
Expect(k8sClient.Status().Update(ctx, hypervisor)).To(Succeed())
251+
})
252+
253+
It("should neither update Aggregates and nor set status condition", func() {
254+
updated := &kvmv1.Hypervisor{}
255+
Expect(tc.Client.Get(ctx, hypervisorName, updated)).To(Succeed())
256+
Expect(updated.Status.Aggregates).To(BeEmpty())
257+
Expect(meta.IsStatusConditionTrue(updated.Status.Conditions, ConditionTypeAggregatesUpdated)).To(BeFalse())
258+
})
259+
})
260+
261+
Context("when terminating", func() {
262+
BeforeEach(func() {
263+
hypervisor := &kvmv1.Hypervisor{}
264+
Expect(k8sClient.Get(ctx, hypervisorName, hypervisor)).To(Succeed())
265+
// Remove the onboarding condition
266+
meta.SetStatusCondition(&hypervisor.Status.Conditions, v1.Condition{
267+
Type: kvmv1.ConditionTypeTerminating,
268+
Status: v1.ConditionTrue,
269+
Reason: "dontcare",
270+
Message: "dontcare",
271+
})
272+
Expect(k8sClient.Status().Update(ctx, hypervisor)).To(Succeed())
273+
})
274+
275+
It("should neither update Aggregates and nor set status condition", func() {
276+
updated := &kvmv1.Hypervisor{}
277+
Expect(tc.Client.Get(ctx, hypervisorName, updated)).To(Succeed())
278+
Expect(updated.Status.Aggregates).To(BeEmpty())
279+
Expect(meta.IsStatusConditionTrue(updated.Status.Conditions, ConditionTypeAggregatesUpdated)).To(BeFalse())
280+
})
281+
})
248282
})

0 commit comments

Comments
 (0)