diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json index 32de6904ade..53e6f73dea5 100644 --- a/api/swagger-spec/v1.json +++ b/api/swagger-spec/v1.json @@ -12646,6 +12646,10 @@ "type": "boolean", "description": "Use the host's ipc namespace. Optional: Default to false." }, + "securityContext": { + "$ref": "v1.PodSecurityContext", + "description": "SecurityContext holds pod-level security attributes and common container settings" + }, "imagePullSecrets": { "type": "array", "items": { @@ -13201,6 +13205,11 @@ } } }, + "v1.PodSecurityContext": { + "id": "v1.PodSecurityContext", + "description": "PodSecurityContext holds pod-level security attributes and common container settings.", + "properties": {} + }, "v1.PodStatus": { "id": "v1.PodStatus", "description": "PodStatus represents information about the status of a pod. Status may trail the actual state of a system.", diff --git a/pkg/api/copy_test.go b/pkg/api/copy_test.go index c3c55283a7f..0744873ce3b 100644 --- a/pkg/api/copy_test.go +++ b/pkg/api/copy_test.go @@ -24,6 +24,9 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" apitesting "k8s.io/kubernetes/pkg/api/testing" + "k8s.io/kubernetes/pkg/util" + + "github.com/google/gofuzz" ) func TestDeepCopyApiObjects(t *testing.T) { @@ -31,21 +34,34 @@ func TestDeepCopyApiObjects(t *testing.T) { for _, version := range []string{"", testapi.Default.Version()} { f := apitesting.FuzzerFor(t, version, rand.NewSource(rand.Int63())) for kind := range api.Scheme.KnownTypes(version) { - item, err := api.Scheme.New(version, kind) - if err != nil { - t.Fatalf("Could not create a %s: %s", kind, err) - } - f.Fuzz(item) - itemCopy, err := api.Scheme.DeepCopy(item) - if err != nil { - t.Errorf("Could not deep copy a %s: %s", kind, err) - continue - } - - if !reflect.DeepEqual(item, itemCopy) { - t.Errorf("\nexpected %#v\ngot %#v", item, itemCopy) - } + doDeepCopyTest(t, version, kind, f) } } } } + +func doDeepCopyTest(t *testing.T, version, kind string, f *fuzz.Fuzzer) { + item, err := api.Scheme.New(version, kind) + if err != nil { + t.Fatalf("Could not create a %s: %s", kind, err) + } + f.Fuzz(item) + itemCopy, err := api.Scheme.DeepCopy(item) + if err != nil { + t.Errorf("Could not deep copy a %s: %s", kind, err) + return + } + + if !reflect.DeepEqual(item, itemCopy) { + t.Errorf("\nexpected: %#v\n\ngot: %#v\n\ndiff: %v", item, itemCopy, util.ObjectDiff(item, itemCopy)) + } +} + +func TestDeepCopySingleType(t *testing.T) { + for i := 0; i < *fuzzIters; i++ { + for _, version := range []string{"", testapi.Default.Version()} { + f := apitesting.FuzzerFor(t, version, rand.NewSource(rand.Int63())) + doDeepCopyTest(t, version, "Pod", f) + } + } +} diff --git a/pkg/api/deep_copy_generated.go b/pkg/api/deep_copy_generated.go index 64acf98541f..8b73d005346 100644 --- a/pkg/api/deep_copy_generated.go +++ b/pkg/api/deep_copy_generated.go @@ -1481,6 +1481,13 @@ func deepCopy_api_PodProxyOptions(in PodProxyOptions, out *PodProxyOptions, c *c return nil } +func deepCopy_api_PodSecurityContext(in PodSecurityContext, out *PodSecurityContext, c *conversion.Cloner) error { + out.HostNetwork = in.HostNetwork + out.HostPID = in.HostPID + out.HostIPC = in.HostIPC + return nil +} + func deepCopy_api_PodSpec(in PodSpec, out *PodSpec, c *conversion.Cloner) error { if in.Volumes != nil { out.Volumes = make([]Volume, len(in.Volumes)) @@ -1526,9 +1533,14 @@ func deepCopy_api_PodSpec(in PodSpec, out *PodSpec, c *conversion.Cloner) error } out.ServiceAccountName = in.ServiceAccountName out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + out.SecurityContext = new(PodSecurityContext) + if err := deepCopy_api_PodSecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { + return err + } + } else { + out.SecurityContext = nil + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -2380,6 +2392,7 @@ func init() { deepCopy_api_PodList, deepCopy_api_PodLogOptions, deepCopy_api_PodProxyOptions, + deepCopy_api_PodSecurityContext, deepCopy_api_PodSpec, deepCopy_api_PodStatus, deepCopy_api_PodStatusResult, diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index c53e198c0f3..509440e16b9 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -108,7 +108,7 @@ func TestSpecificKind(t *testing.T) { api.Scheme.Log(t) defer api.Scheme.Log(nil) - kind := "PodList" + kind := "Pod" doRoundTripTest(kind, t) } @@ -169,6 +169,8 @@ func TestEncode_Ptr(t *testing.T) { DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, + + SecurityContext: &api.PodSecurityContext{}, }, } obj := runtime.Object(pod) @@ -181,7 +183,8 @@ func TestEncode_Ptr(t *testing.T) { t.Fatalf("Got wrong type") } if !api.Semantic.DeepEqual(obj2, pod) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", pod, obj2) + t.Errorf("\nExpected:\n\n %#v,\n\nGot:\n\n %#vDiff: %v\n\n", pod, obj2, util.ObjectDiff(obj2, pod)) + } } diff --git a/pkg/api/testing/compat/compatibility_tester.go b/pkg/api/testing/compat/compatibility_tester.go new file mode 100644 index 00000000000..42314d18621 --- /dev/null +++ b/pkg/api/testing/compat/compatibility_tester.go @@ -0,0 +1,144 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package compat + +import ( + "encoding/json" + "fmt" + "os" + "reflect" + "regexp" + "strconv" + "strings" + "testing" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/fielderrors" + + "k8s.io/kubernetes/pkg/kubectl" +) + +// Based on: https://github.com/openshift/origin/blob/master/pkg/api/compatibility_test.go +// +// TestCompatibility reencodes the input using the codec for the given +// version and checks for the presence of the expected keys and absent +// keys in the resulting JSON. +func TestCompatibility( + t *testing.T, + version string, + input []byte, + validator func(obj runtime.Object) fielderrors.ValidationErrorList, + expectedKeys map[string]string, + absentKeys []string, +) { + + // Decode + codec := runtime.CodecFor(api.Scheme, version) + obj, err := codec.Decode(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Validate + errs := validator(obj) + if len(errs) != 0 { + t.Fatalf("Unexpected validation errors: %v", errs) + } + + // Encode + output, err := codec.Encode(obj) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Validate old and new fields are encoded + generic := map[string]interface{}{} + if err := json.Unmarshal(output, &generic); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + hasError := false + for k, expectedValue := range expectedKeys { + keys := strings.Split(k, ".") + if actualValue, ok, err := getJSONValue(generic, keys...); err != nil || !ok { + t.Errorf("Unexpected error for %s: %v", k, err) + hasError = true + } else if !reflect.DeepEqual(expectedValue, fmt.Sprintf("%v", actualValue)) { + hasError = true + t.Errorf("Unexpected value for %v: expected %v, got %v", k, expectedValue, actualValue) + } + } + + for _, absentKey := range absentKeys { + keys := strings.Split(absentKey, ".") + actualValue, ok, err := getJSONValue(generic, keys...) + if err == nil || ok { + t.Errorf("Unexpected value found for for key %s: %v", absentKey, actualValue) + hasError = true + } + } + + if hasError { + printer := new(kubectl.JSONPrinter) + printer.PrintObj(obj, os.Stdout) + t.Logf("2: Encoded value: %#v", string(output)) + } +} + +func getJSONValue(data map[string]interface{}, keys ...string) (interface{}, bool, error) { + // No keys, current value is it + if len(keys) == 0 { + return data, true, nil + } + + // Get the key (and optional index) + key := keys[0] + index := -1 + if matches := regexp.MustCompile(`^(.*)\[(\d+)\]$`).FindStringSubmatch(key); len(matches) > 0 { + key = matches[1] + index, _ = strconv.Atoi(matches[2]) + } + + // Look up the value + value, ok := data[key] + if !ok { + return nil, false, fmt.Errorf("No key %s found", key) + } + + // Get the indexed value if an index is specified + if index >= 0 { + valueSlice, ok := value.([]interface{}) + if !ok { + return nil, false, fmt.Errorf("Key %s did not hold a slice", key) + } + if index >= len(valueSlice) { + return nil, false, fmt.Errorf("Index %d out of bounds for slice at key: %v", index, key) + } + value = valueSlice[index] + } + + if len(keys) == 1 { + return value, true, nil + } + + childData, ok := value.(map[string]interface{}) + if !ok { + return nil, false, fmt.Errorf("Key %s did not hold a map", keys[0]) + } + return getJSONValue(childData, keys[1:]...) +} diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index d22593c6b19..d0429467f4f 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -92,14 +92,18 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { j.LabelSelector, _ = labels.Parse("a=b") j.FieldSelector, _ = fields.ParseSelector("a=b") }, - func(j *api.PodSpec, c fuzz.Continue) { - c.FuzzNoCustom(j) + func(s *api.PodSpec, c fuzz.Continue) { + c.FuzzNoCustom(s) // has a default value ttl := int64(30) if c.RandBool() { ttl = int64(c.Uint32()) } - j.TerminationGracePeriodSeconds = &ttl + s.TerminationGracePeriodSeconds = &ttl + + if s.SecurityContext == nil { + s.SecurityContext = &api.PodSecurityContext{} + } }, func(j *api.PodPhase, c fuzz.Continue) { statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} diff --git a/pkg/api/testing/pod_specs.go b/pkg/api/testing/pod_specs.go new file mode 100644 index 00000000000..2020b3f7fb0 --- /dev/null +++ b/pkg/api/testing/pod_specs.go @@ -0,0 +1,32 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testing + +import ( + "k8s.io/kubernetes/pkg/api" +) + +// DeepEqualSafePodSpec returns a PodSpec which is ready to be used with api.Semantic.DeepEqual +func DeepEqualSafePodSpec() api.PodSpec { + grace := int64(30) + return api.PodSpec{ + RestartPolicy: api.RestartPolicyAlways, + DNSPolicy: api.DNSClusterFirst, + TerminationGracePeriodSeconds: &grace, + SecurityContext: &api.PodSecurityContext{}, + } +} diff --git a/pkg/api/types.go b/pkg/api/types.go index 274ac5614b0..35aadf2f5a4 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -986,20 +986,27 @@ type PodSpec struct { // the scheduler simply schedules this pod onto that node, assuming that it fits resource // requirements. NodeName string `json:"nodeName,omitempty"` - // Use the host's network namespace. If this option is set, the ports that will be + // SecurityContext holds pod-level security attributes and common container settings + SecurityContext *PodSecurityContext `json:"securityContext,omitempty"` + // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + // If specified, these secrets will be passed to individual puller implementations for them to use. For example, + // in the case of docker, only DockerConfig type secrets are honored. + ImagePullSecrets []LocalObjectReference `json:"imagePullSecrets,omitempty"` +} + +// PodSecurityContext holds pod-level security attributes and common container settings. +type PodSecurityContext struct { + // Use the host's network namespace. If this option is set, the ports that will be // used must be specified. - // Optional: Default to false. + // Optional: Default to false HostNetwork bool `json:"hostNetwork,omitempty"` + // Use the host's pid namespace. // Optional: Default to false. HostPID bool `json:"hostPID,omitempty"` // Use the host's ipc namespace. // Optional: Default to false. HostIPC bool `json:"hostIPC,omitempty"` - // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. - // If specified, these secrets will be passed to individual puller implementations for them to use. For example, - // in the case of docker, only DockerConfig type secrets are honored. - ImagePullSecrets []LocalObjectReference `json:"imagePullSecrets,omitempty"` } // PodStatus represents information about the status of a pod. Status may trail the actual diff --git a/pkg/api/v1/backward_compatibility_test.go b/pkg/api/v1/backward_compatibility_test.go new file mode 100644 index 00000000000..0bcfa3e31b6 --- /dev/null +++ b/pkg/api/v1/backward_compatibility_test.go @@ -0,0 +1,166 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1_test + +import ( + "testing" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/testing/compat" + "k8s.io/kubernetes/pkg/api/validation" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/fielderrors" +) + +func TestCompatibility_v1_PodSecurityContext(t *testing.T) { + cases := []struct { + name string + input string + expectedKeys map[string]string + absentKeys []string + }{ + { + name: "hostNetwork = true", + input: ` +{ + "kind":"Pod", + "apiVersion":"v1", + "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, + "spec": { + "hostNetwork": true, + "containers":[{ + "name":"a", + "image":"my-container-image" + }] + } +} +`, + expectedKeys: map[string]string{ + "spec.hostNetwork": "true", + }, + }, + { + name: "hostNetwork = false", + input: ` +{ + "kind":"Pod", + "apiVersion":"v1", + "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, + "spec": { + "hostNetwork": false, + "containers":[{ + "name":"a", + "image":"my-container-image" + }] + } +} +`, + absentKeys: []string{ + "spec.hostNetwork", + }, + }, + { + name: "hostIPC = true", + input: ` +{ + "kind":"Pod", + "apiVersion":"v1", + "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, + "spec": { + "hostIPC": true, + "containers":[{ + "name":"a", + "image":"my-container-image" + }] + } +} +`, + expectedKeys: map[string]string{ + "spec.hostIPC": "true", + }, + }, + { + name: "hostIPC = false", + input: ` +{ + "kind":"Pod", + "apiVersion":"v1", + "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, + "spec": { + "hostIPC": false, + "containers":[{ + "name":"a", + "image":"my-container-image" + }] + } +} +`, + absentKeys: []string{ + "spec.hostIPC", + }, + }, + { + name: "hostPID = true", + input: ` +{ + "kind":"Pod", + "apiVersion":"v1", + "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, + "spec": { + "hostPID": true, + "containers":[{ + "name":"a", + "image":"my-container-image" + }] + } +} +`, + expectedKeys: map[string]string{ + "spec.hostPID": "true", + }, + }, + { + name: "hostPID = false", + input: ` +{ + "kind":"Pod", + "apiVersion":"v1", + "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, + "spec": { + "hostPID": false, + "containers":[{ + "name":"a", + "image":"my-container-image" + }] + } +} +`, + absentKeys: []string{ + "spec.hostPID", + }, + }, + } + + validator := func(obj runtime.Object) fielderrors.ValidationErrorList { + return validation.ValidatePodSpec(&(obj.(*api.Pod).Spec)) + } + + for _, tc := range cases { + t.Logf("Testing 1.0.0 backward compatibility for %v", tc.name) + compat.TestCompatibility(t, "v1", []byte(tc.input), validator, tc.expectedKeys, tc.absentKeys) + } +} diff --git a/pkg/api/v1/conversion.go b/pkg/api/v1/conversion.go index 1e4bb1959f2..ba5f619d175 100644 --- a/pkg/api/v1/conversion.go +++ b/pkg/api/v1/conversion.go @@ -283,9 +283,16 @@ func convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *PodSpec, s conversi // DeprecatedServiceAccount is an alias for ServiceAccountName. out.DeprecatedServiceAccount = in.ServiceAccountName out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + out.SecurityContext = new(PodSecurityContext) + if err := convert_api_PodSecurityContext_To_v1_PodSecurityContext(in.SecurityContext, out.SecurityContext, s); err != nil { + return err + } + + out.HostPID = in.SecurityContext.HostPID + out.HostNetwork = in.SecurityContext.HostNetwork + out.HostIPC = in.SecurityContext.HostIPC + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -352,9 +359,18 @@ func convert_v1_PodSpec_To_api_PodSpec(in *PodSpec, out *api.PodSpec, s conversi out.ServiceAccountName = in.DeprecatedServiceAccount } out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + out.SecurityContext = new(api.PodSecurityContext) + if err := convert_v1_PodSecurityContext_To_api_PodSecurityContext(in.SecurityContext, out.SecurityContext, s); err != nil { + return err + } + } + if out.SecurityContext == nil { + out.SecurityContext = new(api.PodSecurityContext) + } + out.SecurityContext.HostNetwork = in.HostNetwork + out.SecurityContext.HostPID = in.HostPID + out.SecurityContext.HostIPC = in.HostIPC if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -392,3 +408,17 @@ func convert_v1_ServiceSpec_To_api_ServiceSpec(in *ServiceSpec, out *api.Service } return nil } + +func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurityContext, out *PodSecurityContext, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.PodSecurityContext))(in) + } + return nil +} + +func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *PodSecurityContext, out *api.PodSecurityContext, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*PodSecurityContext))(in) + } + return nil +} diff --git a/pkg/api/v1/conversion_generated.go b/pkg/api/v1/conversion_generated.go index 2969a4cb639..0e9e777d8d2 100644 --- a/pkg/api/v1/conversion_generated.go +++ b/pkg/api/v1/conversion_generated.go @@ -2084,9 +2084,13 @@ func autoconvert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *PodSpec, s conv } out.ServiceAccountName = in.ServiceAccountName out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil { + return err + } + } else { + out.SecurityContext = nil + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -5094,9 +5098,16 @@ func autoconvert_v1_PodSpec_To_api_PodSpec(in *PodSpec, out *api.PodSpec, s conv out.ServiceAccountName = in.ServiceAccountName // in.DeprecatedServiceAccount has no peer in out out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + // in.HostNetwork has no peer in out + // in.HostPID has no peer in out + // in.HostIPC has no peer in out + if in.SecurityContext != nil { + if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil { + return err + } + } else { + out.SecurityContext = nil + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { diff --git a/pkg/api/v1/deep_copy_generated.go b/pkg/api/v1/deep_copy_generated.go index b49972d845b..87cbcf4b71c 100644 --- a/pkg/api/v1/deep_copy_generated.go +++ b/pkg/api/v1/deep_copy_generated.go @@ -1501,6 +1501,10 @@ func deepCopy_v1_PodProxyOptions(in PodProxyOptions, out *PodProxyOptions, c *co return nil } +func deepCopy_v1_PodSecurityContext(in PodSecurityContext, out *PodSecurityContext, c *conversion.Cloner) error { + return nil +} + func deepCopy_v1_PodSpec(in PodSpec, out *PodSpec, c *conversion.Cloner) error { if in.Volumes != nil { out.Volumes = make([]Volume, len(in.Volumes)) @@ -1550,6 +1554,14 @@ func deepCopy_v1_PodSpec(in PodSpec, out *PodSpec, c *conversion.Cloner) error { out.HostNetwork = in.HostNetwork out.HostPID = in.HostPID out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + out.SecurityContext = new(PodSecurityContext) + if err := deepCopy_v1_PodSecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { + return err + } + } else { + out.SecurityContext = nil + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -2393,6 +2405,7 @@ func init() { deepCopy_v1_PodList, deepCopy_v1_PodLogOptions, deepCopy_v1_PodProxyOptions, + deepCopy_v1_PodSecurityContext, deepCopy_v1_PodSpec, deepCopy_v1_PodStatus, deepCopy_v1_PodStatusResult, diff --git a/pkg/api/v1/defaults.go b/pkg/api/v1/defaults.go index 293a54fa558..c4a67e3295a 100644 --- a/pkg/api/v1/defaults.go +++ b/pkg/api/v1/defaults.go @@ -116,6 +116,9 @@ func addDefaultingFuncs() { if obj.HostNetwork { defaultHostNetworkPorts(&obj.Containers) } + if obj.SecurityContext == nil { + obj.SecurityContext = &PodSecurityContext{} + } if obj.TerminationGracePeriodSeconds == nil { period := int64(DefaultTerminationGracePeriodSeconds) obj.TerminationGracePeriodSeconds = &period diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 906682be3a5..13f4618b7b8 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -1238,6 +1238,8 @@ type PodSpec struct { // Use the host's ipc namespace. // Optional: Default to false. HostIPC bool `json:"hostIPC,omitempty"` + // SecurityContext holds pod-level security attributes and common container settings + SecurityContext *PodSecurityContext `json:"securityContext,omitempty"` // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. // If specified, these secrets will be passed to individual puller implementations for them to use. For example, // in the case of docker, only DockerConfig type secrets are honored. @@ -1245,6 +1247,10 @@ type PodSpec struct { ImagePullSecrets []LocalObjectReference `json:"imagePullSecrets,omitempty" patchStrategy:"merge" patchMergeKey:"name"` } +// PodSecurityContext holds pod-level security attributes and common container settings. +type PodSecurityContext struct { +} + // PodStatus represents information about the status of a pod. Status may trail the actual // state of a system. type PodStatus struct { diff --git a/pkg/api/v1/types_swagger_doc_generated.go b/pkg/api/v1/types_swagger_doc_generated.go index f694d049fe5..4f25221a79a 100644 --- a/pkg/api/v1/types_swagger_doc_generated.go +++ b/pkg/api/v1/types_swagger_doc_generated.go @@ -973,6 +973,14 @@ func (PodProxyOptions) SwaggerDoc() map[string]string { return map_PodProxyOptions } +var map_PodSecurityContext = map[string]string{ + "": "PodSecurityContext holds pod-level security attributes and common container settings.", +} + +func (PodSecurityContext) SwaggerDoc() map[string]string { + return map_PodSecurityContext +} + var map_PodSpec = map[string]string{ "": "PodSpec is a description of a pod.", "volumes": "List of volumes that can be mounted by containers belonging to the pod. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md", @@ -988,6 +996,7 @@ var map_PodSpec = map[string]string{ "hostNetwork": "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.", "hostPID": "Use the host's pid namespace. Optional: Default to false.", "hostIPC": "Use the host's ipc namespace. Optional: Default to false.", + "securityContext": "SecurityContext holds pod-level security attributes and common container settings", "imagePullSecrets": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md#specifying-imagepullsecrets-on-a-pod", } diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 8c720630db7..6bfd7881479 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -1107,7 +1107,7 @@ func ValidatePodSpec(spec *api.PodSpec) errs.ValidationErrorList { allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy).Prefix("restartPolicy")...) allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy).Prefix("dnsPolicy")...) allErrs = append(allErrs, ValidateLabels(spec.NodeSelector, "nodeSelector")...) - allErrs = append(allErrs, validateHostNetwork(spec.HostNetwork, spec.Containers).Prefix("hostNetwork")...) + allErrs = append(allErrs, ValidatePodSecurityContext(spec.SecurityContext, spec).Prefix("securityContext")...) allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets).Prefix("imagePullSecrets")...) if len(spec.ServiceAccountName) > 0 { if ok, msg := ValidateServiceAccountName(spec.ServiceAccountName, false); !ok { @@ -1123,6 +1123,17 @@ func ValidatePodSpec(spec *api.PodSpec) errs.ValidationErrorList { return allErrs } +// ValidatePodSecurityContext test that the specified PodSecurityContext has valid data. +func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *api.PodSpec) errs.ValidationErrorList { + allErrs := errs.ValidationErrorList{} + + if securityContext != nil { + allErrs = append(allErrs, validateHostNetwork(securityContext.HostNetwork, spec.Containers).Prefix("hostNetwork")...) + } + + return allErrs +} + // ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields // that cannot be changed. func ValidatePodUpdate(newPod, oldPod *api.Pod) errs.ValidationErrorList { diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index 7604cec7821..6cce29831b6 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -1259,20 +1259,25 @@ func TestValidatePodSpec(t *testing.T) { {HostPort: 8080, ContainerPort: 8080, Protocol: "TCP"}}, }, }, - HostNetwork: true, - HostIPC: true, + SecurityContext: &api.PodSecurityContext{ + HostNetwork: true, + }, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, }, { // Populate HostIPC. - HostIPC: true, + SecurityContext: &api.PodSecurityContext{ + HostIPC: true, + }, Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}}, Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, }, { // Populate HostPID. - HostPID: true, + SecurityContext: &api.PodSecurityContext{ + HostPID: true, + }, Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}}, Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, RestartPolicy: api.RestartPolicyAlways, @@ -1324,7 +1329,9 @@ func TestValidatePodSpec(t *testing.T) { {HostPort: 8080, ContainerPort: 2600, Protocol: "TCP"}}, }, }, - HostNetwork: true, + SecurityContext: &api.PodSecurityContext{ + HostNetwork: true, + }, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, }, diff --git a/pkg/apis/experimental/deep_copy_generated.go b/pkg/apis/experimental/deep_copy_generated.go index 78557cf898c..4b7d2b987fe 100644 --- a/pkg/apis/experimental/deep_copy_generated.go +++ b/pkg/apis/experimental/deep_copy_generated.go @@ -459,6 +459,13 @@ func deepCopy_api_PersistentVolumeClaimVolumeSource(in api.PersistentVolumeClaim return nil } +func deepCopy_api_PodSecurityContext(in api.PodSecurityContext, out *api.PodSecurityContext, c *conversion.Cloner) error { + out.HostNetwork = in.HostNetwork + out.HostPID = in.HostPID + out.HostIPC = in.HostIPC + return nil +} + func deepCopy_api_PodSpec(in api.PodSpec, out *api.PodSpec, c *conversion.Cloner) error { if in.Volumes != nil { out.Volumes = make([]api.Volume, len(in.Volumes)) @@ -504,9 +511,14 @@ func deepCopy_api_PodSpec(in api.PodSpec, out *api.PodSpec, c *conversion.Cloner } out.ServiceAccountName = in.ServiceAccountName out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + out.SecurityContext = new(api.PodSecurityContext) + if err := deepCopy_api_PodSecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { + return err + } + } else { + out.SecurityContext = nil + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -1525,6 +1537,7 @@ func init() { deepCopy_api_ObjectFieldSelector, deepCopy_api_ObjectMeta, deepCopy_api_PersistentVolumeClaimVolumeSource, + deepCopy_api_PodSecurityContext, deepCopy_api_PodSpec, deepCopy_api_PodTemplateSpec, deepCopy_api_Probe, diff --git a/pkg/apis/experimental/v1alpha1/conversion.go b/pkg/apis/experimental/v1alpha1/conversion.go index 2d2c6f44544..515f5f90a6b 100644 --- a/pkg/apis/experimental/v1alpha1/conversion.go +++ b/pkg/apis/experimental/v1alpha1/conversion.go @@ -97,9 +97,16 @@ func convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *v1.PodSpec, s conve // DeprecatedServiceAccount is an alias for ServiceAccountName. out.DeprecatedServiceAccount = in.ServiceAccountName out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + out.SecurityContext = new(v1.PodSecurityContext) + if err := convert_api_PodSecurityContext_To_v1_PodSecurityContext(in.SecurityContext, out.SecurityContext, s); err != nil { + return err + } + + out.HostNetwork = in.SecurityContext.HostNetwork + out.HostPID = in.SecurityContext.HostPID + out.HostIPC = in.SecurityContext.HostIPC + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]v1.LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -166,9 +173,19 @@ func convert_v1_PodSpec_To_api_PodSpec(in *v1.PodSpec, out *api.PodSpec, s conve out.ServiceAccountName = in.DeprecatedServiceAccount } out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + + if in.SecurityContext != nil { + out.SecurityContext = new(api.PodSecurityContext) + if err := convert_v1_PodSecurityContext_To_api_PodSecurityContext(in.SecurityContext, out.SecurityContext, s); err != nil { + return err + } + } + if out.SecurityContext == nil { + out.SecurityContext = new(api.PodSecurityContext) + } + out.SecurityContext.HostNetwork = in.HostNetwork + out.SecurityContext.HostPID = in.HostPID + out.SecurityContext.HostIPC = in.HostIPC if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -309,3 +326,17 @@ func convert_v1alpha1_RollingUpdateDeployment_To_experimental_RollingUpdateDeplo out.MinReadySeconds = in.MinReadySeconds return nil } + +func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurityContext, out *v1.PodSecurityContext, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.PodSecurityContext))(in) + } + return nil +} + +func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *v1.PodSecurityContext, out *api.PodSecurityContext, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.PodSecurityContext))(in) + } + return nil +} diff --git a/pkg/apis/experimental/v1alpha1/conversion_generated.go b/pkg/apis/experimental/v1alpha1/conversion_generated.go index f74edd922ff..9c72e05ff81 100644 --- a/pkg/apis/experimental/v1alpha1/conversion_generated.go +++ b/pkg/apis/experimental/v1alpha1/conversion_generated.go @@ -708,9 +708,13 @@ func autoconvert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *v1.PodSpec, s c } out.ServiceAccountName = in.ServiceAccountName out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil { + return err + } + } else { + out.SecurityContext = nil + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]v1.LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -1755,9 +1759,16 @@ func autoconvert_v1_PodSpec_To_api_PodSpec(in *v1.PodSpec, out *api.PodSpec, s c out.ServiceAccountName = in.ServiceAccountName // in.DeprecatedServiceAccount has no peer in out out.NodeName = in.NodeName - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC + // in.HostNetwork has no peer in out + // in.HostPID has no peer in out + // in.HostIPC has no peer in out + if in.SecurityContext != nil { + if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil { + return err + } + } else { + out.SecurityContext = nil + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { diff --git a/pkg/apis/experimental/v1alpha1/deep_copy_generated.go b/pkg/apis/experimental/v1alpha1/deep_copy_generated.go index 91148b91be6..4cdba093231 100644 --- a/pkg/apis/experimental/v1alpha1/deep_copy_generated.go +++ b/pkg/apis/experimental/v1alpha1/deep_copy_generated.go @@ -497,6 +497,10 @@ func deepCopy_v1_PersistentVolumeClaimVolumeSource(in v1.PersistentVolumeClaimVo return nil } +func deepCopy_v1_PodSecurityContext(in v1.PodSecurityContext, out *v1.PodSecurityContext, c *conversion.Cloner) error { + return nil +} + func deepCopy_v1_PodSpec(in v1.PodSpec, out *v1.PodSpec, c *conversion.Cloner) error { if in.Volumes != nil { out.Volumes = make([]v1.Volume, len(in.Volumes)) @@ -546,6 +550,14 @@ func deepCopy_v1_PodSpec(in v1.PodSpec, out *v1.PodSpec, c *conversion.Cloner) e out.HostNetwork = in.HostNetwork out.HostPID = in.HostPID out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + out.SecurityContext = new(v1.PodSecurityContext) + if err := deepCopy_v1_PodSecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { + return err + } + } else { + out.SecurityContext = nil + } if in.ImagePullSecrets != nil { out.ImagePullSecrets = make([]v1.LocalObjectReference, len(in.ImagePullSecrets)) for i := range in.ImagePullSecrets { @@ -1551,6 +1563,7 @@ func init() { deepCopy_v1_ObjectFieldSelector, deepCopy_v1_ObjectMeta, deepCopy_v1_PersistentVolumeClaimVolumeSource, + deepCopy_v1_PodSecurityContext, deepCopy_v1_PodSpec, deepCopy_v1_PodTemplateSpec, deepCopy_v1_Probe, diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index deb5d5f1a45..0622d00f24b 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -29,6 +29,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/unversioned" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/fake" @@ -39,7 +40,6 @@ import ( ) func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) { - grace := int64(30) pods := &api.PodList{ ListMeta: unversioned.ListMeta{ ResourceVersion: "15", @@ -47,19 +47,11 @@ func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, } @@ -567,7 +559,6 @@ func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { } } func watchTestData() ([]api.Pod, []watch.Event) { - grace := int64(30) pods := []api.Pod{ { ObjectMeta: api.ObjectMeta{ @@ -575,11 +566,7 @@ func watchTestData() ([]api.Pod, []watch.Event) { Namespace: "test", ResourceVersion: "10", }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, } events := []watch.Event{ @@ -591,11 +578,7 @@ func watchTestData() ([]api.Pod, []watch.Event) { Namespace: "test", ResourceVersion: "11", }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, { @@ -606,11 +589,7 @@ func watchTestData() ([]api.Pod, []watch.Event) { Namespace: "test", ResourceVersion: "12", }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, } @@ -652,7 +631,7 @@ func TestWatchSelector(t *testing.T) { expected := []runtime.Object{&api.PodList{Items: pods}, events[0].Object, events[1].Object} actual := tf.Printer.(*testPrinter).Objects if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %#v %#v", expected[0], actual[0]) + t.Errorf("unexpected object:\nExpected: %#v\n\nGot: %#v\n\n", expected[0], actual[0]) } if len(buf.String()) == 0 { t.Errorf("unexpected empty output") @@ -690,7 +669,7 @@ func TestWatchResource(t *testing.T) { expected := []runtime.Object{&pods[0], events[0].Object, events[1].Object} actual := tf.Printer.(*testPrinter).Objects if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %#v", actual) + t.Errorf("unexpected object:\nExpected: %#v\n\nGot: %#v\n\n", expected, actual) } if len(buf.String()) == 0 { t.Errorf("unexpected empty output") diff --git a/pkg/kubectl/cmd/util/helpers_test.go b/pkg/kubectl/cmd/util/helpers_test.go index 2547c10fb0d..a0e7d629943 100644 --- a/pkg/kubectl/cmd/util/helpers_test.go +++ b/pkg/kubectl/cmd/util/helpers_test.go @@ -29,6 +29,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/testapi" + apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/fielderrors" ) @@ -54,11 +55,7 @@ func TestMerge(t *testing.T) { ObjectMeta: api.ObjectMeta{ Name: "foo", }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, /* TODO: uncomment this test once Merge is updated to use @@ -127,6 +124,7 @@ func TestMerge(t *testing.T) { RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, + SecurityContext: &api.PodSecurityContext{}, }, }, }, diff --git a/pkg/kubectl/resource/builder_test.go b/pkg/kubectl/resource/builder_test.go index c80a928fbbe..5f241f57385 100644 --- a/pkg/kubectl/resource/builder_test.go +++ b/pkg/kubectl/resource/builder_test.go @@ -32,6 +32,7 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/testapi" + apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/fake" "k8s.io/kubernetes/pkg/runtime" @@ -83,7 +84,6 @@ func fakeClientWith(testName string, t *testing.T, data map[string]string) Clien } func testData() (*api.PodList, *api.ServiceList) { - grace := int64(30) pods := &api.PodList{ ListMeta: unversioned.ListMeta{ ResourceVersion: "15", @@ -91,19 +91,11 @@ func testData() (*api.PodList, *api.ServiceList) { Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, } diff --git a/pkg/kubectl/resource/helper_test.go b/pkg/kubectl/resource/helper_test.go index 4b3e26afd9c..75c9f847a47 100644 --- a/pkg/kubectl/resource/helper_test.go +++ b/pkg/kubectl/resource/helper_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/fake" "k8s.io/kubernetes/pkg/labels" @@ -129,7 +130,6 @@ func TestHelperCreate(t *testing.T) { return true } - grace := int64(30) tests := []struct { Resp *http.Response RespFunc fake.HTTPClientFunc @@ -173,11 +173,7 @@ func TestHelperCreate(t *testing.T) { Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, ExpectObject: &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess})}, Req: expectPost, @@ -384,7 +380,6 @@ func TestHelperReplace(t *testing.T) { return true } - grace := int64(30) tests := []struct { Resp *http.Response RespFunc fake.HTTPClientFunc @@ -421,11 +416,7 @@ func TestHelperReplace(t *testing.T) { Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, ExpectObject: &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, Overwrite: true, RespFunc: func(req *http.Request) (*http.Response, error) { diff --git a/pkg/kubectl/rolling_updater_test.go b/pkg/kubectl/rolling_updater_test.go index 41c2d0d1a2e..f48f08d7c15 100644 --- a/pkg/kubectl/rolling_updater_test.go +++ b/pkg/kubectl/rolling_updater_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + apitesting "k8s.io/kubernetes/pkg/api/testing" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/fake" "k8s.io/kubernetes/pkg/client/unversioned/testclient" @@ -1011,7 +1012,6 @@ func TestUpdateExistingReplicationController(t *testing.T) { func TestUpdateWithRetries(t *testing.T) { codec := testapi.Default.Codec() - grace := int64(30) rc := &api.ReplicationController{ ObjectMeta: api.ObjectMeta{Name: "rc", Labels: map[string]string{ @@ -1028,11 +1028,7 @@ func TestUpdateWithRetries(t *testing.T) { "foo": "bar", }, }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, } diff --git a/pkg/kubelet/config/common_test.go b/pkg/kubelet/config/common_test.go index 1dc2a21f59c..65f4a17b6bf 100644 --- a/pkg/kubelet/config/common_test.go +++ b/pkg/kubelet/config/common_test.go @@ -53,6 +53,7 @@ func TestDecodeSinglePod(t *testing.T) { TerminationMessagePath: "/dev/termination-log", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), }}, + SecurityContext: &api.PodSecurityContext{}, }, } json, err := testapi.Default.Codec().Encode(pod) @@ -115,6 +116,7 @@ func TestDecodePodList(t *testing.T) { TerminationMessagePath: "/dev/termination-log", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), }}, + SecurityContext: &api.PodSecurityContext{}, }, } podList := &api.PodList{ diff --git a/pkg/kubelet/config/file_test.go b/pkg/kubelet/config/file_test.go index 6c159607485..e4bb154088a 100644 --- a/pkg/kubelet/config/file_test.go +++ b/pkg/kubelet/config/file_test.go @@ -90,7 +90,8 @@ func TestReadPodsFromFile(t *testing.T) { Namespace: "mynamespace", }, Spec: api.PodSpec{ - Containers: []api.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, + Containers: []api.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, + SecurityContext: &api.PodSecurityContext{}, }, }, expected: CreatePodUpdate(kubelet.SET, kubelet.FileSource, &api.Pod{ @@ -111,6 +112,7 @@ func TestReadPodsFromFile(t *testing.T) { TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "IfNotPresent", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, + SecurityContext: &api.PodSecurityContext{}, }, }), }, diff --git a/pkg/kubelet/config/http_test.go b/pkg/kubelet/config/http_test.go index 4835f1a2694..18b52242b68 100644 --- a/pkg/kubelet/config/http_test.go +++ b/pkg/kubelet/config/http_test.go @@ -143,8 +143,9 @@ func TestExtractPodsFromHTTP(t *testing.T) { Namespace: "mynamespace", }, Spec: api.PodSpec{ - NodeName: hostname, - Containers: []api.Container{{Name: "1", Image: "foo", ImagePullPolicy: api.PullAlways}}, + NodeName: hostname, + Containers: []api.Container{{Name: "1", Image: "foo", ImagePullPolicy: api.PullAlways}}, + SecurityContext: &api.PodSecurityContext{}, }, }, expected: CreatePodUpdate(kubelet.SET, @@ -161,6 +162,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, + SecurityContext: &api.PodSecurityContext{}, TerminationGracePeriodSeconds: &grace, Containers: []api.Container{{ @@ -186,8 +188,9 @@ func TestExtractPodsFromHTTP(t *testing.T) { UID: "111", }, Spec: api.PodSpec{ - NodeName: hostname, - Containers: []api.Container{{Name: "1", Image: "foo", ImagePullPolicy: api.PullAlways}}, + NodeName: hostname, + Containers: []api.Container{{Name: "1", Image: "foo", ImagePullPolicy: api.PullAlways}}, + SecurityContext: &api.PodSecurityContext{}, }, }, { @@ -196,8 +199,9 @@ func TestExtractPodsFromHTTP(t *testing.T) { UID: "222", }, Spec: api.PodSpec{ - NodeName: hostname, - Containers: []api.Container{{Name: "2", Image: "bar", ImagePullPolicy: ""}}, + NodeName: hostname, + Containers: []api.Container{{Name: "2", Image: "bar", ImagePullPolicy: ""}}, + SecurityContext: &api.PodSecurityContext{}, }, }, }, @@ -217,6 +221,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, + SecurityContext: &api.PodSecurityContext{}, Containers: []api.Container{{ Name: "1", @@ -239,6 +244,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, + SecurityContext: &api.PodSecurityContext{}, Containers: []api.Container{{ Name: "2", diff --git a/pkg/kubelet/dockertools/manager.go b/pkg/kubelet/dockertools/manager.go index d71d101fbc3..a1ac804f126 100644 --- a/pkg/kubelet/dockertools/manager.go +++ b/pkg/kubelet/dockertools/manager.go @@ -942,9 +942,9 @@ func (dm *DockerManager) podInfraContainerChanged(pod *api.Pod, podInfraContaine if dockerPodInfraContainer.HostConfig != nil { networkMode = dockerPodInfraContainer.HostConfig.NetworkMode } - if pod.Spec.HostNetwork { + if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork { if networkMode != "host" { - glog.V(4).Infof("host: %v, %v", pod.Spec.HostNetwork, networkMode) + glog.V(4).Infof("host: %v, %v", pod.Spec.SecurityContext.HostNetwork, networkMode) return true, nil } } else { @@ -1468,7 +1468,7 @@ func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Containe } utsMode := "" - if pod.Spec.HostNetwork { + if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork { utsMode = "host" } id, err := dm.runContainer(pod, container, opts, ref, netMode, ipcMode, utsMode, pidMode) @@ -1585,7 +1585,7 @@ func (dm *DockerManager) createPodInfraContainer(pod *api.Pod) (kubeletTypes.Doc netNamespace = "none" } - if pod.Spec.HostNetwork { + if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork { netNamespace = "host" } else { // Docker only exports ports from the pod infra container. Let's @@ -1985,7 +1985,7 @@ func (dm *DockerManager) doBackOff(pod *api.Pod, container *api.Container, podSt // getPidMode returns the pid mode to use on the docker container based on pod.Spec.HostPID. func getPidMode(pod *api.Pod) string { pidMode := "" - if pod.Spec.HostPID { + if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostPID { pidMode = "host" } return pidMode @@ -1994,7 +1994,7 @@ func getPidMode(pod *api.Pod) string { // getIPCMode returns the ipc mode to use on the docker container based on pod.Spec.HostIPC. func getIPCMode(pod *api.Pod) string { ipcMode := "" - if pod.Spec.HostIPC { + if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostIPC { ipcMode = "host" } return ipcMode diff --git a/pkg/kubelet/dockertools/manager_test.go b/pkg/kubelet/dockertools/manager_test.go index 11a3b97419d..79d129f1e52 100644 --- a/pkg/kubelet/dockertools/manager_test.go +++ b/pkg/kubelet/dockertools/manager_test.go @@ -517,7 +517,7 @@ func TestIsAExitError(t *testing.T) { func generatePodInfraContainerHash(pod *api.Pod) uint64 { var ports []api.ContainerPort - if !pod.Spec.HostNetwork { + if pod.Spec.SecurityContext == nil || !pod.Spec.SecurityContext.HostNetwork { for _, container := range pod.Spec.Containers { ports = append(ports, container.Ports...) } @@ -1819,7 +1819,9 @@ func TestSyncPodWithHostNetwork(t *testing.T) { Containers: []api.Container{ {Name: "bar"}, }, - HostNetwork: true, + SecurityContext: &api.PodSecurityContext{ + HostNetwork: true, + }, }, } @@ -2041,7 +2043,8 @@ func TestGetPidMode(t *testing.T) { } // test true - pod.Spec.HostPID = true + pod.Spec.SecurityContext = &api.PodSecurityContext{} + pod.Spec.SecurityContext.HostPID = true pidMode = getPidMode(pod) if pidMode != "host" { t.Errorf("expected host pid mode for pod but got %v", pidMode) @@ -2058,7 +2061,8 @@ func TestGetIPCMode(t *testing.T) { } // test true - pod.Spec.HostIPC = true + pod.Spec.SecurityContext = &api.PodSecurityContext{} + pod.Spec.SecurityContext.HostIPC = true ipcMode = getIPCMode(pod) if ipcMode != "host" { t.Errorf("expected host ipc mode for pod but got %v", ipcMode) diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index da8a450732d..8c974a382ea 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -1352,7 +1352,7 @@ func (kl *Kubelet) syncPod(pod *api.Pod, mirrorPod *api.Pod, runningPod kubecont return err } if egress != nil || ingress != nil { - if pod.Spec.HostNetwork { + if podUsesHostNetwork(pod) { kl.recorder.Event(pod, "HostNetworkNotSupported", "Bandwidth shaping is not currently supported on the host network") } else if kl.shaper != nil { status, found := kl.statusManager.GetPodStatus(pod.UID) @@ -1391,6 +1391,10 @@ func (kl *Kubelet) syncPod(pod *api.Pod, mirrorPod *api.Pod, runningPod kubecont return nil } +func podUsesHostNetwork(pod *api.Pod) bool { + return pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork +} + // getPullSecretsForPod inspects the Pod and retrieves the referenced pull secrets // TODO duplicate secrets are being retrieved multiple times and there is no cache. Creating and using a secret manager interface will make this easier to address. func (kl *Kubelet) getPullSecretsForPod(pod *api.Pod) ([]api.Secret, error) { @@ -2632,7 +2636,7 @@ func (kl *Kubelet) generatePodStatus(pod *api.Pod) (api.PodStatus, error) { glog.V(4).Infof("Cannot get host IP: %v", err) } else { podStatus.HostIP = hostIP.String() - if pod.Spec.HostNetwork && podStatus.PodIP == "" { + if podUsesHostNetwork(pod) && podStatus.PodIP == "" { podStatus.PodIP = hostIP.String() } } diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 152b97c84eb..1687d1677eb 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -2920,7 +2920,9 @@ func TestHostNetworkAllowed(t *testing.T) { Containers: []api.Container{ {Name: "foo"}, }, - HostNetwork: true, + SecurityContext: &api.PodSecurityContext{ + HostNetwork: true, + }, }, } kubelet.podManager.SetPods([]*api.Pod{pod}) @@ -2952,7 +2954,9 @@ func TestHostNetworkDisallowed(t *testing.T) { Containers: []api.Container{ {Name: "foo"}, }, - HostNetwork: true, + SecurityContext: &api.PodSecurityContext{ + HostNetwork: true, + }, }, } err := kubelet.syncPod(pod, nil, container.Pod{}, SyncPodUpdate) diff --git a/pkg/kubelet/rkt/rkt.go b/pkg/kubelet/rkt/rkt.go index 80614684e75..36c5b6b8e0b 100644 --- a/pkg/kubelet/rkt/rkt.go +++ b/pkg/kubelet/rkt/rkt.go @@ -585,7 +585,7 @@ func (r *Runtime) preparePod(pod *api.Pod, pullSecrets []api.Secret) (string, *k } var runPrepared string - if pod.Spec.HostNetwork { + if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork { runPrepared = fmt.Sprintf("%s run-prepared --mds-register=false %s", r.rktBinAbsPath, uuid) } else { runPrepared = fmt.Sprintf("%s run-prepared --mds-register=false --private-net %s", r.rktBinAbsPath, uuid) diff --git a/pkg/kubelet/util.go b/pkg/kubelet/util.go index 1c2ab0a29c9..a470e1fe86e 100644 --- a/pkg/kubelet/util.go +++ b/pkg/kubelet/util.go @@ -40,7 +40,7 @@ func CapacityFromMachineInfo(info *cadvisorApi.MachineInfo) api.ResourceList { // Check whether we have the capabilities to run the specified pod. func canRunPod(pod *api.Pod) error { - if pod.Spec.HostNetwork { + if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork { allowed, err := allowHostNetwork(pod) if err != nil { return err @@ -50,7 +50,7 @@ func canRunPod(pod *api.Pod) error { } } - if pod.Spec.HostPID { + if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostPID { allowed, err := allowHostPID(pod) if err != nil { return err @@ -60,7 +60,7 @@ func canRunPod(pod *api.Pod) error { } } - if pod.Spec.HostIPC { + if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostIPC { allowed, err := allowHostIPC(pod) if err != nil { return err diff --git a/pkg/registry/pod/etcd/etcd_test.go b/pkg/registry/pod/etcd/etcd_test.go index 71038dc4022..0c4fd6f6e36 100644 --- a/pkg/registry/pod/etcd/etcd_test.go +++ b/pkg/registry/pod/etcd/etcd_test.go @@ -64,6 +64,7 @@ func validNewPod() *api.Pod { SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), }, }, + SecurityContext: &api.PodSecurityContext{}, }, } } @@ -617,6 +618,7 @@ func TestEtcdUpdateScheduled(t *testing.T) { SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), }, }, + SecurityContext: &api.PodSecurityContext{}, }, }), 1) @@ -631,19 +633,18 @@ func TestEtcdUpdateScheduled(t *testing.T) { }, Spec: api.PodSpec{ NodeName: "machine", - Containers: []api.Container{ - { - Name: "foobar", - Image: "foo:v2", - ImagePullPolicy: api.PullIfNotPresent, - TerminationMessagePath: api.TerminationMessagePathDefault, - SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), - }, - }, + Containers: []api.Container{{ + Name: "foobar", + Image: "foo:v2", + ImagePullPolicy: api.PullIfNotPresent, + TerminationMessagePath: api.TerminationMessagePathDefault, + SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), + }}, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, + SecurityContext: &api.PodSecurityContext{}, }, } _, _, err := storage.Update(ctx, &podIn) @@ -682,6 +683,7 @@ func TestEtcdUpdateStatus(t *testing.T) { SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), }, }, + SecurityContext: &api.PodSecurityContext{}, }, } fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), &podStart), 0) @@ -703,6 +705,7 @@ func TestEtcdUpdateStatus(t *testing.T) { TerminationMessagePath: api.TerminationMessagePathDefault, }, }, + SecurityContext: &api.PodSecurityContext{}, }, Status: api.PodStatus{ Phase: api.PodRunning, diff --git a/pkg/storage/cacher_test.go b/pkg/storage/cacher_test.go index c894c4f9379..e24c28c9ab7 100644 --- a/pkg/storage/cacher_test.go +++ b/pkg/storage/cacher_test.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/testapi" + apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" @@ -56,14 +57,9 @@ func newTestCacher(client tools.EtcdClient) *storage.Cacher { } func makeTestPod(name string) *api.Pod { - gracePeriod := int64(30) return &api.Pod{ ObjectMeta: api.ObjectMeta{Namespace: "ns", Name: name}, - Spec: api.PodSpec{ - TerminationGracePeriodSeconds: &gracePeriod, - DNSPolicy: api.DNSClusterFirst, - RestartPolicy: api.RestartPolicyAlways, - }, + Spec: apitesting.DeepEqualSafePodSpec(), } } diff --git a/pkg/storage/etcd/etcd_helper_test.go b/pkg/storage/etcd/etcd_helper_test.go index b6bc31b4874..43bc56d3951 100644 --- a/pkg/storage/etcd/etcd_helper_test.go +++ b/pkg/storage/etcd/etcd_helper_test.go @@ -34,6 +34,7 @@ import ( "github.com/stretchr/testify/assert" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" @@ -124,33 +125,20 @@ func TestList(t *testing.T) { }, }, } - grace := int64(30) expect := api.PodList{ ListMeta: unversioned.ListMeta{ResourceVersion: "10"}, Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, } @@ -197,17 +185,12 @@ func TestListFiltered(t *testing.T) { }, }, } - grace := int64(30) expect := api.PodList{ ListMeta: unversioned.ListMeta{ResourceVersion: "10"}, Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, } @@ -273,34 +256,21 @@ func TestListAcrossDirectories(t *testing.T) { }, }, } - grace := int64(30) expect := api.PodList{ ListMeta: unversioned.ListMeta{ResourceVersion: "10"}, Items: []api.Pod{ // We expect list to be sorted by directory (e.g. namespace) first, then by name. { ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, } @@ -349,33 +319,20 @@ func TestListExcludesDirectories(t *testing.T) { }, }, } - grace := int64(30) expect := api.PodList{ ListMeta: unversioned.ListMeta{ResourceVersion: "10"}, Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), }, }, } @@ -394,14 +351,9 @@ func TestGet(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix()) key := etcdtest.AddPrefix("/some/key") - grace := int64(30) expect := api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), } fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), &expect), 0) var got api.Pod diff --git a/plugin/pkg/admission/exec/admission.go b/plugin/pkg/admission/exec/admission.go index fc33d4a7060..358dc37a397 100644 --- a/plugin/pkg/admission/exec/admission.go +++ b/plugin/pkg/admission/exec/admission.go @@ -90,11 +90,11 @@ func (d *denyExec) Admit(a admission.Attributes) (err error) { return admission.NewForbidden(a, err) } - if d.hostPID && pod.Spec.HostPID { + if d.hostPID && pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostPID { return admission.NewForbidden(a, fmt.Errorf("Cannot exec into or attach to a container using host pid")) } - if d.hostIPC && pod.Spec.HostIPC { + if d.hostIPC && pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostIPC { return admission.NewForbidden(a, fmt.Errorf("Cannot exec into or attach to a container using host ipc")) } diff --git a/plugin/pkg/admission/exec/admission_test.go b/plugin/pkg/admission/exec/admission_test.go index 2a5742aae34..351faf8b7bc 100644 --- a/plugin/pkg/admission/exec/admission_test.go +++ b/plugin/pkg/admission/exec/admission_test.go @@ -34,10 +34,12 @@ func TestAdmission(t *testing.T) { } hostPIDPod := validPod("hostPID") - hostPIDPod.Spec.HostPID = true + hostPIDPod.Spec.SecurityContext = &api.PodSecurityContext{} + hostPIDPod.Spec.SecurityContext.HostPID = true hostIPCPod := validPod("hostIPC") - hostIPCPod.Spec.HostIPC = true + hostIPCPod.Spec.SecurityContext = &api.PodSecurityContext{} + hostIPCPod.Spec.SecurityContext.HostIPC = true testCases := map[string]struct { pod *api.Pod @@ -130,10 +132,12 @@ func TestDenyExecOnPrivileged(t *testing.T) { } hostPIDPod := validPod("hostPID") - hostPIDPod.Spec.HostPID = true + hostPIDPod.Spec.SecurityContext = &api.PodSecurityContext{} + hostPIDPod.Spec.SecurityContext.HostPID = true hostIPCPod := validPod("hostIPC") - hostIPCPod.Spec.HostIPC = true + hostIPCPod.Spec.SecurityContext = &api.PodSecurityContext{} + hostIPCPod.Spec.SecurityContext.HostIPC = true testCases := map[string]struct { pod *api.Pod diff --git a/plugin/pkg/scheduler/factory/factory_test.go b/plugin/pkg/scheduler/factory/factory_test.go index 1eb0de1537b..7a54f7a69f9 100644 --- a/plugin/pkg/scheduler/factory/factory_test.go +++ b/plugin/pkg/scheduler/factory/factory_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/client/cache" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/runtime" @@ -132,14 +133,9 @@ func PriorityTwo(pod *api.Pod, podLister algorithm.PodLister, nodeLister algorit } func TestDefaultErrorFunc(t *testing.T) { - grace := int64(30) testPod := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - }, + Spec: apitesting.DeepEqualSafePodSpec(), } handler := util.FakeHandler{ StatusCode: 200,