Browse code

Make deploymentConfigs with config change triggers deploy automatically when created

Paul Morie authored on 2014/11/04 07:49:24
Showing 7 changed files
... ...
@@ -60,7 +60,7 @@ All commands assume the `openshift` binary is in your path:
60 60
 
61 61
 7. *Optional:* Add the following webhook to your new github repository:
62 62
 
63
-        $ http://<host>:8080/osapi/v1beta1/buildConfigHooks/build100/secret101/github
63
+        $ http://<host>:8080/osapi/v1beta1/buildConfigHooks/ruby-sample-build/secret101/github
64 64
   * Note: Using the webhook requires your OpenShift server be publicly accessible so github can reach it to invoke the hook.
65 65
 
66 66
 8. Edit application-template.json
... ...
@@ -74,11 +74,11 @@ All commands assume the `openshift` binary is in your path:
74 74
  * If you setup the github webhook in step 7, push a change to app.rb in your ruby sample repository from step 6.
75 75
  * Otherwise you can simulate the webhook invocation by running:
76 76
 
77
-            $ curl -s -A "GitHub-Hookshot/github" -H "Content-Type:application/json" -H "X-Github-Event:push" -d @github-webhook-example.json http://localhost:8080/osapi/v1beta1/buildConfigHooks/build100/secret101/github
77
+            $ curl -s -A "GitHub-Hookshot/github" -H "Content-Type:application/json" -H "X-Github-Event:push" -d @github-webhook-example.json http://localhost:8080/osapi/v1beta1/buildConfigHooks/ruby-sample-build/secret101/github
78 78
 
79 79
     In the OpenShift logs (logs/openshift.log) you should see something like:
80 80
 
81
-        I0916 13:50:22.479529 21375 log.go:134] POST /osapi/v1beta1/buildConfigHooks/build100/secret101/github
81
+        I0916 13:50:22.479529 21375 log.go:134] POST /osapi/v1beta1/buildConfigHooks/ruby-sample-build/secret101/github
82 82
 
83 83
     which confirms the webhook was triggered.
84 84
 
... ...
@@ -136,7 +136,7 @@ You should see a welcome page and a form that allows you to query and update key
136 136
 15. Make a change to your ruby sample main.html file and push it.
137 137
  * If you do not have the webhook enabled, you'll have to manually trigger another build:
138 138
 
139
-            $ curl -s -A "GitHub-Hookshot/github" -H "Content-Type:application/json" -H "X-Github-Event:push" -d @github-webhook-example.json http://localhost:8080/osapi/v1beta1/buildConfigHooks/build100/secret101/github
139
+            $ curl -s -A "GitHub-Hookshot/github" -H "Content-Type:application/json" -H "X-Github-Event:push" -d @github-webhook-example.json http://localhost:8080/osapi/v1beta1/buildConfigHooks/ruby-sample-build/secret101/github
140 140
 
141 141
 16. Repeat step 11 (waiting for the build to complete).  Once the build is complete, refreshing your browser should show your changes.
142 142
 
... ...
@@ -50,7 +50,7 @@
50 50
       }
51 51
     },
52 52
     {
53
-      "id": "build100",
53
+      "id": "ruby-sample-build",
54 54
       "kind": "BuildConfig",
55 55
       "apiVersion": "v1beta1",
56 56
       "parameters": {
... ...
@@ -70,7 +70,7 @@
70 70
       },
71 71
       "secret": "secret101",
72 72
       "labels": {
73
-        "name": "build100"
73
+        "name": "ruby-sample-build"
74 74
       }
75 75
     },
76 76
     {
... ...
@@ -88,9 +88,6 @@
88 88
             "repositoryName": "172.121.17.1:5001/openshift/origin-ruby-sample",
89 89
             "tag": "latest"
90 90
           }
91
-        },
92
-        {
93
-          "type": "ConfigChange"
94 91
         }
95 92
       ],
96 93
       "template": {
... ...
@@ -156,46 +153,52 @@
156 156
     },
