Example #1
0
// Handle processes change triggers for config.
func (c *DeploymentConfigChangeController) Handle(config *deployapi.DeploymentConfig) error {
	if !deployutil.HasChangeTrigger(config) {
		glog.V(5).Infof("Ignoring deployment config %q; no change triggers detected", deployutil.LabelForDeploymentConfig(config))
		return nil
	}

	// Try to decode this deployment config from the encoded annotation found in
	// its latest deployment.
	decoded, err := c.decodeFromLatest(config)
	if err != nil {
		return err
	}

	// If this is the initial deployment, then wait for any images that need to be resolved, otherwise
	// automatically start a new deployment.
	if config.Status.LatestVersion == 0 {
		canTrigger, causes := canTrigger(config, decoded)
		if !canTrigger {
			// If we cannot trigger then we need to wait for the image change controller.
			glog.V(5).Infof("Ignoring deployment config %q; template image needs to be resolved by the image change controller", deployutil.LabelForDeploymentConfig(config))
			return nil
		}
		return c.updateStatus(config, causes)
	}

	// If this is not the initial deployment, check if there is any template difference between
	// this and the decoded deploymentconfig.
	if kapi.Semantic.DeepEqual(config.Spec.Template, decoded.Spec.Template) {
		return nil
	}

	_, causes := canTrigger(config, decoded)
	return c.updateStatus(config, causes)
}
Example #2
0
// Handle processes change triggers for config.
func (c *DeploymentConfigChangeController) Handle(config *deployapi.DeploymentConfig) error {
	if !deployutil.HasChangeTrigger(config) {
		glog.V(5).Infof("Ignoring DeploymentConfig %s; no change triggers detected", deployutil.LabelForDeploymentConfig(config))
		return nil
	}

	if config.Status.LatestVersion == 0 {
		_, _, abort, err := c.generateDeployment(config)
		if err != nil {
			if kerrors.IsConflict(err) {
				return fatalError(fmt.Sprintf("DeploymentConfig %s updated since retrieval; aborting trigger: %v", deployutil.LabelForDeploymentConfig(config), err))
			}
			glog.V(4).Infof("Couldn't create initial deployment for deploymentConfig %q: %v", deployutil.LabelForDeploymentConfig(config), err)
			return nil
		}
		if !abort {
			glog.V(4).Infof("Created initial deployment for deploymentConfig %q", deployutil.LabelForDeploymentConfig(config))
		}
		return nil
	}

	latestDeploymentName := deployutil.LatestDeploymentNameForConfig(config)
	deployment, err := c.changeStrategy.getDeployment(config.Namespace, latestDeploymentName)
	if err != nil {
		// If there's no deployment for the latest config, we have no basis of
		// comparison. It's the responsibility of the deployment config controller
		// to make the deployment for the config, so return early.
		if kerrors.IsNotFound(err) {
			glog.V(5).Infof("Ignoring change for DeploymentConfig %s; no existing Deployment found", deployutil.LabelForDeploymentConfig(config))
			return nil
		}
		return fmt.Errorf("couldn't retrieve Deployment for DeploymentConfig %s: %v", deployutil.LabelForDeploymentConfig(config), err)
	}

	deployedConfig, err := c.decodeConfig(deployment)
	if err != nil {
		return fatalError(fmt.Sprintf("error decoding DeploymentConfig from Deployment %s for DeploymentConfig %s: %v", deployutil.LabelForDeployment(deployment), deployutil.LabelForDeploymentConfig(config), err))
	}

	// Detect template diffs, and return early if there aren't any changes.
	if kapi.Semantic.DeepEqual(config.Spec.Template, deployedConfig.Spec.Template) {
		glog.V(5).Infof("Ignoring DeploymentConfig change for %s (latestVersion=%d); same as Deployment %s", deployutil.LabelForDeploymentConfig(config), config.Status.LatestVersion, deployutil.LabelForDeployment(deployment))
		return nil
	}

	// There was a template diff, so generate a new config version.
	fromVersion, toVersion, abort, err := c.generateDeployment(config)
	if err != nil {
		if kerrors.IsConflict(err) {
			return fatalError(fmt.Sprintf("DeploymentConfig %s updated since retrieval; aborting trigger: %v", deployutil.LabelForDeploymentConfig(config), err))
		}
		return fmt.Errorf("couldn't generate deployment for DeploymentConfig %s: %v", deployutil.LabelForDeploymentConfig(config), err)
	}
	if !abort {
		glog.V(4).Infof("Updated DeploymentConfig %s from version %d to %d for existing deployment %s", deployutil.LabelForDeploymentConfig(config), fromVersion, toVersion, deployutil.LabelForDeployment(deployment))
	}
	return nil
}
Example #3
0
// canTrigger determines if we can trigger a new deployment for config based on the various deployment triggers.
func canTrigger(
	config *deployapi.DeploymentConfig,
	rn kclient.ReplicationControllersNamespacer,
	decoder runtime.Decoder,
	force bool,
) (bool, []deployapi.DeploymentCause, error) {

	decoded, err := decodeFromLatestDeployment(config, rn, decoder)
	if err != nil {
		return false, nil, err
	}

	ictCount, resolved, canTriggerByImageChange := 0, 0, false
	var causes []deployapi.DeploymentCause

	for _, t := range config.Spec.Triggers {
		if t.Type != deployapi.DeploymentTriggerOnImageChange {
			continue
		}
		ictCount++

		// If the image is yet to be resolved then we cannot process this trigger.
		lastTriggered := t.ImageChangeParams.LastTriggeredImage
		if len(lastTriggered) == 0 {
			continue
		}
		resolved++

		// Non-automatic triggers should not be able to trigger deployments.
		if !t.ImageChangeParams.Automatic {
			continue
		}

		// We need stronger checks in order to validate that this template
		// change is an image change. Look at the deserialized config's
		// triggers and compare with the present trigger. Initial deployments
		// should always trigger - there is no previous config to use for the
		// comparison.
		if config.Status.LatestVersion > 0 && !triggeredByDifferentImage(*t.ImageChangeParams, *decoded) {
			continue
		}

		canTriggerByImageChange = true
		causes = append(causes, deployapi.DeploymentCause{
			Type: deployapi.DeploymentTriggerOnImageChange,
			ImageTrigger: &deployapi.DeploymentCauseImageTrigger{
				From: kapi.ObjectReference{
					Name:      t.ImageChangeParams.From.Name,
					Namespace: t.ImageChangeParams.From.Namespace,
					Kind:      "ImageStreamTag",
				},
			},
		})
	}

	if ictCount != resolved {
		err = errors.NewBadRequest(fmt.Sprintf("cannot trigger a deployment for %q because it contains unresolved images", config.Name))
		return false, nil, err
	}

	if force {
		return true, []deployapi.DeploymentCause{{Type: deployapi.DeploymentTriggerManual}}, nil
	}

	canTriggerByConfigChange := false
	if deployutil.HasChangeTrigger(config) && // Our deployment config has a config change trigger
		len(causes) == 0 && // and no other trigger has triggered.
		(config.Status.LatestVersion == 0 || // Either it's the initial deployment
			!kapi.Semantic.DeepEqual(config.Spec.Template, decoded.Spec.Template)) /* or a config change happened so we need to trigger */ {

		canTriggerByConfigChange = true
		causes = []deployapi.DeploymentCause{{Type: deployapi.DeploymentTriggerOnConfigChange}}
	}

	return canTriggerByConfigChange || canTriggerByImageChange, causes, nil
}
Example #4
0
// canTrigger is used by the trigger controller to determine if the provided config can trigger
// a deployment.
//
// Image change triggers are processed first. It is required for all of them to point to images
// that exist. Otherwise, this controller will wait for the images to land and be updated in the
// triggers that point to them by the image change controller.
//
// Config change triggers are processed last. If all images are resolved and an automatic trigger
// was updated, then it should be possible to trigger a new deployment without a config change
// trigger. Otherwise, if a config change trigger exists and the config is not deployed yet or it
// has a podtemplate change, then the controller should trigger a new deployment (assuming all
// image change triggers can trigger).
func canTrigger(config, decoded *deployapi.DeploymentConfig) (bool, []deployapi.DeploymentCause) {
	if decoded == nil {
		// The decoded deployment config will never be nil here but a sanity check
		// never hurts.
		return false, nil
	}
	ictCount, resolved, canTriggerByImageChange := 0, 0, false
	var causes []deployapi.DeploymentCause

	// IMAGE CHANGE TRIGGERS
	for _, t := range config.Spec.Triggers {
		if t.Type != deployapi.DeploymentTriggerOnImageChange {
			continue
		}
		ictCount++

		// If this is the initial deployment then we need to wait for the image change controller
		// to resolve the image inside the pod template.
		lastTriggered := t.ImageChangeParams.LastTriggeredImage
		if len(lastTriggered) == 0 {
			continue
		}
		resolved++

		// Non-automatic triggers should not be able to trigger deployments.
		if !t.ImageChangeParams.Automatic {
			continue
		}

		// We need stronger checks in order to validate that this template
		// change is an image change. Look at the deserialized config's
		// triggers and compare with the present trigger. Initial deployments
		// should always trigger since there is no previous config to compare to.
		if config.Status.LatestVersion > 0 {
			if !triggeredByDifferentImage(*t.ImageChangeParams, *decoded) {
				continue
			}
		}

		canTriggerByImageChange = true
		causes = append(causes, deployapi.DeploymentCause{
			Type: deployapi.DeploymentTriggerOnImageChange,
			ImageTrigger: &deployapi.DeploymentCauseImageTrigger{
				From: kapi.ObjectReference{
					Name:      t.ImageChangeParams.From.Name,
					Namespace: t.ImageChangeParams.From.Namespace,
					Kind:      "ImageStreamTag",
				},
			},
		})
	}

	// We need to wait for all images to resolve before triggering a new deployment.
	if ictCount != resolved {
		return false, nil
	}

	// CONFIG CHANGE TRIGGERS
	canTriggerByConfigChange := false
	// Our deployment config has a config change trigger and no image change has triggered.
	// If an image change had happened, it would be enough to start a new deployment without
	// caring about the config change trigger.
	if deployutil.HasChangeTrigger(config) && !canTriggerByImageChange {
		// This is the initial deployment or the config has a template change. We need to
		// kick a new deployment.
		if config.Status.LatestVersion == 0 || !kapi.Semantic.DeepEqual(config.Spec.Template, decoded.Spec.Template) {
			canTriggerByConfigChange = true
			causes = []deployapi.DeploymentCause{{Type: deployapi.DeploymentTriggerOnConfigChange}}
		}
	}

	return canTriggerByConfigChange || canTriggerByImageChange, causes
}