Browse code

Implement deployments without Deployment

Deprecate the Deployment resource and remplement deployment mechanics
using ReplicationControllers instead of Deployment. The result is a deployment
subsystem which adds fewer new concepts to Kubernetes while providing the same
functionality as the former subsystem.

Dan Mace authored on 2014/12/18 02:01:36
Showing 22 changed files
... ...
@@ -117,14 +117,18 @@ angular.module('openshiftConsole')
117 117
     // Sets up subscription for deployments and deploymentsByConfig
118 118
     var deploymentsCallback = function(action, deployment) {
119 119
       $scope.$apply(function() {
120
-        DataService.objectByAttribute(deployment, "metadata.name", $scope.deployments, action);
121
-        DataService.objectByAttribute(deployment, "metadata.annotations.deploymentConfig", $scope.deploymentsByConfig, action, "id");
120
+        if (deployment.annotations && deployment.annotations.encodedDeploymentConfig) {
121
+          var depConfig = $.parseJSON(deployment.annotations.encodedDeploymentConfig);
122
+          deployment.details = depConfig.details;
123
+        }
124
+        DataService.objectByAttribute(deployment, "id", $scope.deployments, action);
125
+        DataService.objectByAttribute(deployment, "annotations.deploymentConfig", $scope.deploymentsByConfig, action, "id");
122 126
       });
123 127
 
124 128
       console.log("deployments (subscribe)", $scope.deployments);
125 129
       console.log("deploymentsByConfig (subscribe)", $scope.deploymentsByConfig);
126 130
     };
127
-    DataService.subscribe("deployments", deploymentsCallback, $scope);
131
+    DataService.subscribe("replicationControllers", deploymentsCallback, $scope);
128 132
 
129 133
     // Sets up subscription for images and imagesByDockerReference
130 134
     var imagesCallback = function(action, image) {
... ...
@@ -12,7 +12,8 @@ angular.module('openshiftConsole')
12 12
     images : "osapi",
13 13
     projects : "osapi",
14 14
     pods : "api",
15
-    services : "api"
15
+    services : "api",
16
+    replicationControllers: "api"
16 17
   };
17 18
 
18 19
   function DataService() {
... ...
@@ -32,9 +32,9 @@
32 32
               </div>
33 33
             </div>
34 34
             <div ng-repeat="deployment in deploymentsByConfig[deploymentConfigId]" style="margin-top: 10px; text-align: center;">
35
-              <div ng-if="servicePodsByLabel.deployment[deployment.metadata.name]">
35
+              <div ng-if="servicePodsByLabel.deployment[deployment.id]">
36 36
                 <div class="small muted" ng-if="deployment" style="margin-bottom: 10px;">
37
-                  <relative-timestamp timestamp="deployment.metadata.creationTimestamp"></relative-timestamp>
37
+                  <relative-timestamp timestamp="deployment.creationTimestamp"></relative-timestamp>
38 38
                   <span ng-if="deployment.details && deployment.details.causes && deployment.details.causes.length > 0">
39 39
                     <span>, triggered by 
40 40
                       <span ng-repeat="cause in deployment.details.causes">
... ...
@@ -48,8 +48,8 @@
48 48
                 </div>
49 49
                 <div style="display: inline-block;">
50 50
                   <!-- TODO figure out why podTemplate can't be done the same way as pods -->
51
-                  <pod-template ng-init="podTemplate = deployment.controllerTemplate.podTemplate"></pod-template>
52
-                  <pods pods="servicePodsByLabel.deployment[deployment.metadata.name]"></pods>
51
+                  <pod-template ng-init="podTemplate = deployment.desiredState.podTemplate"></pod-template>
52
+                  <pods pods="servicePodsByLabel.deployment[deployment.id]"></pods>
53 53
                 </div>
54 54
               </div>
55 55
             </div>
... ...
@@ -1084,7 +1084,8 @@ deploymentConfigs:"osapi",
1084 1084
 images:"osapi",
1085 1085
 projects:"osapi",
1086 1086
 pods:"api",
1087
-services:"api"
1087
+services:"api",
1088
+replicationControllers:"api"
1088 1089
 };
1089 1090
 a.prototype.getList = function(a, b, c) {
1090 1091
 if (b.fire) {
... ...
@@ -1247,10 +1248,14 @@ f(d), e(b), c.subscribe("services", g, a), c.subscribePolling("pods", e, a);
1247 1247
 }, this));
1248 1248
 var i = function(b, d) {
1249 1249
 a.$apply(function() {
1250
-c.objectByAttribute(d, "metadata.name", a.deployments, b), c.objectByAttribute(d, "metadata.annotations.deploymentConfig", a.deploymentsByConfig, b, "id");
1250
+if (d.annotations && d.annotations.encodedDeploymentConfig) {
1251
+var e = $.parseJSON(d.annotations.encodedDeploymentConfig);
1252
+d.details = e.details;
1253
+}
1254
+c.objectByAttribute(d, "id", a.deployments, b), c.objectByAttribute(d, "annotations.deploymentConfig", a.deploymentsByConfig, b, "id");
1251 1255
 }), console.log("deployments (subscribe)", a.deployments), console.log("deploymentsByConfig (subscribe)", a.deploymentsByConfig);
1252 1256
 };
