| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,276 @@ |
| 0 |
+{
|
|
| 1 |
+ "id":"guestbook-deployment", |
|
| 2 |
+ "kind":"Config", |
|
| 3 |
+ "name":"guestbook-d", |
|
| 4 |
+ "description":"A simple guestbook application configuration", |
|
| 5 |
+ "items":[ |
|
| 6 |
+ {
|
|
| 7 |
+ "id":"frontend", |
|
| 8 |
+ "kind":"Service", |
|
| 9 |
+ "apiVersion":"v1beta1", |
|
| 10 |
+ "port":5432, |
|
| 11 |
+ "selector":{
|
|
| 12 |
+ "name":"frontend" |
|
| 13 |
+ } |
|
| 14 |
+ }, |
|
| 15 |
+ {
|
|
| 16 |
+ "id":"redismaster", |
|
| 17 |
+ "kind":"Service", |
|
| 18 |
+ "apiVersion":"v1beta1", |
|
| 19 |
+ "port":10000, |
|
| 20 |
+ "selector":{
|
|
| 21 |
+ "name":"redis-master" |
|
| 22 |
+ } |
|
| 23 |
+ }, |
|
| 24 |
+ {
|
|
| 25 |
+ "id":"redisslave", |
|
| 26 |
+ "kind":"Service", |
|
| 27 |
+ "apiVersion":"v1beta1", |
|
| 28 |
+ "port":10001, |
|
| 29 |
+ "labels":{
|
|
| 30 |
+ "name":"redisslave" |
|
| 31 |
+ }, |
|
| 32 |
+ "selector":{
|
|
| 33 |
+ "name":"redisslave" |
|
| 34 |
+ } |
|
| 35 |
+ }, |
|
| 36 |
+ {
|
|
| 37 |
+ "id":"redis-master-2", |
|
| 38 |
+ "kind":"Pod", |
|
| 39 |
+ "apiVersion":"v1beta1", |
|
| 40 |
+ "desiredState":{
|
|
| 41 |
+ "manifest":{
|
|
| 42 |
+ "version":"v1beta1", |
|
| 43 |
+ "id":"redis-master-2", |
|
| 44 |
+ "containers":[ |
|
| 45 |
+ {
|
|
| 46 |
+ "name":"master", |
|
| 47 |
+ "image":"dockerfile/redis", |
|
| 48 |
+ "env":[ |
|
| 49 |
+ {
|
|
| 50 |
+ "name":"REDIS_PASSWORD", |
|
| 51 |
+ "value":"secret" |
|
| 52 |
+ } |
|
| 53 |
+ ], |
|
| 54 |
+ "ports":[ |
|
| 55 |
+ {
|
|
| 56 |
+ "containerPort":6379 |
|
| 57 |
+ } |
|
| 58 |
+ ] |
|
| 59 |
+ } |
|
| 60 |
+ ] |
|
| 61 |
+ } |
|
| 62 |
+ } |
|
| 63 |
+ }, |
|
| 64 |
+ {
|
|
| 65 |
+ "id":"frontend-config", |
|
| 66 |
+ "kind":"DeploymentConfig", |
|
| 67 |
+ "apiVersion":"v1beta1", |
|
| 68 |
+ "triggerPolicy":"manual", |
|
| 69 |
+ "template":{
|
|
| 70 |
+ "strategy":{
|
|
| 71 |
+ "type":"customPod", |
|
| 72 |
+ "customPod":{
|
|
| 73 |
+ "image":"127.0.0.1:5000/openshift/kube-deploy" |
|
| 74 |
+ } |
|
| 75 |
+ }, |
|
| 76 |
+ "controllerTemplate":{
|
|
| 77 |
+ "replicas":1, |
|
| 78 |
+ "replicaSelector":{
|
|
| 79 |
+ "name":"frontend" |
|
| 80 |
+ }, |
|
| 81 |
+ "podTemplate":{
|
|
| 82 |
+ "desiredState":{
|
|
| 83 |
+ "manifest":{
|
|
| 84 |
+ "version":"v1beta1", |
|
| 85 |
+ "id":"frontendController", |
|
| 86 |
+ "containers":[ |
|
| 87 |
+ {
|
|
| 88 |
+ "name":"php-redis", |
|
| 89 |
+ "image":"brendanburns/php-redis", |
|
| 90 |
+ "env":[ |
|
| 91 |
+ {
|
|
| 92 |
+ "name":"ADMIN_USERNAME", |
|
| 93 |
+ "value":"admin" |
|
| 94 |
+ }, |
|
| 95 |
+ {
|
|
| 96 |
+ "name":"ADMIN_PASSWORD", |
|
| 97 |
+ "value":"secret" |
|
| 98 |
+ }, |
|
| 99 |
+ {
|
|
| 100 |
+ "name":"REDIS_PASSWORD", |
|
| 101 |
+ "value":"secret" |
|
| 102 |
+ } |
|
| 103 |
+ ], |
|
| 104 |
+ "ports":[ |
|
| 105 |
+ {
|
|
| 106 |
+ "containerPort":80, |
|
| 107 |
+ "hostPort":8000 |
|
| 108 |
+ } |
|
| 109 |
+ ] |
|
| 110 |
+ } |
|
| 111 |
+ ] |
|
| 112 |
+ } |
|
| 113 |
+ }, |
|
| 114 |
+ "labels":{
|
|
| 115 |
+ "name":"frontend" |
|
| 116 |
+ } |
|
| 117 |
+ } |
|
| 118 |
+ } |
|
| 119 |
+ } |
|
| 120 |
+ }, |
|
| 121 |
+ {
|
|
| 122 |
+ "id":"redisslave-config", |
|
| 123 |
+ "kind":"DeploymentConfig", |
|
| 124 |
+ "apiVersion":"v1beta1", |
|
| 125 |
+ "triggerPolicy":"manual", |
|
| 126 |
+ "template":{
|
|
| 127 |
+ "strategy":{
|
|
| 128 |
+ "type":"customPod", |
|
| 129 |
+ "customPod":{
|
|
| 130 |
+ "image":"127.0.0.1:5000/openshift/kube-deploy" |
|
| 131 |
+ } |
|
| 132 |
+ }, |
|
| 133 |
+ "controllerTemplate":{
|
|
| 134 |
+ "replicas":2, |
|
| 135 |
+ "replicaSelector":{
|
|
| 136 |
+ "name":"redisslave" |
|
| 137 |
+ }, |
|
| 138 |
+ "podTemplate":{
|
|
| 139 |
+ "desiredState":{
|
|
| 140 |
+ "manifest":{
|
|
| 141 |
+ "version":"v1beta1", |
|
| 142 |
+ "id":"redisSlaveController", |
|
| 143 |
+ "containers":[ |
|
| 144 |
+ {
|
|
| 145 |
+ "name":"slave", |
|
| 146 |
+ "image":"brendanburns/redis-slave", |
|
| 147 |
+ "env":[ |
|
| 148 |
+ {
|
|
| 149 |
+ "name":"REDIS_PASSWORD", |
|
| 150 |
+ "value":"secret" |
|
| 151 |
+ } |
|
| 152 |
+ ], |
|
| 153 |
+ "ports":[ |
|
| 154 |
+ {
|
|
| 155 |
+ "containerPort":6379, |
|
| 156 |
+ "hostPort":6380 |
|
| 157 |
+ } |
|
| 158 |
+ ] |
|
| 159 |
+ } |
|
| 160 |
+ ] |
|
| 161 |
+ } |
|
| 162 |
+ }, |
|
| 163 |
+ "labels":{
|
|
| 164 |
+ "name":"redisslave" |
|
| 165 |
+ } |
|
| 166 |
+ } |
|
| 167 |
+ } |
|
| 168 |
+ } |
|
| 169 |
+ }, |
|
| 170 |
+ {
|
|
| 171 |
+ "id":"frontend-deploy", |
|
| 172 |
+ "kind":"Deployment", |
|
| 173 |
+ "apiVersion":"v1beta1", |
|
| 174 |
+ "triggerPolicy":"manual", |
|
| 175 |
+ "configId":"frontend-config", |
|
| 176 |
+ "strategy":{
|
|
| 177 |
+ "type":"customPod", |
|
| 178 |
+ "customPod":{
|
|
| 179 |
+ "image":"127.0.0.1:5000/openshift/kube-deploy" |
|
| 180 |
+ } |
|
| 181 |
+ }, |
|
| 182 |
+ "controllerTemplate":{
|
|
| 183 |
+ "replicas":1, |
|
| 184 |
+ "replicaSelector":{
|
|
| 185 |
+ "name":"frontend" |
|
| 186 |
+ }, |
|
| 187 |
+ "podTemplate":{
|
|
| 188 |
+ "desiredState":{
|
|
| 189 |
+ "manifest":{
|
|
| 190 |
+ "version":"v1beta1", |
|
| 191 |
+ "id":"frontendController", |
|
| 192 |
+ "containers":[ |
|
| 193 |
+ {
|
|
| 194 |
+ "name":"php-redis", |
|
| 195 |
+ "image":"brendanburns/php-redis", |
|
| 196 |
+ "env":[ |
|
| 197 |
+ {
|
|
| 198 |
+ "name":"ADMIN_USERNAME", |
|
| 199 |
+ "value":"admin" |
|
| 200 |
+ }, |
|
| 201 |
+ {
|
|
| 202 |
+ "name":"ADMIN_PASSWORD", |
|
| 203 |
+ "value":"secret" |
|
| 204 |
+ }, |
|
| 205 |
+ {
|
|
| 206 |
+ "name":"REDIS_PASSWORD", |
|
| 207 |
+ "value":"secret" |
|
| 208 |
+ } |
|
| 209 |
+ ], |
|
| 210 |
+ "ports":[ |
|
| 211 |
+ {
|
|
| 212 |
+ "containerPort":80, |
|
| 213 |
+ "hostPort":8000 |
|
| 214 |
+ } |
|
| 215 |
+ ] |
|
| 216 |
+ } |
|
| 217 |
+ ] |
|
| 218 |
+ } |
|
| 219 |
+ }, |
|
| 220 |
+ "labels":{
|
|
| 221 |
+ "name":"frontend" |
|
| 222 |
+ } |
|
| 223 |
+ } |
|
| 224 |
+ } |
|
| 225 |
+ }, |
|
| 226 |
+ {
|
|
| 227 |
+ "id":"redisslave-deploy", |
|
| 228 |
+ "kind":"Deployment", |
|
| 229 |
+ "apiVersion":"v1beta1", |
|
| 230 |
+ "triggerPolicy":"manual", |
|
| 231 |
+ "configId":"redisslave-config", |
|
| 232 |
+ "strategy":{
|
|
| 233 |
+ "type":"customPod", |
|
| 234 |
+ "customPod":{
|
|
| 235 |
+ "image":"127.0.0.1:5000/openshift/kube-deploy" |
|
| 236 |
+ } |
|
| 237 |
+ }, |
|
| 238 |
+ "controllerTemplate":{
|
|
| 239 |
+ "replicas":2, |
|
| 240 |
+ "replicaSelector":{
|
|
| 241 |
+ "name":"redisslave" |
|
| 242 |
+ }, |
|
| 243 |
+ "podTemplate":{
|
|
| 244 |
+ "desiredState":{
|
|
| 245 |
+ "manifest":{
|
|
| 246 |
+ "version":"v1beta1", |
|
| 247 |
+ "id":"redisSlaveController", |
|
| 248 |
+ "containers":[ |
|
| 249 |
+ {
|
|
| 250 |
+ "name":"slave", |
|
| 251 |
+ "image":"brendanburns/redis-slave", |
|
| 252 |
+ "env":[ |
|
| 253 |
+ {
|
|
| 254 |
+ "name":"REDIS_PASSWORD", |
|
| 255 |
+ "value":"secret" |
|
| 256 |
+ } |
|
| 257 |
+ ], |
|
| 258 |
+ "ports":[ |
|
| 259 |
+ {
|
|
| 260 |
+ "containerPort":6379, |
|
| 261 |
+ "hostPort":6380 |
|
| 262 |
+ } |
|
| 263 |
+ ] |
|
| 264 |
+ } |
|
| 265 |
+ ] |
|
| 266 |
+ } |
|
| 267 |
+ }, |
|
| 268 |
+ "labels":{
|
|
| 269 |
+ "name":"redisslave" |
|
| 270 |
+ } |
|
| 271 |
+ } |
|
| 272 |
+ } |
|
| 273 |
+ } |
|
| 274 |
+ ] |
|
| 275 |
+} |
|
| 0 | 276 |
\ No newline at end of file |
| 0 | 5 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,112 @@ |
| 0 |
+package main |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net/url" |
|
| 4 |
+ "os" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 7 |
+ kubeclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
| 8 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 10 |
+ "github.com/golang/glog" |
|
| 11 |
+ osclient "github.com/openshift/origin/pkg/client" |
|
| 12 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 13 |
+ "gopkg.in/v1/yaml" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+func main() {
|
|
| 17 |
+ util.InitLogs() |
|
| 18 |
+ defer util.FlushLogs() |
|
| 19 |
+ |
|
| 20 |
+ var masterServer string |
|
| 21 |
+ if len(os.Getenv("KUBERNETES_MASTER")) > 0 {
|
|
| 22 |
+ masterServer = os.Getenv("KUBERNETES_MASTER")
|
|
| 23 |
+ } else {
|
|
| 24 |
+ masterServer = "http://localhost:8080" |
|
| 25 |
+ } |
|
| 26 |
+ _, err := url.Parse(masterServer) |
|
| 27 |
+ if err != nil {
|
|
| 28 |
+ glog.Fatalf("Unable to parse %v as a URL\n", err)
|
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ client, err := kubeclient.New(masterServer, nil) |
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ glog.Errorf("Unable to connect to kubernetes master: %v", err)
|
|
| 34 |
+ os.Exit(1) |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ osClient, err := osclient.New(masterServer, nil) |
|
| 38 |
+ if err != nil {
|
|
| 39 |
+ glog.Errorf("Unable to connect to openshift master: %v", err)
|
|
| 40 |
+ os.Exit(1) |
|
| 41 |
+ } |
|
| 42 |
+ |
|
| 43 |
+ deployTarget(client, osClient) |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func deployTarget(client *kubeclient.Client, osClient osclient.Interface) {
|
|
| 47 |
+ deploymentID := os.Getenv("KUBERNETES_DEPLOYMENT_ID")
|
|
| 48 |
+ if len(deploymentID) == 0 {
|
|
| 49 |
+ glog.Fatal("No deployment id was specified. Expected KUBERNETES_DEPLOYMENT_ID variable.")
|
|
| 50 |
+ return |
|
| 51 |
+ } |
|
| 52 |
+ glog.Infof("Retrieving deployment id: %v", deploymentID)
|
|
| 53 |
+ |
|
| 54 |
+ var deployment deployapi.Deployment |
|
| 55 |
+ var err error |
|
| 56 |
+ if deployment, err = osClient.GetDeployment(deploymentID); err != nil {
|
|
| 57 |
+ glog.Fatalf("An error occurred retrieving the deployment object: %v", err)
|
|
| 58 |
+ return |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ selector, _ := labels.ParseSelector("deployment=" + deployment.ConfigID)
|
|
| 62 |
+ replicationControllers, err := client.ListReplicationControllers(selector) |
|
| 63 |
+ if err != nil {
|
|
| 64 |
+ glog.Fatalf("Unable to get list of replication controllers %v\n", err)
|
|
| 65 |
+ return |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ controller := api.ReplicationController{
|
|
| 69 |
+ DesiredState: deployment.ControllerTemplate, |
|
| 70 |
+ Labels: map[string]string{"deployment": deployment.ConfigID},
|
|
| 71 |
+ } |
|
| 72 |
+ if controller.DesiredState.PodTemplate.Labels == nil {
|
|
| 73 |
+ controller.DesiredState.PodTemplate.Labels = make(map[string]string) |
|
| 74 |
+ } |
|
| 75 |
+ controller.DesiredState.PodTemplate.Labels["deployment"] = deployment.ConfigID |
|
| 76 |
+ |
|
| 77 |
+ glog.Info("Creating replication controller: ")
|
|
| 78 |
+ obj, _ := yaml.Marshal(controller) |
|
| 79 |
+ glog.Info(string(obj)) |
|
| 80 |
+ |
|
| 81 |
+ if _, err := client.CreateReplicationController(controller); err != nil {
|
|
| 82 |
+ glog.Fatalf("An error occurred creating the replication controller: %v", err)
|
|
| 83 |
+ return |
|
| 84 |
+ } |
|
| 85 |
+ |
|
| 86 |
+ glog.Info("Create replication controller")
|
|
| 87 |
+ |
|
| 88 |
+ // For this simple deploy, remove previous replication controllers |
|
| 89 |
+ for _, rc := range replicationControllers.Items {
|
|
| 90 |
+ glog.Info("Stopping replication controller: ")
|
|
| 91 |
+ obj, _ := yaml.Marshal(rc) |
|
| 92 |
+ glog.Info(string(obj)) |
|
| 93 |
+ rcObj, err1 := client.GetReplicationController(rc.ID) |
|
| 94 |
+ if err1 != nil {
|
|
| 95 |
+ glog.Fatalf("Unable to get replication controller %s - error: %#v\n", rc.ID, err1)
|
|
| 96 |
+ } |
|
| 97 |
+ rcObj.DesiredState.Replicas = 0 |
|
| 98 |
+ _, err := client.UpdateReplicationController(rcObj) |
|
| 99 |
+ if err != nil {
|
|
| 100 |
+ glog.Fatalf("Unable to stop replication controller %s - error: %#v\n", rc.ID, err)
|
|
| 101 |
+ } |
|
| 102 |
+ } |
|
| 103 |
+ |
|
| 104 |
+ for _, rc := range replicationControllers.Items {
|
|
| 105 |
+ glog.Infof("Deleting replication controller %s", rc.ID)
|
|
| 106 |
+ err := client.DeleteReplicationController(rc.ID) |
|
| 107 |
+ if err != nil {
|
|
| 108 |
+ glog.Fatalf("Unable to remove replication controller %s - error: %#v\n", rc.ID, err)
|
|
| 109 |
+ } |
|
| 110 |
+ } |
|
| 111 |
+} |
| ... | ... |
@@ -6,6 +6,8 @@ import ( |
| 6 | 6 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" |
| 7 | 7 |
buildapi "github.com/openshift/origin/pkg/build/api" |
| 8 | 8 |
_ "github.com/openshift/origin/pkg/build/api/v1beta1" |
| 9 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 10 |
+ _ "github.com/openshift/origin/pkg/deploy/api/v1beta1" |
|
| 9 | 11 |
imageapi "github.com/openshift/origin/pkg/image/api" |
| 10 | 12 |
_ "github.com/openshift/origin/pkg/image/api/v1beta1" |
| 11 | 13 |
) |
| ... | ... |
@@ -17,6 +19,8 @@ type Interface interface {
|
| 17 | 17 |
ImageInterface |
| 18 | 18 |
ImageRepositoryInterface |
| 19 | 19 |
ImageRepositoryMappingInterface |
| 20 |
+ DeploymentInterface |
|
| 21 |
+ DeploymentConfigInterface |
|
| 20 | 22 |
} |
| 21 | 23 |
|
| 22 | 24 |
// BuildInterface exposes methods on Build resources. |
| ... | ... |
@@ -57,6 +61,24 @@ type ImageRepositoryMappingInterface interface {
|
| 57 | 57 |
CreateImageRepositoryMapping(*imageapi.ImageRepositoryMapping) error |
| 58 | 58 |
} |
| 59 | 59 |
|
| 60 |
+// DeploymentConfigInterface contains methods for working with DeploymentConfigs |
|
| 61 |
+type DeploymentConfigInterface interface {
|
|
| 62 |
+ ListDeploymentConfigs(selector labels.Selector) (*deployapi.DeploymentConfigList, error) |
|
| 63 |
+ GetDeploymentConfig(id string) (*deployapi.DeploymentConfig, error) |
|
| 64 |
+ CreateDeploymentConfig(*deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) |
|
| 65 |
+ UpdateDeploymentConfig(*deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) |
|
| 66 |
+ DeleteDeploymentConfig(string) error |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+// DeploymentInterface contains methods for working with Deployments |
|
| 70 |
+type DeploymentInterface interface {
|
|
| 71 |
+ ListDeployments(selector labels.Selector) (*deployapi.DeploymentList, error) |
|
| 72 |
+ GetDeployment(id string) (*deployapi.Deployment, error) |
|
| 73 |
+ CreateDeployment(*deployapi.Deployment) (*deployapi.Deployment, error) |
|
| 74 |
+ UpdateDeployment(*deployapi.Deployment) (*deployapi.Deployment, error) |
|
| 75 |
+ DeleteDeployment(string) error |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 60 | 78 |
// Client is an OpenShift client object |
| 61 | 79 |
type Client struct {
|
| 62 | 80 |
*kubeclient.RESTClient |
| ... | ... |
@@ -195,3 +217,69 @@ func (c *Client) UpdateImageRepository(repo *imageapi.ImageRepository) (result * |
| 195 | 195 |
func (c *Client) CreateImageRepositoryMapping(mapping *imageapi.ImageRepositoryMapping) error {
|
| 196 | 196 |
return c.Post().Path("imageRepositoryMappings").Body(mapping).Do().Error()
|
| 197 | 197 |
} |
| 198 |
+ |
|
| 199 |
+// ListDeploymentConfigs takes a selector, and returns the list of deploymentConfigs that match that selector |
|
| 200 |
+func (c *Client) ListDeploymentConfigs(selector labels.Selector) (result *deployapi.DeploymentConfigList, err error) {
|
|
| 201 |
+ result = &deployapi.DeploymentConfigList{}
|
|
| 202 |
+ err = c.Get().Path("deploymentConfigs").SelectorParam("labels", selector).Do().Into(result)
|
|
| 203 |
+ return |
|
| 204 |
+} |
|
| 205 |
+ |
|
| 206 |
+// GetDeploymentConfig returns information about a particular deploymentConfig |
|
| 207 |
+func (c *Client) GetDeploymentConfig(id string) (result *deployapi.DeploymentConfig, err error) {
|
|
| 208 |
+ result = &deployapi.DeploymentConfig{}
|
|
| 209 |
+ err = c.Get().Path("deploymentConfigs").Path(id).Do().Into(result)
|
|
| 210 |
+ return |
|
| 211 |
+} |
|
| 212 |
+ |
|
| 213 |
+// CreateDeploymentConfig creates a new deploymentConfig |
|
| 214 |
+func (c *Client) CreateDeploymentConfig(deploymentConfig *deployapi.DeploymentConfig) (result *deployapi.DeploymentConfig, err error) {
|
|
| 215 |
+ result = &deployapi.DeploymentConfig{}
|
|
| 216 |
+ err = c.Post().Path("deploymentConfigs").Body(deploymentConfig).Do().Into(result)
|
|
| 217 |
+ return |
|
| 218 |
+} |
|
| 219 |
+ |
|
| 220 |
+// UpdateDeploymentConfig updates an existing deploymentConfig |
|
| 221 |
+func (c *Client) UpdateDeploymentConfig(deploymentConfig *deployapi.DeploymentConfig) (result *deployapi.DeploymentConfig, err error) {
|
|
| 222 |
+ result = &deployapi.DeploymentConfig{}
|
|
| 223 |
+ err = c.Put().Path("deploymentConfigs").Path(deploymentConfig.ID).Body(deploymentConfig).Do().Into(result)
|
|
| 224 |
+ return |
|
| 225 |
+} |
|
| 226 |
+ |
|
| 227 |
+// DeleteDeploymentConfig deletes an existing deploymentConfig. |
|
| 228 |
+func (c *Client) DeleteDeploymentConfig(id string) error {
|
|
| 229 |
+ return c.Delete().Path("deploymentConfigs").Path(id).Do().Error()
|
|
| 230 |
+} |
|
| 231 |
+ |
|
| 232 |
+// ListDeployments takes a selector, and returns the list of deployments that match that selector |
|
| 233 |
+func (c *Client) ListDeployments(selector labels.Selector) (result *deployapi.DeploymentList, err error) {
|
|
| 234 |
+ result = &deployapi.DeploymentList{}
|
|
| 235 |
+ err = c.Get().Path("deployments").SelectorParam("labels", selector).Do().Into(result)
|
|
| 236 |
+ return |
|
| 237 |
+} |
|
| 238 |
+ |
|
| 239 |
+// GetDeployment returns information about a particular deployment |
|
| 240 |
+func (c *Client) GetDeployment(id string) (result *deployapi.Deployment, err error) {
|
|
| 241 |
+ result = &deployapi.Deployment{}
|
|
| 242 |
+ err = c.Get().Path("deployments").Path(id).Do().Into(result)
|
|
| 243 |
+ return |
|
| 244 |
+} |
|
| 245 |
+ |
|
| 246 |
+// CreateDeployment creates a new deployment |
|
| 247 |
+func (c *Client) CreateDeployment(deployment *deployapi.Deployment) (result *deployapi.Deployment, err error) {
|
|
| 248 |
+ result = &deployapi.Deployment{}
|
|
| 249 |
+ err = c.Post().Path("deployments").Body(deployment).Do().Into(result)
|
|
| 250 |
+ return |
|
| 251 |
+} |
|
| 252 |
+ |
|
| 253 |
+// UpdateDeployment updates an existing deployment |
|
| 254 |
+func (c *Client) UpdateDeployment(deployment *deployapi.Deployment) (result *deployapi.Deployment, err error) {
|
|
| 255 |
+ result = &deployapi.Deployment{}
|
|
| 256 |
+ err = c.Put().Path("deployments").Path(deployment.ID).Body(deployment).Do().Into(result)
|
|
| 257 |
+ return |
|
| 258 |
+} |
|
| 259 |
+ |
|
| 260 |
+// DeleteDeployment deletes an existing replication deployment. |
|
| 261 |
+func (c *Client) DeleteDeployment(id string) error {
|
|
| 262 |
+ return c.Delete().Path("deployments").Path(id).Do().Error()
|
|
| 263 |
+} |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
| 5 | 5 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" |
| 6 | 6 |
buildapi "github.com/openshift/origin/pkg/build/api" |
| 7 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 7 | 8 |
imageapi "github.com/openshift/origin/pkg/image/api" |
| 8 | 9 |
) |
| 9 | 10 |
|
| ... | ... |
@@ -108,3 +109,53 @@ func (c *Fake) CreateImageRepositoryMapping(mapping *imageapi.ImageRepositoryMap |
| 108 | 108 |
c.Actions = append(c.Actions, FakeAction{Action: "create-imagerepository-mapping"})
|
| 109 | 109 |
return nil |
| 110 | 110 |
} |
| 111 |
+ |
|
| 112 |
+func (c *Fake) ListDeploymentConfigs(selector labels.Selector) (*deployapi.DeploymentConfigList, error) {
|
|
| 113 |
+ c.Actions = append(c.Actions, FakeAction{Action: "list-deploymentconfig"})
|
|
| 114 |
+ return &deployapi.DeploymentConfigList{}, nil
|
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+func (c *Fake) GetDeploymentConfig(id string) (*deployapi.DeploymentConfig, error) {
|
|
| 118 |
+ c.Actions = append(c.Actions, FakeAction{Action: "get-deploymentconfig"})
|
|
| 119 |
+ return &deployapi.DeploymentConfig{}, nil
|
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+func (c *Fake) CreateDeploymentConfig(config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
|
|
| 123 |
+ c.Actions = append(c.Actions, FakeAction{Action: "create-deploymentconfig"})
|
|
| 124 |
+ return &deployapi.DeploymentConfig{}, nil
|
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+func (c *Fake) UpdateDeploymentConfig(config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
|
|
| 128 |
+ c.Actions = append(c.Actions, FakeAction{Action: "update-deploymentconfig"})
|
|
| 129 |
+ return &deployapi.DeploymentConfig{}, nil
|
|
| 130 |
+} |
|
| 131 |
+ |
|
| 132 |
+func (c *Fake) DeleteDeploymentConfig(id string) error {
|
|
| 133 |
+ c.Actions = append(c.Actions, FakeAction{Action: "delete-deploymentconfig"})
|
|
| 134 |
+ return nil |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+func (c *Fake) ListDeployments(selector labels.Selector) (*deployapi.DeploymentList, error) {
|
|
| 138 |
+ c.Actions = append(c.Actions, FakeAction{Action: "list-deployment"})
|
|
| 139 |
+ return &deployapi.DeploymentList{}, nil
|
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+func (c *Fake) GetDeployment(id string) (*deployapi.Deployment, error) {
|
|
| 143 |
+ c.Actions = append(c.Actions, FakeAction{Action: "get-deployment"})
|
|
| 144 |
+ return &deployapi.Deployment{}, nil
|
|
| 145 |
+} |
|
| 146 |
+ |
|
| 147 |
+func (c *Fake) CreateDeployment(deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
|
|
| 148 |
+ c.Actions = append(c.Actions, FakeAction{Action: "create-deployment"})
|
|
| 149 |
+ return &deployapi.Deployment{}, nil
|
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+func (c *Fake) UpdateDeployment(deployment *deployapi.Deployment) (*deployapi.Deployment, error) {
|
|
| 153 |
+ c.Actions = append(c.Actions, FakeAction{Action: "update-deployment"})
|
|
| 154 |
+ return &deployapi.Deployment{}, nil
|
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+func (c *Fake) DeleteDeployment(id string) error {
|
|
| 158 |
+ c.Actions = append(c.Actions, FakeAction{Action: "delete-deployment"})
|
|
| 159 |
+ return nil |
|
| 160 |
+} |
| ... | ... |
@@ -44,6 +44,8 @@ import ( |
| 44 | 44 |
"github.com/openshift/origin/pkg/config" |
| 45 | 45 |
configapi "github.com/openshift/origin/pkg/config/api" |
| 46 | 46 |
_ "github.com/openshift/origin/pkg/config/api/v1beta1" |
| 47 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 48 |
+ deployclient "github.com/openshift/origin/pkg/deploy/client" |
|
| 47 | 49 |
imageapi "github.com/openshift/origin/pkg/image/api" |
| 48 | 50 |
) |
| 49 | 51 |
|
| ... | ... |
@@ -101,6 +103,8 @@ var parser = kubecfg.NewParser(map[string]interface{}{
|
| 101 | 101 |
"imageRepositories": imageapi.ImageRepository{},
|
| 102 | 102 |
"imageRepositoryMappings": imageapi.ImageRepositoryMapping{},
|
| 103 | 103 |
"config": configapi.Config{},
|
| 104 |
+ "deployments": deployapi.Deployment{},
|
|
| 105 |
+ "deploymentConfigs": deployapi.DeploymentConfig{},
|
|
| 104 | 106 |
}) |
| 105 | 107 |
|
| 106 | 108 |
func prettyWireStorage() string {
|
| ... | ... |
@@ -198,6 +202,7 @@ func (c *KubeConfig) Run() {
|
| 198 | 198 |
fmt.Printf("Server Version: %#v\n", got)
|
| 199 | 199 |
os.Exit(0) |
| 200 | 200 |
} |
| 201 |
+ |
|
| 201 | 202 |
if c.PreventSkew {
|
| 202 | 203 |
got, err := kubeClient.ServerVersion() |
| 203 | 204 |
if err != nil {
|
| ... | ... |
@@ -227,6 +232,8 @@ func (c *KubeConfig) Run() {
|
| 227 | 227 |
"images": {"Image", client.RESTClient},
|
| 228 | 228 |
"imageRepositories": {"ImageRepository", client.RESTClient},
|
| 229 | 229 |
"imageRepositoryMappings": {"ImageRepositoryMapping", client.RESTClient},
|
| 230 |
+ "deployments": {"Deployment", client.RESTClient},
|
|
| 231 |
+ "deploymentConfigs": {"DeploymentConfig", client.RESTClient},
|
|
| 230 | 232 |
} |
| 231 | 233 |
|
| 232 | 234 |
matchFound := c.executeConfigRequest(method, clients) || c.executeControllerRequest(method, kubeClient) || c.executeAPIRequest(method, clients) |
| ... | ... |
@@ -445,6 +452,7 @@ func humanReadablePrinter() *kubecfg.HumanReadablePrinter {
|
| 445 | 445 |
// Add Handler calls here to support additional types |
| 446 | 446 |
build.RegisterPrintHandlers(printer) |
| 447 | 447 |
image.RegisterPrintHandlers(printer) |
| 448 |
+ deployclient.RegisterPrintHandlers(printer) |
|
| 448 | 449 |
|
| 449 | 450 |
return printer |
| 450 | 451 |
} |
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
"path" |
| 7 | 7 |
"time" |
| 8 | 8 |
|
| 9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 9 | 10 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" |
| 10 | 11 |
kubeclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
| 11 | 12 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller" |
| ... | ... |
@@ -26,6 +27,8 @@ import ( |
| 26 | 26 |
"github.com/google/cadvisor/client" |
| 27 | 27 |
"github.com/spf13/cobra" |
| 28 | 28 |
|
| 29 |
+ _ "github.com/openshift/origin/pkg/api" |
|
| 30 |
+ _ "github.com/openshift/origin/pkg/api/v1beta1" |
|
| 29 | 31 |
"github.com/openshift/origin/pkg/build" |
| 30 | 32 |
buildapi "github.com/openshift/origin/pkg/build/api" |
| 31 | 33 |
buildregistry "github.com/openshift/origin/pkg/build/registry/build" |
| ... | ... |
@@ -35,6 +38,10 @@ import ( |
| 35 | 35 |
"github.com/openshift/origin/pkg/build/webhook/github" |
| 36 | 36 |
osclient "github.com/openshift/origin/pkg/client" |
| 37 | 37 |
"github.com/openshift/origin/pkg/cmd/util/docker" |
| 38 |
+ "github.com/openshift/origin/pkg/deploy" |
|
| 39 |
+ deployregistry "github.com/openshift/origin/pkg/deploy/registry/deploy" |
|
| 40 |
+ deployconfigregistry "github.com/openshift/origin/pkg/deploy/registry/deployconfig" |
|
| 41 |
+ deployetcd "github.com/openshift/origin/pkg/deploy/registry/etcd" |
|
| 38 | 42 |
imageetcd "github.com/openshift/origin/pkg/image/registry/etcd" |
| 39 | 43 |
"github.com/openshift/origin/pkg/image/registry/image" |
| 40 | 44 |
"github.com/openshift/origin/pkg/image/registry/imagerepository" |
| ... | ... |
@@ -134,6 +141,7 @@ func (c *config) startAllInOne() {
|
| 134 | 134 |
c.runScheduler() |
| 135 | 135 |
c.runReplicationController() |
| 136 | 136 |
c.runBuildController() |
| 137 |
+ c.runDeploymentController() |
|
| 137 | 138 |
|
| 138 | 139 |
select {}
|
| 139 | 140 |
} |
| ... | ... |
@@ -144,6 +152,7 @@ func (c *config) startMaster() {
|
| 144 | 144 |
c.runScheduler() |
| 145 | 145 |
c.runReplicationController() |
| 146 | 146 |
c.runBuildController() |
| 147 |
+ c.runDeploymentController() |
|
| 147 | 148 |
|
| 148 | 149 |
select {}
|
| 149 | 150 |
} |
| ... | ... |
@@ -167,6 +176,7 @@ func (c *config) runApiserver() {
|
| 167 | 167 |
etcdClient, etcdServers := c.getEtcdClient() |
| 168 | 168 |
|
| 169 | 169 |
imageRegistry := imageetcd.NewEtcd(etcdClient) |
| 170 |
+ deployEtcd := deployetcd.NewEtcd(etcdClient) |
|
| 170 | 171 |
|
| 171 | 172 |
// initialize OpenShift API |
| 172 | 173 |
storage := map[string]apiserver.RESTStorage{
|
| ... | ... |
@@ -176,6 +186,8 @@ func (c *config) runApiserver() {
|
| 176 | 176 |
"imageRepositories": imagerepository.NewREST(imageRegistry), |
| 177 | 177 |
"imageRepositoryMappings": imagerepositorymapping.NewREST(imageRegistry, imageRegistry), |
| 178 | 178 |
"templateConfigs": template.NewStorage(), |
| 179 |
+ "deployments": deployregistry.NewREST(deployEtcd), |
|
| 180 |
+ "deploymentConfigs": deployconfigregistry.NewREST(deployEtcd), |
|
| 179 | 181 |
} |
| 180 | 182 |
|
| 181 | 183 |
osMux := http.NewServeMux() |
| ... | ... |
@@ -327,6 +339,17 @@ func (c *config) runBuildController() {
|
| 327 | 327 |
buildController.Run(10 * time.Second) |
| 328 | 328 |
} |
| 329 | 329 |
|
| 330 |
+func (c *config) runDeploymentController() {
|
|
| 331 |
+ env := []api.EnvVar{
|
|
| 332 |
+ api.EnvVar{Name: "KUBERNETES_MASTER", Value: "http://" + c.ListenAddr},
|
|
| 333 |
+ } |
|
| 334 |
+ kubeClient := c.getKubeClient() |
|
| 335 |
+ osClient := c.getOsClient() |
|
| 336 |
+ |
|
| 337 |
+ deployController := deploy.NewDeploymentController(kubeClient, osClient, env) |
|
| 338 |
+ deployController.Run(10 * time.Second) |
|
| 339 |
+} |
|
| 340 |
+ |
|
| 330 | 341 |
func env(key string, defaultValue string) string {
|
| 331 | 342 |
val := os.Getenv(key) |
| 332 | 343 |
if len(val) == 0 {
|
| 333 | 344 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,14 @@ |
| 0 |
+package api |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+func init() {
|
|
| 7 |
+ runtime.AddKnownTypes("",
|
|
| 8 |
+ DeploymentList{},
|
|
| 9 |
+ Deployment{},
|
|
| 10 |
+ DeploymentConfigList{},
|
|
| 11 |
+ DeploymentConfig{},
|
|
| 12 |
+ ) |
|
| 13 |
+} |
| 0 | 14 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,81 @@ |
| 0 |
+package api |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+// CustomPodDeploymentStrategy describes a deployment carried out by a custom pod. |
|
| 7 |
+type CustomPodDeploymentStrategy struct {
|
|
| 8 |
+ Image string `json:"image,omitempty" yaml:"image,omitempty"` |
|
| 9 |
+ Environment []api.EnvVar `json:"environment,omitempty" yaml:"environment,omitempty"` |
|
| 10 |
+} |
|
| 11 |
+ |
|
| 12 |
+// DeploymentStrategy describes how to perform a deployment. |
|
| 13 |
+type DeploymentStrategy struct {
|
|
| 14 |
+ Type string `json:"type,omitempty" yaml:"type,omitempty"` |
|
| 15 |
+ CustomPod *CustomPodDeploymentStrategy `json:"customPod,omitempty" yaml:"customPod,omitempty"` |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+// DeploymentTemplate contains all the necessary information to create a Deployment from a |
|
| 19 |
+// DeploymentStrategy. |
|
| 20 |
+type DeploymentTemplate struct {
|
|
| 21 |
+ Strategy DeploymentStrategy `json:"strategy,omitempty" yaml:"strategy,omitempty"` |
|
| 22 |
+ ControllerTemplate api.ReplicationControllerState `json:"controllerTemplate,omitempty" yaml:"controllerTemplate,omitempty"` |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+// DeploymentState decribes the possible states a Deployment can be in. |
|
| 26 |
+type DeploymentState string |
|
| 27 |
+ |
|
| 28 |
+const ( |
|
| 29 |
+ DeploymentNew DeploymentState = "new" |
|
| 30 |
+ DeploymentPending DeploymentState = "pending" |
|
| 31 |
+ DeploymentRunning DeploymentState = "running" |
|
| 32 |
+ DeploymentComplete DeploymentState = "complete" |
|
| 33 |
+ DeploymentFailed DeploymentState = "failed" |
|
| 34 |
+) |
|
| 35 |
+ |
|
| 36 |
+// A Deployment represents a single unique realization of a DeploymentConfig. |
|
| 37 |
+type Deployment struct {
|
|
| 38 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 39 |
+ Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` |
|
| 40 |
+ Strategy DeploymentStrategy `json:"strategy,omitempty" yaml:"strategy,omitempty"` |
|
| 41 |
+ ControllerTemplate api.ReplicationControllerState `json:"controllerTemplate,omitempty" yaml:"controllerTemplate,omitempty"` |
|
| 42 |
+ State DeploymentState `json:"state,omitempty" yaml:"state,omitempty"` |
|
| 43 |
+ ConfigID string `json:"configId,omitempty" yaml:"configId,omitempty"` |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+// DeploymentTriggerPolicy describes the possible triggers that result in a new Deployment. |
|
| 47 |
+type DeploymentTriggerPolicy struct {
|
|
| 48 |
+ Type DeploymentTriggerType `json:"type,omitempty" yaml:"type,omitempty"` |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+type DeploymentTriggerType string |
|
| 52 |
+ |
|
| 53 |
+const ( |
|
| 54 |
+ DeploymentTriggerOnImageChange DeploymentTriggerType = "image-change" |
|
| 55 |
+ DeploymentTriggerOnConfigChange DeploymentTriggerType = "config-change" |
|
| 56 |
+ DeploymentTriggerManual DeploymentTriggerType = "manual" |
|
| 57 |
+) |
|
| 58 |
+ |
|
| 59 |
+// DeploymentConfig represents a configuration for a single deployment of a replication controller: |
|
| 60 |
+// what the template for the deployment, how new deployments are triggered, what the current |
|
| 61 |
+// deployed state is. |
|
| 62 |
+type DeploymentConfig struct {
|
|
| 63 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 64 |
+ Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` |
|
| 65 |
+ TriggerPolicy DeploymentTriggerPolicy `json:"triggerPolicy,omitempty" yaml:"triggerPolicy,omitempty"` |
|
| 66 |
+ Template DeploymentTemplate `json:"template,omitempty" yaml:"template,omitempty"` |
|
| 67 |
+ CurrentState api.ReplicationControllerState `json:"currentState" yaml:"currentState,omitempty"` |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+// A DeploymentConfigList is a collection of deployment configs |
|
| 71 |
+type DeploymentConfigList struct {
|
|
| 72 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 73 |
+ Items []DeploymentConfig `json:"items,omitempty" yaml:"items,omitempty"` |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+// A DeploymentList is a collection of deployments. |
|
| 77 |
+type DeploymentList struct {
|
|
| 78 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 79 |
+ Items []Deployment `json:"items,omitempty" yaml:"items,omitempty"` |
|
| 80 |
+} |
| 0 | 81 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,14 @@ |
| 0 |
+package v1beta1 |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+func init() {
|
|
| 7 |
+ runtime.AddKnownTypes("v1beta1",
|
|
| 8 |
+ DeploymentList{},
|
|
| 9 |
+ Deployment{},
|
|
| 10 |
+ DeploymentConfigList{},
|
|
| 11 |
+ DeploymentConfig{},
|
|
| 12 |
+ ) |
|
| 13 |
+} |
| 0 | 14 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,81 @@ |
| 0 |
+package v1beta1 |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+// CustomPodDeploymentStrategy describes a deployment carried out by a custom pod. |
|
| 7 |
+type CustomPodDeploymentStrategy struct {
|
|
| 8 |
+ Image string `json:"image,omitempty" yaml:"image,omitempty"` |
|
| 9 |
+ Environment []api.EnvVar `json:"environment,omitempty" yaml:"environment,omitempty"` |
|
| 10 |
+} |
|
| 11 |
+ |
|
| 12 |
+// DeploymentStrategy describes how to perform a deployment. |
|
| 13 |
+type DeploymentStrategy struct {
|
|
| 14 |
+ Type string `json:"type,omitempty" yaml:"type,omitempty"` |
|
| 15 |
+ CustomPod *CustomPodDeploymentStrategy `json:"customPod,omitempty" yaml:"customPod,omitempty"` |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+// DeploymentTemplate contains all the necessary information to create a Deployment from a |
|
| 19 |
+// DeploymentStrategy. |
|
| 20 |
+type DeploymentTemplate struct {
|
|
| 21 |
+ Strategy DeploymentStrategy `json:"strategy,omitempty" yaml:"strategy,omitempty"` |
|
| 22 |
+ ControllerTemplate api.ReplicationControllerState `json:"controllerTemplate,omitempty" yaml:"controllerTemplate,omitempty"` |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+// DeploymentState decribes the possible states a Deployment can be in. |
|
| 26 |
+type DeploymentState string |
|
| 27 |
+ |
|
| 28 |
+const ( |
|
| 29 |
+ DeploymentNew DeploymentState = "new" |
|
| 30 |
+ DeploymentPending DeploymentState = "pending" |
|
| 31 |
+ DeploymentRunning DeploymentState = "running" |
|
| 32 |
+ DeploymentComplete DeploymentState = "complete" |
|
| 33 |
+ DeploymentFailed DeploymentState = "failed" |
|
| 34 |
+) |
|
| 35 |
+ |
|
| 36 |
+// A Deployment represents a single unique realization of a DeploymentConfig. |
|
| 37 |
+type Deployment struct {
|
|
| 38 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 39 |
+ Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` |
|
| 40 |
+ Strategy DeploymentStrategy `json:"strategy,omitempty" yaml:"strategy,omitempty"` |
|
| 41 |
+ ControllerTemplate api.ReplicationControllerState `json:"controllerTemplate,omitempty" yaml:"controllerTemplate,omitempty"` |
|
| 42 |
+ State DeploymentState `json:"state,omitempty" yaml:"state,omitempty"` |
|
| 43 |
+ ConfigID string `json:"configId,omitempty" yaml:"configId,omitempty"` |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+// DeploymentTriggerPolicy describes the possible triggers that result in a new Deployment. |
|
| 47 |
+type DeploymentTriggerPolicy struct {
|
|
| 48 |
+ Type DeploymentTriggerType `json:"type,omitempty" yaml:"type,omitempty"` |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+type DeploymentTriggerType string |
|
| 52 |
+ |
|
| 53 |
+const ( |
|
| 54 |
+ DeploymentTriggerOnImageChange DeploymentTriggerType = "image-change" |
|
| 55 |
+ DeploymentTriggerOnConfigChange DeploymentTriggerType = "config-change" |
|
| 56 |
+ DeploymentTriggerManual DeploymentTriggerType = "manual" |
|
| 57 |
+) |
|
| 58 |
+ |
|
| 59 |
+// DeploymentConfig represents a configuration for a single deployment of a replication controller: |
|
| 60 |
+// what the template for the deployment, how new deployments are triggered, what the current |
|
| 61 |
+// deployed state is. |
|
| 62 |
+type DeploymentConfig struct {
|
|
| 63 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 64 |
+ Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` |
|
| 65 |
+ TriggerPolicy DeploymentTriggerPolicy `json:"triggerPolicy,omitempty" yaml:"triggerPolicy,omitempty"` |
|
| 66 |
+ Template DeploymentTemplate `json:"template,omitempty" yaml:"template,omitempty"` |
|
| 67 |
+ CurrentState api.ReplicationControllerState `json:"currentState" yaml:"currentState,omitempty"` |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+// A DeploymentConfigList is a collection of deployment configs |
|
| 71 |
+type DeploymentConfigList struct {
|
|
| 72 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 73 |
+ Items []DeploymentConfig `json:"items,omitempty" yaml:"items,omitempty"` |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+// A DeploymentList is a collection of deployments. |
|
| 77 |
+type DeploymentList struct {
|
|
| 78 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 79 |
+ Items []Deployment `json:"items,omitempty" yaml:"items,omitempty"` |
|
| 80 |
+} |
| 0 | 81 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,56 @@ |
| 0 |
+package validation |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" |
|
| 4 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// TODO: These tests validate the ReplicationControllerState in a Deployment or DeploymentConfig. |
|
| 8 |
+// The upstream validation API isn't factored currently to allow this; we'll make a PR to |
|
| 9 |
+// upstream and fix when it goes in. |
|
| 10 |
+ |
|
| 11 |
+func ValidateDeployment(deployment *deployapi.Deployment) errors.ErrorList {
|
|
| 12 |
+ result := validateDeploymentStrategy(&deployment.Strategy).Prefix("Strategy")
|
|
| 13 |
+ |
|
| 14 |
+ // TODO: validate ReplicationControllerState |
|
| 15 |
+ |
|
| 16 |
+ return result |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+func validateDeploymentStrategy(strategy *deployapi.DeploymentStrategy) errors.ErrorList {
|
|
| 20 |
+ result := errors.ErrorList{}
|
|
| 21 |
+ |
|
| 22 |
+ if len(strategy.Type) == 0 {
|
|
| 23 |
+ result = append(result, errors.NewFieldRequired("Type", ""))
|
|
| 24 |
+ } |
|
| 25 |
+ |
|
| 26 |
+ if strategy.CustomPod == nil {
|
|
| 27 |
+ result = append(result, errors.NewFieldRequired("CustomPod", nil))
|
|
| 28 |
+ } else {
|
|
| 29 |
+ if len(strategy.CustomPod.Image) == 0 {
|
|
| 30 |
+ result = append(result, errors.NewFieldRequired("CustomPod.Image", ""))
|
|
| 31 |
+ } |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ return result |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+func validateTriggerPolicy(policy *deployapi.DeploymentTriggerPolicy) errors.ErrorList {
|
|
| 38 |
+ result := errors.ErrorList{}
|
|
| 39 |
+ |
|
| 40 |
+ if len(policy.Type) == 0 {
|
|
| 41 |
+ result = append(result, errors.NewFieldRequired("Type", ""))
|
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ return result |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func ValidateDeploymentConfig(config *deployapi.DeploymentConfig) errors.ErrorList {
|
|
| 48 |
+ result := errors.ErrorList{}
|
|
| 49 |
+ result = append(result, validateTriggerPolicy(&config.TriggerPolicy).Prefix("TriggerPolicy")...)
|
|
| 50 |
+ result = append(result, validateDeploymentStrategy(&config.Template.Strategy).Prefix("Template.Strategy")...)
|
|
| 51 |
+ |
|
| 52 |
+ // TODO: validate ReplicationControllerState |
|
| 53 |
+ |
|
| 54 |
+ return result |
|
| 55 |
+} |
| 0 | 56 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,175 @@ |
| 0 |
+package validation |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" |
|
| 6 |
+ "github.com/openshift/origin/pkg/deploy/api" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// Convenience methods |
|
| 10 |
+ |
|
| 11 |
+func manualTrigger() api.DeploymentTriggerPolicy {
|
|
| 12 |
+ return api.DeploymentTriggerPolicy{
|
|
| 13 |
+ Type: api.DeploymentTriggerManual, |
|
| 14 |
+ } |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func okTemplate() api.DeploymentTemplate {
|
|
| 18 |
+ return api.DeploymentTemplate{
|
|
| 19 |
+ Strategy: okStrategy(), |
|
| 20 |
+ } |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func okStrategy() api.DeploymentStrategy {
|
|
| 24 |
+ return api.DeploymentStrategy{
|
|
| 25 |
+ Type: "customPod", |
|
| 26 |
+ CustomPod: okCustomPod(), |
|
| 27 |
+ } |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func okCustomPod() *api.CustomPodDeploymentStrategy {
|
|
| 31 |
+ return &api.CustomPodDeploymentStrategy{
|
|
| 32 |
+ Image: "openshift/kube-deploy", |
|
| 33 |
+ } |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+// TODO: test validation errors for ReplicationControllerTemplates |
|
| 37 |
+ |
|
| 38 |
+func TestValidateDeploymentOK(t *testing.T) {
|
|
| 39 |
+ errs := ValidateDeployment(&api.Deployment{
|
|
| 40 |
+ Strategy: okStrategy(), |
|
| 41 |
+ }) |
|
| 42 |
+ if len(errs) > 0 {
|
|
| 43 |
+ t.Errorf("Unxpected non-empty error list: %#v", errs)
|
|
| 44 |
+ } |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func TestValidateDeploymentMissingFields(t *testing.T) {
|
|
| 48 |
+ errorCases := map[string]struct {
|
|
| 49 |
+ D api.Deployment |
|
| 50 |
+ T errors.ValidationErrorType |
|
| 51 |
+ F string |
|
| 52 |
+ }{
|
|
| 53 |
+ "missing Strategy.Type": {
|
|
| 54 |
+ api.Deployment{
|
|
| 55 |
+ Strategy: api.DeploymentStrategy{
|
|
| 56 |
+ CustomPod: okCustomPod(), |
|
| 57 |
+ }, |
|
| 58 |
+ }, |
|
| 59 |
+ errors.ValidationErrorTypeRequired, |
|
| 60 |
+ "Strategy.Type", |
|
| 61 |
+ }, |
|
| 62 |
+ "missing Strategy.CustomPod": {
|
|
| 63 |
+ api.Deployment{
|
|
| 64 |
+ Strategy: api.DeploymentStrategy{
|
|
| 65 |
+ Type: "customPod", |
|
| 66 |
+ }, |
|
| 67 |
+ }, |
|
| 68 |
+ errors.ValidationErrorTypeRequired, |
|
| 69 |
+ "Strategy.CustomPod", |
|
| 70 |
+ }, |
|
| 71 |
+ "missing Strategy.CustomPod.Image": {
|
|
| 72 |
+ api.Deployment{
|
|
| 73 |
+ Strategy: api.DeploymentStrategy{
|
|
| 74 |
+ Type: "customPod", |
|
| 75 |
+ CustomPod: &api.CustomPodDeploymentStrategy{},
|
|
| 76 |
+ }, |
|
| 77 |
+ }, |
|
| 78 |
+ errors.ValidationErrorTypeRequired, |
|
| 79 |
+ "Strategy.CustomPod.Image", |
|
| 80 |
+ }, |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ for k, v := range errorCases {
|
|
| 84 |
+ errs := ValidateDeployment(&v.D) |
|
| 85 |
+ if len(errs) == 0 {
|
|
| 86 |
+ t.Errorf("Expected failure for scenario %s", k)
|
|
| 87 |
+ } |
|
| 88 |
+ for i := range errs {
|
|
| 89 |
+ if errs[i].(errors.ValidationError).Type != v.T {
|
|
| 90 |
+ t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
|
|
| 91 |
+ } |
|
| 92 |
+ if errs[i].(errors.ValidationError).Field != v.F {
|
|
| 93 |
+ t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
|
|
| 94 |
+ } |
|
| 95 |
+ } |
|
| 96 |
+ } |
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+func TestValidateDeploymentConfigOK(t *testing.T) {
|
|
| 100 |
+ errs := ValidateDeploymentConfig(&api.DeploymentConfig{
|
|
| 101 |
+ TriggerPolicy: manualTrigger(), |
|
| 102 |
+ Template: okTemplate(), |
|
| 103 |
+ }) |
|
| 104 |
+ |
|
| 105 |
+ if len(errs) > 0 {
|
|
| 106 |
+ t.Errorf("Unxpected non-empty error list: %#v", errs)
|
|
| 107 |
+ } |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+func TestValidateDeploymentConfigMissingFields(t *testing.T) {
|
|
| 111 |
+ errorCases := map[string]struct {
|
|
| 112 |
+ D api.DeploymentConfig |
|
| 113 |
+ T errors.ValidationErrorType |
|
| 114 |
+ F string |
|
| 115 |
+ }{
|
|
| 116 |
+ "missing TriggerPolicy.Type": {
|
|
| 117 |
+ api.DeploymentConfig{Template: okTemplate()},
|
|
| 118 |
+ errors.ValidationErrorTypeRequired, |
|
| 119 |
+ "TriggerPolicy.Type", |
|
| 120 |
+ }, |
|
| 121 |
+ "missing Strategy.Type": {
|
|
| 122 |
+ api.DeploymentConfig{
|
|
| 123 |
+ TriggerPolicy: manualTrigger(), |
|
| 124 |
+ Template: api.DeploymentTemplate{
|
|
| 125 |
+ Strategy: api.DeploymentStrategy{
|
|
| 126 |
+ CustomPod: okCustomPod(), |
|
| 127 |
+ }, |
|
| 128 |
+ }, |
|
| 129 |
+ }, |
|
| 130 |
+ errors.ValidationErrorTypeRequired, |
|
| 131 |
+ "Template.Strategy.Type", |
|
| 132 |
+ }, |
|
| 133 |
+ "missing Strategy.CustomPod": {
|
|
| 134 |
+ api.DeploymentConfig{
|
|
| 135 |
+ TriggerPolicy: manualTrigger(), |
|
| 136 |
+ Template: api.DeploymentTemplate{
|
|
| 137 |
+ Strategy: api.DeploymentStrategy{
|
|
| 138 |
+ Type: "customPod", |
|
| 139 |
+ }, |
|
| 140 |
+ }, |
|
| 141 |
+ }, |
|
| 142 |
+ errors.ValidationErrorTypeRequired, |
|
| 143 |
+ "Template.Strategy.CustomPod", |
|
| 144 |
+ }, |
|
| 145 |
+ "missing Template.Strategy.CustomPod.Image": {
|
|
| 146 |
+ api.DeploymentConfig{
|
|
| 147 |
+ TriggerPolicy: manualTrigger(), |
|
| 148 |
+ Template: api.DeploymentTemplate{
|
|
| 149 |
+ Strategy: api.DeploymentStrategy{
|
|
| 150 |
+ Type: "customPod", |
|
| 151 |
+ CustomPod: &api.CustomPodDeploymentStrategy{},
|
|
| 152 |
+ }, |
|
| 153 |
+ }, |
|
| 154 |
+ }, |
|
| 155 |
+ errors.ValidationErrorTypeRequired, |
|
| 156 |
+ "Template.Strategy.CustomPod.Image", |
|
| 157 |
+ }, |
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 160 |
+ for k, v := range errorCases {
|
|
| 161 |
+ errs := ValidateDeploymentConfig(&v.D) |
|
| 162 |
+ if len(errs) == 0 {
|
|
| 163 |
+ t.Errorf("Expected failure for scenario %s", k)
|
|
| 164 |
+ } |
|
| 165 |
+ for i := range errs {
|
|
| 166 |
+ if errs[i].(errors.ValidationError).Type != v.T {
|
|
| 167 |
+ t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
|
|
| 168 |
+ } |
|
| 169 |
+ if errs[i].(errors.ValidationError).Field != v.F {
|
|
| 170 |
+ t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
|
|
| 171 |
+ } |
|
| 172 |
+ } |
|
| 173 |
+ } |
|
| 174 |
+} |
| 0 | 175 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,50 @@ |
| 0 |
+package client |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg" |
|
| 7 |
+ "github.com/openshift/origin/pkg/deploy/api" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+var deploymentColumns = []string{"ID", "State"}
|
|
| 11 |
+var deploymentConfigColumns = []string{"ID", "Trigger Policy"}
|
|
| 12 |
+ |
|
| 13 |
+// RegisterPrintHandlers registers human-readable printers for deploy types. |
|
| 14 |
+func RegisterPrintHandlers(printer *kubecfg.HumanReadablePrinter) {
|
|
| 15 |
+ printer.Handler(deploymentColumns, printDeployment) |
|
| 16 |
+ printer.Handler(deploymentColumns, printDeploymentList) |
|
| 17 |
+ printer.Handler(deploymentConfigColumns, printDeploymentConfig) |
|
| 18 |
+ printer.Handler(deploymentConfigColumns, printDeploymentConfigList) |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func printDeployment(d *api.Deployment, w io.Writer) error {
|
|
| 22 |
+ _, err := fmt.Fprintf(w, "%s\t%s\n", d.ID, d.State) |
|
| 23 |
+ return err |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func printDeploymentList(list *api.DeploymentList, w io.Writer) error {
|
|
| 27 |
+ for _, d := range list.Items {
|
|
| 28 |
+ if err := printDeployment(&d, w); err != nil {
|
|
| 29 |
+ return err |
|
| 30 |
+ } |
|
| 31 |
+ } |
|
| 32 |
+ |
|
| 33 |
+ return nil |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func printDeploymentConfig(dc *api.DeploymentConfig, w io.Writer) error {
|
|
| 37 |
+ _, err := fmt.Fprintf(w, "%s\t%s\n", dc.ID, dc.TriggerPolicy) |
|
| 38 |
+ return err |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+func printDeploymentConfigList(list *api.DeploymentConfigList, w io.Writer) error {
|
|
| 42 |
+ for _, dc := range list.Items {
|
|
| 43 |
+ if err := printDeploymentConfig(&dc, w); err != nil {
|
|
| 44 |
+ return err |
|
| 45 |
+ } |
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ return nil |
|
| 49 |
+} |
| 0 | 50 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,214 @@ |
| 0 |
+package deploy |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "time" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 6 |
+ kubeclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
| 7 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 8 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 9 |
+ "github.com/golang/glog" |
|
| 10 |
+ osclient "github.com/openshift/origin/pkg/client" |
|
| 11 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+// A DeploymentController is responsible for executing Deployment objects stored in etcd |
|
| 15 |
+type DeploymentController struct {
|
|
| 16 |
+ osClient osclient.Interface |
|
| 17 |
+ kubeClient kubeclient.Interface |
|
| 18 |
+ syncTicker <-chan time.Time |
|
| 19 |
+ stateHandler DeploymentStateHandler |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+// DeploymentStateHandler holds methods that handle the possible deployment states. |
|
| 23 |
+type DeploymentStateHandler interface {
|
|
| 24 |
+ HandleNew(*deployapi.Deployment) error |
|
| 25 |
+ HandlePending(*deployapi.Deployment) error |
|
| 26 |
+ HandleRunning(*deployapi.Deployment) error |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// DefaultDeploymentRunner is the default implementation of DeploymentRunner interface. |
|
| 30 |
+type DefaultDeploymentHandler struct {
|
|
| 31 |
+ osClient osclient.Interface |
|
| 32 |
+ kubeClient kubeclient.Interface |
|
| 33 |
+ environment []api.EnvVar |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+// NewDeploymentController creates a new DeploymentController. |
|
| 37 |
+func NewDeploymentController(kubeClient kubeclient.Interface, osClient osclient.Interface, initialEnvironment []api.EnvVar) *DeploymentController {
|
|
| 38 |
+ dc := &DeploymentController{
|
|
| 39 |
+ kubeClient: kubeClient, |
|
| 40 |
+ osClient: osClient, |
|
| 41 |
+ stateHandler: &DefaultDeploymentHandler{
|
|
| 42 |
+ osClient: osClient, |
|
| 43 |
+ kubeClient: kubeClient, |
|
| 44 |
+ environment: initialEnvironment, |
|
| 45 |
+ }, |
|
| 46 |
+ } |
|
| 47 |
+ return dc |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// Run begins watching and synchronizing deployment states. |
|
| 51 |
+func (dc *DeploymentController) Run(period time.Duration) {
|
|
| 52 |
+ dc.syncTicker = time.Tick(period) |
|
| 53 |
+ go util.Forever(func() { dc.synchronize() }, period)
|
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// The main synchronization loop. Iterates through all deployments and handles the current state |
|
| 57 |
+// for each. |
|
| 58 |
+func (dc *DeploymentController) synchronize() {
|
|
| 59 |
+ deployments, err := dc.osClient.ListDeployments(labels.Everything()) |
|
| 60 |
+ if err != nil {
|
|
| 61 |
+ glog.Errorf("Synchronization error: %v (%#v)", err, err)
|
|
| 62 |
+ return |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ for ix := range deployments.Items {
|
|
| 66 |
+ id := deployments.Items[ix].ID |
|
| 67 |
+ deployment, err := dc.osClient.GetDeployment(id) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ glog.Errorf("Got error retrieving deployment with id %s -- %v", id, err)
|
|
| 70 |
+ continue |
|
| 71 |
+ } |
|
| 72 |
+ err = dc.syncDeployment(deployment) |
|
| 73 |
+ if err != nil {
|
|
| 74 |
+ glog.Errorf("Error synchronizing: %#v", err)
|
|
| 75 |
+ } |
|
| 76 |
+ } |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Invokes the appropriate handler for the current state of the given deployment. |
|
| 80 |
+func (dc *DeploymentController) syncDeployment(deployment *deployapi.Deployment) error {
|
|
| 81 |
+ glog.Infof("Synchronizing deployment id: %v state: %v resourceVersion: %v", deployment.ID, deployment.State, deployment.ResourceVersion)
|
|
| 82 |
+ var err error = nil |
|
| 83 |
+ switch deployment.State {
|
|
| 84 |
+ case deployapi.DeploymentNew: |
|
| 85 |
+ err = dc.stateHandler.HandleNew(deployment) |
|
| 86 |
+ case deployapi.DeploymentPending: |
|
| 87 |
+ err = dc.stateHandler.HandlePending(deployment) |
|
| 88 |
+ case deployapi.DeploymentRunning: |
|
| 89 |
+ err = dc.stateHandler.HandleRunning(deployment) |
|
| 90 |
+ } |
|
| 91 |
+ return err |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+func (dh *DefaultDeploymentHandler) saveDeployment(deployment *deployapi.Deployment) error {
|
|
| 95 |
+ glog.Infof("Saving deployment %v state: %v", deployment.ID, deployment.State)
|
|
| 96 |
+ _, err := dh.osClient.UpdateDeployment(deployment) |
|
| 97 |
+ if err != nil {
|
|
| 98 |
+ glog.Errorf("Received error while saving deployment %v: %v", deployment.ID, err)
|
|
| 99 |
+ } |
|
| 100 |
+ return err |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+func (dh *DefaultDeploymentHandler) makeDeploymentPod(deployment *deployapi.Deployment) api.Pod {
|
|
| 104 |
+ podID := deploymentPodID(deployment) |
|
| 105 |
+ |
|
| 106 |
+ envVars := deployment.Strategy.CustomPod.Environment |
|
| 107 |
+ envVars = append(envVars, api.EnvVar{Name: "KUBERNETES_DEPLOYMENT_ID", Value: deployment.ID})
|
|
| 108 |
+ for _, env := range dh.environment {
|
|
| 109 |
+ envVars = append(envVars, env) |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ return api.Pod{
|
|
| 113 |
+ JSONBase: api.JSONBase{
|
|
| 114 |
+ ID: podID, |
|
| 115 |
+ }, |
|
| 116 |
+ DesiredState: api.PodState{
|
|
| 117 |
+ Manifest: api.ContainerManifest{
|
|
| 118 |
+ Version: "v1beta1", |
|
| 119 |
+ Containers: []api.Container{
|
|
| 120 |
+ {
|
|
| 121 |
+ Name: "deployment", |
|
| 122 |
+ Image: deployment.Strategy.CustomPod.Image, |
|
| 123 |
+ Env: envVars, |
|
| 124 |
+ RestartPolicy: "runOnce", |
|
| 125 |
+ }, |
|
| 126 |
+ }, |
|
| 127 |
+ }, |
|
| 128 |
+ RestartPolicy: api.RestartPolicy{
|
|
| 129 |
+ Type: api.RestartNever, |
|
| 130 |
+ }, |
|
| 131 |
+ }, |
|
| 132 |
+ } |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+func deploymentPodID(deployment *deployapi.Deployment) string {
|
|
| 136 |
+ return "deploy-" + deployment.ID |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+// Handler for a deployment in the 'new' state. |
|
| 140 |
+func (dh *DefaultDeploymentHandler) HandleNew(deployment *deployapi.Deployment) error {
|
|
| 141 |
+ deploymentPod := dh.makeDeploymentPod(deployment) |
|
| 142 |
+ glog.Infof("Attempting to create deployment pod: %+v", deploymentPod)
|
|
| 143 |
+ if pod, err := dh.kubeClient.CreatePod(deploymentPod); err != nil {
|
|
| 144 |
+ glog.Warningf("Received error creating pod: %v", err)
|
|
| 145 |
+ deployment.State = deployapi.DeploymentFailed |
|
| 146 |
+ } else {
|
|
| 147 |
+ glog.Infof("Successfully created pod %+v", pod)
|
|
| 148 |
+ deployment.State = deployapi.DeploymentPending |
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ return dh.saveDeployment(deployment) |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+// Handler for a deployment in the 'pending' state |
|
| 155 |
+func (dh *DefaultDeploymentHandler) HandlePending(deployment *deployapi.Deployment) error {
|
|
| 156 |
+ podID := deploymentPodID(deployment) |
|
| 157 |
+ glog.Infof("Retrieving deployment pod id %s", podID)
|
|
| 158 |
+ pod, err := dh.kubeClient.GetPod(podID) |
|
| 159 |
+ if err != nil {
|
|
| 160 |
+ glog.Errorf("Error retrieving pod for deployment ID %v: %#v", deployment.ID, err)
|
|
| 161 |
+ deployment.State = deployapi.DeploymentFailed |
|
| 162 |
+ } else {
|
|
| 163 |
+ glog.Infof("Deployment pod is %+v", pod)
|
|
| 164 |
+ |
|
| 165 |
+ switch pod.CurrentState.Status {
|
|
| 166 |
+ case api.PodRunning: |
|
| 167 |
+ deployment.State = deployapi.DeploymentRunning |
|
| 168 |
+ case api.PodTerminated: |
|
| 169 |
+ dh.checkForTerminatedDeploymentPod(deployment, pod) |
|
| 170 |
+ } |
|
| 171 |
+ } |
|
| 172 |
+ |
|
| 173 |
+ return dh.saveDeployment(deployment) |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+// Handler for a deployment in the 'running' state |
|
| 177 |
+func (dh *DefaultDeploymentHandler) HandleRunning(deployment *deployapi.Deployment) error {
|
|
| 178 |
+ podID := deploymentPodID(deployment) |
|
| 179 |
+ glog.Infof("Retrieving deployment pod id %s", podID)
|
|
| 180 |
+ pod, err := dh.kubeClient.GetPod(podID) |
|
| 181 |
+ if err != nil {
|
|
| 182 |
+ glog.Errorf("Error retrieving pod for deployment ID %v: %#v", deployment.ID, err)
|
|
| 183 |
+ deployment.State = deployapi.DeploymentFailed |
|
| 184 |
+ } else {
|
|
| 185 |
+ glog.Infof("Deployment pod is %+v", pod)
|
|
| 186 |
+ dh.checkForTerminatedDeploymentPod(deployment, pod) |
|
| 187 |
+ } |
|
| 188 |
+ |
|
| 189 |
+ return dh.saveDeployment(deployment) |
|
| 190 |
+} |
|
| 191 |
+ |
|
| 192 |
+func (dh *DefaultDeploymentHandler) checkForTerminatedDeploymentPod(deployment *deployapi.Deployment, pod api.Pod) {
|
|
| 193 |
+ if pod.CurrentState.Status != api.PodTerminated {
|
|
| 194 |
+ glog.Infof("The deployment has not yet finished. Pod status is %s. Continuing", pod.CurrentState.Status)
|
|
| 195 |
+ return |
|
| 196 |
+ } |
|
| 197 |
+ |
|
| 198 |
+ deployment.State = deployapi.DeploymentComplete |
|
| 199 |
+ for _, info := range pod.CurrentState.Info {
|
|
| 200 |
+ if info.State.ExitCode != 0 {
|
|
| 201 |
+ deployment.State = deployapi.DeploymentFailed |
|
| 202 |
+ } |
|
| 203 |
+ } |
|
| 204 |
+ |
|
| 205 |
+ if deployment.State == deployapi.DeploymentComplete {
|
|
| 206 |
+ podID := deploymentPodID(deployment) |
|
| 207 |
+ glog.Infof("Removing deployment pod for ID %v", podID)
|
|
| 208 |
+ dh.kubeClient.DeletePod(podID) |
|
| 209 |
+ } |
|
| 210 |
+ |
|
| 211 |
+ glog.Infof("The deployment pod has finished. Setting deployment state to %s", deployment.State)
|
|
| 212 |
+ return |
|
| 213 |
+} |
| 0 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,15 @@ |
| 0 |
+package deploy |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 4 |
+ api "github.com/openshift/origin/pkg/deploy/api" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// Registry is an interface for things that know how to store Deployments |
|
| 8 |
+type Registry interface {
|
|
| 9 |
+ ListDeployments(selector labels.Selector) (*api.DeploymentList, error) |
|
| 10 |
+ GetDeployment(id string) (*api.Deployment, error) |
|
| 11 |
+ CreateDeployment(deployment *api.Deployment) error |
|
| 12 |
+ UpdateDeployment(deployment *api.Deployment) error |
|
| 13 |
+ DeleteDeployment(id string) error |
|
| 14 |
+} |
| 0 | 15 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,101 @@ |
| 0 |
+package deploy |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ |
|
| 5 |
+ "code.google.com/p/go-uuid/uuid" |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 7 |
+ kubeerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" |
|
| 8 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" |
|
| 9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 10 |
+ "github.com/golang/glog" |
|
| 11 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 12 |
+ "github.com/openshift/origin/pkg/deploy/api/validation" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+// REST is an implementation of RESTStorage for the api server. |
|
| 16 |
+type REST struct {
|
|
| 17 |
+ registry Registry |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func NewREST(registry Registry) apiserver.RESTStorage {
|
|
| 21 |
+ return &REST{
|
|
| 22 |
+ registry: registry, |
|
| 23 |
+ } |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+// List obtains a list of Deployments that match selector. |
|
| 27 |
+func (s *REST) List(selector labels.Selector) (interface{}, error) {
|
|
| 28 |
+ deployments, err := s.registry.ListDeployments(selector) |
|
| 29 |
+ if err != nil {
|
|
| 30 |
+ return nil, err |
|
| 31 |
+ } |
|
| 32 |
+ |
|
| 33 |
+ return deployments, nil |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+// New creates a new Deployment for use with Create and Update |
|
| 37 |
+func (s *REST) New() interface{} {
|
|
| 38 |
+ return &deployapi.Deployment{}
|
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+// Get obtains the Deployment specified by its id. |
|
| 42 |
+func (s *REST) Get(id string) (interface{}, error) {
|
|
| 43 |
+ deployment, err := s.registry.GetDeployment(id) |
|
| 44 |
+ if err != nil {
|
|
| 45 |
+ return nil, err |
|
| 46 |
+ } |
|
| 47 |
+ return deployment, err |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// Delete asynchronously deletes the Deployment specified by its id. |
|
| 51 |
+func (s *REST) Delete(id string) (<-chan interface{}, error) {
|
|
| 52 |
+ return apiserver.MakeAsync(func() (interface{}, error) {
|
|
| 53 |
+ return api.Status{Status: api.StatusSuccess}, s.registry.DeleteDeployment(id)
|
|
| 54 |
+ }), nil |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+// Create registers a given new Deployment instance to s.registry. |
|
| 58 |
+func (s *REST) Create(obj interface{}) (<-chan interface{}, error) {
|
|
| 59 |
+ deployment, ok := obj.(*deployapi.Deployment) |
|
| 60 |
+ if !ok {
|
|
| 61 |
+ return nil, fmt.Errorf("not a deployment: %#v", obj)
|
|
| 62 |
+ } |
|
| 63 |
+ |
|
| 64 |
+ glog.Infof("Creating deployment with ID: %v", deployment.ID)
|
|
| 65 |
+ |
|
| 66 |
+ if len(deployment.ID) == 0 {
|
|
| 67 |
+ deployment.ID = uuid.NewUUID().String() |
|
| 68 |
+ } |
|
| 69 |
+ deployment.State = deployapi.DeploymentNew |
|
| 70 |
+ |
|
| 71 |
+ if errs := validation.ValidateDeployment(deployment); len(errs) > 0 {
|
|
| 72 |
+ return nil, kubeerrors.NewInvalid("deployment", deployment.ID, errs)
|
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ return apiserver.MakeAsync(func() (interface{}, error) {
|
|
| 76 |
+ err := s.registry.CreateDeployment(deployment) |
|
| 77 |
+ if err != nil {
|
|
| 78 |
+ return nil, err |
|
| 79 |
+ } |
|
| 80 |
+ return *deployment, nil |
|
| 81 |
+ }), nil |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// Update replaces a given Deployment instance with an existing instance in s.registry. |
|
| 85 |
+func (s *REST) Update(obj interface{}) (<-chan interface{}, error) {
|
|
| 86 |
+ deployment, ok := obj.(*deployapi.Deployment) |
|
| 87 |
+ if !ok {
|
|
| 88 |
+ return nil, fmt.Errorf("not a deployment: %#v", obj)
|
|
| 89 |
+ } |
|
| 90 |
+ if len(deployment.ID) == 0 {
|
|
| 91 |
+ return nil, fmt.Errorf("id is unspecified: %#v", deployment)
|
|
| 92 |
+ } |
|
| 93 |
+ return apiserver.MakeAsync(func() (interface{}, error) {
|
|
| 94 |
+ err := s.registry.UpdateDeployment(deployment) |
|
| 95 |
+ if err != nil {
|
|
| 96 |
+ return nil, err |
|
| 97 |
+ } |
|
| 98 |
+ return deployment, nil |
|
| 99 |
+ }), nil |
|
| 100 |
+} |
| 0 | 101 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,294 @@ |
| 0 |
+package deploy |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "strings" |
|
| 5 |
+ "testing" |
|
| 6 |
+ "time" |
|
| 7 |
+ |
|
| 8 |
+ kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 10 |
+ "github.com/openshift/origin/pkg/deploy/api" |
|
| 11 |
+ "github.com/openshift/origin/pkg/deploy/registry/test" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+func TestListDeploymentsError(t *testing.T) {
|
|
| 15 |
+ mockRegistry := test.NewDeploymentRegistry() |
|
| 16 |
+ mockRegistry.Err = fmt.Errorf("test error")
|
|
| 17 |
+ |
|
| 18 |
+ storage := REST{
|
|
| 19 |
+ registry: mockRegistry, |
|
| 20 |
+ } |
|
| 21 |
+ |
|
| 22 |
+ deployments, err := storage.List(nil) |
|
| 23 |
+ if err != mockRegistry.Err {
|
|
| 24 |
+ t.Errorf("Expected %#v, Got %#v", mockRegistry.Err, err)
|
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ if deployments != nil {
|
|
| 28 |
+ t.Errorf("Unexpected non-nil deployments list: %#v", deployments)
|
|
| 29 |
+ } |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func TestListDeploymentsEmptyList(t *testing.T) {
|
|
| 33 |
+ mockRegistry := test.NewDeploymentRegistry() |
|
| 34 |
+ mockRegistry.Deployments = &api.DeploymentList{
|
|
| 35 |
+ Items: []api.Deployment{},
|
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ storage := REST{
|
|
| 39 |
+ registry: mockRegistry, |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ deployments, err := storage.List(labels.Everything()) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ if len(deployments.(*api.DeploymentList).Items) != 0 {
|
|
| 48 |
+ t.Errorf("Unexpected non-zero deployments list: %#v", deployments)
|
|
| 49 |
+ } |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+func TestListDeploymentsPopulatedList(t *testing.T) {
|
|
| 53 |
+ mockRegistry := test.NewDeploymentRegistry() |
|
| 54 |
+ mockRegistry.Deployments = &api.DeploymentList{
|
|
| 55 |
+ Items: []api.Deployment{
|
|
| 56 |
+ {
|
|
| 57 |
+ JSONBase: kubeapi.JSONBase{
|
|
| 58 |
+ ID: "foo", |
|
| 59 |
+ }, |
|
| 60 |
+ }, |
|
| 61 |
+ {
|
|
| 62 |
+ JSONBase: kubeapi.JSONBase{
|
|
| 63 |
+ ID: "bar", |
|
| 64 |
+ }, |
|
| 65 |
+ }, |
|
| 66 |
+ }, |
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 69 |
+ storage := REST{
|
|
| 70 |
+ registry: mockRegistry, |
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ list, err := storage.List(labels.Everything()) |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ deployments := list.(*api.DeploymentList) |
|
| 79 |
+ |
|
| 80 |
+ if e, a := 2, len(deployments.Items); e != a {
|
|
| 81 |
+ t.Errorf("Expected %v, got %v", e, a)
|
|
| 82 |
+ } |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func TestCreateDeploymentBadObject(t *testing.T) {
|
|
| 86 |
+ storage := REST{}
|
|
| 87 |
+ |
|
| 88 |
+ channel, err := storage.Create("hello")
|
|
| 89 |
+ if channel != nil {
|
|
| 90 |
+ t.Errorf("Expected nil, got %v", channel)
|
|
| 91 |
+ } |
|
| 92 |
+ if strings.Index(err.Error(), "not a deployment") == -1 {
|
|
| 93 |
+ t.Errorf("Expected 'not a deployment' error, got '%v'", err.Error())
|
|
| 94 |
+ } |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func okStrategy() api.DeploymentStrategy {
|
|
| 98 |
+ return api.DeploymentStrategy{
|
|
| 99 |
+ Type: "customPod", |
|
| 100 |
+ CustomPod: okCustomPod(), |
|
| 101 |
+ } |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+func okCustomPod() *api.CustomPodDeploymentStrategy {
|
|
| 105 |
+ return &api.CustomPodDeploymentStrategy{
|
|
| 106 |
+ Image: "openshift/kube-deploy", |
|
| 107 |
+ } |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+func TestCreateRegistrySaveError(t *testing.T) {
|
|
| 111 |
+ mockRegistry := test.NewDeploymentRegistry() |
|
| 112 |
+ mockRegistry.Err = fmt.Errorf("test error")
|
|
| 113 |
+ storage := REST{registry: mockRegistry}
|
|
| 114 |
+ |
|
| 115 |
+ channel, err := storage.Create(&api.Deployment{
|
|
| 116 |
+ JSONBase: kubeapi.JSONBase{ID: "foo"},
|
|
| 117 |
+ Strategy: okStrategy(), |
|
| 118 |
+ }) |
|
| 119 |
+ if channel == nil {
|
|
| 120 |
+ t.Errorf("Expected nil channel, got %v", channel)
|
|
| 121 |
+ } |
|
| 122 |
+ if err != nil {
|
|
| 123 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 124 |
+ } |
|
| 125 |
+ |
|
| 126 |
+ select {
|
|
| 127 |
+ case result := <-channel: |
|
| 128 |
+ status, ok := result.(*kubeapi.Status) |
|
| 129 |
+ if !ok {
|
|
| 130 |
+ t.Errorf("Expected status type, got: %#v", result)
|
|
| 131 |
+ } |
|
| 132 |
+ if status.Status != "failure" || status.Message != "foo" {
|
|
| 133 |
+ t.Errorf("Expected failure status, got %#V", status)
|
|
| 134 |
+ } |
|
| 135 |
+ case <-time.After(50 * time.Millisecond): |
|
| 136 |
+ t.Errorf("Timed out waiting for result")
|
|
| 137 |
+ default: |
|
| 138 |
+ } |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+func TestCreateDeploymentOK(t *testing.T) {
|
|
| 142 |
+ mockRegistry := test.NewDeploymentRegistry() |
|
| 143 |
+ storage := REST{registry: mockRegistry}
|
|
| 144 |
+ |
|
| 145 |
+ channel, err := storage.Create(&api.Deployment{
|
|
| 146 |
+ JSONBase: kubeapi.JSONBase{ID: "foo"},
|
|
| 147 |
+ Strategy: okStrategy(), |
|
| 148 |
+ }) |
|
| 149 |
+ if channel == nil {
|
|
| 150 |
+ t.Errorf("Expected nil channel, got %v", channel)
|
|
| 151 |
+ } |
|
| 152 |
+ if err != nil {
|
|
| 153 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 154 |
+ } |
|
| 155 |
+ |
|
| 156 |
+ select {
|
|
| 157 |
+ case result := <-channel: |
|
| 158 |
+ deployment, ok := result.(*api.Deployment) |
|
| 159 |
+ if !ok {
|
|
| 160 |
+ t.Errorf("Expected deployment type, got: %#v", result)
|
|
| 161 |
+ } |
|
| 162 |
+ if deployment.ID != "foo" {
|
|
| 163 |
+ t.Errorf("Unexpected deployment: %#v", deployment)
|
|
| 164 |
+ } |
|
| 165 |
+ case <-time.After(50 * time.Millisecond): |
|
| 166 |
+ t.Errorf("Timed out waiting for result")
|
|
| 167 |
+ default: |
|
| 168 |
+ } |
|
| 169 |
+} |
|
| 170 |
+ |
|
| 171 |
+func TestGetDeploymentError(t *testing.T) {
|
|
| 172 |
+ mockRegistry := test.NewDeploymentRegistry() |
|
| 173 |
+ mockRegistry.Err = fmt.Errorf("bad")
|
|
| 174 |
+ storage := REST{registry: mockRegistry}
|
|
| 175 |
+ |
|
| 176 |
+ deployment, err := storage.Get("foo")
|
|
| 177 |
+ if deployment != nil {
|
|
| 178 |
+ t.Errorf("Unexpected non-nil deployment: %#v", deployment)
|
|
| 179 |
+ } |
|
| 180 |
+ if err != mockRegistry.Err {
|
|
| 181 |
+ t.Errorf("Expected %#v, got %#v", mockRegistry.Err, err)
|
|
| 182 |
+ } |
|
| 183 |
+} |
|
| 184 |
+ |
|
| 185 |
+func TestGetDeploymentOK(t *testing.T) {
|
|
| 186 |
+ mockRegistry := test.NewDeploymentRegistry() |
|
| 187 |
+ mockRegistry.Deployment = &api.Deployment{
|
|
| 188 |
+ JSONBase: kubeapi.JSONBase{ID: "foo"},
|
|
| 189 |
+ } |
|
| 190 |
+ storage := REST{registry: mockRegistry}
|
|
| 191 |
+ |
|
| 192 |
+ deployment, err := storage.Get("foo")
|
|
| 193 |
+ if deployment == nil {
|
|
| 194 |
+ t.Error("Unexpected nil deployment")
|
|
| 195 |
+ } |
|
| 196 |
+ if err != nil {
|
|
| 197 |
+ t.Errorf("Unexpected non-nil error", err)
|
|
| 198 |
+ } |
|
| 199 |
+ if deployment.(*api.Deployment).ID != "foo" {
|
|
| 200 |
+ t.Errorf("Unexpected deployment: %#v", deployment)
|
|
| 201 |
+ } |
|
| 202 |
+} |
|
| 203 |
+ |
|
| 204 |
+func TestUpdateDeploymentBadObject(t *testing.T) {
|
|
| 205 |
+ storage := REST{}
|
|
| 206 |
+ |
|
| 207 |
+ channel, err := storage.Update("hello")
|
|
| 208 |
+ if channel != nil {
|
|
| 209 |
+ t.Errorf("Expected nil, got %v", channel)
|
|
| 210 |
+ } |
|
| 211 |
+ if strings.Index(err.Error(), "not a deployment:") == -1 {
|
|
| 212 |
+ t.Errorf("Expected 'not a deployment' error, got %v", err)
|
|
| 213 |
+ } |
|
| 214 |
+} |
|
| 215 |
+ |
|
| 216 |
+func TestUpdateDeploymentMissingID(t *testing.T) {
|
|
| 217 |
+ storage := REST{}
|
|
| 218 |
+ |
|
| 219 |
+ channel, err := storage.Update(&api.Deployment{})
|
|
| 220 |
+ if channel != nil {
|
|
| 221 |
+ t.Errorf("Expected nil, got %v", channel)
|
|
| 222 |
+ } |
|
| 223 |
+ if strings.Index(err.Error(), "id is unspecified:") == -1 {
|
|
| 224 |
+ t.Errorf("Expected 'id is unspecified' error, got %v", err)
|
|
| 225 |
+ } |
|
| 226 |
+} |
|
| 227 |
+ |
|
| 228 |
+func TestUpdateRegistryErrorSaving(t *testing.T) {
|
|
| 229 |
+ mockRepositoryRegistry := test.NewDeploymentRegistry() |
|
| 230 |
+ mockRepositoryRegistry.Err = fmt.Errorf("foo")
|
|
| 231 |
+ storage := REST{registry: mockRepositoryRegistry}
|
|
| 232 |
+ |
|
| 233 |
+ channel, err := storage.Update(&api.Deployment{
|
|
| 234 |
+ JSONBase: kubeapi.JSONBase{ID: "bar"},
|
|
| 235 |
+ }) |
|
| 236 |
+ if err != nil {
|
|
| 237 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 238 |
+ } |
|
| 239 |
+ result := <-channel |
|
| 240 |
+ status, ok := result.(*kubeapi.Status) |
|
| 241 |
+ if !ok {
|
|
| 242 |
+ t.Errorf("Expected status, got %#v", result)
|
|
| 243 |
+ } |
|
| 244 |
+ if status.Status != "failure" || status.Message != "foo" {
|
|
| 245 |
+ t.Errorf("Expected status=failure, message=foo, got %#v", status)
|
|
| 246 |
+ } |
|
| 247 |
+} |
|
| 248 |
+ |
|
| 249 |
+func TestUpdateDeploymentOK(t *testing.T) {
|
|
| 250 |
+ mockRepositoryRegistry := test.NewDeploymentRegistry() |
|
| 251 |
+ storage := REST{registry: mockRepositoryRegistry}
|
|
| 252 |
+ |
|
| 253 |
+ channel, err := storage.Update(&api.Deployment{
|
|
| 254 |
+ JSONBase: kubeapi.JSONBase{ID: "bar"},
|
|
| 255 |
+ }) |
|
| 256 |
+ if err != nil {
|
|
| 257 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 258 |
+ } |
|
| 259 |
+ result := <-channel |
|
| 260 |
+ repo, ok := result.(*api.Deployment) |
|
| 261 |
+ if !ok {
|
|
| 262 |
+ t.Errorf("Expected Deployment, got %#v", result)
|
|
| 263 |
+ } |
|
| 264 |
+ if repo.ID != "bar" {
|
|
| 265 |
+ t.Errorf("Unexpected repo returned: %#v", repo)
|
|
| 266 |
+ } |
|
| 267 |
+} |
|
| 268 |
+ |
|
| 269 |
+func TestDeleteDeployment(t *testing.T) {
|
|
| 270 |
+ mockRegistry := test.NewDeploymentRegistry() |
|
| 271 |
+ storage := REST{registry: mockRegistry}
|
|
| 272 |
+ channel, err := storage.Delete("foo")
|
|
| 273 |
+ if channel == nil {
|
|
| 274 |
+ t.Error("Unexpected nil channel")
|
|
| 275 |
+ } |
|
| 276 |
+ if err != nil {
|
|
| 277 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 278 |
+ } |
|
| 279 |
+ |
|
| 280 |
+ select {
|
|
| 281 |
+ case result := <-channel: |
|
| 282 |
+ status, ok := result.(*kubeapi.Status) |
|
| 283 |
+ if !ok {
|
|
| 284 |
+ t.Errorf("Expected status type, got: %#v", result)
|
|
| 285 |
+ } |
|
| 286 |
+ if status.Status != "success" {
|
|
| 287 |
+ t.Errorf("Expected status=success, got: %#v", status)
|
|
| 288 |
+ } |
|
| 289 |
+ case <-time.After(50 * time.Millisecond): |
|
| 290 |
+ t.Errorf("Timed out waiting for result")
|
|
| 291 |
+ default: |
|
| 292 |
+ } |
|
| 293 |
+} |
| 0 | 294 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,15 @@ |
| 0 |
+package deployconfig |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 4 |
+ api "github.com/openshift/origin/pkg/deploy/api" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// Registry is an interface for things that know how to store DeploymentConfigs |
|
| 8 |
+type Registry interface {
|
|
| 9 |
+ ListDeploymentConfigs(selector labels.Selector) (*api.DeploymentConfigList, error) |
|
| 10 |
+ GetDeploymentConfig(id string) (*api.DeploymentConfig, error) |
|
| 11 |
+ CreateDeploymentConfig(deploymentConfig *api.DeploymentConfig) error |
|
| 12 |
+ UpdateDeploymentConfig(deploymentConfig *api.DeploymentConfig) error |
|
| 13 |
+ DeleteDeploymentConfig(id string) error |
|
| 14 |
+} |
| 0 | 15 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,92 @@ |
| 0 |
+package deployconfig |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ |
|
| 5 |
+ "code.google.com/p/go-uuid/uuid" |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 7 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" |
|
| 8 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 9 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+// REST is an implementation of RESTStorage for the api server. |
|
| 13 |
+type REST struct {
|
|
| 14 |
+ registry Registry |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func NewREST(registry Registry) apiserver.RESTStorage {
|
|
| 18 |
+ return &REST{
|
|
| 19 |
+ registry: registry, |
|
| 20 |
+ } |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// List obtains a list of DeploymentConfigs that match selector. |
|
| 24 |
+func (s *REST) List(selector labels.Selector) (interface{}, error) {
|
|
| 25 |
+ deploymentConfigs, err := s.registry.ListDeploymentConfigs(selector) |
|
| 26 |
+ if err != nil {
|
|
| 27 |
+ return nil, err |
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 30 |
+ return deploymentConfigs, nil |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+// Get obtains the DeploymentConfig specified by its id. |
|
| 34 |
+func (s *REST) Get(id string) (interface{}, error) {
|
|
| 35 |
+ deploymentConfig, err := s.registry.GetDeploymentConfig(id) |
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ return nil, err |
|
| 38 |
+ } |
|
| 39 |
+ return deploymentConfig, err |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+// Delete asynchronously deletes the DeploymentConfig specified by its id. |
|
| 43 |
+func (s *REST) Delete(id string) (<-chan interface{}, error) {
|
|
| 44 |
+ return apiserver.MakeAsync(func() (interface{}, error) {
|
|
| 45 |
+ return api.Status{Status: api.StatusSuccess}, s.registry.DeleteDeploymentConfig(id)
|
|
| 46 |
+ }), nil |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+// New creates a new DeploymentConfig for use with Create and Update |
|
| 50 |
+func (s *REST) New() interface{} {
|
|
| 51 |
+ return &deployapi.DeploymentConfig{}
|
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+// Create registers a given new DeploymentConfig instance to s.registry. |
|
| 55 |
+func (s *REST) Create(obj interface{}) (<-chan interface{}, error) {
|
|
| 56 |
+ deploymentConfig, ok := obj.(*deployapi.DeploymentConfig) |
|
| 57 |
+ if !ok {
|
|
| 58 |
+ return nil, fmt.Errorf("not a deploymentConfig: %#v", obj)
|
|
| 59 |
+ } |
|
| 60 |
+ if len(deploymentConfig.ID) == 0 {
|
|
| 61 |
+ deploymentConfig.ID = uuid.NewUUID().String() |
|
| 62 |
+ } |
|
| 63 |
+ |
|
| 64 |
+ //TODO: Add validation |
|
| 65 |
+ |
|
| 66 |
+ return apiserver.MakeAsync(func() (interface{}, error) {
|
|
| 67 |
+ err := s.registry.CreateDeploymentConfig(deploymentConfig) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ return nil, err |
|
| 70 |
+ } |
|
| 71 |
+ return deploymentConfig, nil |
|
| 72 |
+ }), nil |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// Update replaces a given DeploymentConfig instance with an existing instance in s.registry. |
|
| 76 |
+func (s *REST) Update(obj interface{}) (<-chan interface{}, error) {
|
|
| 77 |
+ deploymentConfig, ok := obj.(*deployapi.DeploymentConfig) |
|
| 78 |
+ if !ok {
|
|
| 79 |
+ return nil, fmt.Errorf("not a deploymentConfig: %#v", obj)
|
|
| 80 |
+ } |
|
| 81 |
+ if len(deploymentConfig.ID) == 0 {
|
|
| 82 |
+ return nil, fmt.Errorf("id is unspecified: %#v", deploymentConfig)
|
|
| 83 |
+ } |
|
| 84 |
+ return apiserver.MakeAsync(func() (interface{}, error) {
|
|
| 85 |
+ err := s.registry.UpdateDeploymentConfig(deploymentConfig) |
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return nil, err |
|
| 88 |
+ } |
|
| 89 |
+ return deploymentConfig, nil |
|
| 90 |
+ }), nil |
|
| 91 |
+} |
| 0 | 92 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,279 @@ |
| 0 |
+package deployconfig |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "strings" |
|
| 5 |
+ "testing" |
|
| 6 |
+ "time" |
|
| 7 |
+ |
|
| 8 |
+ kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 10 |
+ "github.com/openshift/origin/pkg/deploy/api" |
|
| 11 |
+ "github.com/openshift/origin/pkg/deploy/registry/test" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+func TestListDeploymentConfigsError(t *testing.T) {
|
|
| 15 |
+ mockRegistry := test.NewDeploymentConfigRegistry() |
|
| 16 |
+ mockRegistry.Err = fmt.Errorf("test error")
|
|
| 17 |
+ |
|
| 18 |
+ storage := REST{
|
|
| 19 |
+ registry: mockRegistry, |
|
| 20 |
+ } |
|
| 21 |
+ |
|
| 22 |
+ deploymentConfigs, err := storage.List(nil) |
|
| 23 |
+ if err != mockRegistry.Err {
|
|
| 24 |
+ t.Errorf("Expected %#v, Got %#v", mockRegistry.Err, err)
|
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ if deploymentConfigs != nil {
|
|
| 28 |
+ t.Errorf("Unexpected non-nil deploymentConfigs list: %#v", deploymentConfigs)
|
|
| 29 |
+ } |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func TestListDeploymentConfigsEmptyList(t *testing.T) {
|
|
| 33 |
+ mockRegistry := test.NewDeploymentConfigRegistry() |
|
| 34 |
+ mockRegistry.DeploymentConfigs = &api.DeploymentConfigList{
|
|
| 35 |
+ Items: []api.DeploymentConfig{},
|
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ storage := REST{
|
|
| 39 |
+ registry: mockRegistry, |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ deploymentConfigs, err := storage.List(labels.Everything()) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ if len(deploymentConfigs.(*api.DeploymentConfigList).Items) != 0 {
|
|
| 48 |
+ t.Errorf("Unexpected non-zero deploymentConfigs list: %#v", deploymentConfigs)
|
|
| 49 |
+ } |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+func TestListDeploymentConfigsPopulatedList(t *testing.T) {
|
|
| 53 |
+ mockRegistry := test.NewDeploymentConfigRegistry() |
|
| 54 |
+ mockRegistry.DeploymentConfigs = &api.DeploymentConfigList{
|
|
| 55 |
+ Items: []api.DeploymentConfig{
|
|
| 56 |
+ {
|
|
| 57 |
+ JSONBase: kubeapi.JSONBase{
|
|
| 58 |
+ ID: "foo", |
|
| 59 |
+ }, |
|
| 60 |
+ }, |
|
| 61 |
+ {
|
|
| 62 |
+ JSONBase: kubeapi.JSONBase{
|
|
| 63 |
+ ID: "bar", |
|
| 64 |
+ }, |
|
| 65 |
+ }, |
|
| 66 |
+ }, |
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 69 |
+ storage := REST{
|
|
| 70 |
+ registry: mockRegistry, |
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ list, err := storage.List(labels.Everything()) |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ deploymentConfigs := list.(*api.DeploymentConfigList) |
|
| 79 |
+ |
|
| 80 |
+ if e, a := 2, len(deploymentConfigs.Items); e != a {
|
|
| 81 |
+ t.Errorf("Expected %v, got %v", e, a)
|
|
| 82 |
+ } |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func TestCreateDeploymentConfigBadObject(t *testing.T) {
|
|
| 86 |
+ storage := REST{}
|
|
| 87 |
+ |
|
| 88 |
+ channel, err := storage.Create("hello")
|
|
| 89 |
+ if channel != nil {
|
|
| 90 |
+ t.Errorf("Expected nil, got %v", channel)
|
|
| 91 |
+ } |
|
| 92 |
+ if strings.Index(err.Error(), "not a deploymentConfig") == -1 {
|
|
| 93 |
+ t.Errorf("Expected 'not a deploymentConfig' error, got '%v'", err.Error())
|
|
| 94 |
+ } |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func TestCreateRegistrySaveError(t *testing.T) {
|
|
| 98 |
+ mockRegistry := test.NewDeploymentConfigRegistry() |
|
| 99 |
+ mockRegistry.Err = fmt.Errorf("test error")
|
|
| 100 |
+ storage := REST{registry: mockRegistry}
|
|
| 101 |
+ |
|
| 102 |
+ channel, err := storage.Create(&api.DeploymentConfig{
|
|
| 103 |
+ JSONBase: kubeapi.JSONBase{ID: "foo"},
|
|
| 104 |
+ }) |
|
| 105 |
+ if channel == nil {
|
|
| 106 |
+ t.Errorf("Expected nil channel, got %v", channel)
|
|
| 107 |
+ } |
|
| 108 |
+ if err != nil {
|
|
| 109 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ select {
|
|
| 113 |
+ case result := <-channel: |
|
| 114 |
+ status, ok := result.(*kubeapi.Status) |
|
| 115 |
+ if !ok {
|
|
| 116 |
+ t.Errorf("Expected status type, got: %#v", result)
|
|
| 117 |
+ } |
|
| 118 |
+ if status.Status != "failure" || status.Message != "foo" {
|
|
| 119 |
+ t.Errorf("Expected failure status, got %#V", status)
|
|
| 120 |
+ } |
|
| 121 |
+ case <-time.After(50 * time.Millisecond): |
|
| 122 |
+ t.Errorf("Timed out waiting for result")
|
|
| 123 |
+ default: |
|
| 124 |
+ } |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+func TestCreateDeploymentConfigOK(t *testing.T) {
|
|
| 128 |
+ mockRegistry := test.NewDeploymentConfigRegistry() |
|
| 129 |
+ storage := REST{registry: mockRegistry}
|
|
| 130 |
+ |
|
| 131 |
+ channel, err := storage.Create(&api.DeploymentConfig{
|
|
| 132 |
+ JSONBase: kubeapi.JSONBase{ID: "foo"},
|
|
| 133 |
+ }) |
|
| 134 |
+ if channel == nil {
|
|
| 135 |
+ t.Errorf("Expected nil channel, got %v", channel)
|
|
| 136 |
+ } |
|
| 137 |
+ if err != nil {
|
|
| 138 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+ select {
|
|
| 142 |
+ case result := <-channel: |
|
| 143 |
+ deploymentConfig, ok := result.(*api.DeploymentConfig) |
|
| 144 |
+ if !ok {
|
|
| 145 |
+ t.Errorf("Expected deploymentConfig type, got: %#v", result)
|
|
| 146 |
+ } |
|
| 147 |
+ if deploymentConfig.ID != "foo" {
|
|
| 148 |
+ t.Errorf("Unexpected deploymentConfig: %#v", deploymentConfig)
|
|
| 149 |
+ } |
|
| 150 |
+ case <-time.After(50 * time.Millisecond): |
|
| 151 |
+ t.Errorf("Timed out waiting for result")
|
|
| 152 |
+ default: |
|
| 153 |
+ } |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+func TestGetDeploymentConfigError(t *testing.T) {
|
|
| 157 |
+ mockRegistry := test.NewDeploymentConfigRegistry() |
|
| 158 |
+ mockRegistry.Err = fmt.Errorf("bad")
|
|
| 159 |
+ storage := REST{registry: mockRegistry}
|
|
| 160 |
+ |
|
| 161 |
+ deploymentConfig, err := storage.Get("foo")
|
|
| 162 |
+ if deploymentConfig != nil {
|
|
| 163 |
+ t.Errorf("Unexpected non-nil deploymentConfig: %#v", deploymentConfig)
|
|
| 164 |
+ } |
|
| 165 |
+ if err != mockRegistry.Err {
|
|
| 166 |
+ t.Errorf("Expected %#v, got %#v", mockRegistry.Err, err)
|
|
| 167 |
+ } |
|
| 168 |
+} |
|
| 169 |
+ |
|
| 170 |
+func TestGetDeploymentConfigOK(t *testing.T) {
|
|
| 171 |
+ mockRegistry := test.NewDeploymentConfigRegistry() |
|
| 172 |
+ mockRegistry.DeploymentConfig = &api.DeploymentConfig{
|
|
| 173 |
+ JSONBase: kubeapi.JSONBase{ID: "foo"},
|
|
| 174 |
+ } |
|
| 175 |
+ storage := REST{registry: mockRegistry}
|
|
| 176 |
+ |
|
| 177 |
+ deploymentConfig, err := storage.Get("foo")
|
|
| 178 |
+ if deploymentConfig == nil {
|
|
| 179 |
+ t.Error("Unexpected nil deploymentConfig")
|
|
| 180 |
+ } |
|
| 181 |
+ if err != nil {
|
|
| 182 |
+ t.Errorf("Unexpected non-nil error", err)
|
|
| 183 |
+ } |
|
| 184 |
+ if deploymentConfig.(*api.DeploymentConfig).ID != "foo" {
|
|
| 185 |
+ t.Errorf("Unexpected deploymentConfig: %#v", deploymentConfig)
|
|
| 186 |
+ } |
|
| 187 |
+} |
|
| 188 |
+ |
|
| 189 |
+func TestUpdateDeploymentConfigBadObject(t *testing.T) {
|
|
| 190 |
+ storage := REST{}
|
|
| 191 |
+ |
|
| 192 |
+ channel, err := storage.Update("hello")
|
|
| 193 |
+ if channel != nil {
|
|
| 194 |
+ t.Errorf("Expected nil, got %v", channel)
|
|
| 195 |
+ } |
|
| 196 |
+ if strings.Index(err.Error(), "not a deploymentConfig:") == -1 {
|
|
| 197 |
+ t.Errorf("Expected 'not a deploymentConfig' error, got %v", err)
|
|
| 198 |
+ } |
|
| 199 |
+} |
|
| 200 |
+ |
|
| 201 |
+func TestUpdateDeploymentConfigMissingID(t *testing.T) {
|
|
| 202 |
+ storage := REST{}
|
|
| 203 |
+ |
|
| 204 |
+ channel, err := storage.Update(&api.DeploymentConfig{})
|
|
| 205 |
+ if channel != nil {
|
|
| 206 |
+ t.Errorf("Expected nil, got %v", channel)
|
|
| 207 |
+ } |
|
| 208 |
+ if strings.Index(err.Error(), "id is unspecified:") == -1 {
|
|
| 209 |
+ t.Errorf("Expected 'id is unspecified' error, got %v", err)
|
|
| 210 |
+ } |
|
| 211 |
+} |
|
| 212 |
+ |
|
| 213 |
+func TestUpdateRegistryErrorSaving(t *testing.T) {
|
|
| 214 |
+ mockRepositoryRegistry := test.NewDeploymentConfigRegistry() |
|
| 215 |
+ mockRepositoryRegistry.Err = fmt.Errorf("foo")
|
|
| 216 |
+ storage := REST{registry: mockRepositoryRegistry}
|
|
| 217 |
+ |
|
| 218 |
+ channel, err := storage.Update(&api.DeploymentConfig{
|
|
| 219 |
+ JSONBase: kubeapi.JSONBase{ID: "bar"},
|
|
| 220 |
+ }) |
|
| 221 |
+ if err != nil {
|
|
| 222 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 223 |
+ } |
|
| 224 |
+ result := <-channel |
|
| 225 |
+ status, ok := result.(*kubeapi.Status) |
|
| 226 |
+ if !ok {
|
|
| 227 |
+ t.Errorf("Expected status, got %#v", result)
|
|
| 228 |
+ } |
|
| 229 |
+ if status.Status != "failure" || status.Message != "foo" {
|
|
| 230 |
+ t.Errorf("Expected status=failure, message=foo, got %#v", status)
|
|
| 231 |
+ } |
|
| 232 |
+} |
|
| 233 |
+ |
|
| 234 |
+func TestUpdateDeploymentConfigOK(t *testing.T) {
|
|
| 235 |
+ mockRepositoryRegistry := test.NewDeploymentConfigRegistry() |
|
| 236 |
+ storage := REST{registry: mockRepositoryRegistry}
|
|
| 237 |
+ |
|
| 238 |
+ channel, err := storage.Update(&api.DeploymentConfig{
|
|
| 239 |
+ JSONBase: kubeapi.JSONBase{ID: "bar"},
|
|
| 240 |
+ }) |
|
| 241 |
+ if err != nil {
|
|
| 242 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 243 |
+ } |
|
| 244 |
+ result := <-channel |
|
| 245 |
+ repo, ok := result.(*api.DeploymentConfig) |
|
| 246 |
+ if !ok {
|
|
| 247 |
+ t.Errorf("Expected DeploymentConfig, got %#v", result)
|
|
| 248 |
+ } |
|
| 249 |
+ if repo.ID != "bar" {
|
|
| 250 |
+ t.Errorf("Unexpected repo returned: %#v", repo)
|
|
| 251 |
+ } |
|
| 252 |
+} |
|
| 253 |
+ |
|
| 254 |
+func TestDeleteDeploymentConfig(t *testing.T) {
|
|
| 255 |
+ mockRegistry := test.NewDeploymentConfigRegistry() |
|
| 256 |
+ storage := REST{registry: mockRegistry}
|
|
| 257 |
+ channel, err := storage.Delete("foo")
|
|
| 258 |
+ if channel == nil {
|
|
| 259 |
+ t.Error("Unexpected nil channel")
|
|
| 260 |
+ } |
|
| 261 |
+ if err != nil {
|
|
| 262 |
+ t.Errorf("Unexpected non-nil error: %#v", err)
|
|
| 263 |
+ } |
|
| 264 |
+ |
|
| 265 |
+ select {
|
|
| 266 |
+ case result := <-channel: |
|
| 267 |
+ status, ok := result.(*kubeapi.Status) |
|
| 268 |
+ if !ok {
|
|
| 269 |
+ t.Errorf("Expected status type, got: %#v", result)
|
|
| 270 |
+ } |
|
| 271 |
+ if status.Status != "success" {
|
|
| 272 |
+ t.Errorf("Expected status=success, got: %#v", status)
|
|
| 273 |
+ } |
|
| 274 |
+ case <-time.After(50 * time.Millisecond): |
|
| 275 |
+ t.Errorf("Timed out waiting for result")
|
|
| 276 |
+ default: |
|
| 277 |
+ } |
|
| 278 |
+} |
| 0 | 279 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,142 @@ |
| 0 |
+package etcd |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" |
|
| 4 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 5 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" |
|
| 7 |
+ "github.com/openshift/origin/pkg/deploy/api" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// Etcd implements deployment.Registry and deploymentconfig.Registry interfaces. |
|
| 11 |
+type Etcd struct {
|
|
| 12 |
+ tools.EtcdHelper |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+// NewEtcd creates an etcd registry using the given etcd client. |
|
| 16 |
+func NewEtcd(client tools.EtcdClient) *Etcd {
|
|
| 17 |
+ registry := &Etcd{
|
|
| 18 |
+ tools.EtcdHelper{client, runtime.Codec, runtime.ResourceVersioner},
|
|
| 19 |
+ } |
|
| 20 |
+ return registry |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// ListDeployments obtains a list of Deployments. |
|
| 24 |
+func (r *Etcd) ListDeployments(selector labels.Selector) (*api.DeploymentList, error) {
|
|
| 25 |
+ deployments := api.DeploymentList{}
|
|
| 26 |
+ err := r.ExtractList("/deployments", &deployments.Items, &deployments.ResourceVersion)
|
|
| 27 |
+ if err != nil {
|
|
| 28 |
+ return nil, err |
|
| 29 |
+ } |
|
| 30 |
+ filtered := []api.Deployment{}
|
|
| 31 |
+ for _, item := range deployments.Items {
|
|
| 32 |
+ if selector.Matches(labels.Set(item.Labels)) {
|
|
| 33 |
+ filtered = append(filtered, item) |
|
| 34 |
+ } |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ deployments.Items = filtered |
|
| 38 |
+ return &deployments, err |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+func makeDeploymentKey(id string) string {
|
|
| 42 |
+ return "/deployments/" + id |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+// GetDeployment gets a specific Deployment specified by its ID. |
|
| 46 |
+func (r *Etcd) GetDeployment(id string) (*api.Deployment, error) {
|
|
| 47 |
+ var deployment api.Deployment |
|
| 48 |
+ key := makeDeploymentKey(id) |
|
| 49 |
+ err := r.ExtractObj(key, &deployment, false) |
|
| 50 |
+ if tools.IsEtcdNotFound(err) {
|
|
| 51 |
+ return nil, errors.NewNotFound("deployment", id)
|
|
| 52 |
+ } |
|
| 53 |
+ if err != nil {
|
|
| 54 |
+ return nil, err |
|
| 55 |
+ } |
|
| 56 |
+ return &deployment, nil |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+// CreateDeployment creates a new Deployment. |
|
| 60 |
+func (r *Etcd) CreateDeployment(deployment *api.Deployment) error {
|
|
| 61 |
+ err := r.CreateObj(makeDeploymentKey(deployment.ID), deployment) |
|
| 62 |
+ if tools.IsEtcdNodeExist(err) {
|
|
| 63 |
+ return errors.NewAlreadyExists("deployment", deployment.ID)
|
|
| 64 |
+ } |
|
| 65 |
+ return err |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// UpdateDeployment replaces an existing Deployment. |
|
| 69 |
+func (r *Etcd) UpdateDeployment(deployment *api.Deployment) error {
|
|
| 70 |
+ return r.SetObj(makeDeploymentKey(deployment.ID), deployment) |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+// DeleteDeployment deletes a Deployment specified by its ID. |
|
| 74 |
+func (r *Etcd) DeleteDeployment(id string) error {
|
|
| 75 |
+ key := makeDeploymentKey(id) |
|
| 76 |
+ err := r.Delete(key, false) |
|
| 77 |
+ if tools.IsEtcdNotFound(err) {
|
|
| 78 |
+ return errors.NewNotFound("deployment", id)
|
|
| 79 |
+ } |
|
| 80 |
+ return err |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+// ListDeploymentConfigs obtains a list of DeploymentConfigs. |
|
| 84 |
+func (r *Etcd) ListDeploymentConfigs(selector labels.Selector) (*api.DeploymentConfigList, error) {
|
|
| 85 |
+ deploymentConfigs := api.DeploymentConfigList{}
|
|
| 86 |
+ err := r.ExtractList("/deploymentConfigs", &deploymentConfigs.Items, &deploymentConfigs.ResourceVersion)
|
|
| 87 |
+ if err != nil {
|
|
| 88 |
+ return nil, err |
|
| 89 |
+ } |
|
| 90 |
+ filtered := []api.DeploymentConfig{}
|
|
| 91 |
+ for _, item := range deploymentConfigs.Items {
|
|
| 92 |
+ if selector.Matches(labels.Set(item.Labels)) {
|
|
| 93 |
+ filtered = append(filtered, item) |
|
| 94 |
+ } |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ deploymentConfigs.Items = filtered |
|
| 98 |
+ return &deploymentConfigs, err |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+func makeDeploymentConfigKey(id string) string {
|
|
| 102 |
+ return "/deploymentConfigs/" + id |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+// GetDeploymentConfig gets a specific DeploymentConfig specified by its ID. |
|
| 106 |
+func (r *Etcd) GetDeploymentConfig(id string) (*api.DeploymentConfig, error) {
|
|
| 107 |
+ var deploymentConfig api.DeploymentConfig |
|
| 108 |
+ key := makeDeploymentConfigKey(id) |
|
| 109 |
+ err := r.ExtractObj(key, &deploymentConfig, false) |
|
| 110 |
+ if tools.IsEtcdNotFound(err) {
|
|
| 111 |
+ return nil, errors.NewNotFound("deploymentConfig", id)
|
|
| 112 |
+ } |
|
| 113 |
+ if err != nil {
|
|
| 114 |
+ return nil, err |
|
| 115 |
+ } |
|
| 116 |
+ return &deploymentConfig, nil |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+// CreateDeploymentConfig creates a new DeploymentConfig. |
|
| 120 |
+func (r *Etcd) CreateDeploymentConfig(deploymentConfig *api.DeploymentConfig) error {
|
|
| 121 |
+ err := r.CreateObj(makeDeploymentConfigKey(deploymentConfig.ID), deploymentConfig) |
|
| 122 |
+ if tools.IsEtcdNodeExist(err) {
|
|
| 123 |
+ return errors.NewAlreadyExists("deploymentConfig", deploymentConfig.ID)
|
|
| 124 |
+ } |
|
| 125 |
+ return err |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+// UpdateDeploymentConfig replaces an existing DeploymentConfig. |
|
| 129 |
+func (r *Etcd) UpdateDeploymentConfig(deploymentConfig *api.DeploymentConfig) error {
|
|
| 130 |
+ return r.SetObj(makeDeploymentConfigKey(deploymentConfig.ID), deploymentConfig) |
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+// DeleteDeploymentConfig deletes a DeploymentConfig specified by its ID. |
|
| 134 |
+func (r *Etcd) DeleteDeploymentConfig(id string) error {
|
|
| 135 |
+ key := makeDeploymentConfigKey(id) |
|
| 136 |
+ err := r.Delete(key, false) |
|
| 137 |
+ if tools.IsEtcdNotFound(err) {
|
|
| 138 |
+ return errors.NewNotFound("deploymentConfig", id)
|
|
| 139 |
+ } |
|
| 140 |
+ return err |
|
| 141 |
+} |
| 0 | 142 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,506 @@ |
| 0 |
+package etcd |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "testing" |
|
| 5 |
+ |
|
| 6 |
+ kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 7 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" |
|
| 8 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
|
| 10 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" |
|
| 11 |
+ "github.com/coreos/go-etcd/etcd" |
|
| 12 |
+ "github.com/openshift/origin/pkg/deploy/api" |
|
| 13 |
+ |
|
| 14 |
+ _ "github.com/openshift/origin/pkg/deploy/api/v1beta1" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+func NewTestEtcd(client tools.EtcdClient) *Etcd {
|
|
| 18 |
+ return NewEtcd(client) |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func TestEtcdListEmptyDeployments(t *testing.T) {
|
|
| 22 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 23 |
+ key := "/deployments" |
|
| 24 |
+ fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
| 25 |
+ R: &etcd.Response{
|
|
| 26 |
+ Node: &etcd.Node{
|
|
| 27 |
+ Nodes: []*etcd.Node{},
|
|
| 28 |
+ }, |
|
| 29 |
+ }, |
|
| 30 |
+ E: nil, |
|
| 31 |
+ } |
|
| 32 |
+ registry := NewTestEtcd(fakeClient) |
|
| 33 |
+ deployments, err := registry.ListDeployments(labels.Everything()) |
|
| 34 |
+ if err != nil {
|
|
| 35 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ if len(deployments.Items) != 0 {
|
|
| 39 |
+ t.Errorf("Unexpected deployments list: %#v", deployments)
|
|
| 40 |
+ } |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func TestEtcdListErrorDeployments(t *testing.T) {
|
|
| 44 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 45 |
+ key := "/deployments" |
|
| 46 |
+ fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
| 47 |
+ R: &etcd.Response{
|
|
| 48 |
+ Node: nil, |
|
| 49 |
+ }, |
|
| 50 |
+ E: fmt.Errorf("some error"),
|
|
| 51 |
+ } |
|
| 52 |
+ registry := NewTestEtcd(fakeClient) |
|
| 53 |
+ deployments, err := registry.ListDeployments(labels.Everything()) |
|
| 54 |
+ if err == nil {
|
|
| 55 |
+ t.Error("unexpected nil error")
|
|
| 56 |
+ } |
|
| 57 |
+ |
|
| 58 |
+ if deployments != nil {
|
|
| 59 |
+ t.Errorf("Unexpected non-nil deployments: %#v", deployments)
|
|
| 60 |
+ } |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func TestEtcdListEverythingDeployments(t *testing.T) {
|
|
| 64 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 65 |
+ key := "/deployments" |
|
| 66 |
+ fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
| 67 |
+ R: &etcd.Response{
|
|
| 68 |
+ Node: &etcd.Node{
|
|
| 69 |
+ Nodes: []*etcd.Node{
|
|
| 70 |
+ {
|
|
| 71 |
+ Value: runtime.EncodeOrDie(api.Deployment{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
|
|
| 72 |
+ }, |
|
| 73 |
+ {
|
|
| 74 |
+ Value: runtime.EncodeOrDie(api.Deployment{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
|
|
| 75 |
+ }, |
|
| 76 |
+ }, |
|
| 77 |
+ }, |
|
| 78 |
+ }, |
|
| 79 |
+ E: nil, |
|
| 80 |
+ } |
|
| 81 |
+ registry := NewTestEtcd(fakeClient) |
|
| 82 |
+ deployments, err := registry.ListDeployments(labels.Everything()) |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 87 |
+ if len(deployments.Items) != 2 || deployments.Items[0].ID != "foo" || deployments.Items[1].ID != "bar" {
|
|
| 88 |
+ t.Errorf("Unexpected deployments list: %#v", deployments)
|
|
| 89 |
+ } |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func TestEtcdListFilteredDeployments(t *testing.T) {
|
|
| 93 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 94 |
+ key := "/deployments" |
|
| 95 |
+ fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
| 96 |
+ R: &etcd.Response{
|
|
| 97 |
+ Node: &etcd.Node{
|
|
| 98 |
+ Nodes: []*etcd.Node{
|
|
| 99 |
+ {
|
|
| 100 |
+ Value: runtime.EncodeOrDie(api.Deployment{
|
|
| 101 |
+ JSONBase: kubeapi.JSONBase{ID: "foo"},
|
|
| 102 |
+ Labels: map[string]string{"env": "prod"},
|
|
| 103 |
+ }), |
|
| 104 |
+ }, |
|
| 105 |
+ {
|
|
| 106 |
+ Value: runtime.EncodeOrDie(api.Deployment{
|
|
| 107 |
+ JSONBase: kubeapi.JSONBase{ID: "bar"},
|
|
| 108 |
+ Labels: map[string]string{"env": "dev"},
|
|
| 109 |
+ }), |
|
| 110 |
+ }, |
|
| 111 |
+ }, |
|
| 112 |
+ }, |
|
| 113 |
+ }, |
|
| 114 |
+ E: nil, |
|
| 115 |
+ } |
|
| 116 |
+ registry := NewTestEtcd(fakeClient) |
|
| 117 |
+ deployments, err := registry.ListDeployments(labels.SelectorFromSet(labels.Set{"env": "dev"}))
|
|
| 118 |
+ if err != nil {
|
|
| 119 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 120 |
+ } |
|
| 121 |
+ |
|
| 122 |
+ if len(deployments.Items) != 1 || deployments.Items[0].ID != "bar" {
|
|
| 123 |
+ t.Errorf("Unexpected deployments list: %#v", deployments)
|
|
| 124 |
+ } |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+func TestEtcdGetDeployments(t *testing.T) {
|
|
| 128 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 129 |
+ fakeClient.Set("/deployments/foo", runtime.EncodeOrDie(api.Deployment{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
|
|
| 130 |
+ registry := NewTestEtcd(fakeClient) |
|
| 131 |
+ deployment, err := registry.GetDeployment("foo")
|
|
| 132 |
+ if err != nil {
|
|
| 133 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ if deployment.ID != "foo" {
|
|
| 137 |
+ t.Errorf("Unexpected deployment: %#v", deployment)
|
|
| 138 |
+ } |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+func TestEtcdGetNotFoundDeployments(t *testing.T) {
|
|
| 142 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 143 |
+ fakeClient.Data["/deployments/foo"] = tools.EtcdResponseWithError{
|
|
| 144 |
+ R: &etcd.Response{
|
|
| 145 |
+ Node: nil, |
|
| 146 |
+ }, |
|
| 147 |
+ E: tools.EtcdErrorNotFound, |
|
| 148 |
+ } |
|
| 149 |
+ registry := NewTestEtcd(fakeClient) |
|
| 150 |
+ deployment, err := registry.GetDeployment("foo")
|
|
| 151 |
+ if err == nil {
|
|
| 152 |
+ t.Errorf("Unexpected non-error.")
|
|
| 153 |
+ } |
|
| 154 |
+ if deployment != nil {
|
|
| 155 |
+ t.Errorf("Unexpected deployment: %#v", deployment)
|
|
| 156 |
+ } |
|
| 157 |
+} |
|
| 158 |
+ |
|
| 159 |
+func TestEtcdCreateDeployments(t *testing.T) {
|
|
| 160 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 161 |
+ fakeClient.TestIndex = true |
|
| 162 |
+ fakeClient.Data["/deployments/foo"] = tools.EtcdResponseWithError{
|
|
| 163 |
+ R: &etcd.Response{
|
|
| 164 |
+ Node: nil, |
|
| 165 |
+ }, |
|
| 166 |
+ E: tools.EtcdErrorNotFound, |
|
| 167 |
+ } |
|
| 168 |
+ registry := NewTestEtcd(fakeClient) |
|
| 169 |
+ err := registry.CreateDeployment(&api.Deployment{
|
|
| 170 |
+ JSONBase: kubeapi.JSONBase{
|
|
| 171 |
+ ID: "foo", |
|
| 172 |
+ }, |
|
| 173 |
+ }) |
|
| 174 |
+ if err != nil {
|
|
| 175 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 176 |
+ } |
|
| 177 |
+ |
|
| 178 |
+ resp, err := fakeClient.Get("/deployments/foo", false, false)
|
|
| 179 |
+ if err != nil {
|
|
| 180 |
+ t.Fatalf("Unexpected error %v", err)
|
|
| 181 |
+ } |
|
| 182 |
+ var deployment api.Deployment |
|
| 183 |
+ err = runtime.DecodeInto([]byte(resp.Node.Value), &deployment) |
|
| 184 |
+ if err != nil {
|
|
| 185 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 186 |
+ } |
|
| 187 |
+ |
|
| 188 |
+ if deployment.ID != "foo" {
|
|
| 189 |
+ t.Errorf("Unexpected deployment: %#v %s", deployment, resp.Node.Value)
|
|
| 190 |
+ } |
|
| 191 |
+} |
|
| 192 |
+ |
|
| 193 |
+func TestEtcdCreateAlreadyExistsDeployments(t *testing.T) {
|
|
| 194 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 195 |
+ fakeClient.Data["/deployments/foo"] = tools.EtcdResponseWithError{
|
|
| 196 |
+ R: &etcd.Response{
|
|
| 197 |
+ Node: &etcd.Node{
|
|
| 198 |
+ Value: runtime.EncodeOrDie(api.Deployment{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
|
|
| 199 |
+ }, |
|
| 200 |
+ }, |
|
| 201 |
+ E: nil, |
|
| 202 |
+ } |
|
| 203 |
+ registry := NewTestEtcd(fakeClient) |
|
| 204 |
+ err := registry.CreateDeployment(&api.Deployment{
|
|
| 205 |
+ JSONBase: kubeapi.JSONBase{
|
|
| 206 |
+ ID: "foo", |
|
| 207 |
+ }, |
|
| 208 |
+ }) |
|
| 209 |
+ if err == nil {
|
|
| 210 |
+ t.Error("Unexpected non-error")
|
|
| 211 |
+ } |
|
| 212 |
+ if !errors.IsAlreadyExists(err) {
|
|
| 213 |
+ t.Errorf("Expected 'already exists' error, got %#v", err)
|
|
| 214 |
+ } |
|
| 215 |
+} |
|
| 216 |
+ |
|
| 217 |
+func TestEtcdUpdateOkDeployments(t *testing.T) {
|
|
| 218 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 219 |
+ registry := NewTestEtcd(fakeClient) |
|
| 220 |
+ err := registry.UpdateDeployment(&api.Deployment{})
|
|
| 221 |
+ if err != nil {
|
|
| 222 |
+ t.Error("Unexpected error")
|
|
| 223 |
+ } |
|
| 224 |
+} |
|
| 225 |
+ |
|
| 226 |
+func TestEtcdDeleteNotFoundDeployments(t *testing.T) {
|
|
| 227 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 228 |
+ fakeClient.Err = tools.EtcdErrorNotFound |
|
| 229 |
+ registry := NewTestEtcd(fakeClient) |
|
| 230 |
+ err := registry.DeleteDeployment("foo")
|
|
| 231 |
+ if err == nil {
|
|
| 232 |
+ t.Error("Unexpected non-error")
|
|
| 233 |
+ } |
|
| 234 |
+ if !errors.IsNotFound(err) {
|
|
| 235 |
+ t.Errorf("Expected 'not found' error, got %#v", err)
|
|
| 236 |
+ } |
|
| 237 |
+} |
|
| 238 |
+ |
|
| 239 |
+func TestEtcdDeleteErrorDeployments(t *testing.T) {
|
|
| 240 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 241 |
+ fakeClient.Err = fmt.Errorf("Some error")
|
|
| 242 |
+ registry := NewTestEtcd(fakeClient) |
|
| 243 |
+ err := registry.DeleteDeployment("foo")
|
|
| 244 |
+ if err == nil {
|
|
| 245 |
+ t.Error("Unexpected non-error")
|
|
| 246 |
+ } |
|
| 247 |
+} |
|
| 248 |
+ |
|
| 249 |
+func TestEtcdDeleteOkDeployments(t *testing.T) {
|
|
| 250 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 251 |
+ registry := NewTestEtcd(fakeClient) |
|
| 252 |
+ key := "/deployments/foo" |
|
| 253 |
+ err := registry.DeleteDeployment("foo")
|
|
| 254 |
+ if err != nil {
|
|
| 255 |
+ t.Errorf("Unexpected error: %#v", err)
|
|
| 256 |
+ } |
|
| 257 |
+ if len(fakeClient.DeletedKeys) != 1 {
|
|
| 258 |
+ t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
|
|
| 259 |
+ } else if fakeClient.DeletedKeys[0] != key {
|
|
| 260 |
+ t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
|
| 261 |
+ } |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+func TestEtcdListEmptyDeploymentConfig(t *testing.T) {
|
|
| 265 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 266 |
+ key := "/deploymentConfigs" |
|
| 267 |
+ fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
| 268 |
+ R: &etcd.Response{
|
|
| 269 |
+ Node: &etcd.Node{
|
|
| 270 |
+ Nodes: []*etcd.Node{},
|
|
| 271 |
+ }, |
|
| 272 |
+ }, |
|
| 273 |
+ E: nil, |
|
| 274 |
+ } |
|
| 275 |
+ registry := NewTestEtcd(fakeClient) |
|
| 276 |
+ deploymentConfigs, err := registry.ListDeploymentConfigs(labels.Everything()) |
|
| 277 |
+ if err != nil {
|
|
| 278 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 279 |
+ } |
|
| 280 |
+ |
|
| 281 |
+ if len(deploymentConfigs.Items) != 0 {
|
|
| 282 |
+ t.Errorf("Unexpected deploymentConfigs list: %#v", deploymentConfigs)
|
|
| 283 |
+ } |
|
| 284 |
+} |
|
| 285 |
+ |
|
| 286 |
+func TestEtcdListErrorDeploymentConfig(t *testing.T) {
|
|
| 287 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 288 |
+ key := "/deploymentConfigs" |
|
| 289 |
+ fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
| 290 |
+ R: &etcd.Response{
|
|
| 291 |
+ Node: nil, |
|
| 292 |
+ }, |
|
| 293 |
+ E: fmt.Errorf("some error"),
|
|
| 294 |
+ } |
|
| 295 |
+ registry := NewTestEtcd(fakeClient) |
|
| 296 |
+ deploymentConfigs, err := registry.ListDeploymentConfigs(labels.Everything()) |
|
| 297 |
+ if err == nil {
|
|
| 298 |
+ t.Error("unexpected nil error")
|
|
| 299 |
+ } |
|
| 300 |
+ |
|
| 301 |
+ if deploymentConfigs != nil {
|
|
| 302 |
+ t.Errorf("Unexpected non-nil deploymentConfigs: %#v", deploymentConfigs)
|
|
| 303 |
+ } |
|
| 304 |
+} |
|
| 305 |
+ |
|
| 306 |
+func TestEtcdListEverythingDeploymentConfig(t *testing.T) {
|
|
| 307 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 308 |
+ key := "/deploymentConfigs" |
|
| 309 |
+ fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
| 310 |
+ R: &etcd.Response{
|
|
| 311 |
+ Node: &etcd.Node{
|
|
| 312 |
+ Nodes: []*etcd.Node{
|
|
| 313 |
+ {
|
|
| 314 |
+ Value: runtime.EncodeOrDie(api.DeploymentConfig{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
|
|
| 315 |
+ }, |
|
| 316 |
+ {
|
|
| 317 |
+ Value: runtime.EncodeOrDie(api.DeploymentConfig{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
|
|
| 318 |
+ }, |
|
| 319 |
+ }, |
|
| 320 |
+ }, |
|
| 321 |
+ }, |
|
| 322 |
+ E: nil, |
|
| 323 |
+ } |
|
| 324 |
+ registry := NewTestEtcd(fakeClient) |
|
| 325 |
+ deploymentConfigs, err := registry.ListDeploymentConfigs(labels.Everything()) |
|
| 326 |
+ if err != nil {
|
|
| 327 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 328 |
+ } |
|
| 329 |
+ |
|
| 330 |
+ if len(deploymentConfigs.Items) != 2 || deploymentConfigs.Items[0].ID != "foo" || deploymentConfigs.Items[1].ID != "bar" {
|
|
| 331 |
+ t.Errorf("Unexpected deploymentConfigs list: %#v", deploymentConfigs)
|
|
| 332 |
+ } |
|
| 333 |
+} |
|
| 334 |
+ |
|
| 335 |
+func TestEtcdListFilteredDeploymentConfig(t *testing.T) {
|
|
| 336 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 337 |
+ key := "/deploymentConfigs" |
|
| 338 |
+ fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
| 339 |
+ R: &etcd.Response{
|
|
| 340 |
+ Node: &etcd.Node{
|
|
| 341 |
+ Nodes: []*etcd.Node{
|
|
| 342 |
+ {
|
|
| 343 |
+ Value: runtime.EncodeOrDie(api.DeploymentConfig{
|
|
| 344 |
+ JSONBase: kubeapi.JSONBase{ID: "foo"},
|
|
| 345 |
+ Labels: map[string]string{"env": "prod"},
|
|
| 346 |
+ }), |
|
| 347 |
+ }, |
|
| 348 |
+ {
|
|
| 349 |
+ Value: runtime.EncodeOrDie(api.DeploymentConfig{
|
|
| 350 |
+ JSONBase: kubeapi.JSONBase{ID: "bar"},
|
|
| 351 |
+ Labels: map[string]string{"env": "dev"},
|
|
| 352 |
+ }), |
|
| 353 |
+ }, |
|
| 354 |
+ }, |
|
| 355 |
+ }, |
|
| 356 |
+ }, |
|
| 357 |
+ E: nil, |
|
| 358 |
+ } |
|
| 359 |
+ registry := NewTestEtcd(fakeClient) |
|
| 360 |
+ deploymentConfigs, err := registry.ListDeploymentConfigs(labels.SelectorFromSet(labels.Set{"env": "dev"}))
|
|
| 361 |
+ if err != nil {
|
|
| 362 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 363 |
+ } |
|
| 364 |
+ |
|
| 365 |
+ if len(deploymentConfigs.Items) != 1 || deploymentConfigs.Items[0].ID != "bar" {
|
|
| 366 |
+ t.Errorf("Unexpected deploymentConfigs list: %#v", deploymentConfigs)
|
|
| 367 |
+ } |
|
| 368 |
+} |
|
| 369 |
+ |
|
| 370 |
+func TestEtcdGetDeploymentConfig(t *testing.T) {
|
|
| 371 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 372 |
+ fakeClient.Set("/deploymentConfigs/foo", runtime.EncodeOrDie(api.DeploymentConfig{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
|
|
| 373 |
+ registry := NewTestEtcd(fakeClient) |
|
| 374 |
+ deployment, err := registry.GetDeploymentConfig("foo")
|
|
| 375 |
+ if err != nil {
|
|
| 376 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 377 |
+ } |
|
| 378 |
+ |
|
| 379 |
+ if deployment.ID != "foo" {
|
|
| 380 |
+ t.Errorf("Unexpected deployment: %#v", deployment)
|
|
| 381 |
+ } |
|
| 382 |
+} |
|
| 383 |
+ |
|
| 384 |
+func TestEtcdGetNotFoundDeploymentConfig(t *testing.T) {
|
|
| 385 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 386 |
+ fakeClient.Data["/deploymentConfigs/foo"] = tools.EtcdResponseWithError{
|
|
| 387 |
+ R: &etcd.Response{
|
|
| 388 |
+ Node: nil, |
|
| 389 |
+ }, |
|
| 390 |
+ E: tools.EtcdErrorNotFound, |
|
| 391 |
+ } |
|
| 392 |
+ registry := NewTestEtcd(fakeClient) |
|
| 393 |
+ deployment, err := registry.GetDeploymentConfig("foo")
|
|
| 394 |
+ if err == nil {
|
|
| 395 |
+ t.Errorf("Unexpected non-error.")
|
|
| 396 |
+ } |
|
| 397 |
+ if deployment != nil {
|
|
| 398 |
+ t.Errorf("Unexpected deployment: %#v", deployment)
|
|
| 399 |
+ } |
|
| 400 |
+} |
|
| 401 |
+ |
|
| 402 |
+func TestEtcdCreateDeploymentConfig(t *testing.T) {
|
|
| 403 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 404 |
+ fakeClient.TestIndex = true |
|
| 405 |
+ fakeClient.Data["/deploymentConfigs/foo"] = tools.EtcdResponseWithError{
|
|
| 406 |
+ R: &etcd.Response{
|
|
| 407 |
+ Node: nil, |
|
| 408 |
+ }, |
|
| 409 |
+ E: tools.EtcdErrorNotFound, |
|
| 410 |
+ } |
|
| 411 |
+ registry := NewTestEtcd(fakeClient) |
|
| 412 |
+ err := registry.CreateDeploymentConfig(&api.DeploymentConfig{
|
|
| 413 |
+ JSONBase: kubeapi.JSONBase{
|
|
| 414 |
+ ID: "foo", |
|
| 415 |
+ }, |
|
| 416 |
+ }) |
|
| 417 |
+ if err != nil {
|
|
| 418 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 419 |
+ } |
|
| 420 |
+ |
|
| 421 |
+ resp, err := fakeClient.Get("/deploymentConfigs/foo", false, false)
|
|
| 422 |
+ if err != nil {
|
|
| 423 |
+ t.Fatalf("Unexpected error %v", err)
|
|
| 424 |
+ } |
|
| 425 |
+ var d api.DeploymentConfig |
|
| 426 |
+ err = runtime.DecodeInto([]byte(resp.Node.Value), &d) |
|
| 427 |
+ if err != nil {
|
|
| 428 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 429 |
+ } |
|
| 430 |
+ |
|
| 431 |
+ if d.ID != "foo" {
|
|
| 432 |
+ t.Errorf("Unexpected deploymentConfig: %#v %s", d, resp.Node.Value)
|
|
| 433 |
+ } |
|
| 434 |
+} |
|
| 435 |
+ |
|
| 436 |
+func TestEtcdCreateAlreadyExistsDeploymentConfig(t *testing.T) {
|
|
| 437 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 438 |
+ fakeClient.Data["/deploymentConfigs/foo"] = tools.EtcdResponseWithError{
|
|
| 439 |
+ R: &etcd.Response{
|
|
| 440 |
+ Node: &etcd.Node{
|
|
| 441 |
+ Value: runtime.EncodeOrDie(api.DeploymentConfig{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
|
|
| 442 |
+ }, |
|
| 443 |
+ }, |
|
| 444 |
+ E: nil, |
|
| 445 |
+ } |
|
| 446 |
+ registry := NewTestEtcd(fakeClient) |
|
| 447 |
+ err := registry.CreateDeploymentConfig(&api.DeploymentConfig{
|
|
| 448 |
+ JSONBase: kubeapi.JSONBase{
|
|
| 449 |
+ ID: "foo", |
|
| 450 |
+ }, |
|
| 451 |
+ }) |
|
| 452 |
+ if err == nil {
|
|
| 453 |
+ t.Error("Unexpected non-error")
|
|
| 454 |
+ } |
|
| 455 |
+ if !errors.IsAlreadyExists(err) {
|
|
| 456 |
+ t.Errorf("Expected 'already exists' error, got %#v", err)
|
|
| 457 |
+ } |
|
| 458 |
+} |
|
| 459 |
+ |
|
| 460 |
+func TestEtcdUpdateOkDeploymentConfig(t *testing.T) {
|
|
| 461 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 462 |
+ registry := NewTestEtcd(fakeClient) |
|
| 463 |
+ err := registry.UpdateDeploymentConfig(&api.DeploymentConfig{})
|
|
| 464 |
+ if err != nil {
|
|
| 465 |
+ t.Error("Unexpected error")
|
|
| 466 |
+ } |
|
| 467 |
+} |
|
| 468 |
+ |
|
| 469 |
+func TestEtcdDeleteNotFoundDeploymentConfig(t *testing.T) {
|
|
| 470 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 471 |
+ fakeClient.Err = tools.EtcdErrorNotFound |
|
| 472 |
+ registry := NewTestEtcd(fakeClient) |
|
| 473 |
+ err := registry.DeleteDeploymentConfig("foo")
|
|
| 474 |
+ if err == nil {
|
|
| 475 |
+ t.Error("Unexpected non-error")
|
|
| 476 |
+ } |
|
| 477 |
+ if !errors.IsNotFound(err) {
|
|
| 478 |
+ t.Errorf("Expected 'not found' error, got %#v", err)
|
|
| 479 |
+ } |
|
| 480 |
+} |
|
| 481 |
+ |
|
| 482 |
+func TestEtcdDeleteErrorDeploymentConfig(t *testing.T) {
|
|
| 483 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 484 |
+ fakeClient.Err = fmt.Errorf("Some error")
|
|
| 485 |
+ registry := NewTestEtcd(fakeClient) |
|
| 486 |
+ err := registry.DeleteDeploymentConfig("foo")
|
|
| 487 |
+ if err == nil {
|
|
| 488 |
+ t.Error("Unexpected non-error")
|
|
| 489 |
+ } |
|
| 490 |
+} |
|
| 491 |
+ |
|
| 492 |
+func TestEtcdDeleteOkDeploymentConfig(t *testing.T) {
|
|
| 493 |
+ fakeClient := tools.NewFakeEtcdClient(t) |
|
| 494 |
+ registry := NewTestEtcd(fakeClient) |
|
| 495 |
+ key := "/deploymentConfigs/foo" |
|
| 496 |
+ err := registry.DeleteDeploymentConfig("foo")
|
|
| 497 |
+ if err != nil {
|
|
| 498 |
+ t.Errorf("Unexpected error: %#v", err)
|
|
| 499 |
+ } |
|
| 500 |
+ if len(fakeClient.DeletedKeys) != 1 {
|
|
| 501 |
+ t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
|
|
| 502 |
+ } else if fakeClient.DeletedKeys[0] != key {
|
|
| 503 |
+ t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
|
| 504 |
+ } |
|
| 505 |
+} |
| 0 | 506 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,56 @@ |
| 0 |
+package test |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "sync" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 6 |
+ "github.com/openshift/origin/pkg/deploy/api" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+type DeploymentRegistry struct {
|
|
| 10 |
+ Err error |
|
| 11 |
+ Deployment *api.Deployment |
|
| 12 |
+ Deployments *api.DeploymentList |
|
| 13 |
+ sync.Mutex |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+func NewDeploymentRegistry() *DeploymentRegistry {
|
|
| 17 |
+ return &DeploymentRegistry{}
|
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func (r *DeploymentRegistry) ListDeployments(selector labels.Selector) (*api.DeploymentList, error) {
|
|
| 21 |
+ r.Lock() |
|
| 22 |
+ defer r.Unlock() |
|
| 23 |
+ |
|
| 24 |
+ return r.Deployments, r.Err |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+func (r *DeploymentRegistry) GetDeployment(id string) (*api.Deployment, error) {
|
|
| 28 |
+ r.Lock() |
|
| 29 |
+ defer r.Unlock() |
|
| 30 |
+ |
|
| 31 |
+ return r.Deployment, r.Err |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func (r *DeploymentRegistry) CreateDeployment(deployment *api.Deployment) error {
|
|
| 35 |
+ r.Lock() |
|
| 36 |
+ defer r.Unlock() |
|
| 37 |
+ |
|
| 38 |
+ r.Deployment = deployment |
|
| 39 |
+ return r.Err |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+func (r *DeploymentRegistry) UpdateDeployment(deployment *api.Deployment) error {
|
|
| 43 |
+ r.Lock() |
|
| 44 |
+ defer r.Unlock() |
|
| 45 |
+ |
|
| 46 |
+ r.Deployment = deployment |
|
| 47 |
+ return r.Err |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+func (r *DeploymentRegistry) DeleteDeployment(id string) error {
|
|
| 51 |
+ r.Lock() |
|
| 52 |
+ defer r.Unlock() |
|
| 53 |
+ |
|
| 54 |
+ return r.Err |
|
| 55 |
+} |
| 0 | 56 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,56 @@ |
| 0 |
+package test |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "sync" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 6 |
+ "github.com/openshift/origin/pkg/deploy/api" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+type DeploymentConfigRegistry struct {
|
|
| 10 |
+ Err error |
|
| 11 |
+ DeploymentConfig *api.DeploymentConfig |
|
| 12 |
+ DeploymentConfigs *api.DeploymentConfigList |
|
| 13 |
+ sync.Mutex |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+func NewDeploymentConfigRegistry() *DeploymentConfigRegistry {
|
|
| 17 |
+ return &DeploymentConfigRegistry{}
|
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func (r *DeploymentConfigRegistry) ListDeploymentConfigs(selector labels.Selector) (*api.DeploymentConfigList, error) {
|
|
| 21 |
+ r.Lock() |
|
| 22 |
+ defer r.Unlock() |
|
| 23 |
+ |
|
| 24 |
+ return r.DeploymentConfigs, r.Err |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+func (r *DeploymentConfigRegistry) GetDeploymentConfig(id string) (*api.DeploymentConfig, error) {
|
|
| 28 |
+ r.Lock() |
|
| 29 |
+ defer r.Unlock() |
|
| 30 |
+ |
|
| 31 |
+ return r.DeploymentConfig, r.Err |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func (r *DeploymentConfigRegistry) CreateDeploymentConfig(image *api.DeploymentConfig) error {
|
|
| 35 |
+ r.Lock() |
|
| 36 |
+ defer r.Unlock() |
|
| 37 |
+ |
|
| 38 |
+ r.DeploymentConfig = image |
|
| 39 |
+ return r.Err |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+func (r *DeploymentConfigRegistry) UpdateDeploymentConfig(image *api.DeploymentConfig) error {
|
|
| 43 |
+ r.Lock() |
|
| 44 |
+ defer r.Unlock() |
|
| 45 |
+ |
|
| 46 |
+ r.DeploymentConfig = image |
|
| 47 |
+ return r.Err |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+func (r *DeploymentConfigRegistry) DeleteDeploymentConfig(id string) error {
|
|
| 51 |
+ r.Lock() |
|
| 52 |
+ defer r.Unlock() |
|
| 53 |
+ |
|
| 54 |
+ return r.Err |
|
| 55 |
+} |