Skip to content

Commit 69d5e20

Browse files
committed
Never deploy unspecified images in DC
1 parent 588b87f commit 69d5e20

File tree

1 file changed

+67
-35
lines changed

1 file changed

+67
-35
lines changed

pkg/apps/controller/deploymentconfig/deploymentconfig_controller.go

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func (c *DeploymentConfigController) Handle(config *appsapi.DeploymentConfig) er
8787
glog.V(5).Infof("Reconciling %s/%s", config.Namespace, config.Name)
8888
// There's nothing to reconcile until the version is nonzero.
8989
if appsutil.IsInitialDeployment(config) && !appsutil.HasTrigger(config) {
90-
return c.updateStatus(config, []*v1.ReplicationController{})
90+
return c.updateStatus(config, []*v1.ReplicationController{}, true)
9191
}
9292

9393
// List all ReplicationControllers to find also those we own but that no longer match our selector.
@@ -118,7 +118,7 @@ func (c *DeploymentConfigController) Handle(config *appsapi.DeploymentConfig) er
118118
// the latest available information. Some deletions make take some time to complete so there
119119
// is value in doing this.
120120
if config.DeletionTimestamp != nil {
121-
return c.updateStatus(config, existingDeployments)
121+
return c.updateStatus(config, existingDeployments, true)
122122
}
123123

124124
latestExists, latestDeployment := appsutil.LatestDeploymentInfo(config, existingDeployments)
@@ -128,28 +128,44 @@ func (c *DeploymentConfigController) Handle(config *appsapi.DeploymentConfig) er
128128
return err
129129
}
130130
}
131+
132+
// Never deploy with invalid or unresolved images
133+
for i, container := range config.Spec.Template.Spec.Containers {
134+
if strings.TrimSpace(container.Image) == "" {
135+
glog.V(4).Infof("Postponing rollout #%d for DeploymentConfig %s/%s because of invalid or unresolved image for container #%d (name=%s)", config.Status.LatestVersion, config.Namespace, config.Name, i, container.Name)
136+
return c.updateStatus(config, existingDeployments, true)
137+
}
138+
}
139+
131140
configCopy := config.DeepCopy()
132141
// Process triggers and start an initial rollouts
133-
shouldTrigger, shouldSkip := triggerActivated(configCopy, latestExists, latestDeployment, c.codec)
134-
if !shouldSkip && shouldTrigger {
135-
configCopy.Status.LatestVersion++
136-
return c.updateStatus(configCopy, existingDeployments)
142+
shouldTrigger, shouldSkip, err := triggerActivated(configCopy, latestExists, latestDeployment, c.codec)
143+
if err != nil {
144+
return fmt.Errorf("triggerActivated failed: %v", err)
137145
}
138-
// Have to wait for the image trigger to get the image before proceeding.
139-
if shouldSkip && appsutil.IsInitialDeployment(config) {
140-
return c.updateStatus(configCopy, existingDeployments)
146+
147+
if shouldSkip {
148+
return c.updateStatus(configCopy, existingDeployments, true)
149+
}
150+
151+
if shouldTrigger {
152+
configCopy.Status.LatestVersion++
153+
_, err := c.dn.DeploymentConfigs(configCopy.Namespace).UpdateStatus(configCopy)
154+
return err
141155
}
156+
142157
// If the latest deployment already exists, reconcile existing deployments
143158
// and return early.
144159
if latestExists {
145160
// If the latest deployment is still running, try again later. We don't
146161
// want to compete with the deployer.
147162
if !appsutil.IsTerminatedDeployment(latestDeployment) {
148-
return c.updateStatus(config, existingDeployments)
163+
return c.updateStatus(config, existingDeployments, false)
149164
}
150165

151166
return c.reconcileDeployments(existingDeployments, config, cm)
152167
}
168+
153169
// If the config is paused we shouldn't create new deployments for it.
154170
if config.Spec.Paused {
155171
// in order for revision history limit cleanup to work for paused
@@ -158,8 +174,18 @@ func (c *DeploymentConfigController) Handle(config *appsapi.DeploymentConfig) er
158174
c.recorder.Eventf(config, v1.EventTypeWarning, "DeploymentCleanupFailed", "Couldn't clean up deployments: %v", err)
159175
}
160176

161-
return c.updateStatus(config, existingDeployments)
177+
return c.updateStatus(config, existingDeployments, true)
178+
}
179+
180+
// Never deploy with invalid or unresolved images
181+
for i, container := range config.Spec.Template.Spec.Containers {
182+
if strings.TrimSpace(container.Image) == "" {
183+
glog.V(4).Infof("Postponing rollout #%d for DeploymentConfig %s/%s because of invalid or unresolved image for container #%d (name=%s)", config.Status.LatestVersion, config.Namespace, config.Name, i, container.Name)
184+
return c.updateStatus(config, existingDeployments, true)
185+
}
162186
}
187+
188+
163189
// No deployments are running and the latest deployment doesn't exist, so
164190
// create the new deployment.
165191
deployment, err := appsutil.MakeDeploymentV1(config, c.codec)
@@ -182,17 +208,17 @@ func (c *DeploymentConfigController) Handle(config *appsapi.DeploymentConfig) er
182208
if isOurs {
183209
// If the deployment was already created, just move on. The cache could be
184210
// stale, or another process could have already handled this update.
185-
return c.updateStatus(config, existingDeployments)
211+
return c.updateStatus(config, existingDeployments, true)
186212
} else {
187-
err = fmt.Errorf("replication controller %s already exists and deployment config is not allowed to claim it.", deployment.Name)
213+
err = fmt.Errorf("replication controller %s already exists and deployment config is not allowed to claim it", deployment.Name)
188214
c.recorder.Eventf(config, v1.EventTypeWarning, "DeploymentCreationFailed", "Couldn't deploy version %d: %v", config.Status.LatestVersion, err)
189-
return c.updateStatus(config, existingDeployments)
215+
return c.updateStatus(config, existingDeployments, true)
190216
}
191217
}
192218
c.recorder.Eventf(config, v1.EventTypeWarning, "DeploymentCreationFailed", "Couldn't deploy version %d: %s", config.Status.LatestVersion, err)
193219
// We don't care about this error since we need to report the create failure.
194220
cond := appsutil.NewDeploymentCondition(appsapi.DeploymentProgressing, kapi.ConditionFalse, appsapi.FailedRcCreateReason, err.Error())
195-
_ = c.updateStatus(config, existingDeployments, *cond)
221+
_ = c.updateStatus(config, existingDeployments, true, *cond)
196222
return fmt.Errorf("couldn't create deployment for deployment config %s: %v", appsutil.LabelForDeploymentConfig(config), err)
197223
}
198224
msg := fmt.Sprintf("Created new replication controller %q for version %d", created.Name, config.Status.LatestVersion)
@@ -206,7 +232,7 @@ func (c *DeploymentConfigController) Handle(config *appsapi.DeploymentConfig) er
206232
}
207233

