... | ... |
@@ -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 |
+} |