Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 27 additions & 25 deletions api/v1alpha1/networkconfiguration_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@
package v1alpha1

import (
"context"
"regexp"
"strings"

"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"
)

Expand Down Expand Up @@ -60,39 +59,42 @@ func (e missingDeviceClassNameError) Error() string {

// SetupWebhookWithManager will setup the manager to manage the webhooks
func (r *NetworkClusterPolicy) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
return ctrl.NewWebhookManagedBy(mgr, r).
WithDefaulter(r).
WithValidator(r).
Complete()
}

//+kubebuilder:webhook:path=/mutate-intel-com-v1alpha1-networkclusterpolicy,mutating=true,failurePolicy=fail,sideEffects=None,groups=intel.com,resources=networkclusterpolicy,verbs=create;update,versions=v1alpha1,name=mnetworkclusterpolicy.kb.io,admissionReviewVersions=v1

var _ webhook.Defaulter = &NetworkClusterPolicy{}
var _ admission.Defaulter[*NetworkClusterPolicy] = &NetworkClusterPolicy{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *NetworkClusterPolicy) Default() {
netpolicylog.Info("default", "name", r.Name)
// Default implements admission.Defaulter so a webhook will be registered for the type
func (r *NetworkClusterPolicy) Default(_ context.Context, obj *NetworkClusterPolicy) error {
netpolicylog.Info("default", "name", obj.Name)

switch r.Spec.ConfigurationType {
switch obj.Spec.ConfigurationType {
case gaudiScaleOut:
if len(r.Spec.GaudiScaleOut.Image) == 0 {
r.Spec.GaudiScaleOut.Image = "intel/intel-network-linkdiscovery:latest"
if len(obj.Spec.GaudiScaleOut.Image) == 0 {
obj.Spec.GaudiScaleOut.Image = "intel/intel-network-linkdiscovery:latest"
}
case hostNicScaleOut:
rdmaDeviceClass := r.Spec.HostNicScaleOut.Dranet.RDMADeviceClass
rdmaDeviceClass := obj.Spec.HostNicScaleOut.Dranet.RDMADeviceClass
if rdmaDeviceClass != nil && rdmaDeviceClass.Name == "" {
r.Spec.HostNicScaleOut.Dranet.RDMADeviceClass.Name = DefaultRDMADeviceClass
obj.Spec.HostNicScaleOut.Dranet.RDMADeviceClass.Name = DefaultRDMADeviceClass
}

}

return nil
}

// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
//+kubebuilder:webhook:path=/validate-intel-com-v1alpha1-networkclusterpolicy,mutating=false,failurePolicy=fail,sideEffects=None,groups=intel.com,resources=networkclusterpolicy,verbs=create;update,versions=v1alpha1,name=vnetworkclusterpolicy.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &NetworkClusterPolicy{}
var _ admission.Validator[*NetworkClusterPolicy] = &NetworkClusterPolicy{}

var labelHostRegex = regexp.MustCompile(`^([A-Za-z0-9][A-Za-z0-9_\.]*)?[A-Za-z0-9]$`)
var labelPathRegex = regexp.MustCompile(`^([A-Za-z0-9][A-Za-z0-9-\._\/]*)?[A-Za-z0-9]$`)
Expand Down Expand Up @@ -154,23 +156,23 @@ func validateSpec(s NetworkClusterPolicySpec) (admission.Warnings, error) {
}
}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *NetworkClusterPolicy) ValidateCreate() (admission.Warnings, error) {
netpolicylog.Info("validate create", "name", r.Name)
// ValidateCreate implements admission.Validator so a webhook will be registered for the type
func (r *NetworkClusterPolicy) ValidateCreate(_ context.Context, obj *NetworkClusterPolicy) (admission.Warnings, error) {
netpolicylog.Info("validate create", "name", obj.Name)

return validateSpec(r.Spec)
return validateSpec(obj.Spec)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *NetworkClusterPolicy) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
netpolicylog.Info("validate update", "name", r.Name)
// ValidateUpdate implements admission.Validator so a webhook will be registered for the type
func (r *NetworkClusterPolicy) ValidateUpdate(_ context.Context, oldObj, newObj *NetworkClusterPolicy) (admission.Warnings, error) {
netpolicylog.Info("validate update", "name", newObj.Name)

return validateSpec(r.Spec)
return validateSpec(newObj.Spec)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *NetworkClusterPolicy) ValidateDelete() (admission.Warnings, error) {
netpolicylog.Info("validate delete", "name", r.Name)
// ValidateDelete implements admission.Validator so a webhook will be registered for the type
func (r *NetworkClusterPolicy) ValidateDelete(_ context.Context, obj *NetworkClusterPolicy) (admission.Warnings, error) {
netpolicylog.Info("validate delete", "name", obj.Name)

return nil, nil
}
50 changes: 32 additions & 18 deletions api/v1alpha1/networkconfiguration_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package v1alpha1

import (
"context"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -33,7 +35,7 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
nc.Spec.ConfigurationType = gaudiScaleOut
nc.Spec.GaudiScaleOut.Layer = "L2"

nc.Default()
_ = nc.Default(context.Background(), &nc)

Expect(nc.Spec.GaudiScaleOut.Image).To(BeEquivalentTo("intel/intel-network-linkdiscovery:latest"))
})
Expand All @@ -43,7 +45,7 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
nc.Spec.HostNicScaleOut.InstallDRANet = true
nc.Spec.HostNicScaleOut.Dranet.RDMADeviceClass = &RDMADeviceClassSpec{}

nc.Default()
_ = nc.Default(context.Background(), &nc)

Expect(nc.Spec.HostNicScaleOut.Dranet.RDMADeviceClass.Name).To(BeEquivalentTo(DefaultRDMADeviceClass))
})
Expand All @@ -54,15 +56,15 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
Name: "my-device-class",
}

