Skip to content

Commit fc12add

Browse files
TomerNewmanybettan
authored andcommitted
Adding PreflightValidation Webhook
Until now users were able to add invalid kernel versions to PreflightValidation, which made KMM controller panic. This commit adds a validation webhook for PreflightValidation resource.
1 parent 1ee54cd commit fc12add

File tree

7 files changed

+160
-9
lines changed

7 files changed

+160
-9
lines changed

cmd/webhook-server/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ func main() {
109109
cmd.FatalError(setupLogger, err, "unable to create conversion webhook", "name", "PreflightValidation/v1beta1")
110110
}
111111

112-
if err = ctrl.NewWebhookManagedBy(mgr).For(&kmmv1beta2.PreflightValidation{}).Complete(); err != nil {
113-
cmd.FatalError(setupLogger, err, "unable to create conversion webhook", "name", "PreflightValidation/v1beta2")
112+
logger.Info("Enabling PreflightValidation webhook")
113+
if err = webhook.NewPreflightValidationValidator(logger).SetupWebhookWithManager(mgr, &kmmv1beta2.PreflightValidation{}); err != nil {
114+
cmd.FatalError(setupLogger, err, "unable to create webhook", "webhook", "PreflightValidationValidator")
114115
}
115116
}
116117

config/webhook/manifests.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,23 @@ webhooks:
4343
resources:
4444
- modules
4545
sideEffects: None
46+
- admissionReviewVersions:
47+
- v1
48+
clientConfig:
49+
service:
50+
name: webhook-service
51+
namespace: system
52+
path: /validate-kmm-sigs-x-k8s-io-v1beta2-preflightvalidation
53+
failurePolicy: Fail
54+
name: vpreflightvalidation.kb.io
55+
rules:
56+
- apiGroups:
57+
- kmm.sigs.x-k8s.io
58+
apiVersions:
59+
- v1beta2
60+
operations:
61+
- CREATE
62+
- UPDATE
63+
resources:
64+
- preflightvalidations
65+
sideEffects: None

internal/module/kernelmapper.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,10 @@ func (kh *kernelMapperHelper) prepareModuleLoaderData(mapping *kmmv1beta1.Kernel
149149
}
150150

