Browse code

configchange: abort update once an image change is detected

kargakis authored on 2016/02/23 02:45:03
Showing 3 changed files
... ...
@@ -26,6 +26,24 @@ func OkDeploymentConfigStatus(version int) deployapi.DeploymentConfigStatus {
26 26
 	}
27 27
 }
28 28
 
29
+func OkImageChangeDetails() *deployapi.DeploymentDetails {
30
+	return &deployapi.DeploymentDetails{
31
+		Causes: []*deployapi.DeploymentCause{{
32
+			Type: deployapi.DeploymentTriggerOnImageChange,
33
+			ImageTrigger: &deployapi.DeploymentCauseImageTrigger{
34
+				From: kapi.ObjectReference{
35
+					Name: imageapi.JoinImageStreamTag("test", "tag"),
36
+					Kind: "ImageStreamTag",
37
+				}}}}}
38
+}
39
+
40
+func OkConfigChangeDetails() *deployapi.DeploymentDetails {
41
+	return &deployapi.DeploymentDetails{
42
+		Causes: []*deployapi.DeploymentCause{{
43
+			Type: deployapi.DeploymentTriggerOnConfigChange,
44
+		}}}
45
+}
46
+
29 47
 func OkStrategy() deployapi.DeploymentStrategy {
30 48
 	return deployapi.DeploymentStrategy{
31 49
 		Type: deployapi.DeploymentStrategyTypeRecreate,
... ...
@@ -39,7 +39,7 @@ func (c *DeploymentConfigChangeController) Handle(config *deployapi.DeploymentCo
39 39
 	}
40 40
 
41 41
 	if config.Status.LatestVersion == 0 {
42
-		_, _, err := c.generateDeployment(config)
42
+		_, _, abort, err := c.generateDeployment(config)
43 43
 		if err != nil {
44 44
 			if kerrors.IsConflict(err) {
45 45
 				return fatalError(fmt.Sprintf("DeploymentConfig %s updated since retrieval; aborting trigger: %v", deployutil.LabelForDeploymentConfig(config), err))
... ...
@@ -47,7 +47,9 @@ func (c *DeploymentConfigChangeController) Handle(config *deployapi.DeploymentCo
47 47
 			glog.V(4).Infof("Couldn't create initial deployment for deploymentConfig %q: %v", deployutil.LabelForDeploymentConfig(config), err)
48 48
 			return nil
49 49
 		}
50
-		glog.V(4).Infof("Created initial deployment for deploymentConfig %q", deployutil.LabelForDeploymentConfig(config))
50
+		if !abort {
51
+			glog.V(4).Infof("Created initial deployment for deploymentConfig %q", deployutil.LabelForDeploymentConfig(config))
52
+		}
51 53
 		return nil
52 54
 	}
53 55
 
... ...
@@ -76,21 +78,31 @@ func (c *DeploymentConfigChangeController) Handle(config *deployapi.DeploymentCo
76 76
 	}
77 77
 
78 78
 	// There was a template diff, so generate a new config version.
79
-	fromVersion, toVersion, err := c.generateDeployment(config)
79
+	fromVersion, toVersion, abort, err := c.generateDeployment(config)
80 80
 	if err != nil {
81 81
 		if kerrors.IsConflict(err) {
82 82
 			return fatalError(fmt.Sprintf("DeploymentConfig %s updated since retrieval; aborting trigger: %v", deployutil.LabelForDeploymentConfig(config), err))
83 83
 		}
84 84
 		return fmt.Errorf("couldn't generate deployment for DeploymentConfig %s: %v", deployutil.LabelForDeploymentConfig(config), err)
85 85
 	}
86
-	glog.V(4).Infof("Updated DeploymentConfig %s from version %d to %d for existing deployment %s", deployutil.LabelForDeploymentConfig(config), fromVersion, toVersion, deployutil.LabelForDeployment(deployment))
86
+	if !abort {
87
+		glog.V(4).Infof("Updated DeploymentConfig %s from version %d to %d for existing deployment %s", deployutil.LabelForDeploymentConfig(config), fromVersion, toVersion, deployutil.LabelForDeployment(deployment))
88
+	}
87 89
 	return nil
88 90
 }
89 91
 
90
-func (c *DeploymentConfigChangeController) generateDeployment(config *deployapi.DeploymentConfig) (int, int, error) {
92
+func (c *DeploymentConfigChangeController) generateDeployment(config *deployapi.DeploymentConfig) (int, int, bool, error) {
91 93
 	newConfig, err := c.changeStrategy.generateDeploymentConfig(config.Namespace, config.Name)
92 94
 	if err != nil {
93
-		return config.Status.LatestVersion, 0, err
95
+		return -1, -1, false, err
96
+	}
97
+
98
+	// The generator returns a cause only when there is an image change. If the configchange
99
+	// controller detects an image change, it should just quit, otherwise it is racing with
100
+	// the imagechange controller.
101
+	if newConfig.Status.LatestVersion != config.Status.LatestVersion &&
102
+		newConfig.Status.Details != nil && len(newConfig.Status.Details.Causes) > 0 {
103
+		return -1, -1, true, nil
94 104
 	}
95 105
 
96 106
 	if newConfig.Status.LatestVersion == config.Status.LatestVersion {
... ...
@@ -112,10 +124,10 @@ func (c *DeploymentConfigChangeController) generateDeployment(config *deployapi.
112 112
 	// current config will be captured in future events.
113 113
 	updatedConfig, err := c.changeStrategy.updateDeploymentConfig(config.Namespace, newConfig)
114 114
 	if err != nil {
115
-		return config.Status.LatestVersion, newConfig.Status.LatestVersion, err
115
+		return config.Status.LatestVersion, newConfig.Status.LatestVersion, false, err
116 116
 	}
117 117
 
118
-	return config.Status.LatestVersion, updatedConfig.Status.LatestVersion, nil
118
+	return config.Status.LatestVersion, updatedConfig.Status.LatestVersion, false, nil
119 119
 }
120 120
 
121 121
 // changeStrategy knows how to generate and update DeploymentConfigs.
... ...
@@ -170,3 +170,45 @@ func TestHandle_changeWithTemplateDiff(t *testing.T) {
170 170
 		}
171 171
 	}
172 172
 }
173
+
174
+func TestHandle_raceWithTheImageController(t *testing.T) {
175
+	var updated *deployapi.DeploymentConfig
176
+
177
+	controller := &DeploymentConfigChangeController{
178
+		decodeConfig: func(deployment *kapi.ReplicationController) (*deployapi.DeploymentConfig, error) {
179
+			return deployutil.DecodeDeploymentConfig(deployment, kapi.Codecs.LegacyCodec(deployapi.SchemeGroupVersion))
180
+		},
181
+		changeStrategy: &changeStrategyImpl{
182
+			generateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
183
+				generated := deployapitest.OkDeploymentConfig(1)
184
+				generated.Status.Details = deployapitest.OkImageChangeDetails()
185
+				updated = generated
186
+				return generated, nil
187
+			},
188
+			updateDeploymentConfigFunc: func(namespace string, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
189
+				t.Errorf("an update should never run in the presence of races")
190
+				updated.Status.Details = deployapitest.OkConfigChangeDetails()
191
+				return updated, nil
192
+			},
193
+		},
194
+	}
195
+
196
+	config := deployapitest.OkDeploymentConfig(0)
197
+	config.Spec.Triggers = []deployapi.DeploymentTriggerPolicy{deployapitest.OkConfigChangeTrigger(), deployapitest.OkImageChangeTrigger()}
198
+
199
+	if err := controller.Handle(config); err != nil {
200
+		t.Fatalf("unexpected error: %v", err)
201
+	}
202
+
203
+	if e, a := 1, updated.Status.LatestVersion; e != a {
204
+		t.Fatalf("expected update to latestversion=%d, got %d", e, a)
205
+	}
206
+
207
+	if updated.Status.Details == nil {
208
+		t.Fatalf("expected config change details to be set")
209
+	} else if updated.Status.Details.Causes == nil {
210
+		t.Fatalf("expected config change causes to be set")
211
+	} else if updated.Status.Details.Causes[0].Type != deployapi.DeploymentTriggerOnImageChange {
212
+		t.Fatalf("expected config change cause to be set to image change trigger, got %s", updated.Status.Details.Causes[0].Type)
213
+	}
214
+}