diff --git a/api/examples/pod-list.json b/api/examples/pod-list.json index ea6f9f874e9..01e4fd4de75 100644 --- a/api/examples/pod-list.json +++ b/api/examples/pod-list.json @@ -6,7 +6,7 @@ "id": "my-pod-1", "labels": { "name": "testRun", - "replicationController": "testRun" + "replicationcontroller": "testRun" }, "desiredState": { "manifest": { @@ -30,7 +30,7 @@ "id": "my-pod-2", "labels": { "name": "testRun", - "replicationController": "testRun" + "replicationcontroller": "testRun" }, "desiredState": { "manifest": { diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index e303a6b581e..0f7cc50a13f 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -19,7 +19,6 @@ limitations under the License. package main import ( - "encoding/json" "io/ioutil" "net" "net/http" @@ -237,24 +236,24 @@ func runReplicationControllerTest(c *client.Client) { if err != nil { glog.Fatalf("Unexpected error: %#v", err) } - var controllerRequest api.ReplicationController - if err := json.Unmarshal(data, &controllerRequest); err != nil { + var controller api.ReplicationController + if err := api.Scheme.DecodeInto(data, &controller); err != nil { glog.Fatalf("Unexpected error: %#v", err) } glog.Infof("Creating replication controllers") - if _, err := c.ReplicationControllers(api.NamespaceDefault).Create(&controllerRequest); err != nil { + if _, err := c.ReplicationControllers(api.NamespaceDefault).Create(&controller); err != nil { glog.Fatalf("Unexpected error: %#v", err) } glog.Infof("Done creating replication controllers") // Give the controllers some time to actually create the pods - if err := wait.Poll(time.Second, time.Second*30, c.ControllerHasDesiredReplicas(controllerRequest)); err != nil { + if err := wait.Poll(time.Second, time.Second*30, c.ControllerHasDesiredReplicas(controller)); err != nil { glog.Fatalf("FAILED: pods never created %v", err) } // wait for minions to indicate they have info about the desired pods - pods, err := c.Pods(api.NamespaceDefault).List(labels.Set(controllerRequest.DesiredState.ReplicaSelector).AsSelector()) + pods, err := c.Pods(api.NamespaceDefault).List(labels.Set(controller.Spec.Selector).AsSelector()) if err != nil { glog.Fatalf("FAILED: unable to get pods to list: %v", err) } diff --git a/examples/examples_test.go b/examples/examples_test.go index ff4552a1f4b..f659517007f 100644 --- a/examples/examples_test.go +++ b/examples/examples_test.go @@ -36,12 +36,18 @@ func validateObject(obj runtime.Object) (errors []error) { ctx := api.NewDefaultContext() switch t := obj.(type) { case *api.ReplicationController: - errors = validation.ValidateManifest(&t.DesiredState.PodTemplate.DesiredState.Manifest) + if t.Namespace == "" { + t.Namespace = api.NamespaceDefault + } + errors = validation.ValidateReplicationController(t) case *api.ReplicationControllerList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } case *api.Service: + if t.Namespace == "" { + t.Namespace = api.NamespaceDefault + } api.ValidNamespace(ctx, &t.ObjectMeta) errors = validation.ValidateService(t, registrytest.NewServiceRegistry(), api.NewDefaultContext()) case *api.ServiceList: @@ -49,8 +55,11 @@ func validateObject(obj runtime.Object) (errors []error) { errors = append(errors, validateObject(&t.Items[i])...) } case *api.Pod: + if t.Namespace == "" { + t.Namespace = api.NamespaceDefault + } api.ValidNamespace(ctx, &t.ObjectMeta) - errors = validation.ValidateManifest(&t.DesiredState.Manifest) + errors = validation.ValidatePod(t) case *api.PodList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) diff --git a/examples/walkthrough/pod-with-http-healthcheck.yaml b/examples/walkthrough/pod-with-http-healthcheck.yaml index 58c9cc005af..e2774a6404a 100644 --- a/examples/walkthrough/pod-with-http-healthcheck.yaml +++ b/examples/walkthrough/pod-with-http-healthcheck.yaml @@ -1,5 +1,6 @@ kind: Pod apiVersion: v1beta1 +id: pod-with-healthcheck desiredState: manifest: version: v1beta1 diff --git a/examples/walkthrough/replication-controller.yaml b/examples/walkthrough/replication-controller.yaml index 37117a40b96..fa60c96d9db 100644 --- a/examples/walkthrough/replication-controller.yaml +++ b/examples/walkthrough/replication-controller.yaml @@ -22,4 +22,4 @@ desiredState: # Important: these labels need to match the selector above # The api server enforces this constraint. labels: - - name: nginx + name: nginx diff --git a/pkg/api/conversion.go b/pkg/api/conversion.go index ebdce3d5180..c679ef7c3ca 100644 --- a/pkg/api/conversion.go +++ b/pkg/api/conversion.go @@ -59,6 +59,8 @@ func init() { } return nil }, + + // ContainerManifestList func(in *ContainerManifestList, out *BoundPods, s conversion.Scope) error { if err := s.Convert(&in.Items, &out.Items, 0); err != nil { return err @@ -90,5 +92,32 @@ func init() { out.CreationTimestamp = in.CreationTimestamp return nil }, + + // Conversion between Manifest and PodSpec + func(in *PodSpec, out *ContainerManifest, s conversion.Scope) error { + if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil { + return err + } + if err := s.Convert(&in.Containers, &out.Containers, 0); err != nil { + return err + } + if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { + return err + } + out.Version = "v1beta2" + return nil + }, + func(in *ContainerManifest, out *PodSpec, s conversion.Scope) error { + if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil { + return err + } + if err := s.Convert(&in.Containers, &out.Containers, 0); err != nil { + return err + } + if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { + return err + } + return nil + }, ) } diff --git a/pkg/api/errors/validation.go b/pkg/api/errors/validation.go index 2d01fbb9f22..c7785367d71 100644 --- a/pkg/api/errors/validation.go +++ b/pkg/api/errors/validation.go @@ -140,7 +140,7 @@ func (list ValidationErrorList) Prefix(prefix string) ValidationErrorList { } list[i] = err } else { - glog.Warningf("ValidationErrorList holds non-ValidationError: %T", list) + glog.Warningf("Programmer error: ValidationErrorList holds non-ValidationError: %#v", list[i]) } } return list diff --git a/pkg/api/latest/latest_test.go b/pkg/api/latest/latest_test.go index 0587ad2c2ad..3e5648598c3 100644 --- a/pkg/api/latest/latest_test.go +++ b/pkg/api/latest/latest_test.go @@ -75,6 +75,23 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( statuses := []internal.PodCondition{internal.PodPending, internal.PodRunning, internal.PodFailed} *j = statuses[c.Rand.Intn(len(statuses))] }, + func(j *internal.ReplicationControllerSpec, c fuzz.Continue) { + // TemplateRef must be nil for round trip + c.Fuzz(&j.Template) + if j.Template == nil { + // TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2 + // conversion compare converted object to nil via DeepEqual + j.Template = &internal.PodTemplateSpec{} + } + j.Template.ObjectMeta = internal.ObjectMeta{Labels: j.Template.ObjectMeta.Labels} + j.Template.Spec.NodeSelector = nil + c.Fuzz(&j.Selector) + j.Replicas = int(c.RandUint64()) + }, + func(j *internal.ReplicationControllerStatus, c fuzz.Continue) { + // only replicas round trips + j.Replicas = int(c.RandUint64()) + }, func(intstr *util.IntOrString, c fuzz.Continue) { // util.IntOrString will panic if its kind is set wrong. if c.RandBool() { @@ -127,6 +144,7 @@ func TestInternalRoundTrip(t *testing.T) { if err := internal.Scheme.Convert(obj, newer); err != nil { t.Errorf("unable to convert %#v to %#v: %v", obj, newer, err) + continue } actual, err := internal.Scheme.New("", k) @@ -137,6 +155,7 @@ func TestInternalRoundTrip(t *testing.T) { if err := internal.Scheme.Convert(newer, actual); err != nil { t.Errorf("unable to convert %#v to %#v: %v", newer, actual, err) + continue } if !reflect.DeepEqual(obj, actual) { diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 8a94b1f118d..b849e1c56b6 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -90,6 +90,23 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( statuses := []api.PodCondition{api.PodPending, api.PodRunning, api.PodFailed} *j = statuses[c.Rand.Intn(len(statuses))] }, + func(j *api.ReplicationControllerSpec, c fuzz.Continue) { + // TemplateRef must be nil for round trip + c.Fuzz(&j.Template) + if j.Template == nil { + // TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2 + // conversion compare converted object to nil via DeepEqual + j.Template = &api.PodTemplateSpec{} + } + j.Template.ObjectMeta = api.ObjectMeta{Labels: j.Template.ObjectMeta.Labels} + j.Template.Spec.NodeSelector = nil + c.Fuzz(&j.Selector) + j.Replicas = int(c.RandUint64()) + }, + func(j *api.ReplicationControllerStatus, c fuzz.Continue) { + // only replicas round trips + j.Replicas = int(c.RandUint64()) + }, func(intstr *util.IntOrString, c fuzz.Continue) { // util.IntOrString will panic if its kind is set wrong. if c.RandBool() { @@ -176,18 +193,21 @@ func TestSpecificKind(t *testing.T) { api.Scheme.Log(nil) } -func TestTypes(t *testing.T) { +var nonRoundTrippableTypes = util.NewStringSet("ContainerManifest") + +func TestRoundTripTypes(t *testing.T) { for kind := range api.Scheme.KnownTypes("") { + if nonRoundTrippableTypes.Has(kind) { + continue + } // Try a few times, since runTest uses random values. for i := 0; i < *fuzzIters; i++ { item, err := api.Scheme.New("", kind) if err != nil { - t.Errorf("Couldn't make a %v? %v", kind, err) - continue + t.Fatalf("Couldn't make a %v? %v", kind, err) } if _, err := meta.Accessor(item); err != nil { - t.Logf("%s is not a TypeMeta and cannot be round tripped: %v", kind, err) - continue + t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err) } runTest(t, v1beta1.Codec, item) runTest(t, v1beta2.Codec, item) diff --git a/pkg/api/types.go b/pkg/api/types.go index 10ab6221d96..bc278d82fc2 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -442,6 +442,15 @@ type PodList struct { Items []Pod `json:"items" yaml:"items"` } +// PodSpec is a description of a pod +type PodSpec struct { + Volumes []Volume `json:"volumes" yaml:"volumes"` + Containers []Container `json:"containers" yaml:"containers"` + RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"` + // NodeSelector is a selector which must be true for the pod to fit on a node + NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"` +} + // Pod is a collection of containers, used as either input (create, update) or as output (list, get). type Pod struct { TypeMeta `json:",inline" yaml:",inline"` @@ -453,11 +462,72 @@ type Pod struct { NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"` } -// ReplicationControllerState is the state of a replication controller, either input (create, update) or as output (list, get). -type ReplicationControllerState struct { - Replicas int `json:"replicas" yaml:"replicas"` - ReplicaSelector map[string]string `json:"replicaSelector,omitempty" yaml:"replicaSelector,omitempty"` - PodTemplate PodTemplate `json:"podTemplate,omitempty" yaml:"podTemplate,omitempty"` +// PodTemplateSpec describes the data a pod should have when created from a template +type PodTemplateSpec struct { + // Metadata of the pods created from this template. + ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + + // Spec defines the behavior of a pod. + Spec PodSpec `json:"spec,omitempty" yaml:"spec,omitempty"` +} + +// PodTemplate describes a template for creating copies of a predefined pod. +type PodTemplate struct { + TypeMeta `json:",inline" yaml:",inline"` + ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + + // Spec defines the pods that will be created from this template + Spec PodTemplateSpec `json:"spec,omitempty" yaml:"spec,omitempty"` +} + +// PodTemplateList is a list of PodTemplates. +type PodTemplateList struct { + TypeMeta `json:",inline" yaml:",inline"` + ListMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + + Items []PodTemplate `json:"items" yaml:"items"` +} + +// ReplicationControllerSpec is the specification of a replication controller. +// As the internal representation of a replication controller, it may have either +// a TemplateRef or a Template set. +type ReplicationControllerSpec struct { + // Replicas is the number of desired replicas. + Replicas int `json:"replicas" yaml:"replicas"` + + // Selector is a label query over pods that should match the Replicas count. + Selector map[string]string `json:"selector" yaml:"selector"` + + // TemplateRef is a reference to an object that describes the pod that will be created if + // insufficient replicas are detected. This reference is ignored if a Template is set. + // Must be set before converting to a v1beta3 API object + TemplateRef *ObjectReference `json:"templateRef,omitempty" yaml:"templateRef,omitempty"` + + // Template is the object that describes the pod that will be created if + // insufficient replicas are detected. Internally, this takes precedence over a + // TemplateRef. + // Must be set before converting to a v1beta1 or v1beta2 API object. + Template *PodTemplateSpec `json:"template,omitempty" yaml:"template,omitempty"` +} + +// ReplicationControllerStatus represents the current status of a replication +// controller. +type ReplicationControllerStatus struct { + // Replicas is the number of actual replicas. + Replicas int `json:"replicas" yaml:"replicas"` +} + +// ReplicationController represents the configuration of a replication controller. +type ReplicationController struct { + TypeMeta `json:",inline" yaml:",inline"` + ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + + // Spec defines the desired behavior of this replication controller. + Spec ReplicationControllerSpec `json:"spec,omitempty" yaml:"spec,omitempty"` + + // Status is the current status of this replication controller. This data may be + // out of date by some window of time. + Status ReplicationControllerStatus `json:"status,omitempty" yaml:"status,omitempty"` } // ReplicationControllerList is a collection of replication controllers. @@ -468,21 +538,6 @@ type ReplicationControllerList struct { Items []ReplicationController `json:"items" yaml:"items"` } -// ReplicationController represents the configuration of a replication controller. -type ReplicationController struct { - TypeMeta `json:",inline" yaml:",inline"` - ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` - - DesiredState ReplicationControllerState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"` - CurrentState ReplicationControllerState `json:"currentState,omitempty" yaml:"currentState,omitempty"` -} - -// PodTemplate holds the information used for creating pods. -type PodTemplate struct { - DesiredState PodState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"` - Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` -} - // ServiceList holds a list of services. type ServiceList struct { TypeMeta `json:",inline" yaml:",inline"` @@ -872,15 +927,6 @@ type ContainerManifestList struct { Items []ContainerManifest `json:"items" yaml:"items,omitempty"` } -// Included in partial form from v1beta3 to replace ContainerManifest - -// PodSpec is a description of a pod -type PodSpec struct { - Volumes []Volume `json:"volumes" yaml:"volumes"` - Containers []Container `json:"containers" yaml:"containers"` - RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"` -} - // BoundPod is a collection of containers that should be run on a host. A BoundPod // defines how a Pod may change after a Binding is created. A Pod is a request to // execute a pod, whereas a BoundPod is the specification that would be run on a server. diff --git a/pkg/api/v1beta1/conversion.go b/pkg/api/v1beta1/conversion.go index 401091e53fc..15ca3f2eafa 100644 --- a/pkg/api/v1beta1/conversion.go +++ b/pkg/api/v1beta1/conversion.go @@ -256,12 +256,10 @@ func init() { return err } - if err := s.Convert(&in.DesiredState, &out.DesiredState, 0); err != nil { - return err - } - if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil { + if err := s.Convert(&in.Spec, &out.DesiredState, 0); err != nil { return err } + out.CurrentState.Replicas = in.Status.Replicas return nil }, func(in *ReplicationController, out *newer.ReplicationController, s conversion.Scope) error { @@ -275,10 +273,80 @@ func init() { return err } - if err := s.Convert(&in.DesiredState, &out.DesiredState, 0); err != nil { + if err := s.Convert(&in.DesiredState, &out.Spec, 0); err != nil { return err } - if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil { + out.Status.Replicas = in.CurrentState.Replicas + return nil + }, + + func(in *newer.ReplicationControllerSpec, out *ReplicationControllerState, s conversion.Scope) error { + out.Replicas = in.Replicas + if err := s.Convert(&in.Selector, &out.ReplicaSelector, 0); err != nil { + return err + } + if in.TemplateRef != nil && in.Template == nil { + return errors.New("objects with a template ref cannot be converted to older objects, must populate template") + } + if in.Template != nil { + if err := s.Convert(in.Template, &out.PodTemplate, 0); err != nil { + return err + } + } + return nil + }, + func(in *ReplicationControllerState, out *newer.ReplicationControllerSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + if err := s.Convert(&in.ReplicaSelector, &out.Selector, 0); err != nil { + return err + } + out.Template = &newer.PodTemplateSpec{} + if err := s.Convert(&in.PodTemplate, out.Template, 0); err != nil { + return err + } + return nil + }, + + func(in *newer.PodTemplateSpec, out *PodTemplate, s conversion.Scope) error { + if err := s.Convert(&in.Spec, &out.DesiredState.Manifest, 0); err != nil { + return err + } + if err := s.Convert(&in.ObjectMeta.Labels, &out.Labels, 0); err != nil { + return err + } + return nil + }, + func(in *PodTemplate, out *newer.PodTemplateSpec, s conversion.Scope) error { + if err := s.Convert(&in.DesiredState.Manifest, &out.Spec, 0); err != nil { + return err + } + if err := s.Convert(&in.Labels, &out.ObjectMeta.Labels, 0); err != nil { + return err + } + return nil + }, + + func(in *newer.PodSpec, out *ContainerManifest, s conversion.Scope) error { + if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil { + return err + } + if err := s.Convert(&in.Containers, &out.Containers, 0); err != nil { + return err + } + if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { + return err + } + out.Version = "v1beta2" + return nil + }, + func(in *ContainerManifest, out *newer.PodSpec, s conversion.Scope) error { + if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil { + return err + } + if err := s.Convert(&in.Containers, &out.Containers, 0); err != nil { + return err + } + if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { return err } return nil diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 2ae145f5b8f..0d7dacf2b03 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -749,6 +749,8 @@ type PodSpec struct { Volumes []Volume `json:"volumes" yaml:"volumes"` Containers []Container `json:"containers" yaml:"containers"` RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"` + // NodeSelector is a selector which must be true for the pod to fit on a node + NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"` } // BoundPod is a collection of containers that should be run on a host. A BoundPod diff --git a/pkg/api/v1beta2/conversion.go b/pkg/api/v1beta2/conversion.go index 78f2a4b65fc..3a34b11ea63 100644 --- a/pkg/api/v1beta2/conversion.go +++ b/pkg/api/v1beta2/conversion.go @@ -186,12 +186,10 @@ func init() { return err } - if err := s.Convert(&in.DesiredState, &out.DesiredState, 0); err != nil { - return err - } - if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil { + if err := s.Convert(&in.Spec, &out.DesiredState, 0); err != nil { return err } + out.CurrentState.Replicas = in.Status.Replicas return nil }, func(in *ReplicationController, out *newer.ReplicationController, s conversion.Scope) error { @@ -205,10 +203,80 @@ func init() { return err } - if err := s.Convert(&in.DesiredState, &out.DesiredState, 0); err != nil { + if err := s.Convert(&in.DesiredState, &out.Spec, 0); err != nil { return err } - if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil { + out.Status.Replicas = in.CurrentState.Replicas + return nil + }, + + func(in *newer.ReplicationControllerSpec, out *ReplicationControllerState, s conversion.Scope) error { + out.Replicas = in.Replicas + if err := s.Convert(&in.Selector, &out.ReplicaSelector, 0); err != nil { + return err + } + if in.TemplateRef != nil && in.Template == nil { + return errors.New("objects with a template ref cannot be converted to older objects, must populate template") + } + if in.Template != nil { + if err := s.Convert(in.Template, &out.PodTemplate, 0); err != nil { + return err + } + } + return nil + }, + func(in *ReplicationControllerState, out *newer.ReplicationControllerSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + if err := s.Convert(&in.ReplicaSelector, &out.Selector, 0); err != nil { + return err + } + out.Template = &newer.PodTemplateSpec{} + if err := s.Convert(&in.PodTemplate, out.Template, 0); err != nil { + return err + } + return nil + }, + + func(in *newer.PodTemplateSpec, out *PodTemplate, s conversion.Scope) error { + if err := s.Convert(&in.Spec, &out.DesiredState.Manifest, 0); err != nil { + return err + } + if err := s.Convert(&in.ObjectMeta.Labels, &out.Labels, 0); err != nil { + return err + } + return nil + }, + func(in *PodTemplate, out *newer.PodTemplateSpec, s conversion.Scope) error { + if err := s.Convert(&in.DesiredState.Manifest, &out.Spec, 0); err != nil { + return err + } + if err := s.Convert(&in.Labels, &out.ObjectMeta.Labels, 0); err != nil { + return err + } + return nil + }, + + func(in *newer.PodSpec, out *ContainerManifest, s conversion.Scope) error { + if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil { + return err + } + if err := s.Convert(&in.Containers, &out.Containers, 0); err != nil { + return err + } + if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { + return err + } + out.Version = "v1beta2" + return nil + }, + func(in *ContainerManifest, out *newer.PodSpec, s conversion.Scope) error { + if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil { + return err + } + if err := s.Convert(&in.Containers, &out.Containers, 0); err != nil { + return err + } + if err := s.Convert(&in.RestartPolicy, &out.RestartPolicy, 0); err != nil { return err } return nil diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index f58ae83b46b..9b4754d8418 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -750,6 +750,8 @@ type PodSpec struct { Volumes []Volume `json:"volumes" yaml:"volumes"` Containers []Container `json:"containers" yaml:"containers"` RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"` + // NodeSelector is a selector which must be true for the pod to fit on a node + NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"` } // BoundPod is a collection of containers that should be run on a host. A BoundPod diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index e9f399b6653..ebb86d9e3ef 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -167,7 +167,7 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes util.StringSet) errs if len(mnt.Name) == 0 { mErrs = append(mErrs, errs.NewFieldRequired("name", mnt.Name)) } else if !volumes.Has(mnt.Name) { - mErrs = append(mErrs, errs.NewNotFound("name", mnt.Name)) + mErrs = append(mErrs, errs.NewFieldNotFound("name", mnt.Name)) } if len(mnt.MountPath) == 0 { mErrs = append(mErrs, errs.NewFieldRequired("mountPath", mnt.MountPath)) @@ -293,6 +293,7 @@ var supportedManifestVersions = util.NewStringSet("v1beta1", "v1beta2") // This includes checking formatting and uniqueness. It also canonicalizes the // structure by setting default values and implementing any backwards-compatibility // tricks. +// TODO: replaced by ValidatePodSpec func ValidateManifest(manifest *api.ContainerManifest) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} @@ -348,6 +349,21 @@ func ValidatePod(pod *api.Pod) errs.ValidationErrorList { return allErrs } +// ValidatePodSpec tests that the specified PodSpec has valid data. +// This includes checking formatting and uniqueness. It also canonicalizes the +// structure by setting default values and implementing any backwards-compatibility +// tricks. +func ValidatePodSpec(spec *api.PodSpec) errs.ValidationErrorList { + allErrs := errs.ValidationErrorList{} + + allVolumes, vErrs := validateVolumes(spec.Volumes) + allErrs = append(allErrs, vErrs.Prefix("volumes")...) + allErrs = append(allErrs, validateContainers(spec.Containers, allVolumes).Prefix("containers")...) + allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy).Prefix("restartPolicy")...) + allErrs = append(allErrs, validateLabels(spec.NodeSelector).Prefix("nodeSelector")...) + return allErrs +} + func validateLabels(labels map[string]string) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} for k := range labels { @@ -435,30 +451,44 @@ func ValidateReplicationController(controller *api.ReplicationController) errs.V if !util.IsDNSSubdomain(controller.Namespace) { allErrs = append(allErrs, errs.NewFieldInvalid("namespace", controller.Namespace)) } - allErrs = append(allErrs, ValidateReplicationControllerState(&controller.DesiredState).Prefix("desiredState")...) + allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec).Prefix("spec")...) allErrs = append(allErrs, validateLabels(controller.Labels)...) return allErrs } -// ValidateReplicationControllerState tests if required fields in the replication controller state are set. -func ValidateReplicationControllerState(state *api.ReplicationControllerState) errs.ValidationErrorList { +// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set. +func ValidateReplicationControllerSpec(spec *api.ReplicationControllerSpec) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} - if labels.Set(state.ReplicaSelector).AsSelector().Empty() { - allErrs = append(allErrs, errs.NewFieldRequired("replicaSelector", state.ReplicaSelector)) + + selector := labels.Set(spec.Selector).AsSelector() + if selector.Empty() { + allErrs = append(allErrs, errs.NewFieldRequired("selector", spec.Selector)) } - selector := labels.Set(state.ReplicaSelector).AsSelector() - labels := labels.Set(state.PodTemplate.Labels) - if !selector.Matches(labels) { - allErrs = append(allErrs, errs.NewFieldInvalid("podTemplate.labels", state.PodTemplate)) + if spec.Replicas < 0 { + allErrs = append(allErrs, errs.NewFieldInvalid("replicas", spec.Replicas)) } - allErrs = append(allErrs, validateLabels(labels)...) - if state.Replicas < 0 { - allErrs = append(allErrs, errs.NewFieldInvalid("replicas", state.Replicas)) + + if spec.Template == nil { + allErrs = append(allErrs, errs.NewFieldRequired("template", spec.Template)) + } else { + labels := labels.Set(spec.Template.Labels) + if !selector.Matches(labels) { + allErrs = append(allErrs, errs.NewFieldInvalid("template.labels", spec.Template.Labels)) + } + allErrs = append(allErrs, validateLabels(spec.Template.Labels).Prefix("template.labels")...) + allErrs = append(allErrs, ValidatePodTemplateSpec(spec.Template).Prefix("template")...) } - allErrs = append(allErrs, ValidateManifest(&state.PodTemplate.DesiredState.Manifest).Prefix("podTemplate.desiredState.manifest")...) - allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(state.PodTemplate.DesiredState.Manifest.Volumes).Prefix("podTemplate.desiredState.manifest")...) return allErrs } + +// ValidatePodTemplateSpec validates the spec of a pod template +func ValidatePodTemplateSpec(spec *api.PodTemplateSpec) errs.ValidationErrorList { + allErrs := errs.ValidationErrorList{} + allErrs = append(allErrs, ValidatePodSpec(&spec.Spec).Prefix("spec")...) + allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(spec.Spec.Volumes).Prefix("spec.volumes")...) + return allErrs +} + func ValidateReadOnlyPersistentDisks(volumes []api.Volume) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} for _, vol := range volumes { diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index ef48034a1ea..5dae2a6957b 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -862,43 +862,41 @@ func TestValidateService(t *testing.T) { func TestValidateReplicationController(t *testing.T) { validSelector := map[string]string{"a": "b"} validPodTemplate := api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Version: "v1beta1", + Spec: api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: validSelector, }, }, - Labels: validSelector, } invalidVolumePodTemplate := api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Version: "v1beta1", + Spec: api.PodTemplateSpec{ + Spec: api.PodSpec{ Volumes: []api.Volume{{Name: "gcepd", Source: &api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}}, }, }, } invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} invalidPodTemplate := api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Version: "v1beta1", + Spec: api.PodTemplateSpec{ + Spec: api.PodSpec{}, + ObjectMeta: api.ObjectMeta{ + Labels: invalidSelector, }, }, - Labels: invalidSelector, } successCases := []api.ReplicationController{ { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, { ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, } @@ -911,49 +909,49 @@ func TestValidateReplicationController(t *testing.T) { errorCases := map[string]api.ReplicationController{ "zero-length ID": { ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, "missing-namespace": { ObjectMeta: api.ObjectMeta{Name: "abc-123"}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, "empty selector": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Template: &validPodTemplate.Spec, }, }, "selector_doesnt_match": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: map[string]string{"foo": "bar"}, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: map[string]string{"foo": "bar"}, + Template: &validPodTemplate.Spec, }, }, "invalid manifest": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, }, }, "read-write presistent disk": { ObjectMeta: api.ObjectMeta{Name: "abc"}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: invalidVolumePodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &invalidVolumePodTemplate.Spec, }, }, "negative_replicas": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - Replicas: -1, - ReplicaSelector: validSelector, + Spec: api.ReplicationControllerSpec{ + Replicas: -1, + Selector: validSelector, }, }, "invalid_label": { @@ -964,9 +962,9 @@ func TestValidateReplicationController(t *testing.T) { "NoUppercaseOrSpecialCharsLike=Equals": "bar", }, }, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, "invalid_label 2": { @@ -977,8 +975,8 @@ func TestValidateReplicationController(t *testing.T) { "NoUppercaseOrSpecialCharsLike=Equals": "bar", }, }, - DesiredState: api.ReplicationControllerState{ - PodTemplate: invalidPodTemplate, + Spec: api.ReplicationControllerSpec{ + Template: &invalidPodTemplate.Spec, }, }, } @@ -989,13 +987,14 @@ func TestValidateReplicationController(t *testing.T) { } for i := range errs { field := errs[i].(errors.ValidationError).Field - if !strings.HasPrefix(field, "desiredState.podTemplate.") && + if !strings.HasPrefix(field, "spec.template.") && field != "name" && field != "namespace" && - field != "desiredState.replicaSelector" && + field != "spec.selector" && + field != "spec.template" && field != "GCEPersistentDisk.ReadOnly" && - field != "desiredState.replicas" && - field != "desiredState.label" && + field != "spec.replicas" && + field != "spec.template.label" && field != "label" { t.Errorf("%s: missing prefix for: %v", k, errs[i]) } diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 4c14aa61ba1..46809e93c17 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -89,7 +89,7 @@ func (c *testClient) Validate(t *testing.T, received runtime.Object, err error) c.ValidateCommon(t, err) if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) { - t.Errorf("bad response for request %#v: expected %s, got %s", c.Request, c.Response.Body, received) + t.Errorf("bad response for request %#v: expected %#v, got %#v", c.Request, c.Response.Body, received) } } @@ -97,7 +97,7 @@ func (c *testClient) ValidateRaw(t *testing.T, received []byte, err error) { c.ValidateCommon(t, err) if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) { - t.Errorf("bad response for request %#v: expected %s, got %s", c.Request, c.Response.Body, received) + t.Errorf("bad response for request %#v: expected %#v, got %#v", c.Request, c.Response.Body, received) } } @@ -309,8 +309,9 @@ func TestListControllers(t *testing.T) { "name": "baz", }, }, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 2, + Template: &api.PodTemplateSpec{}, }, }, }, @@ -335,8 +336,9 @@ func TestGetController(t *testing.T) { "name": "baz", }, }, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 2, + Template: &api.PodTemplateSpec{}, }, }, }, @@ -361,8 +363,9 @@ func TestUpdateController(t *testing.T) { "name": "baz", }, }, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 2, + Template: &api.PodTemplateSpec{}, }, }, }, @@ -396,8 +399,9 @@ func TestCreateController(t *testing.T) { "name": "baz", }, }, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 2, + Template: &api.PodTemplateSpec{}, }, }, }, diff --git a/pkg/client/conditions.go b/pkg/client/conditions.go index 743bea902a4..a5934d90286 100644 --- a/pkg/client/conditions.go +++ b/pkg/client/conditions.go @@ -26,10 +26,10 @@ import ( // for a controller's ReplicaSelector equals the Replicas count. func (c *Client) ControllerHasDesiredReplicas(controller api.ReplicationController) wait.ConditionFunc { return func() (bool, error) { - pods, err := c.Pods(controller.Namespace).List(labels.Set(controller.DesiredState.ReplicaSelector).AsSelector()) + pods, err := c.Pods(controller.Namespace).List(labels.Set(controller.Spec.Selector).AsSelector()) if err != nil { return false, err } - return len(pods.Items) == controller.DesiredState.Replicas, nil + return len(pods.Items) == controller.Spec.Replicas, nil } } diff --git a/pkg/controller/replication_controller.go b/pkg/controller/replication_controller.go index 24b7053302a..32125d5e8ae 100644 --- a/pkg/controller/replication_controller.go +++ b/pkg/controller/replication_controller.go @@ -35,14 +35,14 @@ type ReplicationManager struct { syncTime <-chan time.Time // To allow injection of syncReplicationController for testing. - syncHandler func(controllerSpec api.ReplicationController) error + syncHandler func(controller api.ReplicationController) error } // PodControlInterface is an interface that knows how to add or delete pods // created as an interface to allow testing. type PodControlInterface interface { // createReplica creates new replicated pods according to the spec. - createReplica(namespace string, controllerSpec api.ReplicationController) + createReplica(namespace string, controller api.ReplicationController) // deletePod deletes the pod identified by podID. deletePod(namespace string, podID string) error } @@ -52,16 +52,23 @@ type RealPodControl struct { kubeClient client.Interface } -func (r RealPodControl) createReplica(namespace string, controllerSpec api.ReplicationController) { +func (r RealPodControl) createReplica(namespace string, controller api.ReplicationController) { desiredLabels := make(labels.Set) - for k, v := range controllerSpec.DesiredState.PodTemplate.Labels { + for k, v := range controller.Spec.Template.Labels { desiredLabels[k] = v } pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ Labels: desiredLabels, }, - DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState, + } + if err := api.Scheme.Convert(&controller.Spec.Template.Spec, &pod.DesiredState.Manifest); err != nil { + glog.Errorf("Unable to convert pod template: %v", err) + return + } + if labels.Set(pod.Labels).AsSelector().Empty() { + glog.Errorf("Unable to create pod replica, no labels") + return } if _, err := r.kubeClient.Pods(namespace).Create(pod); err != nil { glog.Errorf("Unable to create pod replica: %v", err) @@ -144,14 +151,14 @@ func (rm *ReplicationManager) filterActivePods(pods []api.Pod) []api.Pod { return result } -func (rm *ReplicationManager) syncReplicationController(controllerSpec api.ReplicationController) error { - s := labels.Set(controllerSpec.DesiredState.ReplicaSelector).AsSelector() - podList, err := rm.kubeClient.Pods(controllerSpec.Namespace).List(s) +func (rm *ReplicationManager) syncReplicationController(controller api.ReplicationController) error { + s := labels.Set(controller.Spec.Selector).AsSelector() + podList, err := rm.kubeClient.Pods(controller.Namespace).List(s) if err != nil { return err } filteredList := rm.filterActivePods(podList.Items) - diff := len(filteredList) - controllerSpec.DesiredState.Replicas + diff := len(filteredList) - controller.Spec.Replicas if diff < 0 { diff *= -1 wait := sync.WaitGroup{} @@ -160,7 +167,7 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli for i := 0; i < diff; i++ { go func() { defer wait.Done() - rm.podControl.createReplica(controllerSpec.Namespace, controllerSpec) + rm.podControl.createReplica(controller.Namespace, controller) }() } wait.Wait() @@ -171,7 +178,7 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli for i := 0; i < diff; i++ { go func(ix int) { defer wait.Done() - rm.podControl.deletePod(controllerSpec.Namespace, filteredList[ix].Name) + rm.podControl.deletePod(controller.Namespace, filteredList[ix].Name) }(i) } wait.Wait() @@ -182,20 +189,20 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli func (rm *ReplicationManager) synchronize() { // TODO: remove this method completely and rely on the watch. // Add resource version tracking to watch to make this work. - var controllerSpecs []api.ReplicationController + var controllers []api.ReplicationController list, err := rm.kubeClient.ReplicationControllers(api.NamespaceAll).List(labels.Everything()) if err != nil { glog.Errorf("Synchronization error: %v (%#v)", err, err) return } - controllerSpecs = list.Items + controllers = list.Items wg := sync.WaitGroup{} - wg.Add(len(controllerSpecs)) - for ix := range controllerSpecs { + wg.Add(len(controllers)) + for ix := range controllers { go func(ix int) { defer wg.Done() - glog.V(4).Infof("periodic sync of %v", controllerSpecs[ix].Name) - err := rm.syncHandler(controllerSpecs[ix]) + glog.V(4).Infof("periodic sync of %v", controllers[ix].Name) + err := rm.syncHandler(controllers[ix]) if err != nil { glog.Errorf("Error synchronizing: %#v", err) } diff --git a/pkg/controller/replication_controller_test.go b/pkg/controller/replication_controller_test.go index 5034c1a64af..39c9716627e 100644 --- a/pkg/controller/replication_controller_test.go +++ b/pkg/controller/replication_controller_test.go @@ -62,21 +62,21 @@ func (f *FakePodControl) deletePod(namespace string, podName string) error { func newReplicationController(replicas int) api.ReplicationController { return api.ReplicationController{ - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: replicas, - PodTemplate: api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Containers: []api.Container{ - { - Image: "foo/bar", - }, - }, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{ + "name": "foo", + "type": "production", }, }, - Labels: map[string]string{ - "name": "foo", - "type": "production", + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Image: "foo/bar", + }, + }, }, }, }, @@ -188,21 +188,21 @@ func TestCreateReplica(t *testing.T) { ObjectMeta: api.ObjectMeta{ Name: "test", }, - DesiredState: api.ReplicationControllerState{ - PodTemplate: api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Containers: []api.Container{ - { - Image: "foo/bar", - }, - }, + Spec: api.ReplicationControllerSpec{ + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{ + "name": "foo", + "type": "production", + "replicationController": "test", }, }, - Labels: map[string]string{ - "name": "foo", - "type": "production", - "replicationController": "test", + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Image: "foo/bar", + }, + }, }, }, }, @@ -210,11 +210,18 @@ func TestCreateReplica(t *testing.T) { podControl.createReplica(ns, controllerSpec) + manifest := api.ContainerManifest{} + if err := api.Scheme.Convert(&controllerSpec.Spec.Template.Spec, &manifest); err != nil { + t.Fatalf("unexpected error", err) + } + expectedPod := api.Pod{ ObjectMeta: api.ObjectMeta{ - Labels: controllerSpec.DesiredState.PodTemplate.Labels, + Labels: controllerSpec.Spec.Template.Labels, + }, + DesiredState: api.PodState{ + Manifest: manifest, }, - DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState, } fakeHandler.ValidateRequest(t, makeURL("/pods?namespace=default"), "POST", nil) actualPod, err := client.Codec.Decode([]byte(fakeHandler.RequestBody)) @@ -230,42 +237,42 @@ func TestCreateReplica(t *testing.T) { func TestSynchonize(t *testing.T) { controllerSpec1 := api.ReplicationController{ TypeMeta: api.TypeMeta{APIVersion: testapi.Version()}, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 4, - PodTemplate: api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Containers: []api.Container{ - { - Image: "foo/bar", - }, - }, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{ + "name": "foo", + "type": "production", }, }, - Labels: map[string]string{ - "name": "foo", - "type": "production", + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Image: "foo/bar", + }, + }, }, }, }, } controllerSpec2 := api.ReplicationController{ TypeMeta: api.TypeMeta{APIVersion: testapi.Version()}, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 3, - PodTemplate: api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Containers: []api.Container{ - { - Image: "bar/baz", - }, - }, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{ + "name": "bar", + "type": "production", }, }, - Labels: map[string]string{ - "name": "bar", - "type": "production", + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Image: "bar/baz", + }, + }, }, }, }, diff --git a/pkg/conversion/meta.go b/pkg/conversion/meta.go index 357f099351e..0fe9cdd6484 100644 --- a/pkg/conversion/meta.go +++ b/pkg/conversion/meta.go @@ -116,7 +116,7 @@ func EnforcePtr(obj interface{}) (reflect.Value, error) { if v.Kind() == reflect.Invalid { return reflect.Value{}, fmt.Errorf("expected pointer, but got invalid kind") } - return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type().Name()) + return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type()) } if v.IsNil() { return reflect.Value{}, fmt.Errorf("expected pointer, but got nil") diff --git a/pkg/conversion/scheme.go b/pkg/conversion/scheme.go index b5e73715100..9a3b005c6c2 100644 --- a/pkg/conversion/scheme.go +++ b/pkg/conversion/scheme.go @@ -208,7 +208,7 @@ func (s *Scheme) Convert(in, out interface{}) error { if v, _, err := s.ObjectVersionAndKind(out); err == nil { outVersion = v } - return s.converter.Convert(in, out, 0, s.generateConvertMeta(inVersion, outVersion)) + return s.converter.Convert(in, out, AllowDifferentFieldTypeNames, s.generateConvertMeta(inVersion, outVersion)) } // ConvertToVersion attempts to convert an input object to its matching Kind in another diff --git a/pkg/kubecfg/kubecfg.go b/pkg/kubecfg/kubecfg.go index d303f2e1fbf..687f4939e53 100644 --- a/pkg/kubecfg/kubecfg.go +++ b/pkg/kubecfg/kubecfg.go @@ -134,14 +134,14 @@ func Update(ctx api.Context, name string, client client.Interface, updatePeriod } if len(imageName) != 0 { - controller.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image = imageName + controller.Spec.Template.Spec.Containers[0].Image = imageName controller, err = client.ReplicationControllers(controller.Namespace).Update(controller) if err != nil { return err } } - s := labels.Set(controller.DesiredState.ReplicaSelector).AsSelector() + s := labels.Set(controller.Spec.Selector).AsSelector() podList, err := client.Pods(api.Namespace(ctx)).List(s) if err != nil { @@ -181,7 +181,7 @@ func ResizeController(ctx api.Context, name string, replicas int, client client. if err != nil { return err } - controller.DesiredState.Replicas = replicas + controller.Spec.Replicas = replicas controllerOut, err := client.ReplicationControllers(api.Namespace(ctx)).Update(controller) if err != nil { return err @@ -251,26 +251,25 @@ func RunController(ctx api.Context, image, name string, replicas int, client cli ObjectMeta: api.ObjectMeta{ Name: name, }, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: replicas, - ReplicaSelector: map[string]string{ + Selector: map[string]string{ "name": name, }, - PodTemplate: api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Version: "v1beta2", - Containers: []api.Container{ - { - Name: strings.ToLower(name), - Image: image, - Ports: ports, - }, - }, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{ + "name": name, }, }, - Labels: map[string]string{ - "name": name, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: strings.ToLower(name), + Image: image, + Ports: ports, + }, + }, }, }, }, @@ -328,8 +327,8 @@ func DeleteController(ctx api.Context, name string, client client.Interface) err if err != nil { return err } - if controller.DesiredState.Replicas != 0 { - return fmt.Errorf("controller has non-zero replicas (%d), please stop it first", controller.DesiredState.Replicas) + if controller.Spec.Replicas != 0 { + return fmt.Errorf("controller has non-zero replicas (%d), please stop it first", controller.Spec.Replicas) } return client.ReplicationControllers(api.Namespace(ctx)).Delete(name) } diff --git a/pkg/kubecfg/kubecfg_test.go b/pkg/kubecfg/kubecfg_test.go index 4ca4eb64b13..238c2f57840 100644 --- a/pkg/kubecfg/kubecfg_test.go +++ b/pkg/kubecfg/kubecfg_test.go @@ -74,13 +74,11 @@ func TestUpdateWithNewImage(t *testing.T) { }, }, Ctrl: api.ReplicationController{ - DesiredState: api.ReplicationControllerState{ - PodTemplate: api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Containers: []api.Container{ - {Image: "fooImage:1"}, - }, + Spec: api.ReplicationControllerSpec{ + Template: &api.PodTemplateSpec{ + Spec: api.PodSpec{ + Containers: []api.Container{ + {Image: "fooImage:1"}, }, }, }, @@ -94,7 +92,7 @@ func TestUpdateWithNewImage(t *testing.T) { validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t) newCtrl := api.Scheme.CopyOrDie(&fakeClient.Ctrl).(*api.ReplicationController) - newCtrl.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image = "fooImage:2" + newCtrl.Spec.Template.Spec.Containers[0].Image = "fooImage:2" validateAction(client.FakeAction{Action: "update-controller", Value: newCtrl}, fakeClient.Actions[1], t) validateAction(client.FakeAction{Action: "list-pods"}, fakeClient.Actions[2], t) @@ -115,8 +113,8 @@ func TestRunController(t *testing.T) { } controller := fakeClient.Actions[0].Value.(*api.ReplicationController) if controller.Name != name || - controller.DesiredState.Replicas != replicas || - controller.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image != image { + controller.Spec.Replicas != replicas || + controller.Spec.Template.Spec.Containers[0].Image != image { t.Errorf("Unexpected controller: %#v", controller) } } @@ -136,8 +134,8 @@ func TestRunControllerWithWrongArgs(t *testing.T) { } controller := fakeClient.Actions[0].Value.(*api.ReplicationController) if controller.Name != name || - controller.DesiredState.Replicas != replicas || - controller.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image != image { + controller.Spec.Replicas != replicas || + controller.Spec.Template.Spec.Containers[0].Image != image { t.Errorf("Unexpected controller: %#v", controller) } } @@ -155,8 +153,8 @@ func TestRunControllerWithService(t *testing.T) { } controller := fakeClient.Actions[0].Value.(*api.ReplicationController) if controller.Name != name || - controller.DesiredState.Replicas != replicas || - controller.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image != image { + controller.Spec.Replicas != replicas || + controller.Spec.Template.Spec.Containers[0].Image != image { t.Errorf("Unexpected controller: %#v", controller) } } @@ -174,7 +172,7 @@ func TestStopController(t *testing.T) { } controller := fakeClient.Actions[1].Value.(*api.ReplicationController) if fakeClient.Actions[1].Action != "update-controller" || - controller.DesiredState.Replicas != 0 { + controller.Spec.Replicas != 0 { t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1]) } } @@ -193,7 +191,7 @@ func TestResizeController(t *testing.T) { } controller := fakeClient.Actions[1].Value.(*api.ReplicationController) if fakeClient.Actions[1].Action != "update-controller" || - controller.DesiredState.Replicas != 17 { + controller.Spec.Replicas != 17 { t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1]) } } @@ -221,7 +219,7 @@ func TestCloudCfgDeleteController(t *testing.T) { func TestCloudCfgDeleteControllerWithReplicas(t *testing.T) { fakeClient := client.Fake{ Ctrl: api.ReplicationController{ - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 2, }, }, diff --git a/pkg/kubecfg/parse_test.go b/pkg/kubecfg/parse_test.go index 0f36dd8f6c4..619518770f8 100644 --- a/pkg/kubecfg/parse_test.go +++ b/pkg/kubecfg/parse_test.go @@ -107,18 +107,15 @@ func TestParseController(t *testing.T) { DoParseTest(t, "replicationControllers", &api.ReplicationController{ TypeMeta: api.TypeMeta{APIVersion: "v1beta1", Kind: "ReplicationController"}, ObjectMeta: api.ObjectMeta{Name: "my controller"}, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 9001, - PodTemplate: api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - ID: "My manifest", - Containers: []api.Container{ - {Name: "my container"}, - }, - Volumes: []api.Volume{ - {Name: "volume"}, - }, + Template: &api.PodTemplateSpec{ + Spec: api.PodSpec{ + Containers: []api.Container{ + {Name: "my container"}, + }, + Volumes: []api.Volume{ + {Name: "volume"}, }, }, }, diff --git a/pkg/kubecfg/resource_printer.go b/pkg/kubecfg/resource_printer.go index 64ce0915d72..7c540aa0671 100644 --- a/pkg/kubecfg/resource_printer.go +++ b/pkg/kubecfg/resource_printer.go @@ -185,6 +185,14 @@ func makeImageList(manifest api.ContainerManifest) string { return strings.Join(images, ",") } +func makeImageListPodSpec(spec api.PodSpec) string { + var images []string + for _, container := range spec.Containers { + images = append(images, container.Image) + } + return strings.Join(images, ",") +} + func podHostString(host, ip string) string { if host == "" && ip == "" { return "" @@ -211,8 +219,8 @@ func printPodList(podList *api.PodList, w io.Writer) error { func printReplicationController(controller *api.ReplicationController, w io.Writer) error { _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n", - controller.Name, makeImageList(controller.DesiredState.PodTemplate.DesiredState.Manifest), - labels.Set(controller.DesiredState.ReplicaSelector), controller.DesiredState.Replicas) + controller.Name, makeImageListPodSpec(controller.Spec.Template.Spec), + labels.Set(controller.Spec.Selector), controller.Spec.Replicas) return err } diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index f1e0b823f6b..4e79253fdf5 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -87,9 +87,15 @@ func (d *PodDescriber) Describe(namespace, name string) (string, error) { return "", err } + // TODO: remove me when pods are converted + spec := &api.PodSpec{} + if err := api.Scheme.Convert(&pod.DesiredState.Manifest, spec); err != nil { + glog.Errorf("Unable to convert pod manifest: %v", err) + } + return tabbedString(func(out *tabwriter.Writer) error { fmt.Fprintf(out, "Name:\t%s\n", pod.Name) - fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(pod.DesiredState.Manifest)) + fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(spec)) fmt.Fprintf(out, "Host:\t%s\n", pod.CurrentState.Host+"/"+pod.CurrentState.HostIP) fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(pod.Labels)) fmt.Fprintf(out, "Status:\t%s\n", string(pod.CurrentState.Status)) @@ -127,10 +133,10 @@ func (d *ReplicationControllerDescriber) Describe(namespace, name string) (strin return tabbedString(func(out *tabwriter.Writer) error { fmt.Fprintf(out, "Name:\t%s\n", controller.Name) - fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(controller.DesiredState.PodTemplate.DesiredState.Manifest)) - fmt.Fprintf(out, "Selector:\t%s\n", formatLabels(controller.DesiredState.ReplicaSelector)) + fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&controller.Spec.Template.Spec)) + fmt.Fprintf(out, "Selector:\t%s\n", formatLabels(controller.Spec.Selector)) fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(controller.Labels)) - fmt.Fprintf(out, "Replicas:\t%d current / %d desired\n", controller.CurrentState.Replicas, controller.DesiredState.Replicas) + fmt.Fprintf(out, "Replicas:\t%d current / %d desired\n", controller.Status.Replicas, controller.Spec.Replicas) fmt.Fprintf(out, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed) return nil }) @@ -197,7 +203,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface, // Find the ones that match labelsToMatch. var matchingRCs []api.ReplicationController for _, controller := range rcs.Items { - selector := labels.SelectorFromSet(controller.DesiredState.ReplicaSelector) + selector := labels.SelectorFromSet(controller.Spec.Selector) if selector.Matches(labelsToMatch) { matchingRCs = append(matchingRCs, controller) } @@ -206,7 +212,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface, // Format the matching RC's into strings. var rcStrings []string for _, controller := range matchingRCs { - rcStrings = append(rcStrings, fmt.Sprintf("%s (%d/%d replicas created)", controller.Name, controller.CurrentState.Replicas, controller.DesiredState.Replicas)) + rcStrings = append(rcStrings, fmt.Sprintf("%s (%d/%d replicas created)", controller.Name, controller.Status.Replicas, controller.Spec.Replicas)) } list := strings.Join(rcStrings, ", ") @@ -217,7 +223,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface, } func getPodStatusForReplicationController(c client.PodInterface, controller *api.ReplicationController) (running, waiting, succeeded, failed int, err error) { - rcPods, err := c.List(labels.SelectorFromSet(controller.DesiredState.ReplicaSelector)) + rcPods, err := c.List(labels.SelectorFromSet(controller.Spec.Selector)) if err != nil { return } diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go index 0066f3e845d..7bd216e905d 100644 --- a/pkg/kubectl/kubectl.go +++ b/pkg/kubectl/kubectl.go @@ -139,9 +139,9 @@ func formatLabels(labelMap map[string]string) string { return l } -func makeImageList(manifest api.ContainerManifest) string { +func makeImageList(spec *api.PodSpec) string { var images []string - for _, container := range manifest.Containers { + for _, container := range spec.Containers { images = append(images, container.Image) } return strings.Join(images, ",") diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 9f55f84d629..399a7395c39 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -251,8 +251,14 @@ func podHostString(host, ip string) string { } func printPod(pod *api.Pod, w io.Writer) error { + // TODO: remove me when pods are converted + spec := &api.PodSpec{} + if err := api.Scheme.Convert(&pod.DesiredState.Manifest, spec); err != nil { + glog.Errorf("Unable to convert pod manifest: %v", err) + } + _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", - pod.Name, makeImageList(pod.DesiredState.Manifest), + pod.Name, makeImageList(spec), podHostString(pod.CurrentState.Host, pod.CurrentState.HostIP), labels.Set(pod.Labels), pod.CurrentState.Status) return err @@ -269,8 +275,8 @@ func printPodList(podList *api.PodList, w io.Writer) error { func printReplicationController(controller *api.ReplicationController, w io.Writer) error { _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n", - controller.Name, makeImageList(controller.DesiredState.PodTemplate.DesiredState.Manifest), - labels.Set(controller.DesiredState.ReplicaSelector), controller.DesiredState.Replicas) + controller.Name, makeImageList(&controller.Spec.Template.Spec), + labels.Set(controller.Spec.Selector), controller.Spec.Replicas) return err } diff --git a/pkg/registry/controller/rest.go b/pkg/registry/controller/rest.go index 591b239e5b5..bed047dc0b5 100644 --- a/pkg/registry/controller/rest.go +++ b/pkg/registry/controller/rest.go @@ -64,8 +64,6 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE if len(controller.Name) == 0 { controller.Name = util.NewUUID().String() } - // Pod Manifest ID should be assigned by the pod API - controller.DesiredState.PodTemplate.DesiredState.Manifest.ID = "" if errs := validation.ValidateReplicationController(controller); len(errs) > 0 { return nil, errors.NewInvalid("replicationController", controller.Name, errs) } @@ -158,41 +156,41 @@ func (rs *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVer // TODO(lavalamp): remove watch.Filter, which is broken. Implement consistent way of filtering. // TODO(lavalamp): this watch method needs a test. return watch.Filter(incoming, func(e watch.Event) (watch.Event, bool) { - repController, ok := e.Object.(*api.ReplicationController) + controller, ok := e.Object.(*api.ReplicationController) if !ok { // must be an error event-- pass it on return e, true } - match := label.Matches(labels.Set(repController.Labels)) + match := label.Matches(labels.Set(controller.Labels)) if match { - rs.fillCurrentState(ctx, repController) + rs.fillCurrentState(ctx, controller) } return e, match }), nil } -func (rs *REST) waitForController(ctx api.Context, ctrl *api.ReplicationController) (runtime.Object, error) { +func (rs *REST) waitForController(ctx api.Context, controller *api.ReplicationController) (runtime.Object, error) { for { - pods, err := rs.podLister.ListPods(ctx, labels.Set(ctrl.DesiredState.ReplicaSelector).AsSelector()) + pods, err := rs.podLister.ListPods(ctx, labels.Set(controller.Spec.Selector).AsSelector()) if err != nil { - return ctrl, err + return controller, err } - if len(pods.Items) == ctrl.DesiredState.Replicas { + if len(pods.Items) == controller.Spec.Replicas { break } time.Sleep(rs.pollPeriod) } - return ctrl, nil + return controller, nil } -func (rs *REST) fillCurrentState(ctx api.Context, ctrl *api.ReplicationController) error { +func (rs *REST) fillCurrentState(ctx api.Context, controller *api.ReplicationController) error { if rs.podLister == nil { return nil } - list, err := rs.podLister.ListPods(ctx, labels.Set(ctrl.DesiredState.ReplicaSelector).AsSelector()) + list, err := rs.podLister.ListPods(ctx, labels.Set(controller.Spec.Selector).AsSelector()) if err != nil { return err } - ctrl.CurrentState.Replicas = len(list.Items) + controller.Status.Replicas = len(list.Items) return nil } diff --git a/pkg/registry/controller/rest_test.go b/pkg/registry/controller/rest_test.go index 28e8e832be1..1606d2cc0ea 100644 --- a/pkg/registry/controller/rest_test.go +++ b/pkg/registry/controller/rest_test.go @@ -116,6 +116,15 @@ func TestControllerDecode(t *testing.T) { ObjectMeta: api.ObjectMeta{ Name: "foo", }, + Spec: api.ReplicationControllerSpec{ + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{ + "name": "nginx", + }, + }, + }, + }, } body, err := latest.Codec.Encode(controller) if err != nil { @@ -140,30 +149,30 @@ func TestControllerParsing(t *testing.T) { "name": "nginx", }, }, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 2, - ReplicaSelector: map[string]string{ + Selector: map[string]string{ "name": "nginx", }, - PodTemplate: api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Containers: []api.Container{ - { - Image: "dockerfile/nginx", - Ports: []api.Port{ - { - ContainerPort: 80, - HostPort: 8080, - }, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{ + "name": "nginx", + }, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Image: "dockerfile/nginx", + Ports: []api.Port{ + { + ContainerPort: 80, + HostPort: 8080, }, }, }, }, }, - Labels: map[string]string{ - "name": "nginx", - }, }, }, } @@ -205,9 +214,11 @@ func TestControllerParsing(t *testing.T) { } var validPodTemplate = api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Version: "v1beta1", + Spec: api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{"a": "b"}, + }, + Spec: api.PodSpec{ Containers: []api.Container{ { Name: "test", @@ -216,7 +227,6 @@ var validPodTemplate = api.PodTemplate{ }, }, }, - Labels: map[string]string{"a": "b"}, } func TestCreateController(t *testing.T) { @@ -240,10 +250,10 @@ func TestCreateController(t *testing.T) { } controller := &api.ReplicationController{ ObjectMeta: api.ObjectMeta{Name: "test"}, - DesiredState: api.ReplicationControllerState{ - Replicas: 2, - ReplicaSelector: map[string]string{"a": "b"}, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Replicas: 2, + Selector: map[string]string{"a": "b"}, + Template: &validPodTemplate.Spec, }, } ctx := api.NewDefaultContext() @@ -273,13 +283,13 @@ func TestControllerStorageValidatesCreate(t *testing.T) { failureCases := map[string]api.ReplicationController{ "empty ID": { ObjectMeta: api.ObjectMeta{Name: ""}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: map[string]string{"bar": "baz"}, + Spec: api.ReplicationControllerSpec{ + Selector: map[string]string{"bar": "baz"}, }, }, "empty selector": { - ObjectMeta: api.ObjectMeta{Name: "abc"}, - DesiredState: api.ReplicationControllerState{}, + ObjectMeta: api.ObjectMeta{Name: "abc"}, + Spec: api.ReplicationControllerSpec{}, }, } ctx := api.NewDefaultContext() @@ -304,13 +314,13 @@ func TestControllerStorageValidatesUpdate(t *testing.T) { failureCases := map[string]api.ReplicationController{ "empty ID": { ObjectMeta: api.ObjectMeta{Name: ""}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: map[string]string{"bar": "baz"}, + Spec: api.ReplicationControllerSpec{ + Selector: map[string]string{"bar": "baz"}, }, }, "empty selector": { - ObjectMeta: api.ObjectMeta{Name: "abc"}, - DesiredState: api.ReplicationControllerState{}, + ObjectMeta: api.ObjectMeta{Name: "abc"}, + Spec: api.ReplicationControllerSpec{}, }, } ctx := api.NewDefaultContext() @@ -351,19 +361,19 @@ func TestFillCurrentState(t *testing.T) { podLister: &fakeLister, } controller := api.ReplicationController{ - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: map[string]string{ + Spec: api.ReplicationControllerSpec{ + Selector: map[string]string{ "foo": "bar", }, }, } ctx := api.NewContext() storage.fillCurrentState(ctx, &controller) - if controller.CurrentState.Replicas != 2 { - t.Errorf("expected 2, got: %d", controller.CurrentState.Replicas) + if controller.Status.Replicas != 2 { + t.Errorf("expected 2, got: %d", controller.Status.Replicas) } - if !reflect.DeepEqual(fakeLister.s, labels.Set(controller.DesiredState.ReplicaSelector).AsSelector()) { - t.Errorf("unexpected output: %#v %#v", labels.Set(controller.DesiredState.ReplicaSelector).AsSelector(), fakeLister.s) + if !reflect.DeepEqual(fakeLister.s, labels.Set(controller.Spec.Selector).AsSelector()) { + t.Errorf("unexpected output: %#v %#v", labels.Set(controller.Spec.Selector).AsSelector(), fakeLister.s) } } diff --git a/pkg/registry/etcd/etcd_test.go b/pkg/registry/etcd/etcd_test.go index f1367003a18..3836b7e0f6e 100644 --- a/pkg/registry/etcd/etcd_test.go +++ b/pkg/registry/etcd/etcd_test.go @@ -960,7 +960,7 @@ func TestEtcdUpdateController(t *testing.T) { registry := NewTestEtcdRegistry(fakeClient) err := registry.UpdateController(ctx, &api.ReplicationController{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: strconv.FormatUint(resp.Node.ModifiedIndex, 10)}, - DesiredState: api.ReplicationControllerState{ + Spec: api.ReplicationControllerSpec{ Replicas: 2, }, }) @@ -969,7 +969,7 @@ func TestEtcdUpdateController(t *testing.T) { } ctrl, err := registry.GetController(ctx, "foo") - if ctrl.DesiredState.Replicas != 2 { + if ctrl.Spec.Replicas != 2 { t.Errorf("Unexpected controller: %#v", ctrl) } }