nc.Default()
_ = nc.Default(context.Background(), &nc)

Expect(nc.Spec.HostNicScaleOut.Dranet.RDMADeviceClass.Name).To(BeEquivalentTo("my-device-class"))
})

It("Should not add an RDMA device class if none is requested", func() {
nc.Spec.ConfigurationType = hostNicScaleOut

nc.Default()
_ = nc.Default(context.Background(), &nc)

Expect(nc.Spec.HostNicScaleOut.Dranet.RDMADeviceClass).To(BeNil())
})
Expand All @@ -71,7 +73,7 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
nc.Spec.ConfigurationType = gaudiScaleOut
nc.Spec.HostNicScaleOut.Dranet.RDMADeviceClass = &RDMADeviceClassSpec{}

nc.Default()
_ = nc.Default(context.Background(), &nc)

Expect(nc.Spec.HostNicScaleOut.Dranet.RDMADeviceClass.Name).To(BeEmpty())
})
Expand All @@ -81,7 +83,8 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
It("Should deny if there's no nodeSelector", func() {
nc.Spec.ConfigurationType = gaudiScaleOut

Expect(nc.ValidateCreate()).Error().NotTo(BeNil())
_, err := nc.ValidateCreate(context.Background(), &nc)
Expect(err).NotTo(BeNil())
})

It("Should deny if the configuration type is invalid InputVal", func() {
Expand All @@ -91,7 +94,8 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {

nc.Spec.ConfigurationType = "foo bar"

Expect(nc.ValidateCreate()).Error().To(BeEquivalentTo(unknownConfigurationError{}))
_, err := nc.ValidateCreate(context.Background(), &nc)
Expect(err).To(BeEquivalentTo(unknownConfigurationError{}))
})

It("Should accept good nodeSelectors", func() {
Expand All @@ -113,7 +117,8 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
for _, v := range goodValues {
nc.Spec.NodeSelector = v

Expect(nc.ValidateCreate()).Error().To(BeNil(), "selector: %+v", v)
_, err := nc.ValidateCreate(context.Background(), &nc)
Expect(err).To(BeNil(), "selector: %+v", v)
}
})

Expand Down Expand Up @@ -144,7 +149,8 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
for _, v := range badValues {
nc.Spec.NodeSelector = v

Expect(nc.ValidateCreate()).Error().To(Not(BeNil()), "selector: %+v", v)
_, err := nc.ValidateCreate(context.Background(), &nc)
Expect(err).To(Not(BeNil()), "selector: %+v", v)
}
})

Expand All @@ -165,13 +171,15 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
}
nc2 := nc.DeepCopy()

Expect(nc2.ValidateUpdate(&nc)).Error().To(BeNil())
_, err := nc2.ValidateUpdate(context.Background(), &nc, nc2)
Expect(err).To(BeNil())

nc2.Spec.NodeSelector = map[string]string{
"foobar.com?foo": "bar", // bad
}

Expect(nc2.ValidateUpdate(&nc)).Error().NotTo(BeNil())
_, err = nc2.ValidateUpdate(context.Background(), &nc, nc2)
Expect(err).NotTo(BeNil())
})

It("Should always accept delete", func() {
Expand All @@ -187,7 +195,8 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
},
}

Expect(nc.ValidateDelete()).Error().To(BeNil())
_, err := nc.ValidateDelete(context.Background(), &nc)
Expect(err).To(BeNil())
})

It("Should accept a hostnic configuration without a nodeSelector", func() {
Expand All @@ -200,7 +209,8 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
},
}