1253
-c.subscribe("deployments", i, a);
1257
+c.subscribe("replicationControllers", i, a);
1254 1258
 var j = function(b, d) {
1255 1259
 a.$apply(function() {
1256 1260
 c.objectByAttribute(d, "metadata.name", a.images, b), c.objectByAttribute(d, "dockerImageReference", a.imagesByDockerReference, b);
... ...
@@ -45546,7 +45551,7 @@ func views_pods_html2() ([]byte, error) {
45546 45546
 	return _views_pods_html2, nil
45547 45547
 }
45548 45548
 
45549
-var _views_project_html = []byte(`<div> <h1 style="margin-top: 10px">Project {{project.displayName || project.metadata.name}}</h1> <div class="small muted" style="margin-top: -15px; margin-bottom: 20px" ng-if="project | annotation : 'description'">{{project | annotation : 'description'}}</div> <!-- TODO handle things that don't live under services --> <!-- TODO handle multiple services mapping to the same deploymentConfig/deployment/pod --> <section ng-repeat="(serviceId, servicePodsByLabel) in podsByServiceByLabel"> <div class="row"> <div class="col-md-12"> <div class="tile"> <h2 class="service">{{serviceId}} <span class="small"> - routing {{services[serviceId].protocol}} traffic on {{services[serviceId].portalIP}}:{{services[serviceId].port}} to port {{services[serviceId].containerPort}}</span> <span class="connector connector-vertical" style="left: 50%; top: 35px; height: 21px"> <span class="connector-endpoint connector-endpoint-top"></span> <span class="connector-line" style="height: 21px"></span> <span class="connector-endpoint" style="top: 18px"></span> </span> </h2> <div class="separator"></div> <div ng-repeat="(deploymentConfigId, deploymentConfig) in deploymentConfigs" ng-if="!deploymentConfig || services[serviceId].selector.name == deploymentConfig.template.controllerTemplate.replicaSelector.name"> <!-- deploymentConfig could be null when we have any deployments that were not generated from a deploymentConfig --> <div ng-if="deploymentConfig"> <div ng-repeat="trigger in deploymentConfig.triggers"> <div ng-repeat="build in trigger.builds" style="border-bottom: 1px solid #ddd; padding-bottom: 10px; margin-top: 10px" ng-if="(build.status != 'Complete' && build.status != 'Failed') || (build.metadata.creationTimestamp | ageLessThan : 5 : 'minutes')" class="animate-repeat"> <span ng-switch="build.status" class="hide-ng-leave"> <span ng-switch-when="Complete" class="font-icon icon-ok font-icon-green" aria-hidden="true" style="margin-right: 5px"></span> <span ng-switch-when="Failed" class="font-icon icon-remove font-icon-red" aria-hidden="true" style="margin-right: 5px"></span> <span ng-switch-default class="font-icon icon-ci-arrows clockwise" aria-hidden="true" style="margin-right: 5px"></span> </span> <span>A build of {{build.metadata.labels.buildconfig}} is {{build.status | lowercase}}.</span> <span ng-if="build.status != 'Complete' && build.status != 'Failed' && trigger.imageChangeParams.automatic"> A new deployment will be created automatically once the build completes.</span> </div> </div> </div> <div ng-repeat="deployment in deploymentsByConfig[deploymentConfigId]" style="margin-top: 10px; text-align: center"> <div ng-if="servicePodsByLabel.deployment[deployment.metadata.name]"> <div class="small muted" ng-if="deployment" style="margin-bottom: 10px"> <relative-timestamp timestamp="deployment.metadata.creationTimestamp"></relative-timestamp> <span ng-if="deployment.details && deployment.details.causes && deployment.details.causes.length > 0"> <span>, triggered by <span ng-repeat="cause in deployment.details.causes"> <span ng-switch="cause.type"> <span ng-switch-when="ImageChange">new image for {{cause.imageTrigger.repositoryName | imageName}}:{{cause.imageTrigger.tag}}</span> <span ng-switch-when="ConfigChange">deployment configuration change</span> </span> </span> </span> </span> </div> <div style="display: inline-block"> <!-- TODO figure out why podTemplate can't be done the same way as pods --> <pod-template ng-init="podTemplate = deployment.controllerTemplate.podTemplate"></pod-template> <pods pods="servicePodsByLabel.deployment[deployment.metadata.name]"></pods> </div> </div> </div> </div> <!-- TODO implement filters for empty and present to return booleans for cases like this --> <div ng-if="(servicePodsByLabel | hashSize) == 0" style="margin-top: 10px"> <span style="margin-left: 20px">There are currently no pods for this service.</span> </div> </div> </div> </div> </section> </div>`)
45549
+var _views_project_html = []byte(`<div> <h1 style="margin-top: 10px">Project {{project.displayName || project.metadata.name}}</h1> <div class="small muted" style="margin-top: -15px; margin-bottom: 20px" ng-if="project | annotation : 'description'">{{project | annotation : 'description'}}</div> <!-- TODO handle things that don't live under services --> <!-- TODO handle multiple services mapping to the same deploymentConfig/deployment/pod --> <section ng-repeat="(serviceId, servicePodsByLabel) in podsByServiceByLabel"> <div class="row"> <div class="col-md-12"> <div class="tile"> <h2 class="service">{{serviceId}} <span class="small"> - routing {{services[serviceId].protocol}} traffic on {{services[serviceId].portalIP}}:{{services[serviceId].port}} to port {{services[serviceId].containerPort}}</span> <span class="connector connector-vertical" style="left: 50%; top: 35px; height: 21px"> <span class="connector-endpoint connector-endpoint-top"></span> <span class="connector-line" style="height: 21px"></span> <span class="connector-endpoint" style="top: 18px"></span> </span> </h2> <div class="separator"></div> <div ng-repeat="(deploymentConfigId, deploymentConfig) in deploymentConfigs" ng-if="!deploymentConfig || services[serviceId].selector.name == deploymentConfig.template.controllerTemplate.replicaSelector.name"> <!-- deploymentConfig could be null when we have any deployments that were not generated from a deploymentConfig --> <div ng-if="deploymentConfig"> <div ng-repeat="trigger in deploymentConfig.triggers"> <div ng-repeat="build in trigger.builds" style="border-bottom: 1px solid #ddd; padding-bottom: 10px; margin-top: 10px" ng-if="(build.status != 'Complete' && build.status != 'Failed') || (build.metadata.creationTimestamp | ageLessThan : 5 : 'minutes')" class="animate-repeat"> <span ng-switch="build.status" class="hide-ng-leave"> <span ng-switch-when="Complete" class="font-icon icon-ok font-icon-green" aria-hidden="true" style="margin-right: 5px"></span> <span ng-switch-when="Failed" class="font-icon icon-remove font-icon-red" aria-hidden="true" style="margin-right: 5px"></span> <span ng-switch-default class="font-icon icon-ci-arrows clockwise" aria-hidden="true" style="margin-right: 5px"></span> </span> <span>A build of {{build.metadata.labels.buildconfig}} is {{build.status | lowercase}}.</span> <span ng-if="build.status != 'Complete' && build.status != 'Failed' && trigger.imageChangeParams.automatic"> A new deployment will be created automatically once the build completes.</span> </div> </div> </div> <div ng-repeat="deployment in deploymentsByConfig[deploymentConfigId]" style="margin-top: 10px; text-align: center"> <div ng-if="servicePodsByLabel.deployment[deployment.id]"> <div class="small muted" ng-if="deployment" style="margin-bottom: 10px"> <relative-timestamp timestamp="deployment.creationTimestamp"></relative-timestamp> <span ng-if="deployment.details && deployment.details.causes && deployment.details.causes.length > 0"> <span>, triggered by <span ng-repeat="cause in deployment.details.causes"> <span ng-switch="cause.type"> <span ng-switch-when="ImageChange">new image for {{cause.imageTrigger.repositoryName | imageName}}:{{cause.imageTrigger.tag}}</span> <span ng-switch-when="ConfigChange">deployment configuration change</span> </span> </span> </span> </span> </div> <div style="display: inline-block"> <!-- TODO figure out why podTemplate can't be done the same way as pods --> <pod-template ng-init="podTemplate = deployment.desiredState.podTemplate"></pod-template> <pods pods="servicePodsByLabel.deployment[deployment.id]"></pods> </div> </div> </div> </div> <!-- TODO implement filters for empty and present to return booleans for cases like this --> <div ng-if="(servicePodsByLabel | hashSize) == 0" style="margin-top: 10px"> <span style="margin-left: 20px">There are currently no pods for this service.</span> </div> </div> </div> </div> </section> </div>`)
45550 45550
 
45551 45551
 func views_project_html() ([]byte, error) {
45552 45552
 	return _views_project_html, nil
... ...
@@ -7,9 +7,11 @@ import (
7 7
 	"github.com/golang/glog"
8 8
 	"github.com/spf13/cobra"
9 9
 
10
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
11
+
12
+	"github.com/openshift/origin/pkg/api/latest"
10 13
 	"github.com/openshift/origin/pkg/cmd/util"
11 14
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
12
-	deployapi "github.com/openshift/origin/pkg/deploy/api"
13 15
 	strategy "github.com/openshift/origin/pkg/deploy/strategy/recreate"
14 16
 )
15 17
 
... ...
@@ -52,7 +54,7 @@ func NewCommandDeployer(name string) *cobra.Command {
52 52
 
53 53
 // deploy starts the deployer
54 54
 func deploy(cfg *config) error {
55
-	kClient, osClient, err := cfg.Config.Clients()
55
+	kClient, _, err := cfg.Config.Clients()
56 56
 	if err != nil {
57 57
 		return err
58 58
 	}
... ...
@@ -60,12 +62,15 @@ func deploy(cfg *config) error {
60 60
 		return errors.New("No deployment name was specified.")
61 61
 	}
62 62
 
63
-	var deployment *deployapi.Deployment
64
-	if deployment, err = osClient.Deployments(cfg.Namespace).Get(cfg.DeploymentName); err != nil {
63
+	var deployment *kapi.ReplicationController
64
+	if deployment, err = kClient.ReplicationControllers(cfg.Namespace).Get(cfg.DeploymentName); err != nil {
65 65
 		return err
66 66
 	}
67 67
 
68 68
 	// TODO: Choose a strategy based on some input
69
-	strategy := &strategy.DeploymentStrategy{strategy.RealReplicationController{KubeClient: kClient}}
69
+	strategy := &strategy.DeploymentStrategy{
70
+		ReplicationController: strategy.RealReplicationController{KubeClient: kClient},
71
+		Codec: latest.Codec,
72
+	}
70 73
 	return strategy.Deploy(deployment)
71 74
 }
... ...
@@ -130,9 +130,10 @@ func (c *MasterConfig) InstallAPI(container *restful.Container) []string {
130 130
 	oauthEtcd := oauthetcd.New(c.EtcdHelper)
131 131
 
132 132
 	deployConfigGenerator := &deployconfiggenerator.DeploymentConfigGenerator{
133
-		DeploymentInterface:       deployEtcd,
133
+		DeploymentInterface:       &clientDeploymentInterface{c.KubeClient},
134 134
 		DeploymentConfigInterface: deployEtcd,
135 135
 		ImageRepositoryInterface:  imageEtcd,
136
+		Codec: latest.Codec,
136 137
 	}
137 138
 
138 139
 	defaultRegistry := env("OPENSHIFT_DEFAULT_REGISTRY", "")
... ...
@@ -354,6 +355,7 @@ func (c *MasterConfig) RunDeploymentController() {
354 354
 	factory := deploycontrollerfactory.DeploymentControllerFactory{
355 355
 		Client:     c.OSClient,
356 356
 		KubeClient: c.KubeClient,
357
+		Codec:      latest.Codec,
357 358
 		Environment: []api.EnvVar{
358 359
 			{Name: "KUBERNETES_MASTER", Value: c.MasterAddr},
359 360
 			{Name: "OPENSHIFT_MASTER", Value: c.MasterAddr},
... ...
@@ -367,13 +369,21 @@ func (c *MasterConfig) RunDeploymentController() {
367 367
 }
368 368
 
369 369
 func (c *MasterConfig) RunDeploymentConfigController() {
370
-	factory := deploycontrollerfactory.DeploymentConfigControllerFactory{Client: c.OSClient}
370
+	factory := deploycontrollerfactory.DeploymentConfigControllerFactory{
371
+		Client:     c.OSClient,
372
+		KubeClient: c.KubeClient,
373
+		Codec:      latest.Codec,
374
+	}
371 375
 	controller := factory.Create()
372 376
 	controller.Run()
373 377
 }
374 378
 
375 379
 func (c *MasterConfig) RunDeploymentConfigChangeController() {
376
-	factory := deploycontrollerfactory.DeploymentConfigChangeControllerFactory{Client: c.OSClient}
380
+	factory := deploycontrollerfactory.DeploymentConfigChangeControllerFactory{
381
+		Client:     c.OSClient,
382
+		KubeClient: c.KubeClient,
383
+		Codec:      latest.Codec,
384
+	}
377 385
 	controller := factory.Create()
378 386
 	controller.Run()
379 387
 }
... ...
@@ -420,3 +430,11 @@ func (c ClientWebhookInterface) CreateBuild(namespace string, build *buildapi.Bu
420 420
 func (c ClientWebhookInterface) GetBuildConfig(namespace, name string) (*buildapi.BuildConfig, error) {
421 421
 	return c.Client.BuildConfigs(namespace).Get(name)
422 422
 }
423
+
424
+type clientDeploymentInterface struct {
425
+	KubeClient kclient.Interface
426
+}
427
+
428
+func (c *clientDeploymentInterface) GetDeployment(ctx api.Context, id string) (*api.ReplicationController, error) {
429
+	return c.KubeClient.ReplicationControllers(api.Namespace(ctx)).Get(id)
430
+}
... ...
@@ -6,6 +6,9 @@ import (
6 6
 
7 7
 // A deployment represents a single configuration of a pod deployed into the cluster, and may
8 8
 // represent both a current deployment or a historical deployment.
9
+//
10
+// DEPRECATED: This type longer drives any system behavior. Deployments are now represented directly
11
+// by ReplicationControllers. Use DeploymentConfig to drive deployments.
9 12
 type Deployment struct {
10 13
 	kapi.TypeMeta   `json:",inline" yaml:",inline"`
11 14
 	kapi.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
... ...
@@ -25,7 +28,7 @@ type Deployment struct {
25 25
 	Details *DeploymentDetails `json:"details,omitempty" yaml:"details,omitempty"`
26 26
 }
27 27
 
28
-// DeploymentStatus decribes the possible states a Deployment can be in.
28
+// DeploymentStatus decribes the possible states a deployment can be in.
29 29
 type DeploymentStatus string
30 30
 
31 31
 const (
... ...
@@ -72,30 +75,49 @@ type CustomDeploymentStrategyParams struct {
72 72
 }
73 73
 
74 74
 // A DeploymentList is a collection of deployments.
75
+// DEPRECATED: Like Deployment, this is no longer used.
75 76
 type DeploymentList struct {
76 77
 	kapi.TypeMeta `json:",inline" yaml:",inline"`
77 78
 	kapi.ListMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
78 79
 	Items         []Deployment `json:"items" yaml:"items"`
79 80
 }
80 81
 
81
-// These constants represent annotation keys used for correlating objects related to deployments.
82
+// These constants represent keys used for correlating objects related to deployments.
82 83
 const (
84
+	// DeploymentConfigAnnotation is an annotation name used to correlate a deployment with the
85
+	// DeploymentConfig on which the deployment is based.
83 86
 	DeploymentConfigAnnotation = "deploymentConfig"
84
-	DeploymentAnnotation       = "deployment"
85
-	DeploymentPodAnnotation    = "pod"
87
+	// DeploymentAnnotation is an annotation on a deployer Pod. The annotation value is the name
88
+	// of the deployment (a ReplicationController) on which the deployer Pod acts.
89
+	DeploymentAnnotation = "deployment"
90
+	// DeploymentPodAnnotation is an annotation on a deployment (a ReplicationController). The
91
+	// annotation value is the name of the deployer Pod which will act upon the ReplicationController
92
+	// to implement the deployment behavior.
93
+	DeploymentPodAnnotation = "pod"
94
+	// DeploymentStatusAnnotation is an annotation name used to retrieve the DeploymentStatus of
95
+	// a deployment.
96
+	DeploymentStatusAnnotation = "deploymentStatus"
97
+	// DeploymentEncodedConfigAnnotation is an annotation name used to retrieve specific encoded
98
+	// DeploymentConfig on which a given deployment is based.
99
+	DeploymentEncodedConfigAnnotation = "encodedDeploymentConfig"
100
+	// DeploymentVersionAnnotation is an annotation on a deployment (a ReplicationController). The
101
+	// annotation value is the LatestVersion value of the DeploymentConfig which was the basis for
102
+	// the deployment.
103
+	DeploymentVersionAnnotation = "deploymentVersion"
104
+	// DeploymentLabel is the name of a label used to correlate a deployment with the Pod created
105
+	// to execute the deployment logic.
86 106
 	// TODO: This is a workaround for upstream's lack of annotation support on PodTemplate. Once
87 107
 	// annotations are available on PodTemplate, audit this constant with the goal of removing it.
88 108
 	DeploymentLabel = "deployment"
89
-)
90
-
91
-// These constants represent label keys used for correlating objects related to deployment.
92
-const (
109
+	// DeploymentConfigLabel is the name of a label used to correlate a deployment with the
110
+	// DeploymentConfigs on which the deployment is based.
93 111
 	DeploymentConfigLabel = "deploymentconfig"
94 112
 )
95 113
 
96
-// DeploymentConfig represents a configuration for a single deployment of a replication controller:
97
-// what the template is for the deployment, how new deployments are triggered, what the desired
98
-// deployment state is.
114
+// DeploymentConfig represents a configuration for a single deployment (represented as a
115
+// ReplicationController). It also contains details about changes which resulted in the current
116
+// state of the DeploymentConfig. Each change to the DeploymentConfig which should result in
117
+// a new deployment results in an increment of LatestVersion.
99 118
 type DeploymentConfig struct {
100 119
 	kapi.TypeMeta   `json:",inline" yaml:",inline"`
101 120
 	kapi.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
... ...
@@ -113,7 +135,7 @@ type DeploymentConfig struct {
113 113
 	Details *DeploymentDetails `json:"details,omitempty" yaml:"details,omitempty"`
114 114
 }
115 115
 
116
-// DeploymentTemplate contains all the necessary information to create a Deployment from a
116
+// DeploymentTemplate contains all the necessary information to create a deployment from a
117 117
 // DeploymentStrategy.
118 118
 type DeploymentTemplate struct {
119 119
 	// Strategy describes how a deployment is executed.
... ...
@@ -122,7 +144,7 @@ type DeploymentTemplate struct {
122 122
 	ControllerTemplate kapi.ReplicationControllerSpec `json:"controllerTemplate,omitempty" yaml:"controllerTemplate,omitempty"`
123 123
 }
124 124
 
125
-// DeploymentTriggerPolicy describes a policy for a single trigger that results in a new Deployment.
125
+// DeploymentTriggerPolicy describes a policy for a single trigger that results in a new deployment.
126 126
 type DeploymentTriggerPolicy struct {
127 127
 	Type DeploymentTriggerType `json:"type,omitempty" yaml:"type,omitempty"`
128 128
 	// ImageChangeParams represents the parameters for the ImageChange trigger.
... ...
@@ -7,6 +7,9 @@ import (
7 7
 
8 8
 // A deployment represents a single configuration of a pod deployed into the cluster, and may
9 9
 // represent both a current deployment or a historical deployment.
10
+//
11
+// DEPRECATED: This type longer drives any system behavior. Deployments are now represented directly
12
+// by ReplicationControllers. Use DeploymentConfig to drive deployments.
10 13
 type Deployment struct {
11 14
 	v1beta3.TypeMeta   `json:",inline" yaml:",inline"`
12 15
 	v1beta3.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
... ...
@@ -26,7 +29,7 @@ type Deployment struct {
26 26
 	Details *DeploymentDetails `json:"details,omitempty" yaml:"details,omitempty"`
27 27
 }
28 28
 
29
-// DeploymentStatus decribes the possible states a Deployment can be in.
29
+// DeploymentStatus decribes the possible states a deployment can be in.
30 30
 type DeploymentStatus string
31 31
 
32 32
 const (
... ...
@@ -73,30 +76,49 @@ type CustomDeploymentStrategyParams struct {
73 73
 }
74 74
 
75 75
 // A DeploymentList is a collection of deployments.
76
+// DEPRECATED: Like Deployment, this is no longer used.
76 77
 type DeploymentList struct {
77 78
 	v1beta3.TypeMeta `json:",inline" yaml:",inline"`
78 79
 	v1beta3.ListMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
79 80
 	Items            []Deployment `json:"items" yaml:"items"`
80 81
 }
81 82
 
82
-// These constants represent annotation keys used for correlating objects related to deployments.
83
+// These constants represent keys used for correlating objects related to deployments.
83 84
 const (
85
+	// DeploymentConfigAnnotation is an annotation name used to correlate a deployment with the
86
+	// DeploymentConfig on which the deployment is based.
84 87
 	DeploymentConfigAnnotation = "deploymentConfig"
85
-	DeploymentAnnotation       = "deployment"
86
-	DeploymentPodAnnotation    = "pod"
88
+	// DeploymentAnnotation is an annotation on a deployer Pod. The annotation value is the name
89
+	// of the deployment (a ReplicationController) on which the deployer Pod acts.
90
+	DeploymentAnnotation = "deployment"
91
+	// DeploymentPodAnnotation is an annotation on a deployment (a ReplicationController). The
92
+	// annotation value is the name of the deployer Pod which will act upon the ReplicationController
93
+	// to implement the deployment behavior.
94
+	DeploymentPodAnnotation = "pod"
95
+	// DeploymentStatusAnnotation is an annotation name used to retrieve the DeploymentStatus of
96
+	// a deployment.
97
+	DeploymentStatusAnnotation = "deploymentStatus"
98
+	// DeploymentEncodedConfigAnnotation is an annotation name used to retrieve specific encoded
99
+	// DeploymentConfig on which a given deployment is based.
100
+	DeploymentEncodedConfigAnnotation = "encodedDeploymentConfig"
101
+	// DeploymentVersionAnnotation is an annotation on a deployment (a ReplicationController). The
102
+	// annotation value is the LatestVersion value of the DeploymentConfig which was the basis for
103
+	// the deployment.
104
+	DeploymentVersionAnnotation = "deploymentVersion"
105
+	// DeploymentLabel is the name of a label used to correlate a deployment with the Pod created
106
+	// to execute the deployment logic.
87 107
 	// TODO: This is a workaround for upstream's lack of annotation support on PodTemplate. Once
88 108
 	// annotations are available on PodTemplate, audit this constant with the goal of removing it.
89 109
 	DeploymentLabel = "deployment"
90
-)
91
-
92
-// These constants represent label keys used for correlating objects related to deployment.
93
-const (
110
+	// DeploymentConfigLabel is the name of a label used to correlate a deployment with the
111
+	// DeploymentConfigs on which the deployment is based.
94 112
 	DeploymentConfigLabel = "deploymentconfig"
95 113
 )
96 114
 
97
-// DeploymentConfig represents a configuration for a single deployment of a replication controller:
98
-// what the template is for the deployment, how new deployments are triggered, what the desired
99
-// deployment state is.
115
+// DeploymentConfig represents a configuration for a single deployment (represented as a
116
+// ReplicationController). It also contains details about changes which resulted in the current
117
+// state of the DeploymentConfig. Each change to the DeploymentConfig which should result in
118
+// a new deployment results in an increment of LatestVersion.
100 119
 type DeploymentConfig struct {
101 120
 	v1beta3.TypeMeta   `json:",inline" yaml:",inline"`
102 121
 	v1beta3.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
... ...
@@ -114,7 +136,8 @@ type DeploymentConfig struct {
114 114
 	Details *DeploymentDetails `json:"details,omitempty" yaml:"details,omitempty"`
115 115
 }
116 116
 
117
-// DeploymentTemplate templatizes the configurable fields of a Deployment.
117
+// DeploymentTemplate contains all the necessary information to create a deployment from a
118
+// DeploymentStrategy.
118 119
 type DeploymentTemplate struct {
119 120
 	// Strategy describes how a deployment is executed.
120 121
 	Strategy DeploymentStrategy `json:"strategy,omitempty" yaml:"strategy,omitempty"`
... ...
@@ -122,7 +145,7 @@ type DeploymentTemplate struct {
122 122
 	ControllerTemplate v1beta1.ReplicationControllerState `json:"controllerTemplate,omitempty" yaml:"controllerTemplate,omitempty"`
123 123
 }
124 124
 
125
-// DeploymentTriggerPolicy describes a policy for a single trigger that results in a new Deployment.
125
+// DeploymentTriggerPolicy describes a policy for a single trigger that results in a new deployment.
126 126
 type DeploymentTriggerPolicy struct {
127 127
 	Type DeploymentTriggerType `json:"type,omitempty" yaml:"type,omitempty"`
128 128
 	// ImageChangeParams represents the parameters for the ImageChange trigger.
... ...
@@ -1,7 +1,9 @@
1 1
 package controller
2 2
 
3 3
 import (
4
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4 5
 	cache "github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
6
+	runtime "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
5 7
 	util "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
6 8
 
7 9
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
... ...
@@ -17,6 +19,7 @@ type DeploymentConfigChangeController struct {
17 17
 	ChangeStrategy       changeStrategy
18 18
 	NextDeploymentConfig func() *deployapi.DeploymentConfig
19 19
 	DeploymentStore      cache.Store
20
+	Codec                runtime.Codec
20 21
 	// Stop is an optional channel that controls when the controller exits
21 22
 	Stop <-chan struct{}
22 23
 }
... ...
@@ -62,17 +65,21 @@ func (dc *DeploymentConfigChangeController) HandleDeploymentConfig() {
62 62
 		return
63 63
 	}
64 64
 
65
-	deployment := obj.(*deployapi.Deployment)
65
+	deployment := obj.(*kapi.ReplicationController)
66 66
 
67
-	if deployutil.PodSpecsEqual(config.Template.ControllerTemplate.Template.Spec, deployment.ControllerTemplate.Template.Spec) {
68
-		glog.V(4).Infof("Ignoring updated config %s with LatestVersion=%d because it matches deployment %s", config.Name, config.LatestVersion, deployment.Name)
69
-		return
67
+	if deployedConfig, err := deployutil.DecodeDeploymentConfig(deployment, dc.Codec); err == nil {
68
+		if deployutil.PodSpecsEqual(config.Template.ControllerTemplate.Template.Spec, deployedConfig.Template.ControllerTemplate.Template.Spec) {
69
+			glog.V(4).Infof("Ignoring updated config %s with LatestVersion=%d because it matches deployed config %s", config.Name, config.LatestVersion, deployment.Name)
70
+			return
71
+		}
72
+	} else {
73
+		glog.V(0).Infof("Error decoding deploymentConfig from deployment %s: %v", deployment.Name, err)
70 74
 	}
71 75
 
72 76
 	dc.generateDeployment(config, deployment)
73 77
 }
74 78
 
75
-func (dc *DeploymentConfigChangeController) generateDeployment(config *deployapi.DeploymentConfig, deployment *deployapi.Deployment) {
79
+func (dc *DeploymentConfigChangeController) generateDeployment(config *deployapi.DeploymentConfig, deployment *kapi.ReplicationController) {
76 80
 	newConfig, err := dc.ChangeStrategy.GenerateDeploymentConfig(config.Namespace, config.Name)
77 81
 	if err != nil {
78 82
 		glog.V(2).Infof("Error generating new version of deploymentConfig %v: %#v", config.Name, err)
... ...
@@ -4,8 +4,11 @@ import (
4 4
 	"testing"
5 5
 
6 6
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
+
8
+	api "github.com/openshift/origin/pkg/api/latest"
7 9
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
8 10
 	deploytest "github.com/openshift/origin/pkg/deploy/controller/test"
11
+	deployutil "github.com/openshift/origin/pkg/deploy/util"
9 12
 )
10 13
 
11 14
 // Test the controller's response to a new DeploymentConfig with a config change trigger.
... ...
@@ -14,6 +17,7 @@ func TestNewConfigWithoutTrigger(t *testing.T) {
14 14
 	updated := false
15 15
 
16 16
 	controller := &DeploymentConfigChangeController{
17
+		Codec: api.Codec,
17 18
 		ChangeStrategy: &testChangeStrategy{
18 19
 			GenerateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
19 20
 				generated = true
... ...
@@ -48,6 +52,7 @@ func TestNewConfigWithTrigger(t *testing.T) {
48 48
 	)
49 49
 
50 50
 	controller := &DeploymentConfigChangeController{
51
+		Codec: api.Codec,
51 52
 		ChangeStrategy: &testChangeStrategy{
52 53
 			GenerateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
53 54
 				generatedName = name
... ...
@@ -89,6 +94,7 @@ func TestChangeWithTemplateDiff(t *testing.T) {
89 89
 	)
90 90
 
91 91
 	controller := &DeploymentConfigChangeController{
92
+		Codec: api.Codec,
92 93
 		ChangeStrategy: &testChangeStrategy{
93 94
 			GenerateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
94 95
 				generatedName = name
... ...
@@ -102,7 +108,7 @@ func TestChangeWithTemplateDiff(t *testing.T) {
102 102
 		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
103 103
 			return diffedConfig()
104 104
 		},
105
-		DeploymentStore: deploytest.NewFakeDeploymentStore(matchingInitialDeployment()),
105
+		DeploymentStore: deploytest.NewFakeDeploymentStore(matchingInitialDeployment(generatedConfig())),
106 106
 	}
107 107
 
108 108
 	controller.HandleDeploymentConfig()
... ...
@@ -123,14 +129,16 @@ func TestChangeWithTemplateDiff(t *testing.T) {
123 123
 }
124 124
 
125 125
 func TestChangeWithoutTemplateDiff(t *testing.T) {
126
+	config := existingConfigWithTrigger()
126 127
 	generated := false
127 128
 	updated := false
128 129
 
129 130
 	controller := &DeploymentConfigChangeController{
131
+		Codec: api.Codec,
130 132
 		ChangeStrategy: &testChangeStrategy{
131 133
 			GenerateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
132 134
 				generated = true
133
-				return nil, nil
135
+				return config, nil
134 136
 			},
135 137
 			UpdateDeploymentConfigFunc: func(namespace string, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
136 138
 				updated = true
... ...
@@ -138,9 +146,9 @@ func TestChangeWithoutTemplateDiff(t *testing.T) {
138 138
 			},
139 139
 		},
140 140
 		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
141
-			return existingConfigWithTrigger()
141
+			return config
142 142
 		},
143
-		DeploymentStore: deploytest.NewFakeDeploymentStore(matchingInitialDeployment()),
143
+		DeploymentStore: deploytest.NewFakeDeploymentStore(matchingInitialDeployment(config)),
144 144
 	}
145 145
 
146 146
 	controller.HandleDeploymentConfig()
... ...
@@ -291,30 +299,18 @@ func generatedConfig() *deployapi.DeploymentConfig {
291 291
 	return config
292 292
 }
293 293
 
294
-func matchingInitialDeployment() *deployapi.Deployment {
295
-	return &deployapi.Deployment{
296
-		ObjectMeta: kapi.ObjectMeta{Name: "test-deploy-config-1"},
297
-		Status:     deployapi.DeploymentStatusNew,
298
-		ControllerTemplate: kapi.ReplicationControllerSpec{
299
-			Replicas: 1,
300
-			Selector: map[string]string{
301
-				"name": "test-pod",
302
-			},
303
-			Template: &kapi.PodTemplateSpec{
304
-				ObjectMeta: kapi.ObjectMeta{
305
-					Labels: map[string]string{
306
-						"name": "test-pod",
307
-					},
308
-				},
309
-				Spec: kapi.PodSpec{
310
-					Containers: []kapi.Container{
311
-						{
312
-							Name:  "container-1",
313
-							Image: "registry:8080/openshift/test-image:ref-1",
314
-						},
315
-					},
316
-				},
294
+func matchingInitialDeployment(config *deployapi.DeploymentConfig) *kapi.ReplicationController {
295
+	encodedConfig, _ := deployutil.EncodeDeploymentConfig(config, api.Codec)
296
+
297
+	return &kapi.ReplicationController{
298
+		ObjectMeta: kapi.ObjectMeta{
299
+			Name: deployutil.LatestDeploymentIDForConfig(config),
300
+			Annotations: map[string]string{
301
+				deployapi.DeploymentConfigAnnotation:        config.Name,
302
+				deployapi.DeploymentStatusAnnotation:        string(deployapi.DeploymentStatusNew),
303
+				deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
317 304
 			},
318 305
 		},
306
+		Spec: config.Template.ControllerTemplate,
319 307
 	}
320 308
 }
... ...
@@ -1,30 +1,38 @@
1 1
 package controller
2 2
 
3 3
 import (
4
+	"strconv"
5
+
4 6
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
5 7
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
6 9
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
7 10
 	"github.com/golang/glog"
8 11
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
9 12
 	deployutil "github.com/openshift/origin/pkg/deploy/util"
10 13
 )
11 14
 
12
-// DeploymentConfigController is responsible for creating a Deployment when a DeploymentConfig is
15
+// DeploymentConfigController is responsible for creating a deployment when a DeploymentConfig is
13 16
 // updated with a new LatestVersion. Any deployment created is correlated to a DeploymentConfig
14 17
 // by setting the DeploymentConfigLabel on the deployment.
18
+//
19
+// Deployments are represented by ReplicationControllers. The DeploymentConfig used to create the
20
+// ReplicationController is encoded and stored in an annotation on the ReplicationController.
15 21
 type DeploymentConfigController struct {
16 22
 	// DeploymentInterface provides access to Deployments.
17 23
 	DeploymentInterface dccDeploymentInterface
18 24
 	// NextDeploymentConfig blocks until the next DeploymentConfig is available.
19 25
 	NextDeploymentConfig func() *deployapi.DeploymentConfig
20
-	// Stop is an optional channel that controls when the controller exits
26
+	// Codec is used to encode DeploymentConfigs which are stored on deployments.
27
+	Codec runtime.Codec
28
+	// Stop is an optional channel that controls when the controller exits.
21 29
 	Stop <-chan struct{}
22 30
 }
23 31
 
24 32
 // dccDeploymentInterface is a small private interface for dealing with Deployments.
25 33
 type dccDeploymentInterface interface {
26
-	GetDeployment(namespace, name string) (*deployapi.Deployment, error)
27
-	CreateDeployment(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error)
34
+	GetDeployment(namespace, name string) (*kapi.ReplicationController, error)
35
+	CreateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error)
28 36
 }
29 37
 
30 38
 // Process DeploymentConfig events one at a time.
... ...
@@ -80,7 +88,7 @@ func (c *DeploymentConfigController) shouldDeploy(config *deployapi.DeploymentCo
80 80
 }
81 81
 
82 82
 // TODO: reduce code duplication between trigger and config controllers
83
-func (c *DeploymentConfigController) latestDeploymentForConfig(config *deployapi.DeploymentConfig) (*deployapi.Deployment, error) {
83
+func (c *DeploymentConfigController) latestDeploymentForConfig(config *deployapi.DeploymentConfig) (*kapi.ReplicationController, error) {
84 84
 	latestDeploymentID := deployutil.LatestDeploymentIDForConfig(config)
85 85
 	deployment, err := c.DeploymentInterface.GetDeployment(config.Namespace, latestDeploymentID)
86 86
 	if err != nil {
... ...
@@ -93,21 +101,33 @@ func (c *DeploymentConfigController) latestDeploymentForConfig(config *deployapi
93 93
 
94 94
 // deploy performs the work of actually creating a Deployment from the given DeploymentConfig.
95 95
 func (c *DeploymentConfigController) deploy(config *deployapi.DeploymentConfig) error {
96
-	deployment := &deployapi.Deployment{
96
+	var err error
97
+	var encodedConfig string
98
+
99
+	if encodedConfig, err = deployutil.EncodeDeploymentConfig(config, c.Codec); err != nil {
100
+		return err
101
+	}
97 102
 
103
+	deployment := &kapi.ReplicationController{
98 104
 		ObjectMeta: kapi.ObjectMeta{
99 105
 			Name: deployutil.LatestDeploymentIDForConfig(config),
100 106
 			Annotations: map[string]string{
101
-				deployapi.DeploymentConfigAnnotation: config.Name,
107
+				deployapi.DeploymentConfigAnnotation:        config.Name,
108
+				deployapi.DeploymentStatusAnnotation:        string(deployapi.DeploymentStatusNew),
109
+				deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
110
+				deployapi.DeploymentVersionAnnotation:       strconv.Itoa(config.LatestVersion),
102 111
 			},
103 112
 			Labels: config.Labels,
104 113
 		},
105
-		Strategy:           config.Template.Strategy,
106
-		ControllerTemplate: config.Template.ControllerTemplate,
107
-		Details:            config.Details,
114
+		Spec: config.Template.ControllerTemplate,
108 115
 	}
109 116
 
117
+	deployment.Spec.Replicas = 0
118
+	deployment.Spec.Template.Labels[deployapi.DeploymentConfigLabel] = config.Name
119
+	// TODO: Switch this to an annotation once upstream supports annotations on a PodTemplate
120
+	deployment.Spec.Template.Labels[deployapi.DeploymentLabel] = deployment.Name
121
+
110 122
 	glog.V(4).Infof("Creating new deployment from config %s", config.Name)
111
-	_, err := c.DeploymentInterface.CreateDeployment(config.Namespace, deployment)
123
+	_, err = c.DeploymentInterface.CreateDeployment(config.Namespace, deployment)
112 124
 	return err
113 125
 }
... ...
@@ -1,21 +1,26 @@
1 1
 package controller
2 2
 
3 3
 import (
4
+	"strconv"
4 5
 	"testing"
5 6
 
6 7
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7 8
 	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
9
+
10
+	api "github.com/openshift/origin/pkg/api/latest"
8 11
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
12
+	deployutil "github.com/openshift/origin/pkg/deploy/util"
9 13
 )
10 14
 
11 15
 func TestHandleNewDeploymentConfig(t *testing.T) {
12 16
 	controller := &DeploymentConfigController{
17
+		Codec: api.Codec,
13 18
 		DeploymentInterface: &testDeploymentInterface{
14
-			GetDeploymentFunc: func(namespace, name string) (*deployapi.Deployment, error) {
19
+			GetDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
15 20
 				t.Fatalf("unexpected call with name %s", name)
16 21
 				return nil, nil
17 22
 			},
18
-			CreateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
23
+			CreateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
19 24
 				t.Fatalf("unexpected call with deployment %v", deployment)
20 25
 				return nil, nil
21 26
 			},
... ...
@@ -34,14 +39,15 @@ func TestHandleInitialDeployment(t *testing.T) {
34 34
 	deploymentConfig := manualDeploymentConfig()
35 35
 	deploymentConfig.LatestVersion = 1
36 36
 
37
-	var deployed *deployapi.Deployment
37
+	var deployed *kapi.ReplicationController
38 38
 
39 39
 	controller := &DeploymentConfigController{
40
+		Codec: api.Codec,
40 41
 		DeploymentInterface: &testDeploymentInterface{
41
-			GetDeploymentFunc: func(namespace, name string) (*deployapi.Deployment, error) {
42
-				return nil, kerrors.NewNotFound("deployment", name)
42
+			GetDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
43
+				return nil, kerrors.NewNotFound("replicationController", name)
43 44
 			},
44
-			CreateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
45
+			CreateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
45 46
 				deployed = deployment
46 47
 				return deployment, nil
47 48
 			},
... ...
@@ -57,25 +63,41 @@ func TestHandleInitialDeployment(t *testing.T) {
57 57
 		t.Fatalf("expected a deployment")
58 58
 	}
59 59
 
60
-	if e, a := deploymentConfig.Name, deployed.Annotations[deployapi.DeploymentConfigAnnotation]; e != a {
61
-		t.Fatalf("expected deployment with deploymentConfig annotation %s, got %s", e, a)
60
+	expectedAnnotations := map[string]string{
61
+		deployapi.DeploymentConfigAnnotation:  deploymentConfig.Name,
62
+		deployapi.DeploymentStatusAnnotation:  string(deployapi.DeploymentStatusNew),
63
+		deployapi.DeploymentVersionAnnotation: strconv.Itoa(deploymentConfig.LatestVersion),
64
+	}
65
+
66
+	for key, expected := range expectedAnnotations {
67
+		if actual := deployed.Annotations[key]; actual != expected {
68
+			t.Fatalf("expected deployment annotation %s=%s, got %s", key, expected, actual)
69
+		}
70
+	}
71
+
72
+	// TODO: add stronger assertion on the encoded value once the controller methods are free
73
+	// of side effects on the deploymentConfig
74
+	if len(deployed.Annotations[deployapi.DeploymentEncodedConfigAnnotation]) == 0 {
75
+		t.Fatalf("expected deployment with DeploymentEncodedConfigAnnotation annotation")
62 76
 	}
63 77
 }
64 78
 
65 79
 func TestHandleConfigChangeNoPodTemplateDiff(t *testing.T) {
80
+	deploymentConfig := manualDeploymentConfig()
81
+	deploymentConfig.LatestVersion = 0
82
+
66 83
 	controller := &DeploymentConfigController{
84
+		Codec: api.Codec,
67 85
 		DeploymentInterface: &testDeploymentInterface{
68
-			GetDeploymentFunc: func(namespace, name string) (*deployapi.Deployment, error) {
69
-				return matchingDeployment(), nil
86
+			GetDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
87
+				return matchingDeployment(deploymentConfig), nil
70 88
 			},
71
-			CreateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
89
+			CreateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
72 90
 				t.Fatalf("unexpected call to to create deployment: %v", deployment)
73 91
 				return nil, nil
74 92
 			},
75 93
 		},
76 94
 		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
77
-			deploymentConfig := manualDeploymentConfig()
78
-			deploymentConfig.LatestVersion = 0
79 95
 			return deploymentConfig
80 96
 		},
81 97
 	}
... ...
@@ -88,14 +110,15 @@ func TestHandleConfigChangeWithPodTemplateDiff(t *testing.T) {
88 88
 	deploymentConfig.LatestVersion = 2
89 89
 	deploymentConfig.Template.ControllerTemplate.Template.Labels["foo"] = "bar"
90 90
 
91
-	var deployed *deployapi.Deployment
91
+	var deployed *kapi.ReplicationController
92 92
 
93 93
 	controller := &DeploymentConfigController{
94
+		Codec: api.Codec,
94 95
 		DeploymentInterface: &testDeploymentInterface{
95
-			GetDeploymentFunc: func(namespace, name string) (*deployapi.Deployment, error) {
96
+			GetDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
96 97
 				return nil, kerrors.NewNotFound("deployment", name)
97 98
 			},
98
-			CreateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
99
+			CreateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
99 100
 				deployed = deployment
100 101
 				return deployment, nil
101 102
 			},
... ...
@@ -117,15 +140,15 @@ func TestHandleConfigChangeWithPodTemplateDiff(t *testing.T) {
117 117
 }
118 118
 
119 119
 type testDeploymentInterface struct {
120
-	GetDeploymentFunc    func(namespace, name string) (*deployapi.Deployment, error)
121
-	CreateDeploymentFunc func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error)
120
+	GetDeploymentFunc    func(namespace, name string) (*kapi.ReplicationController, error)
121
+	CreateDeploymentFunc func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error)
122 122
 }
123 123
 
124
-func (i *testDeploymentInterface) GetDeployment(namespace, name string) (*deployapi.Deployment, error) {
124
+func (i *testDeploymentInterface) GetDeployment(namespace, name string) (*kapi.ReplicationController, error) {
125 125
 	return i.GetDeploymentFunc(namespace, name)
126 126
 }
127 127
 
128
-func (i *testDeploymentInterface) CreateDeployment(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
128
+func (i *testDeploymentInterface) CreateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
129 129
 	return i.CreateDeploymentFunc(namespace, deployment)
130 130
 }
131 131
 
... ...
@@ -166,14 +189,18 @@ func manualDeploymentConfig() *deployapi.DeploymentConfig {
166 166
 	}
167 167
 }
168 168
 
169
-func matchingDeployment() *deployapi.Deployment {
170
-	return &deployapi.Deployment{
171
-		ObjectMeta: kapi.ObjectMeta{Name: "manual-deploy-config-1"},
172
-		Status:     deployapi.DeploymentStatusNew,
173
-		Strategy: deployapi.DeploymentStrategy{
174
-			Type: deployapi.DeploymentStrategyTypeRecreate,
169
+func matchingDeployment(config *deployapi.DeploymentConfig) *kapi.ReplicationController {
170
+	encodedConfig, _ := deployutil.EncodeDeploymentConfig(config, api.Codec)
171
+	return &kapi.ReplicationController{
172
+		ObjectMeta: kapi.ObjectMeta{
173
+			Name: deployutil.LatestDeploymentIDForConfig(config),
174
+			Annotations: map[string]string{
175
+				deployapi.DeploymentConfigAnnotation:        config.Name,
176
+				deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
177
+			},
178
+			Labels: config.Labels,
175 179
 		},
176
-		ControllerTemplate: kapi.ReplicationControllerSpec{
180
+		Spec: kapi.ReplicationControllerSpec{
177 181
 			Replicas: 1,
178 182
 			Selector: map[string]string{
179 183
 				"name": "test-pod",
... ...
@@ -6,13 +6,17 @@ import (
6 6
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7 7
 	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8 8
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
9 10
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
10 11
 
11 12
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
13
+	deployutil "github.com/openshift/origin/pkg/deploy/util"
12 14
 )
13 15
 
14 16
 // DeploymentController performs a deployment by creating a pod which is defined by a strategy.
15
-// The status of the resulting Deployment will follow the status of the corresponding pod.
17
+// The status of the resulting deployment will follow the status of the corresponding pod.
18
+//
19
+// Deployments are represented by a ReplicationController.
16 20
 type DeploymentController struct {
17 21
 	// ContainerCreator makes the container for the deployment pod based on the strategy.
18 22
 	ContainerCreator DeploymentContainerCreator
... ...
@@ -21,7 +25,7 @@ type DeploymentController struct {
21 21
 	// PodInterface provides access to pods.
22 22
 	PodInterface dcPodInterface
23 23
 	// NextDeployment blocks until the next deployment is available.
24
-	NextDeployment func() *deployapi.Deployment
24
+	NextDeployment func() *kapi.ReplicationController
25 25
 	// NextPod blocks until the next pod is available.
26 26
 	NextPod func() *kapi.Pod
27 27
 	// DeploymentStore is a cache of deployments.
... ...
@@ -31,7 +35,9 @@ type DeploymentController struct {
31 31
 	Environment []kapi.EnvVar
32 32
 	// UseLocalImages configures the ImagePullPolicy for containers in the deployment pod.
33 33
 	UseLocalImages bool
34
-	// Stop is an optional channel that controls when the controller exits
34
+	// Codec is used to decode DeploymentConfigs.
35
+	Codec runtime.Codec
36
+	// Stop is an optional channel that controls when the controller exits.
35 37
 	Stop <-chan struct{}
36 38
 }
37 39
 
... ...
@@ -42,7 +48,7 @@ type DeploymentContainerCreator interface {
42 42
 }
43 43
 
44 44
 type dcDeploymentInterface interface {
45
-	UpdateDeployment(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error)
45
+	UpdateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error)
46 46
 }
47 47
 
48 48
 type dcPodInterface interface {
... ...
@@ -56,42 +62,43 @@ func (dc *DeploymentController) Run() {
56 56
 	go util.Until(func() { dc.HandlePod() }, 0, dc.Stop)
57 57
 }
58 58
 
59
-// HandleDeployment processes a new Deployment and creates a new Pod which implements the specific
59
+// HandleDeployment processes a new deployment and creates a new Pod which implements the specific
60 60
 // deployment behavior. The deployment and pod are correlated with annotations. If the pod was
61 61
 // successfully created, the deployment's status is transitioned to pending; otherwise, the status
62 62
 // is transitioned to failed.
63 63
 func (dc *DeploymentController) HandleDeployment() {
64 64
 	deployment := dc.NextDeployment()
65 65
 
66
-	if deployment.Status != deployapi.DeploymentStatusNew {
66
+	if deployment.Annotations[deployapi.DeploymentStatusAnnotation] != string(deployapi.DeploymentStatusNew) {
67 67
 		glog.V(4).Infof("Ignoring deployment %s with non-New status", deployment.Name)
68 68
 		return
69 69
 	}
70 70
 
71
-	deploymentPod := dc.makeDeploymentPod(deployment)
71
+	// TODO: transition to a failed state? seems like yes since this is probably not recoverable
72
+	var deploymentPod *kapi.Pod
73
+	var deploymentPodError error
74
+	if deploymentPod, deploymentPodError = dc.makeDeploymentPod(deployment); deploymentPodError != nil {
75
+		glog.V(0).Infof("Failed to make deployment pod for %s: %v", deployment.Name, deploymentPodError)
76
+		return
77
+	}
72 78
 
73
-	nextStatus := deployment.Status
79
+	nextStatus := deployment.Annotations[deployapi.DeploymentStatusAnnotation]
74 80
 	if pod, err := dc.PodInterface.CreatePod(deployment.Namespace, deploymentPod); err != nil {
75 81
 		// If the pod already exists, it's possible that a previous CreatePod succeeded but
76 82
 		// the deployment state update failed and now we're re-entering.
77 83
 		if kerrors.IsAlreadyExists(err) {
78
-			nextStatus = deployapi.DeploymentStatusPending
84
+			nextStatus = string(deployapi.DeploymentStatusPending)
79 85
 		} else {
80 86
 			glog.Infof("Error creating pod for deployment %s: %v", deployment.Name, err)
81
-			nextStatus = deployapi.DeploymentStatusFailed
87
+			nextStatus = string(deployapi.DeploymentStatusFailed)
82 88
 		}
83 89
 	} else {
84 90
 		glog.V(2).Infof("Created pod %s for deployment %s", pod.Name, deployment.Name)
85
-
86
-		if deployment.Annotations == nil {
87
-			deployment.Annotations = make(map[string]string)
88
-		}
89 91
 		deployment.Annotations[deployapi.DeploymentPodAnnotation] = pod.Name
90
-
91
-		nextStatus = deployapi.DeploymentStatusPending
92
+		nextStatus = string(deployapi.DeploymentStatusPending)
92 93
 	}
93 94
 
94
-	deployment.Status = nextStatus
95
+	deployment.Annotations[deployapi.DeploymentStatusAnnotation] = nextStatus
95 96
 
96 97
 	glog.V(2).Infof("Updating deployment %s status %s -> %s", deployment.Name, deployment.Status, nextStatus)
97 98
 	if _, err := dc.DeploymentInterface.UpdateDeployment(deployment.Namespace, deployment); err != nil {
... ...
@@ -117,23 +124,23 @@ func (dc *DeploymentController) HandlePod() {
117 117
 		return
118 118
 	}
119 119
 
120
-	deployment := deploymentObj.(*deployapi.Deployment)
121
-	nextDeploymentStatus := deployment.Status
120
+	deployment := deploymentObj.(*kapi.ReplicationController)
121
+	nextDeploymentStatus := deployment.Annotations[deployapi.DeploymentStatusAnnotation]
122 122
 
123 123
 	switch pod.Status.Phase {
124 124
 	case kapi.PodRunning:
125
-		nextDeploymentStatus = deployapi.DeploymentStatusRunning
125
+		nextDeploymentStatus = string(deployapi.DeploymentStatusRunning)
126 126
 	case kapi.PodSucceeded, kapi.PodFailed:
127
-		nextDeploymentStatus = deployapi.DeploymentStatusComplete
127
+		nextDeploymentStatus = string(deployapi.DeploymentStatusComplete)
128 128
 		// Detect failure based on the container state
129 129
 		for _, info := range pod.Status.Info {
130 130
 			if info.State.Termination != nil && info.State.Termination.ExitCode != 0 {
131
-				nextDeploymentStatus = deployapi.DeploymentStatusFailed
131
+				nextDeploymentStatus = string(deployapi.DeploymentStatusFailed)
132 132
 			}
133 133
 		}
134 134
 
135 135
 		// Automatically clean up successful pods
136
-		if nextDeploymentStatus == deployapi.DeploymentStatusComplete {
136
+		if nextDeploymentStatus == string(deployapi.DeploymentStatusComplete) {
137 137
 			if err := dc.PodInterface.DeletePod(deployment.Namespace, pod.Name); err != nil {
138 138
 				glog.V(4).Infof("Couldn't delete completed pod %s for deployment %s: %#v", pod.Name, deployment.Name, err)
139 139
 			} else {
... ...
@@ -142,9 +149,9 @@ func (dc *DeploymentController) HandlePod() {
142 142
 		}
143 143
 	}
144 144
 
145
-	if deployment.Status != nextDeploymentStatus {
146
-		glog.V(2).Infof("Updating deployment %s status %s -> %s", deployment.Name, deployment.Status, nextDeploymentStatus)
147
-		deployment.Status = nextDeploymentStatus
145
+	if deployment.Annotations[deployapi.DeploymentStatusAnnotation] != nextDeploymentStatus {
146
+		glog.V(2).Infof("Updating deployment %s status %s -> %s", deployment.Name, deployment.Annotations[deployapi.DeploymentStatusAnnotation], nextDeploymentStatus)
147
+		deployment.Annotations[deployapi.DeploymentStatusAnnotation] = nextDeploymentStatus
148 148
 		if _, err := dc.DeploymentInterface.UpdateDeployment(pod.Namespace, deployment); err != nil {
149 149
 			glog.V(2).Infof("Failed to update deployment %v: %v", deployment.Name, err)
150 150
 		}
... ...
@@ -153,8 +160,14 @@ func (dc *DeploymentController) HandlePod() {
153 153
 
154 154
 // makeDeploymentPod creates a pod which implements deployment behavior. The pod is correlated to
155 155
 // the deployment with an annotation.
156
-func (dc *DeploymentController) makeDeploymentPod(deployment *deployapi.Deployment) *kapi.Pod {
157
-	container := dc.ContainerCreator.CreateContainer(&deployment.Strategy)
156
+func (dc *DeploymentController) makeDeploymentPod(deployment *kapi.ReplicationController) (*kapi.Pod, error) {
157
+	var deploymentConfig *deployapi.DeploymentConfig
158
+	var decodeError error
159
+	if deploymentConfig, decodeError = deployutil.DecodeDeploymentConfig(deployment, dc.Codec); decodeError != nil {
160
+		return nil, decodeError
161
+	}
162
+
163
+	container := dc.ContainerCreator.CreateContainer(&deploymentConfig.Template.Strategy)
158 164
 
159 165
 	// Combine the container environment, controller environment, and deployment values into
160 166
 	// the pod's environment.
... ...
@@ -190,5 +203,5 @@ func (dc *DeploymentController) makeDeploymentPod(deployment *deployapi.Deployme
190 190
 		pod.Spec.Containers[0].ImagePullPolicy = kapi.PullIfNotPresent
191 191
 	}
192 192
 
193
-	return pod
193
+	return pod, nil
194 194
 }
... ...
@@ -7,20 +7,23 @@ import (
7 7
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
8 8
 	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
9 9
 
10
+	api "github.com/openshift/origin/pkg/api/latest"
10 11
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
11 12
 	deploytest "github.com/openshift/origin/pkg/deploy/controller/test"
13
+	deployutil "github.com/openshift/origin/pkg/deploy/util"
12 14
 )
13 15
 
14 16
 func TestHandleNewDeploymentCreatePodOk(t *testing.T) {
15 17
 	var (
16
-		updatedDeployment *deployapi.Deployment
18
+		updatedDeployment *kapi.ReplicationController
17 19
 		createdPod        *kapi.Pod
18 20
 		expectedContainer = basicContainer()
19 21
 	)
20 22
 
21 23
 	controller := &DeploymentController{
24
+		Codec: api.Codec,
22 25
 		DeploymentInterface: &testDcDeploymentInterface{
23
-			UpdateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
26
+			UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
24 27
 				updatedDeployment = deployment
25 28
 				return updatedDeployment, nil
26 29
 			},
... ...
@@ -31,9 +34,9 @@ func TestHandleNewDeploymentCreatePodOk(t *testing.T) {
31 31
 				return pod, nil
32 32
 			},
33 33
 		},
34
-		NextDeployment: func() *deployapi.Deployment {
34
+		NextDeployment: func() *kapi.ReplicationController {
35 35
 			deployment := basicDeployment()
36
-			deployment.Status = deployapi.DeploymentStatusNew
36
+			deployment.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusNew)
37 37
 			return deployment
38 38
 		},
39 39
 		ContainerCreator: &testContainerCreator{
... ...
@@ -50,7 +53,7 @@ func TestHandleNewDeploymentCreatePodOk(t *testing.T) {
50 50
 		t.Fatalf("expected an updated deployment")
51 51
 	}
52 52
 
53
-	if e, a := deployapi.DeploymentStatusPending, updatedDeployment.Status; e != a {
53
+	if e, a := string(deployapi.DeploymentStatusPending), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a {
54 54
 		t.Fatalf("expected updated deployment status %s, got %s", e, a)
55 55
 	}
56 56
 
... ...
@@ -86,11 +89,12 @@ func TestHandleNewDeploymentCreatePodOk(t *testing.T) {
86 86
 }
87 87
 
88 88
 func TestHandleNewDeploymentCreatePodFail(t *testing.T) {
89
-	var updatedDeployment *deployapi.Deployment
89
+	var updatedDeployment *kapi.ReplicationController
90 90
 
91 91
 	controller := &DeploymentController{
92
+		Codec: api.Codec,
92 93
 		DeploymentInterface: &testDcDeploymentInterface{
93
-			UpdateDeploymentFunc: func(namspace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
94
+			UpdateDeploymentFunc: func(namspace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
94 95
 				updatedDeployment = deployment
95 96
 				return updatedDeployment, nil
96 97
 			},
... ...
@@ -100,9 +104,9 @@ func TestHandleNewDeploymentCreatePodFail(t *testing.T) {
100 100
 				return nil, fmt.Errorf("Failed to create pod %s", pod.Name)
101 101
 			},
102 102
 		},
103
-		NextDeployment: func() *deployapi.Deployment {
103
+		NextDeployment: func() *kapi.ReplicationController {
104 104
 			deployment := basicDeployment()
105
-			deployment.Status = deployapi.DeploymentStatusNew
105
+			deployment.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusNew)
106 106
 			return deployment
107 107
 		},
108 108
 		ContainerCreator: &testContainerCreator{
... ...
@@ -119,17 +123,18 @@ func TestHandleNewDeploymentCreatePodFail(t *testing.T) {
119 119
 		t.Fatalf("expected an updated deployment")
120 120
 	}
121 121
 
122
-	if e, a := deployapi.DeploymentStatusFailed, updatedDeployment.Status; e != a {
122
+	if e, a := string(deployapi.DeploymentStatusFailed), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a {
123 123
 		t.Fatalf("expected updated deployment status %s, got %s", e, a)
124 124
 	}
125 125
 }
126 126
 
127 127
 func TestHandleNewDeploymentCreatePodAlreadyExists(t *testing.T) {
128
-	var updatedDeployment *deployapi.Deployment
128
+	var updatedDeployment *kapi.ReplicationController
129 129
 
130 130
 	controller := &DeploymentController{
131
+		Codec: api.Codec,
131 132
 		DeploymentInterface: &testDcDeploymentInterface{
132
-			UpdateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
133
+			UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
133 134
 				updatedDeployment = deployment
134 135
 				return updatedDeployment, nil
135 136
 			},
... ...
@@ -139,9 +144,9 @@ func TestHandleNewDeploymentCreatePodAlreadyExists(t *testing.T) {
139 139
 				return nil, kerrors.NewAlreadyExists("pod", pod.Name)
140 140
 			},
141 141
 		},
142
-		NextDeployment: func() *deployapi.Deployment {
142
+		NextDeployment: func() *kapi.ReplicationController {
143 143
 			deployment := basicDeployment()
144
-			deployment.Status = deployapi.DeploymentStatusNew
144
+			deployment.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusNew)
145 145
 			return deployment
146 146
 		},
147 147
 		ContainerCreator: &testContainerCreator{
... ...
@@ -158,21 +163,22 @@ func TestHandleNewDeploymentCreatePodAlreadyExists(t *testing.T) {
158 158
 		t.Fatalf("expected an updated deployment")
159 159
 	}
160 160
 
161
-	if e, a := deployapi.DeploymentStatusPending, updatedDeployment.Status; e != a {
161
+	if e, a := string(deployapi.DeploymentStatusPending), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a {
162 162
 		t.Fatalf("expected updated deployment status %s, got %s", e, a)
163 163
 	}
164 164
 }
165 165
 
166 166
 func TestHandleUncorrelatedPod(t *testing.T) {
167 167
 	controller := &DeploymentController{
168
+		Codec: api.Codec,
168 169
 		DeploymentInterface: &testDcDeploymentInterface{
169
-			UpdateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
170
+			UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
170 171
 				t.Fatalf("Unexpected deployment update")
171 172
 				return nil, nil
172 173
 			},
173 174
 		},
174 175
 		PodInterface:   &testDcPodInterface{},
175
-		NextDeployment: func() *deployapi.Deployment { return nil },
176
+		NextDeployment: func() *kapi.ReplicationController { return nil },
176 177
 		NextPod: func() *kapi.Pod {
177 178
 			pod := runningPod()
178 179
 			pod.Annotations = make(map[string]string)
... ...
@@ -187,14 +193,15 @@ func TestHandleUncorrelatedPod(t *testing.T) {
187 187
 
188 188
 func TestHandleOrphanedPod(t *testing.T) {
189 189
 	controller := &DeploymentController{
190
+		Codec: api.Codec,
190 191
 		DeploymentInterface: &testDcDeploymentInterface{
191
-			UpdateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
192
+			UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
192 193
 				t.Fatalf("Unexpected deployment update")
193 194
 				return nil, nil
194 195
 			},
195 196
 		},
196 197
 		PodInterface:    &testDcPodInterface{},
197
-		NextDeployment:  func() *deployapi.Deployment { return nil },
198
+		NextDeployment:  func() *kapi.ReplicationController { return nil },
198 199
 		NextPod:         func() *kapi.Pod { return runningPod() },
199 200
 		DeploymentStore: deploytest.NewFakeDeploymentStore(nil),
200 201
 	}
... ...
@@ -204,17 +211,18 @@ func TestHandleOrphanedPod(t *testing.T) {
204 204
 }
205 205
 
206 206
 func TestHandlePodRunning(t *testing.T) {
207
-	var updatedDeployment *deployapi.Deployment
207
+	var updatedDeployment *kapi.ReplicationController
208 208
 
209 209
 	controller := &DeploymentController{
210
+		Codec: api.Codec,
210 211
 		DeploymentInterface: &testDcDeploymentInterface{
211
-			UpdateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
212
+			UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
212 213
 				updatedDeployment = deployment
213 214
 				return deployment, nil
214 215
 			},
215 216
 		},
216 217
 		PodInterface: &testDcPodInterface{},
217
-		NextDeployment: func() *deployapi.Deployment {
218
+		NextDeployment: func() *kapi.ReplicationController {
218 219
 			return nil
219 220
 		},
220 221
 		NextPod:         func() *kapi.Pod { return runningPod() },
... ...
@@ -227,18 +235,19 @@ func TestHandlePodRunning(t *testing.T) {
227 227
 		t.Fatalf("Expected a deployment to be updated")
228 228
 	}
229 229
 
230
-	if e, a := deployapi.DeploymentStatusRunning, updatedDeployment.Status; e != a {
230
+	if e, a := string(deployapi.DeploymentStatusRunning), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a {
231 231
 		t.Fatalf("expected updated deployment status %s, got %s", e, a)
232 232
 	}
233 233
 }
234 234
 
235 235
 func TestHandlePodTerminatedOk(t *testing.T) {
236
-	var updatedDeployment *deployapi.Deployment
236
+	var updatedDeployment *kapi.ReplicationController
237 237
 	var deletedPodID string
238 238
 
239 239
 	controller := &DeploymentController{
240
+		Codec: api.Codec,
240 241
 		DeploymentInterface: &testDcDeploymentInterface{
241
-			UpdateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
242
+			UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
242 243
 				updatedDeployment = deployment
243 244
 				return deployment, nil
244 245
 			},
... ...
@@ -249,7 +258,7 @@ func TestHandlePodTerminatedOk(t *testing.T) {
249 249
 				return nil
250 250
 			},
251 251
 		},
252
-		NextDeployment:  func() *deployapi.Deployment { return nil },
252
+		NextDeployment:  func() *kapi.ReplicationController { return nil },
253 253
 		NextPod:         func() *kapi.Pod { return succeededPod() },
254 254
 		DeploymentStore: deploytest.NewFakeDeploymentStore(runningDeployment()),
255 255
 	}
... ...
@@ -260,7 +269,7 @@ func TestHandlePodTerminatedOk(t *testing.T) {
260 260
 		t.Fatalf("Expected a deployment to be updated")
261 261
 	}
262 262
 
263
-	if e, a := deployapi.DeploymentStatusComplete, updatedDeployment.Status; e != a {
263
+	if e, a := string(deployapi.DeploymentStatusComplete), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a {
264 264
 		t.Fatalf("expected updated deployment status %s, got %s", e, a)
265 265
 	}
266 266
 
... ...
@@ -270,11 +279,12 @@ func TestHandlePodTerminatedOk(t *testing.T) {
270 270
 }
271 271
 
272 272
 func TestHandlePodTerminatedNotOk(t *testing.T) {
273
-	var updatedDeployment *deployapi.Deployment
273
+	var updatedDeployment *kapi.ReplicationController
274 274
 
275 275
 	controller := &DeploymentController{
276
+		Codec: api.Codec,
276 277
 		DeploymentInterface: &testDcDeploymentInterface{
277
-			UpdateDeploymentFunc: func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
278
+			UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
278 279
 				updatedDeployment = deployment
279 280
 				return deployment, nil
280 281
 			},
... ...
@@ -290,7 +300,7 @@ func TestHandlePodTerminatedNotOk(t *testing.T) {
290 290
 				return basicContainer()
291 291
 			},
292 292
 		},
293
-		NextDeployment:  func() *deployapi.Deployment { return nil },
293
+		NextDeployment:  func() *kapi.ReplicationController { return nil },
294 294
 		NextPod:         func() *kapi.Pod { return failedPod() },
295 295
 		DeploymentStore: deploytest.NewFakeDeploymentStore(runningDeployment()),
296 296
 	}
... ...
@@ -301,7 +311,7 @@ func TestHandlePodTerminatedNotOk(t *testing.T) {
301 301
 		t.Fatalf("Expected a deployment to be updated")
302 302
 	}
303 303
 
304
-	if e, a := deployapi.DeploymentStatusFailed, updatedDeployment.Status; e != a {
304
+	if e, a := string(deployapi.DeploymentStatusFailed), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a {
305 305
 		t.Fatalf("expected updated deployment status %s, got %s", e, a)
306 306
 	}
307 307
 }
... ...
@@ -315,10 +325,10 @@ func (t *testContainerCreator) CreateContainer(strategy *deployapi.DeploymentStr
315 315
 }
316 316
 
317 317
 type testDcDeploymentInterface struct {
318
-	UpdateDeploymentFunc func(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error)
318
+	UpdateDeploymentFunc func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error)
319 319
 }
320 320
 
321
-func (i *testDcDeploymentInterface) UpdateDeployment(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
321
+func (i *testDcDeploymentInterface) UpdateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
322 322
 	return i.UpdateDeploymentFunc(namespace, deployment)
323 323
 }
324 324
 
... ...
@@ -335,14 +345,57 @@ func (i *testDcPodInterface) DeletePod(namespace, name string) error {
335 335
 	return i.DeletePodFunc(namespace, name)
336 336
 }
337 337
 
338
-func basicDeployment() *deployapi.Deployment {
339
-	return &deployapi.Deployment{
338
+func basicDeploymentConfig() *deployapi.DeploymentConfig {
339
+	return &deployapi.DeploymentConfig{
340 340
 		ObjectMeta: kapi.ObjectMeta{Name: "deploy1"},
341
-		Status:     deployapi.DeploymentStatusNew,
342
-		Strategy: deployapi.DeploymentStrategy{
343
-			Type: deployapi.DeploymentStrategyTypeRecreate,
341
+		Triggers: []deployapi.DeploymentTriggerPolicy{
342
+			{
343
+				Type: deployapi.DeploymentTriggerManual,
344
+			},
345
+		},
346
+		Template: deployapi.DeploymentTemplate{
347
+			Strategy: deployapi.DeploymentStrategy{
348
+				Type: deployapi.DeploymentStrategyTypeRecreate,
349
+			},
350
+			ControllerTemplate: kapi.ReplicationControllerSpec{
351
+				Replicas: 1,
352
+				Selector: map[string]string{
353
+					"name": "test-pod",
354
+				},
355
+				Template: &kapi.PodTemplateSpec{
356
+					ObjectMeta: kapi.ObjectMeta{
357
+						Labels: map[string]string{
358
+							"name": "test-pod",
359
+						},
360
+					},
361
+					Spec: kapi.PodSpec{
362
+						Containers: []kapi.Container{
363
+							{
364
+								Name:  "container-1",
365
+								Image: "registry:8080/openshift/test-image:ref-1",
366
+							},
367
+						},
368
+					},
369
+				},
370
+			},
371
+		},
372
+	}
373
+}
374
+
375
+func basicDeployment() *kapi.ReplicationController {
376
+	config := basicDeploymentConfig()
377
+	encodedConfig, _ := deployutil.EncodeDeploymentConfig(config, api.Codec)
378
+	return &kapi.ReplicationController{
379
+		ObjectMeta: kapi.ObjectMeta{
380
+			Name: "deploy1",
381
+			Annotations: map[string]string{
382
+				deployapi.DeploymentConfigAnnotation:        config.Name,
383
+				deployapi.DeploymentStatusAnnotation:        string(deployapi.DeploymentStatusNew),
384
+				deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
385
+			},
386
+			Labels: config.Labels,
344 387
 		},
345
-		ControllerTemplate: kapi.ReplicationControllerSpec{
388
+		Spec: kapi.ReplicationControllerSpec{
346 389
 			Template: &kapi.PodTemplateSpec{
347 390
 				Spec: kapi.PodSpec{
348 391
 					Containers: []kapi.Container{
... ...
@@ -357,15 +410,15 @@ func basicDeployment() *deployapi.Deployment {
357 357
 	}
358 358
 }
359 359
 
360
-func pendingDeployment() *deployapi.Deployment {
360
+func pendingDeployment() *kapi.ReplicationController {
361 361
 	d := basicDeployment()
362
-	d.Status = deployapi.DeploymentStatusPending
362
+	d.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusPending)
363 363
 	return d
364 364
 }
365 365
 
366
-func runningDeployment() *deployapi.Deployment {
366
+func runningDeployment() *kapi.ReplicationController {
367 367
 	d := basicDeployment()
368
-	d.Status = deployapi.DeploymentStatusRunning
368
+	d.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusRunning)
369 369
 	return d
370 370
 }
371 371
 
... ...
@@ -21,8 +21,10 @@ import (
21 21
 // DeploymentConfigControllerFactory can create a DeploymentConfigController which obtains
22 22
 // DeploymentConfigs from a queue populated from a watch of all DeploymentConfigs.
23 23
 type DeploymentConfigControllerFactory struct {
24
-	Client *osclient.Client
25
-	Stop   <-chan struct{}
24
+	Client     *osclient.Client
25
+	KubeClient kclient.Interface
26
+	Codec      runtime.Codec
27
+	Stop       <-chan struct{}
26 28
 }
27 29
 
28 30
 func (factory *DeploymentConfigControllerFactory) Create() *controller.DeploymentConfigController {
... ...
@@ -30,13 +32,14 @@ func (factory *DeploymentConfigControllerFactory) Create() *controller.Deploymen
30 30
 	cache.NewReflector(&deploymentConfigLW{factory.Client}, &deployapi.DeploymentConfig{}, queue).Run()
31 31
 
32 32
 	return &controller.DeploymentConfigController{
33
-		DeploymentInterface: ClientDeploymentInterface{factory.Client},
33
+		DeploymentInterface: &ClientDeploymentInterace{factory.KubeClient},
34 34
 		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
35 35
 			config := queue.Pop().(*deployapi.DeploymentConfig)
36 36
 			panicIfStopped(factory.Stop, "deployment config controller stopped")
37 37
 			return config
38 38
 		},
39
-		Stop: factory.Stop,
39
+		Codec: factory.Codec,
40
+		Stop:  factory.Stop,
40 41
 	}
41 42
 }
42 43
 
... ...
@@ -54,6 +57,8 @@ type DeploymentControllerFactory struct {
54 54
 	UseLocalImages bool
55 55
 	// RecreateStrategyImage specifies which Docker image which should implement the Recreate strategy.
56 56
 	RecreateStrategyImage string
57
+	// Codec is used to decode DeploymentConfigs.
58
+	Codec runtime.Codec
57 59
 	// Stop may be set to allow controllers created by this factory to be terminated.
58 60
 	Stop <-chan struct{}
59 61
 
... ...
@@ -63,10 +68,10 @@ type DeploymentControllerFactory struct {
63 63
 
64 64
 func (factory *DeploymentControllerFactory) Create() *controller.DeploymentController {
65 65
 	deploymentQueue := cache.NewFIFO()
66
-	cache.NewReflector(&deploymentLW{client: factory.Client, field: labels.Everything()}, &deployapi.Deployment{}, deploymentQueue).Run()
66
+	cache.NewReflector(&deploymentLW{client: factory.KubeClient, field: labels.Everything()}, &kapi.ReplicationController{}, deploymentQueue).Run()
67 67
 
68 68
 	factory.deploymentStore = cache.NewStore()
69
-	cache.NewReflector(&deploymentLW{client: factory.Client, field: labels.Everything()}, &deployapi.Deployment{}, factory.deploymentStore).Run()
69
+	cache.NewReflector(&deploymentLW{client: factory.KubeClient, field: labels.Everything()}, &kapi.ReplicationController{}, factory.deploymentStore).Run()
70 70
 
71 71
 	// Kubernetes does not currently synchronize Pod status in storage with a Pod's container
72 72
 	// states. Because of this, we can't receive events related to container (and thus Pod)
... ...
@@ -81,11 +86,11 @@ func (factory *DeploymentControllerFactory) Create() *controller.DeploymentContr
81 81
 
82 82
 	return &controller.DeploymentController{
83 83
 		ContainerCreator:    factory,
84
-		DeploymentInterface: ClientDeploymentInterface{factory.Client},
84
+		DeploymentInterface: &ClientDeploymentInterace{factory.KubeClient},
85 85
 		PodInterface:        &DeploymentControllerPodInterface{factory.KubeClient},
86 86
 		Environment:         factory.Environment,
87
-		NextDeployment: func() *deployapi.Deployment {
88
-			deployment := deploymentQueue.Pop().(*deployapi.Deployment)
87
+		NextDeployment: func() *kapi.ReplicationController {
88
+			deployment := deploymentQueue.Pop().(*kapi.ReplicationController)
89 89
 			panicIfStopped(factory.Stop, "deployment controller stopped")
90 90
 			return deployment
91 91
 		},
... ...
@@ -96,6 +101,7 @@ func (factory *DeploymentControllerFactory) Create() *controller.DeploymentContr
96 96
 		},
97 97
 		DeploymentStore: factory.deploymentStore,
98 98
 		UseLocalImages:  factory.UseLocalImages,
99
+		Codec:           factory.Codec,
99 100
 		Stop:            factory.Stop,
100 101
 	}
101 102
 }
... ...
@@ -129,9 +135,9 @@ func (factory *DeploymentControllerFactory) pollPods() (cache.Enumerator, error)
129 129
 	list := &kapi.PodList{}
130 130
 
131 131
 	for _, obj := range factory.deploymentStore.List() {
132
-		deployment := obj.(*deployapi.Deployment)
132
+		deployment := obj.(*kapi.ReplicationController)
133 133
 
134
-		switch deployment.Status {
134
+		switch deployapi.DeploymentStatus(deployment.Annotations[deployapi.DeploymentStatusAnnotation]) {
135 135
 		case deployapi.DeploymentStatusPending, deployapi.DeploymentStatusRunning:
136 136
 			// Validate the correlating pod annotation
137 137
 			podID, hasPodID := deployment.Annotations[deployapi.DeploymentPodAnnotation]
... ...
@@ -186,7 +192,9 @@ func (pe *podEnumerator) Get(index int) (string, interface{}) {
186 186
 // DeploymentConfigChangeControllerFactory can create a DeploymentConfigChangeController which obtains DeploymentConfigs
187 187
 // from a queue populated from a watch of all DeploymentConfigs.
188 188
 type DeploymentConfigChangeControllerFactory struct {
189
-	Client osclient.Interface
189
+	Client     osclient.Interface
190
+	KubeClient kclient.Interface
191
+	Codec      runtime.Codec
190 192
 	// Stop may be set to allow controllers created by this factory to be terminated.
191 193
 	Stop <-chan struct{}
192 194
 }
... ...
@@ -196,16 +204,17 @@ func (factory *DeploymentConfigChangeControllerFactory) Create() *controller.Dep
196 196
 	cache.NewReflector(&deploymentConfigLW{factory.Client}, &deployapi.DeploymentConfig{}, queue).Run()
197 197
 
198 198
 	store := cache.NewStore()
199
-	cache.NewReflector(&deploymentLW{client: factory.Client, field: labels.Everything()}, &deployapi.Deployment{}, store).Run()
199
+	cache.NewReflector(&deploymentLW{client: factory.KubeClient, field: labels.Everything()}, &kapi.ReplicationController{}, store).Run()
200 200
 
201 201
 	return &controller.DeploymentConfigChangeController{
202
-		ChangeStrategy: ClientDeploymentConfigInterface{factory.Client},
202
+		ChangeStrategy: &ClientDeploymentConfigInterface{factory.Client},
203 203
 		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
204 204
 			config := queue.Pop().(*deployapi.DeploymentConfig)
205 205
 			panicIfStopped(factory.Stop, "deployment config change controller stopped")
206 206
 			return config
207 207
 		},
208 208
 		DeploymentStore: store,
209
+		Codec:           factory.Codec,
209 210
 		Stop:            factory.Stop,
210 211
 	}
211 212
 }
... ...
@@ -226,7 +235,7 @@ func (factory *ImageChangeControllerFactory) Create() *controller.ImageChangeCon
226 226
 	cache.NewReflector(&deploymentConfigLW{factory.Client}, &deployapi.DeploymentConfig{}, store).Run()
227 227
 
228 228
 	return &controller.ImageChangeController{
229
-		DeploymentConfigInterface: ClientDeploymentConfigInterface{factory.Client},
229
+		DeploymentConfigInterface: &ClientDeploymentConfigInterface{factory.Client},
230 230
 		DeploymentConfigStore:     store,
231 231
 		NextImageRepository: func() *imageapi.ImageRepository {
232 232
 			repo := queue.Pop().(*imageapi.ImageRepository)
... ...
@@ -248,18 +257,18 @@ func panicIfStopped(ch <-chan struct{}, message interface{}) {
248 248
 
249 249
 // deploymentLW is a ListWatcher implementation for Deployments.
250 250
 type deploymentLW struct {
251
-	client osclient.Interface
251
+	client kclient.Interface
252 252
 	field  labels.Selector
253 253
 }
254 254
 
255 255
 // List lists all Deployments which match the given field selector.
256 256
 func (lw *deploymentLW) List() (runtime.Object, error) {
257
-	return lw.client.Deployments(kapi.NamespaceAll).List(labels.Everything(), lw.field)
257
+	return lw.client.ReplicationControllers(kapi.NamespaceAll).List(labels.Everything())
258 258
 }
259 259
 
260 260
 // Watch watches all Deployments matching the given field selector.
261 261
 func (lw *deploymentLW) Watch(resourceVersion string) (watch.Interface, error) {
262
-	return lw.client.Deployments(kapi.NamespaceAll).Watch(labels.Everything(), lw.field, "0")
262
+	return lw.client.ReplicationControllers(kapi.NamespaceAll).Watch(labels.Everything(), lw.field, "0")
263 263
 }
264 264
 
265 265
 // deploymentConfigLW is a ListWatcher implementation for DeploymentConfigs.
... ...
@@ -292,24 +301,24 @@ func (lw *imageRepositoryLW) Watch(resourceVersion string) (watch.Interface, err
292 292
 	return lw.client.ImageRepositories(kapi.NamespaceAll).Watch(labels.Everything(), labels.Everything(), "0")
293 293
 }
294 294
 
295
-// ClientDeploymentInterface is a dccDeploymentInterface and dcDeploymentInterface which delegates to the OpenShift client interfaces
296
-type ClientDeploymentInterface struct {
297
-	Client osclient.Interface
295
+// ClientDeploymentInterace is a dccDeploymentInterface and dcDeploymentInterface which delegates to the OpenShift client interfaces
296
+type ClientDeploymentInterace struct {
297
+	Client kclient.Interface
298 298
 }
299 299
 
300 300
 // GetDeployment returns deployment using OpenShift client.
301
-func (c ClientDeploymentInterface) GetDeployment(namespace, name string) (*deployapi.Deployment, error) {
302
-	return c.Client.Deployments(namespace).Get(name)
301
+func (c ClientDeploymentInterace) GetDeployment(namespace, name string) (*kapi.ReplicationController, error) {
302
+	return c.Client.ReplicationControllers(namespace).Get(name)
303 303
 }
304 304
 
305 305
 // CreateDeployment creates deployment using OpenShift client.
306
-func (c ClientDeploymentInterface) CreateDeployment(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
307
-	return c.Client.Deployments(namespace).Create(deployment)
306
+func (c ClientDeploymentInterace) CreateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
307
+	return c.Client.ReplicationControllers(namespace).Create(deployment)
308 308
 }
309 309
 
310 310
 // UpdateDeployment creates deployment using OpenShift client.
311
-func (c ClientDeploymentInterface) UpdateDeployment(namespace string, deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
312
-	return c.Client.Deployments(namespace).Update(deployment)
311
+func (c ClientDeploymentInterace) UpdateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
312
+	return c.Client.ReplicationControllers(namespace).Update(deployment)
313 313
 }
314 314
 
315 315
 // ClientDeploymentConfigInterface is a changeStrategy which delegates to the OpenShift client interfaces
... ...
@@ -1,15 +1,15 @@
1 1
 package test
2 2
 
3 3
 import (
4
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4 5
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
5
-	deployapi "github.com/openshift/origin/pkg/deploy/api"
6 6
 )
7 7
 
8 8
 type FakeDeploymentStore struct {
9
-	Deployment *deployapi.Deployment
9
+	Deployment *kapi.ReplicationController
10 10
 }
11 11
 
12
-func NewFakeDeploymentStore(deployment *deployapi.Deployment) FakeDeploymentStore {
12
+func NewFakeDeploymentStore(deployment *kapi.ReplicationController) FakeDeploymentStore {
13 13
 	return FakeDeploymentStore{deployment}
14 14
 }
15 15
 
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9 9
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
10 10
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
11
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
11 12
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
12 13
 
13 14
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
... ...
@@ -22,10 +23,11 @@ type DeploymentConfigGenerator struct {
22 22
 	DeploymentInterface       deploymentInterface
23 23
 	DeploymentConfigInterface deploymentConfigInterface
24 24
 	ImageRepositoryInterface  imageRepositoryInterface
25
+	Codec                     runtime.Codec
25 26
 }
26 27
 
27 28
 type deploymentInterface interface {
28
-	GetDeployment(ctx kapi.Context, id string) (*deployapi.Deployment, error)
29
+	GetDeployment(ctx kapi.Context, id string) (*kapi.ReplicationController, error)
29 30
 }
30 31
 
31 32
 type deploymentConfigInterface interface {
... ...
@@ -54,6 +56,7 @@ func (g *DeploymentConfigGenerator) Generate(ctx kapi.Context, deploymentConfigI
54 54
 		glog.V(2).Infof("Error getting deployment: %#v", err)
55 55
 		return nil, err
56 56
 	}
57
+	deploymentExists := !errors.IsNotFound(err)
57 58
 
58 59
 	configPodTemplateSpec := deploymentConfig.Template.ControllerTemplate.Template
59 60
 
... ...
@@ -78,17 +81,27 @@ func (g *DeploymentConfigGenerator) Generate(ctx kapi.Context, deploymentConfigI
78 78
 		updateContainers(configPodTemplateSpec, util.NewStringSet(params.ContainerNames...), newImage)
79 79
 	}
80 80
 
81
-	if deployment == nil {
81
+	if deploymentExists {
82
+		if deployedConfig, err := deployutil.DecodeDeploymentConfig(deployment, g.Codec); err == nil {
83
+			if !deployutil.PodSpecsEqual(configPodTemplateSpec.Spec, deployedConfig.Template.ControllerTemplate.Template.Spec) {
84
+				deploymentConfig.LatestVersion++
85
+				// reset the details of the deployment trigger for this deploymentConfig
86
+				deploymentConfig.Details = nil
87
+				glog.V(4).Infof("Incremented deploymentConfig %s to %d due to template inequality with deployed config", deploymentConfig.Name, deploymentConfig.LatestVersion)
88
+			} else {
89
+				glog.V(4).Infof("No diff detected between deploymentConfig %s and deployed config %s", deploymentConfig.Name, deployedConfig.Name)
90
+			}
91
+		} else {
92
+			glog.V(0).Infof("Failed to decode DeploymentConfig from deployment %s: %v", deployment.Name, err)
93
+		}
94
+	} else {
82 95
 		if deploymentConfig.LatestVersion == 0 {
83 96
 			// If the latest version is zero, and the generation's being called, bump it.
84 97
 			deploymentConfig.LatestVersion = 1
85 98
 			// reset the details of the deployment trigger for this deploymentConfig
86 99
 			deploymentConfig.Details = nil
100
+			glog.V(4).Infof("Set deploymentConfig %s to version %d for initial deployment", deploymentConfig.Name, deploymentConfig.LatestVersion)
87 101
 		}
88
-	} else if !deployutil.PodSpecsEqual(configPodTemplateSpec.Spec, deployment.ControllerTemplate.Template.Spec) {
89
-		deploymentConfig.LatestVersion++
90
-		// reset the details of the deployment trigger for this deploymentConfig
91
-		deploymentConfig.Details = nil
92 102
 	}
93 103
 
94 104
 	return deploymentConfig, nil
... ...
@@ -7,12 +7,15 @@ import (
7 7
 	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8 8
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
9 9
 
10
+	api "github.com/openshift/origin/pkg/api/latest"
10 11
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
12
+	deployutil "github.com/openshift/origin/pkg/deploy/util"
11 13
 	imageapi "github.com/openshift/origin/pkg/image/api"
12 14
 )
13 15
 
14 16
 func TestGenerateFromMissingDeploymentConfig(t *testing.T) {
15 17
 	generator := &DeploymentConfigGenerator{
18
+		Codec: api.Codec,
16 19
 		DeploymentConfigInterface: &testDeploymentConfigInterface{
17 20
 			GetDeploymentConfigFunc: func(id string) (*deployapi.DeploymentConfig, error) {
18 21
 				return nil, kerrors.NewNotFound("deploymentConfig", id)
... ...
@@ -33,6 +36,7 @@ func TestGenerateFromMissingDeploymentConfig(t *testing.T) {
33 33
 
34 34
 func TestGenerateFromConfigWithoutTagChange(t *testing.T) {
35 35
 	generator := &DeploymentConfigGenerator{
36
+		Codec: api.Codec,
36 37
 		DeploymentConfigInterface: &testDeploymentConfigInterface{
37 38
 			GetDeploymentConfigFunc: func(id string) (*deployapi.DeploymentConfig, error) {
38 39
 				return basicDeploymentConfig(), nil
... ...
@@ -44,12 +48,8 @@ func TestGenerateFromConfigWithoutTagChange(t *testing.T) {
44 44
 			},
45 45
 		},
46 46
 		DeploymentInterface: &testDeploymentInterface{
47
-			GetDeploymentFunc: func(id string) (*deployapi.Deployment, error) {
48
-				return &deployapi.Deployment{
49
-					ControllerTemplate: kapi.ReplicationControllerSpec{
50
-						Template: basicPodTemplate(),
51
-					},
52
-				}, nil
47
+			GetDeploymentFunc: func(id string) (*kapi.ReplicationController, error) {
48
+				return basicDeployment(), nil
53 49
 			},
54 50
 		},
55 51
 	}
... ...
@@ -71,6 +71,7 @@ func TestGenerateFromConfigWithoutTagChange(t *testing.T) {
71 71
 
72 72
 func TestGenerateFromConfigWithNoDeployment(t *testing.T) {
73 73
 	generator := &DeploymentConfigGenerator{
74
+		Codec: api.Codec,
74 75
 		DeploymentConfigInterface: &testDeploymentConfigInterface{
75 76
 			GetDeploymentConfigFunc: func(id string) (*deployapi.DeploymentConfig, error) {
76 77
 				return basicDeploymentConfig(), nil
... ...
@@ -82,8 +83,8 @@ func TestGenerateFromConfigWithNoDeployment(t *testing.T) {
82 82
 			},
83 83
 		},
84 84
 		DeploymentInterface: &testDeploymentInterface{
85
-			GetDeploymentFunc: func(id string) (*deployapi.Deployment, error) {
86
-				return nil, kerrors.NewNotFound("deployment", id)
85
+			GetDeploymentFunc: func(id string) (*kapi.ReplicationController, error) {
86
+				return nil, kerrors.NewNotFound("replicationController", id)
87 87
 			},
88 88
 		},
89 89
 	}
... ...
@@ -105,6 +106,7 @@ func TestGenerateFromConfigWithNoDeployment(t *testing.T) {
105 105
 
106 106
 func TestGenerateFromConfigWithUpdatedImageRef(t *testing.T) {
107 107
 	generator := &DeploymentConfigGenerator{
108
+		Codec: api.Codec,
108 109
 		DeploymentConfigInterface: &testDeploymentConfigInterface{
109 110
 			GetDeploymentConfigFunc: func(id string) (*deployapi.DeploymentConfig, error) {
110 111
 				return basicDeploymentConfig(), nil
... ...
@@ -116,12 +118,8 @@ func TestGenerateFromConfigWithUpdatedImageRef(t *testing.T) {
116 116
 			},
117 117
 		},
118 118
 		DeploymentInterface: &testDeploymentInterface{
119
-			GetDeploymentFunc: func(id string) (*deployapi.Deployment, error) {
120
-				return &deployapi.Deployment{
121
-					ControllerTemplate: kapi.ReplicationControllerSpec{
122
-						Template: basicPodTemplate(),
123
-					},
124
-				}, nil
119
+			GetDeploymentFunc: func(id string) (*kapi.ReplicationController, error) {
120
+				return basicDeployment(), nil
125 121
 			},
126 122
 		},
127 123
 	}
... ...
@@ -148,10 +146,10 @@ func TestGenerateFromConfigWithUpdatedImageRef(t *testing.T) {
148 148
 }
149 149
 
150 150
 type testDeploymentInterface struct {
151
-	GetDeploymentFunc func(id string) (*deployapi.Deployment, error)
151
+	GetDeploymentFunc func(id string) (*kapi.ReplicationController, error)
152 152
 }
153 153
 
154
-func (i *testDeploymentInterface) GetDeployment(ctx kapi.Context, id string) (*deployapi.Deployment, error) {
154
+func (i *testDeploymentInterface) GetDeployment(ctx kapi.Context, id string) (*kapi.ReplicationController, error) {
155 155
 	return i.GetDeploymentFunc(id)
156 156
 }
157 157
 
... ...
@@ -187,6 +185,7 @@ func basicPodTemplate() *kapi.PodTemplateSpec {
187 187
 		},
188 188
 	}
189 189
 }
190
+
190 191
 func basicDeploymentConfig() *deployapi.DeploymentConfig {
191 192
 	return &deployapi.DeploymentConfig{
192 193
 		ObjectMeta:    kapi.ObjectMeta{Name: "deploy1"},
... ...
@@ -211,6 +210,26 @@ func basicDeploymentConfig() *deployapi.DeploymentConfig {
211 211
 	}
212 212
 }
213 213
 
214
+func basicDeployment() *kapi.ReplicationController {
215
+	config := basicDeploymentConfig()
216
+	encodedConfig, _ := deployutil.EncodeDeploymentConfig(config, api.Codec)
217
+
218
+	return &kapi.ReplicationController{
219
+		ObjectMeta: kapi.ObjectMeta{
220
+			Name: deployutil.LatestDeploymentIDForConfig(config),
221
+			Annotations: map[string]string{
222
+				deployapi.DeploymentConfigAnnotation:        config.Name,
223
+				deployapi.DeploymentStatusAnnotation:        string(deployapi.DeploymentStatusNew),
224
+				deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
225
+			},
226
+			Labels: config.Labels,
227
+		},
228
+		Spec: kapi.ReplicationControllerSpec{
229
+			Template: basicPodTemplate(),
230
+		},
231
+	}
232
+}
233
+
214 234
 func basicImageRepo() *imageapi.ImageRepositoryList {
215 235
 	return &imageapi.ImageRepositoryList{
216 236
 		Items: []imageapi.ImageRepository{
... ...
@@ -8,8 +8,10 @@ import (
8 8
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9 9
 	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
10 10
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
11
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
11 12
 
12 13
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
14
+	deployutil "github.com/openshift/origin/pkg/deploy/util"
13 15
 )
14 16
 
15 17
 // DeploymentStrategy is a simple strategy appropriate as a default. Its behavior
... ...
@@ -19,12 +21,12 @@ import (
19 19
 // A failure to remove any existing ReplicationController will be considered a deployment failure.
20 20
 type DeploymentStrategy struct {
21 21
 	ReplicationController ReplicationControllerInterface
22
+	Codec                 runtime.Codec
22 23
 }
23 24
 
24 25
 type ReplicationControllerInterface interface {
25 26
 	listReplicationControllers(namespace string, selector labels.Selector) (*kapi.ReplicationControllerList, error)
26 27
 	getReplicationController(namespace, id string) (*kapi.ReplicationController, error)
27
-	createReplicationController(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error)
28 28
 	updateReplicationController(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error)
29 29
 	deleteReplicationController(namespace string, id string) error
30 30
 }
... ...
@@ -41,10 +43,6 @@ func (r RealReplicationController) getReplicationController(namespace string, id
41 41
 	return r.KubeClient.ReplicationControllers(namespace).Get(id)
42 42
 }
43 43
 
44
-func (r RealReplicationController) createReplicationController(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error) {
45
-	return r.KubeClient.ReplicationControllers(namespace).Create(ctrl)
46
-}
47
-
48 44
 func (r RealReplicationController) updateReplicationController(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error) {
49 45
 	return r.KubeClient.ReplicationControllers(namespace).Update(ctrl)
50 46
 }
... ...
@@ -53,7 +51,7 @@ func (r RealReplicationController) deleteReplicationController(namespace string,
53 53
 	return r.KubeClient.ReplicationControllers(namespace).Delete(id)
54 54
 }
55 55
 
56
-func (s *DeploymentStrategy) Deploy(deployment *deployapi.Deployment) error {
56
+func (s *DeploymentStrategy) Deploy(deployment *kapi.ReplicationController) error {
57 57
 	controllers := &kapi.ReplicationControllerList{}
58 58
 	namespace := deployment.Namespace
59 59
 	var err error
... ...
@@ -69,30 +67,16 @@ func (s *DeploymentStrategy) Deploy(deployment *deployapi.Deployment) error {
69 69
 		return fmt.Errorf("Unable to get list of replication controllers for previous deploymentConfig %s: %v\n", configID, err)
70 70
 	}
71 71
 
72
-	deploymentCopy, err := kapi.Scheme.Copy(deployment)
73
-	if err != nil {
74
-		return fmt.Errorf("Unable to copy deployment %s: %v\n", deployment.Name, err)
75
-	}
76
-
77
-	controller := &kapi.ReplicationController{
78
-		ObjectMeta: kapi.ObjectMeta{
79
-			Annotations: map[string]string{deployapi.DeploymentAnnotation: deployment.Name},
80
-			Labels:      map[string]string{deployapi.DeploymentConfigLabel: configID},
81
-		},
82
-		Spec: deploymentCopy.(*deployapi.Deployment).ControllerTemplate,
83
-	}
84
-
85
-	// Correlate pods created by the ReplicationController to the deployment objects
86
-	if controller.Spec.Template.Labels == nil {
87
-		controller.Spec.Template.Labels = make(map[string]string)
72
+	var deploymentConfig *deployapi.DeploymentConfig
73
+	var decodeError error
74
+	if deploymentConfig, decodeError = deployutil.DecodeDeploymentConfig(deployment, s.Codec); decodeError != nil {
75
+		return fmt.Errorf("Couldn't decode DeploymentConfig from deployment %s: %v", deployment.Name, decodeError)
88 76
 	}
89
-	controller.Spec.Template.Labels[deployapi.DeploymentConfigLabel] = configID
90
-	// TODO: Switch this to an annotation once upstream supports annotations on a PodTemplate
91
-	controller.Spec.Template.Labels[deployapi.DeploymentLabel] = deployment.Name
92 77
 
93
-	glog.Infof("Creating replicationController for deployment %s", deployment.Name)
94
-	if _, err := s.ReplicationController.createReplicationController(namespace, controller); err != nil {
95
-		return fmt.Errorf("An error occurred creating the replication controller for deployment %s: %v", deployment.Name, err)
78
+	deployment.Spec.Replicas = deploymentConfig.Template.ControllerTemplate.Replicas
79
+	glog.Infof("Updating replicationController for deployment %s to replica count %d", deployment.Name, deployment.Spec.Replicas)
80
+	if _, err := s.ReplicationController.updateReplicationController(namespace, deployment); err != nil {
81
+		return fmt.Errorf("An error occurred updating the replication controller for deployment %s: %v", deployment.Name, err)
96 82
 	}
97 83
 
98 84
 	// For this simple deploy, remove previous replication controllers.
... ...
@@ -6,32 +6,31 @@ import (
6 6
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7 7
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
8 8
 
9
+	api "github.com/openshift/origin/pkg/api/latest"
9 10
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
11
+	deployutil "github.com/openshift/origin/pkg/deploy/util"
10 12
 )
11 13
 
12 14
 func TestFirstDeployment(t *testing.T) {
13 15
 	var (
14
-		createdController *kapi.ReplicationController
15
-		deployment        = okDeployment()
16
+		updatedController *kapi.ReplicationController
17
+		deployment        = okDeployment(okDeploymentConfig())
16 18
 	)
17 19
 
18 20
 	strategy := &DeploymentStrategy{
21
+		Codec: api.Codec,
19 22
 		ReplicationController: &testControllerClient{
20 23
 			listReplicationControllersFunc: func(namespace string, selector labels.Selector) (*kapi.ReplicationControllerList, error) {
21 24
 				return &kapi.ReplicationControllerList{}, nil
22 25
 			},
23
-			createReplicationControllerFunc: func(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error) {
24
-				createdController = ctrl
26
+			updateReplicationControllerFunc: func(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error) {
27
+				updatedController = ctrl
25 28
 				return ctrl, nil
26 29
 			},
27 30
 			deleteReplicationControllerFunc: func(namespaceBravo, id string) error {
28 31
 				t.Fatalf("unexpected call to DeleteReplicationController")
29 32
 				return nil
30 33
 			},
31
-			updateReplicationControllerFunc: func(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error) {
32
-				t.Fatalf("unexpected call to UpdateReplicationController")
33
-				return nil, nil
34
-			},
35 34
 		},
36 35
 	}
37 36
 
... ...
@@ -41,99 +40,67 @@ func TestFirstDeployment(t *testing.T) {
41 41
 		t.Fatalf("unexpected deploy error: %#v", err)
42 42
 	}
43 43
 
44
-	if createdController == nil {
44
+	if updatedController == nil {
45 45
 		t.Fatalf("expected a ReplicationController")
46 46
 	}
47 47
 
48
-	if e, a := "deploy1", createdController.Annotations[deployapi.DeploymentAnnotation]; e != a {
49
-		t.Fatalf("expected controller deployment annotation %s, git %s", e, a)
50
-	}
51
-
52
-	if e, a := "deploymentConfig1", createdController.Labels[deployapi.DeploymentConfigLabel]; e != a {
53
-		t.Fatalf("expected controller with label %s, got %s", e, a)
54
-	}
55
-
56
-	if e, a := "deploymentConfig1", createdController.Spec.Template.Labels[deployapi.DeploymentConfigLabel]; e != a {
57
-		t.Fatalf("expected controller podtemplate label %s, got %s", e, a)
58
-	}
59
-
60
-	if e, a := "deploy1", createdController.Spec.Template.Labels[deployapi.DeploymentLabel]; e != a {
61
-		t.Fatalf("expected controller podtemplate label %s, got %s", e, a)
62
-	}
63
-
64
-	if e, a := 2, createdController.Spec.Replicas; e != a {
48
+	if e, a := 2, updatedController.Spec.Replicas; e != a {
65 49
 		t.Fatalf("expected controller replicas to be %d, got %d", e, a)
66 50
 	}
67 51
 }
68 52
 
69 53
 func TestSecondDeployment(t *testing.T) {
70
-	var (
71
-		createdController   *kapi.ReplicationController
72
-		updatedController   *kapi.ReplicationController
73
-		deletedControllerID string
74
-		deployment          = okDeployment()
75
-	)
54
+	var deletedControllerID string
55
+	updatedControllers := make(map[string]*kapi.ReplicationController)
56
+	oldDeployment := okDeployment(okDeploymentConfig())
76 57
 
77 58
 	strategy := &DeploymentStrategy{
59
+		Codec: api.Codec,
78 60
 		ReplicationController: &testControllerClient{
79 61
 			listReplicationControllersFunc: func(namespace string, selector labels.Selector) (*kapi.ReplicationControllerList, error) {
80 62
 				return &kapi.ReplicationControllerList{
81 63
 					Items: []kapi.ReplicationController{
82
-						okReplicationController(),
64
+						*oldDeployment,
83 65
 					},
84 66
 				}, nil
85 67
 			},
86
-			createReplicationControllerFunc: func(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error) {
87
-				createdController = ctrl
88
-				return ctrl, nil
89
-			},
90 68
 			deleteReplicationControllerFunc: func(namespace, id string) error {
91 69
 				deletedControllerID = id
92 70
 				return nil
93 71
 			},
94 72
 			updateReplicationControllerFunc: func(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error) {
95
-				updatedController = ctrl
73
+				updatedControllers[ctrl.Name] = ctrl
96 74
 				return ctrl, nil
97 75
 			},
98 76
 		},
99 77
 	}
100 78
 
101
-	deployment.Name = "deploy2"
102
-	err := strategy.Deploy(deployment)
79
+	newConfig := okDeploymentConfig()
80
+	newConfig.LatestVersion = 2
81
+	newDeployment := okDeployment(newConfig)
82
+
83
+	err := strategy.Deploy(newDeployment)
103 84
 
104 85
 	if err != nil {
105 86
 		t.Fatalf("unexpected deploy error: %#v", err)
106 87
 	}
107 88
 
108
-	if createdController == nil {
109
-		t.Fatalf("expected a ReplicationController")
110
-	}
111
-
112
-	if e, a := "deploy2", createdController.Annotations[deployapi.DeploymentAnnotation]; e != a {
113
-		t.Fatalf("expected controller deployment annotation %s, git %s", e, a)
114
-	}
115
-
116
-	if e, a := "deploymentConfig1", createdController.Labels[deployapi.DeploymentConfigLabel]; e != a {
117
-		t.Fatalf("expected controller with label %s, got %s", e, a)
118
-	}
119
-
120
-	if e, a := "deploymentConfig1", createdController.Spec.Template.Labels[deployapi.DeploymentConfigLabel]; e != a {
121
-		t.Fatalf("expected controller podtemplate label %s, got %s", e, a)
122
-	}
123
-
124
-	if e, a := 0, updatedController.Spec.Replicas; e != a {
89
+	if e, a := 0, updatedControllers[oldDeployment.Name].Spec.Replicas; e != a {
125 90
 		t.Fatalf("expected old controller replicas to be %d, got %d", e, a)
126 91
 	}
127 92
 
128
-	if e, a := "controller1", deletedControllerID; e != a {
93
+	if e, a := oldDeployment.Name, deletedControllerID; e != a {
129 94
 		t.Fatalf("expected deletion of controller %s, got %s", e, a)
130 95
 	}
96
+
97
+	if e, a := 2, updatedControllers[newDeployment.Name].Spec.Replicas; e != a {
98
+		t.Fatalf("expected new controller replicas to be %d, got %d", e, a)
99
+	}
131 100
 }
132 101
 
133 102
 type testControllerClient struct {
134 103
 	listReplicationControllersFunc  func(namespace string, selector labels.Selector) (*kapi.ReplicationControllerList, error)
135 104
 	getReplicationControllerFunc    func(namespace, id string) (*kapi.ReplicationController, error)
136
-	createReplicationControllerFunc func(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error)
137 105
 	updateReplicationControllerFunc func(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error)
138 106
 	deleteReplicationControllerFunc func(namespace, id string) error
139 107
 }
... ...
@@ -146,10 +113,6 @@ func (t *testControllerClient) getReplicationController(namespace, id string) (*
146 146
 	return t.getReplicationControllerFunc(namespace, id)
147 147
 }
148 148
 
149
-func (t *testControllerClient) createReplicationController(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error) {
150
-	return t.createReplicationControllerFunc(namespace, ctrl)
151
-}
152
-
153 149
 func (t *testControllerClient) updateReplicationController(namespace string, ctrl *kapi.ReplicationController) (*kapi.ReplicationController, error) {
154 150
 	return t.updateReplicationControllerFunc(namespace, ctrl)
155 151
 }
... ...
@@ -158,26 +121,23 @@ func (t *testControllerClient) deleteReplicationController(namespace, id string)
158 158
 	return t.deleteReplicationControllerFunc(namespace, id)
159 159
 }
160 160
 
161
-func okDeployment() *deployapi.Deployment {
162
-	return &deployapi.Deployment{
163
-		ObjectMeta: kapi.ObjectMeta{
164
-			Name: "deploy1",
165
-			Annotations: map[string]string{
166
-				deployapi.DeploymentConfigAnnotation: "deploymentConfig1",
161
+func okDeploymentConfig() *deployapi.DeploymentConfig {
162
+	return &deployapi.DeploymentConfig{
163
+		ObjectMeta:    kapi.ObjectMeta{Name: "deploymentConfig"},
164
+		LatestVersion: 1,
165
+		Template: deployapi.DeploymentTemplate{
166
+			Strategy: deployapi.DeploymentStrategy{
167
+				Type: deployapi.DeploymentStrategyTypeRecreate,
167 168
 			},
168
-		},
169
-		Status: deployapi.DeploymentStatusNew,
170
-		Strategy: deployapi.DeploymentStrategy{
171
-			Type: deployapi.DeploymentStrategyTypeRecreate,
172
-		},
173
-		ControllerTemplate: kapi.ReplicationControllerSpec{
174
-			Replicas: 2,
175
-			Template: &kapi.PodTemplateSpec{
176
-				Spec: kapi.PodSpec{
177
-					Containers: []kapi.Container{
178
-						{
179
-							Name:  "container1",
180
-							Image: "registry:8080/repo1:ref1",
169
+			ControllerTemplate: kapi.ReplicationControllerSpec{
170
+				Replicas: 2,
171
+				Template: &kapi.PodTemplateSpec{
172
+					Spec: kapi.PodSpec{
173
+						Containers: []kapi.Container{
174
+							{
175
+								Name:  "container1",
176
+								Image: "registry:8080/repo1:ref1",
177
+							},
181 178
 						},
182 179
 					},
183 180
 				},
... ...
@@ -186,32 +146,21 @@ func okDeployment() *deployapi.Deployment {
186 186
 	}
187 187
 }
188 188
 
189
-func okReplicationController() kapi.ReplicationController {
190
-	return kapi.ReplicationController{
189
+func okDeployment(config *deployapi.DeploymentConfig) *kapi.ReplicationController {
190
+	encodedConfig, _ := deployutil.EncodeDeploymentConfig(config, api.Codec)
191
+	controller := &kapi.ReplicationController{
191 192
 		ObjectMeta: kapi.ObjectMeta{
192
-			Name:        "controller1",
193
-			Annotations: map[string]string{deployapi.DeploymentAnnotation: "deploy1"},
194
-			Labels: map[string]string{
195
-				deployapi.DeploymentConfigLabel: "deploymentConfig1",
196
-			},
197
-		},
198
-		Spec: kapi.ReplicationControllerSpec{
199
-			Replicas: 1,
200
-			Template: &kapi.PodTemplateSpec{
201
-				ObjectMeta: kapi.ObjectMeta{
202
-					Labels: map[string]string{
203
-						deployapi.DeploymentConfigLabel: "deploymentConfig1",
204
-					},
205
-				},
206
-				Spec: kapi.PodSpec{
207
-					Containers: []kapi.Container{
208
-						{
209
-							Name:  "container1",
210
-							Image: "registry:8080/repo1:ref1",
211
-						},
212
-					},
213
-				},
193
+			Name: deployutil.LatestDeploymentIDForConfig(config),
194
+			Annotations: map[string]string{
195
+				deployapi.DeploymentConfigAnnotation:        config.Name,
196
+				deployapi.DeploymentStatusAnnotation:        string(deployapi.DeploymentStatusNew),
197
+				deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
214 198
 			},
199
+			Labels: config.Labels,
215 200
 		},
201
+		Spec: config.Template.ControllerTemplate,
216 202
 	}
203
+
204
+	controller.Spec.Replicas = 0
205
+	return controller
217 206
 }
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"github.com/golang/glog"
11 11
 
12 12
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
13
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
13 14
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
14 15
 
15 16
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
... ...
@@ -85,3 +86,24 @@ func HashPodSpec(t api.PodSpec) uint64 {
85 85
 func PodSpecsEqual(a, b api.PodSpec) bool {
86 86
 	return HashPodSpec(a) == HashPodSpec(b)
87 87
 }
88
+
89
+func DecodeDeploymentConfig(controller *api.ReplicationController, codec runtime.Codec) (*deployapi.DeploymentConfig, error) {
90
+	encodedConfig := []byte(controller.Annotations[deployapi.DeploymentEncodedConfigAnnotation])
91
+	if decoded, err := codec.Decode(encodedConfig); err == nil {
92
+		if config, ok := decoded.(*deployapi.DeploymentConfig); ok {
93
+			return config, nil
94
+		} else {
95
+			return nil, fmt.Errorf("Decoded deploymentConfig from controller is not a DeploymentConfig: %v", err)
96
+		}
97
+	} else {
98
+		return nil, fmt.Errorf("Failed to decode DeploymentConfig from controller: %v", err)
99
+	}
100
+}
101
+
102
+func EncodeDeploymentConfig(config *deployapi.DeploymentConfig, codec runtime.Codec) (string, error) {
103
+	if bytes, err := codec.Encode(config); err == nil {
104
+		return string(bytes[:]), nil
105
+	} else {
106
+		return "", err
107
+	}
108
+}
... ...
@@ -3,6 +3,7 @@
3 3
 package integration
4 4
 
5 5
 import (
6
+	"flag"
6 7
 	"net/http"
7 8
 	"net/http/httptest"
8 9
 	"testing"
... ...
@@ -46,7 +47,7 @@ func TestSuccessfulManualDeployment(t *testing.T) {
46 46
 	config := manualDeploymentConfig()
47 47
 	var err error
48 48
 
49
-	watch, err := openshift.Client.Deployments(testNamespace).Watch(labels.Everything(), labels.Everything(), "0")
49
+	watch, err := openshift.KubeClient.ReplicationControllers(testNamespace).Watch(labels.Everything(), labels.Everything(), "0")
50 50
 	if err != nil {
51 51
 		t.Fatalf("Couldn't subscribe to Deployments: %v", err)
52 52
 	}
... ...
@@ -67,7 +68,7 @@ func TestSuccessfulManualDeployment(t *testing.T) {
67 67
 	if e, a := watchapi.Added, event.Type; e != a {
68 68
 		t.Fatalf("expected watch event type %s, got %s", e, a)
69 69
 	}
70
-	deployment := event.Object.(*deployapi.Deployment)
70
+	deployment := event.Object.(*kapi.ReplicationController)
71 71
 
72 72
 	if e, a := config.Name, deployment.Annotations[deployapi.DeploymentConfigAnnotation]; e != a {
73 73
 		t.Fatalf("Expected deployment annotated with deploymentConfig '%s', got '%s'", e, a)
... ...
@@ -90,7 +91,7 @@ func TestSimpleImageChangeTrigger(t *testing.T) {
90 90
 	config := imageChangeDeploymentConfig()
91 91
 	var err error
92 92
 
93
-	watch, err := openshift.Client.Deployments(testNamespace).Watch(labels.Everything(), labels.Everything(), "0")
93
+	watch, err := openshift.KubeClient.ReplicationControllers(testNamespace).Watch(labels.Everything(), labels.Everything(), "0")
94 94
 	if err != nil {
95 95
 		t.Fatalf("Couldn't subscribe to Deployments %v", err)
96 96
 	}
... ...
@@ -115,7 +116,7 @@ func TestSimpleImageChangeTrigger(t *testing.T) {
115 115
 	if e, a := watchapi.Added, event.Type; e != a {
116 116
 		t.Fatalf("expected watch event type %s, got %s", e, a)
117 117
 	}
118
-	deployment := event.Object.(*deployapi.Deployment)
118
+	deployment := event.Object.(*kapi.ReplicationController)
119 119
 
120 120
 	if e, a := config.Name, deployment.Annotations[deployapi.DeploymentConfigAnnotation]; e != a {
121 121
 		t.Fatalf("Expected deployment annotated with deploymentConfig '%s', got '%s'", e, a)
... ...
@@ -131,7 +132,7 @@ func TestSimpleImageChangeTrigger(t *testing.T) {
131 131
 	if e, a := watchapi.Added, event.Type; e != a {
132 132
 		t.Fatalf("expected watch event type %s, got %s", e, a)
133 133
 	}
134
-	newDeployment := event.Object.(*deployapi.Deployment)
134
+	newDeployment := event.Object.(*kapi.ReplicationController)
135 135
 
136 136
 	if newDeployment.Name == deployment.Name {
137 137
 		t.Fatalf("expected new deployment; old=%s, new=%s", deployment.Name, newDeployment.Name)
... ...
@@ -146,7 +147,7 @@ func TestSimpleConfigChangeTrigger(t *testing.T) {
146 146
 	config := changeDeploymentConfig()
147 147
 	var err error
148 148
 
149
-	watch, err := openshift.Client.Deployments(testNamespace).Watch(labels.Everything(), labels.Everything(), "0")
149
+	watch, err := openshift.KubeClient.ReplicationControllers(testNamespace).Watch(labels.Everything(), labels.Everything(), "0")
150 150
 	if err != nil {
151 151
 		t.Fatalf("Couldn't subscribe to Deployments %v", err)
152 152
 	}
... ...
@@ -162,7 +163,7 @@ func TestSimpleConfigChangeTrigger(t *testing.T) {
162 162
 		t.Fatalf("expected watch event type %s, got %s", e, a)
163 163
 	}
164 164
 
165
-	deployment := event.Object.(*deployapi.Deployment)
165
+	deployment := event.Object.(*kapi.ReplicationController)
166 166
 
167 167
 	if e, a := config.Name, deployment.Annotations[deployapi.DeploymentConfigAnnotation]; e != a {
168 168
 		t.Fatalf("Expected deployment annotated with deploymentConfig '%s', got '%s'", e, a)
... ...
@@ -185,7 +186,7 @@ func TestSimpleConfigChangeTrigger(t *testing.T) {
185 185
 	if e, a := watchapi.Added, event.Type; e != a {
186 186
 		t.Fatalf("expected watch event type %s, got %s", e, a)
187 187
 	}
188
-	newDeployment := event.Object.(*deployapi.Deployment)
188
+	newDeployment := event.Object.(*kapi.ReplicationController)
189 189
 
190 190
 	assertEnvVarEquals("ENV_TEST", "UPDATED", newDeployment, t)
191 191
 
... ...
@@ -194,8 +195,8 @@ func TestSimpleConfigChangeTrigger(t *testing.T) {
194 194
 	}
195 195
 }
196 196
 
197
-func assertEnvVarEquals(name string, value string, deployment *deployapi.Deployment, t *testing.T) {
198
-	env := deployment.ControllerTemplate.Template.Spec.Containers[0].Env
197
+func assertEnvVarEquals(name string, value string, deployment *kapi.ReplicationController, t *testing.T) {
198
+	env := deployment.Spec.Template.Spec.Containers[0].Env
199 199
 
200 200
 	for _, e := range env {
201 201
 		if e.Name == name && e.Value == value {
... ...
@@ -216,12 +217,14 @@ func (p *podInfoGetter) GetPodInfo(host, namespace, podID string) (kapi.PodInfo,
216 216
 }
217 217
 
218 218
 type testOpenshift struct {
219
-	Client *osclient.Client
220
-	Server *httptest.Server
221
-	stop   chan struct{}
219
+	Client     *osclient.Client
220
+	KubeClient kclient.Interface
221
+	Server     *httptest.Server
222
+	stop       chan struct{}
222 223
 }
223 224
 
224 225
 func NewTestOpenshift(t *testing.T) *testOpenshift {
226
+	flag.Set("v", "4")
225 227
 	glog.Info("Starting test openshift")
226 228
 
227 229
 	openshift := &testOpenshift{
... ...
@@ -238,6 +241,7 @@ func NewTestOpenshift(t *testing.T) *testOpenshift {
238 238
 	osClient, _ := osclient.New(&client.Config{Host: openshift.Server.URL, Version: latest.Version})
239 239
 
240 240
 	openshift.Client = osClient
241
+	openshift.KubeClient = kubeClient
241 242
 
242 243
 	kubeletClient, err := kclient.NewKubeletClient(&kclient.KubeletConfig{Port: 10250})
243 244
 	if err != nil {
... ...
@@ -257,7 +261,8 @@ func NewTestOpenshift(t *testing.T) *testOpenshift {
257 257
 	imageEtcd := imageetcd.New(etcdHelper)
258 258
 	deployEtcd := deployetcd.New(etcdHelper)
259 259
 	deployConfigGenerator := &deployconfiggenerator.DeploymentConfigGenerator{
260
-		DeploymentInterface:       deployEtcd,
260
+		Codec:                     latest.Codec,
261
+		DeploymentInterface:       &clientDeploymentInterface{kubeClient},
261 262
 		DeploymentConfigInterface: deployEtcd,
262 263
 		ImageRepositoryInterface:  imageEtcd,
263 264
 	}
... ...
@@ -278,14 +283,18 @@ func NewTestOpenshift(t *testing.T) *testOpenshift {
278 278
 	apiserver.NewAPIGroupVersion(storage, v1beta1.Codec, osPrefix, interfaces.MetadataAccessor).InstallREST(handlerContainer, "/osapi", "v1beta1")
279 279
 
280 280
 	dccFactory := deploycontrollerfactory.DeploymentConfigControllerFactory{
281
-		Client: osClient,
282
-		Stop:   openshift.stop,
281
+		Client:     osClient,
282
+		KubeClient: kubeClient,
283
+		Codec:      latest.Codec,
284
+		Stop:       openshift.stop,
283 285
 	}
284 286
 	dccFactory.Create().Run()
285 287
 
286 288
 	cccFactory := deploycontrollerfactory.DeploymentConfigChangeControllerFactory{
287
-		Client: osClient,
288
-		Stop:   openshift.stop,
289
+		Client:     osClient,
290
+		KubeClient: kubeClient,
291
+		Codec:      latest.Codec,
292
+		Stop:       openshift.stop,
289 293
 	}
290 294
 	cccFactory.Create().Run()
291 295
 
... ...
@@ -303,6 +312,14 @@ func (t *testOpenshift) Close() {
303 303
 	close(t.stop)
304 304
 }
305 305
 
306
+type clientDeploymentInterface struct {
307
+	KubeClient kclient.Interface
308
+}
309
+
310
+func (c *clientDeploymentInterface) GetDeployment(ctx kapi.Context, id string) (*kapi.ReplicationController, error) {
311
+	return c.KubeClient.ReplicationControllers(kapi.Namespace(ctx)).Get(id)
312
+}
313
+
306 314
 func imageChangeDeploymentConfig() *deployapi.DeploymentConfig {
307 315
 	return &deployapi.DeploymentConfig{
308 316
 		ObjectMeta: kapi.ObjectMeta{Name: "image-deploy-config"},