Skip to content

Commit b2877d5

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 eae0eb2 commit b2877d5

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
@@ -187,6 +187,10 @@ spec:
187187
value: "{{.ClusterType}}"
188188
- name: DEV_MODE
189189
value: "{{.DevMode}}"
190+
{{- if .UseExternalDrainer }}
191+
- name: USE_EXTERNAL_DRAINER
192+
value: "{{.UseExternalDrainer}}"
193+
{{- end }}
190194
resources:
191195
requests:
192196
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
@@ -131,7 +131,7 @@ var _ = Describe("Drain Controller", Ordered, func() {
131131
Context("when there is only one node", func() {
132132

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

136136
simulateDaemonSetAnnotation(node, constants.DrainRequired)
137137

@@ -145,7 +145,7 @@ var _ = Describe("Drain Controller", Ordered, func() {
145145
})
146146

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

150150
simulateDaemonSetAnnotation(node, constants.RebootRequired)
151151

@@ -158,8 +158,8 @@ var _ = Describe("Drain Controller", Ordered, func() {
158158
})
159159

160160
It("should drain on reboot for multiple node", func(ctx context.Context) {
161-
node, nodeState := createNode(ctx, "node1")
162-
createNode(ctx, "node2")
161+
node, nodeState := createNode(ctx, "node1", false)
162+
createNode(ctx, "node2", false)
163163

164164
simulateDaemonSetAnnotation(node, constants.RebootRequired)
165165

@@ -175,9 +175,9 @@ var _ = Describe("Drain Controller", Ordered, func() {
175175
Context("when there are multiple nodes", func() {
176176

177177
It("should drain nodes serially with default pool selector", func(ctx context.Context) {
178-
node1, nodeState1 := createNode(ctx, "node1")
179-
node2, nodeState2 := createNode(ctx, "node2")
180-
node3, nodeState3 := createNode(ctx, "node3")
178+
node1, nodeState1 := createNode(ctx, "node1", false)
179+
node2, nodeState2 := createNode(ctx, "node2", false)
180+
node3, nodeState3 := createNode(ctx, "node3", false)
181181

182182
// Two nodes require to drain at the same time
183183
simulateDaemonSetAnnotation(node1, constants.DrainRequired)
@@ -215,9 +215,9 @@ var _ = Describe("Drain Controller", Ordered, func() {
215215
})
216216

217217
It("should drain nodes in parallel with a custom pool selector", func(ctx context.Context) {
218-
node1, nodeState1 := createNode(ctx, "node1")
219-
node2, nodeState2 := createNode(ctx, "node2")
220-
node3, nodeState3 := createNode(ctx, "node3")
218+
node1, nodeState1 := createNode(ctx, "node1", false)
219+
node2, nodeState2 := createNode(ctx, "node2", false)
220+
node3, nodeState3 := createNode(ctx, "node3", false)
221221

222222
maxun := intstr.Parse("2")
223223
poolConfig := &sriovnetworkv1.SriovNetworkPoolConfig{}
@@ -266,9 +266,9 @@ var _ = Describe("Drain Controller", Ordered, func() {
266266
})
267267

268268
It("should drain nodes in parallel with a custom pool selector and honor MaxUnavailable", func(ctx context.Context) {
269-
node1, nodeState1 := createNode(ctx, "node1")
270-
node2, nodeState2 := createNode(ctx, "node2")
271-
node3, nodeState3 := createNode(ctx, "node3")
269+
node1, nodeState1 := createNode(ctx, "node1", false)
270+
node2, nodeState2 := createNode(ctx, "node2", false)
271+
node3, nodeState3 := createNode(ctx, "node3", false)
272272

273273
maxun := intstr.Parse("2")
274274
poolConfig := &sriovnetworkv1.SriovNetworkPoolConfig{}
@@ -291,9 +291,9 @@ var _ = Describe("Drain Controller", Ordered, func() {
291291
})
292292

293293
It("should drain all nodes in parallel with a custom pool using nil in max unavailable", func(ctx context.Context) {
294-
node1, nodeState1 := createNode(ctx, "node1")
295-
node2, nodeState2 := createNode(ctx, "node2")
296-
node3, nodeState3 := createNode(ctx, "node3")
294+
node1, nodeState1 := createNode(ctx, "node1", false)
295+
node2, nodeState2 := createNode(ctx, "node2", false)
296+
node3, nodeState3 := createNode(ctx, "node3", false)
297297

298298
poolConfig := &sriovnetworkv1.SriovNetworkPoolConfig{}
299299
poolConfig.SetNamespace(testNamespace)
@@ -319,7 +319,7 @@ var _ = Describe("Drain Controller", Ordered, func() {
319319
})
320320

321321
It("should drain in parallel nodes from two different pools, one custom and one default", func() {
322-
node1, nodeState1 := createNode(ctx, "node1")
322+
node1, nodeState1 := createNode(ctx, "node1", false)
323323
node2, nodeState2 := createNodeWithLabel(ctx, "node2", "pool")
324324
createPodOnNode(ctx, "test-node-2", "node2")
325325

@@ -342,7 +342,7 @@ var _ = Describe("Drain Controller", Ordered, func() {
342342
})
343343

344344
It("should select all the nodes to drain in parallel when the selector is empty", func() {
345-
node1, nodeState1 := createNode(ctx, "node3")
345+
node1, nodeState1 := createNode(ctx, "node3", false)
346346
node2, nodeState2 := createNodeWithLabel(ctx, "node4", "pool")
347347
createPodOnNode(ctx, "test-empty-1", "node3")
348348
createPodOnNode(ctx, "test-empty-2", "node4")
@@ -424,7 +424,7 @@ func simulateDaemonSetAnnotation(node *corev1.Node, drainAnnotationValue string)
424424
ToNot(HaveOccurred())
425425
}
426426

427-
func createNode(ctx context.Context, nodeName string) (*corev1.Node, *sriovnetworkv1.SriovNetworkNodeState) {
427+
func createNode(ctx context.Context, nodeName string, useExternalDrainer bool) (*corev1.Node, *sriovnetworkv1.SriovNetworkNodeState) {
428428
node := corev1.Node{
429429
ObjectMeta: metav1.ObjectMeta{
430430
Name: nodeName,
@@ -448,6 +448,10 @@ func createNode(ctx context.Context, nodeName string) (*corev1.Node, *sriovnetwo
448448
},
449449
}
450450

451+
if useExternalDrainer {
452+
nodeState.Annotations[constants.NodeStateExternalDrainerAnnotation] = "true"
453+
}
454+
451455
Expect(k8sClient.Create(ctx, &node)).ToNot(HaveOccurred())
452456
Expect(k8sClient.Create(ctx, &nodeState)).ToNot(HaveOccurred())
453457

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
@@ -86,8 +86,10 @@ spec:
8686
value: {{ .Values.operator.metricsExporter.certificates.secretName }}
8787
- name: METRICS_EXPORTER_KUBE_RBAC_PROXY_IMAGE
8888
value: {{ .Values.images.metricsExporterKubeRbacProxy }}
89-
- name: USE_EXTERNAL_DRAINER
90-
value: {{ .Values.operator.externalDrainer.enabled | quote }}
89+
{{- if .Values.operator.externalDrainer.enabled }}
90+
- name: USE_EXTERNAL_DRAINER
91+
value: {{ .Values.operator.externalDrainer.enabled | quote }}
92+
{{- end }}
9193
{{- if .Values.operator.metricsExporter.prometheusOperator.enabled }}
9294
- name: METRICS_EXPORTER_PROMETHEUS_OPERATOR_ENABLED
9395
value: {{ .Values.operator.metricsExporter.prometheusOperator.enabled | quote}}

main.go

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

281280
// 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
@@ -92,14 +92,15 @@ const (
9292
SriovDevicePluginLabelEnabled = "Enabled"
9393
SriovDevicePluginLabelDisabled = "Disabled"
9494

95-
NodeDrainAnnotation = "sriovnetwork.openshift.io/state"
96-
NodeStateDrainAnnotation = "sriovnetwork.openshift.io/desired-state"
97-
NodeStateDrainAnnotationCurrent = "sriovnetwork.openshift.io/current-state"
98-
DrainIdle = "Idle"
99-
DrainRequired = "Drain_Required"
100-
RebootRequired = "Reboot_Required"
101-
Draining = "Draining"
102-
DrainComplete = "DrainComplete"
95+
NodeDrainAnnotation = "sriovnetwork.openshift.io/state"
96+
NodeStateDrainAnnotation = "sriovnetwork.openshift.io/desired-state"
97+
NodeStateDrainAnnotationCurrent = "sriovnetwork.openshift.io/current-state"
98+
NodeStateExternalDrainerAnnotation = "sriovnetwork.openshift.io/use-external-drainer"
99+
DrainIdle = "Idle"
100+
DrainRequired = "Drain_Required"
101+
RebootRequired = "Reboot_Required"
102+
Draining = "Draining"
103+
DrainComplete = "DrainComplete"
103104

104105
SyncStatusSucceeded = "Succeeded"
105106
SyncStatusFailed = "Failed"

pkg/daemon/daemon.go

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

427+
// remove external drainer nodestate annotation
428+
err = utils.AnnotateObject(ctx, desiredNodeState,
429+
consts.NodeStateExternalDrainerAnnotation, "", dn.client)
430+
if err != nil {
431+
if !errors.IsNotFound(err) {
432+
reqLogger.Error(err, "failed to remove node external drainer annotation")
433+
return ctrl.Result{}, err
434+
}
435+
}
436+
427437
reqLogger.Info("sync succeeded")
428438
syncStatus := consts.SyncStatusSucceeded
429439
lastSyncError := ""
@@ -548,6 +558,16 @@ func (dn *NodeReconciler) handleDrain(ctx context.Context, desiredNodeState *sri
548558
return false, nil
549559
}
550560

561+
// add external drainer nodestate annotation if flag is enabled
562+
if vars.UseExternalDrainer {
563+
err := utils.AnnotateObject(ctx, desiredNodeState,
564+
consts.NodeStateExternalDrainerAnnotation, "true", dn.client)
565+
if err != nil {
566+
funcLog.Error(err, "failed to add node external drainer annotation")
567+
return false, err
568+
}
569+
}
570+
551571
// annotate both node and node state with drain or reboot
552572
annotation := consts.DrainRequired
553573
if reqReboot {

pkg/daemon/daemon_test.go

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ var (
6969
const (
7070
waitTime = 2 * time.Minute
7171
retryTime = 5 * time.Second
72+
nodeName = "node1"
7273
)
7374

7475
var _ = Describe("Daemon Controller", Ordered, func() {
@@ -168,7 +169,7 @@ var _ = Describe("Daemon Controller", Ordered, func() {
168169
daemonReconciler = createDaemon(hostHelper, platformHelper, featureGates, []string{})
169170
startDaemon(daemonReconciler)
170171

171-
_, nodeState = createNode("node1")
172+
_, nodeState = createNode(nodeName)
172173
})
173174

174175
AfterAll(func() {
@@ -270,6 +271,11 @@ var _ = Describe("Daemon Controller", Ordered, func() {
270271
ToNot(HaveOccurred())
271272

272273
g.Expect(nodeState.Annotations[constants.NodeStateDrainAnnotation]).To(Equal(constants.DrainRequired))
274+
// verify that external drainer annotation doesn't exist
275+
node := &corev1.Node{}
276+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: nodeName}, node)).
277+
ToNot(HaveOccurred())
278+
g.Expect(nodeState.Annotations).NotTo(ContainElement(constants.NodeStateExternalDrainerAnnotation))
273279
}, waitTime, retryTime).Should(Succeed())
274280

275281
patchAnnotation(nodeState, constants.NodeStateDrainAnnotationCurrent, constants.DrainComplete)
@@ -351,6 +357,67 @@ var _ = Describe("Daemon Controller", Ordered, func() {
351357
eventuallySyncStatusEqual(nodeState, constants.SyncStatusSucceeded)
352358
assertLastStatusTransitionsContains(nodeState, 2, constants.SyncStatusInProgress)
353359
})
360+
361+
It("Should apply external drainer annotation when useExternalDrainer is true", func(ctx context.Context) {
362+
DeferCleanup(func(x bool) { vars.UseExternalDrainer = x }, vars.UseExternalDrainer)
363+
vars.UseExternalDrainer = true
364+
365+
discoverSriovReturn.Store(&[]sriovnetworkv1.InterfaceExt{
366+
{
367+
Name: "eno1",
368+
Driver: "ice",
369+
PciAddress: "0000:16:00.0",
370+
DeviceID: "1593",
371+
Vendor: "8086",
372+
EswitchMode: "legacy",
373+
LinkAdminState: "up",
374+
LinkSpeed: "10000 Mb/s",
375+
LinkType: "ETH",
376+
Mac: "aa:bb:cc:dd:ee:ff",
377+
Mtu: 1500,
378+
TotalVfs: 2,
379+
NumVfs: 0,
380+
},
381+
})
382+
383+
By("waiting for state to be in progress")
384+
eventuallySyncStatusEqual(nodeState, constants.SyncStatusInProgress)
385+
386+
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: nodeState.Namespace, Name: nodeState.Name}, nodeState)
387+
Expect(err).ToNot(HaveOccurred())
388+
389+
By("waiting to require drain")
390+
EventuallyWithOffset(1, func(g Gomega) {
391+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Namespace: nodeState.Namespace, Name: nodeState.Name}, nodeState)).
392+
ToNot(HaveOccurred())
393+
394+
g.Expect(nodeState.Annotations[constants.NodeStateDrainAnnotation]).To(Equal(constants.DrainRequired))
395+
}, waitTime, retryTime).Should(Succeed())
396+
397+
// Validate status
398+
EventuallyWithOffset(1, func(g Gomega) {
399+
// get node by name
400+
node := &corev1.Node{}
401+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: nodeName}, node)).
402+
ToNot(HaveOccurred())
403+
g.Expect(nodeState.Annotations[constants.NodeStateExternalDrainerAnnotation]).To(Equal("true"))
404+
}, waitTime, retryTime).Should(Succeed())
405+
406+
patchAnnotation(nodeState, constants.NodeStateDrainAnnotationCurrent, constants.DrainComplete)
407+
// Validate status
408+
EventuallyWithOffset(1, func(g Gomega) {
409+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Namespace: nodeState.Namespace, Name: nodeState.Name}, nodeState)).
410+
ToNot(HaveOccurred())
411+
412+
g.Expect(nodeState.Status.SyncStatus).To(Equal(constants.SyncStatusInProgress))
413+
node := &corev1.Node{}
414+
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: nodeName}, node)).
415+
ToNot(HaveOccurred())
416+
// verify that external drainer annotation is removed
417+
g.Expect(nodeState.Annotations).NotTo(ContainElement(constants.NodeStateExternalDrainerAnnotation))
418+
}, waitTime, retryTime).Should(Succeed())
419+
420+
})
354421
})
355422
})
356423

0 commit comments

Comments
 (0)