Skip to content

Commit b04650c

Browse files
scotwellsclaude
andcommitted
feat(webhook): validation updates for federation
Update Workload webhook and Instance validation so the API accepts the fields federated scheduling adds and continues to reject invalid placement and runtime specs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8fb70f0 commit b04650c

3 files changed

Lines changed: 75 additions & 79 deletions

File tree

internal/validation/instance_validation.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ import (
1717
networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha"
1818
)
1919

20+
// Validation constants for well-known string literals used across multiple
21+
// validation functions.
22+
const (
23+
// diskTypePDStandard is the only currently supported disk type.
24+
diskTypePDStandard = "pd-standard"
25+
26+
// defaultImageName is the only currently supported container image.
27+
defaultImageName = "datumcloud/ubuntu-2204-lts"
28+
29+
// defaultInstanceType is the only currently supported instance type.
30+
defaultInstanceType = "datumcloud/d1-standard-2"
31+
)
32+
2033
func validateInstanceTemplate(
2134
template computev1alpha.InstanceTemplateSpec,
2235
fieldPath *field.Path,
@@ -97,6 +110,11 @@ func validateInstanceNetworkInterfaces(
97110
allErrs = append(allErrs, field.Invalid(networkNameField, networkInterface.Network, msg))
98111
}
99112

113+
extra := make(map[string]authorizationv1.ExtraValue, len(opts.AdmissionRequest.UserInfo.Extra))
114+
for k, v := range opts.AdmissionRequest.UserInfo.Extra {
115+
extra[k] = authorizationv1.ExtraValue(v)
116+
}
117+
100118
review := authorizationv1.SubjectAccessReview{
101119
Spec: authorizationv1.SubjectAccessReviewSpec{
102120
ResourceAttributes: &authorizationv1.ResourceAttributes{
@@ -110,6 +128,7 @@ func validateInstanceNetworkInterfaces(
110128
User: opts.AdmissionRequest.UserInfo.Username,
111129
Groups: opts.AdmissionRequest.UserInfo.Groups,
112130
UID: opts.AdmissionRequest.UserInfo.UID,
131+
Extra: extra,
113132
},
114133
}
115134

@@ -258,8 +277,8 @@ func validateDiskVolumeSource(diskSource *computev1alpha.DiskTemplateVolumeSourc
258277
diskTemplateSpecField := diskTemplateField.Child("spec")
259278

260279
// TODO(jrese) look up valid disk types
261-
if diskTemplate.Spec.Type != "pd-standard" {
262-
allErrs = append(allErrs, field.NotSupported(diskTemplateSpecField.Child("type"), diskTemplate.Spec.Type, []string{"pd-standard"}))
280+
if diskTemplate.Spec.Type != diskTypePDStandard {
281+
allErrs = append(allErrs, field.NotSupported(diskTemplateSpecField.Child("type"), diskTemplate.Spec.Type, []string{diskTypePDStandard}))
263282
}
264283

265284
populatorResourceRequests, errs := validateDiskPopulator(diskTemplate.Spec.Populator, diskTemplateField.Child("populator"))
@@ -400,8 +419,8 @@ func validateDiskPopulator(populator *computev1alpha.DiskPopulator, fieldPath *f
400419

401420
// TODO(jreese) look up image
402421
imagePopulator := populator.Image
403-
if imagePopulator.Name != "datumcloud/ubuntu-2204-lts" {
404-
allErrs = append(allErrs, field.NotSupported(imageField.Child("name"), imagePopulator.Name, []string{"datumcloud/ubuntu-2204-lts"}))
422+
if imagePopulator.Name != defaultImageName {
423+
allErrs = append(allErrs, field.NotSupported(imageField.Child("name"), imagePopulator.Name, []string{defaultImageName}))
405424
}
406425
}
407426
}
@@ -657,8 +676,8 @@ func validateInstanceRuntimeResources(resources computev1alpha.InstanceRuntimeRe
657676
allErrs := field.ErrorList{}
658677

659678
// TODO(jreese) look up available instance types
660-
if resources.InstanceType != "datumcloud/d1-standard-2" {
661-
allErrs = append(allErrs, field.NotSupported(fieldPath, resources.InstanceType, []string{"datumcloud/d1-standard-2"}))
679+
if resources.InstanceType != defaultInstanceType {
680+
allErrs = append(allErrs, field.NotSupported(fieldPath, resources.InstanceType, []string{defaultInstanceType}))
662681
}
663682

664683
if resources.Requests != nil {

internal/validation/workload_validation_test.go

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ import (
2323
networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha"
2424
)
2525

26+
// Test constants for repeated string literals.
27+
const (
28+
testCPUResource = "cpu"
29+
testVolName = "vol"
30+
testDuplicateMountPath = "duplicate-mount-path"
31+
testDefaultNamespace = "default"
32+
testCityCodeDFW = "DFW"
33+
)
34+
2635
func TestValidateWorkloads(t *testing.T) {
2736
scenarios := map[string]struct {
2837
workload *computev1alpha.Workload
@@ -157,7 +166,7 @@ func TestValidateWorkloads(t *testing.T) {
157166
w.Spec.Placements[0].ScaleSettings.Metrics = []computev1alpha.MetricSpec{
158167
{
159168
Resource: &computev1alpha.ResourceMetricSource{
160-
Name: "cpu",
169+
Name: testCPUResource,
161170
Target: computev1alpha.MetricTarget{
162171
Value: resource.NewQuantity(50, resource.DecimalSI),
163172
AverageValue: resource.NewQuantity(50, resource.DecimalSI),
@@ -181,7 +190,7 @@ func TestValidateWorkloads(t *testing.T) {
181190
w.Spec.Placements[0].ScaleSettings.Metrics = []computev1alpha.MetricSpec{
182191
{
183192
Resource: &computev1alpha.ResourceMetricSource{
184-
Name: "cpu",
193+
Name: testCPUResource,
185194
Target: computev1alpha.MetricTarget{
186195
Value: resource.NewQuantity(-1, resource.DecimalSI),
187196
},
@@ -202,7 +211,7 @@ func TestValidateWorkloads(t *testing.T) {
202211
w.Spec.Placements[0].ScaleSettings.Metrics = []computev1alpha.MetricSpec{
203212
{
204213
Resource: &computev1alpha.ResourceMetricSource{
205-
Name: "cpu",
214+
Name: testCPUResource,
206215
Target: computev1alpha.MetricTarget{
207216
AverageValue: resource.NewQuantity(-1, resource.DecimalSI),
208217
},
@@ -223,7 +232,7 @@ func TestValidateWorkloads(t *testing.T) {
223232
w.Spec.Placements[0].ScaleSettings.Metrics = []computev1alpha.MetricSpec{
224233
{
225234
Resource: &computev1alpha.ResourceMetricSource{
226-
Name: "cpu",
235+
Name: testCPUResource,
227236
Target: computev1alpha.MetricTarget{
228237
AverageUtilization: proto.Int32(0),
229238
},
@@ -336,16 +345,16 @@ func TestValidateWorkloads(t *testing.T) {
336345
w.Spec.Template.Spec.Runtime.VirtualMachine.VolumeAttachments = append(
337346
w.Spec.Template.Spec.Runtime.VirtualMachine.VolumeAttachments,
338347
computev1alpha.VolumeAttachment{
339-
Name: "vol",
348+
Name: testVolName,
340349
},
341350
)
342351
w.Spec.Template.Spec.Volumes = append(w.Spec.Template.Spec.Volumes, computev1alpha.InstanceVolume{
343-
Name: "vol",
352+
Name: testVolName,
344353
VolumeSource: computev1alpha.VolumeSource{
345354
Disk: &computev1alpha.DiskTemplateVolumeSource{
346355
Template: &computev1alpha.DiskTemplateVolumeSourceTemplate{
347356
Spec: computev1alpha.DiskSpec{
348-
Type: "pd-standard",
357+
Type: diskTypePDStandard,
349358
Resources: &computev1alpha.DiskResourceRequirements{
350359
Requests: k8scorev1.ResourceList{
351360
k8scorev1.ResourceStorage: resource.MustParse("1Gi"),
@@ -369,16 +378,16 @@ func TestValidateWorkloads(t *testing.T) {
369378
w.Spec.Template.Spec.Runtime.VirtualMachine.VolumeAttachments = append(
370379
w.Spec.Template.Spec.Runtime.VirtualMachine.VolumeAttachments,
371380
computev1alpha.VolumeAttachment{
372-
Name: "vol",
381+
Name: testVolName,
373382
},
374383
)
375384
w.Spec.Template.Spec.Volumes = append(w.Spec.Template.Spec.Volumes, computev1alpha.InstanceVolume{
376-
Name: "vol",
385+
Name: testVolName,
377386
VolumeSource: computev1alpha.VolumeSource{
378387
Disk: &computev1alpha.DiskTemplateVolumeSource{
379388
Template: &computev1alpha.DiskTemplateVolumeSourceTemplate{
380389
Spec: computev1alpha.DiskSpec{
381-
Type: "pd-standard",
390+
Type: diskTypePDStandard,
382391
Resources: &computev1alpha.DiskResourceRequirements{
383392
Requests: k8scorev1.ResourceList{
384393
k8scorev1.ResourceStorage: resource.MustParse("1Pi"),
@@ -402,16 +411,16 @@ func TestValidateWorkloads(t *testing.T) {
402411
w.Spec.Template.Spec.Runtime.VirtualMachine.VolumeAttachments = append(
403412
w.Spec.Template.Spec.Runtime.VirtualMachine.VolumeAttachments,
404413
computev1alpha.VolumeAttachment{
405-
Name: "vol",
414+
Name: testVolName,
406415
},
407416
)
408417
w.Spec.Template.Spec.Volumes = append(w.Spec.Template.Spec.Volumes, computev1alpha.InstanceVolume{
409-
Name: "vol",
418+
Name: testVolName,
410419
VolumeSource: computev1alpha.VolumeSource{
411420
Disk: &computev1alpha.DiskTemplateVolumeSource{
412421
Template: &computev1alpha.DiskTemplateVolumeSourceTemplate{
413422
Spec: computev1alpha.DiskSpec{
414-
Type: "pd-standard",
423+
Type: diskTypePDStandard,
415424
Resources: &computev1alpha.DiskResourceRequirements{
416425
Requests: k8scorev1.ResourceList{
417426
k8scorev1.ResourceStorage: resource.MustParse("10.5Gi"),
@@ -436,7 +445,7 @@ func TestValidateWorkloads(t *testing.T) {
436445
Disk: &computev1alpha.DiskTemplateVolumeSource{
437446
Template: &computev1alpha.DiskTemplateVolumeSourceTemplate{
438447
Spec: computev1alpha.DiskSpec{
439-
Type: "pd-standard",
448+
Type: diskTypePDStandard,
440449
Resources: &computev1alpha.DiskResourceRequirements{
441450
Requests: k8scorev1.ResourceList{
442451
k8scorev1.ResourceStorage: resource.MustParse("10Gi"),
@@ -473,7 +482,7 @@ func TestValidateWorkloads(t *testing.T) {
473482
Disk: &computev1alpha.DiskTemplateVolumeSource{
474483
Template: &computev1alpha.DiskTemplateVolumeSourceTemplate{
475484
Spec: computev1alpha.DiskSpec{
476-
Type: "pd-standard",
485+
Type: diskTypePDStandard,
477486
Resources: &computev1alpha.DiskResourceRequirements{
478487
Requests: k8scorev1.ResourceList{
479488
k8scorev1.ResourceStorage: resource.MustParse("10Gi"),
@@ -490,11 +499,11 @@ func TestValidateWorkloads(t *testing.T) {
490499
}
491500
w.Spec.Template.Spec.Runtime.Sandbox.Containers[0].VolumeAttachments = []computev1alpha.VolumeAttachment{
492501
{
493-
Name: "duplicate-mount-path",
502+
Name: testDuplicateMountPath,
494503
MountPath: proto.String("/mount1"),
495504
},
496505
{
497-
Name: "duplicate-mount-path",
506+
Name: testDuplicateMountPath,
498507
MountPath: proto.String("/mount1"),
499508
},
500509
{
@@ -503,7 +512,7 @@ func TestValidateWorkloads(t *testing.T) {
503512
}
504513
w.Spec.Template.Spec.Volumes = []computev1alpha.InstanceVolume{
505514
{
506-
Name: "duplicate-mount-path",
515+
Name: testDuplicateMountPath,
507516
VolumeSource: volumeSource,
508517
},
509518
}
@@ -540,7 +549,7 @@ func TestValidateWorkloads(t *testing.T) {
540549
interceptorFuncs: &interceptor.Funcs{
541550
Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error {
542551
if sar, ok := obj.(*authorizationv1.SubjectAccessReview); ok {
543-
if sar.Spec.ResourceAttributes.Name == "default" &&
552+
if sar.Spec.ResourceAttributes.Name == testDefaultNamespace &&
544553
sar.Spec.ResourceAttributes.Group == networkingv1alpha.GroupVersion.Group &&
545554
sar.Spec.ResourceAttributes.Version == networkingv1alpha.GroupVersion.Version &&
546555
sar.Spec.ResourceAttributes.Resource == "networks" {
@@ -559,8 +568,8 @@ func TestValidateWorkloads(t *testing.T) {
559568
initObjs := []client.Object{
560569
&networkingv1alpha.Network{
561570
ObjectMeta: metav1.ObjectMeta{
562-
Namespace: "default",
563-
Name: "default",
571+
Namespace: testDefaultNamespace,
572+
Name: testDefaultNamespace,
564573
},
565574
},
566575
}
@@ -606,7 +615,7 @@ func TestValidateWorkloads(t *testing.T) {
606615
)
607616

608617
if len(scenario.opts.ValidCityCodes) == 0 {
609-
scenario.opts.ValidCityCodes = []string{"DFW"}
618+
scenario.opts.ValidCityCodes = []string{testCityCodeDFW}
610619
}
611620

612621
t.Run(name, func(t *testing.T) {
@@ -645,7 +654,7 @@ func MakeSandboxWorkload(name string, tweaks ...Tweak) *computev1alpha.Workload
645654
},
646655
Runtime: computev1alpha.InstanceRuntimeSpec{
647656
Resources: computev1alpha.InstanceRuntimeResources{
648-
InstanceType: "datumcloud/d1-standard-2",
657+
InstanceType: defaultInstanceType,
649658
},
650659
Sandbox: &computev1alpha.SandboxRuntime{
651660
Containers: []computev1alpha.SandboxContainer{
@@ -661,7 +670,7 @@ func MakeSandboxWorkload(name string, tweaks ...Tweak) *computev1alpha.Workload
661670
Placements: []computev1alpha.WorkloadPlacement{
662671
{
663672
Name: "placement1",
664-
CityCodes: []string{"DFW"},
673+
CityCodes: []string{testCityCodeDFW},
665674
ScaleSettings: computev1alpha.HorizontalScaleSettings{
666675
MinReplicas: 1,
667676
},
@@ -702,7 +711,7 @@ func MakeVMWorkload(name string, tweaks ...Tweak) *computev1alpha.Workload {
702711
},
703712
Runtime: computev1alpha.InstanceRuntimeSpec{
704713
Resources: computev1alpha.InstanceRuntimeResources{
705-
InstanceType: "datumcloud/d1-standard-2",
714+
InstanceType: defaultInstanceType,
706715
},
707716
VirtualMachine: &computev1alpha.VirtualMachineRuntime{
708717
VolumeAttachments: []computev1alpha.VolumeAttachment{
@@ -719,10 +728,10 @@ func MakeVMWorkload(name string, tweaks ...Tweak) *computev1alpha.Workload {
719728
Disk: &computev1alpha.DiskTemplateVolumeSource{
720729
Template: &computev1alpha.DiskTemplateVolumeSourceTemplate{
721730
Spec: computev1alpha.DiskSpec{
722-
Type: "pd-standard",
731+
Type: diskTypePDStandard,
723732
Populator: &computev1alpha.DiskPopulator{
724733
Image: &computev1alpha.ImageDiskPopulator{
725-
Name: "datumcloud/ubuntu-2204-lts",
734+
Name: defaultImageName,
726735
},
727736
},
728737
},
@@ -736,7 +745,7 @@ func MakeVMWorkload(name string, tweaks ...Tweak) *computev1alpha.Workload {
736745
Placements: []computev1alpha.WorkloadPlacement{
737746
{
738747
Name: "placement1",
739-
CityCodes: []string{"DFW"},
748+
CityCodes: []string{testCityCodeDFW},
740749
ScaleSettings: computev1alpha.HorizontalScaleSettings{
741750
MinReplicas: 1,
742751
},

0 commit comments

Comments
 (0)