mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Promote PodOS field to GA
This commit is contained in:
parent
5108b0a3a0
commit
b79ebb8165
@ -418,8 +418,6 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
|
|||||||
AllowWindowsHostProcessField: utilfeature.DefaultFeatureGate.Enabled(features.WindowsHostProcessContainers),
|
AllowWindowsHostProcessField: utilfeature.DefaultFeatureGate.Enabled(features.WindowsHostProcessContainers),
|
||||||
// Allow pod spec with expanded DNS configuration
|
// Allow pod spec with expanded DNS configuration
|
||||||
AllowExpandedDNSConfig: utilfeature.DefaultFeatureGate.Enabled(features.ExpandedDNSConfig) || haveSameExpandedDNSConfig(podSpec, oldPodSpec),
|
AllowExpandedDNSConfig: utilfeature.DefaultFeatureGate.Enabled(features.ExpandedDNSConfig) || haveSameExpandedDNSConfig(podSpec, oldPodSpec),
|
||||||
// Allow pod spec to use OS field
|
|
||||||
AllowOSField: utilfeature.DefaultFeatureGate.Enabled(features.IdentifyPodOS),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldPodSpec != nil {
|
if oldPodSpec != nil {
|
||||||
@ -435,9 +433,6 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
|
|||||||
// if old spec has Windows Host Process fields set, we must allow it
|
// if old spec has Windows Host Process fields set, we must allow it
|
||||||
opts.AllowWindowsHostProcessField = opts.AllowWindowsHostProcessField || setsWindowsHostProcess(oldPodSpec)
|
opts.AllowWindowsHostProcessField = opts.AllowWindowsHostProcessField || setsWindowsHostProcess(oldPodSpec)
|
||||||
|
|
||||||
// if old spec has OS field set, we must allow it
|
|
||||||
opts.AllowOSField = opts.AllowOSField || oldPodSpec.OS != nil
|
|
||||||
|
|
||||||
// if old spec used non-integer multiple of huge page unit size, we must allow it
|
// if old spec used non-integer multiple of huge page unit size, we must allow it
|
||||||
opts.AllowIndivisibleHugePagesValues = usesIndivisibleHugePagesValues(oldPodSpec)
|
opts.AllowIndivisibleHugePagesValues = usesIndivisibleHugePagesValues(oldPodSpec)
|
||||||
|
|
||||||
@ -556,10 +551,6 @@ func dropDisabledFields(
|
|||||||
|
|
||||||
dropDisabledCSIVolumeSourceAlphaFields(podSpec, oldPodSpec)
|
dropDisabledCSIVolumeSourceAlphaFields(podSpec, oldPodSpec)
|
||||||
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IdentifyPodOS) && !podOSInUse(oldPodSpec) {
|
|
||||||
podSpec.OS = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
dropDisabledTopologySpreadConstraintsFields(podSpec, oldPodSpec)
|
dropDisabledTopologySpreadConstraintsFields(podSpec, oldPodSpec)
|
||||||
dropDisabledNodeInclusionPolicyFields(podSpec, oldPodSpec)
|
dropDisabledNodeInclusionPolicyFields(podSpec, oldPodSpec)
|
||||||
}
|
}
|
||||||
@ -591,17 +582,6 @@ func minDomainsInUse(podSpec *api.PodSpec) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// podOSInUse returns true if the pod spec is non-nil and has OS field set
|
|
||||||
func podOSInUse(podSpec *api.PodSpec) bool {
|
|
||||||
if podSpec == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if podSpec.OS != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// dropDisabledProcMountField removes disabled fields from PodSpec related
|
// dropDisabledProcMountField removes disabled fields from PodSpec related
|
||||||
// to ProcMount only if it is not already used by the old spec
|
// to ProcMount only if it is not already used by the old spec
|
||||||
func dropDisabledProcMountField(podSpec, oldPodSpec *api.PodSpec) {
|
func dropDisabledProcMountField(podSpec, oldPodSpec *api.PodSpec) {
|
||||||
|
@ -1687,88 +1687,6 @@ func TestDropDisabledTopologySpreadConstraintsFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDropOSField(t *testing.T) {
|
|
||||||
podWithOSField := func() *api.Pod {
|
|
||||||
osField := api.PodOS{Name: "linux"}
|
|
||||||
return &api.Pod{
|
|
||||||
Spec: api.PodSpec{
|
|
||||||
OS: &osField,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
podWithoutOSField := func() *api.Pod { return &api.Pod{} }
|
|
||||||
podInfo := []struct {
|
|
||||||
description string
|
|
||||||
hasPodOSField bool
|
|
||||||
pod func() *api.Pod
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
description: "has PodOS field",
|
|
||||||
hasPodOSField: true,
|
|
||||||
pod: podWithOSField,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "does not have PodOS field",
|
|
||||||
hasPodOSField: false,
|
|
||||||
pod: podWithoutOSField,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "is nil",
|
|
||||||
hasPodOSField: false,
|
|
||||||
pod: func() *api.Pod { return nil },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, enabled := range []bool{true, false} {
|
|
||||||
for _, oldPodInfo := range podInfo {
|
|
||||||
for _, newPodInfo := range podInfo {
|
|
||||||
oldPodHasOsField, oldPod := oldPodInfo.hasPodOSField, oldPodInfo.pod()
|
|
||||||
newPodHasOSField, newPod := newPodInfo.hasPodOSField, newPodInfo.pod()
|
|
||||||
if newPod == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IdentifyPodOS, enabled)()
|
|
||||||
|
|
||||||
var oldPodSpec *api.PodSpec
|
|
||||||
if oldPod != nil {
|
|
||||||
oldPodSpec = &oldPod.Spec
|
|
||||||
}
|
|
||||||
dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
|
|
||||||
|
|
||||||
// old pod should never be changed
|
|
||||||
if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
|
|
||||||
t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod()))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case enabled || oldPodHasOsField:
|
|
||||||
// new pod should not be changed if the feature is enabled, or if the old pod had subpaths
|
|
||||||
if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
|
|
||||||
t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
|
|
||||||
}
|
|
||||||
case newPodHasOSField:
|
|
||||||
// new pod should be changed
|
|
||||||
if reflect.DeepEqual(newPod, newPodInfo.pod()) {
|
|
||||||
t.Errorf("new pod was not changed")
|
|
||||||
}
|
|
||||||
// new pod should not have OSfield
|
|
||||||
if !reflect.DeepEqual(newPod, podWithoutOSField()) {
|
|
||||||
t.Errorf("new pod has OS field: %v", cmp.Diff(newPod, podWithoutOSField()))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// new pod should not need to be changed
|
|
||||||
if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
|
|
||||||
t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDropNodeInclusionPolicyFields(t *testing.T) {
|
func TestDropNodeInclusionPolicyFields(t *testing.T) {
|
||||||
ignore := api.NodeInclusionPolicyIgnore
|
ignore := api.NodeInclusionPolicyIgnore
|
||||||
honor := api.NodeInclusionPolicyHonor
|
honor := api.NodeInclusionPolicyHonor
|
||||||
|
@ -3396,8 +3396,6 @@ type PodValidationOptions struct {
|
|||||||
AllowWindowsHostProcessField bool
|
AllowWindowsHostProcessField bool
|
||||||
// Allow more DNSSearchPaths and longer DNSSearchListChars
|
// Allow more DNSSearchPaths and longer DNSSearchListChars
|
||||||
AllowExpandedDNSConfig bool
|
AllowExpandedDNSConfig bool
|
||||||
// Allow OSField to be set in the pod spec
|
|
||||||
AllowOSField bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set,
|
// validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set,
|
||||||
@ -6345,9 +6343,6 @@ func validateOS(podSpec *core.PodSpec, fldPath *field.Path, opts PodValidationOp
|
|||||||
if os == nil {
|
if os == nil {
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
if !opts.AllowOSField {
|
|
||||||
return append(allErrs, field.Forbidden(fldPath, "cannot be set when IdentifyPodOS feature is not enabled"))
|
|
||||||
}
|
|
||||||
if len(os.Name) == 0 {
|
if len(os.Name) == 0 {
|
||||||
return append(allErrs, field.Required(fldPath.Child("name"), "cannot be empty"))
|
return append(allErrs, field.Required(fldPath.Child("name"), "cannot be empty"))
|
||||||
}
|
}
|
||||||
|
@ -6851,40 +6851,24 @@ func TestValidateWindowsPodSecurityContext(t *testing.T) {
|
|||||||
validWindowsSC := &core.PodSecurityContext{WindowsOptions: &core.WindowsSecurityContextOptions{RunAsUserName: utilpointer.String("dummy")}}
|
validWindowsSC := &core.PodSecurityContext{WindowsOptions: &core.WindowsSecurityContextOptions{RunAsUserName: utilpointer.String("dummy")}}
|
||||||
invalidWindowsSC := &core.PodSecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummyRole"}}
|
invalidWindowsSC := &core.PodSecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummyRole"}}
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
podSec *core.PodSpec
|
podSec *core.PodSpec
|
||||||
expectErr bool
|
expectErr bool
|
||||||
errorType field.ErrorType
|
errorType field.ErrorType
|
||||||
errorDetail string
|
errorDetail string
|
||||||
featureEnabled bool
|
|
||||||
}{
|
}{
|
||||||
"valid SC, windows, no error": {
|
"valid SC, windows, no error": {
|
||||||
podSec: &core.PodSpec{SecurityContext: validWindowsSC},
|
podSec: &core.PodSpec{SecurityContext: validWindowsSC},
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
featureEnabled: true,
|
|
||||||
},
|
},
|
||||||
"invalid SC, windows, error": {
|
"invalid SC, windows, error": {
|
||||||
podSec: &core.PodSpec{SecurityContext: invalidWindowsSC},
|
podSec: &core.PodSpec{SecurityContext: invalidWindowsSC},
|
||||||
errorType: "FieldValueForbidden",
|
errorType: "FieldValueForbidden",
|
||||||
errorDetail: "cannot be set for a windows pod",
|
errorDetail: "cannot be set for a windows pod",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
featureEnabled: true,
|
|
||||||
},
|
|
||||||
"valid SC, windows, no error, no IdentifyPodOS featuregate": {
|
|
||||||
podSec: &core.PodSpec{SecurityContext: validWindowsSC},
|
|
||||||
expectErr: false,
|
|
||||||
featureEnabled: false,
|
|
||||||
},
|
|
||||||
"invalid SC, windows, error, no IdentifyPodOS featuregate": {
|
|
||||||
podSec: &core.PodSpec{SecurityContext: invalidWindowsSC},
|
|
||||||
errorType: "FieldValueForbidden",
|
|
||||||
errorDetail: "cannot be set for a windows pod",
|
|
||||||
expectErr: true,
|
|
||||||
featureEnabled: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range cases {
|
for k, v := range cases {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IdentifyPodOS, v.featureEnabled)()
|
|
||||||
errs := validateWindows(v.podSec, field.NewPath("field"))
|
errs := validateWindows(v.podSec, field.NewPath("field"))
|
||||||
if v.expectErr && len(errs) > 0 {
|
if v.expectErr && len(errs) > 0 {
|
||||||
if errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
|
if errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
|
||||||
@ -6916,40 +6900,24 @@ func TestValidateLinuxPodSecurityContext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
podSpec *core.PodSpec
|
podSpec *core.PodSpec
|
||||||
expectErr bool
|
expectErr bool
|
||||||
errorType field.ErrorType
|
errorType field.ErrorType
|
||||||
errorDetail string
|
errorDetail string
|
||||||
featureEnabled bool
|
|
||||||
}{
|
}{
|
||||||
"valid SC, linux, no error": {
|
"valid SC, linux, no error": {
|
||||||
podSpec: &core.PodSpec{SecurityContext: validLinuxSC},
|
podSpec: &core.PodSpec{SecurityContext: validLinuxSC},
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
featureEnabled: true,
|
|
||||||
},
|
},
|
||||||
"invalid SC, linux, error": {
|
"invalid SC, linux, error": {
|
||||||
podSpec: &core.PodSpec{SecurityContext: invalidLinuxSC},
|
podSpec: &core.PodSpec{SecurityContext: invalidLinuxSC},
|
||||||
errorType: "FieldValueForbidden",
|
errorType: "FieldValueForbidden",
|
||||||
errorDetail: "windows options cannot be set for a linux pod",
|
errorDetail: "windows options cannot be set for a linux pod",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
featureEnabled: true,
|
|
||||||
},
|
|
||||||
"valid SC, linux, no error, no IdentifyPodOS featuregate": {
|
|
||||||
podSpec: &core.PodSpec{SecurityContext: validLinuxSC},
|
|
||||||
expectErr: false,
|
|
||||||
featureEnabled: false,
|
|
||||||
},
|
|
||||||
"invalid SC, linux, error, no IdentifyPodOS featuregate": {
|
|
||||||
podSpec: &core.PodSpec{SecurityContext: invalidLinuxSC},
|
|
||||||
errorType: "FieldValueForbidden",
|
|
||||||
errorDetail: "windows options cannot be set for a linux pod",
|
|
||||||
expectErr: true,
|
|
||||||
featureEnabled: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range cases {
|
for k, v := range cases {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IdentifyPodOS, v.featureEnabled)()
|
|
||||||
errs := validateLinux(v.podSpec, field.NewPath("field"))
|
errs := validateLinux(v.podSpec, field.NewPath("field"))
|
||||||
if v.expectErr && len(errs) > 0 {
|
if v.expectErr && len(errs) > 0 {
|
||||||
if errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
|
if errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
|
||||||
@ -9964,11 +9932,10 @@ func TestValidatePodUpdate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
new core.Pod
|
new core.Pod
|
||||||
old core.Pod
|
old core.Pod
|
||||||
err string
|
err string
|
||||||
test string
|
test string
|
||||||
enablePodOS bool
|
|
||||||
}{
|
}{
|
||||||
{new: core.Pod{}, old: core.Pod{}, err: "", test: "nothing"},
|
{new: core.Pod{}, old: core.Pod{}, err: "", test: "nothing"},
|
||||||
{
|
{
|
||||||
@ -10792,9 +10759,8 @@ func TestValidatePodUpdate(t *testing.T) {
|
|||||||
SecurityContext: &core.PodSecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}},
|
SecurityContext: &core.PodSecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image`,",
|
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
|
||||||
test: "pod OS changing from Linux to Windows, no IdentifyPodOS featuregate set, no validation done",
|
test: "pod OS changing from Linux to Windows, IdentifyPodOS featuregate set",
|
||||||
enablePodOS: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: core.Pod{
|
new: core.Pod{
|
||||||
@ -10815,32 +10781,8 @@ func TestValidatePodUpdate(t *testing.T) {
|
|||||||
SecurityContext: &core.PodSecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}},
|
SecurityContext: &core.PodSecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
|
err: "spec.securityContext.seLinuxOptions: Forbidden",
|
||||||
test: "pod OS changing from Linux to Windows, IdentifyPodOS featuregate set",
|
test: "pod OS changing from Linux to Windows, IdentifyPodOS featuregate set, we'd get SELinux errors as well",
|
||||||
enablePodOS: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
new: core.Pod{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
Spec: core.PodSpec{
|
|
||||||
OS: &core.PodOS{Name: core.Windows},
|
|
||||||
SecurityContext: &core.PodSecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
old: core.Pod{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
Spec: core.PodSpec{
|
|
||||||
OS: &core.PodOS{Name: core.Linux},
|
|
||||||
SecurityContext: &core.PodSecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: "spec.securityContext.seLinuxOptions: Forbidden",
|
|
||||||
test: "pod OS changing from Linux to Windows, IdentifyPodOS featuregate set, we'd get SELinux errors as well",
|
|
||||||
enablePodOS: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: core.Pod{
|
new: core.Pod{
|
||||||
@ -10857,28 +10799,8 @@ func TestValidatePodUpdate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Spec: core.PodSpec{},
|
Spec: core.PodSpec{},
|
||||||
},
|
},
|
||||||
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
|
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
|
||||||
test: "invalid PodOS update, IdentifyPodOS featuregate set",
|
test: "invalid PodOS update, IdentifyPodOS featuregate set",
|
||||||
enablePodOS: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
new: core.Pod{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
Spec: core.PodSpec{
|
|
||||||
OS: &core.PodOS{Name: core.Windows},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
old: core.Pod{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
Spec: core.PodSpec{},
|
|
||||||
},
|
|
||||||
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
|
|
||||||
test: "no pod spec OS to a valid value, no featuregate",
|
|
||||||
enablePodOS: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: core.Pod{
|
new: core.Pod{
|
||||||
@ -10927,7 +10849,7 @@ func TestValidatePodUpdate(t *testing.T) {
|
|||||||
test.old.Spec.RestartPolicy = "Always"
|
test.old.Spec.RestartPolicy = "Always"
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := ValidatePodUpdate(&test.new, &test.old, PodValidationOptions{AllowOSField: test.enablePodOS})
|
errs := ValidatePodUpdate(&test.new, &test.old, PodValidationOptions{})
|
||||||
if test.err == "" {
|
if test.err == "" {
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
|
t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
|
||||||
@ -17504,61 +17426,34 @@ func TestValidateEndpointsUpdate(t *testing.T) {
|
|||||||
|
|
||||||
func TestValidateWindowsSecurityContext(t *testing.T) {
|
func TestValidateWindowsSecurityContext(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
sc *core.PodSpec
|
sc *core.PodSpec
|
||||||
expectError bool
|
expectError bool
|
||||||
errorMsg string
|
errorMsg string
|
||||||
errorType field.ErrorType
|
errorType field.ErrorType
|
||||||
featureEnabled bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "pod with SELinux Options",
|
name: "pod with SELinux Options",
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}}}}},
|
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}}}}},
|
||||||
expectError: true,
|
expectError: true,
|
||||||
errorMsg: "cannot be set for a windows pod",
|
errorMsg: "cannot be set for a windows pod",
|
||||||
errorType: "FieldValueForbidden",
|
errorType: "FieldValueForbidden",
|
||||||
featureEnabled: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pod with SeccompProfile",
|
name: "pod with SeccompProfile",
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SeccompProfile: &core.SeccompProfile{LocalhostProfile: utilpointer.String("dummy")}}}}},
|
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SeccompProfile: &core.SeccompProfile{LocalhostProfile: utilpointer.String("dummy")}}}}},
|
||||||
expectError: true,
|
expectError: true,
|
||||||
errorMsg: "cannot be set for a windows pod",
|
errorMsg: "cannot be set for a windows pod",
|
||||||
errorType: "FieldValueForbidden",
|
errorType: "FieldValueForbidden",
|
||||||
featureEnabled: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pod with WindowsOptions, no error",
|
name: "pod with WindowsOptions, no error",
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{WindowsOptions: &core.WindowsSecurityContextOptions{RunAsUserName: utilpointer.String("dummy")}}}}},
|
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{WindowsOptions: &core.WindowsSecurityContextOptions{RunAsUserName: utilpointer.String("dummy")}}}}},
|
||||||
expectError: false,
|
expectError: false,
|
||||||
featureEnabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "pod with SELinux Options, no IdentifyPodOS enabled",
|
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}}}}},
|
|
||||||
expectError: true,
|
|
||||||
errorMsg: "cannot be set for a windows pod",
|
|
||||||
errorType: "FieldValueForbidden",
|
|
||||||
featureEnabled: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "pod with SeccompProfile, no IdentifyPodOS enabled",
|
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SeccompProfile: &core.SeccompProfile{LocalhostProfile: utilpointer.String("dummy")}}}}},
|
|
||||||
expectError: true,
|
|
||||||
errorMsg: "cannot be set for a windows pod",
|
|
||||||
errorType: "FieldValueForbidden",
|
|
||||||
featureEnabled: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "pod with WindowsOptions, no error, no IdentifyPodOS enabled",
|
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{WindowsOptions: &core.WindowsSecurityContextOptions{RunAsUserName: utilpointer.String("dummy")}}}}},
|
|
||||||
expectError: false,
|
|
||||||
featureEnabled: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IdentifyPodOS, test.featureEnabled)()
|
|
||||||
errs := validateWindows(test.sc, field.NewPath("field"))
|
errs := validateWindows(test.sc, field.NewPath("field"))
|
||||||
if test.expectError && len(errs) > 0 {
|
if test.expectError && len(errs) > 0 {
|
||||||
if errs[0].Type != test.errorType {
|
if errs[0].Type != test.errorType {
|
||||||
@ -17843,40 +17738,24 @@ func TestValidateLinuxSecurityContext(t *testing.T) {
|
|||||||
WindowsOptions: &core.WindowsSecurityContextOptions{RunAsUserName: utilpointer.String("myUser")},
|
WindowsOptions: &core.WindowsSecurityContextOptions{RunAsUserName: utilpointer.String("myUser")},
|
||||||
}
|
}
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
sc *core.PodSpec
|
sc *core.PodSpec
|
||||||
expectErr bool
|
expectErr bool
|
||||||
errorType field.ErrorType
|
errorType field.ErrorType
|
||||||
errorDetail string
|
errorDetail string
|
||||||
featureEnabled bool
|
|
||||||
}{
|
}{
|
||||||
"valid SC, linux, no error": {
|
"valid SC, linux, no error": {
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: validLinuxSC}}},
|
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: validLinuxSC}}},
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
featureEnabled: true,
|
|
||||||
},
|
},
|
||||||
"invalid SC, linux, error": {
|
"invalid SC, linux, error": {
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: invalidLinuxSC}}},
|
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: invalidLinuxSC}}},
|
||||||
errorType: "FieldValueForbidden",
|
errorType: "FieldValueForbidden",
|
||||||
errorDetail: "windows options cannot be set for a linux pod",
|
errorDetail: "windows options cannot be set for a linux pod",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
featureEnabled: true,
|
|
||||||
},
|
|
||||||
"valid SC, linux, no error, no IdentifyPodOS featuregate": {
|
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: validLinuxSC}}},
|
|
||||||
expectErr: false,
|
|
||||||
featureEnabled: false,
|
|
||||||
},
|
|
||||||
"invalid SC, linux, error, no IdentifyPodOS featuregate": {
|
|
||||||
sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: invalidLinuxSC}}},
|
|
||||||
errorType: "FieldValueForbidden",
|
|
||||||
errorDetail: "windows options cannot be set for a linux pod",
|
|
||||||
expectErr: true,
|
|
||||||
featureEnabled: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range cases {
|
for k, v := range cases {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IdentifyPodOS, v.featureEnabled)()
|
|
||||||
errs := validateLinux(v.sc, field.NewPath("field"))
|
errs := validateLinux(v.sc, field.NewPath("field"))
|
||||||
if v.expectErr && len(errs) > 0 {
|
if v.expectErr && len(errs) > 0 {
|
||||||
if errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
|
if errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
|
||||||
@ -20377,83 +20256,44 @@ func TestValidateWindowsHostProcessPod(t *testing.T) {
|
|||||||
|
|
||||||
func TestValidateOS(t *testing.T) {
|
func TestValidateOS(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
expectError bool
|
expectError bool
|
||||||
featureEnabled bool
|
podSpec *core.PodSpec
|
||||||
podSpec *core.PodSpec
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no OS field, featuregate",
|
name: "no OS field, featuregate",
|
||||||
expectError: false,
|
expectError: false,
|
||||||
featureEnabled: true,
|
podSpec: &core.PodSpec{OS: nil},
|
||||||
podSpec: &core.PodSpec{OS: nil},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty OS field, featuregate",
|
name: "empty OS field, featuregate",
|
||||||
expectError: true,
|
expectError: true,
|
||||||
featureEnabled: true,
|
podSpec: &core.PodSpec{OS: &core.PodOS{}},
|
||||||
podSpec: &core.PodSpec{OS: &core.PodOS{}},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no OS field, no featuregate",
|
name: "OS field, featuregate, valid OS",
|
||||||
expectError: false,
|
expectError: false,
|
||||||
featureEnabled: false,
|
podSpec: &core.PodSpec{OS: &core.PodOS{Name: core.Linux}},
|
||||||
podSpec: &core.PodSpec{OS: nil},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty OS field, no featuregate",
|
name: "OS field, featuregate, valid OS",
|
||||||
expectError: true,
|
expectError: false,
|
||||||
featureEnabled: false,
|
podSpec: &core.PodSpec{OS: &core.PodOS{Name: core.Windows}},
|
||||||
podSpec: &core.PodSpec{OS: &core.PodOS{}},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OS field, featuregate, valid OS",
|
name: "OS field, featuregate, empty OS",
|
||||||
expectError: false,
|
expectError: true,
|
||||||
featureEnabled: true,
|
podSpec: &core.PodSpec{OS: &core.PodOS{Name: ""}},
|
||||||
podSpec: &core.PodSpec{OS: &core.PodOS{Name: core.Linux}},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OS field, featuregate, valid OS",
|
name: "OS field, featuregate, invalid OS",
|
||||||
expectError: false,
|
expectError: true,
|
||||||
featureEnabled: true,
|
podSpec: &core.PodSpec{OS: &core.PodOS{Name: "dummyOS"}},
|
||||||
podSpec: &core.PodSpec{OS: &core.PodOS{Name: core.Windows}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "OS field, featuregate, empty OS",
|
|
||||||
expectError: true,
|
|
||||||
featureEnabled: true,
|
|
||||||
podSpec: &core.PodSpec{OS: &core.PodOS{Name: ""}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "OS field, no featuregate, empty OS",
|
|
||||||
expectError: true,
|
|
||||||
featureEnabled: false,
|
|
||||||
podSpec: &core.PodSpec{OS: &core.PodOS{Name: ""}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "OS field, featuregate, invalid OS",
|
|
||||||
expectError: true,
|
|
||||||
featureEnabled: true,
|
|
||||||
podSpec: &core.PodSpec{OS: &core.PodOS{Name: "dummyOS"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "OS field, no featuregate, valid OS",
|
|
||||||
expectError: true,
|
|
||||||
featureEnabled: false,
|
|
||||||
podSpec: &core.PodSpec{OS: &core.PodOS{Name: core.Linux}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "OS field, no featuregate, invalid OS",
|
|
||||||
expectError: true,
|
|
||||||
featureEnabled: false,
|
|
||||||
podSpec: &core.PodSpec{OS: &core.PodOS{Name: "dummyOS"}},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IdentifyPodOS, testCase.featureEnabled)()
|
errs := validateOS(testCase.podSpec, field.NewPath("spec"), PodValidationOptions{})
|
||||||
|
|
||||||
errs := validateOS(testCase.podSpec, field.NewPath("spec"), PodValidationOptions{AllowOSField: testCase.featureEnabled})
|
|
||||||
if testCase.expectError && len(errs) == 0 {
|
if testCase.expectError && len(errs) == 0 {
|
||||||
t.Errorf("Unexpected success")
|
t.Errorf("Unexpected success")
|
||||||
}
|
}
|
||||||
|
@ -374,6 +374,7 @@ const (
|
|||||||
// owner: @ravig
|
// owner: @ravig
|
||||||
// alpha: v1.23
|
// alpha: v1.23
|
||||||
// beta: v1.24
|
// beta: v1.24
|
||||||
|
// GA: v1.25
|
||||||
// IdentifyPodOS allows user to specify OS on which they'd like the Pod run. The user should still set the nodeSelector
|
// IdentifyPodOS allows user to specify OS on which they'd like the Pod run. The user should still set the nodeSelector
|
||||||
// with appropriate `kubernetes.io/os` label for scheduler to identify appropriate node for the pod to run.
|
// with appropriate `kubernetes.io/os` label for scheduler to identify appropriate node for the pod to run.
|
||||||
IdentifyPodOS featuregate.Feature = "IdentifyPodOS"
|
IdentifyPodOS featuregate.Feature = "IdentifyPodOS"
|
||||||
@ -904,7 +905,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
HonorPVReclaimPolicy: {Default: false, PreRelease: featuregate.Alpha},
|
HonorPVReclaimPolicy: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
IdentifyPodOS: {Default: true, PreRelease: featuregate.Beta},
|
IdentifyPodOS: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.27
|
||||||
|
|
||||||
InTreePluginAWSUnregister: {Default: false, PreRelease: featuregate.Alpha},
|
InTreePluginAWSUnregister: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user