Repository: intel/intel-device-plugins-for-kubernetes
Pull request #2074 (2025-06-16 07:36:09)

Allow providing a pull secret for the plugin daemonsets
Change is split into two commits, one adding the support to provide the secrets, and  another to restructure how data is passed to the controllers. The restructuring can be dropped or the two can be merged.

Fixes #https://github.com/intel/intel-device-plugins-for-kubernetes/issues/1981


Patch:
diff --git a/cmd/operator/main.go b/cmd/operator/main.go
index 2fca03da..7c5ac2a7 100644
--- a/cmd/operator/main.go
+++ b/cmd/operator/main.go
@@ -34,6 +34,7 @@ import (
 
 	devicepluginv1 "github.com/intel/intel-device-plugins-for-kubernetes/pkg/apis/deviceplugin/v1"
 	fpgav2 "github.com/intel/intel-device-plugins-for-kubernetes/pkg/apis/fpga/v2"
+	"github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers"
 	"github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers/dlb"
 	"github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers/dsa"
 	"github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers/fpga"
@@ -61,7 +62,7 @@ func init() {
 	// +kubebuilder:scaffold:scheme
 }
 
-type devicePluginControllerAndWebhook map[string](func(ctrl.Manager, string, bool) error)
+type devicePluginControllerAndWebhook map[string](func(ctrl.Manager, controllers.ControllerOptions) error)
 
 type flagList []string
 
@@ -208,15 +209,17 @@ func main() {
 		os.Exit(1)
 	}
 
-	ns := os.Getenv("DEVICEPLUGIN_NAMESPACE")
-	if ns == "" {
-		ns = devicePluginNamespace
+	cargs := controllers.ControllerOptions{WithWebhook: true}
+
+	cargs.Namespace = os.Getenv("DEVICEPLUGIN_NAMESPACE")
+	if cargs.Namespace == "" {
+		cargs.Namespace = devicePluginNamespace
 	}
 
-	withWebhook := true
+	cargs.ImagePullSecretName = os.Getenv("DEVICEPLUGIN_SECRET")
 
 	for _, device := range devices {
-		if err = setupControllerAndWebhook[device](mgr, ns, withWebhook); err != nil {
+		if err = setupControllerAndWebhook[device](mgr, cargs); err != nil {
 			setupLog.Error(err, "unable to initialize controller", "controller", device)
 			os.Exit(1)
 		}
diff --git a/pkg/controllers/dlb/controller.go b/pkg/controllers/dlb/controller.go
index ba770a52..27b6461b 100644
--- a/pkg/controllers/dlb/controller.go
+++ b/pkg/controllers/dlb/controller.go
@@ -43,13 +43,13 @@ var defaultNodeSelector map[string]string = deployments.DLBPluginDaemonSet().Spe
 // +kubebuilder:rbac:groups=deviceplugin.intel.com,resources=dlbdeviceplugins/finalizers,verbs=update
 
 // SetupReconciler creates a new reconciler for DlbDevicePlugin objects.
-func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error {
-	c := &controller{scheme: mgr.GetScheme(), ns: namespace}
+func SetupReconciler(mgr ctrl.Manager, args controllers.ControllerOptions) error {
+	c := &controller{scheme: mgr.GetScheme(), args: args}
 	if err := controllers.SetupWithManager(mgr, c, devicepluginv1.GroupVersion.String(), "DlbDevicePlugin", ownerKey); err != nil {
 		return err
 	}
 
-	if withWebhook {
+	if args.WithWebhook {
 		return (&devicepluginv1.DlbDevicePlugin{}).SetupWebhookWithManager(mgr)
 	}
 
@@ -59,7 +59,7 @@ func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error
 type controller struct {
 	controllers.DefaultServiceAccountFactory
 	scheme *runtime.Scheme
-	ns     string
+	args   controllers.ControllerOptions
 }
 
 func (c *controller) CreateEmptyObject() client.Object {
@@ -92,7 +92,13 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		setInitContainer(&ds.Spec.Template.Spec, devicePlugin.Spec)
 	}
 
-	ds.ObjectMeta.Namespace = c.ns
+	ds.ObjectMeta.Namespace = c.args.Namespace
+
+	if len(c.args.ImagePullSecretName) > 0 {
+		ds.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
 
 	ds.Spec.Template.Spec.Containers[0].Args = getPodArgs(devicePlugin)
 	ds.Spec.Template.Spec.Containers[0].Image = devicePlugin.Spec.Image
diff --git a/pkg/controllers/dsa/controller.go b/pkg/controllers/dsa/controller.go
index fd7f6078..19f6b61f 100644
--- a/pkg/controllers/dsa/controller.go
+++ b/pkg/controllers/dsa/controller.go
@@ -47,13 +47,13 @@ var defaultNodeSelector = deployments.DSAPluginDaemonSet().Spec.Template.Spec.No
 // +kubebuilder:rbac:groups=deviceplugin.intel.com,resources=dsadeviceplugins/finalizers,verbs=update
 
 // SetupReconciler creates a new reconciler for DsaDevicePlugin objects.
-func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error {
-	c := &controller{scheme: mgr.GetScheme(), ns: namespace}
+func SetupReconciler(mgr ctrl.Manager, args controllers.ControllerOptions) error {
+	c := &controller{scheme: mgr.GetScheme(), args: args}
 	if err := controllers.SetupWithManager(mgr, c, devicepluginv1.GroupVersion.String(), "DsaDevicePlugin", ownerKey); err != nil {
 		return err
 	}
 
-	if withWebhook {
+	if args.WithWebhook {
 		return (&devicepluginv1.DsaDevicePlugin{}).SetupWebhookWithManager(mgr)
 	}
 
@@ -63,7 +63,7 @@ func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error
 type controller struct {
 	controllers.DefaultServiceAccountFactory
 	scheme *runtime.Scheme
-	ns     string
+	args   controllers.ControllerOptions
 }
 
 func (c *controller) CreateEmptyObject() client.Object {
@@ -200,7 +200,7 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		daemonSet.Spec.Template.Spec.Tolerations = devicePlugin.Spec.Tolerations
 	}
 
-	daemonSet.ObjectMeta.Namespace = c.ns
+	daemonSet.ObjectMeta.Namespace = c.args.Namespace
 	daemonSet.Spec.Template.Spec.Containers[0].Args = getPodArgs(devicePlugin)
 	daemonSet.Spec.Template.Spec.Containers[0].Image = devicePlugin.Spec.Image
 
@@ -208,6 +208,12 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		addInitContainer(daemonSet, devicePlugin)
 	}
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	return daemonSet
 }
 
diff --git a/pkg/controllers/fpga/controller.go b/pkg/controllers/fpga/controller.go
index 3614fb16..3a96032e 100644
--- a/pkg/controllers/fpga/controller.go
+++ b/pkg/controllers/fpga/controller.go
@@ -22,6 +22,7 @@ import (
 	"strings"
 
 	apps "k8s.io/api/apps/v1"
+	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/tools/reference"
 	ctrl "sigs.k8s.io/controller-runtime"
@@ -42,13 +43,13 @@ var defaultNodeSelector = deployments.FPGAPluginDaemonSet().Spec.Template.Spec.N
 // +kubebuilder:rbac:groups=deviceplugin.intel.com,resources=fpgadeviceplugins/finalizers,verbs=update
 
 // SetupReconciler creates a new reconciler for FpgaDevicePlugin objects.
-func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error {
-	c := &controller{scheme: mgr.GetScheme(), ns: namespace}
+func SetupReconciler(mgr ctrl.Manager, args controllers.ControllerOptions) error {
+	c := &controller{scheme: mgr.GetScheme(), args: args}
 	if err := controllers.SetupWithManager(mgr, c, devicepluginv1.GroupVersion.String(), "FpgaDevicePlugin", ownerKey); err != nil {
 		return err
 	}
 
-	if withWebhook {
+	if args.WithWebhook {
 		return (&devicepluginv1.FpgaDevicePlugin{}).SetupWebhookWithManager(mgr)
 	}
 
@@ -58,7 +59,7 @@ func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error
 type controller struct {
 	controllers.DefaultServiceAccountFactory
 	scheme *runtime.Scheme
-	ns     string
+	args   controllers.ControllerOptions
 }
 
 func (c *controller) CreateEmptyObject() client.Object {
@@ -84,7 +85,13 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		daemonSet.Spec.Template.Spec.Tolerations = devicePlugin.Spec.Tolerations
 	}
 
-	daemonSet.ObjectMeta.Namespace = c.ns
+	daemonSet.ObjectMeta.Namespace = c.args.Namespace
+
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
 
 	daemonSet.Spec.Template.Spec.Containers[0].Args = getPodArgs(devicePlugin)
 	daemonSet.Spec.Template.Spec.Containers[0].Image = devicePlugin.Spec.Image
diff --git a/pkg/controllers/gpu/controller.go b/pkg/controllers/gpu/controller.go
index 3c943f52..09e52675 100644
--- a/pkg/controllers/gpu/controller.go
+++ b/pkg/controllers/gpu/controller.go
@@ -49,13 +49,13 @@ var defaultNodeSelector = deployments.GPUPluginDaemonSet().Spec.Template.Spec.No
 // +kubebuilder:rbac:groups=deviceplugin.intel.com,resources=gpudeviceplugins/finalizers,verbs=update
 
 // SetupReconciler creates a new reconciler for GpuDevicePlugin objects.
-func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error {
-	c := &controller{scheme: mgr.GetScheme(), ns: namespace}
+func SetupReconciler(mgr ctrl.Manager, args controllers.ControllerOptions) error {
+	c := &controller{scheme: mgr.GetScheme(), args: args}
 	if err := controllers.SetupWithManager(mgr, c, devicepluginv1.GroupVersion.String(), "GpuDevicePlugin", ownerKey); err != nil {
 		return err
 	}
 
-	if withWebhook {
+	if args.WithWebhook {
 		return (&devicepluginv1.GpuDevicePlugin{}).SetupWebhookWithManager(mgr)
 	}
 
@@ -64,7 +64,7 @@ func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error
 
 type controller struct {
 	scheme *runtime.Scheme
-	ns     string
+	args   controllers.ControllerOptions
 }
 
 func (c *controller) CreateEmptyObject() client.Object {
@@ -80,7 +80,7 @@ func (c *controller) NewSharedServiceAccount() *v1.ServiceAccount {
 	return &v1.ServiceAccount{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      serviceAccountName,
-			Namespace: c.ns,
+			Namespace: c.args.Namespace,
 		},
 	}
 }
@@ -89,13 +89,13 @@ func (c *controller) NewSharedClusterRoleBinding() *rbacv1.ClusterRoleBinding {
 	return &rbacv1.ClusterRoleBinding{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      roleBindingName,
-			Namespace: c.ns,
+			Namespace: c.args.Namespace,
 		},
 		Subjects: []rbacv1.Subject{
 			{
 				Kind:      "ServiceAccount",
 				Name:      serviceAccountName,
-				Namespace: c.ns,
+				Namespace: c.args.Namespace,
 			},
 		},
 		RoleRef: rbacv1.RoleRef{
@@ -140,10 +140,16 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		daemonSet.Spec.Template.Spec.Tolerations = devicePlugin.Spec.Tolerations
 	}
 
-	daemonSet.ObjectMeta.Namespace = c.ns
+	daemonSet.ObjectMeta.Namespace = c.args.Namespace
 	daemonSet.Spec.Template.Spec.Containers[0].Args = getPodArgs(devicePlugin)
 	daemonSet.Spec.Template.Spec.Containers[0].Image = devicePlugin.Spec.Image
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	if devicePlugin.Spec.InitImage == "" {
 		daemonSet.Spec.Template.Spec.InitContainers = nil
 		daemonSet.Spec.Template.Spec.Volumes = removeVolume(daemonSet.Spec.Template.Spec.Volumes, "nfd-features")
diff --git a/pkg/controllers/iaa/controller.go b/pkg/controllers/iaa/controller.go
index bf2ce865..0fe99678 100644
--- a/pkg/controllers/iaa/controller.go
+++ b/pkg/controllers/iaa/controller.go
@@ -45,13 +45,13 @@ const (
 // +kubebuilder:rbac:groups=deviceplugin.intel.com,resources=iaadeviceplugins/finalizers,verbs=update
 
 // SetupReconciler creates a new reconciler for IaaDevicePlugin objects.
-func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error {
-	c := &controller{scheme: mgr.GetScheme(), ns: namespace}
+func SetupReconciler(mgr ctrl.Manager, args controllers.ControllerOptions) error {
+	c := &controller{scheme: mgr.GetScheme(), args: args}
 	if err := controllers.SetupWithManager(mgr, c, devicepluginv1.GroupVersion.String(), "IaaDevicePlugin", ownerKey); err != nil {
 		return err
 	}
 
-	if withWebhook {
+	if args.WithWebhook {
 		return (&devicepluginv1.IaaDevicePlugin{}).SetupWebhookWithManager(mgr)
 	}
 
@@ -61,7 +61,7 @@ func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error
 type controller struct {
 	controllers.DefaultServiceAccountFactory
 	scheme *runtime.Scheme
-	ns     string
+	args   controllers.ControllerOptions
 }
 
 func (c *controller) CreateEmptyObject() client.Object {
@@ -199,7 +199,7 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		daemonSet.Spec.Template.Spec.Tolerations = devicePlugin.Spec.Tolerations
 	}
 
-	daemonSet.ObjectMeta.Namespace = c.ns
+	daemonSet.ObjectMeta.Namespace = c.args.Namespace
 
 	daemonSet.Spec.Template.Spec.Containers[0].Args = getPodArgs(devicePlugin)
 	daemonSet.Spec.Template.Spec.Containers[0].Image = devicePlugin.Spec.Image
@@ -208,6 +208,12 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		addInitContainer(daemonSet, devicePlugin)
 	}
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	return daemonSet
 }
 
diff --git a/pkg/controllers/qat/controller.go b/pkg/controllers/qat/controller.go
index 36153d86..bcc45a95 100644
--- a/pkg/controllers/qat/controller.go
+++ b/pkg/controllers/qat/controller.go
@@ -47,13 +47,13 @@ var defaultNodeSelector = deployments.QATPluginDaemonSet().Spec.Template.Spec.No
 // +kubebuilder:rbac:groups=deviceplugin.intel.com,resources=qatdeviceplugins/finalizers,verbs=update
 
 // SetupReconciler creates a new reconciler for QatDevicePlugin objects.
-func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error {
-	c := &controller{scheme: mgr.GetScheme(), ns: namespace}
+func SetupReconciler(mgr ctrl.Manager, args controllers.ControllerOptions) error {
+	c := &controller{scheme: mgr.GetScheme(), args: args}
 	if err := controllers.SetupWithManager(mgr, c, devicepluginv1.GroupVersion.String(), "QatDevicePlugin", ownerKey); err != nil {
 		return err
 	}
 
-	if withWebhook {
+	if args.WithWebhook {
 		return (&devicepluginv1.QatDevicePlugin{}).SetupWebhookWithManager(mgr)
 	}
 
@@ -63,7 +63,7 @@ func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error
 type controller struct {
 	controllers.DefaultServiceAccountFactory
 	scheme *runtime.Scheme
-	ns     string
+	args   controllers.ControllerOptions
 }
 
 func (c *controller) CreateEmptyObject() client.Object {
@@ -93,7 +93,13 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		setInitContainer(&daemonSet.Spec.Template.Spec, devicePlugin.Spec)
 	}
 
-	daemonSet.ObjectMeta.Namespace = c.ns
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
+	daemonSet.ObjectMeta.Namespace = c.args.Namespace
 	daemonSet.Spec.Template.Spec.Containers[0].Args = getPodArgs(devicePlugin)
 	daemonSet.Spec.Template.Spec.Containers[0].Image = devicePlugin.Spec.Image
 
diff --git a/pkg/controllers/reconciler.go b/pkg/controllers/reconciler.go
index 86879cb3..ded2b7f0 100644
--- a/pkg/controllers/reconciler.go
+++ b/pkg/controllers/reconciler.go
@@ -94,6 +94,12 @@ type DevicePluginController interface {
 	Upgrade(ctx context.Context, obj client.Object) (upgrade bool)
 }
 
+type ControllerOptions struct {
+	Namespace           string
+	ImagePullSecretName string
+	WithWebhook         bool
+}
+
 type reconciler struct {
 	controller DevicePluginController
 	client.Client
diff --git a/pkg/controllers/sgx/controller.go b/pkg/controllers/sgx/controller.go
index 05a7f163..2804ceac 100644
--- a/pkg/controllers/sgx/controller.go
+++ b/pkg/controllers/sgx/controller.go
@@ -43,13 +43,13 @@ var defaultNodeSelector = deployments.SGXPluginDaemonSet().Spec.Template.Spec.No
 // +kubebuilder:rbac:groups=deviceplugin.intel.com,resources=sgxdeviceplugins/finalizers,verbs=update
 
 // SetupReconciler creates a new reconciler for SgxDevicePlugin objects.
-func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error {
-	c := &controller{scheme: mgr.GetScheme(), ns: namespace}
+func SetupReconciler(mgr ctrl.Manager, args controllers.ControllerOptions) error {
+	c := &controller{scheme: mgr.GetScheme(), args: args}
 	if err := controllers.SetupWithManager(mgr, c, devicepluginv1.GroupVersion.String(), "SgxDevicePlugin", ownerKey); err != nil {
 		return err
 	}
 
-	if withWebhook {
+	if args.WithWebhook {
 		return (&devicepluginv1.SgxDevicePlugin{}).SetupWebhookWithManager(mgr)
 	}
 
@@ -59,7 +59,7 @@ func SetupReconciler(mgr ctrl.Manager, namespace string, withWebhook bool) error
 type controller struct {
 	controllers.DefaultServiceAccountFactory
 	scheme *runtime.Scheme
-	ns     string
+	args   controllers.ControllerOptions
 }
 
 func (c *controller) Upgrade(ctx context.Context, obj client.Object) bool {
@@ -126,7 +126,7 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		daemonSet.Spec.Template.Spec.NodeSelector = devicePlugin.Spec.NodeSelector
 	}
 
-	daemonSet.ObjectMeta.Namespace = c.ns
+	daemonSet.ObjectMeta.Namespace = c.args.Namespace
 
 	daemonSet.Spec.Template.Spec.Containers[0].Args = getPodArgs(devicePlugin)
 	daemonSet.Spec.Template.Spec.Containers[0].Image = devicePlugin.Spec.Image
@@ -136,6 +136,12 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
 		setInitContainer(&daemonSet.Spec.Template.Spec, devicePlugin.Spec.InitImage)
 	}
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	return daemonSet
 }
 


Test patch:
diff --git a/pkg/controllers/dlb/controller_test.go b/pkg/controllers/dlb/controller_test.go
index 34770861..af1cdb23 100644
--- a/pkg/controllers/dlb/controller_test.go
+++ b/pkg/controllers/dlb/controller_test.go
@@ -45,7 +45,7 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace: c.ns,
+			Namespace: c.args.Namespace,
 			Name:      appLabel + "-" + devicePlugin.Name,
 			Labels: map[string]string{
 				"app": appLabel,
@@ -155,6 +155,12 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 		},
 	}
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	return &daemonSet
 }
 
@@ -171,4 +177,12 @@ func TestNewDaemonSetDLB(t *testing.T) {
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("expected and actuall daemonsets differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
 	}
+
+	c.args.ImagePullSecretName = "mysecret"
+
+	expected = c.newDaemonSetExpected(plugin)
+	actual = c.NewDaemonSet(plugin)
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("expected and actual daemonsets with secret differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
+	}
 }
diff --git a/pkg/controllers/dsa/controller_test.go b/pkg/controllers/dsa/controller_test.go
index 4181e42b..46872f60 100644
--- a/pkg/controllers/dsa/controller_test.go
+++ b/pkg/controllers/dsa/controller_test.go
@@ -47,7 +47,7 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace: c.ns,
+			Namespace: c.args.Namespace,
 			Name:      appLabel + "-" + devicePlugin.Name,
 			Labels: map[string]string{
 				"app": appLabel,
@@ -177,6 +177,12 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 		addInitContainer(&daemonSet, devicePlugin)
 	}
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	return &daemonSet
 }
 
@@ -193,4 +199,12 @@ func TestNewDaemonSetDSA(t *testing.T) {
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("expected and actuall daemonsets differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
 	}
+
+	c.args.ImagePullSecretName = "mysecret"
+
+	expected = c.newDaemonSetExpected(plugin)
+	actual = c.NewDaemonSet(plugin)
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("expected and actual daemonsets with secret differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
+	}
 }
diff --git a/pkg/controllers/fpga/controller_test.go b/pkg/controllers/fpga/controller_test.go
index b0ca45b8..5a389833 100644
--- a/pkg/controllers/fpga/controller_test.go
+++ b/pkg/controllers/fpga/controller_test.go
@@ -42,13 +42,13 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 	maxUnavailable := intstr.FromInt(1)
 	maxSurge := intstr.FromInt(0)
 
-	return &apps.DaemonSet{
+	ds := &apps.DaemonSet{
 		TypeMeta: metav1.TypeMeta{
 			Kind:       "DaemonSet",
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace: c.ns,
+			Namespace: c.args.Namespace,
 			Name:      appLabel + "-" + devicePlugin.Name,
 			Labels: map[string]string{
 				"app": appLabel,
@@ -198,6 +198,14 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 			},
 		},
 	}
+
+	if len(c.args.ImagePullSecretName) > 0 {
+		ds.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
+	return ds
 }
 
 // Test that FPGA daemonset created by using go:embed is
@@ -218,4 +226,12 @@ func TestNewDaemonSetFPGA(t *testing.T) {
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("expected and actuall daemonsets differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
 	}
+
+	c.args.ImagePullSecretName = "mysecret"
+
+	expected = c.newDaemonSetExpected(plugin)
+	actual = c.NewDaemonSet(plugin)
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("expected and actual daemonsets with secret differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
+	}
 }
diff --git a/pkg/controllers/gpu/controller_test.go b/pkg/controllers/gpu/controller_test.go
index 994f0d88..aad31697 100644
--- a/pkg/controllers/gpu/controller_test.go
+++ b/pkg/controllers/gpu/controller_test.go
@@ -29,6 +29,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	devicepluginv1 "github.com/intel/intel-device-plugins-for-kubernetes/pkg/apis/deviceplugin/v1"
+	"github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers"
 )
 
 const appLabel = "intel-gpu-plugin"
@@ -50,7 +51,7 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace: c.ns,
+			Namespace: c.args.Namespace,
 			Name:      appLabel + "-" + devicePlugin.Name,
 			Labels: map[string]string{
 				"app": appLabel,
@@ -196,6 +197,12 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 		addVolumeMountIfMissing(&daemonSet.Spec.Template.Spec, "sysfsdevices", "/sys/devices", true)
 	}
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	return &daemonSet
 }
 
@@ -272,6 +279,24 @@ func TestNewDamonSetGPU(t *testing.T) {
 	}
 }
 
+func TestNewDamonSetGPUWithSecret(t *testing.T) {
+	c := &controller{
+		args: controllers.ControllerOptions{
+			ImagePullSecretName: "mysecret",
+		},
+	}
+
+	plugin := &devicepluginv1.GpuDevicePlugin{}
+	plugin.Name = "new-gpu-cr-testing"
+
+	expected := c.newDaemonSetExpected(plugin)
+	actual := c.NewDaemonSet(plugin)
+
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("expected and actual daemonsets with secret differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
+	}
+}
+
 func TestUpdateDamonSetGPU(t *testing.T) {
 	tcases := []struct {
 		name        string
diff --git a/pkg/controllers/iaa/controller_test.go b/pkg/controllers/iaa/controller_test.go
index e74ae955..73b7f04c 100644
--- a/pkg/controllers/iaa/controller_test.go
+++ b/pkg/controllers/iaa/controller_test.go
@@ -47,7 +47,7 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace: c.ns,
+			Namespace: c.args.Namespace,
 			Name:      appLabel + "-" + devicePlugin.Name,
 			Labels: map[string]string{
 				"app": appLabel,
@@ -177,6 +177,12 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 		addInitContainer(&daemonSet, devicePlugin)
 	}
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	return &daemonSet
 }
 
@@ -193,4 +199,13 @@ func TestNewDaemonSetIAA(t *testing.T) {
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("expected and actuall daemonsets differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
 	}
+
+	c.args.ImagePullSecretName = "mysecret"
+
+	expected = c.newDaemonSetExpected(plugin)
+	actual = c.NewDaemonSet(plugin)
+
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("expected and actual daemonsets with secret differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
+	}
 }
diff --git a/pkg/controllers/qat/controller_test.go b/pkg/controllers/qat/controller_test.go
index 4d81ca61..3b12647d 100644
--- a/pkg/controllers/qat/controller_test.go
+++ b/pkg/controllers/qat/controller_test.go
@@ -48,7 +48,7 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace: c.ns,
+			Namespace: c.args.Namespace,
 			Name:      appLabel + "-" + devicePlugin.Name,
 			Labels: map[string]string{
 				"app": appLabel,
@@ -176,6 +176,12 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 		setInitContainer(&daemonSet.Spec.Template.Spec, devicePlugin.Spec)
 	}
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	return &daemonSet
 }
 
@@ -194,4 +200,13 @@ func TestNewDaemonSetQAT(t *testing.T) {
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("expected and actuall daemonsets differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
 	}
+
+	c.args.ImagePullSecretName = "mysecret"
+
+	expected = c.newDaemonSetExpected(plugin)
+	actual = c.NewDaemonSet(plugin)
+
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("expected and actual daemonsets with secret differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
+	}
 }
diff --git a/pkg/controllers/sgx/controller_test.go b/pkg/controllers/sgx/controller_test.go
index 47f653ed..78c9f4d0 100644
--- a/pkg/controllers/sgx/controller_test.go
+++ b/pkg/controllers/sgx/controller_test.go
@@ -49,7 +49,7 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace: c.ns,
+			Namespace: c.args.Namespace,
 			Name:      appLabel + "-" + devicePlugin.Name,
 			Labels: map[string]string{
 				"app": appLabel,
@@ -157,6 +157,12 @@ func (c *controller) newDaemonSetExpected(rawObj client.Object) *apps.DaemonSet
 		setInitContainer(&daemonSet.Spec.Template.Spec, devicePlugin.Spec.InitImage)
 	}
 
+	if len(c.args.ImagePullSecretName) > 0 {
+		daemonSet.Spec.Template.Spec.ImagePullSecrets = []v1.LocalObjectReference{
+			{Name: c.args.ImagePullSecretName},
+		}
+	}
+
 	return &daemonSet
 }
 
@@ -173,4 +179,13 @@ func TestNewDaemonSetSGX(t *testing.T) {
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("expected and actuall daemonsets differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
 	}
+
+	c.args.ImagePullSecretName = "mysecret"
+
+	expected = c.newDaemonSetExpected(plugin)
+	actual = c.NewDaemonSet(plugin)
+
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("expected and actual daemonsets with secret differ: %+s", diff.ObjectGoPrintDiff(expected, actual))
+	}
 }
diff --git a/test/envtest/suite_test.go b/test/envtest/suite_test.go
index 44e4e090..f2c6d904 100644
--- a/test/envtest/suite_test.go
+++ b/test/envtest/suite_test.go
@@ -113,21 +113,21 @@ func up() {
 	k8sManager, managerErr := ctrl.NewManager(cfg, ctrl.Options{Scheme: scheme.Scheme, Metrics: metricsserver.Options{BindAddress: "0"}, Controller: config.Controller{SkipNameValidation: &yes}})
 	Expect(managerErr).To(BeNil())
 
-	withWebhook := true
+	args := ctr.ControllerOptions{Namespace: ns, WithWebhook: false}
 
-	Expect(dlbctr.SetupReconciler(k8sManager, ns, !withWebhook)).To(BeNil())
+	Expect(dlbctr.SetupReconciler(k8sManager, args)).To(BeNil())
 
-	Expect(dsactr.SetupReconciler(k8sManager, ns, !withWebhook)).To(BeNil())
+	Expect(dsactr.SetupReconciler(k8sManager, args)).To(BeNil())
 
-	Expect(fpgactr.SetupReconciler(k8sManager, ns, !withWebhook)).To(BeNil())
+	Expect(fpgactr.SetupReconciler(k8sManager, args)).To(BeNil())
 
-	Expect(gpuctr.SetupReconciler(k8sManager, ns, !withWebhook)).To(BeNil())
+	Expect(gpuctr.SetupReconciler(k8sManager, args)).To(BeNil())
 
-	Expect(iaactr.SetupReconciler(k8sManager, ns, !withWebhook)).To(BeNil())
+	Expect(iaactr.SetupReconciler(k8sManager, args)).To(BeNil())
 
-	Expect(qatctr.SetupReconciler(k8sManager, ns, !withWebhook)).To(BeNil())
+	Expect(qatctr.SetupReconciler(k8sManager, args)).To(BeNil())
 
-	Expect(sgxctr.SetupReconciler(k8sManager, ns, !withWebhook)).To(BeNil())
+	Expect(sgxctr.SetupReconciler(k8sManager, args)).To(BeNil())
 
 	ctx, cancel = context.WithCancel(context.TODO())