Example #1
0
// ValidateBuildConfig tests required fields for a Build.
func ValidateBuildConfig(config *buildapi.BuildConfig) field.ErrorList {
	allErrs := field.ErrorList{}
	allErrs = append(allErrs, validation.ValidateObjectMeta(&config.ObjectMeta, true, validation.NameIsDNSSubdomain, field.NewPath("metadata"))...)

	// image change triggers that refer
	fromRefs := map[string]struct{}{}
	specPath := field.NewPath("spec")
	triggersPath := specPath.Child("triggers")
	buildFrom := buildutil.GetInputReference(config.Spec.Strategy)
	for i, trg := range config.Spec.Triggers {
		allErrs = append(allErrs, validateTrigger(&trg, buildFrom, triggersPath.Index(i))...)
		if trg.Type != buildapi.ImageChangeBuildTriggerType || trg.ImageChange == nil {
			continue
		}
		from := trg.ImageChange.From
		if from == nil {
			from = buildFrom
		}
		fromKey := refKey(config.Namespace, from)
		_, exists := fromRefs[fromKey]
		if exists {
			allErrs = append(allErrs, field.Invalid(triggersPath, config.Spec.Triggers, "multiple ImageChange triggers refer to the same image stream tag"))
		}
		fromRefs[fromKey] = struct{}{}
	}

	allErrs = append(allErrs, validateBuildSpec(&config.Spec.BuildSpec, specPath)...)

	return allErrs
}
Example #2
0
// findImageChangeTrigger finds an image change trigger that has a from that matches the passed in ref
// if no match is found but there is an image change trigger with a null from, that trigger is returned
func findImageChangeTrigger(bc *buildapi.BuildConfig, ref *kapi.ObjectReference) *buildapi.ImageChangeTrigger {
	if ref == nil {
		return nil
	}
	for _, trigger := range bc.Spec.Triggers {
		if trigger.Type != buildapi.ImageChangeBuildTriggerType {
			continue
		}
		imageChange := trigger.ImageChange
		triggerRef := imageChange.From
		if triggerRef == nil {
			triggerRef = buildutil.GetInputReference(bc.Spec.Strategy)
			if triggerRef == nil || triggerRef.Kind != "ImageStreamTag" {
				continue
			}
		}
		triggerNs := triggerRef.Namespace
		if triggerNs == "" {
			triggerNs = bc.Namespace
		}
		refNs := ref.Namespace
		if refNs == "" {
			refNs = bc.Namespace
		}
		if triggerRef.Name == ref.Name && triggerNs == refNs {
			return imageChange
		}
	}
	return nil
}
Example #3
0
// ImageStreamReferenceIndexFunc is a default index function that indexes based on image stream references.
func ImageStreamReferenceIndexFunc(obj interface{}) ([]string, error) {
	switch t := obj.(type) {
	case *buildapi.BuildConfig:
		var keys []string
		for _, trigger := range t.Spec.Triggers {
			if trigger.Type != buildapi.ImageChangeBuildTriggerType {
				continue
			}
			from := trigger.ImageChange.From

			// We're indexing on the imagestream referenced by the imagechangetrigger,
			// however buildconfigs allow one "default" imagechangetrigger in which
			// the ICT does not explicitly indicate the image it is triggering on,
			// instead it triggers on the image being used as the builder/base image
			// as referenced in the build strategy, so if this is an ICT w/ no
			// explicit image reference, use the image referenced by the strategy
			// because this is the default ICT.
			if from == nil {
				from = buildutil.GetInputReference(t.Spec.Strategy)
				if from == nil || from.Kind != "ImageStreamTag" {
					continue
				}
			}
			name, _, _ := imageapi.SplitImageStreamTag(from.Name)
			namespace := from.Namespace

			// if the imagestream reference has no namespace, use the
			// namespace of the buildconfig.
			if len(namespace) == 0 {
				namespace = t.Namespace
			}
			keys = append(keys, namespace+"/"+name)
		}

		if len(keys) == 0 {
			// Return an empty key for configs that don't hold object references.
			keys = append(keys, "")
		}
		return keys, nil
	case *deployapi.DeploymentConfig:
		var keys []string
		for _, trigger := range t.Spec.Triggers {
			if trigger.Type != deployapi.DeploymentTriggerOnImageChange {
				continue
			}
			params := trigger.ImageChangeParams
			name, _, _ := imageapi.SplitImageStreamTag(params.From.Name)
			keys = append(keys, params.From.Namespace+"/"+name)
		}

		if len(keys) == 0 {
			// Return an empty key for configs that don't hold object references.
			keys = append(keys, "")
		}

		return keys, nil
	}
	return nil, fmt.Errorf("image stream reference index not implemented for %#v", obj)
}
Example #4
0
// AddInputEdges links the build config to its input image and source nodes.
func AddInputEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) {
	if in := buildgraph.EnsureSourceRepositoryNode(g, node.BuildConfig.Spec.Source); in != nil {
		g.AddEdge(in, node, BuildInputEdgeKind)
	}
	inputImage := buildutil.GetInputReference(node.BuildConfig.Spec.Strategy)
	if input := imageRefNode(g, inputImage, node.BuildConfig); input != nil {
		g.AddEdge(input, node, BuildInputImageEdgeKind)
	}
}
Example #5
0
// checkCircularReferences ensures there are no builds that can trigger themselves
// due to an imagechangetrigger that matches the output destination of the image.
// objects is a list of api objects produced by new-app.
func (c *AppConfig) checkCircularReferences(objects app.Objects) error {
	for i, obj := range objects {

		if glog.V(5) {
			json, _ := json.MarshalIndent(obj, "", "\t")
			glog.Infof("\n\nCycle check input object %v:\n%v\n", i, string(json))
		}

		if bc, ok := obj.(*buildapi.BuildConfig); ok {
			input := buildutil.GetInputReference(bc.Spec.Strategy)
			output := bc.Spec.Output.To

			if output == nil || input == nil {
				return nil
			}

			dockerInput, err := c.followRefToDockerImage(input, nil, objects)
			if err != nil {
				glog.Warningf("Unable to check for circular build input: %v", err)
				return nil
			}
			glog.V(5).Infof("Post follow input:\n%#v\n", dockerInput)

			dockerOutput, err := c.followRefToDockerImage(output, nil, objects)
			if err != nil {
				glog.Warningf("Unable to check for circular build output: %v", err)
				return nil
			}
			glog.V(5).Infof("Post follow:\n%#v\n", dockerOutput)

			if dockerInput != nil && dockerOutput != nil {
				if reflect.DeepEqual(dockerInput, dockerOutput) {
					return app.CircularOutputReferenceError{Reference: fmt.Sprintf("%s", dockerInput.Name)}
				}
			}

			// If it is not possible to follow input and output out to DockerImages,
			// it is likely they are referencing newly created ImageStreams. Just
			// make sure they are not the same image stream.
			inCopy := *input
			outCopy := *output
			for _, ref := range []*kapi.ObjectReference{&inCopy, &outCopy} {
				// Some code paths add namespace and others don't. Make things
				// consistent.
				if len(ref.Namespace) == 0 {
					ref.Namespace = c.OriginNamespace
				}
			}

			if reflect.DeepEqual(inCopy, outCopy) {
				return app.CircularOutputReferenceError{Reference: fmt.Sprintf("%s/%s", inCopy.Namespace, inCopy.Name)}
			}
		}
	}
	return nil
}
Example #6
0
// strategyTrigger returns a synthetic ImageChangeTrigger that represents the image stream tag the build strategy
// points to, or nil if no such strategy trigger is possible (if the build doesn't point to an ImageStreamTag).
func strategyTrigger(config *buildapi.BuildConfig) *ImageChangeTrigger {
	if from := buildutil.GetInputReference(config.Spec.Strategy); from != nil {
		if from.Kind == "ImageStreamTag" {
			// normalize the strategy object reference
			from.Namespace = defaultNamespace(from.Namespace, config.Namespace)
			return &ImageChangeTrigger{From: from.Name, Namespace: from.Namespace}
		}
	}
	return nil
}
Example #7
0
// AddTriggerEdges links the build config to its trigger input image nodes.
func AddTriggerEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) {
	for _, trigger := range node.BuildConfig.Spec.Triggers {
		if trigger.Type != buildapi.ImageChangeBuildTriggerType {
			continue
		}
		from := trigger.ImageChange.From
		if trigger.ImageChange.From == nil {
			from = buildutil.GetInputReference(node.BuildConfig.Spec.Strategy)
		}
		triggerNode := imageRefNode(g, from, node.BuildConfig)
		g.AddEdge(triggerNode, node, BuildTriggerImageEdgeKind)
	}
}
Example #8
0
// checkCircularReferences ensures there are no builds that can trigger themselves
// due to an imagechangetrigger that matches the output destination of the image.
// objects is a list of api objects produced by new-app.
func (c *AppConfig) checkCircularReferences(objects app.Objects) error {
	for _, obj := range objects {
		if bc, ok := obj.(*buildapi.BuildConfig); ok {
			input := buildutil.GetInputReference(bc.Spec.Strategy)
			if bc.Spec.Output.To != nil && input != nil &&
				reflect.DeepEqual(input, bc.Spec.Output.To) {
				ns := input.Namespace
				if len(ns) == 0 {
					ns = c.OriginNamespace
				}
				return app.CircularOutputReferenceError{Reference: fmt.Sprintf("%s/%s", ns, input.Name)}
			}
		}
	}
	return nil
}
Example #9
0
// updateImageTriggers sets the LastTriggeredImageID on all the ImageChangeTriggers on the BuildConfig and
// updates the From reference of the strategy if the strategy uses an ImageStream or ImageStreamTag reference
func (g *BuildGenerator) updateImageTriggers(ctx kapi.Context, bc *buildapi.BuildConfig, from, triggeredBy *kapi.ObjectReference) error {
	var requestTrigger *buildapi.ImageChangeTrigger
	if from != nil {
		requestTrigger = findImageChangeTrigger(bc, from)
	}
	if requestTrigger != nil && triggeredBy != nil && requestTrigger.LastTriggeredImageID == triggeredBy.Name {
		glog.V(2).Infof("Aborting imageid triggered build for BuildConfig %s/%s with imageid %s because the BuildConfig already matches this imageid", bc.Namespace, bc.Name, triggeredBy.Name)
		return fmt.Errorf("build config %s/%s has already instantiated a build for imageid %s", bc.Namespace, bc.Name, triggeredBy.Name)
	}
	// Update last triggered image id for all image change triggers
	for _, trigger := range bc.Spec.Triggers {
		if trigger.Type != buildapi.ImageChangeBuildTriggerType {
			continue
		}
		// Use the requested image id for the trigger that caused the build, otherwise resolve to the latest
		if triggeredBy != nil && trigger.ImageChange == requestTrigger {
			trigger.ImageChange.LastTriggeredImageID = triggeredBy.Name
			continue
		}

		triggerImageRef := trigger.ImageChange.From
		if triggerImageRef == nil {
			triggerImageRef = buildutil.GetInputReference(bc.Spec.Strategy)
		}
		if triggerImageRef == nil {
			glog.Warningf("Could not get ImageStream reference for default ImageChangeTrigger on BuildConfig %s/%s", bc.Namespace, bc.Name)
			continue
		}
		image, err := g.resolveImageStreamReference(ctx, *triggerImageRef, bc.Namespace)
		if err != nil {
			// If the trigger is for the strategy from ref, return an error
			if trigger.ImageChange.From == nil {
				return err
			}
			// Otherwise, warn that an error occurred, but continue
			glog.Warningf("Could not resolve trigger reference for build config %s/%s: %#v", bc.Namespace, bc.Name, triggerImageRef)
		}
		trigger.ImageChange.LastTriggeredImageID = image
	}
	return nil
}
Example #10
0
// addBuildStrategyImageReferencesToGraph ads references from the build strategy's parent node to the image
// the build strategy references.
//
// Edges are added to the graph from each predecessor (build or build config)
// to the image specified by strategy.from, as long as the image is managed by
// OpenShift.
func addBuildStrategyImageReferencesToGraph(g graph.Graph, strategy buildapi.BuildStrategy, predecessor gonum.Node) {
	from := buildutil.GetInputReference(strategy)
	if from == nil {
		glog.V(4).Infof("Unable to determine 'from' reference - skipping")
		return
	}

	glog.V(4).Infof("Examining build strategy with from: %#v", from)

	var imageID string

	switch from.Kind {
	case "ImageStreamImage":
		_, id, err := imagestreamimage.ParseNameAndID(from.Name)
		if err != nil {
			glog.V(2).Infof("Error parsing ImageStreamImage name %q: %v - skipping", from.Name, err)
			return
		}
		imageID = id
	case "DockerImage":
		ref, err := imageapi.ParseDockerImageReference(from.Name)
		if err != nil {
			glog.V(2).Infof("Error parsing DockerImage name %q: %v - skipping", from.Name, err)
			return
		}
		imageID = ref.ID
	default:
		return
	}

	glog.V(4).Infof("Looking for image %q in graph", imageID)
	imageNode := imagegraph.FindImage(g, imageID)
	if imageNode == nil {
		glog.V(4).Infof("Unable to find image %q in graph - skipping", imageID)
		return
	}

	glog.V(4).Infof("Adding edge from %v to %v", predecessor, imageNode)
	g.AddEdge(predecessor, imageNode, ReferencedImageEdgeKind)
}
Example #11
0
// setBuildSourceImage set BuildSource Image item for new build
func (g *BuildGenerator) setBuildSourceImage(ctx kapi.Context, builderSecrets []kapi.Secret, bcCopy *buildapi.BuildConfig, Source *buildapi.BuildSource) error {
	var err error

	strategyImageChangeTrigger := getStrategyImageChangeTrigger(bcCopy)
	for i, sourceImage := range Source.Images {
		if sourceImage.PullSecret == nil {
			sourceImage.PullSecret = g.resolveImageSecret(ctx, builderSecrets, &sourceImage.From, bcCopy.Namespace)
		}

		var sourceImageSpec string
		// if the imagesource matches the strategy from, and we have a trigger for the strategy from,
		// use the imageid from the trigger rather than resolving it.
		if strategyFrom := buildutil.GetInputReference(bcCopy.Spec.Strategy); strategyFrom != nil &&
			reflect.DeepEqual(sourceImage.From, *strategyFrom) &&
			strategyImageChangeTrigger != nil {
			sourceImageSpec = strategyImageChangeTrigger.LastTriggeredImageID
		} else {
			refImageChangeTrigger := getImageChangeTriggerForRef(bcCopy, &sourceImage.From)
			// if there is no trigger associated with this imagesource, resolve the imagesource reference now.
			// otherwise use the imageid from the imagesource trigger.
			if refImageChangeTrigger == nil {
				sourceImageSpec, err = g.resolveImageStreamReference(ctx, sourceImage.From, bcCopy.Namespace)
				if err != nil {
					return err
				}
			} else {
				sourceImageSpec = refImageChangeTrigger.LastTriggeredImageID
			}
		}

		sourceImage.From.Kind = "DockerImage"
		sourceImage.From.Name = sourceImageSpec
		sourceImage.From.Namespace = ""
		Source.Images[i] = sourceImage
	}

	return nil
}
Example #12
0
func Convert_v1_BuildConfig_To_api_BuildConfig(in *BuildConfig, out *newer.BuildConfig, s conversion.Scope) error {
	if err := autoConvert_v1_BuildConfig_To_api_BuildConfig(in, out, s); err != nil {
		return err
	}

	newTriggers := []newer.BuildTriggerPolicy{}
	// strip off any default imagechange triggers where the buildconfig's
	// "from" is not an ImageStreamTag, because those triggers
	// will never be invoked.
	imageRef := buildutil.GetInputReference(out.Spec.Strategy)
	hasIST := imageRef != nil && imageRef.Kind == "ImageStreamTag"
	for _, trigger := range out.Spec.Triggers {
		if trigger.Type != newer.ImageChangeBuildTriggerType {
			newTriggers = append(newTriggers, trigger)
			continue
		}
		if (trigger.ImageChange == nil || trigger.ImageChange.From == nil) && !hasIST {
			continue
		}
		newTriggers = append(newTriggers, trigger)
	}
	out.Spec.Triggers = newTriggers
	return nil
}
Example #13
0
// HandleImageStream processes the next ImageStream event.
func (c *ImageChangeController) HandleImageStream(stream *imageapi.ImageStream) error {
	glog.V(4).Infof("Build image change controller detected ImageStream change %s", stream.Status.DockerImageRepository)

	// Loop through all build configurations and record if there was an error
	// instead of breaking the loop. The error will be returned in the end, so the
	// retry controller can retry. Any BuildConfigs that were processed successfully
	// should have had their LastTriggeredImageID updated, so the retry should result
	// in a no-op for them.
	hasError := false

	bcs, err := c.BuildConfigIndex.GetConfigsForImageStreamTrigger(stream.Namespace, stream.Name)
	if err != nil {
		return err
	}
	for _, config := range bcs {
		var (
			from           *kapi.ObjectReference
			shouldBuild    = false
			triggeredImage = ""
			latest         *imageapi.TagEvent
		)
		// For every ImageChange trigger find the latest tagged image from the image stream and
		// invoke a build using that image id. A new build is triggered only if the latest tagged image id or pull spec
		// differs from the last triggered build recorded on the build config for that trigger
		for _, trigger := range config.Spec.Triggers {
			if trigger.Type != buildapi.ImageChangeBuildTriggerType {
				continue
			}
			if trigger.ImageChange.From != nil {
				from = trigger.ImageChange.From
			} else {
				from = buildutil.GetInputReference(config.Spec.Strategy)
			}

			if from == nil || from.Kind != "ImageStreamTag" {
				continue
			}
			fromStreamName, tag, ok := imageapi.SplitImageStreamTag(from.Name)
			if !ok {
				glog.Errorf("Invalid image stream tag: %s in build config %s/%s", from.Name, config.Name, config.Namespace)
				continue
			}

			fromNamespace := from.Namespace
			if len(fromNamespace) == 0 {
				fromNamespace = config.Namespace
			}

			// only trigger a build if this image stream matches the name and namespace of the stream ref in the build trigger
			// also do not trigger if the imagerepo does not have a valid DockerImageRepository value for us to pull
			// the image from
			if len(stream.Status.DockerImageRepository) == 0 || fromStreamName != stream.Name || fromNamespace != stream.Namespace {
				continue
			}

			// This split is safe because ImageStreamTag names always have the form
			// name:tag.
			latest = imageapi.LatestTaggedImage(stream, tag)
			if latest == nil {
				glog.V(4).Infof("unable to find tagged image: no image recorded for %s/%s:%s", stream.Namespace, stream.Name, tag)
				continue
			}
			glog.V(4).Infof("Found ImageStream %s/%s with tag %s", stream.Namespace, stream.Name, tag)

			// (must be different) to trigger a build
			last := trigger.ImageChange.LastTriggeredImageID
			next := latest.DockerImageReference

			if len(last) == 0 || (len(next) > 0 && next != last) {
				triggeredImage = next
				shouldBuild = true
				// it doesn't really make sense to have multiple image change triggers any more,
				// so just exit the loop now
				break
			}
		}

		if shouldBuild {
			glog.V(4).Infof("Running build for BuildConfig %s/%s", config.Namespace, config.Name)
			buildCauses := []buildapi.BuildTriggerCause{}
			// instantiate new build
			request := &buildapi.BuildRequest{
				ObjectMeta: kapi.ObjectMeta{
					Name:      config.Name,
					Namespace: config.Namespace,
				},
				TriggeredBy: append(buildCauses,
					buildapi.BuildTriggerCause{
						Message: "Image change",
						ImageChangeBuild: &buildapi.ImageChangeCause{
							ImageID: latest.DockerImageReference,
							FromRef: from,
						},
					},
				),
				TriggeredByImage: &kapi.ObjectReference{
					Kind: "DockerImage",
					Name: triggeredImage,
				},
				From: from,
			}
			if _, err := c.BuildConfigInstantiator.Instantiate(config.Namespace, request); err != nil {
				if kerrors.IsConflict(err) {
					utilruntime.HandleError(fmt.Errorf("unable to instantiate Build for BuildConfig %s/%s due to a conflicting update: %v", config.Namespace, config.Name, err))
				} else {
					utilruntime.HandleError(fmt.Errorf("error instantiating Build from BuildConfig %s/%s: %v", config.Namespace, config.Name, err))
				}
				hasError = true
				continue
			}
		}
	}
	if hasError {
		return fmt.Errorf("an error occurred processing 1 or more build configurations; the image change trigger for image stream %s will be retried", stream.Status.DockerImageRepository)
	}
	return nil
}
Example #14
0
// generateBuildFromConfig generates a build definition based on the current imageid
// from any ImageStream that is associated to the BuildConfig by From reference in
// the Strategy, or uses the Image field of the Strategy. If binary is provided, override
// the current build strategy with a binary artifact for this specific build.
// Takes a BuildConfig to base the build on, and an optional SourceRevision to build.
func (g *BuildGenerator) generateBuildFromConfig(ctx kapi.Context, bc *buildapi.BuildConfig, revision *buildapi.SourceRevision, binary *buildapi.BinaryBuildSource) (*buildapi.Build, error) {
	serviceAccount := bc.Spec.ServiceAccount
	if len(serviceAccount) == 0 {
		serviceAccount = g.DefaultServiceAccountName
	}
	if len(serviceAccount) == 0 {
		serviceAccount = bootstrappolicy.BuilderServiceAccountName
	}
	// Need to copy the buildConfig here so that it doesn't share pointers with
	// the build object which could be (will be) modified later.
	obj, _ := kapi.Scheme.Copy(bc)
	bcCopy := obj.(*buildapi.BuildConfig)
	build := &buildapi.Build{
		Spec: buildapi.BuildSpec{
			CommonSpec: buildapi.CommonSpec{
				ServiceAccount:            serviceAccount,
				Source:                    bcCopy.Spec.Source,
				Strategy:                  bcCopy.Spec.Strategy,
				Output:                    bcCopy.Spec.Output,
				Revision:                  revision,
				Resources:                 bcCopy.Spec.Resources,
				PostCommit:                bcCopy.Spec.PostCommit,
				CompletionDeadlineSeconds: bcCopy.Spec.CompletionDeadlineSeconds,
			},
		},
		ObjectMeta: kapi.ObjectMeta{
			Labels: bcCopy.Labels,
		},
		Status: buildapi.BuildStatus{
			Phase: buildapi.BuildPhaseNew,
			Config: &kapi.ObjectReference{
				Kind:      "BuildConfig",
				Name:      bc.Name,
				Namespace: bc.Namespace,
			},
		},
	}
	if binary != nil {
		build.Spec.Source.Git = nil
		build.Spec.Source.Binary = binary
		if build.Spec.Source.Dockerfile != nil && binary.AsFile == "Dockerfile" {
			build.Spec.Source.Dockerfile = nil
		}
	} else {
		// must explicitly set this because we copied the source values from the buildconfig.
		build.Spec.Source.Binary = nil
	}

	build.Name = getNextBuildName(bc)
	if build.Annotations == nil {
		build.Annotations = make(map[string]string)
	}
	build.Annotations[buildapi.BuildNumberAnnotation] = strconv.FormatInt(bc.Status.LastVersion, 10)
	build.Annotations[buildapi.BuildConfigAnnotation] = bcCopy.Name
	if build.Labels == nil {
		build.Labels = make(map[string]string)
	}
	build.Labels[buildapi.BuildConfigLabelDeprecated] = buildapi.LabelValue(bcCopy.Name)
	build.Labels[buildapi.BuildConfigLabel] = buildapi.LabelValue(bcCopy.Name)
	build.Labels[buildapi.BuildRunPolicyLabel] = string(bc.Spec.RunPolicy)

	builderSecrets, err := g.FetchServiceAccountSecrets(bc.Namespace, serviceAccount)
	if err != nil {
		return nil, err
	}
	if build.Spec.Output.PushSecret == nil {
		build.Spec.Output.PushSecret = g.resolveImageSecret(ctx, builderSecrets, build.Spec.Output.To, bc.Namespace)
	}
	strategyImageChangeTrigger := getStrategyImageChangeTrigger(bc)

	// Resolve image source if present
	for i, sourceImage := range build.Spec.Source.Images {
		if sourceImage.PullSecret == nil {
			sourceImage.PullSecret = g.resolveImageSecret(ctx, builderSecrets, &sourceImage.From, bc.Namespace)
		}

		var sourceImageSpec string
		// if the imagesource matches the strategy from, and we have a trigger for the strategy from,
		// use the imageid from the trigger rather than resolving it.
		if strategyFrom := buildutil.GetInputReference(bc.Spec.Strategy); reflect.DeepEqual(sourceImage.From, *strategyFrom) &&
			strategyImageChangeTrigger != nil {
			sourceImageSpec = strategyImageChangeTrigger.LastTriggeredImageID
		} else {
			refImageChangeTrigger := getImageChangeTriggerForRef(bc, &sourceImage.From)
			// if there is no trigger associated with this imagesource, resolve the imagesource reference now.
			// otherwise use the imageid from the imagesource trigger.
			if refImageChangeTrigger == nil {
				sourceImageSpec, err = g.resolveImageStreamReference(ctx, sourceImage.From, bc.Namespace)
				if err != nil {
					return nil, err
				}
			} else {
				sourceImageSpec = refImageChangeTrigger.LastTriggeredImageID
			}
		}

		sourceImage.From.Kind = "DockerImage"
		sourceImage.From.Name = sourceImageSpec
		sourceImage.From.Namespace = ""
		build.Spec.Source.Images[i] = sourceImage
	}

	// If the Build is using a From reference instead of a resolved image, we need to resolve that From
	// reference to a valid image so we can run the build.  Builds do not consume ImageStream references,
	// only image specs.
	var image string
	if strategyImageChangeTrigger != nil {
		image = strategyImageChangeTrigger.LastTriggeredImageID
	}

	switch {
	case build.Spec.Strategy.SourceStrategy != nil:
		if image == "" {
			image, err = g.resolveImageStreamReference(ctx, build.Spec.Strategy.SourceStrategy.From, build.Status.Config.Namespace)
			if err != nil {
				return nil, err
			}
		}
		build.Spec.Strategy.SourceStrategy.From = kapi.ObjectReference{
			Kind: "DockerImage",
			Name: image,
		}
		if build.Spec.Strategy.SourceStrategy.RuntimeImage != nil {
			runtimeImageName, err := g.resolveImageStreamReference(ctx, *build.Spec.Strategy.SourceStrategy.RuntimeImage, build.Status.Config.Namespace)
			if err != nil {
				return nil, err
			}
			build.Spec.Strategy.SourceStrategy.RuntimeImage = &kapi.ObjectReference{
				Kind: "DockerImage",
				Name: runtimeImageName,
			}
		}
		if build.Spec.Strategy.SourceStrategy.PullSecret == nil {
			// we have 3 different variations:
			// 1) builder and runtime images use the same secret => use builder image secret
			// 2) builder and runtime images use different secrets => use builder image secret
			// 3) builder doesn't need a secret but runtime image requires it => use runtime image secret
			// The case when both of the images don't use secret (equals to nil) is covered by the first variant.
			pullSecret := g.resolveImageSecret(ctx, builderSecrets, &build.Spec.Strategy.SourceStrategy.From, bc.Namespace)
			if pullSecret == nil {
				pullSecret = g.resolveImageSecret(ctx, builderSecrets, build.Spec.Strategy.SourceStrategy.RuntimeImage, bc.Namespace)
			}

			build.Spec.Strategy.SourceStrategy.PullSecret = pullSecret
		}
	case build.Spec.Strategy.DockerStrategy != nil &&
		build.Spec.Strategy.DockerStrategy.From != nil:
		if image == "" {
			image, err = g.resolveImageStreamReference(ctx, *build.Spec.Strategy.DockerStrategy.From, build.Status.Config.Namespace)
			if err != nil {
				return nil, err
			}
		}
		build.Spec.Strategy.DockerStrategy.From = &kapi.ObjectReference{
			Kind: "DockerImage",
			Name: image,
		}
		if build.Spec.Strategy.DockerStrategy.PullSecret == nil {
			build.Spec.Strategy.DockerStrategy.PullSecret = g.resolveImageSecret(ctx, builderSecrets, build.Spec.Strategy.DockerStrategy.From, bc.Namespace)
		}
	case build.Spec.Strategy.CustomStrategy != nil:
		if image == "" {
			image, err = g.resolveImageStreamReference(ctx, build.Spec.Strategy.CustomStrategy.From, build.Status.Config.Namespace)
			if err != nil {
				return nil, err
			}
		}
		build.Spec.Strategy.CustomStrategy.From = kapi.ObjectReference{
			Kind: "DockerImage",
			Name: image,
		}
		if build.Spec.Strategy.CustomStrategy.PullSecret == nil {
			build.Spec.Strategy.CustomStrategy.PullSecret = g.resolveImageSecret(ctx, builderSecrets, &build.Spec.Strategy.CustomStrategy.From, bc.Namespace)
		}
		updateCustomImageEnv(build.Spec.Strategy.CustomStrategy, image)
	}
	return build, nil
}
Example #15
0
func TestInstantiateWithImageTrigger(t *testing.T) {
	imageID := "the-image-id-12345"
	defaultTriggers := func() []buildapi.BuildTriggerPolicy {
		return []buildapi.BuildTriggerPolicy{
			{
				Type: buildapi.GenericWebHookBuildTriggerType,
			},
			{
				Type:        buildapi.ImageChangeBuildTriggerType,
				ImageChange: &buildapi.ImageChangeTrigger{},
			},
			{
				Type: buildapi.ImageChangeBuildTriggerType,
				ImageChange: &buildapi.ImageChangeTrigger{
					From: &kapi.ObjectReference{
						Name: "image1:tag1",
						Kind: "ImageStreamTag",
					},
				},
			},
			{
				Type: buildapi.ImageChangeBuildTriggerType,
				ImageChange: &buildapi.ImageChangeTrigger{
					From: &kapi.ObjectReference{
						Name:      "image2:tag2",
						Namespace: "image2ns",
						Kind:      "ImageStreamTag",
					},
				},
			},
		}
	}
	triggersWithImageID := func() []buildapi.BuildTriggerPolicy {
		triggers := defaultTriggers()
		triggers[2].ImageChange.LastTriggeredImageID = imageID
		return triggers
	}
	tests := []struct {
		name          string
		reqFrom       *kapi.ObjectReference
		triggerIndex  int // index of trigger that will be updated with the image id, if -1, no update expected
		triggers      []buildapi.BuildTriggerPolicy
		errorExpected bool
	}{
		{
			name: "default trigger",
			reqFrom: &kapi.ObjectReference{
				Kind: "ImageStreamTag",
				Name: "image3:tag3",
			},
			triggerIndex: 1,
			triggers:     defaultTriggers(),
		},
		{
			name: "trigger with from",
			reqFrom: &kapi.ObjectReference{
				Kind: "ImageStreamTag",
				Name: "image1:tag1",
			},
			triggerIndex: 2,
			triggers:     defaultTriggers(),
		},
		{
			name: "trigger with from and namespace",
			reqFrom: &kapi.ObjectReference{
				Kind:      "ImageStreamTag",
				Name:      "image2:tag2",
				Namespace: "image2ns",
			},
			triggerIndex: 3,
			triggers:     defaultTriggers(),
		},
		{
			name: "existing image id",
			reqFrom: &kapi.ObjectReference{
				Kind: "ImageStreamTag",
				Name: "image1:tag1",
			},
			triggers:      triggersWithImageID(),
			errorExpected: true,
		},
	}

	for _, tc := range tests {
		bc := &buildapi.BuildConfig{
			Spec: buildapi.BuildConfigSpec{
				BuildSpec: buildapi.BuildSpec{
					Strategy: buildapi.BuildStrategy{
						SourceStrategy: &buildapi.SourceBuildStrategy{
							From: kapi.ObjectReference{
								Name: "image3:tag3",
								Kind: "ImageStreamTag",
							},
						},
					},
				},
				Triggers: tc.triggers,
			},
		}
		generator := mockBuildGeneratorForInstantiate()
		client := generator.Client.(Client)
		client.GetBuildConfigFunc =
			func(ctx kapi.Context, name string) (*buildapi.BuildConfig, error) {
				return bc, nil
			}
		client.UpdateBuildConfigFunc =
			func(ctx kapi.Context, buildConfig *buildapi.BuildConfig) error {
				bc = buildConfig
				return nil
			}
		generator.Client = client

		req := &buildapi.BuildRequest{
			TriggeredByImage: &kapi.ObjectReference{
				Kind: "DockerImage",
				Name: imageID,
			},
			From: tc.reqFrom,
		}
		_, err := generator.Instantiate(kapi.NewDefaultContext(), req)
		if err != nil && !tc.errorExpected {
			t.Errorf("%s: unexpected error %v", tc.name, err)
			continue
		}
		if err == nil && tc.errorExpected {
			t.Errorf("%s: expected error but didn't get one", tc.name)
			continue
		}
		if tc.errorExpected {
			continue
		}
		for i := range bc.Spec.Triggers {
			if i == tc.triggerIndex {
				// Verify that the trigger got updated
				if bc.Spec.Triggers[i].ImageChange.LastTriggeredImageID != imageID {
					t.Errorf("%s: expeccted trigger at index %d to contain imageID %s", tc.name, i, imageID)
				}
				continue
			}
			// Ensure that other triggers are updated with the latest docker image ref
			if bc.Spec.Triggers[i].Type == buildapi.ImageChangeBuildTriggerType {
				from := bc.Spec.Triggers[i].ImageChange.From
				if from == nil {
					from = buildutil.GetInputReference(bc.Spec.Strategy)
				}
				if bc.Spec.Triggers[i].ImageChange.LastTriggeredImageID != ("ref@" + from.Name) {
					t.Errorf("%s: expected LastTriggeredImageID for trigger at %d to be %s. Got: %s", tc.name, i, "ref@"+from.Name, bc.Spec.Triggers[i].ImageChange.LastTriggeredImageID)
				}
			}
		}
	}
}