Skip to content

Commit 40509de

Browse files
committed
setting nodestate annotation with 'sriovnetwork.openshift.io/use-external-drainer=true' in case exteranl drainer is enabled
The motivation is for external drainer verification, that SRIOV operator is set with external drainer Signed-off-by: Ido Heyvi <[email protected]>
1 parent c9e86ec commit 40509de

File tree

9 files changed

+137
-32
lines changed

9 files changed

+137
-32
lines changed

bindata/manifests/daemon/daemonset.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ spec:
169169
value: "{{.ClusterType}}"
170170
- name: DEV_MODE
171171
value: "{{.DevMode}}"
172+
{{- if .UseExternalDrainer }}
173+
- name: USE_EXTERNAL_DRAINER
174+
value: "{{.UseExternalDrainer}}"
175+
{{- end }}
172176
resources:
173177
requests:
174178
cpu: 100m

controllers/drain_controller.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ func (dr *DrainReconcile) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
106106
return ctrl.Result{}, nil
107107
}
108108

109+
// check if use-external-drainer is set for a specific node state
110+
if utils.ObjectHasAnnotation(nodeNetworkState, constants.NodeStateExternalDrainerAnnotation, "true") {
111+
reqLogger.Info(`nodestate is set with external drainer annotation, don't requeue the request.
112+
Draining will be done externally`, "nodeState", req.Name)
113+
return ctrl.Result{}, nil
114+
}
115+
109116
// create the drain state annotation if it doesn't exist in the sriovNetworkNodeState object
110117
nodeStateDrainAnnotationCurrent, currentNodeStateExist, err := dr.ensureAnnotationExists(ctx, nodeNetworkState, constants.NodeStateDrainAnnotationCurrent)
111118
if err != nil {

controllers/drain_controller_test.go

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ var _ = Describe("Drain Controller", Ordered, func() {
115115
Context("when there is only one node", func() {
116116

117117
It("should drain single node on drain require", func(ctx context.Context) {
118-
node, nodeState := createNode(ctx, "node1")
118+
node, nodeState := createNode(ctx, "node1", false)
119119

120120
simulateDaemonSetAnnotation(node, constants.DrainRequired)
121121

@@ -129,7 +129,7 @@ var _ = Describe("Drain Controller", Ordered, func() {
129129
})
130130

131131
It("should not drain on reboot for single node", func(ctx context.Context) {
132-
node, nodeState := createNode(ctx, "node1")
132+
node, nodeState := createNode(ctx, "node1", false)
133133

134134
simulateDaemonSetAnnotation(node, constants.RebootRequired)
135135

@@ -142,8 +142,8 @@ var _ = Describe("Drain Controller", Ordered, func() {
142142
})
143143

144144
It("should drain on reboot for multiple node", func(ctx context.Context) {
145-
node, nodeState := createNode(ctx, "node1")
146-
createNode(ctx, "node2")
145+
node, nodeState := createNode(ctx, "node1", false)
146+
createNode(ctx, "node2", false)
147147

148148
simulateDaemonSetAnnotation(node, constants.RebootRequired)
149149

@@ -159,9 +159,9 @@ var _ = Describe("Drain Controller", Ordered, func() {
159159
Context("when there are multiple nodes", func() {
160160

161161
It("should drain nodes serially with default pool selector", func(ctx context.Context) {
162-
node1, nodeState1 := createNode(ctx, "node1")
163-
node2, nodeState2 := createNode(ctx, "node2")
164-
node3, nodeState3 := createNode(ctx, "node3")
162+
node1, nodeState1 := createNode(ctx, "node1", false)
163+
node2, nodeState2 := createNode(ctx, "node2", false)
164+
node3, nodeState3 := createNode(ctx, "node3", false)
165165

166166
// Two nodes require to drain at the same time
167167
simulateDaemonSetAnnotation(node1, constants.DrainRequired)
@@ -199,9 +199,9 @@ var _ = Describe("Drain Controller", Ordered, func() {
199199
})
200200

201201
It("should drain nodes in parallel with a custom pool selector", func(ctx context.Context) {
202-
node1, nodeState1 := createNode(ctx, "node1")
203-
node2, nodeState2 := createNode(ctx, "node2")
204-
node3, nodeState3 := createNode(ctx, "node3")
202+
node1, nodeState1 := createNode(ctx, "node1", false)
203+
node2, nodeState2 := createNode(ctx, "node2", false)
204+
node3, nodeState3 := createNode(ctx, "node3", false)
205205

206206
maxun := intstr.Parse("2")
207207
poolConfig := &sriovnetworkv1.SriovNetworkPoolConfig{}
@@ -250,9 +250,9 @@ var _ = Describe("Drain Controller", Ordered, func() {
250250
})
251251

252252
It("should drain nodes in parallel with a custom pool selector and honor MaxUnavailable", func(ctx context.Context) {
253-
node1, nodeState1 := createNode(ctx, "node1")
254-
node2, nodeState2 := createNode(ctx, "node2")
255-
node3, nodeState3 := createNode(ctx, "node3")
253+
node1, nodeState1 := createNode(ctx, "node1", false)
254+
node2, nodeState2 := createNode(ctx, "node2", false)
255+
node3, nodeState3 := createNode(ctx, "node3", false)
256256

257257
maxun := intstr.Parse("2")
258258
poolConfig := &sriovnetworkv1.SriovNetworkPoolConfig{}
@@ -275,9 +275,9 @@ var _ = Describe("Drain Controller", Ordered, func() {
275275
})
276276

277277
It("should drain all nodes in parallel with a custom pool using nil in max unavailable", func(ctx context.Context) {
278-
node1, nodeState1 := createNode(ctx, "node1")
279-
node2, nodeState2 := createNode(ctx, "node2")
280-
node3, nodeState3 := createNode(ctx, "node3")
278+
node1, nodeState1 := createNode(ctx, "node1", false)
279+
node2, nodeState2 := createNode(ctx, "node2", false)
280+
node3, nodeState3 := createNode(ctx, "node3", false)
281281

282282
poolConfig := &sriovnetworkv1.SriovNetworkPoolConfig{}
283283
poolConfig.SetNamespace(testNamespace)
@@ -303,7 +303,7 @@ var _ = Describe("Drain Controller", Ordered, func() {
303303
})
304304

305305
It("should drain in parallel nodes from two different pools, one custom and one default", func() {
306-
node1, nodeState1 := createNode(ctx, "node1")
306+
node1, nodeState1 := createNode(ctx, "node1", false)
307307
node2, nodeState2 := createNodeWithLabel(ctx, "node2", "pool")
308308
createPodOnNode(ctx, "test-node-2", "node2")
309309

@@ -326,7 +326,7 @@ var _ = Describe("Drain Controller", Ordered, func() {
326326
})
327327

328328
It("should select all the nodes to drain in parallel when the selector is empty", func() {
329-
node1, nodeState1 := createNode(ctx, "node3")
329+
node1, nodeState1 := createNode(ctx, "node3", false)
330330
node2, nodeState2 := createNodeWithLabel(ctx, "node4", "pool")
331331
createPodOnNode(ctx, "test-empty-1", "node3")
332332
createPodOnNode(ctx, "test-empty-2", "node4")
@@ -408,7 +408,7 @@ func simulateDaemonSetAnnotation(node *corev1.Node, drainAnnotationValue string)
408408
ToNot(HaveOccurred())
409409
}
410410

411-
func createNode(ctx context.Context, nodeName string) (*corev1.Node, *sriovnetworkv1.SriovNetworkNodeState) {
411+
func createNode(ctx context.Context, nodeName string, useExternalDrainer bool) (*corev1.Node, *sriovnetworkv1.SriovNetworkNodeState) {
412412
node := corev1.Node{
413413
ObjectMeta: metav1.ObjectMeta{
414414
Name: nodeName,
@@ -432,6 +432,10 @@ func createNode(ctx context.Context, nodeName string) (*corev1.Node, *sriovnetwo
432432
},
433433
}
434434

435+
if useExternalDrainer {
436+
nodeState.Annotations[constants.NodeStateExternalDrainerAnnotation] = "true"
437+
}
438+
435439
Expect(k8sClient.Create(ctx, &node)).ToNot(HaveOccurred())
436440
Expect(k8sClient.Create(ctx, &nodeState)).ToNot(HaveOccurred())
437441

controllers/sriovoperatorconfig_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ func (r *SriovOperatorConfigReconciler) syncConfigDaemonSet(ctx context.Context,
196196
data.Data["ReleaseVersion"] = os.Getenv("RELEASEVERSION")
197197
data.Data["ClusterType"] = vars.ClusterType
198198
data.Data["DevMode"] = os.Getenv("DEV_MODE")
199+
data.Data["UseExternalDrainer"] = vars.UseExternalDrainer
199200
data.Data["ImagePullSecrets"] = GetImagePullSecrets()
200201
if dc.Spec.ConfigurationMode == sriovnetworkv1.SystemdConfigurationMode {
201202
data.Data["UsedSystemdMode"] = true

deployment/sriov-network-operator-chart/templates/operator.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ spec:
7878
value: {{ .Values.operator.metricsExporter.certificates.secretName }}
7979
- name: METRICS_EXPORTER_KUBE_RBAC_PROXY_IMAGE
8080
value: {{ .Values.images.metricsExporterKubeRbacProxy }}
81-
- name: USE_EXTERNAL_DRAINER
82-
value: {{ .Values.operator.externalDrainer.enabled | quote }}
81+
{{- if .Values.operator.externalDrainer.enabled }}
82+
- name: USE_EXTERNAL_DRAINER
83+
value: {{ .Values.operator.externalDrainer.enabled | quote }}
84+
{{- end }}
8385
{{- if .Values.operator.metricsExporter.prometheusOperator.enabled }}
8486
- name: METRICS_EXPORTER_PROMETHEUS_OPERATOR_ENABLED
8587
value: {{ .Values.operator.metricsExporter.prometheusOperator.enabled | quote}}

main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,7 @@ func main() {
299299
func setupDrainController(mgr ctrl.Manager, restConfig *rest.Config,
300300
platformsHelper platforms.Interface, scheme *runtime.Scheme) error {
301301
if vars.UseExternalDrainer {
302-
setupLog.Info("internal drain controller is disabled, draining will be done externally by the maintenance operator")
303-
return nil
302+
setupLog.Info("'UseExternalDrainer' is set, draining will be done externally")
304303
}
305304

306305
// we need a client that doesn't use the local cache for the objects

pkg/consts/constants.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,15 @@ const (
7676
SriovDevicePluginLabelEnabled = "Enabled"
7777
SriovDevicePluginLabelDisabled = "Disabled"
7878

79-
NodeDrainAnnotation = "sriovnetwork.openshift.io/state"
80-
NodeStateDrainAnnotation = "sriovnetwork.openshift.io/desired-state"
81-
NodeStateDrainAnnotationCurrent = "sriovnetwork.openshift.io/current-state"
82-
DrainIdle = "Idle"
83-
DrainRequired = "Drain_Required"
84-
RebootRequired = "Reboot_Required"
85-
Draining = "Draining"
86-
DrainComplete = "DrainComplete"
79+
NodeDrainAnnotation = "sriovnetwork.openshift.io/state"
80+
NodeStateDrainAnnotation = "sriovnetwork.openshift.io/desired-state"
81+
NodeStateDrainAnnotationCurrent = "sriovnetwork.openshift.io/current-state"
82+
NodeStateExternalDrainerAnnotation = "sriovnetwork.openshift.io/use-external-drainer"
83+
DrainIdle = "Idle"
84+
DrainRequired = "Drain_Required"
85+
RebootRequired = "Reboot_Required"
86+
Draining = "Draining"
87+
DrainComplete = "DrainComplete"
8788

8889
SyncStatusSucceeded = "Succeeded"
8990
SyncStatusFailed = "Failed"

pkg/daemon/daemon.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,16 @@ func (dn *NodeReconciler) apply(ctx context.Context, desiredNodeState *sriovnetw
408408
return ctrl.Result{}, err
409409
}
410410

411+
// remove external drainer nodestate annotation
412+
err = utils.AnnotateObject(ctx, desiredNodeState,
413+
consts.NodeStateExternalDrainerAnnotation, "", dn.client)
414+
if err != nil {
415+
if !errors.IsNotFound(err) {
416+
reqLogger.Error(err, "failed to remove node external drainer annotation")
417+
return ctrl.Result{}, err
418+
}
419+
}
420+
411421
reqLogger.Info("sync succeeded")
412422
syncStatus := consts.SyncStatusSucceeded
413423
lastSyncError := ""
@@ -532,6 +542,16 @@ func (dn *NodeReconciler) handleDrain(ctx context.Context, desiredNodeState *sri
532542
return false, nil
533543
}
534544

545+
// add external drainer nodestate annotation if flag is enabled
546+
if vars.UseExternalDrainer {
547+
err := utils.AnnotateObject(ctx, desiredNodeState,
548+
consts.NodeStateExternalDrainerAnnotation, "true", dn.client)
549+
if err != nil {
550+
funcLog.Error(err, "failed to add node external drainer annotation")
551+
return false, err
552+
}
553+
}
554+
535555
// annotate both node and node state with drain or reboot
536556
annotation := consts.DrainRequired
537557
if reqReboot {

pkg/daemon/daemon_test.go

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ var (
5353
const (
5454
waitTime = 30 * time.Minute
5555
retryTime = 5 * time.Second
56+
nodeName = "node1"
5657
)
5758

5859
var _ = Describe("Daemon Controller", Ordered, func() {
@@ -151,7 +152,7 @@ var _ = Describe("Daemon Controller", Ordered, func() {
151152
daemonReconciler = createDaemon(hostHelper, platformHelper, featureGates, []string{})
152153
startDaemon(daemonReconciler)
153154

154-
_, nodeState = createNode("node1")
155+
_, nodeState = createNode(nodeName)
155156
})
156157

157158
AfterAll(func() {
@@ -253,6 +254,11 @@ var _ = Describe("Daemon Controller", Ordered, func() {
253254
ToNot(HaveOccurred())
254255

255256
g.Expect(nodeState.Annotations[constants.NodeStateDrainAnnotation]).To(Equal(constants.DrainRequired))
257+
// verify that external drainer annotation doesn't exist
258+
node := &corev1.Node{}
259+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: nodeName}, node)).
260+
ToNot(HaveOccurred())
261+
g.Expect(nodeState.Annotations).NotTo(ContainElement(constants.NodeStateExternalDrainerAnnotation))
256262
}, waitTime, retryTime).Should(Succeed())
257263

258264
patchAnnotation(nodeState, constants.NodeStateDrainAnnotationCurrent, constants.DrainComplete)
@@ -334,6 +340,67 @@ var _ = Describe("Daemon Controller", Ordered, func() {
334340
eventuallySyncStatusEqual(nodeState, constants.SyncStatusSucceeded)
335341
assertLastStatusTransitionsContains(nodeState, 2, constants.SyncStatusInProgress)
336342
})
343+
344+
It("Should apply external drainer annotation when useExternalDrainer is true", func(ctx context.Context) {
345+
DeferCleanup(func(x bool) { vars.UseExternalDrainer = x }, vars.UseExternalDrainer)
346+
vars.UseExternalDrainer = true
347+
348+
discoverSriovReturn.Store(&[]sriovnetworkv1.InterfaceExt{
349+
{
350+
Name: "eno1",
351+
Driver: "ice",
352+
PciAddress: "0000:16:00.0",
353+
DeviceID: "1593",
354+
Vendor: "8086",
355+
EswitchMode: "legacy",
356+
LinkAdminState: "up",
357+
LinkSpeed: "10000 Mb/s",
358+
LinkType: "ETH",
359+
Mac: "aa:bb:cc:dd:ee:ff",
360+
Mtu: 1500,
361+
TotalVfs: 2,
362+
NumVfs: 0,
363+
},
364+
})
365+
366+
By("waiting for state to be in progress")
367+
eventuallySyncStatusEqual(nodeState, constants.SyncStatusInProgress)
368+
369+
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: nodeState.Namespace, Name: nodeState.Name}, nodeState)
370+
Expect(err).ToNot(HaveOccurred())
371+
372+
By("waiting to require drain")
373+
EventuallyWithOffset(1, func(g Gomega) {
374+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Namespace: nodeState.Namespace, Name: nodeState.Name}, nodeState)).
375+
ToNot(HaveOccurred())
376+
377+
g.Expect(nodeState.Annotations[constants.NodeStateDrainAnnotation]).To(Equal(constants.DrainRequired))
378+
}, waitTime, retryTime).Should(Succeed())
379+
380+
// Validate status
381+
EventuallyWithOffset(1, func(g Gomega) {
382+
// get node by name
383+
node := &corev1.Node{}
384+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: nodeName}, node)).
385+
ToNot(HaveOccurred())
386+
g.Expect(nodeState.Annotations[constants.NodeStateExternalDrainerAnnotation]).To(Equal("true"))
387+
}, waitTime, retryTime).Should(Succeed())
388+
389+
patchAnnotation(nodeState, constants.NodeStateDrainAnnotationCurrent, constants.DrainComplete)
390+
// Validate status
391+
EventuallyWithOffset(1, func(g Gomega) {
392+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Namespace: nodeState.Namespace, Name: nodeState.Name}, nodeState)).
393+
ToNot(HaveOccurred())
394+
395+
g.Expect(nodeState.Status.SyncStatus).To(Equal(constants.SyncStatusInProgress))
396+
node := &corev1.Node{}
397+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: nodeName}, node)).
398+
ToNot(HaveOccurred())
399+
// verify that external drainer annotation is removed
400+
g.Expect(nodeState.Annotations).NotTo(ContainElement(constants.NodeStateExternalDrainerAnnotation))
401+
}, waitTime, retryTime).Should(Succeed())
402+
403+
})
337404
})
338405
})
339406

0 commit comments

Comments
 (0)