// addDeploymentConfig is used for making sure that new deployment configs with triggers pointing // to existing images will be deployed as soon as they are created. func (c *ImageChangeController) addDeploymentConfig(obj interface{}) { dc := obj.(*deployapi.DeploymentConfig) for _, stream := range c.streamLister.GetStreamsForConfig(dc) { glog.V(4).Infof("Reconciling stream %q for config %q\n", imageapi.LabelForStream(stream), deployutil.LabelForDeploymentConfig(dc)) c.enqueueImageStream(stream) } }
// updateDeploymentConfig is used for making sure that deployment configs with new triggers // pointing to existing images will be deployed as soon as they are updated. func (c *ImageChangeController) updateDeploymentConfig(old, cur interface{}) { // A periodic relist will send update events for all known configs. newDc := cur.(*deployapi.DeploymentConfig) oldDc := old.(*deployapi.DeploymentConfig) if newDc.ResourceVersion == oldDc.ResourceVersion { return } for _, stream := range c.streamLister.GetStreamsForConfig(newDc) { glog.V(4).Infof("Reconciling stream %q for config %q\n", imageapi.LabelForStream(stream), deployutil.LabelForDeploymentConfig(newDc)) c.enqueueImageStream(stream) } }
// Handle processes image change triggers associated with imagestream. func (c *ImageChangeController) Handle(stream *imageapi.ImageStream) error { configs, err := c.dcLister.GetConfigsForImageStream(stream) if err != nil { return fmt.Errorf("couldn't get list of deployment configs while handling image stream %q: %v", imageapi.LabelForStream(stream), err) } // Find any configs which should be updated based on the new image state var configsToUpdate []*deployapi.DeploymentConfig for n, config := range configs { glog.V(4).Infof("Detecting image changes for deployment config %q", deployutil.LabelForDeploymentConfig(config)) hasImageChange := false for j := range config.Spec.Triggers { // because config can be copied during this loop, make sure we load from config for subsequent loops trigger := config.Spec.Triggers[j] params := trigger.ImageChangeParams // Only automatic image change triggers should fire if trigger.Type != deployapi.DeploymentTriggerOnImageChange { continue } // All initial deployments should have their images resolved in order to // be able to work and not try to pull non-existent images from DockerHub. // Deployments with automatic set to false that have been deployed at least // once shouldn't have their images updated. if (!params.Automatic || config.Spec.Paused) && len(params.LastTriggeredImage) > 0 { continue } // Check if the image stream matches the trigger if !triggerMatchesImage(config, params, stream) { continue } _, tag, ok := imageapi.SplitImageStreamTag(params.From.Name) if !ok { glog.Warningf("Invalid image stream tag %q in %q", params.From.Name, deployutil.LabelForDeploymentConfig(config)) continue } // Find the latest tag event for the trigger tag latestEvent := imageapi.LatestTaggedImage(stream, tag) if latestEvent == nil { glog.V(5).Infof("Couldn't find latest tag event for tag %q in image stream %q", tag, imageapi.LabelForStream(stream)) continue } // Ensure a change occurred if len(latestEvent.DockerImageReference) == 0 || latestEvent.DockerImageReference == params.LastTriggeredImage { glog.V(4).Infof("No image changes for deployment config %q were detected", deployutil.LabelForDeploymentConfig(config)) continue } names := sets.NewString(params.ContainerNames...) for i := range config.Spec.Template.Spec.Containers { container := &config.Spec.Template.Spec.Containers[i] if !names.Has(container.Name) { continue } if !hasImageChange { // create a copy prior to mutation result, err := deployutil.DeploymentConfigDeepCopy(configs[n]) if err != nil { utilruntime.HandleError(err) continue } configs[n] = result container = &configs[n].Spec.Template.Spec.Containers[i] params = configs[n].Spec.Triggers[j].ImageChangeParams } // Update the image container.Image = latestEvent.DockerImageReference // Log the last triggered image ID params.LastTriggeredImage = latestEvent.DockerImageReference hasImageChange = true } } if hasImageChange { configsToUpdate = append(configsToUpdate, configs[n]) } } // Attempt to regenerate all configs which may contain image updates anyFailed := false for _, config := range configsToUpdate { if _, err := c.dn.DeploymentConfigs(config.Namespace).Update(config); err != nil { utilruntime.HandleError(err) anyFailed = true } else { glog.V(4).Infof("Updated deployment config %q for trigger on image stream %q", deployutil.LabelForDeploymentConfig(config), imageapi.LabelForStream(stream)) } } if anyFailed { return fmt.Errorf("couldn't update some deployment configs for trigger on image stream %q", imageapi.LabelForStream(stream)) } return nil }
// Handle processes image change triggers associated with imagestream. func (c *ImageChangeController) Handle(stream *imageapi.ImageStream) error { configs, err := c.listDeploymentConfigs() if err != nil { return fmt.Errorf("couldn't get list of deployment configs while handling image stream %q: %v", imageapi.LabelForStream(stream), err) } // Find any configs which should be updated based on the new image state configsToUpdate := []*deployapi.DeploymentConfig{} for _, config := range configs { glog.V(4).Infof("Detecting image changes for deployment config %q", deployutil.LabelForDeploymentConfig(config)) hasImageChange := false for _, trigger := range config.Spec.Triggers { params := trigger.ImageChangeParams // Only automatic image change triggers should fire if trigger.Type != deployapi.DeploymentTriggerOnImageChange { continue } // All initial deployments (latestVersion == 0) should have their images resolved in order // to be able to work and not try to pull non-existent images from DockerHub. // Deployments with automatic set to false that have been deployed at least once (latestVersion > 0) // shouldn't have their images updated. if !params.Automatic && config.Status.LatestVersion != 0 { continue } // Check if the image stream matches the trigger if !triggerMatchesImage(config, params, stream) { continue } _, tag, ok := imageapi.SplitImageStreamTag(params.From.Name) if !ok { glog.Warningf("Invalid image stream tag %q in %q", params.From.Name, deployutil.LabelForDeploymentConfig(config)) continue } // Find the latest tag event for the trigger tag latestEvent := imageapi.LatestTaggedImage(stream, tag) if latestEvent == nil { glog.V(5).Infof("Couldn't find latest tag event for tag %q in image stream %q", tag, imageapi.LabelForStream(stream)) continue } // Ensure a change occurred if len(latestEvent.DockerImageReference) == 0 || latestEvent.DockerImageReference == params.LastTriggeredImage { glog.V(4).Infof("No image changes for deployment config %q were detected", deployutil.LabelForDeploymentConfig(config)) continue } names := sets.NewString(params.ContainerNames...) for i := range config.Spec.Template.Spec.Containers { container := &config.Spec.Template.Spec.Containers[i] if !names.Has(container.Name) { continue } // Update the image container.Image = latestEvent.DockerImageReference // Log the last triggered image ID params.LastTriggeredImage = latestEvent.DockerImageReference hasImageChange = true } } if hasImageChange { configsToUpdate = append(configsToUpdate, config) } } // Attempt to regenerate all configs which may contain image updates anyFailed := false for _, config := range configsToUpdate { if _, err := c.client.DeploymentConfigs(config.Namespace).Update(config); err != nil { anyFailed = true glog.V(2).Infof("Couldn't update deployment config %q: %v", deployutil.LabelForDeploymentConfig(config), err) } } if anyFailed { return fatalError(fmt.Sprintf("couldn't update some deployment configs for trigger on image stream %q", imageapi.LabelForStream(stream))) } glog.V(5).Infof("Updated all deployment configs for trigger on image stream %q", imageapi.LabelForStream(stream)) return nil }