Skip to content

Commit f1e5508

Browse files
committed
review comments
1 parent 12e2bbc commit f1e5508

File tree

10 files changed

+361
-207
lines changed

10 files changed

+361
-207
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ Feature gates are used to enable or disable specific features in the operator.
421421

422422
6. **Block Device Plugin Until Configured** (`blockDevicePluginUntilConfigured`)
423423
- **Description:** Prevents the SR-IOV device plugin from starting until the sriov-config-daemon has applied the SR-IOV configuration for the node. When enabled, the device plugin daemonset runs an init container that sets a wait-for-config annotation on its pod and waits until the sriov-config-daemon removes this annotation after applying the configuration. This addresses the race condition where the device plugin starts and reports available resources before the configuration is actually applied, which can lead to pods being scheduled prematurely.
424-
- **Default:** Disabled
424+
- **Default:** Enabled
425425

426426
### Enabling Feature Gates
427427

bindata/manifests/plugins/sriov-device-plugin.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ spec:
2323
component: network
2424
type: infra
2525
openshift.io/component: network
26+
# NOTE: The controller uses equality.Semantic.DeepDerivative(in.Spec, ds.Spec)
27+
# to detect changes in the DaemonSet's spec and decide if an update is needed.
28+
# To ensure the init container is properly managed when toggling the
29+
# BlockDevicePluginUntilConfigured feature gate, we define an explicit field
30+
# (init-container-enabled) that is always present in the DaemonSet labels.
31+
# Its value reflects the state of the feature gate and guarantees spec changes
32+
# are propagated, ensuring the init container is added or removed as required.
33+
init-container-enabled: "{{ .BlockDevicePluginUntilConfigured }}"
2634
spec:
2735
hostNetwork: true
2836
nodeSelector:
@@ -59,11 +67,7 @@ spec:
5967
fieldPath: metadata.namespace
6068
{{- end }}
6169
containers:
62-
{{- if .BlockDevicePluginUntilConfigured }}
63-
- name: sriov-device-plugin-main
64-
{{- else }}
6570
- name: sriov-device-plugin
66-
{{- end }}
6771
image: {{.SRIOVDevicePluginImage}}
6872
args:
6973
- --log-level=10

cmd/sriov-network-config-daemon/start.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ func initFeatureGates(defaultConfig *sriovnetworkv1.SriovOperatorConfig) (featur
185185
featureGates := featuregate.New()
186186
featureGates.Init(defaultConfig.Spec.FeatureGates)
187187
fnLogger.Info("Enabled featureGates", "featureGates", featureGates.String())
188-
188+
vars.FeatureGate = featureGates
189189
return featureGates, nil
190190
}
191191

cmd/sriov-network-config-daemon/waitforconfig.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"sigs.k8s.io/controller-runtime/pkg/cache"
3232
"sigs.k8s.io/controller-runtime/pkg/client"
3333
"sigs.k8s.io/controller-runtime/pkg/log"
34+
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
3435

