From c30e80bf867e69121f4f5a7f3f367b632ab2cae4 Mon Sep 17 00:00:00 2001 From: Mikko Ylinen Date: Tue, 12 Nov 2024 09:22:15 +0200 Subject: [PATCH] operator: rework webhooks controller-runtime has deprecated webhook.Defaulter/Validator and they will be removed in the next controller-runtime release. Move deviceplugin webhooks to use admission.CustomDefaulter/Validator. Common defaulter/validator types can serve all the plugins with only plugin specific data initialized. As part of the rework, move away from (the unmaintained) pkg/errors to errors in Go standard library. Signed-off-by: Mikko Ylinen --- .../v1/dlbdeviceplugin_webhook.go | 60 ++------ .../v1/dsadeviceplugin_webhook.go | 65 ++------- .../v1/fpgadeviceplugin_webhook.go | 64 ++------- .../v1/gpudeviceplugin_webhook.go | 72 +++------- .../v1/iaadeviceplugin_webhook.go | 65 ++------- .../v1/qatdeviceplugin_webhook.go | 67 ++------- .../v1/sgxdeviceplugin_webhook.go | 60 ++------ pkg/apis/deviceplugin/v1/webhook_common.go | 131 +++++++++++++++++- 8 files changed, 219 insertions(+), 365 deletions(-) diff --git a/pkg/apis/deviceplugin/v1/dlbdeviceplugin_webhook.go b/pkg/apis/deviceplugin/v1/dlbdeviceplugin_webhook.go index baff36a75..132682c71 100644 --- a/pkg/apis/deviceplugin/v1/dlbdeviceplugin_webhook.go +++ b/pkg/apis/deviceplugin/v1/dlbdeviceplugin_webhook.go @@ -15,73 +15,35 @@ package v1 import ( - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers" ) -var ( - // dlbdevicepluginlog is for logging in this package. - dlbdevicepluginlog = logf.Log.WithName("dlbdeviceplugin-resource") - - dlbMinVersion = controllers.ImageMinVersion -) - // SetupWebhookWithManager sets up a webhook for DlbDevicePlugin custom resources. func (r *DlbDevicePlugin) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(&commonDevicePluginDefaulter{ + defaultImage: "intel/intel-dlb-plugin:" + controllers.ImageMinVersion.String(), + }). + WithValidator(&commonDevicePluginValidator{ + expectedImage: "intel-dlb-plugin", + expectedInitImage: "intel-dlb-initimage", + expectedVersion: *controllers.ImageMinVersion, + }). Complete() } // +kubebuilder:webhook:path=/mutate-deviceplugin-intel-com-v1-dlbdeviceplugin,mutating=true,failurePolicy=fail,groups=deviceplugin.intel.com,resources=dlbdeviceplugins,verbs=create;update,versions=v1,name=mdlbdeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &DlbDevicePlugin{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type. -func (r *DlbDevicePlugin) Default() { - dlbdevicepluginlog.Info("default", "name", r.Name) - - if len(r.Spec.Image) == 0 { - r.Spec.Image = "intel/intel-dlb-plugin:" + dlbMinVersion.String() - } -} - // +kubebuilder:webhook:verbs=create;update,path=/validate-deviceplugin-intel-com-v1-dlbdeviceplugin,mutating=false,failurePolicy=fail,groups=deviceplugin.intel.com,resources=dlbdeviceplugins,versions=v1,name=vdlbdeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 -var _ webhook.Validator = &DlbDevicePlugin{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (r *DlbDevicePlugin) ValidateCreate() (admission.Warnings, error) { - dlbdevicepluginlog.Info("validate create", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *DlbDevicePlugin) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - dlbdevicepluginlog.Info("validate update", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (r *DlbDevicePlugin) ValidateDelete() (admission.Warnings, error) { - dlbdevicepluginlog.Info("validate delete", "name", r.Name) - - return nil, nil -} - -func (r *DlbDevicePlugin) validatePlugin() error { +func (r *DlbDevicePlugin) validatePlugin(ref *commonDevicePluginValidator) error { if r.Spec.InitImage != "" { - if err := validatePluginImage(r.Spec.InitImage, "intel-dlb-initcontainer", dlbMinVersion); err != nil { + if err := validatePluginImage(r.Spec.InitImage, ref.expectedInitImage, &ref.expectedVersion); err != nil { return err } } - return validatePluginImage(r.Spec.Image, "intel-dlb-plugin", dlbMinVersion) + return validatePluginImage(r.Spec.Image, ref.expectedImage, &ref.expectedVersion) } diff --git a/pkg/apis/deviceplugin/v1/dsadeviceplugin_webhook.go b/pkg/apis/deviceplugin/v1/dsadeviceplugin_webhook.go index 59777bf3d..cdf5da471 100644 --- a/pkg/apis/deviceplugin/v1/dsadeviceplugin_webhook.go +++ b/pkg/apis/deviceplugin/v1/dsadeviceplugin_webhook.go @@ -15,79 +15,42 @@ package v1 import ( - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/runtime" + "fmt" + ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers" ) -var ( - // dsadevicepluginlog is for logging in this package. - dsadevicepluginlog = logf.Log.WithName("dsadeviceplugin-resource") - - dsaMinVersion = controllers.ImageMinVersion -) - // SetupWebhookWithManager sets up a webhook for DsaDevicePlugin custom resources. func (r *DsaDevicePlugin) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(&commonDevicePluginDefaulter{ + defaultImage: "intel/intel-dsa-plugin:" + controllers.ImageMinVersion.String(), + }). + WithValidator(&commonDevicePluginValidator{ + expectedImage: "intel-dsa-plugin", + expectedInitImage: "intel-idxd-config-initcontainer", + expectedVersion: *controllers.ImageMinVersion, + }). Complete() } // +kubebuilder:webhook:path=/mutate-deviceplugin-intel-com-v1-dsadeviceplugin,mutating=true,failurePolicy=fail,groups=deviceplugin.intel.com,resources=dsadeviceplugins,verbs=create;update,versions=v1,name=mdsadeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &DsaDevicePlugin{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type. -func (r *DsaDevicePlugin) Default() { - dsadevicepluginlog.Info("default", "name", r.Name) - - if len(r.Spec.Image) == 0 { - r.Spec.Image = "intel/intel-dsa-plugin:" + dsaMinVersion.String() - } -} - // +kubebuilder:webhook:verbs=create;update,path=/validate-deviceplugin-intel-com-v1-dsadeviceplugin,mutating=false,failurePolicy=fail,groups=deviceplugin.intel.com,resources=dsadeviceplugins,versions=v1,name=vdsadeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 -var _ webhook.Validator = &DsaDevicePlugin{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (r *DsaDevicePlugin) ValidateCreate() (admission.Warnings, error) { - dsadevicepluginlog.Info("validate create", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *DsaDevicePlugin) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - dsadevicepluginlog.Info("validate update", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (r *DsaDevicePlugin) ValidateDelete() (admission.Warnings, error) { - dsadevicepluginlog.Info("validate delete", "name", r.Name) - - return nil, nil -} - -func (r *DsaDevicePlugin) validatePlugin() error { - if err := validatePluginImage(r.Spec.Image, "intel-dsa-plugin", dsaMinVersion); err != nil { +func (r *DsaDevicePlugin) validatePlugin(ref *commonDevicePluginValidator) error { + if err := validatePluginImage(r.Spec.Image, ref.expectedImage, &ref.expectedVersion); err != nil { return err } if len(r.Spec.ProvisioningConfig) > 0 && len(r.Spec.InitImage) == 0 { - return errors.Errorf("ProvisioningConfig is set with no InitImage") + return fmt.Errorf("%w: ProvisioningConfig is set with no InitImage", errValidation) } if len(r.Spec.InitImage) > 0 { - return validatePluginImage(r.Spec.InitImage, "intel-idxd-config-initcontainer", dsaMinVersion) + return validatePluginImage(r.Spec.InitImage, ref.expectedInitImage, &ref.expectedVersion) } return nil diff --git a/pkg/apis/deviceplugin/v1/fpgadeviceplugin_webhook.go b/pkg/apis/deviceplugin/v1/fpgadeviceplugin_webhook.go index bff8de5df..09d06272b 100644 --- a/pkg/apis/deviceplugin/v1/fpgadeviceplugin_webhook.go +++ b/pkg/apis/deviceplugin/v1/fpgadeviceplugin_webhook.go @@ -15,75 +15,33 @@ package v1 import ( - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers" ) -var ( - // fpgadevicepluginlog is for logging in this package. - fpgadevicepluginlog = logf.Log.WithName("fpgadeviceplugin-resource") - - fpgaMinVersion = controllers.ImageMinVersion -) - // SetupWebhookWithManager sets up a webhook for FpgaDevicePlugin custom resources. func (r *FpgaDevicePlugin) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(&commonDevicePluginDefaulter{ + defaultImage: "intel/intel-fpga-plugin:" + controllers.ImageMinVersion.String(), + }). + WithValidator(&commonDevicePluginValidator{ + expectedImage: "intel-fpga-plugin", + expectedInitImage: "intel-fpga-initimage", + expectedVersion: *controllers.ImageMinVersion, + }). Complete() } // +kubebuilder:webhook:path=/mutate-deviceplugin-intel-com-v1-fpgadeviceplugin,mutating=true,failurePolicy=fail,groups=deviceplugin.intel.com,resources=fpgadeviceplugins,verbs=create;update,versions=v1,name=mfpgadeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &FpgaDevicePlugin{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type. -func (r *FpgaDevicePlugin) Default() { - fpgadevicepluginlog.Info("default", "name", r.Name) - - if len(r.Spec.Image) == 0 { - r.Spec.Image = "intel/intel-fpga-plugin:" + fpgaMinVersion.String() - } - - if len(r.Spec.InitImage) == 0 { - r.Spec.InitImage = "intel/intel-fpga-initcontainer:" + fpgaMinVersion.String() - } -} - // +kubebuilder:webhook:verbs=create;update,path=/validate-deviceplugin-intel-com-v1-fpgadeviceplugin,mutating=false,failurePolicy=fail,groups=deviceplugin.intel.com,resources=fpgadeviceplugins,versions=v1,name=vfpgadeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 -var _ webhook.Validator = &FpgaDevicePlugin{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (r *FpgaDevicePlugin) ValidateCreate() (admission.Warnings, error) { - fpgadevicepluginlog.Info("validate create", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *FpgaDevicePlugin) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - fpgadevicepluginlog.Info("validate update", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (r *FpgaDevicePlugin) ValidateDelete() (admission.Warnings, error) { - fpgadevicepluginlog.Info("validate delete", "name", r.Name) - - return nil, nil -} - -func (r *FpgaDevicePlugin) validatePlugin() error { - if err := validatePluginImage(r.Spec.Image, "intel-fpga-plugin", fpgaMinVersion); err != nil { +func (r *FpgaDevicePlugin) validatePlugin(ref *commonDevicePluginValidator) error { + if err := validatePluginImage(r.Spec.Image, ref.expectedImage, &ref.expectedVersion); err != nil { return err } - return validatePluginImage(r.Spec.InitImage, "intel-fpga-initcontainer", fpgaMinVersion) + return validatePluginImage(r.Spec.InitImage, ref.expectedInitImage, &ref.expectedVersion) } diff --git a/pkg/apis/deviceplugin/v1/gpudeviceplugin_webhook.go b/pkg/apis/deviceplugin/v1/gpudeviceplugin_webhook.go index 84343e86a..a886fbe02 100644 --- a/pkg/apis/deviceplugin/v1/gpudeviceplugin_webhook.go +++ b/pkg/apis/deviceplugin/v1/gpudeviceplugin_webhook.go @@ -16,25 +16,15 @@ package v1 import ( "context" + "fmt" - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers" ) -var ( - // gpudevicepluginlog is for logging in this package. - gpudevicepluginlog = logf.Log.WithName("gpudeviceplugin-resource") - - gpuMinVersion = controllers.ImageMinVersion -) - var cli client.Client // SetupWebhookWithManager sets up a webhook for GpuDevicePlugin custom resources. @@ -43,53 +33,25 @@ func (r *GpuDevicePlugin) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(&commonDevicePluginDefaulter{ + defaultImage: "intel/intel-gpu-plugin:" + controllers.ImageMinVersion.String(), + }). + WithValidator(&commonDevicePluginValidator{ + expectedImage: "intel-gpu-plugin", + expectedVersion: *controllers.ImageMinVersion, + }). Complete() } // +kubebuilder:webhook:path=/mutate-deviceplugin-intel-com-v1-gpudeviceplugin,mutating=true,failurePolicy=fail,groups=deviceplugin.intel.com,resources=gpudeviceplugins,verbs=create;update,versions=v1,name=mgpudeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &GpuDevicePlugin{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type. -func (r *GpuDevicePlugin) Default() { - gpudevicepluginlog.Info("default", "name", r.Name) - - if len(r.Spec.Image) == 0 { - r.Spec.Image = "intel/intel-gpu-plugin:" + gpuMinVersion.String() - } -} - // +kubebuilder:webhook:verbs=create;update,path=/validate-deviceplugin-intel-com-v1-gpudeviceplugin,mutating=false,failurePolicy=fail,groups=deviceplugin.intel.com,resources=gpudeviceplugins,versions=v1,name=vgpudeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 -var _ webhook.Validator = &GpuDevicePlugin{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (r *GpuDevicePlugin) ValidateCreate() (admission.Warnings, error) { - gpudevicepluginlog.Info("validate create", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *GpuDevicePlugin) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - gpudevicepluginlog.Info("validate update", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (r *GpuDevicePlugin) ValidateDelete() (admission.Warnings, error) { - gpudevicepluginlog.Info("validate delete", "name", r.Name) - - return nil, nil -} - -func (r *GpuDevicePlugin) crossCheckResourceManagement() bool { - ctx := context.Background() +func (r *GpuDevicePlugin) crossCheckResourceManagement(ctx context.Context) bool { + log := logf.FromContext(ctx) gpuCrs := GpuDevicePluginList{} if err := cli.List(ctx, &gpuCrs); err != nil { - gpudevicepluginlog.Info("unable to list GPU CRs") + log.Info("unable to list GPU CRs") return false } @@ -108,18 +70,18 @@ func (r *GpuDevicePlugin) crossCheckResourceManagement() bool { return true } -func (r *GpuDevicePlugin) validatePlugin() error { +func (r *GpuDevicePlugin) validatePlugin(ctx context.Context, ref *commonDevicePluginValidator) error { if r.Spec.SharedDevNum == 1 && r.Spec.PreferredAllocationPolicy != "none" { - return errors.Errorf("PreferredAllocationPolicy is valid only when setting sharedDevNum > 1") + return fmt.Errorf("%w: PreferredAllocationPolicy is valid only when setting sharedDevNum > 1", errValidation) } if r.Spec.SharedDevNum == 1 && r.Spec.ResourceManager { - return errors.Errorf("resourceManager is valid only when setting sharedDevNum > 1") + return fmt.Errorf("%w: resourceManager is valid only when setting sharedDevNum > 1", errValidation) } - if !r.crossCheckResourceManagement() { - return errors.Errorf("All GPU CRs must be with or without resource management") + if !r.crossCheckResourceManagement(ctx) { + return fmt.Errorf("%w: All GPU CRs must be with or without resource management", errValidation) } - return validatePluginImage(r.Spec.Image, "intel-gpu-plugin", gpuMinVersion) + return validatePluginImage(r.Spec.Image, ref.expectedImage, &ref.expectedVersion) } diff --git a/pkg/apis/deviceplugin/v1/iaadeviceplugin_webhook.go b/pkg/apis/deviceplugin/v1/iaadeviceplugin_webhook.go index aaa20f5ae..8e6e232ff 100644 --- a/pkg/apis/deviceplugin/v1/iaadeviceplugin_webhook.go +++ b/pkg/apis/deviceplugin/v1/iaadeviceplugin_webhook.go @@ -15,79 +15,42 @@ package v1 import ( - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/runtime" + "fmt" + ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers" ) -var ( - // iaadevicepluginlog is for logging in this package. - iaadevicepluginlog = logf.Log.WithName("iaadeviceplugin-resource") - - iaaMinVersion = controllers.ImageMinVersion -) - // SetupWebhookWithManager sets up a webhook for IaaDevicePlugin custom resources. func (r *IaaDevicePlugin) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(&commonDevicePluginDefaulter{ + defaultImage: "intel/intel-iaa-plugin:" + controllers.ImageMinVersion.String(), + }). + WithValidator(&commonDevicePluginValidator{ + expectedImage: "intel-iaa-plugin", + expectedInitImage: "intel-idxd-config-initcontainer", + expectedVersion: *controllers.ImageMinVersion, + }). Complete() } // +kubebuilder:webhook:path=/mutate-deviceplugin-intel-com-v1-iaadeviceplugin,mutating=true,failurePolicy=fail,groups=deviceplugin.intel.com,resources=iaadeviceplugins,verbs=create;update,versions=v1,name=miaadeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &IaaDevicePlugin{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type. -func (r *IaaDevicePlugin) Default() { - iaadevicepluginlog.Info("default", "name", r.Name) - - if len(r.Spec.Image) == 0 { - r.Spec.Image = "intel/intel-iaa-plugin:" + iaaMinVersion.String() - } -} - // +kubebuilder:webhook:verbs=create;update,path=/validate-deviceplugin-intel-com-v1-iaadeviceplugin,mutating=false,failurePolicy=fail,groups=deviceplugin.intel.com,resources=iaadeviceplugins,versions=v1,name=viaadeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 -var _ webhook.Validator = &IaaDevicePlugin{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (r *IaaDevicePlugin) ValidateCreate() (admission.Warnings, error) { - iaadevicepluginlog.Info("validate create", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *IaaDevicePlugin) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - iaadevicepluginlog.Info("validate update", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (r *IaaDevicePlugin) ValidateDelete() (admission.Warnings, error) { - iaadevicepluginlog.Info("validate delete", "name", r.Name) - - return nil, nil -} - -func (r *IaaDevicePlugin) validatePlugin() error { - if err := validatePluginImage(r.Spec.Image, "intel-iaa-plugin", iaaMinVersion); err != nil { +func (r *IaaDevicePlugin) validatePlugin(ref *commonDevicePluginValidator) error { + if err := validatePluginImage(r.Spec.Image, ref.expectedImage, &ref.expectedVersion); err != nil { return err } if len(r.Spec.ProvisioningConfig) > 0 && len(r.Spec.InitImage) == 0 { - return errors.Errorf("ProvisioningConfig is set with no InitImage") + return fmt.Errorf("%w: ProvisioningConfig is set with no InitImage", errValidation) } if len(r.Spec.InitImage) > 0 { - return validatePluginImage(r.Spec.InitImage, "intel-idxd-config-initcontainer", iaaMinVersion) + return validatePluginImage(r.Spec.InitImage, ref.expectedInitImage, &ref.expectedVersion) } return nil diff --git a/pkg/apis/deviceplugin/v1/qatdeviceplugin_webhook.go b/pkg/apis/deviceplugin/v1/qatdeviceplugin_webhook.go index 0a4c65a7e..d15c4a72a 100644 --- a/pkg/apis/deviceplugin/v1/qatdeviceplugin_webhook.go +++ b/pkg/apis/deviceplugin/v1/qatdeviceplugin_webhook.go @@ -15,78 +15,41 @@ package v1 import ( - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/runtime" + "fmt" + ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers" ) -var ( - // qatdevicepluginlog is for logging in this package. - qatdevicepluginlog = logf.Log.WithName("qatdeviceplugin-resource") - - qatMinVersion = controllers.ImageMinVersion -) - // SetupWebhookWithManager sets up a webhook for QatDevicePlugin custom resources. func (r *QatDevicePlugin) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(&commonDevicePluginDefaulter{ + defaultImage: "intel/intel-qat-plugin:" + controllers.ImageMinVersion.String(), + }). + WithValidator(&commonDevicePluginValidator{ + expectedImage: "intel-qat-plugin", + expectedInitImage: "intel-qat-initcontainer", + expectedVersion: *controllers.ImageMinVersion, + }). Complete() } // +kubebuilder:webhook:path=/mutate-deviceplugin-intel-com-v1-qatdeviceplugin,mutating=true,failurePolicy=fail,groups=deviceplugin.intel.com,resources=qatdeviceplugins,verbs=create;update,versions=v1,name=mqatdeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &QatDevicePlugin{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type. -func (r *QatDevicePlugin) Default() { - qatdevicepluginlog.Info("default", "name", r.Name) - - if len(r.Spec.Image) == 0 { - r.Spec.Image = "intel/intel-qat-plugin:" + qatMinVersion.String() - } -} - // +kubebuilder:webhook:verbs=create;update,path=/validate-deviceplugin-intel-com-v1-qatdeviceplugin,mutating=false,failurePolicy=fail,groups=deviceplugin.intel.com,resources=qatdeviceplugins,versions=v1,name=vqatdeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 -var _ webhook.Validator = &QatDevicePlugin{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (r *QatDevicePlugin) ValidateCreate() (admission.Warnings, error) { - qatdevicepluginlog.Info("validate create", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *QatDevicePlugin) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - qatdevicepluginlog.Info("validate update", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (r *QatDevicePlugin) ValidateDelete() (admission.Warnings, error) { - qatdevicepluginlog.Info("validate delete", "name", r.Name) - - return nil, nil -} - -func (r *QatDevicePlugin) validatePlugin() error { +func (r *QatDevicePlugin) validatePlugin(ref *commonDevicePluginValidator) error { if r.Spec.InitImage != "" { - if err := validatePluginImage(r.Spec.InitImage, "intel-qat-initcontainer", qatMinVersion); err != nil { + if err := validatePluginImage(r.Spec.InitImage, ref.expectedInitImage, &ref.expectedVersion); err != nil { return err } } if len(r.Spec.ProvisioningConfig) > 0 { if len(r.Spec.InitImage) == 0 { - return errors.Errorf("ProvisioningConfig is set with no InitImage") + return fmt.Errorf("%w: ProvisioningConfig is set with no InitImage", errValidation) } // check if 4xxxvf is enabled @@ -104,9 +67,9 @@ func (r *QatDevicePlugin) validatePlugin() error { } if !contains { - return errors.Errorf("ProvisioningConfig is available only for 4xxx and 420xx devices") + return fmt.Errorf("%w: ProvisioningConfig is available only for 4xxx and 420xx devices", errValidation) } } - return validatePluginImage(r.Spec.Image, "intel-qat-plugin", qatMinVersion) + return validatePluginImage(r.Spec.Image, ref.expectedImage, &ref.expectedVersion) } diff --git a/pkg/apis/deviceplugin/v1/sgxdeviceplugin_webhook.go b/pkg/apis/deviceplugin/v1/sgxdeviceplugin_webhook.go index 8bf1e356f..ea405faf4 100644 --- a/pkg/apis/deviceplugin/v1/sgxdeviceplugin_webhook.go +++ b/pkg/apis/deviceplugin/v1/sgxdeviceplugin_webhook.go @@ -15,69 +15,31 @@ package v1 import ( - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/intel/intel-device-plugins-for-kubernetes/pkg/controllers" ) -var ( - // sgxdevicepluginlog is for logging in this package. - sgxdevicepluginlog = logf.Log.WithName("sgxdeviceplugin-resource") - - sgxMinVersion = controllers.ImageMinVersion -) - // SetupWebhookWithManager sets up a webhook for SgxDevicePlugin custom resources. func (r *SgxDevicePlugin) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(&commonDevicePluginDefaulter{ + defaultImage: "intel/intel-sgx-plugin:" + controllers.ImageMinVersion.String(), + }). + WithValidator(&commonDevicePluginValidator{ + expectedImage: "intel-sgx-plugin", + expectedInitImage: "intel-sgx-initcontainer", + expectedVersion: *controllers.ImageMinVersion, + }). Complete() } // +kubebuilder:webhook:path=/mutate-deviceplugin-intel-com-v1-sgxdeviceplugin,mutating=true,failurePolicy=fail,groups=deviceplugin.intel.com,resources=sgxdeviceplugins,verbs=create;update,versions=v1,name=msgxdeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1,reinvocationPolicy=IfNeeded - -var _ webhook.Defaulter = &SgxDevicePlugin{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type. -func (r *SgxDevicePlugin) Default() { - sgxdevicepluginlog.Info("default", "name", r.Name) - - if len(r.Spec.Image) == 0 { - r.Spec.Image = "intel/intel-sgx-plugin:" + sgxMinVersion.String() - } -} - // +kubebuilder:webhook:verbs=create;update,path=/validate-deviceplugin-intel-com-v1-sgxdeviceplugin,mutating=false,failurePolicy=fail,groups=deviceplugin.intel.com,resources=sgxdeviceplugins,versions=v1,name=vsgxdeviceplugin.kb.io,sideEffects=None,admissionReviewVersions=v1 -var _ webhook.Validator = &SgxDevicePlugin{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (r *SgxDevicePlugin) ValidateCreate() (admission.Warnings, error) { - sgxdevicepluginlog.Info("validate create", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *SgxDevicePlugin) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - sgxdevicepluginlog.Info("validate update", "name", r.Name) - - return nil, r.validatePlugin() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (r *SgxDevicePlugin) ValidateDelete() (admission.Warnings, error) { - sgxdevicepluginlog.Info("validate delete", "name", r.Name) - - return nil, nil -} - -func (r *SgxDevicePlugin) validatePlugin() error { - if err := validatePluginImage(r.Spec.Image, "intel-sgx-plugin", sgxMinVersion); err != nil { +func (r *SgxDevicePlugin) validatePlugin(ref *commonDevicePluginValidator) error { + if err := validatePluginImage(r.Spec.Image, ref.expectedImage, &ref.expectedVersion); err != nil { return err } @@ -85,5 +47,5 @@ func (r *SgxDevicePlugin) validatePlugin() error { return nil } - return validatePluginImage(r.Spec.InitImage, "intel-sgx-initcontainer", sgxMinVersion) + return validatePluginImage(r.Spec.InitImage, ref.expectedInitImage, &ref.expectedVersion) } diff --git a/pkg/apis/deviceplugin/v1/webhook_common.go b/pkg/apis/deviceplugin/v1/webhook_common.go index c41e70fd6..e175f11e2 100644 --- a/pkg/apis/deviceplugin/v1/webhook_common.go +++ b/pkg/apis/deviceplugin/v1/webhook_common.go @@ -15,18 +15,139 @@ package v1 import ( + "context" + "errors" + "fmt" "path/filepath" "regexp" "strings" - "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/version" + + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) const sha256RE = "@sha256:[0-9a-f]{64}$" +var errObjType = errors.New("invalid object") +var errValidation = errors.New("invalid resource") + // common functions for webhooks +type commonDevicePluginDefaulter struct { + defaultImage string +} + +var _ admission.CustomDefaulter = &commonDevicePluginDefaulter{} + +type commonDevicePluginValidator struct { + expectedImage string + expectedInitImage string + expectedVersion version.Version +} + +var _ admission.CustomValidator = &commonDevicePluginValidator{} + +// Default implements admission.CustomDefaulter so a webhook will be registered for the type. +func (r *commonDevicePluginDefaulter) Default(ctx context.Context, obj runtime.Object) error { + logf.FromContext(ctx).Info("default") + + // type switches can have only one type in a case so the same repeats for + // all xDevicePlugin types. + // TODO: implement receivers if more complex logic is needed. + switch v := obj.(type) { + case *DlbDevicePlugin: + if len(v.Spec.Image) == 0 { + v.Spec.Image = r.defaultImage + } + case *DsaDevicePlugin: + if len(v.Spec.Image) == 0 { + v.Spec.Image = r.defaultImage + } + case *FpgaDevicePlugin: + if len(v.Spec.Image) == 0 { + v.Spec.Image = r.defaultImage + } + case *GpuDevicePlugin: + if len(v.Spec.Image) == 0 { + v.Spec.Image = r.defaultImage + } + case *IaaDevicePlugin: + if len(v.Spec.Image) == 0 { + v.Spec.Image = r.defaultImage + } + case *QatDevicePlugin: + if len(v.Spec.Image) == 0 { + v.Spec.Image = r.defaultImage + } + case *SgxDevicePlugin: + if len(v.Spec.Image) == 0 { + v.Spec.Image = r.defaultImage + } + default: + return fmt.Errorf("%w: expected an xDevicePlugin object but got %T", errObjType, obj) + } + + return nil +} + +// ValidateCreate implements admission.CustomValidator so a webhook will be registered for the type. +func (r *commonDevicePluginValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + logf.FromContext(ctx).Info("validate create") + + switch v := obj.(type) { + case *DlbDevicePlugin: + return nil, v.validatePlugin(r) + case *DsaDevicePlugin: + return nil, v.validatePlugin(r) + case *GpuDevicePlugin: + return nil, v.validatePlugin(ctx, r) + case *FpgaDevicePlugin: + return nil, v.validatePlugin(r) + case *IaaDevicePlugin: + return nil, v.validatePlugin(r) + case *QatDevicePlugin: + return nil, v.validatePlugin(r) + case *SgxDevicePlugin: + return nil, v.validatePlugin(r) + default: + return nil, fmt.Errorf("%w: expected an xDevicePlugin object but got %T", errObjType, obj) + } +} + +// ValidateUpdate implements admission.CustomValidator so a webhook will be registered for the type. +func (r *commonDevicePluginValidator) ValidateUpdate(ctx context.Context, oldObj runtime.Object, newObj runtime.Object) (admission.Warnings, error) { + logf.FromContext(ctx).Info("validate update") + + switch v := oldObj.(type) { + case *DlbDevicePlugin: + return nil, v.validatePlugin(r) + case *DsaDevicePlugin: + return nil, v.validatePlugin(r) + case *GpuDevicePlugin: + return nil, v.validatePlugin(ctx, r) + case *FpgaDevicePlugin: + return nil, v.validatePlugin(r) + case *IaaDevicePlugin: + return nil, v.validatePlugin(r) + case *QatDevicePlugin: + return nil, v.validatePlugin(r) + case *SgxDevicePlugin: + return nil, v.validatePlugin(r) + default: + return nil, fmt.Errorf("%w: expected an xDevicePlugin object but got %T", errObjType, oldObj) + } +} + +// ValidateDelete implements admission.CustomValidator so a webhook will be registered for the type. +func (r *commonDevicePluginValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + logf.FromContext(ctx).Info("validate delete") + + return nil, nil +} + func validatePluginImage(image, expectedImageName string, expectedMinVersion *version.Version) error { imageRe := regexp.MustCompile(expectedImageName + sha256RE) if imageRe.MatchString(image) { @@ -36,7 +157,7 @@ func validatePluginImage(image, expectedImageName string, expectedMinVersion *ve // Ignore registry, vendor and extract the image name with the tag parts := strings.SplitN(filepath.Base(image), ":", 2) if len(parts) != 2 { - return errors.Errorf("incorrect image field %q", image) + return fmt.Errorf("%w: incorrect image field %q", errValidation, image) } imageName := parts[0] @@ -44,16 +165,16 @@ func validatePluginImage(image, expectedImageName string, expectedMinVersion *ve // If user provided faulty SHA digest, the image name may include @sha256 suffix so strip it. if strings.TrimSuffix(imageName, "@sha256") != expectedImageName { - return errors.Errorf("incorrect image name %q. Make sure you use '/%s'", imageName, expectedImageName) + return fmt.Errorf("%w: incorrect image name %q. Make sure you use '/%s'", errValidation, imageName, expectedImageName) } ver, err := version.ParseSemantic(versionStr) if err != nil { - return errors.Wrapf(err, "unable to parse version %q. Make sure it's either valid SHA digest or semver tag", versionStr) + return fmt.Errorf("%w: %w: Make sure it's either valid SHA digest or semver tag", errValidation, err) } if !ver.AtLeast(expectedMinVersion) { - return errors.Errorf("version %q is too low. Should be at least %q", ver, expectedMinVersion) + return fmt.Errorf("%w: version %q is too low. Should be at least %q", errValidation, ver, expectedMinVersion) } return nil