157 157
     {
158 158
       "id": "database",
159
-      "kind": "Deployment",
159
+      "kind": "DeploymentConfig",
160 160
       "apiVersion": "v1beta1",
161
-      "triggerPolicy": "manual",
162
-      "strategy": {
163
-        "type": "Basic"
164
-      },
165
-      "controllerTemplate": {
166
-        "replicas": 1,
167
-        "replicaSelector": {
168
-          "name": "database"
169
-      },
170
-      "podTemplate": {
171
-        "desiredState": {
172
-          "manifest": {
173
-            "version": "v1beta1",
174
-            "containers": [
175
-              {
176
-                 "name": "ruby-helloworld-database",
177
-                 "image": "mysql",
178
-                  "env": [
179
-                    {
180
-                      "name": "MYSQL_ROOT_PASSWORD",
181
-                      "value": "${MYSQL_ROOT_PASSWORD}"
182
-                    },
183
-                    {
184
-                      "name": "MYSQL_DATABASE",
185
-                      "value": "${MYSQL_DATABASE}"
186
-                    }
187
-                  ],
188
-                  "ports": [
189
-                    {
190
-                      "containerPort": 3306
191
-                    }
192
-                  ]
193
-                }
194
-              ]
195
-            }
196
-          },
197
-          "labels": {
161
+      "triggers": [
162
+        {
163
+          "type": "ConfigChange"
164
+        }
165
+      ],
166
+      "template": {
167
+        "strategy": {
168
+          "type": "Basic"
169
+        },
170
+        "controllerTemplate": {
171
+          "replicas": 1,
172
+          "replicaSelector": {
198 173
             "name": "database"
174
+        },
175
+        "podTemplate": {
176
+          "desiredState": {
177
+            "manifest": {
178
+              "version": "v1beta1",
179
+              "containers": [
180
+                {
181
+                   "name": "ruby-helloworld-database",
182
+                   "image": "mysql",
183
+                    "env": [
184
+                      {
185
+                        "name": "MYSQL_ROOT_PASSWORD",
186
+                        "value": "${MYSQL_ROOT_PASSWORD}"
187
+                      },
188
+                      {
189
+                        "name": "MYSQL_DATABASE",
190
+                        "value": "${MYSQL_DATABASE}"
191
+                      }
192
+                    ],
193
+                    "ports": [
194
+                      {
195
+                        "containerPort": 3306
196
+                      }
197
+                    ]
198
+                  }
199
+                ]
200
+              }
201
+            },
202
+            "labels": {
203
+              "name": "database"
204
+            }
199 205
           }
200 206
         }
201 207
       }
... ...
@@ -1,83 +1,95 @@
1 1
 {
2
-  "id": "docker-registry",
3
-  "kind": "Config",
4
-  "apiVersion": "v1beta1",
5
-  "creationTimestamp": "2014-09-18T18:28:38-04:00",
6
-  "name": "docker-registry-config",
7
-  "description": "Creates a private docker registry",
8
-  "items": [
2
+  "id":"docker-registry",
3
+  "kind":"Config",
4
+  "apiVersion":"v1beta1",
5
+  "creationTimestamp":"2014-09-18T18:28:38-04:00",
6
+  "name":"docker-registry-config",
7
+  "description":"Creates a private docker registry",
8
+  "items":[
9 9
     {
10
-      "apiVersion": "v1beta1",
11
-      "creationTimestamp": null,
12
-      "id": "docker-registry",
13
-      "kind": "Service",
14
-      "port": 5001,
15
-      "containerPort": 5000,
16
-      "selector": {
17
-        "name": "registrypod"
10
+      "apiVersion":"v1beta1",
11
+      "creationTimestamp":null,
12
+      "id":"docker-registry",
13
+      "kind":"Service",
14
+      "port":5001,
15
+      "containerPort":5000,
16
+      "selector":{
17
+        "name":"registrypod"
18 18
       }
19 19
     },
20 20
     {
21
-      "id": "docker-registry",
22
-      "kind": "ReplicationController",
23
-      "apiVersion": "v1beta1",
24
-      "desiredState": {
25
-        "replicas": 1,
26
-        "replicaSelector": {
27
-          "name": "registrypod"
21
+      "id":"docker-registry",
22
+      "kind":"DeploymentConfig",
23
+      "apiVersion":"v1beta1",
24
+      "triggers":[
25
+        {
26
+          "type":"ConfigChange",
27
+        }
28
+      ],
29
+      "template":{
30
+        "strategy":{
31
+          "type":"Basic"
28 32
         },
29
-        "podTemplate": {
30
-          "desiredState": {
31
-            "manifest": {
32
-              "containers": [
33
-                {
34
-                  "image": "openshift/docker-registry",
35
-                  "name": "registry-container",
36
-                  "ports": [
37
-                    {
38
-                      "containerPort": 5000,
39
-                      "protocol": "TCP"
40
-                    }
41
-                  ],
42
-                  "env": [
43
-                    {
44
-                      "name": "STORAGE_PATH",
45
-                      "value": "/tmp/openshift.local.registry"
46
-                    },
47
-                    {
48
-                      "name": "OPENSHIFT_URL",
49
-                      "value": "http://172.17.42.1:8080/osapi/v1beta1"
50
-                    },
51
-                    {
52
-                      "name": "REGISTRY_URL",
53
-                      "value": "172.121.17.1:5001"
54
-                    }
55
-                  ],
56
-                  "volumeMounts": [
57
-                    {
58
-                      "name": "registry-storage",
59
-                      "mountPath": "/tmp/openshift.local.registry",
60
-                      "readOnly": false
61
-                    }
62
-                  ]
63
-                }
64
-              ],
65
-              "version": "v1beta1",
66
-              "volumes": [
67
-                {
68
-                  "name": "registry-storage",
69
-                  "source": {
70
-                    "hostDir": {
71
-                      "path": "/tmp/openshift.local.registry"
33
+        "controllerTemplate":{
34
+          "replicas":1,
35
+          "replicaSelector":{
36
+            "name":"registrypod"
37
+          },
38
+          "podTemplate":{
39
+            "desiredState":{
40
+              "manifest":{
41
+                "containers":[
42
+                  {
43
+                    "image":"openshift/docker-registry",
44
+                    "name":"registry-container",
45
+                    "ports":[
46
+                      {
47
+                        "containerPort":5000,
48
+                        "protocol":"TCP"
49
+                      }
50
+                    ],
51
+                    "env":[
52
+                      {
53
+                        "name":"STORAGE_PATH",
54
+                        "value":"/tmp/openshift.local.registry"
55
+                      },
56
+                      {
57
+                        "name":"OPENSHIFT_URL",
58
+                        "value":"http://172.17.42.1:8080/osapi/v1beta1"
59
+                      },
60
+                      {
61
+                        "name":"REGISTRY_URL",
62
+                        "value":"172.121.17.1:5001"
63
+                      }
64
+                    ],
65
+                    "volumeMounts":[
66
+                      {
67
+                        "name":"registry-storage",
68
+                        "mountPath":"/tmp/openshift.local.registry",
69
+                        "readOnly":false
70
+                      }
71
+                    ]
72
+                  }
73
+                ],
74
+                "version":"v1beta1",
75
+                "volumes":[
76
+                  {
77
+                    "name":"registry-storage",
78
+                    "source":{
79
+                      "hostDir":{
80
+                        "path":"/tmp/openshift.local.registry"
81
+                      }
72 82
                     }
73 83
                   }
74
-                }
75
-              ]
84
+                ]
85
+              },
86
+              "restartpolicy":{
87
+
88
+              }
76 89
             },
77
-            "restartpolicy": {}
78
-          },
79
-          "labels": {
80
-            "name": "registrypod"
90
+            "labels":{
91
+              "name":"registrypod"
92
+            }
81 93
           }
82 94
         }
83 95
       }
... ...
@@ -139,7 +139,7 @@ $openshift kube apply --ns=${NAMESPACE} -c $CONFIG_FILE
139 139
 
140 140
 # Trigger build
141 141
 echo "[INFO] Simulating github hook to trigger new build using curl"
142
-curl -s -A "GitHub-Hookshot/github" -H "Content-Type:application/json" -H "X-Github-Event:push" -d @${FIXTURE_DIR}/github-webhook-example.json http://localhost:8080/osapi/v1beta1/buildConfigHooks/build100/secret101/github?namespace=${NAMESPACE}
142
+curl -s -A "GitHub-Hookshot/github" -H "Content-Type:application/json" -H "X-Github-Event:push" -d @${FIXTURE_DIR}/github-webhook-example.json http://localhost:8080/osapi/v1beta1/buildConfigHooks/ruby-sample-build/secret101/github?namespace=${NAMESPACE}
143 143
 
144 144
 # Wait for build to complete
145 145
 echo "[INFO] Waiting for build to complete"
... ...
@@ -48,7 +48,8 @@ func (dc *DeploymentConfigChangeController) HandleDeploymentConfig() {
48 48
 	}
49 49
 
50 50
 	if config.LatestVersion == 0 {
51
-		glog.V(4).Info("Ignoring config change with LatestVersion=0")
51
+		glog.V(4).Infof("Creating new deployment for config %v", config.ID)
52
+		dc.generateDeployment(config, nil)
52 53
 		return
53 54
 	}
54 55
 
... ...
@@ -67,6 +68,10 @@ func (dc *DeploymentConfigChangeController) HandleDeploymentConfig() {
67 67
 		return
68 68
 	}
69 69
 
70
+	dc.generateDeployment(config, deployment)
71
+}
72
+
73
+func (dc *DeploymentConfigChangeController) generateDeployment(config *deployapi.DeploymentConfig, deployment *deployapi.Deployment) {
70 74
 	ctx := kapi.WithNamespace(kapi.NewContext(), config.Namespace)
71 75
 	newConfig, err := dc.ChangeStrategy.GenerateDeploymentConfig(ctx, config.ID)
72 76
 	if err != nil {
... ...
@@ -74,7 +79,9 @@ func (dc *DeploymentConfigChangeController) HandleDeploymentConfig() {
74 74
 		return
75 75
 	}
76 76
 
77
-	glog.V(4).Infof("Updating config %s (LatestVersion: %d -> %d) to advance existing deployment %s", config.ID, config.LatestVersion, newConfig.LatestVersion, deployment.ID)
77
+	if deployment != nil {
78
+		glog.V(4).Infof("Updating config %s (LatestVersion: %d -> %d) to advance existing deployment %s", config.ID, config.LatestVersion, newConfig.LatestVersion, deployment.ID)
79
+	}
78 80
 
79 81
 	// set the trigger details for the new deployment config
80 82
 	causes := []*deployapi.DeploymentCause{}
... ...
@@ -8,8 +8,8 @@ import (
8 8
 	deploytest "github.com/openshift/origin/pkg/deploy/controller/test"
9 9
 )
10 10
 
11
-// Test the controller's response to a new DeploymentConfig
12
-func TestNewConfig(t *testing.T) {
11
+// Test the controller's response to a new DeploymentConfig with a config change trigger.
12
+func TestNewConfigWithoutTrigger(t *testing.T) {
13 13
 	generated := false
14 14
 	updated := false
15 15
 
... ...
@@ -25,9 +25,9 @@ func TestNewConfig(t *testing.T) {
25 25
 			},
26 26
 		},
27 27
 		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
28
-			return initialConfig()
28
+			return newConfigWithoutTrigger()
29 29
 		},
30
-		DeploymentStore: deploytest.NewFakeDeploymentStore(matchingInitialDeployment()),
30
+		DeploymentStore: deploytest.NewFakeDeploymentStore(nil),
31 31
 	}
32 32
 
33 33
 	controller.HandleDeploymentConfig()
... ...
@@ -41,6 +41,46 @@ func TestNewConfig(t *testing.T) {
41 41
 	}
42 42
 }
43 43
 
44
+func TestNewConfigWithTrigger(t *testing.T) {
45
+	var (
46
+		generatedId string
47
+		updated     *deployapi.DeploymentConfig
48
+	)
49
+
50
+	controller := &DeploymentConfigChangeController{
51
+		ChangeStrategy: &testChangeStrategy{
52
+			GenerateDeploymentConfigFunc: func(id string) (*deployapi.DeploymentConfig, error) {
53
+				generatedId = id
54
+				return generatedConfig(), nil
55
+			},
56
+			UpdateDeploymentConfigFunc: func(config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
57
+				updated = config
58
+				return config, nil
59
+			},
60
+		},
61
+		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
62
+			return newConfigWithTrigger()
63
+		},
64
+		DeploymentStore: deploytest.NewFakeDeploymentStore(nil),
65
+	}
66
+
67
+	controller.HandleDeploymentConfig()
68
+
69
+	if generatedId != "test-deploy-config" {
70
+		t.Fatalf("Unexpected generated config id.  Expected test-deploy-config, got: %v", generatedId)
71
+	}
72
+
73
+	if updated.ID != "test-deploy-config" {
74
+		t.Fatalf("Unexpected updated config id.  Expected test-deploy-config, got: %v", updated.ID)
75
+	} else if updated.Details == nil {
76
+		t.Fatalf("expected config change details to be set")
77
+	} else if updated.Details.Causes == nil {
78
+		t.Fatalf("expected config change causes to be set")
79
+	} else if updated.Details.Causes[0].Type != deployapi.DeploymentTriggerOnConfigChange {
80
+		t.Fatalf("expected config change cause to be set to config change trigger, got %s", updated.Details.Causes[0].Type)
81
+	}
82
+}
83
+
44 84
 // Test the controller's response when the pod template is changed
45 85
 func TestChangeWithTemplateDiff(t *testing.T) {
46 86
 	var (
... ...
@@ -52,7 +92,7 @@ func TestChangeWithTemplateDiff(t *testing.T) {
52 52
 		ChangeStrategy: &testChangeStrategy{
53 53
 			GenerateDeploymentConfigFunc: func(id string) (*deployapi.DeploymentConfig, error) {
54 54
 				generatedId = id
55
-				return generatedConfig(), nil
55
+				return generatedExistingConfig(), nil
56 56
 			},
57 57
 			UpdateDeploymentConfigFunc: func(config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
58 58
 				updated = config
... ...
@@ -98,7 +138,7 @@ func TestChangeWithoutTemplateDiff(t *testing.T) {
98 98
 			},
99 99
 		},
100 100
 		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
101
-			return initialConfig()
101
+			return existingConfigWithTrigger()
102 102
 		},
103 103
 		DeploymentStore: deploytest.NewFakeDeploymentStore(matchingInitialDeployment()),
104 104
 	}
... ...
@@ -127,7 +167,7 @@ func (i *testChangeStrategy) UpdateDeploymentConfig(ctx kapi.Context, config *de
127 127
 	return i.UpdateDeploymentConfigFunc(config)
128 128
 }
129 129
 
130
-func initialConfig() *deployapi.DeploymentConfig {
130
+func existingConfigWithTrigger() *deployapi.DeploymentConfig {
131 131
 	return &deployapi.DeploymentConfig{
132 132
 		TypeMeta: kapi.TypeMeta{ID: "test-deploy-config"},
133 133
 		Triggers: []deployapi.DeploymentTriggerPolicy{
... ...
@@ -163,6 +203,19 @@ func initialConfig() *deployapi.DeploymentConfig {
163 163
 	}
164 164
 }
165 165
 
166
+func newConfigWithTrigger() *deployapi.DeploymentConfig {
167
+	config := existingConfigWithTrigger()
168
+	config.LatestVersion = 0
169
+	return config
170
+}
171
+
172
+func newConfigWithoutTrigger() *deployapi.DeploymentConfig {
173
+	config := existingConfigWithTrigger()
174
+	config.LatestVersion = 0
175
+	config.Triggers = []deployapi.DeploymentTriggerPolicy{}
176
+	return config
177
+}
178
+
166 179
 func diffedConfig() *deployapi.DeploymentConfig {
167 180
 	return &deployapi.DeploymentConfig{
168 181
 		TypeMeta: kapi.TypeMeta{ID: "test-deploy-config"},
... ...
@@ -199,7 +252,7 @@ func diffedConfig() *deployapi.DeploymentConfig {
199 199
 	}
200 200
 }
201 201
 
202
-func generatedConfig() *deployapi.DeploymentConfig {
202
+func generatedExistingConfig() *deployapi.DeploymentConfig {
203 203
 	return &deployapi.DeploymentConfig{
204 204
 		TypeMeta: kapi.TypeMeta{ID: "test-deploy-config"},
205 205
 		Triggers: []deployapi.DeploymentTriggerPolicy{
... ...
@@ -235,6 +288,12 @@ func generatedConfig() *deployapi.DeploymentConfig {
235 235
 	}
236 236
 }
237 237
 
238
+func generatedConfig() *deployapi.DeploymentConfig {
239
+	config := generatedExistingConfig()
240
+	config.LatestVersion = 0
241
+	return config
242
+}
243
+
238 244
 func matchingInitialDeployment() *deployapi.Deployment {
239 245
 	return &deployapi.Deployment{
240 246
 		TypeMeta: kapi.TypeMeta{ID: "test-deploy-config-1"},
... ...
@@ -154,15 +154,6 @@ func TestSimpleConfigChangeTrigger(t *testing.T) {
154 154
 		t.Fatalf("Couldn't create DeploymentConfig: %v", err)
155 155
 	}
156 156
 
157
-	// submit the initial generated config, which will cause an initial deployment
158
-	if config, err = openshift.Client.GenerateDeploymentConfig(ctx, config.ID); err != nil {
159
-		t.Fatalf("Error generating config: %v", err)
160
-	}
161
-
162
-	if _, err := openshift.Client.UpdateDeploymentConfig(ctx, config); err != nil {
163
-		t.Fatalf("Couldn't create updated DeploymentConfig: %v", err)
164
-	}
165
-
166 157
 	// verify the initial deployment exists
167 158
 	event := <-watch.ResultChan()
168 159
 	deployment := event.Object.(*deployapi.Deployment)