Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pod templates as their own type #5012

Merged
merged 4 commits into from
Apr 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
331 changes: 266 additions & 65 deletions api/swagger-spec/v1beta1.json

Large diffs are not rendered by default.

327 changes: 264 additions & 63 deletions api/swagger-spec/v1beta2.json

Large diffs are not rendered by default.

985 changes: 927 additions & 58 deletions api/swagger-spec/v1beta3.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions contrib/completions/bash/kubectl
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ _kubectl_get()
must_have_one_noun+=("persistentvolume")
must_have_one_noun+=("persistentvolumeclaim")
must_have_one_noun+=("pod")
must_have_one_noun+=("podtemplate")
must_have_one_noun+=("replicationcontroller")
must_have_one_noun+=("resourcequota")
must_have_one_noun+=("secret")
Expand Down
13 changes: 9 additions & 4 deletions examples/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (
)

func validateObject(obj runtime.Object) (errors []error) {
ctx := api.NewDefaultContext()
switch t := obj.(type) {
case *api.ReplicationController:
if t.Namespace == "" {
Expand All @@ -49,7 +48,6 @@ func validateObject(obj runtime.Object) (errors []error) {
if t.Namespace == "" {
t.Namespace = api.NamespaceDefault
}
api.ValidNamespace(ctx, &t.ObjectMeta)
errors = validation.ValidateService(t)
case *api.ServiceList:
for i := range t.Items {
Expand All @@ -59,7 +57,6 @@ func validateObject(obj runtime.Object) (errors []error) {
if t.Namespace == "" {
t.Namespace = api.NamespaceDefault
}
api.ValidNamespace(ctx, &t.ObjectMeta)
errors = validation.ValidatePod(t)
case *api.PodList:
for i := range t.Items {
Expand All @@ -68,8 +65,15 @@ func validateObject(obj runtime.Object) (errors []error) {
case *api.PersistentVolume:
errors = validation.ValidatePersistentVolume(t)
case *api.PersistentVolumeClaim:
api.ValidNamespace(ctx, &t.ObjectMeta)
if t.Namespace == "" {
t.Namespace = api.NamespaceDefault
}
errors = validation.ValidatePersistentVolumeClaim(t)
case *api.PodTemplate:
if t.Namespace == "" {
t.Namespace = api.NamespaceDefault
}
errors = validation.ValidatePodTemplate(t)
default:
return []error{fmt.Errorf("no validation defined for %#v", obj)}
}
Expand Down Expand Up @@ -156,6 +160,7 @@ func TestExampleObjectSchemas(t *testing.T) {
"pod-with-http-healthcheck": &api.Pod{},
"service": &api.Service{},
"replication-controller": &api.ReplicationController{},
"podtemplate": &api.PodTemplate{},
},
"../examples/update-demo": {
"kitten-rc": &api.ReplicationController{},
Expand Down
22 changes: 22 additions & 0 deletions examples/walkthrough/podtemplate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"apiVersion": "v1beta3",
"kind": "PodTemplate",
"metadata": {
"name": "nginx"
},
"template": {
"metadata": {
"labels": {
"name": "nginx"
},
"generateName": "nginx-"
},
"spec": {
"containers": [{
"name": "nginx",
"image": "dockerfile/nginx",
"ports": [{"containerPort": 80}]
}]
}
}
}
28 changes: 28 additions & 0 deletions hack/test-cmd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,34 @@ for version in "${kube_api_versions[@]}"; do
kube::test::get_object_assert 'pods --namespace=other' "{{range.items}}{{$id_field}}:{{end}}" ''


#################
# Pod templates #
#################

# Note: pod templates exist only in v1beta3 and above, so output will always be in that form

### Create PODTEMPLATE
# Pre-condition: no PODTEMPLATE
kube::test::get_object_assert podtemplates "{{range.items}}{{.metadata.name}}:{{end}}" ''
# Command
kubectl create -f examples/walkthrough/podtemplate.json "${kube_flags[@]}"
# Post-condition: nginx PODTEMPLATE is available
kube::test::get_object_assert podtemplates "{{range.items}}{{.metadata.name}}:{{end}}" 'nginx:'

### Printing pod templates works
kubectl get podtemplates "${kube_flags[@]}"
### Display of an object which doesn't existing in v1beta1 and v1beta2 works
[[ "$(kubectl get podtemplates -o yaml "${kube_flags[@]}" | grep nginx)" ]]

### Delete nginx pod template by name
# Pre-condition: nginx pod template is available
kube::test::get_object_assert podtemplates "{{range.items}}{{.metadata.name}}:{{end}}" 'nginx:'
# Command
kubectl delete podtemplate nginx "${kube_flags[@]}"
# Post-condition: No templates exist
kube::test::get_object_assert podtemplate "{{range.items}}{{.metadata.name}}:{{end}}" ''


############
# Services #
############
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/latest/latest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ func TestRESTMapper(t *testing.T) {
t.Errorf("unexpected version mapping: %s %s %v", v, k, err)
}

if m, err := RESTMapper.RESTMapping("PodTemplate", ""); err != nil || m.APIVersion != "v1beta3" || m.Resource != "podtemplates" {
t.Errorf("unexpected version mapping: %#v %v", m, err)
}

for _, version := range Versions {
mapping, err := RESTMapper.RESTMapping("ReplicationController", version)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func init() {
&Pod{},
&PodList{},
&PodStatusResult{},
&PodTemplate{},
&PodTemplateList{},
&ReplicationControllerList{},
&ReplicationController{},
&ServiceList{},
Expand Down Expand Up @@ -71,6 +73,8 @@ func init() {
func (*Pod) IsAnAPIObject() {}
func (*PodList) IsAnAPIObject() {}
func (*PodStatusResult) IsAnAPIObject() {}
func (*PodTemplate) IsAnAPIObject() {}
func (*PodTemplateList) IsAnAPIObject() {}
func (*ReplicationController) IsAnAPIObject() {}
func (*ReplicationControllerList) IsAnAPIObject() {}
func (*Service) IsAnAPIObject() {}
Expand Down
27 changes: 25 additions & 2 deletions pkg/api/rest/resttest/resttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func (t *Tester) TestCreateRejectsMismatchedNamespace(valid runtime.Object) {
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") {
t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err.Error())
t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err)
}
}

Expand All @@ -195,7 +195,30 @@ func (t *Tester) TestCreateRejectsNamespace(valid runtime.Object) {
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") {
t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err.Error())
t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err)
}
}

func (t *Tester) TestUpdate(valid runtime.Object, existing, older runtime.Object) {
t.TestUpdateFailsOnNotFound(copyOrDie(valid))
t.TestUpdateFailsOnVersion(copyOrDie(older))
}

func (t *Tester) TestUpdateFailsOnNotFound(valid runtime.Object) {
_, _, err := t.storage.(rest.Updater).Update(api.NewDefaultContext(), valid)
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if !errors.IsNotFound(err) {
t.Errorf("Expected NotFound error, got '%v'", err)
}
}

func (t *Tester) TestUpdateFailsOnVersion(older runtime.Object) {
_, _, err := t.storage.(rest.Updater).Update(api.NewDefaultContext(), older)
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if !errors.IsConflict(err) {
t.Errorf("Expected Conflict error, got '%v'", err)
}
}

Expand Down
23 changes: 17 additions & 6 deletions pkg/api/serialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,20 @@ func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) {
}

// roundTripSame verifies the same source object is tested in all API versions.
func roundTripSame(t *testing.T, item runtime.Object) {
func roundTripSame(t *testing.T, item runtime.Object, except ...string) {
set := util.NewStringSet(except...)
seed := rand.Int63()
fuzzInternalObject(t, "", item, seed)
roundTrip(t, v1beta1.Codec, item)
roundTrip(t, v1beta2.Codec, item)
fuzzInternalObject(t, "v1beta3", item, seed)
roundTrip(t, v1beta3.Codec, item)
if !set.Has("v1beta1") {
roundTrip(t, v1beta1.Codec, item)
}
if !set.Has("v1beta2") {
roundTrip(t, v1beta2.Codec, item)
}
if !set.Has("v1beta3") {
fuzzInternalObject(t, "v1beta3", item, seed)
roundTrip(t, v1beta3.Codec, item)
}
}

func roundTripAll(t *testing.T, item runtime.Object) {
Expand Down Expand Up @@ -130,6 +137,10 @@ func TestList(t *testing.T) {

var nonRoundTrippableTypes = util.NewStringSet("ContainerManifest", "ContainerManifestList")
var nonInternalRoundTrippableTypes = util.NewStringSet("List", "ListOptions", "PodExecOptions")
var nonRoundTrippableTypesByVersion = map[string][]string{
"PodTemplate": {"v1beta1", "v1beta2"},
"PodTemplateList": {"v1beta1", "v1beta2"},
}

func TestRoundTripTypes(t *testing.T) {
// api.Scheme.Log(t)
Expand All @@ -148,7 +159,7 @@ func TestRoundTripTypes(t *testing.T) {
if _, err := meta.TypeAccessor(item); err != nil {
t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err)
}
roundTripSame(t, item)
roundTripSame(t, item, nonRoundTrippableTypesByVersion[kind]...)
if !nonInternalRoundTrippableTypes.Has(kind) {
roundTrip(t, api.Codec, fuzzInternalObject(t, "", item, rand.Int63()))
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -848,8 +848,8 @@ type PodTemplate struct {
TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty"`

// Spec defines the pods that will be created from this template
Spec PodTemplateSpec `json:"spec,omitempty"`
// Template defines the pods that will be created from this pod template
Template PodTemplateSpec `json:"template,omitempty"`
}

// PodTemplateList is a list of PodTemplates.
Expand Down
4 changes: 2 additions & 2 deletions pkg/api/v1beta3/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -854,8 +854,8 @@ type PodTemplate struct {
TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty" description:"standard object metadata; see https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/api-conventions.md#metadata"`

// Spec defines the behavior of a pod.
Spec PodTemplateSpec `json:"spec,omitempty" description:"specification of the desired behavior of the pod; https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/api-conventions.md#spec-and-status"`
// Template defines the pods that will be created from this pod template
Template PodTemplateSpec `json:"template,omitempty" description:"the template of the desired behavior of the pod; https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/api-conventions.md#spec-and-status"`
}

// PodTemplateList is a list of PodTemplates.
Expand Down
18 changes: 18 additions & 0 deletions pkg/api/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,24 @@ func ValidatePodStatusUpdate(newPod, oldPod *api.Pod) errs.ValidationErrorList {
return allErrs
}

// ValidatePodTemplate tests if required fields in the pod template are set.
func ValidatePodTemplate(pod *api.PodTemplate) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{}
allErrs = append(allErrs, ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName).Prefix("metadata")...)
allErrs = append(allErrs, ValidatePodTemplateSpec(&pod.Template, 0).Prefix("template")...)
return allErrs
}

// ValidatePodTemplateUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
// that cannot be changed.
func ValidatePodTemplateUpdate(newPod, oldPod *api.PodTemplate) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{}

allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldPod.ObjectMeta, &newPod.ObjectMeta).Prefix("metadata")...)
allErrs = append(allErrs, ValidatePodTemplateSpec(&newPod.Template, 0).Prefix("template")...)
return allErrs
}

var supportedSessionAffinityType = util.NewStringSet(string(api.AffinityTypeClientIP), string(api.AffinityTypeNone))

// ValidateService tests if required fields in the service are set.
Expand Down
Loading