3536
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
3637
snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log"
@@ -141,7 +142,7 @@ func startWaitForConfigManager(setupLog logr.Logger, config *rest.Config, podNam
141142

142143
// Set annotation on pod to signal that we are waiting for config
143144
setupLog.Info("Setting annotation on pod", "annotation", consts.DevicePluginWaitConfigAnnotation)
144-
err = setAnnotationOnPod(context.Background(), setupLog, tempClient, podName)
145+
err = setAnnotationOnPod(ctx, setupLog, tempClient, podName)
145146
if err != nil {
146147
setupLog.Error(err, "failed to set annotation on pod")
147148
return err
@@ -152,6 +153,7 @@ func startWaitForConfigManager(setupLog logr.Logger, config *rest.Config, podNam
152153
// Watch only specific pod object
153154
selector := fields.SelectorFromSet(fields.Set{"metadata.name": podName.Name})
154155
mgr, err := ctrl.NewManager(config, ctrl.Options{
156+
Metrics: metricsserver.Options{BindAddress: "0"},
155157
Cache: cache.Options{
156158
DefaultNamespaces: map[string]cache.Config{podName.Namespace: {}},
157159
ByObject: map[client.Object]cache.ByObject{

controllers/helper.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
4646
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/apply"
4747
constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
48+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate"
4849
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render"
4950
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
5051
)
@@ -172,7 +173,8 @@ func GetNodeSelectorForDevicePlugin(dc *sriovnetworkv1.SriovOperatorConfig) map[
172173
func syncPluginDaemonObjs(ctx context.Context,
173174
client k8sclient.Client,
174175
scheme *runtime.Scheme,
175-
dc *sriovnetworkv1.SriovOperatorConfig) error {
176+
dc *sriovnetworkv1.SriovOperatorConfig,
177+
featureGate featuregate.FeatureGate) error {
176178
logger := log.Log.WithName("syncPluginDaemonObjs")
177179
logger.V(1).Info("Start to sync sriov daemons objects")
178180

@@ -186,7 +188,7 @@ func syncPluginDaemonObjs(ctx context.Context,
186188
data.Data["ImagePullSecrets"] = GetImagePullSecrets()
187189
data.Data["NodeSelectorField"] = GetNodeSelectorForDevicePlugin(dc)
188190
data.Data["UseCDI"] = dc.Spec.UseCDI
189-
data.Data["BlockDevicePluginUntilConfigured"] = dc.Spec.FeatureGates[constants.BlockDevicePluginUntilConfiguredFeatureGate]
191+
data.Data["BlockDevicePluginUntilConfigured"] = featureGate.IsEnabled(constants.BlockDevicePluginUntilConfiguredFeatureGate)
190192
objs, err := renderDsForCR(constants.PluginPath, &data)
191193
if err != nil {
192194
logger.Error(err, "Fail to render SR-IoV manifests")

controllers/sriovoperatorconfig_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func (r *SriovOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl.
138138
return reconcile.Result{}, err
139139
}
140140

141-
if err = syncPluginDaemonObjs(ctx, r.Client, r.Scheme, defaultConfig); err != nil {
141+
if err = syncPluginDaemonObjs(ctx, r.Client, r.Scheme, defaultConfig, r.FeatureGate); err != nil {
142142
return reconcile.Result{}, err
143143
}
144144

pkg/daemon/daemon.go

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,20 @@ func (dn *NodeReconciler) checkSystemdStatus() (*hosttypes.SriovResult, bool, er
370370
// 7. Updating the lastAppliedGeneration to the current generation.
371371
func (dn *NodeReconciler) apply(ctx context.Context, desiredNodeState *sriovnetworkv1.SriovNetworkNodeState, reqReboot bool, sriovResult *hosttypes.SriovResult) (ctrl.Result, error) {
372372
reqLogger := log.FromContext(ctx).WithName("Apply")
373+
374+
// Restart the device plugin *before* applying configuration if the
375+
// BlockDevicePluginUntilConfiguredFeatureGate feature is enabled.
376+
// With this gate enabled, the device plugin will remain blocked until it is
377+
// explicitly unblocked after configuration (see waitForDevicePluginPodAndTryUnblock).
378+
// If the feature gate is not enabled, preserve legacy behavior by
379+
// restarting the device plugin *after* configuration is applied.
380+
if vars.FeatureGate.IsEnabled(consts.BlockDevicePluginUntilConfiguredFeatureGate) {
381+
if err := dn.restartDevicePluginPod(ctx); err != nil {
382+
reqLogger.Error(err, "failed to restart device plugin on the node")
383+
return ctrl.Result{}, err
384+
}
385+
}
386+
373387
// apply the additional plugins after we are done with drain if needed
374388
for _, p := range dn.additionalPlugins {
375389
err := p.Apply()
@@ -395,15 +409,18 @@ func (dn *NodeReconciler) apply(ctx context.Context, desiredNodeState *sriovnetw
395409
return ctrl.Result{}, dn.rebootNode()
396410
}
397411

398-
if err := dn.restartDevicePluginPod(ctx); err != nil {
399-
reqLogger.Error(err, "failed to restart device plugin on the node")
400-
return ctrl.Result{}, err
401-
}
402412
if vars.FeatureGate.IsEnabled(consts.BlockDevicePluginUntilConfiguredFeatureGate) {
403413
if err := dn.waitForDevicePluginPodAndTryUnblock(ctx, desiredNodeState); err != nil {
404414
reqLogger.Error(err, "failed to wait for device plugin pod to start and try to unblock it")
405415
return ctrl.Result{}, err
406416
}
417+
} else {
418+
// if the feature gate is not enabled we preserver the old behavior
419+
// and restart device plugin after configuration is applied
420+
if err := dn.restartDevicePluginPod(ctx); err != nil {
421+
reqLogger.Error(err, "failed to restart device plugin on the node")
422+
return ctrl.Result{}, err
423+
}
407424
}
408425

409426
err := dn.annotate(ctx, desiredNodeState, consts.DrainIdle)
@@ -452,7 +469,7 @@ func (dn *NodeReconciler) tryUnblockDevicePlugin(ctx context.Context,
452469
for _, pod := range devicePluginPods {
453470
if err := utils.RemoveAnnotationFromObject(ctx, &pod,
454471
consts.DevicePluginWaitConfigAnnotation, dn.client); err != nil {
455-
return fmt.Errorf("failed to remove wait-for-config annotation from pod: %w", err)
472+
return fmt.Errorf("failed to remove %s annotation from pod: %w", consts.DevicePluginWaitConfigAnnotation, err)
456473
}
457474
}
458475
return nil
@@ -577,18 +594,19 @@ func (dn *NodeReconciler) handleDrain(ctx context.Context, desiredNodeState *sri
577594

578595
// getDevicePluginPods returns the device plugin pods running on this node
579596
func (dn *NodeReconciler) getDevicePluginPodsForNode(ctx context.Context) ([]corev1.Pod, error) {
597+
funcLog := log.Log.WithName("getDevicePluginPodsForNode")
580598
pods := &corev1.PodList{}
581599
err := dn.client.List(ctx, pods, &client.ListOptions{
582600
Namespace: vars.Namespace, Raw: &metav1.ListOptions{
583601
LabelSelector: "app=sriov-device-plugin",
584602
FieldSelector: "spec.nodeName=" + vars.NodeName,
585603
}})
586604
if err != nil {
587-
log.Log.Error(err, "getDevicePluginPodsForNode(): failed to list device plugin pods")
605+
funcLog.Error(err, "failed to list device plugin pods")
588606
return []corev1.Pod{}, err
589607
}
590608
if len(pods.Items) == 0 {
591-
log.Log.Info("getDevicePluginPodsForNode(): no device plugin pods found")
609+
funcLog.Info("no device plugin pods found")
592610
return []corev1.Pod{}, nil
593611
}
594612
return pods.Items, nil
@@ -649,18 +667,18 @@ func (dn *NodeReconciler) restartDevicePluginPod(ctx context.Context) error {
649667
// for the periodic check. We expect to have at least one device plugin pod for the node.
650668
func (dn *NodeReconciler) waitForDevicePluginPodAndTryUnblock(ctx context.Context, desiredNodeState *sriovnetworkv1.SriovNetworkNodeState) error {
651669
funcLog := log.Log.WithName("waitForDevicePluginPodAndTryUnblock")
652-
funcLog.Info("waiting for device plugin to set wait-for-config annotation")
670+
funcLog.Info("waiting for device plugin to set wait-for-config annotation", "annotation", consts.DevicePluginWaitConfigAnnotation)
653671
var devicePluginPods []corev1.Pod
654672
err := wait.PollUntilContextTimeout(ctx, time.Second, 2*time.Minute, true,
655673
func(ctx context.Context) (bool, error) {
656674
var err error
657675
devicePluginPods, err = dn.getDevicePluginPodsForNode(ctx)
658676
if err != nil {
659-
log.Log.Error(err, "waitForDevicePluginPodAndUnblock(): failed to get device plugin pod while waiting for a new pod to start")
677+
funcLog.Error(err, "failed to get device plugin pod while waiting for a new pod to start")
660678
return false, err
661679
}
662680
if len(devicePluginPods) == 0 {
663-
log.Log.V(2).Info("waitForDevicePluginPodAndUnblock(): no device plugin pods found while waiting for a new pod to start")
681+
funcLog.V(2).Info("no device plugin pods found while waiting for a new pod to start")
664682
return false, nil
665683
}
666684
for _, pod := range devicePluginPods {
@@ -669,12 +687,12 @@ func (dn *NodeReconciler) waitForDevicePluginPodAndTryUnblock(ctx context.Contex
669687
// may also match our selector. Since unmanaged pods won't have this annotation,
670688
// we only require one pod (the managed one) to have it.
671689
if utils.ObjectHasAnnotationKey(&pod, consts.DevicePluginWaitConfigAnnotation) {
672-
log.Log.Info("waitForDevicePluginPodAndUnblock(): wait-for-config annotation found on pod",
690+
funcLog.Info("wait-for-config annotation found on pod",
673691
"pod", pod.Name)
674692
return true, nil
675693
}
676694
}
677-
log.Log.V(2).Info("waitForDevicePluginPodAndUnblock(): waiting for new device plugin pod to have wait-for-config annotation")
695+
funcLog.V(2).Info("waiting for new device plugin pod to have wait-for-config annotation")
678696
return false, nil
679697
})
680698
if err != nil {
@@ -683,8 +701,8 @@ func (dn *NodeReconciler) waitForDevicePluginPodAndTryUnblock(ctx context.Contex
683701
}
684702
// If annotation is not found within the timeout, log a warning and proceed.
685703
// The device plugin pod will be unblocked by the periodic check logic in tryUnblockDevicePlugin.
686-
log.Log.Info("waitForDevicePluginPodAndUnblock(): WARNING: device plugin pod with " +
687-
"wait-for-config annotation not found within timeout")
704+
funcLog.Info("WARNING: device plugin pod with wait-for-config annotation not found within timeout")
705+
return nil
688706
}
689707
if len(devicePluginPods) > 0 {
690708
// try to unblock all device plugin pods we retrieved with the latest loop iteration

0 commit comments

Comments
 (0)