Skip to content

Commit e4487b4

Browse files
committed
feat(operator): enforce RFC 1035 validation using CEL rules for TrainJob name
Signed-off-by: Junie <juniemariam@gmail.com>
1 parent 3846ba6 commit e4487b4

5 files changed

Lines changed: 13 additions & 47 deletions

File tree

charts/kubeflow-trainer/crds/trainer.kubeflow.org_trainjobs.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3081,6 +3081,11 @@ spec:
30813081
x-kubernetes-list-type: map
30823082
type: object
30833083
type: object
3084+
x-kubernetes-validations:
3085+
- message: metadata.name must match RFC 1035 DNS label format
3086+
rule: self.metadata.name.matches('^[a-z]([-a-z0-9]*[a-z0-9])?$')
3087+
- message: metadata.name must be no more than 63 characters
3088+
rule: size(self.metadata.name) <= 63
30843089
served: true
30853090
storage: true
30863091
subresources:

manifests/base/crds/trainer.kubeflow.org_trainjobs.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3081,6 +3081,11 @@ spec:
30813081
x-kubernetes-list-type: map
30823082
type: object
30833083
type: object
3084+
x-kubernetes-validations:
3085+
- message: metadata.name must match RFC 1035 DNS label format
3086+
rule: self.metadata.name.matches('^[a-z]([-a-z0-9]*[a-z0-9])?$')
3087+
- message: metadata.name must be no more than 63 characters
3088+
rule: size(self.metadata.name) <= 63
30843089
served: true
30853090
storage: true
30863091
subresources:

pkg/apis/trainer/v1alpha1/trainjob_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ const (
3434
// +kubebuilder:storageversion
3535
// +kubebuilder:printcolumn:name="State",type=string,JSONPath=`.status.conditions[-1:].type`
3636
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
37+
// +kubebuilder:validation:XValidation:rule="self.metadata.name.matches('^[a-z]([-a-z0-9]*[a-z0-9])?$')", message="metadata.name must match RFC 1035 DNS label format"
38+
// +kubebuilder:validation:XValidation:rule="size(self.metadata.name) <= 63", message="metadata.name must be no more than 63 characters"
3739

3840
// TrainJob represents configuration of a training job.
3941
type TrainJob struct {

pkg/webhooks/trainjob_webhook.go

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ import (
2121
"fmt"
2222

2323
apiruntime "k8s.io/apimachinery/pkg/runtime"
24-
"k8s.io/apimachinery/pkg/util/validation"
25-
"k8s.io/apimachinery/pkg/util/validation/field"
2624
"k8s.io/klog/v2"
2725
ctrl "sigs.k8s.io/controller-runtime"
2826
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -52,18 +50,13 @@ func (w *TrainJobWebhook) ValidateCreate(ctx context.Context, obj apiruntime.Obj
5250
log := ctrl.LoggerFrom(ctx).WithName("trainJob-webhook")
5351
log.V(5).Info("Validating create", "TrainJob", klog.KObj(trainJob))
5452

55-
allErrs := field.ErrorList{}
56-
57-
// Validate TrainJob name (RFC 1035 compliance)
58-
allErrs = append(allErrs, validateTrainJobNameRFC1035(trainJob.Name)...)
59-
6053
runtimeRefGK := runtime.RuntimeRefToRuntimeRegistryKey(trainJob.Spec.RuntimeRef)
6154
runtime, ok := w.runtimes[runtimeRefGK]
6255
if !ok {
6356
return nil, fmt.Errorf("unsupported runtime: %s", runtimeRefGK)
6457
}
6558
warnings, errors := runtime.ValidateObjects(ctx, nil, trainJob)
66-
return warnings, append(allErrs, errors...).ToAggregate()
59+
return warnings, errors.ToAggregate()
6760
}
6861

6962
func (w *TrainJobWebhook) ValidateUpdate(ctx context.Context, oldObj apiruntime.Object, newObj apiruntime.Object) (admission.Warnings, error) {
@@ -83,17 +76,3 @@ func (w *TrainJobWebhook) ValidateUpdate(ctx context.Context, oldObj apiruntime.
8376
func (w *TrainJobWebhook) ValidateDelete(context.Context, apiruntime.Object) (admission.Warnings, error) {
8477
return nil, nil
8578
}
86-
87-
// validateTrainJobNameRFC1035 checks if the name is compliant with RFC 1035 label requirements
88-
func validateTrainJobNameRFC1035(name string) field.ErrorList {
89-
var allErrs field.ErrorList
90-
namePath := field.NewPath("metadata", "name")
91-
92-
for _, err := range validation.IsDNS1035Label(name) {
93-
allErrs = append(allErrs, field.Invalid(namePath, name, err))
94-
}
95-
if len(name) > 63 {
96-
allErrs = append(allErrs, field.Invalid(namePath, name, "must be no more than 63 characters"))
97-
}
98-
return allErrs
99-
}

pkg/webhooks/trainjob_webhook_test.go

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,31 +45,6 @@ func TestValidateCreate(t *testing.T) {
4545
wantError: nil,
4646
wantWarnings: nil,
4747
},
48-
"invalid trainjob name with uppercase letters": {
49-
obj: testingutil.MakeTrainJobWrapper("default", "Invalid-job-name").
50-
RuntimeRef(trainer.SchemeGroupVersion.WithKind(trainer.ClusterTrainingRuntimeKind), "test-runtime").
51-
Obj(),
52-
wantError: field.ErrorList{
53-
&field.Error{
54-
Type: field.ErrorTypeInvalid,
55-
Field: "metadata.name",
56-
BadValue: "Invalid-job-name",
57-
},
58-
},
59-
wantWarnings: nil,
60-
},
61-
"trainjob name exceeds 63 characters": {
62-
obj: testingutil.MakeTrainJobWrapper("default", "this-name-is-way-too-long-for-a-rfc1035-label-and-should-fail-validation").
63-
RuntimeRef(trainer.SchemeGroupVersion.WithKind(trainer.ClusterTrainingRuntimeKind), "test-runtime").
64-
Obj(),
65-
wantError: field.ErrorList{
66-
&field.Error{
67-
Type: field.ErrorTypeInvalid,
68-
Field: "metadata.name",
69-
BadValue: "this-name-is-way-too-long-for-a-rfc1035-label-and-should-fail-validation" },
70-
},
71-
wantWarnings: nil,
72-
},
7348
"unsupported runtime": {
7449
obj: testingutil.MakeTrainJobWrapper("default", "valid-job-name").
7550
RuntimeRef(trainer.SchemeGroupVersion.WithKind(trainer.ClusterTrainingRuntimeKind), "unsupported-runtime").

0 commit comments

Comments
 (0)