Skip to content

Commit

Permalink
Add PodSecurityContext and backward compatibility tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pmorie committed Oct 6, 2015
1 parent 608244f commit 227dd82
Show file tree
Hide file tree
Showing 41 changed files with 717 additions and 236 deletions.
9 changes: 9 additions & 0 deletions api/swagger-spec/v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -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.",
Expand Down
44 changes: 30 additions & 14 deletions pkg/api/copy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,44 @@ 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) {
for i := 0; i < *fuzzIters; i++ {
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)
}
}
}
19 changes: 16 additions & 3 deletions pkg/api/deep_copy_generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 5 additions & 2 deletions pkg/api/serialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -169,6 +169,8 @@ func TestEncode_Ptr(t *testing.T) {
DNSPolicy: api.DNSClusterFirst,

TerminationGracePeriodSeconds: &grace,

SecurityContext: &api.PodSecurityContext{},
},
}
obj := runtime.Object(pod)
Expand All @@ -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))

}
}

Expand Down
144 changes: 144 additions & 0 deletions pkg/api/testing/compat/compatibility_tester.go
Original file line number Diff line number Diff line change
@@ -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:]...)
}
10 changes: 7 additions & 3 deletions pkg/api/testing/fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
32 changes: 32 additions & 0 deletions pkg/api/testing/pod_specs.go
Original file line number Diff line number Diff line change
@@ -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{},
}
}
19 changes: 13 additions & 6 deletions pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 227dd82

Please sign in to comment.