208234
cond := appsutil.NewDeploymentCondition(appsapi.DeploymentProgressing, kapi.ConditionTrue, appsapi.NewReplicationControllerReason, msg)
209-
return c.updateStatus(config, existingDeployments, *cond)
235+
return c.updateStatus(config, existingDeployments, true, *cond)
210236
}
211237

212238
// reconcileDeployments reconciles existing deployment replica counts which
@@ -285,13 +311,13 @@ func (c *DeploymentConfigController) reconcileDeployments(existingDeployments []
285311
c.recorder.Eventf(config, v1.EventTypeWarning, "ReplicationControllerCleanupFailed", "Couldn't clean up replication controllers: %v", err)
286312
}
287313

288-
return c.updateStatus(config, updatedDeployments)
314+
return c.updateStatus(config, updatedDeployments, true)
289315
}
290316

291317
// Update the status of the provided deployment config. Additional conditions will override any other condition in the
292318
// deployment config status.
293-
func (c *DeploymentConfigController) updateStatus(config *appsapi.DeploymentConfig, deployments []*v1.ReplicationController, additional ...appsapi.DeploymentCondition) error {
294-
newStatus := calculateStatus(config, deployments, additional...)
319+
func (c *DeploymentConfigController) updateStatus(config *appsapi.DeploymentConfig, deployments []*v1.ReplicationController, updateObservedGeneration bool, additional ...appsapi.DeploymentCondition) error {
320+
newStatus := calculateStatus(config, deployments, updateObservedGeneration, additional...)
295321

296322
// NOTE: We should update the status of the deployment config only if we need to, otherwise
297323
// we hotloop between updates.
@@ -376,7 +402,7 @@ func (c *DeploymentConfigController) cancelRunningRollouts(config *appsapi.Deplo
376402
return nil
377403
}
378404

379-
func calculateStatus(config *appsapi.DeploymentConfig, rcs []*v1.ReplicationController, additional ...appsapi.DeploymentCondition) appsapi.DeploymentConfigStatus {
405+
func calculateStatus(config *appsapi.DeploymentConfig, rcs []*v1.ReplicationController, updateObservedGeneration bool, additional ...appsapi.DeploymentCondition) appsapi.DeploymentConfigStatus {
380406
// UpdatedReplicas represents the replicas that use the current deployment config template which means
381407
// we should inform about the replicas of the latest deployment and not the active.
382408
latestReplicas := int32(0)
@@ -394,10 +420,17 @@ func calculateStatus(config *appsapi.DeploymentConfig, rcs []*v1.ReplicationCont
394420
unavailableReplicas = 0
395421
}
396422

423+
var generation int64
424+
if updateObservedGeneration {
425+
generation = config.Generation
426+
} else {
427+
generation = config.Status.ObservedGeneration
428+
}
429+
397430
status := appsapi.DeploymentConfigStatus{
398431
LatestVersion: config.Status.LatestVersion,
399432
Details: config.Status.Details,
400-
ObservedGeneration: config.Generation,
433+
ObservedGeneration: generation,
401434
Replicas: appsutil.GetStatusReplicaCountForDeployments(rcs),
402435
UpdatedReplicas: latestReplicas,
403436
AvailableReplicas: available,
@@ -519,17 +552,17 @@ func (c *DeploymentConfigController) cleanupOldDeployments(existingDeployments [
519552
// triggers were activated (config change or image change). The first bool indicates that
520553
// the triggers are active and second indicates if we should skip the rollout because we
521554
// are waiting for the trigger to complete update (waiting for image for example).
522-
func triggerActivated(config *appsapi.DeploymentConfig, latestExists bool, latestDeployment *v1.ReplicationController, codec runtime.Codec) (bool, bool) {
555+
func triggerActivated(config *appsapi.DeploymentConfig, latestExists bool, latestDeployment *v1.ReplicationController, codec runtime.Codec) (bool, bool, error) {
523556
if config.Spec.Paused {
524-
return false, false
557+
return false, false, nil
525558
}
526559
imageTrigger := appsutil.HasImageChangeTrigger(config)
527560
configTrigger := appsutil.HasChangeTrigger(config)
528561
hasTrigger := imageTrigger || configTrigger
529562

530563
// no-op when no triggers are defined.
531564
if !hasTrigger {
532-
return false, false
565+
return false, false, nil
533566
}
534567

535568
// Handle initial rollouts
@@ -542,50 +575,49 @@ func triggerActivated(config *appsapi.DeploymentConfig, latestExists bool, lates
542575
// TODO: Technically this is not a config change cause, but we will have to report the image that caused the trigger.
543576
// In some cases it might be difficult because config can have multiple ICT.
544577
appsutil.RecordConfigChangeCause(config)
545-
return true, false
578+
return true, false, nil
546579
}
547580
glog.V(4).Infof("Rolling out initial deployment for %s/%s deferred until its images are ready", config.Namespace, config.Name)
548-
return false, true
581+
return false, true, nil
549582
}
550583
// Rollout if we only have config change trigger.
551584
if configTrigger {
552585
glog.V(4).Infof("Rolling out initial deployment for %s/%s", config.Namespace, config.Name)
553586
appsutil.RecordConfigChangeCause(config)
554-
return true, false
587+
return true, false, nil
555588
}
556589
// We are waiting for the initial RC to be created.
557-
return false, false
590+
return false, false, nil
558591
}
559592

560593
// Wait for the RC to be created
561594
if !latestExists {
562-
return false, true
595+
return false, false, nil
563596
}
564597

565598
// We need existing deployment at this point to compare its template with current config template.
566599
if latestDeployment == nil {
567-
return false, false
600+
return false, false, nil
568601
}
569602

570603
if imageTrigger {
571604
if ok, imageNames := appsutil.HasUpdatedImages(config, latestDeployment); ok {
572605
glog.V(4).Infof("Rolling out #%d deployment for %s/%s caused by image changes (%s)", config.Status.LatestVersion+1, config.Namespace, config.Name, strings.Join(imageNames, ","))
573606
appsutil.RecordImageChangeCauses(config, imageNames)
574-
return true, false
607+
return true, false, nil
575608
}
576609
}
577610

578611
if configTrigger {
579612
isLatest, changes, err := appsutil.HasLatestPodTemplate(config, latestDeployment, codec)
580613
if err != nil {
581-
glog.Errorf("Error while checking for latest pod template in replication controller: %v", err)
582-
return false, true
614+
return false, false, fmt.Errorf("error while checking for latest pod template in replication controller: %v", err)
583615
}
584616
if !isLatest {
585617
glog.V(4).Infof("Rolling out #%d deployment for %s/%s caused by config change, diff: %s", config.Status.LatestVersion+1, config.Namespace, config.Name, changes)
586618
appsutil.RecordConfigChangeCause(config)
587-
return true, false
619+
return true, false, nil
588620
}
589621
}
590-
return false, false
622+
return false, false, nil
591623
}

0 commit comments

Comments
 (0)