Skip to content

Commit ec220ed

Browse files
TomerNewmank8s-ci-robot
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 5ece8f0 commit ec220ed

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)