151151
func (kh *kernelMapperHelper) replaceTemplates(mld *api.ModuleLoaderData) error {
152-
osConfigEnvVars := utils.KernelComponentsAsEnvVars(mld.KernelNormalizedVersion)
152+
osConfigEnvVars, err := utils.KernelComponentsAsEnvVars(mld.KernelNormalizedVersion)
153+
if err != nil {
154+
return fmt.Errorf("failed to get kernel componnents as env variables, %v", err)
155+
}
153156
osConfigEnvVars = append(osConfigEnvVars, "MOD_NAME="+mld.Name, "MOD_NAMESPACE="+mld.Namespace)
154157

155158
replacedContainerImage, err := utils.ReplaceInTemplates(osConfigEnvVars, mld.ContainerImage)
@@ -205,9 +208,12 @@ func (kh *kernelMapperHelper) getRelevantSign(moduleSign *kmmv1beta1.Sign, mappi
205208
signConfig.FilesToSign = append(signConfig.FilesToSign, mappingSign.FilesToSign...)
206209
}
207210

208-
osConfigEnvVars := utils.KernelComponentsAsEnvVars(
211+
osConfigEnvVars, err := utils.KernelComponentsAsEnvVars(
209212
kernel.NormalizeVersion(kernelVersion),
210213
)
214+
if err != nil {
215+
return nil, fmt.Errorf("failed to get kernel componnents as env variables, %v", err)
216+
}
211217
unsignedImage, err := utils.ReplaceInTemplates(osConfigEnvVars, signConfig.UnsignedImage)
212218
if err != nil {
213219
return nil, err

internal/utils/replaceStrings.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ const (
1414
kernelVersionPatchIdx = 2
1515
)
1616

17-
var kernelRegexp = regexp.MustCompile("[.,-]")
17+
var KernelRegexp = regexp.MustCompile("[.,-]")
1818

19-
func KernelComponentsAsEnvVars(kernel string) []string {
20-
osConfigFieldsList := kernelRegexp.Split(kernel, -1)
19+
func KernelComponentsAsEnvVars(kernel string) ([]string, error) {
20+
osConfigFieldsList := KernelRegexp.Split(kernel, -1)
21+
if len(osConfigFieldsList) < 3 {
22+
return nil, fmt.Errorf("invalid kernel version %s: expected at least three components (major.minor.patch)", kernel)
23+
}
2124

2225
envvars := []string{
2326
"KERNEL_FULL_VERSION=" + kernel,
@@ -28,7 +31,7 @@ func KernelComponentsAsEnvVars(kernel string) []string {
2831
"KERNEL_Z=" + osConfigFieldsList[kernelVersionPatchIdx],
2932
}
3033

31-
return envvars
34+
return envvars, nil
3235
}
3336

3437
func ReplaceInTemplates(envvars []string, templates ...string) ([]string, error) {

internal/utils/replaceStrings_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,16 @@ var _ = Describe("KernelComponentsAsEnvVars", func() {
1717
"KERNEL_Y=0",
1818
"KERNEL_Z=15",
1919
}
20+
osConfigEnvVars, err := KernelComponentsAsEnvVars(kernelVersion)
21+
Expect(osConfigEnvVars).To(Equal(expected))
22+
Expect(err).ToNot(HaveOccurred())
23+
})
2024

21-
Expect(KernelComponentsAsEnvVars(kernelVersion)).To(Equal(expected))
25+
It("should fail due to invalid kernel version", func() {
26+
const kernelVersion = "test"
27+
osConfigEnvVars, err := KernelComponentsAsEnvVars(kernelVersion)
28+
Expect(osConfigEnvVars).To(BeNil())
29+
Expect(err).To(HaveOccurred())
2230
})
2331
})
2432

internal/webhook/preflight.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package webhook
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/go-logr/logr"
7+
kmmv1beta2 "github.com/kubernetes-sigs/kernel-module-management/api/v1beta2"
8+
"github.com/kubernetes-sigs/kernel-module-management/internal/utils"
9+
"k8s.io/apimachinery/pkg/runtime"
10+
ctrl "sigs.k8s.io/controller-runtime"
11+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
12+
)
13+
14+
// PreflightValidationValidator validates PreflightValidation resources.
15+
type PreflightValidationValidator struct {
16+
logger logr.Logger
17+
}
18+
19+
func NewPreflightValidationValidator(logger logr.Logger) *PreflightValidationValidator {
20+
return &PreflightValidationValidator{logger: logger}
21+
}
22+
23+
func (v *PreflightValidationValidator) SetupWebhookWithManager(mgr ctrl.Manager, pf *kmmv1beta2.PreflightValidation) error {
24+
return ctrl.NewWebhookManagedBy(mgr).
25+
For(pf).
26+
WithValidator(v).
27+
Complete()
28+
}
29+
30+
//+kubebuilder:webhook:path=/validate-kmm-sigs-x-k8s-io-v1beta2-preflightvalidation,mutating=false,failurePolicy=fail,sideEffects=None,groups=kmm.sigs.x-k8s.io,resources=preflightvalidations,verbs=create;update,versions=v1beta2,name=vpreflightvalidation.kb.io,admissionReviewVersions=v1
31+
32+
func (v *PreflightValidationValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
33+
pv, ok := obj.(*kmmv1beta2.PreflightValidation)
34+
if !ok {
35+
return nil, fmt.Errorf("bad type for the object; expected %v, got %v", pv, obj)
36+
}
37+
38+
v.logger.Info("Validating PreflightValidation creation", "name", pv.Name)
39+
return validatePreflight(pv)
40+
}
41+
42+
func (v *PreflightValidationValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
43+
oldPV, ok := oldObj.(*kmmv1beta2.PreflightValidation)
44+
if !ok {
45+
return nil, fmt.Errorf("bad type for the old object; expected %v, got %v", oldPV, oldObj)
46+
}
47+
48+
newPV, ok := newObj.(*kmmv1beta2.PreflightValidation)
49+
if !ok {
50+
return nil, fmt.Errorf("bad type for the new object; expected %v, got %v", newPV, newObj)
51+
}
52+
53+
v.logger.Info("Validating PreflightValidation update", "name", oldPV.Name)
54+
return validatePreflight(newPV)
55+
}
56+
57+
func (v *PreflightValidationValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
58+
return nil, NotImplemented
59+
}
60+
61+
func validatePreflight(pv *kmmv1beta2.PreflightValidation) (admission.Warnings, error) {
62+
if pv.Spec.KernelVersion == "" {
63+
return nil, fmt.Errorf("kernelVersion cannot be empty")
64+
}
65+
66+
fields := utils.KernelRegexp.Split(pv.Spec.KernelVersion, -1)
67+
if len(fields) < 3 {
68+
return nil, fmt.Errorf("invalid kernelVersion %s", pv.Spec.KernelVersion)
69+
}
70+
71+
return nil, nil
72+
}

internal/webhook/preflight_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package webhook
2+
3+
import (
4+
"context"
5+
6+
kmmv1beta2 "github.com/kubernetes-sigs/kernel-module-management/api/v1beta2"
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
var _ = Describe("validatePreflight", func() {
12+
It("should fail with invalid kernel version", func() {
13+
pv := &kmmv1beta2.PreflightValidation{
14+
Spec: kmmv1beta2.PreflightValidationSpec{
15+
KernelVersion: "test",
16+
},
17+
}
18+
_, err := validatePreflight(pv)
19+
Expect(err).To(HaveOccurred())
20+
})
21+
22+
It("should pass with valid kernel version", func() {
23+
pv := &kmmv1beta2.PreflightValidation{
24+
Spec: kmmv1beta2.PreflightValidationSpec{
25+
KernelVersion: "6.0.15-300.fc37.x86_64",
26+
},
27+
}
28+
_, err := validatePreflight(pv)
29+
Expect(err).NotTo(HaveOccurred())
30+
})
31+
})
32+
33+
var _ = Describe("PreflightValidationValidator", func() {
34+
v := NewPreflightValidationValidator(GinkgoLogr)
35+
ctx := context.TODO()
36+
37+
It("ValidateDelete should return not implemented", func() {
38+
_, err := v.ValidateDelete(ctx, nil)
39+
Expect(err).To(Equal(NotImplemented))
40+
})
41+
})

0 commit comments

Comments
 (0)