Expect(nc.ValidateCreate()).Error().To(BeNil())
_, err := nc.ValidateCreate(context.Background(), &nc)
Expect(err).To(BeNil())
})

It("Should accept a hostnic configuration with a named RDMA device class", func() {
Expand All @@ -218,8 +228,10 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
},
}

Expect(nc.ValidateCreate()).Error().To(BeNil())
Expect(nc.ValidateUpdate(nc.DeepCopy())).Error().To(BeNil())
_, err := nc.ValidateCreate(context.Background(), &nc)
Expect(err).To(BeNil())
_, err = nc.ValidateUpdate(context.Background(), &nc, nc.DeepCopy())
Expect(err).To(BeNil())
})

It("Should deny a hostnic configuration with an unnamed RDMA device class InputVal", func() {
Expand All @@ -235,13 +247,15 @@ var _ = Describe("NetworkClusterPolicy Webhook", func() {
},
}

Expect(nc.ValidateCreate()).Error().To(BeEquivalentTo(missingDeviceClassNameError{}))
_, err := nc.ValidateCreate(context.Background(), &nc)
Expect(err).To(BeEquivalentTo(missingDeviceClassNameError{}))
Expect(missingDeviceClassNameError{}.Error()).NotTo(BeEmpty())

// Defaulting gives the device class a name, making the spec valid.
nc.Default()
_ = nc.Default(context.Background(), &nc)

Expect(nc.ValidateCreate()).Error().To(BeNil())
_, err = nc.ValidateCreate(context.Background(), &nc)
Expect(err).To(BeNil())
})
})
})
2 changes: 1 addition & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions cmd/operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func tlsOptions(enableHTTP2 bool) []func(*tls.Config) {
// metricsOptions returns the configuration of the metrics server. The metrics
// endpoint is enabled in 'config/default/kustomization.yaml'.
// More info:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/server
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.25.2/pkg/metrics/server
// - https://book.kubebuilder.io/reference/metrics.html
func metricsOptions(addr string, secure bool, tlsOpts []func(*tls.Config)) metricsserver.Options {
metricsServerOptions := metricsserver.Options{
Expand All @@ -148,7 +148,7 @@ func metricsOptions(addr string, secure bool, tlsOpts []func(*tls.Config)) metri

if secure {
// More info:
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/filters#WithAuthenticationAndAuthorization
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.25.2/pkg/metrics/filters#WithAuthenticationAndAuthorization
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
}

Expand Down
30 changes: 13 additions & 17 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,27 @@ require (
github.com/spf13/cobra v1.10.2
github.com/vishvananda/netlink v1.3.1
golang.org/x/sys v0.49.0
k8s.io/api v0.35.0
k8s.io/api v0.37.1
k8s.io/apimachinery v0.37.1
k8s.io/client-go v0.35.0
k8s.io/client-go v0.37.1
k8s.io/klog/v2 v2.140.0
sigs.k8s.io/controller-runtime v0.19.1
sigs.k8s.io/controller-runtime v0.25.2
sigs.k8s.io/yaml v1.6.0
)

require (
cel.dev/expr v0.25.2 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
Expand All @@ -55,8 +54,7 @@ require (
github.com/go-openapi/swag/yamlutils v0.27.1 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/cel-go v0.29.0 // indirect
github.com/google/cel-go v0.29.2 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect
github.com/google/uuid v1.6.0 // indirect
Expand All @@ -66,7 +64,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.3 // indirect
github.com/prometheus/common v0.72.0 // indirect
Expand All @@ -75,7 +72,7 @@ require (
github.com/vishvananda/netns v0.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect
go.opentelemetry.io/otel v1.45.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0 // indirect
Expand All @@ -84,10 +81,10 @@ require (
go.opentelemetry.io/otel/trace v1.45.0 // indirect
go.opentelemetry.io/proto/otlp v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap v1.27.1 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
golang.org/x/mod v0.41.0 // indirect
golang.org/x/net v0.60.0 // indirect
golang.org/x/oauth2 v0.37.0 // indirect
Expand All @@ -103,14 +100,13 @@ require (
google.golang.org/protobuf v1.36.12 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apiextensions-apiserver v0.31.0 // indirect
k8s.io/apiserver v0.31.0 // indirect
k8s.io/component-base v0.31.0 // indirect
k8s.io/apiextensions-apiserver v0.37.0 // indirect
k8s.io/apiserver v0.37.0 // indirect
k8s.io/component-base v0.37.0 // indirect
k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad // indirect
k8s.io/streaming v0.37.1 // indirect
k8s.io/utils v0.0.0-20260626114624-be93311217bd // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.36.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect
Expand Down
Loading
Loading