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 <mikko.ylinen@intel.com>
This commit is contained in:
Mikko Ylinen 2024-11-12 09:22:15 +02:00
parent 76b5118a86
commit c30e80bf86
8 changed files with 219 additions and 365 deletions

View File

@ -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)
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 '<vendor>/%s'", imageName, expectedImageName)
return fmt.Errorf("%w: incorrect image name %q. Make sure you use '<vendor>/%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