diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json index 32de6904ade9a..53e6f73dea5bf 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 c3c55283a7fd7..0744873ce3b1e 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 64acf98541feb..8b73d005346a4 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 c53e198c0f312..509440e16b9fc 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 0000000000000..42314d1862194 --- /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 d22593c6b19fd..d0429467f4f76 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 0000000000000..2020b3f7fb0ed --- /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 274ac5614b0b6..35aadf2f5a497 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 0000000000000..0bcfa3e31b6c5 --- /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 1e4bb1959f220..ba5f619d175e5 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 2969a4cb639d3..0e9e777d8d26f 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 b49972d845ba4..87cbcf4b71c7a 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 293a54fa55866..c4a67e3295aca 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 906682be3a5c3..13f4618b7b8d1 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 f694d049fe57a..4f25221a79aed 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 8c720630db7df..6bfd788147962 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 7604cec7821be..6cce29831b60b 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 056784d538426..a4b080e26aaf1 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 { @@ -1470,6 +1482,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 2d2c6f44544ed..515f5f90a6bd0 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 463c91237aa00..e3810f5b2a05e 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 cfb2033a3d174..7568dd1b85d1e 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 { @@ -1496,6 +1508,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 deb5d5f1a4530..0622d00f24b22 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 2547c10fb0de8..a0e7d62994307 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 c80a928fbbe4c..5f241f5738505 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 4b3e26afd9c96..75c9f847a47df 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 41c2d0d1a2e5c..f48f08d7c15f1 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 1dc2a21f59cec..65f4a17b6bf50 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 6c159607485da..e4bb154088af8 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 4835f1a2694e6..18b52242b688a 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 d71d101fbc37e..a1ac804f12601 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 11a3b97419db8..79d129f1e52e4 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 e6db9884d49ad..f59fa930df2e8 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -1350,7 +1350,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) @@ -1389,6 +1389,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) { @@ -2630,7 +2634,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 152b97c84ebd9..1687d1677eb7a 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 1f9c9a13b1309..cd8901b0ba4b0 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 1c2ab0a29c904..a470e1fe86e37 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 71038dc4022a6..0c4fd6f6e3657 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 c894c4f93793e..e24c28c9ab7fe 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 b6bc31b4874b8..43bc56d395193 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 fc33d4a70607a..358dc37a39745 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 2a5742aae3416..351faf8b7bc71 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 1eb0de1537b3d..7a54f7a69f932 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,