Skip to content

Commit

Permalink
operator: rework webhooks
Browse files Browse the repository at this point in the history
Signed-off-by: Mikko Ylinen <[email protected]>
  • Loading branch information
mythi committed Nov 12, 2024
1 parent 99ef5bb commit 1b68395
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 215 deletions.
91 changes: 62 additions & 29 deletions pkg/apis/deviceplugin/v1/dlbdeviceplugin_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,73 +15,106 @@
package v1

import (
"context"
"fmt"

"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
)
var 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(&dlbDevicePluginDefaulter{}).
WithValidator(&dlbDevicePluginValidator{}).
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{}
type dlbDevicePluginDefaulter struct{}

// Default implements webhook.Defaulter so a webhook will be registered for the type.
func (r *DlbDevicePlugin) Default() {
dlbdevicepluginlog.Info("default", "name", r.Name)
var _ admission.CustomDefaulter = &dlbDevicePluginDefaulter{}

if len(r.Spec.Image) == 0 {
r.Spec.Image = "intel/intel-dlb-plugin:" + dlbMinVersion.String()
// Default implements admission.CustomDefaulter so a webhook will be registered for the type.
func (r *dlbDevicePluginDefaulter) Default(ctx context.Context, obj runtime.Object) error {
log := logf.FromContext(ctx)
cr, ok := obj.(*DlbDevicePlugin)

if !ok {
return fmt.Errorf("%w: expected an DlbDevicePlugin object but got %T", objectTypeError, obj)
}

log.Info("default", "resource", obj.GetObjectKind(), "name", cr.Name)

if len(cr.Spec.Image) == 0 {
cr.Spec.Image = "intel/intel-dlb-plugin:" + dlbMinVersion.String()
}

return nil
}

// +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{}
type dlbDevicePluginValidator struct{}

var _ admission.CustomValidator = &dlbDevicePluginValidator{}

// 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)
// ValidateCreate implements admission.CustomValidator so a webhook will be registered for the type.
func (r *dlbDevicePluginValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
log := logf.FromContext(ctx)
cr, ok := obj.(*DlbDevicePlugin)

return nil, r.validatePlugin()
if !ok {
return nil, fmt.Errorf("%w: expected an DlbDevicePlugin object but got %T", objectTypeError, obj)
}

log.Info("validate create", "resource", obj.GetObjectKind(), "name", cr.Name)

return nil, r.validatePlugin(ctx, cr)
}

// 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)
// ValidateUpdate implements admission.CustomValidator so a webhook will be registered for the type.
func (r *dlbDevicePluginValidator) ValidateUpdate(ctx context.Context, oldObj runtime.Object, newObj runtime.Object) (admission.Warnings, error) {
log := logf.FromContext(ctx)
cr, ok := oldObj.(*DlbDevicePlugin)

if !ok {
return nil, fmt.Errorf("%w: expected an DlbDevicePlugin object but got %T", objectTypeError, oldObj)
}

return nil, r.validatePlugin()
log.Info("validate update", "resource", oldObj.GetObjectKind(), "name", cr.Name)

return nil, r.validatePlugin(ctx, cr)
}

// 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)
// ValidateDelete implements admission.CustomValidator so a webhook will be registered for the type.
func (r *dlbDevicePluginValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
log := logf.FromContext(ctx)
cr, ok := obj.(*DlbDevicePlugin)

if !ok {
return nil, fmt.Errorf("%w: expected an DlbDevicePlugin object but got %T", objectTypeError, obj)
}

log.Info("validate delete", "resource", obj.GetObjectKind(), "name", cr.Name)

return nil, nil
}

func (r *DlbDevicePlugin) validatePlugin() error {
if r.Spec.InitImage != "" {
if err := validatePluginImage(r.Spec.InitImage, "intel-dlb-initcontainer", dlbMinVersion); err != nil {
func (r *dlbDevicePluginValidator) validatePlugin(ctx context.Context, cr *DlbDevicePlugin) error {
if cr.Spec.InitImage != "" {
if err := validatePluginImage(cr.Spec.InitImage, "intel-dlb-initcontainer", dlbMinVersion); err != nil {
return err
}
}

return validatePluginImage(r.Spec.Image, "intel-dlb-plugin", dlbMinVersion)
return validatePluginImage(cr.Spec.Image, "intel-dlb-plugin", dlbMinVersion)
}
93 changes: 63 additions & 30 deletions pkg/apis/deviceplugin/v1/dsadeviceplugin_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,79 +15,112 @@
package v1

import (
"context"
"fmt"

"github.com/pkg/errors"
"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 (
// dsadevicepluginlog is for logging in this package.
dsadevicepluginlog = logf.Log.WithName("dsadeviceplugin-resource")

dsaMinVersion = controllers.ImageMinVersion
)
var 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(&dsaDevicePluginDefaulter{}).
WithValidator(&dsaDevicePluginValidator{}).
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{}
type dsaDevicePluginDefaulter struct{}

var _ admission.CustomDefaulter = &dsaDevicePluginDefaulter{}

// Default implements admission.CustomDefaulter so a webhook will be registered for the type.
func (r *dsaDevicePluginDefaulter) Default(ctx context.Context, obj runtime.Object) error {
log := logf.FromContext(ctx)
cr, ok := obj.(*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 !ok {
return fmt.Errorf("%w: expected an DsaDevicePlugin object but got %T", objectTypeError, obj)
}

log.Info("default", "resource", obj.GetObjectKind(), "name", cr.Name)

if len(r.Spec.Image) == 0 {
r.Spec.Image = "intel/intel-dsa-plugin:" + dsaMinVersion.String()
if len(cr.Spec.Image) == 0 {
cr.Spec.Image = "intel/intel-dsa-plugin:" + dsaMinVersion.String()
}

return nil
}

// +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{}
type dsaDevicePluginValidator struct{}

var _ admission.CustomValidator = &dsaDevicePluginValidator{}

// 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)
// ValidateCreate implements admission.CustomValidator so a webhook will be registered for the type.
func (r *dsaDevicePluginValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
log := logf.FromContext(ctx)
cr, ok := obj.(*DsaDevicePlugin)

return nil, r.validatePlugin()
if !ok {
return nil, fmt.Errorf("%w: expected an DsaDevicePlugin object but got %T", objectTypeError, obj)
}

log.Info("validate create", "resource", obj.GetObjectKind(), "name", cr.Name)

return nil, r.validatePlugin(ctx, cr)
}

// 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)
// ValidateUpdate implements admission.CustomValidator so a webhook will be registered for the type.
func (r *dsaDevicePluginValidator) ValidateUpdate(ctx context.Context, oldObj runtime.Object, newObj runtime.Object) (admission.Warnings, error) {
log := logf.FromContext(ctx)
cr, ok := oldObj.(*DsaDevicePlugin)

if !ok {
return nil, fmt.Errorf("%w: expected an DsaDevicePlugin object but got %T", objectTypeError, oldObj)
}

return nil, r.validatePlugin()
log.Info("validate update", "resource", oldObj.GetObjectKind(), "name", cr.Name)

return nil, r.validatePlugin(ctx, cr)
}

// 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)
// ValidateDelete implements admission.CustomValidator so a webhook will be registered for the type.
func (r *dsaDevicePluginValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
log := logf.FromContext(ctx)
cr, ok := obj.(*DsaDevicePlugin)

if !ok {
return nil, fmt.Errorf("%w: expected an DsaDevicePlugin object but got %T", objectTypeError, obj)
}

log.Info("validate delete", "resource", obj.GetObjectKind(), "name", cr.Name)

return nil, nil
}

func (r *DsaDevicePlugin) validatePlugin() error {
if err := validatePluginImage(r.Spec.Image, "intel-dsa-plugin", dsaMinVersion); err != nil {
func (r *dsaDevicePluginValidator) validatePlugin(ctx context.Context, cr *DsaDevicePlugin) error {
if err := validatePluginImage(cr.Spec.Image, "intel-dsa-plugin", dsaMinVersion); err != nil {
return err
}

if len(r.Spec.ProvisioningConfig) > 0 && len(r.Spec.InitImage) == 0 {
if len(cr.Spec.ProvisioningConfig) > 0 && len(cr.Spec.InitImage) == 0 {
return errors.Errorf("ProvisioningConfig is set with no InitImage")
}

if len(r.Spec.InitImage) > 0 {
return validatePluginImage(r.Spec.InitImage, "intel-idxd-config-initcontainer", dsaMinVersion)
if len(cr.Spec.InitImage) > 0 {
return validatePluginImage(cr.Spec.InitImage, "intel-idxd-config-initcontainer", dsaMinVersion)
}

return nil
Expand Down
Loading

0 comments on commit 1b68395

Please sign in to comment.