Commit 0348a674 authored by Daniel Smith's avatar Daniel Smith

Merge pull request #2195 from smarterclayton/prepare_pod_template_v1beta3

Allow an internal pod template reference or object
parents ab3bf094 8a590004
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
"id": "my-pod-1", "id": "my-pod-1",
"labels": { "labels": {
"name": "testRun", "name": "testRun",
"replicationController": "testRun" "replicationcontroller": "testRun"
}, },
"desiredState": { "desiredState": {
"manifest": { "manifest": {
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
"id": "my-pod-2", "id": "my-pod-2",
"labels": { "labels": {
"name": "testRun", "name": "testRun",
"replicationController": "testRun" "replicationcontroller": "testRun"
}, },
"desiredState": { "desiredState": {
"manifest": { "manifest": {
......
...@@ -19,7 +19,6 @@ limitations under the License. ...@@ -19,7 +19,6 @@ limitations under the License.
package main package main
import ( import (
"encoding/json"
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
...@@ -237,24 +236,24 @@ func runReplicationControllerTest(c *client.Client) { ...@@ -237,24 +236,24 @@ func runReplicationControllerTest(c *client.Client) {
if err != nil { if err != nil {
glog.Fatalf("Unexpected error: %#v", err) glog.Fatalf("Unexpected error: %#v", err)
} }
var controllerRequest api.ReplicationController var controller api.ReplicationController
if err := json.Unmarshal(data, &controllerRequest); err != nil { if err := api.Scheme.DecodeInto(data, &controller); err != nil {
glog.Fatalf("Unexpected error: %#v", err) glog.Fatalf("Unexpected error: %#v", err)
} }
glog.Infof("Creating replication controllers") 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.Fatalf("Unexpected error: %#v", err)
} }
glog.Infof("Done creating replication controllers") glog.Infof("Done creating replication controllers")
// Give the controllers some time to actually create the pods // 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) glog.Fatalf("FAILED: pods never created %v", err)
} }
// wait for minions to indicate they have info about the desired pods // 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 { if err != nil {
glog.Fatalf("FAILED: unable to get pods to list: %v", err) glog.Fatalf("FAILED: unable to get pods to list: %v", err)
} }
......
...@@ -36,12 +36,18 @@ func validateObject(obj runtime.Object) (errors []error) { ...@@ -36,12 +36,18 @@ func validateObject(obj runtime.Object) (errors []error) {
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
switch t := obj.(type) { switch t := obj.(type) {
case *api.ReplicationController: 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: case *api.ReplicationControllerList:
for i := range t.Items { for i := range t.Items {
errors = append(errors, validateObject(&t.Items[i])...) errors = append(errors, validateObject(&t.Items[i])...)
} }
case *api.Service: case *api.Service:
if t.Namespace == "" {
t.Namespace = api.NamespaceDefault
}
api.ValidNamespace(ctx, &t.ObjectMeta) api.ValidNamespace(ctx, &t.ObjectMeta)
errors = validation.ValidateService(t, registrytest.NewServiceRegistry(), api.NewDefaultContext()) errors = validation.ValidateService(t, registrytest.NewServiceRegistry(), api.NewDefaultContext())
case *api.ServiceList: case *api.ServiceList:
...@@ -49,8 +55,11 @@ func validateObject(obj runtime.Object) (errors []error) { ...@@ -49,8 +55,11 @@ func validateObject(obj runtime.Object) (errors []error) {
errors = append(errors, validateObject(&t.Items[i])...) errors = append(errors, validateObject(&t.Items[i])...)
} }
case *api.Pod: case *api.Pod:
if t.Namespace == "" {
t.Namespace = api.NamespaceDefault
}
api.ValidNamespace(ctx, &t.ObjectMeta) api.ValidNamespace(ctx, &t.ObjectMeta)
errors = validation.ValidateManifest(&t.DesiredState.Manifest) errors = validation.ValidatePod(t)
case *api.PodList: case *api.PodList:
for i := range t.Items { for i := range t.Items {
errors = append(errors, validateObject(&t.Items[i])...) errors = append(errors, validateObject(&t.Items[i])...)
......
kind: Pod kind: Pod
apiVersion: v1beta1 apiVersion: v1beta1
id: pod-with-healthcheck
desiredState: desiredState:
manifest: manifest:
version: v1beta1 version: v1beta1
......
...@@ -22,4 +22,4 @@ desiredState: ...@@ -22,4 +22,4 @@ desiredState:
# Important: these labels need to match the selector above # Important: these labels need to match the selector above
# The api server enforces this constraint. # The api server enforces this constraint.
labels: labels:
- name: nginx name: nginx
...@@ -59,6 +59,8 @@ func init() { ...@@ -59,6 +59,8 @@ func init() {
} }
return nil return nil
}, },
// ContainerManifestList
func(in *ContainerManifestList, out *BoundPods, s conversion.Scope) error { func(in *ContainerManifestList, out *BoundPods, s conversion.Scope) error {
if err := s.Convert(&in.Items, &out.Items, 0); err != nil { if err := s.Convert(&in.Items, &out.Items, 0); err != nil {
return err return err
...@@ -90,5 +92,32 @@ func init() { ...@@ -90,5 +92,32 @@ func init() {
out.CreationTimestamp = in.CreationTimestamp out.CreationTimestamp = in.CreationTimestamp
return nil 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
},
) )
} }
...@@ -140,7 +140,7 @@ func (list ValidationErrorList) Prefix(prefix string) ValidationErrorList { ...@@ -140,7 +140,7 @@ func (list ValidationErrorList) Prefix(prefix string) ValidationErrorList {
} }
list[i] = err list[i] = err
} else { } else {
glog.Warningf("ValidationErrorList holds non-ValidationError: %T", list) glog.Warningf("Programmer error: ValidationErrorList holds non-ValidationError: %#v", list[i])
} }
} }
return list return list
......
...@@ -75,6 +75,23 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( ...@@ -75,6 +75,23 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
statuses := []internal.PodCondition{internal.PodPending, internal.PodRunning, internal.PodFailed} statuses := []internal.PodCondition{internal.PodPending, internal.PodRunning, internal.PodFailed}
*j = statuses[c.Rand.Intn(len(statuses))] *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) { func(intstr *util.IntOrString, c fuzz.Continue) {
// util.IntOrString will panic if its kind is set wrong. // util.IntOrString will panic if its kind is set wrong.
if c.RandBool() { if c.RandBool() {
...@@ -127,6 +144,7 @@ func TestInternalRoundTrip(t *testing.T) { ...@@ -127,6 +144,7 @@ func TestInternalRoundTrip(t *testing.T) {
if err := internal.Scheme.Convert(obj, newer); err != nil { if err := internal.Scheme.Convert(obj, newer); err != nil {
t.Errorf("unable to convert %#v to %#v: %v", obj, newer, err) t.Errorf("unable to convert %#v to %#v: %v", obj, newer, err)
continue
} }
actual, err := internal.Scheme.New("", k) actual, err := internal.Scheme.New("", k)
...@@ -137,6 +155,7 @@ func TestInternalRoundTrip(t *testing.T) { ...@@ -137,6 +155,7 @@ func TestInternalRoundTrip(t *testing.T) {
if err := internal.Scheme.Convert(newer, actual); err != nil { if err := internal.Scheme.Convert(newer, actual); err != nil {
t.Errorf("unable to convert %#v to %#v: %v", newer, actual, err) t.Errorf("unable to convert %#v to %#v: %v", newer, actual, err)
continue
} }
if !reflect.DeepEqual(obj, actual) { if !reflect.DeepEqual(obj, actual) {
......
...@@ -90,6 +90,23 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( ...@@ -90,6 +90,23 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
statuses := []api.PodCondition{api.PodPending, api.PodRunning, api.PodFailed} statuses := []api.PodCondition{api.PodPending, api.PodRunning, api.PodFailed}
*j = statuses[c.Rand.Intn(len(statuses))] *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) { func(intstr *util.IntOrString, c fuzz.Continue) {
// util.IntOrString will panic if its kind is set wrong. // util.IntOrString will panic if its kind is set wrong.
if c.RandBool() { if c.RandBool() {
...@@ -176,18 +193,21 @@ func TestSpecificKind(t *testing.T) { ...@@ -176,18 +193,21 @@ func TestSpecificKind(t *testing.T) {
api.Scheme.Log(nil) 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("") { for kind := range api.Scheme.KnownTypes("") {
if nonRoundTrippableTypes.Has(kind) {
continue
}
// Try a few times, since runTest uses random values. // Try a few times, since runTest uses random values.
for i := 0; i < *fuzzIters; i++ { for i := 0; i < *fuzzIters; i++ {
item, err := api.Scheme.New("", kind) item, err := api.Scheme.New("", kind)
if err != nil { if err != nil {
t.Errorf("Couldn't make a %v? %v", kind, err) t.Fatalf("Couldn't make a %v? %v", kind, err)
continue
} }
if _, err := meta.Accessor(item); err != nil { if _, err := meta.Accessor(item); err != nil {
t.Logf("%s is not a TypeMeta and cannot be round tripped: %v", kind, err) t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err)
continue
} }
runTest(t, v1beta1.Codec, item) runTest(t, v1beta1.Codec, item)
runTest(t, v1beta2.Codec, item) runTest(t, v1beta2.Codec, item)
......
...@@ -442,6 +442,15 @@ type PodList struct { ...@@ -442,6 +442,15 @@ type PodList struct {
Items []Pod `json:"items" yaml:"items"` 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). // Pod is a collection of containers, used as either input (create, update) or as output (list, get).
type Pod struct { type Pod struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline" yaml:",inline"`
...@@ -453,19 +462,59 @@ type Pod struct { ...@@ -453,19 +462,59 @@ type Pod struct {
NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"` 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). // PodTemplateSpec describes the data a pod should have when created from a template
type ReplicationControllerState struct { type PodTemplateSpec struct {
Replicas int `json:"replicas" yaml:"replicas"` // Metadata of the pods created from this template.
ReplicaSelector map[string]string `json:"replicaSelector,omitempty" yaml:"replicaSelector,omitempty"` ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
PodTemplate PodTemplate `json:"podTemplate,omitempty" yaml:"podTemplate,omitempty"`
// Spec defines the behavior of a pod.
Spec PodSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
} }
// ReplicationControllerList is a collection of replication controllers. // PodTemplate describes a template for creating copies of a predefined pod.
type ReplicationControllerList struct { 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"` TypeMeta `json:",inline" yaml:",inline"`
ListMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` ListMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Items []ReplicationController `json:"items" yaml:"items"` 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. // ReplicationController represents the configuration of a replication controller.
...@@ -473,14 +522,20 @@ type ReplicationController struct { ...@@ -473,14 +522,20 @@ type ReplicationController struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline" yaml:",inline"`
ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
DesiredState ReplicationControllerState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"` // Spec defines the desired behavior of this replication controller.
CurrentState ReplicationControllerState `json:"currentState,omitempty" yaml:"currentState,omitempty"` 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"`
} }
// PodTemplate holds the information used for creating pods. // ReplicationControllerList is a collection of replication controllers.
type PodTemplate struct { type ReplicationControllerList struct {
DesiredState PodState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"` TypeMeta `json:",inline" yaml:",inline"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` ListMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Items []ReplicationController `json:"items" yaml:"items"`
} }
// ServiceList holds a list of services. // ServiceList holds a list of services.
...@@ -872,15 +927,6 @@ type ContainerManifestList struct { ...@@ -872,15 +927,6 @@ type ContainerManifestList struct {
Items []ContainerManifest `json:"items" yaml:"items,omitempty"` 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 // 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 // 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. // execute a pod, whereas a BoundPod is the specification that would be run on a server.
......
...@@ -256,12 +256,10 @@ func init() { ...@@ -256,12 +256,10 @@ func init() {
return err return err
} }
if err := s.Convert(&in.DesiredState, &out.DesiredState, 0); err != nil { if err := s.Convert(&in.Spec, &out.DesiredState, 0); err != nil {
return err
}
if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil {
return err return err
} }
out.CurrentState.Replicas = in.Status.Replicas
return nil return nil
}, },
func(in *ReplicationController, out *newer.ReplicationController, s conversion.Scope) error { func(in *ReplicationController, out *newer.ReplicationController, s conversion.Scope) error {
...@@ -275,10 +273,80 @@ func init() { ...@@ -275,10 +273,80 @@ func init() {
return err 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 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 err
} }
return nil return nil
......
...@@ -749,6 +749,8 @@ type PodSpec struct { ...@@ -749,6 +749,8 @@ type PodSpec struct {
Volumes []Volume `json:"volumes" yaml:"volumes"` Volumes []Volume `json:"volumes" yaml:"volumes"`
Containers []Container `json:"containers" yaml:"containers"` Containers []Container `json:"containers" yaml:"containers"`
RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"` 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 // BoundPod is a collection of containers that should be run on a host. A BoundPod
......
...@@ -186,12 +186,10 @@ func init() { ...@@ -186,12 +186,10 @@ func init() {
return err return err
} }
if err := s.Convert(&in.DesiredState, &out.DesiredState, 0); err != nil { if err := s.Convert(&in.Spec, &out.DesiredState, 0); err != nil {
return err
}
if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil {
return err return err
} }
out.CurrentState.Replicas = in.Status.Replicas
return nil return nil
}, },
func(in *ReplicationController, out *newer.ReplicationController, s conversion.Scope) error { func(in *ReplicationController, out *newer.ReplicationController, s conversion.Scope) error {
...@@ -205,10 +203,80 @@ func init() { ...@@ -205,10 +203,80 @@ func init() {
return err 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 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 err
} }
return nil return nil
......
...@@ -750,6 +750,8 @@ type PodSpec struct { ...@@ -750,6 +750,8 @@ type PodSpec struct {
Volumes []Volume `json:"volumes" yaml:"volumes"` Volumes []Volume `json:"volumes" yaml:"volumes"`
Containers []Container `json:"containers" yaml:"containers"` Containers []Container `json:"containers" yaml:"containers"`
RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"` 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 // BoundPod is a collection of containers that should be run on a host. A BoundPod
......
...@@ -167,7 +167,7 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes util.StringSet) errs ...@@ -167,7 +167,7 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes util.StringSet) errs
if len(mnt.Name) == 0 { if len(mnt.Name) == 0 {
mErrs = append(mErrs, errs.NewFieldRequired("name", mnt.Name)) mErrs = append(mErrs, errs.NewFieldRequired("name", mnt.Name))
} else if !volumes.Has(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 { if len(mnt.MountPath) == 0 {
mErrs = append(mErrs, errs.NewFieldRequired("mountPath", mnt.MountPath)) mErrs = append(mErrs, errs.NewFieldRequired("mountPath", mnt.MountPath))
...@@ -293,6 +293,7 @@ var supportedManifestVersions = util.NewStringSet("v1beta1", "v1beta2") ...@@ -293,6 +293,7 @@ var supportedManifestVersions = util.NewStringSet("v1beta1", "v1beta2")
// This includes checking formatting and uniqueness. It also canonicalizes the // This includes checking formatting and uniqueness. It also canonicalizes the
// structure by setting default values and implementing any backwards-compatibility // structure by setting default values and implementing any backwards-compatibility
// tricks. // tricks.
// TODO: replaced by ValidatePodSpec
func ValidateManifest(manifest *api.ContainerManifest) errs.ValidationErrorList { func ValidateManifest(manifest *api.ContainerManifest) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{} allErrs := errs.ValidationErrorList{}
...@@ -348,6 +349,21 @@ func ValidatePod(pod *api.Pod) errs.ValidationErrorList { ...@@ -348,6 +349,21 @@ func ValidatePod(pod *api.Pod) errs.ValidationErrorList {
return allErrs 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 { func validateLabels(labels map[string]string) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{} allErrs := errs.ValidationErrorList{}
for k := range labels { for k := range labels {
...@@ -435,30 +451,44 @@ func ValidateReplicationController(controller *api.ReplicationController) errs.V ...@@ -435,30 +451,44 @@ func ValidateReplicationController(controller *api.ReplicationController) errs.V
if !util.IsDNSSubdomain(controller.Namespace) { if !util.IsDNSSubdomain(controller.Namespace) {
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", 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)...) allErrs = append(allErrs, validateLabels(controller.Labels)...)
return allErrs return allErrs
} }
// ValidateReplicationControllerState tests if required fields in the replication controller state are set. // ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
func ValidateReplicationControllerState(state *api.ReplicationControllerState) errs.ValidationErrorList { func ValidateReplicationControllerSpec(spec *api.ReplicationControllerSpec) errs.ValidationErrorList {
allErrs := 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() if spec.Replicas < 0 {
labels := labels.Set(state.PodTemplate.Labels) allErrs = append(allErrs, errs.NewFieldInvalid("replicas", spec.Replicas))
if !selector.Matches(labels) {
allErrs = append(allErrs, errs.NewFieldInvalid("podTemplate.labels", state.PodTemplate))
} }
allErrs = append(allErrs, validateLabels(labels)...)
if state.Replicas < 0 { if spec.Template == nil {
allErrs = append(allErrs, errs.NewFieldInvalid("replicas", state.Replicas)) 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 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 { func ValidateReadOnlyPersistentDisks(volumes []api.Volume) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{} allErrs := errs.ValidationErrorList{}
for _, vol := range volumes { for _, vol := range volumes {
......
...@@ -862,43 +862,41 @@ func TestValidateService(t *testing.T) { ...@@ -862,43 +862,41 @@ func TestValidateService(t *testing.T) {
func TestValidateReplicationController(t *testing.T) { func TestValidateReplicationController(t *testing.T) {
validSelector := map[string]string{"a": "b"} validSelector := map[string]string{"a": "b"}
validPodTemplate := api.PodTemplate{ validPodTemplate := api.PodTemplate{
DesiredState: api.PodState{ Spec: api.PodTemplateSpec{
Manifest: api.ContainerManifest{ ObjectMeta: api.ObjectMeta{
Version: "v1beta1", Labels: validSelector,
}, },
}, },
Labels: validSelector,
} }
invalidVolumePodTemplate := api.PodTemplate{ invalidVolumePodTemplate := api.PodTemplate{
DesiredState: api.PodState{ Spec: api.PodTemplateSpec{
Manifest: api.ContainerManifest{ Spec: api.PodSpec{
Version: "v1beta1",
Volumes: []api.Volume{{Name: "gcepd", Source: &api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}}, Volumes: []api.Volume{{Name: "gcepd", Source: &api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}},
}, },
}, },
} }
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
invalidPodTemplate := api.PodTemplate{ invalidPodTemplate := api.PodTemplate{
DesiredState: api.PodState{ Spec: api.PodTemplateSpec{
Manifest: api.ContainerManifest{ Spec: api.PodSpec{},
Version: "v1beta1", ObjectMeta: api.ObjectMeta{
Labels: invalidSelector,
}, },
}, },
Labels: invalidSelector,
} }
successCases := []api.ReplicationController{ successCases := []api.ReplicationController{
{ {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
{ {
ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
} }
...@@ -911,49 +909,49 @@ func TestValidateReplicationController(t *testing.T) { ...@@ -911,49 +909,49 @@ func TestValidateReplicationController(t *testing.T) {
errorCases := map[string]api.ReplicationController{ errorCases := map[string]api.ReplicationController{
"zero-length ID": { "zero-length ID": {
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"missing-namespace": { "missing-namespace": {
ObjectMeta: api.ObjectMeta{Name: "abc-123"}, ObjectMeta: api.ObjectMeta{Name: "abc-123"},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"empty selector": { "empty selector": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"selector_doesnt_match": { "selector_doesnt_match": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: map[string]string{"foo": "bar"}, Selector: map[string]string{"foo": "bar"},
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"invalid manifest": { "invalid manifest": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
}, },
}, },
"read-write presistent disk": { "read-write presistent disk": {
ObjectMeta: api.ObjectMeta{Name: "abc"}, ObjectMeta: api.ObjectMeta{Name: "abc"},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: invalidVolumePodTemplate, Template: &invalidVolumePodTemplate.Spec,
}, },
}, },
"negative_replicas": { "negative_replicas": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: -1, Replicas: -1,
ReplicaSelector: validSelector, Selector: validSelector,
}, },
}, },
"invalid_label": { "invalid_label": {
...@@ -964,9 +962,9 @@ func TestValidateReplicationController(t *testing.T) { ...@@ -964,9 +962,9 @@ func TestValidateReplicationController(t *testing.T) {
"NoUppercaseOrSpecialCharsLike=Equals": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar",
}, },
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"invalid_label 2": { "invalid_label 2": {
...@@ -977,8 +975,8 @@ func TestValidateReplicationController(t *testing.T) { ...@@ -977,8 +975,8 @@ func TestValidateReplicationController(t *testing.T) {
"NoUppercaseOrSpecialCharsLike=Equals": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar",
}, },
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
PodTemplate: invalidPodTemplate, Template: &invalidPodTemplate.Spec,
}, },
}, },
} }
...@@ -989,13 +987,14 @@ func TestValidateReplicationController(t *testing.T) { ...@@ -989,13 +987,14 @@ func TestValidateReplicationController(t *testing.T) {
} }
for i := range errs { for i := range errs {
field := errs[i].(errors.ValidationError).Field field := errs[i].(errors.ValidationError).Field
if !strings.HasPrefix(field, "desiredState.podTemplate.") && if !strings.HasPrefix(field, "spec.template.") &&
field != "name" && field != "name" &&
field != "namespace" && field != "namespace" &&
field != "desiredState.replicaSelector" && field != "spec.selector" &&
field != "spec.template" &&
field != "GCEPersistentDisk.ReadOnly" && field != "GCEPersistentDisk.ReadOnly" &&
field != "desiredState.replicas" && field != "spec.replicas" &&
field != "desiredState.label" && field != "spec.template.label" &&
field != "label" { field != "label" {
t.Errorf("%s: missing prefix for: %v", k, errs[i]) t.Errorf("%s: missing prefix for: %v", k, errs[i])
} }
......
...@@ -89,7 +89,7 @@ func (c *testClient) Validate(t *testing.T, received runtime.Object, err error) ...@@ -89,7 +89,7 @@ func (c *testClient) Validate(t *testing.T, received runtime.Object, err error)
c.ValidateCommon(t, err) c.ValidateCommon(t, err)
if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) { 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) { ...@@ -97,7 +97,7 @@ func (c *testClient) ValidateRaw(t *testing.T, received []byte, err error) {
c.ValidateCommon(t, err) c.ValidateCommon(t, err)
if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) { 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) { ...@@ -309,8 +309,9 @@ func TestListControllers(t *testing.T) {
"name": "baz", "name": "baz",
}, },
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 2, Replicas: 2,
Template: &api.PodTemplateSpec{},
}, },
}, },
}, },
...@@ -335,8 +336,9 @@ func TestGetController(t *testing.T) { ...@@ -335,8 +336,9 @@ func TestGetController(t *testing.T) {
"name": "baz", "name": "baz",
}, },
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 2, Replicas: 2,
Template: &api.PodTemplateSpec{},
}, },
}, },
}, },
...@@ -361,8 +363,9 @@ func TestUpdateController(t *testing.T) { ...@@ -361,8 +363,9 @@ func TestUpdateController(t *testing.T) {
"name": "baz", "name": "baz",
}, },
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 2, Replicas: 2,
Template: &api.PodTemplateSpec{},
}, },
}, },
}, },
...@@ -396,8 +399,9 @@ func TestCreateController(t *testing.T) { ...@@ -396,8 +399,9 @@ func TestCreateController(t *testing.T) {
"name": "baz", "name": "baz",
}, },
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 2, Replicas: 2,
Template: &api.PodTemplateSpec{},
}, },
}, },
}, },
......
...@@ -26,10 +26,10 @@ import ( ...@@ -26,10 +26,10 @@ import (
// for a controller's ReplicaSelector equals the Replicas count. // for a controller's ReplicaSelector equals the Replicas count.
func (c *Client) ControllerHasDesiredReplicas(controller api.ReplicationController) wait.ConditionFunc { func (c *Client) ControllerHasDesiredReplicas(controller api.ReplicationController) wait.ConditionFunc {
return func() (bool, error) { 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 { if err != nil {
return false, err return false, err
} }
return len(pods.Items) == controller.DesiredState.Replicas, nil return len(pods.Items) == controller.Spec.Replicas, nil
} }
} }
...@@ -35,14 +35,14 @@ type ReplicationManager struct { ...@@ -35,14 +35,14 @@ type ReplicationManager struct {
syncTime <-chan time.Time syncTime <-chan time.Time
// To allow injection of syncReplicationController for testing. // 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 // PodControlInterface is an interface that knows how to add or delete pods
// created as an interface to allow testing. // created as an interface to allow testing.
type PodControlInterface interface { type PodControlInterface interface {
// createReplica creates new replicated pods according to the spec. // 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 deletes the pod identified by podID.
deletePod(namespace string, podID string) error deletePod(namespace string, podID string) error
} }
...@@ -52,16 +52,23 @@ type RealPodControl struct { ...@@ -52,16 +52,23 @@ type RealPodControl struct {
kubeClient client.Interface 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) desiredLabels := make(labels.Set)
for k, v := range controllerSpec.DesiredState.PodTemplate.Labels { for k, v := range controller.Spec.Template.Labels {
desiredLabels[k] = v desiredLabels[k] = v
} }
pod := &api.Pod{ pod := &api.Pod{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Labels: desiredLabels, 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 { if _, err := r.kubeClient.Pods(namespace).Create(pod); err != nil {
glog.Errorf("Unable to create pod replica: %v", err) glog.Errorf("Unable to create pod replica: %v", err)
...@@ -144,14 +151,14 @@ func (rm *ReplicationManager) filterActivePods(pods []api.Pod) []api.Pod { ...@@ -144,14 +151,14 @@ func (rm *ReplicationManager) filterActivePods(pods []api.Pod) []api.Pod {
return result return result
} }
func (rm *ReplicationManager) syncReplicationController(controllerSpec api.ReplicationController) error { func (rm *ReplicationManager) syncReplicationController(controller api.ReplicationController) error {
s := labels.Set(controllerSpec.DesiredState.ReplicaSelector).AsSelector() s := labels.Set(controller.Spec.Selector).AsSelector()
podList, err := rm.kubeClient.Pods(controllerSpec.Namespace).List(s) podList, err := rm.kubeClient.Pods(controller.Namespace).List(s)
if err != nil { if err != nil {
return err return err
} }
filteredList := rm.filterActivePods(podList.Items) filteredList := rm.filterActivePods(podList.Items)
diff := len(filteredList) - controllerSpec.DesiredState.Replicas diff := len(filteredList) - controller.Spec.Replicas
if diff < 0 { if diff < 0 {
diff *= -1 diff *= -1
wait := sync.WaitGroup{} wait := sync.WaitGroup{}
...@@ -160,7 +167,7 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli ...@@ -160,7 +167,7 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli
for i := 0; i < diff; i++ { for i := 0; i < diff; i++ {
go func() { go func() {
defer wait.Done() defer wait.Done()
rm.podControl.createReplica(controllerSpec.Namespace, controllerSpec) rm.podControl.createReplica(controller.Namespace, controller)
}() }()
} }
wait.Wait() wait.Wait()
...@@ -171,7 +178,7 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli ...@@ -171,7 +178,7 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli
for i := 0; i < diff; i++ { for i := 0; i < diff; i++ {
go func(ix int) { go func(ix int) {
defer wait.Done() defer wait.Done()
rm.podControl.deletePod(controllerSpec.Namespace, filteredList[ix].Name) rm.podControl.deletePod(controller.Namespace, filteredList[ix].Name)
}(i) }(i)
} }
wait.Wait() wait.Wait()
...@@ -182,20 +189,20 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli ...@@ -182,20 +189,20 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli
func (rm *ReplicationManager) synchronize() { func (rm *ReplicationManager) synchronize() {
// TODO: remove this method completely and rely on the watch. // TODO: remove this method completely and rely on the watch.
// Add resource version tracking to watch to make this work. // 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()) list, err := rm.kubeClient.ReplicationControllers(api.NamespaceAll).List(labels.Everything())
if err != nil { if err != nil {
glog.Errorf("Synchronization error: %v (%#v)", err, err) glog.Errorf("Synchronization error: %v (%#v)", err, err)
return return
} }
controllerSpecs = list.Items controllers = list.Items
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(len(controllerSpecs)) wg.Add(len(controllers))
for ix := range controllerSpecs { for ix := range controllers {
go func(ix int) { go func(ix int) {
defer wg.Done() defer wg.Done()
glog.V(4).Infof("periodic sync of %v", controllerSpecs[ix].Name) glog.V(4).Infof("periodic sync of %v", controllers[ix].Name)
err := rm.syncHandler(controllerSpecs[ix]) err := rm.syncHandler(controllers[ix])
if err != nil { if err != nil {
glog.Errorf("Error synchronizing: %#v", err) glog.Errorf("Error synchronizing: %#v", err)
} }
......
...@@ -62,21 +62,21 @@ func (f *FakePodControl) deletePod(namespace string, podName string) error { ...@@ -62,21 +62,21 @@ func (f *FakePodControl) deletePod(namespace string, podName string) error {
func newReplicationController(replicas int) api.ReplicationController { func newReplicationController(replicas int) api.ReplicationController {
return api.ReplicationController{ return api.ReplicationController{
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: replicas, Replicas: replicas,
PodTemplate: api.PodTemplate{ Template: &api.PodTemplateSpec{
DesiredState: api.PodState{ ObjectMeta: api.ObjectMeta{
Manifest: api.ContainerManifest{ Labels: map[string]string{
Containers: []api.Container{ "name": "foo",
{ "type": "production",
Image: "foo/bar",
},
},
}, },
}, },
Labels: map[string]string{ Spec: api.PodSpec{
"name": "foo", Containers: []api.Container{
"type": "production", {
Image: "foo/bar",
},
},
}, },
}, },
}, },
...@@ -188,21 +188,21 @@ func TestCreateReplica(t *testing.T) { ...@@ -188,21 +188,21 @@ func TestCreateReplica(t *testing.T) {
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "test", Name: "test",
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
PodTemplate: api.PodTemplate{ Template: &api.PodTemplateSpec{
DesiredState: api.PodState{ ObjectMeta: api.ObjectMeta{
Manifest: api.ContainerManifest{ Labels: map[string]string{
Containers: []api.Container{ "name": "foo",
{ "type": "production",
Image: "foo/bar", "replicationController": "test",
},
},
}, },
}, },
Labels: map[string]string{ Spec: api.PodSpec{
"name": "foo", Containers: []api.Container{
"type": "production", {
"replicationController": "test", Image: "foo/bar",
},
},
}, },
}, },
}, },
...@@ -210,11 +210,18 @@ func TestCreateReplica(t *testing.T) { ...@@ -210,11 +210,18 @@ func TestCreateReplica(t *testing.T) {
podControl.createReplica(ns, controllerSpec) 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{ expectedPod := api.Pod{
ObjectMeta: api.ObjectMeta{ 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) fakeHandler.ValidateRequest(t, makeURL("/pods?namespace=default"), "POST", nil)
actualPod, err := client.Codec.Decode([]byte(fakeHandler.RequestBody)) actualPod, err := client.Codec.Decode([]byte(fakeHandler.RequestBody))
...@@ -230,42 +237,42 @@ func TestCreateReplica(t *testing.T) { ...@@ -230,42 +237,42 @@ func TestCreateReplica(t *testing.T) {
func TestSynchonize(t *testing.T) { func TestSynchonize(t *testing.T) {
controllerSpec1 := api.ReplicationController{ controllerSpec1 := api.ReplicationController{
TypeMeta: api.TypeMeta{APIVersion: testapi.Version()}, TypeMeta: api.TypeMeta{APIVersion: testapi.Version()},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 4, Replicas: 4,
PodTemplate: api.PodTemplate{ Template: &api.PodTemplateSpec{
DesiredState: api.PodState{ ObjectMeta: api.ObjectMeta{
Manifest: api.ContainerManifest{ Labels: map[string]string{
Containers: []api.Container{ "name": "foo",
{ "type": "production",
Image: "foo/bar",
},
},
}, },
}, },
Labels: map[string]string{ Spec: api.PodSpec{
"name": "foo", Containers: []api.Container{
"type": "production", {
Image: "foo/bar",
},
},
}, },
}, },
}, },
} }
controllerSpec2 := api.ReplicationController{ controllerSpec2 := api.ReplicationController{
TypeMeta: api.TypeMeta{APIVersion: testapi.Version()}, TypeMeta: api.TypeMeta{APIVersion: testapi.Version()},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 3, Replicas: 3,
PodTemplate: api.PodTemplate{ Template: &api.PodTemplateSpec{
DesiredState: api.PodState{ ObjectMeta: api.ObjectMeta{
Manifest: api.ContainerManifest{ Labels: map[string]string{
Containers: []api.Container{ "name": "bar",
{ "type": "production",
Image: "bar/baz",
},
},
}, },
}, },
Labels: map[string]string{ Spec: api.PodSpec{
"name": "bar", Containers: []api.Container{
"type": "production", {
Image: "bar/baz",
},
},
}, },
}, },
}, },
......
...@@ -116,7 +116,7 @@ func EnforcePtr(obj interface{}) (reflect.Value, error) { ...@@ -116,7 +116,7 @@ func EnforcePtr(obj interface{}) (reflect.Value, error) {
if v.Kind() == reflect.Invalid { 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 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() { if v.IsNil() {
return reflect.Value{}, fmt.Errorf("expected pointer, but got nil") return reflect.Value{}, fmt.Errorf("expected pointer, but got nil")
......
...@@ -208,7 +208,7 @@ func (s *Scheme) Convert(in, out interface{}) error { ...@@ -208,7 +208,7 @@ func (s *Scheme) Convert(in, out interface{}) error {
if v, _, err := s.ObjectVersionAndKind(out); err == nil { if v, _, err := s.ObjectVersionAndKind(out); err == nil {
outVersion = v 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 // ConvertToVersion attempts to convert an input object to its matching Kind in another
......
...@@ -134,14 +134,14 @@ func Update(ctx api.Context, name string, client client.Interface, updatePeriod ...@@ -134,14 +134,14 @@ func Update(ctx api.Context, name string, client client.Interface, updatePeriod
} }
if len(imageName) != 0 { 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) controller, err = client.ReplicationControllers(controller.Namespace).Update(controller)
if err != nil { if err != nil {
return err 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) podList, err := client.Pods(api.Namespace(ctx)).List(s)
if err != nil { if err != nil {
...@@ -181,7 +181,7 @@ func ResizeController(ctx api.Context, name string, replicas int, client client. ...@@ -181,7 +181,7 @@ func ResizeController(ctx api.Context, name string, replicas int, client client.
if err != nil { if err != nil {
return err return err
} }
controller.DesiredState.Replicas = replicas controller.Spec.Replicas = replicas
controllerOut, err := client.ReplicationControllers(api.Namespace(ctx)).Update(controller) controllerOut, err := client.ReplicationControllers(api.Namespace(ctx)).Update(controller)
if err != nil { if err != nil {
return err return err
...@@ -251,26 +251,25 @@ func RunController(ctx api.Context, image, name string, replicas int, client cli ...@@ -251,26 +251,25 @@ func RunController(ctx api.Context, image, name string, replicas int, client cli
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: name, Name: name,
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: replicas, Replicas: replicas,
ReplicaSelector: map[string]string{ Selector: map[string]string{
"name": name, "name": name,
}, },
PodTemplate: api.PodTemplate{ Template: &api.PodTemplateSpec{
DesiredState: api.PodState{ ObjectMeta: api.ObjectMeta{
Manifest: api.ContainerManifest{ Labels: map[string]string{
Version: "v1beta2", "name": name,
Containers: []api.Container{
{
Name: strings.ToLower(name),
Image: image,
Ports: ports,
},
},
}, },
}, },
Labels: map[string]string{ Spec: api.PodSpec{
"name": name, 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 ...@@ -328,8 +327,8 @@ func DeleteController(ctx api.Context, name string, client client.Interface) err
if err != nil { if err != nil {
return err return err
} }
if controller.DesiredState.Replicas != 0 { if controller.Spec.Replicas != 0 {
return fmt.Errorf("controller has non-zero replicas (%d), please stop it first", controller.DesiredState.Replicas) return fmt.Errorf("controller has non-zero replicas (%d), please stop it first", controller.Spec.Replicas)
} }
return client.ReplicationControllers(api.Namespace(ctx)).Delete(name) return client.ReplicationControllers(api.Namespace(ctx)).Delete(name)
} }
...@@ -74,13 +74,11 @@ func TestUpdateWithNewImage(t *testing.T) { ...@@ -74,13 +74,11 @@ func TestUpdateWithNewImage(t *testing.T) {
}, },
}, },
Ctrl: api.ReplicationController{ Ctrl: api.ReplicationController{
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
PodTemplate: api.PodTemplate{ Template: &api.PodTemplateSpec{
DesiredState: api.PodState{ Spec: api.PodSpec{
Manifest: api.ContainerManifest{ Containers: []api.Container{
Containers: []api.Container{ {Image: "fooImage:1"},
{Image: "fooImage:1"},
},
}, },
}, },
}, },
...@@ -94,7 +92,7 @@ func TestUpdateWithNewImage(t *testing.T) { ...@@ -94,7 +92,7 @@ func TestUpdateWithNewImage(t *testing.T) {
validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t) validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t)
newCtrl := api.Scheme.CopyOrDie(&fakeClient.Ctrl).(*api.ReplicationController) 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: "update-controller", Value: newCtrl}, fakeClient.Actions[1], t)
validateAction(client.FakeAction{Action: "list-pods"}, fakeClient.Actions[2], t) validateAction(client.FakeAction{Action: "list-pods"}, fakeClient.Actions[2], t)
...@@ -115,8 +113,8 @@ func TestRunController(t *testing.T) { ...@@ -115,8 +113,8 @@ func TestRunController(t *testing.T) {
} }
controller := fakeClient.Actions[0].Value.(*api.ReplicationController) controller := fakeClient.Actions[0].Value.(*api.ReplicationController)
if controller.Name != name || if controller.Name != name ||
controller.DesiredState.Replicas != replicas || controller.Spec.Replicas != replicas ||
controller.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image != image { controller.Spec.Template.Spec.Containers[0].Image != image {
t.Errorf("Unexpected controller: %#v", controller) t.Errorf("Unexpected controller: %#v", controller)
} }
} }
...@@ -136,8 +134,8 @@ func TestRunControllerWithWrongArgs(t *testing.T) { ...@@ -136,8 +134,8 @@ func TestRunControllerWithWrongArgs(t *testing.T) {
} }
controller := fakeClient.Actions[0].Value.(*api.ReplicationController) controller := fakeClient.Actions[0].Value.(*api.ReplicationController)
if controller.Name != name || if controller.Name != name ||
controller.DesiredState.Replicas != replicas || controller.Spec.Replicas != replicas ||
controller.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image != image { controller.Spec.Template.Spec.Containers[0].Image != image {
t.Errorf("Unexpected controller: %#v", controller) t.Errorf("Unexpected controller: %#v", controller)
} }
} }
...@@ -155,8 +153,8 @@ func TestRunControllerWithService(t *testing.T) { ...@@ -155,8 +153,8 @@ func TestRunControllerWithService(t *testing.T) {
} }
controller := fakeClient.Actions[0].Value.(*api.ReplicationController) controller := fakeClient.Actions[0].Value.(*api.ReplicationController)
if controller.Name != name || if controller.Name != name ||
controller.DesiredState.Replicas != replicas || controller.Spec.Replicas != replicas ||
controller.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image != image { controller.Spec.Template.Spec.Containers[0].Image != image {
t.Errorf("Unexpected controller: %#v", controller) t.Errorf("Unexpected controller: %#v", controller)
} }
} }
...@@ -174,7 +172,7 @@ func TestStopController(t *testing.T) { ...@@ -174,7 +172,7 @@ func TestStopController(t *testing.T) {
} }
controller := fakeClient.Actions[1].Value.(*api.ReplicationController) controller := fakeClient.Actions[1].Value.(*api.ReplicationController)
if fakeClient.Actions[1].Action != "update-controller" || if fakeClient.Actions[1].Action != "update-controller" ||
controller.DesiredState.Replicas != 0 { controller.Spec.Replicas != 0 {
t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1]) t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1])
} }
} }
...@@ -193,7 +191,7 @@ func TestResizeController(t *testing.T) { ...@@ -193,7 +191,7 @@ func TestResizeController(t *testing.T) {
} }
controller := fakeClient.Actions[1].Value.(*api.ReplicationController) controller := fakeClient.Actions[1].Value.(*api.ReplicationController)
if fakeClient.Actions[1].Action != "update-controller" || if fakeClient.Actions[1].Action != "update-controller" ||
controller.DesiredState.Replicas != 17 { controller.Spec.Replicas != 17 {
t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1]) t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1])
} }
} }
...@@ -221,7 +219,7 @@ func TestCloudCfgDeleteController(t *testing.T) { ...@@ -221,7 +219,7 @@ func TestCloudCfgDeleteController(t *testing.T) {
func TestCloudCfgDeleteControllerWithReplicas(t *testing.T) { func TestCloudCfgDeleteControllerWithReplicas(t *testing.T) {
fakeClient := client.Fake{ fakeClient := client.Fake{
Ctrl: api.ReplicationController{ Ctrl: api.ReplicationController{
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 2, Replicas: 2,
}, },
}, },
......
...@@ -107,18 +107,15 @@ func TestParseController(t *testing.T) { ...@@ -107,18 +107,15 @@ func TestParseController(t *testing.T) {
DoParseTest(t, "replicationControllers", &api.ReplicationController{ DoParseTest(t, "replicationControllers", &api.ReplicationController{
TypeMeta: api.TypeMeta{APIVersion: "v1beta1", Kind: "ReplicationController"}, TypeMeta: api.TypeMeta{APIVersion: "v1beta1", Kind: "ReplicationController"},
ObjectMeta: api.ObjectMeta{Name: "my controller"}, ObjectMeta: api.ObjectMeta{Name: "my controller"},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 9001, Replicas: 9001,
PodTemplate: api.PodTemplate{ Template: &api.PodTemplateSpec{
DesiredState: api.PodState{ Spec: api.PodSpec{
Manifest: api.ContainerManifest{ Containers: []api.Container{
ID: "My manifest", {Name: "my container"},
Containers: []api.Container{ },
{Name: "my container"}, Volumes: []api.Volume{
}, {Name: "volume"},
Volumes: []api.Volume{
{Name: "volume"},
},
}, },
}, },
}, },
......
...@@ -185,6 +185,14 @@ func makeImageList(manifest api.ContainerManifest) string { ...@@ -185,6 +185,14 @@ func makeImageList(manifest api.ContainerManifest) string {
return strings.Join(images, ",") 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 { func podHostString(host, ip string) string {
if host == "" && ip == "" { if host == "" && ip == "" {
return "<unassigned>" return "<unassigned>"
...@@ -211,8 +219,8 @@ func printPodList(podList *api.PodList, w io.Writer) error { ...@@ -211,8 +219,8 @@ func printPodList(podList *api.PodList, w io.Writer) error {
func printReplicationController(controller *api.ReplicationController, 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", _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n",
controller.Name, makeImageList(controller.DesiredState.PodTemplate.DesiredState.Manifest), controller.Name, makeImageListPodSpec(controller.Spec.Template.Spec),
labels.Set(controller.DesiredState.ReplicaSelector), controller.DesiredState.Replicas) labels.Set(controller.Spec.Selector), controller.Spec.Replicas)
return err return err
} }
......
...@@ -87,9 +87,15 @@ func (d *PodDescriber) Describe(namespace, name string) (string, error) { ...@@ -87,9 +87,15 @@ func (d *PodDescriber) Describe(namespace, name string) (string, error) {
return "", err 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 { return tabbedString(func(out *tabwriter.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", pod.Name) 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, "Host:\t%s\n", pod.CurrentState.Host+"/"+pod.CurrentState.HostIP)
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(pod.Labels)) fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(pod.Labels))
fmt.Fprintf(out, "Status:\t%s\n", string(pod.CurrentState.Status)) fmt.Fprintf(out, "Status:\t%s\n", string(pod.CurrentState.Status))
...@@ -127,10 +133,10 @@ func (d *ReplicationControllerDescriber) Describe(namespace, name string) (strin ...@@ -127,10 +133,10 @@ func (d *ReplicationControllerDescriber) Describe(namespace, name string) (strin
return tabbedString(func(out *tabwriter.Writer) error { return tabbedString(func(out *tabwriter.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", controller.Name) 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, "Image(s):\t%s\n", makeImageList(&controller.Spec.Template.Spec))
fmt.Fprintf(out, "Selector:\t%s\n", formatLabels(controller.DesiredState.ReplicaSelector)) fmt.Fprintf(out, "Selector:\t%s\n", formatLabels(controller.Spec.Selector))
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(controller.Labels)) 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) fmt.Fprintf(out, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
return nil return nil
}) })
...@@ -197,7 +203,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface, ...@@ -197,7 +203,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface,
// Find the ones that match labelsToMatch. // Find the ones that match labelsToMatch.
var matchingRCs []api.ReplicationController var matchingRCs []api.ReplicationController
for _, controller := range rcs.Items { for _, controller := range rcs.Items {
selector := labels.SelectorFromSet(controller.DesiredState.ReplicaSelector) selector := labels.SelectorFromSet(controller.Spec.Selector)
if selector.Matches(labelsToMatch) { if selector.Matches(labelsToMatch) {
matchingRCs = append(matchingRCs, controller) matchingRCs = append(matchingRCs, controller)
} }
...@@ -206,7 +212,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface, ...@@ -206,7 +212,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface,
// Format the matching RC's into strings. // Format the matching RC's into strings.
var rcStrings []string var rcStrings []string
for _, controller := range matchingRCs { 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, ", ") list := strings.Join(rcStrings, ", ")
...@@ -217,7 +223,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface, ...@@ -217,7 +223,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface,
} }
func getPodStatusForReplicationController(c client.PodInterface, controller *api.ReplicationController) (running, waiting, succeeded, failed int, err error) { 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 { if err != nil {
return return
} }
......
...@@ -139,9 +139,9 @@ func formatLabels(labelMap map[string]string) string { ...@@ -139,9 +139,9 @@ func formatLabels(labelMap map[string]string) string {
return l return l
} }
func makeImageList(manifest api.ContainerManifest) string { func makeImageList(spec *api.PodSpec) string {
var images []string var images []string
for _, container := range manifest.Containers { for _, container := range spec.Containers {
images = append(images, container.Image) images = append(images, container.Image)
} }
return strings.Join(images, ",") return strings.Join(images, ",")
......
...@@ -251,8 +251,14 @@ func podHostString(host, ip string) string { ...@@ -251,8 +251,14 @@ func podHostString(host, ip string) string {
} }
func printPod(pod *api.Pod, w io.Writer) error { 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", _, 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), podHostString(pod.CurrentState.Host, pod.CurrentState.HostIP),
labels.Set(pod.Labels), pod.CurrentState.Status) labels.Set(pod.Labels), pod.CurrentState.Status)
return err return err
...@@ -269,8 +275,8 @@ func printPodList(podList *api.PodList, w io.Writer) error { ...@@ -269,8 +275,8 @@ func printPodList(podList *api.PodList, w io.Writer) error {
func printReplicationController(controller *api.ReplicationController, 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", _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n",
controller.Name, makeImageList(controller.DesiredState.PodTemplate.DesiredState.Manifest), controller.Name, makeImageList(&controller.Spec.Template.Spec),
labels.Set(controller.DesiredState.ReplicaSelector), controller.DesiredState.Replicas) labels.Set(controller.Spec.Selector), controller.Spec.Replicas)
return err return err
} }
......
...@@ -64,8 +64,6 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE ...@@ -64,8 +64,6 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
if len(controller.Name) == 0 { if len(controller.Name) == 0 {
controller.Name = util.NewUUID().String() 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 { if errs := validation.ValidateReplicationController(controller); len(errs) > 0 {
return nil, errors.NewInvalid("replicationController", controller.Name, errs) return nil, errors.NewInvalid("replicationController", controller.Name, errs)
} }
...@@ -158,41 +156,41 @@ func (rs *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVer ...@@ -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): remove watch.Filter, which is broken. Implement consistent way of filtering.
// TODO(lavalamp): this watch method needs a test. // TODO(lavalamp): this watch method needs a test.
return watch.Filter(incoming, func(e watch.Event) (watch.Event, bool) { 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 { if !ok {
// must be an error event-- pass it on // must be an error event-- pass it on
return e, true return e, true
} }
match := label.Matches(labels.Set(repController.Labels)) match := label.Matches(labels.Set(controller.Labels))
if match { if match {
rs.fillCurrentState(ctx, repController) rs.fillCurrentState(ctx, controller)
} }
return e, match return e, match
}), nil }), 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 { 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 { if err != nil {
return ctrl, err return controller, err
} }
if len(pods.Items) == ctrl.DesiredState.Replicas { if len(pods.Items) == controller.Spec.Replicas {
break break
} }
time.Sleep(rs.pollPeriod) 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 { if rs.podLister == nil {
return 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 { if err != nil {
return err return err
} }
ctrl.CurrentState.Replicas = len(list.Items) controller.Status.Replicas = len(list.Items)
return nil return nil
} }
...@@ -116,6 +116,15 @@ func TestControllerDecode(t *testing.T) { ...@@ -116,6 +116,15 @@ func TestControllerDecode(t *testing.T) {
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "foo", Name: "foo",
}, },
Spec: api.ReplicationControllerSpec{
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{
"name": "nginx",
},
},
},
},
} }
body, err := latest.Codec.Encode(controller) body, err := latest.Codec.Encode(controller)
if err != nil { if err != nil {
...@@ -140,30 +149,30 @@ func TestControllerParsing(t *testing.T) { ...@@ -140,30 +149,30 @@ func TestControllerParsing(t *testing.T) {
"name": "nginx", "name": "nginx",
}, },
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 2, Replicas: 2,
ReplicaSelector: map[string]string{ Selector: map[string]string{
"name": "nginx", "name": "nginx",
}, },
PodTemplate: api.PodTemplate{ Template: &api.PodTemplateSpec{
DesiredState: api.PodState{ ObjectMeta: api.ObjectMeta{
Manifest: api.ContainerManifest{ Labels: map[string]string{
Containers: []api.Container{ "name": "nginx",
{ },
Image: "dockerfile/nginx", },
Ports: []api.Port{ Spec: api.PodSpec{
{ Containers: []api.Container{
ContainerPort: 80, {
HostPort: 8080, 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) { ...@@ -205,9 +214,11 @@ func TestControllerParsing(t *testing.T) {
} }
var validPodTemplate = api.PodTemplate{ var validPodTemplate = api.PodTemplate{
DesiredState: api.PodState{ Spec: api.PodTemplateSpec{
Manifest: api.ContainerManifest{ ObjectMeta: api.ObjectMeta{
Version: "v1beta1", Labels: map[string]string{"a": "b"},
},
Spec: api.PodSpec{
Containers: []api.Container{ Containers: []api.Container{
{ {
Name: "test", Name: "test",
...@@ -216,7 +227,6 @@ var validPodTemplate = api.PodTemplate{ ...@@ -216,7 +227,6 @@ var validPodTemplate = api.PodTemplate{
}, },
}, },
}, },
Labels: map[string]string{"a": "b"},
} }
func TestCreateController(t *testing.T) { func TestCreateController(t *testing.T) {
...@@ -240,10 +250,10 @@ func TestCreateController(t *testing.T) { ...@@ -240,10 +250,10 @@ func TestCreateController(t *testing.T) {
} }
controller := &api.ReplicationController{ controller := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{Name: "test"}, ObjectMeta: api.ObjectMeta{Name: "test"},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 2, Replicas: 2,
ReplicaSelector: map[string]string{"a": "b"}, Selector: map[string]string{"a": "b"},
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
} }
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
...@@ -273,13 +283,13 @@ func TestControllerStorageValidatesCreate(t *testing.T) { ...@@ -273,13 +283,13 @@ func TestControllerStorageValidatesCreate(t *testing.T) {
failureCases := map[string]api.ReplicationController{ failureCases := map[string]api.ReplicationController{
"empty ID": { "empty ID": {
ObjectMeta: api.ObjectMeta{Name: ""}, ObjectMeta: api.ObjectMeta{Name: ""},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: map[string]string{"bar": "baz"}, Selector: map[string]string{"bar": "baz"},
}, },
}, },
"empty selector": { "empty selector": {
ObjectMeta: api.ObjectMeta{Name: "abc"}, ObjectMeta: api.ObjectMeta{Name: "abc"},
DesiredState: api.ReplicationControllerState{}, Spec: api.ReplicationControllerSpec{},
}, },
} }
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
...@@ -304,13 +314,13 @@ func TestControllerStorageValidatesUpdate(t *testing.T) { ...@@ -304,13 +314,13 @@ func TestControllerStorageValidatesUpdate(t *testing.T) {
failureCases := map[string]api.ReplicationController{ failureCases := map[string]api.ReplicationController{
"empty ID": { "empty ID": {
ObjectMeta: api.ObjectMeta{Name: ""}, ObjectMeta: api.ObjectMeta{Name: ""},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: map[string]string{"bar": "baz"}, Selector: map[string]string{"bar": "baz"},
}, },
}, },
"empty selector": { "empty selector": {
ObjectMeta: api.ObjectMeta{Name: "abc"}, ObjectMeta: api.ObjectMeta{Name: "abc"},
DesiredState: api.ReplicationControllerState{}, Spec: api.ReplicationControllerSpec{},
}, },
} }
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
...@@ -351,19 +361,19 @@ func TestFillCurrentState(t *testing.T) { ...@@ -351,19 +361,19 @@ func TestFillCurrentState(t *testing.T) {
podLister: &fakeLister, podLister: &fakeLister,
} }
controller := api.ReplicationController{ controller := api.ReplicationController{
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: map[string]string{ Selector: map[string]string{
"foo": "bar", "foo": "bar",
}, },
}, },
} }
ctx := api.NewContext() ctx := api.NewContext()
storage.fillCurrentState(ctx, &controller) storage.fillCurrentState(ctx, &controller)
if controller.CurrentState.Replicas != 2 { if controller.Status.Replicas != 2 {
t.Errorf("expected 2, got: %d", controller.CurrentState.Replicas) t.Errorf("expected 2, got: %d", controller.Status.Replicas)
} }
if !reflect.DeepEqual(fakeLister.s, labels.Set(controller.DesiredState.ReplicaSelector).AsSelector()) { if !reflect.DeepEqual(fakeLister.s, labels.Set(controller.Spec.Selector).AsSelector()) {
t.Errorf("unexpected output: %#v %#v", labels.Set(controller.DesiredState.ReplicaSelector).AsSelector(), fakeLister.s) t.Errorf("unexpected output: %#v %#v", labels.Set(controller.Spec.Selector).AsSelector(), fakeLister.s)
} }
} }
......
...@@ -960,7 +960,7 @@ func TestEtcdUpdateController(t *testing.T) { ...@@ -960,7 +960,7 @@ func TestEtcdUpdateController(t *testing.T) {
registry := NewTestEtcdRegistry(fakeClient) registry := NewTestEtcdRegistry(fakeClient)
err := registry.UpdateController(ctx, &api.ReplicationController{ err := registry.UpdateController(ctx, &api.ReplicationController{
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: strconv.FormatUint(resp.Node.ModifiedIndex, 10)}, ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: strconv.FormatUint(resp.Node.ModifiedIndex, 10)},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: 2, Replicas: 2,
}, },
}) })
...@@ -969,7 +969,7 @@ func TestEtcdUpdateController(t *testing.T) { ...@@ -969,7 +969,7 @@ func TestEtcdUpdateController(t *testing.T) {
} }
ctrl, err := registry.GetController(ctx, "foo") ctrl, err := registry.GetController(ctx, "foo")
if ctrl.DesiredState.Replicas != 2 { if ctrl.Spec.Replicas != 2 {
t.Errorf("Unexpected controller: %#v", ctrl) t.Errorf("Unexpected controller: %#v", ctrl)
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment