api: add resource claims to core API

The resource.k8s.io/ClaimTemplate only gets referenced by name, therefore the
changes to the core API are limited.
This commit is contained in:
Patrick Ohly
2022-11-04 13:46:49 +01:00
parent 155d49813f
commit 7d11b422e3
7 changed files with 687 additions and 30 deletions

View File

@@ -5371,7 +5371,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
resource.BinarySI),
},
}
if errs := ValidateResourceRequirements(&containerLimitCase, field.NewPath("resources"), PodValidationOptions{}); len(errs) != 0 {
if errs := ValidateResourceRequirements(&containerLimitCase, nil, field.NewPath("resources"), PodValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
@@ -6841,7 +6841,7 @@ func TestValidateEphemeralContainers(t *testing.T) {
},
},
} {
if errs := validateEphemeralContainers(ephemeralContainers, containers, initContainers, vols, field.NewPath("ephemeralContainers"), PodValidationOptions{}); len(errs) != 0 {
if errs := validateEphemeralContainers(ephemeralContainers, containers, initContainers, vols, nil, field.NewPath("ephemeralContainers"), PodValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success for '%s' but got errors: %v", title, errs)
}
}
@@ -7123,7 +7123,7 @@ func TestValidateEphemeralContainers(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.title+"__@L"+tc.line, func(t *testing.T) {
errs := validateEphemeralContainers(tc.ephemeralContainers, containers, initContainers, vols, field.NewPath("ephemeralContainers"), PodValidationOptions{})
errs := validateEphemeralContainers(tc.ephemeralContainers, containers, initContainers, vols, nil, field.NewPath("ephemeralContainers"), PodValidationOptions{})
if len(errs) == 0 {
t.Fatal("expected error but received none")
}
@@ -7402,7 +7402,7 @@ func TestValidateContainers(t *testing.T) {
TerminationMessagePolicy: "File",
},
}
if errs := validateContainers(successCase, volumeDevices, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
if errs := validateContainers(successCase, volumeDevices, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
@@ -8026,7 +8026,7 @@ func TestValidateContainers(t *testing.T) {
}
for _, tc := range errorCases {
t.Run(tc.title+"__@L"+tc.line, func(t *testing.T) {
errs := validateContainers(tc.containers, volumeDevices, field.NewPath("containers"), PodValidationOptions{})
errs := validateContainers(tc.containers, volumeDevices, nil, field.NewPath("containers"), PodValidationOptions{})
if len(errs) == 0 {
t.Fatal("expected error but received none")
}
@@ -8076,7 +8076,7 @@ func TestValidateInitContainers(t *testing.T) {
TerminationMessagePolicy: "File",
},
}
if errs := validateInitContainers(successCase, containers, volumeDevices, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
if errs := validateInitContainers(successCase, containers, volumeDevices, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
@@ -8268,7 +8268,7 @@ func TestValidateInitContainers(t *testing.T) {
}
for _, tc := range errorCases {
t.Run(tc.title+"__@L"+tc.line, func(t *testing.T) {
errs := validateInitContainers(tc.initContainers, containers, volumeDevices, field.NewPath("initContainers"), PodValidationOptions{})
errs := validateInitContainers(tc.initContainers, containers, volumeDevices, nil, field.NewPath("initContainers"), PodValidationOptions{})
if len(errs) == 0 {
t.Fatal("expected error but received none")
}
@@ -18611,6 +18611,9 @@ func TestValidateOSFields(t *testing.T) {
"Priority",
"PriorityClassName",
"ReadinessGates",
"ResourceClaims[*].Name",
"ResourceClaims[*].Source.ResourceClaimName",
"ResourceClaims[*].Source.ResourceClaimTemplateName",
"RestartPolicy",
"RuntimeClassName",
"SchedulerName",
@@ -20914,7 +20917,7 @@ func TestValidateResourceRequirements(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if errs := ValidateResourceRequirements(&tc.requirements, path, tc.opts); len(errs) != 0 {
if errs := ValidateResourceRequirements(&tc.requirements, nil, path, tc.opts); len(errs) != 0 {
t.Errorf("unexpected errors: %v", errs)
}
})
@@ -20941,7 +20944,7 @@ func TestValidateResourceRequirements(t *testing.T) {
for _, tc := range errTests {
t.Run(tc.name, func(t *testing.T) {
if errs := ValidateResourceRequirements(&tc.requirements, path, tc.opts); len(errs) == 0 {
if errs := ValidateResourceRequirements(&tc.requirements, nil, path, tc.opts); len(errs) == 0 {
t.Error("expected errors")
}
})
@@ -21681,3 +21684,220 @@ func TestValidatePVSecretReference(t *testing.T) {
})
}
}
func TestValidateDynamicResourceAllocation(t *testing.T) {
externalClaimName := "some-claim"
externalClaimTemplateName := "some-claim-template"
goodClaimSource := core.ClaimSource{
ResourceClaimName: &externalClaimName,
}
successCases := map[string]core.PodSpec{
"resource claim reference": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: core.ClaimSource{
ResourceClaimName: &externalClaimName,
},
},
},
},
"resource claim template": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: core.ClaimSource{
ResourceClaimTemplateName: &externalClaimTemplateName,
},
},
},
},
"multiple claims": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}, {Name: "another-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: goodClaimSource,
},
{
Name: "another-claim",
Source: goodClaimSource,
},
},
},
"init container": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
InitContainers: []core.Container{{Name: "ctr-init", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: goodClaimSource,
},
},
},
}
for k, v := range successCases {
t.Run(k, func(t *testing.T) {
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
})
}
failureCases := map[string]core.PodSpec{
"pod claim name with prefix": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "../my-claim",
Source: goodClaimSource,
},
},
},
"pod claim name with path": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my/claim",
Source: goodClaimSource,
},
},
},
"pod claim name empty": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "",
Source: goodClaimSource,
},
},
},
"duplicate pod claim entries": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: goodClaimSource,
},
{
Name: "my-claim",
Source: goodClaimSource,
},
},
},
"resource claim source empty": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: core.ClaimSource{},
},
},
},
"resource claim reference and template": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: core.ClaimSource{
ResourceClaimName: &externalClaimName,
ResourceClaimTemplateName: &externalClaimTemplateName,
},
},
},
},
"claim not found": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "no-such-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: goodClaimSource,
},
},
},
"claim name empty": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: ""}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: goodClaimSource,
},
},
},
"pod claim name duplicates": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}, {Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: goodClaimSource,
},
},
},
"no claims defined": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
"duplicate pod claim name": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: goodClaimSource,
},
{
Name: "my-claim",
Source: goodClaimSource,
},
},
},
"ephemeral container don't support resource requirements": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
EphemeralContainers: []core.EphemeralContainer{{EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "ctr-ephemeral", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}, TargetContainerName: "ctr"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
ResourceClaims: []core.PodResourceClaim{
{
Name: "my-claim",
Source: goodClaimSource,
},
},
},
}
for k, v := range failureCases {
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
t.Errorf("expected failure for %q", k)
}
}
}