示例#1
0
// ValidateBuildConfig tests required fields for a Build.
func ValidateBuildConfig(config *buildapi.BuildConfig) fielderrors.ValidationErrorList {
	allErrs := fielderrors.ValidationErrorList{}
	allErrs = append(allErrs, validation.ValidateObjectMeta(&config.ObjectMeta, true, validation.NameIsDNSSubdomain).Prefix("metadata")...)

	// image change triggers that refer
	fromRefs := map[string]struct{}{}
	for i, trg := range config.Spec.Triggers {
		allErrs = append(allErrs, validateTrigger(&trg).PrefixIndex(i).Prefix("triggers")...)
		if trg.Type != buildapi.ImageChangeBuildTriggerType || trg.ImageChange == nil {
			continue
		}
		from := trg.ImageChange.From
		if from == nil {
			from = buildutil.GetImageStreamForStrategy(config.Spec.Strategy)
		}
		fromKey := refKey(config.Namespace, from)
		_, exists := fromRefs[fromKey]
		if exists {
			allErrs = append(allErrs, fielderrors.NewFieldInvalid("triggers", config.Spec.Triggers, "multiple ImageChange triggers refer to the same image stream tag"))
		}
		fromRefs[fromKey] = struct{}{}
	}

	allErrs = append(allErrs, validateBuildSpec(&config.Spec.BuildSpec).Prefix("spec")...)
	return allErrs
}
示例#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.GetImageStreamForStrategy(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
}
示例#3
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.GetImageStreamForStrategy(node.BuildConfig.Spec.Strategy)
	if input := imageRefNode(g, inputImage, node.BuildConfig); input != nil {
		g.AddEdge(input, node, BuildInputImageEdgeKind)
	}
}
示例#4
0
文件: types.go 项目: mignev/origin
// BuildConfig adds a graph node for the specific build config if it does not exist,
// and will link the build config to other nodes for the images and source repositories
// it depends on.
func BuildConfig(g MutableUniqueGraph, config *build.BuildConfig) graph.Node {
	node, found := g.FindOrCreate(
		UniqueName(fmt.Sprintf("%d|%s/%s", BuildConfigGraphKind, config.Namespace, config.Name)),
		func(node Node) graph.Node {
			return &BuildConfigNode{
				Node:        node,
				BuildConfig: config,
			}
		},
	)
	if found {
		return node
	}

	output := config.Parameters.Output
	to := output.To
	switch {
	case to != nil && len(to.Name) > 0:
		out := ImageStreamTag(g, defaultNamespace(to.Namespace, config.Namespace), to.Name, output.Tag)
		g.AddEdge(node, out, BuildOutputGraphEdgeKind)
	case len(output.DockerImageReference) > 0:
		out := DockerRepository(g, output.DockerImageReference, output.Tag)
		g.AddEdge(node, out, BuildOutputGraphEdgeKind)
	}

	if in, ok := SourceRepository(g, config.Parameters.Source); ok {
		g.AddEdge(in, node, BuildInputGraphEdgeKind)
	}

	from := buildutil.GetImageStreamForStrategy(config.Parameters.Strategy)
	if from != nil {
		switch from.Kind {
		case "DockerImage":
			if ref, err := image.ParseDockerImageReference(from.Name); err == nil {
				tag := ref.Tag
				ref.Tag = ""
				in := DockerRepository(g, ref.String(), tag)
				g.AddEdge(in, node, BuildInputImageGraphEdgeKind)
			}
		case "ImageStream":
			tag := image.DefaultImageTag
			in := ImageStreamTag(g, defaultNamespace(from.Namespace, config.Namespace), from.Name, tag)
			g.AddEdge(in, node, BuildInputImageGraphEdgeKind)
		case "ImageStreamTag":
			name, tag, _ := image.SplitImageStreamTag(from.Name)
			in := ImageStreamTag(g, defaultNamespace(from.Namespace, config.Namespace), name, tag)
			g.AddEdge(in, node, BuildInputImageGraphEdgeKind)
		case "ImageStreamImage":
			glog.V(4).Infof("Ignoring ImageStreamImage reference in BuildConfig %s/%s", config.Namespace, config.Name)
		}
	}
	return node
}
示例#5
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.GetImageStreamForStrategy(node.BuildConfig.Spec.Strategy)
		}
		triggerNode := imageRefNode(g, from, node.BuildConfig)
		g.AddEdge(triggerNode, node, BuildTriggerImageEdgeKind)
	}
}
示例#6
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.GetImageStreamForStrategy(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
}
示例#7
0
// getStreams iterates over a given set of build configurations
// and extracts all the image streams which trigger a
// build when the image stream is updated
func getStreams(configs []buildapi.BuildConfig) map[string][]string {
	glog.V(1).Infof("Scanning BuildConfigs")
	avoidDuplicates := make(map[string][]string)
	for _, cfg := range configs {
		glog.V(1).Infof("Scanning BuildConfigs %v", cfg)
		for _, tr := range cfg.Spec.Triggers {
			glog.V(1).Infof("Scanning trigger %v", tr)
			from := buildutil.GetImageStreamForStrategy(cfg.Spec.Strategy)
			glog.V(1).Infof("Strategy from= %v", from)
			if tr.ImageChange != nil && from != nil && from.Name != "" {
				glog.V(1).Infof("found ICT with from %s kind %s", from.Name, from.Kind)
				var name, tag string
				switch from.Kind {
				case "ImageStreamTag":
					bits := strings.Split(from.Name, ":")
					name = bits[0]
					tag = bits[1]
				default:
					// ImageStreamImage and DockerImage are never updated and so never
					// trigger builds.
					continue
				}
				var stream string
				switch from.Namespace {
				case "":
					stream = join(cfg.Namespace, name)
				default:
					stream = join(from.Namespace, name)
				}

				uniqueTag := true
				for _, prev := range avoidDuplicates[stream] {
					if prev == tag {
						uniqueTag = false
						break
					}
				}
				glog.V(1).Infof("checking unique tag %v %s", uniqueTag, tag)
				if uniqueTag {
					avoidDuplicates[stream] = append(avoidDuplicates[stream], tag)
				}
			}
		}
	}

	return avoidDuplicates
}
示例#8
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) {
	glog.V(4).Infof("Examining build strategy with type %q", strategy.Type)

	from := buildutil.GetImageStreamForStrategy(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)
}
示例#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.GetImageStreamForStrategy(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
}
示例#10
0
文件: edges.go 项目: lucasjo/origin
// AddInputOutputEdges links the build config to other nodes for the images and source repositories it depends on.
func AddInputOutputEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) *buildgraph.BuildConfigNode {
	output := node.BuildConfig.Spec.Output
	to := output.To
	switch {
	case to == nil:
	case to.Kind == "DockerImage":
		out := imagegraph.EnsureDockerRepositoryNode(g, to.Name, "")
		g.AddEdge(node, out, BuildOutputEdgeKind)
	case to.Kind == "ImageStreamTag":
		out := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta2(defaultNamespace(to.Namespace, node.BuildConfig.Namespace), to.Name))
		g.AddEdge(node, out, BuildOutputEdgeKind)
	}

	if in := buildgraph.EnsureSourceRepositoryNode(g, node.BuildConfig.Spec.Source); in != nil {
		g.AddEdge(in, node, BuildInputEdgeKind)
	}

	from := buildutil.GetImageStreamForStrategy(node.BuildConfig.Spec.Strategy)
	if from != nil {
		switch from.Kind {
		case "DockerImage":
			if ref, err := imageapi.ParseDockerImageReference(from.Name); err == nil {
				tag := ref.Tag
				ref.Tag = ""
				in := imagegraph.EnsureDockerRepositoryNode(g, ref.String(), tag)
				g.AddEdge(in, node, BuildInputImageEdgeKind)
			}
		case "ImageStream":
			in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), from.Name, imageapi.DefaultImageTag))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		case "ImageStreamTag":
			in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta2(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), from.Name))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		case "ImageStreamImage":
			in := imagegraph.FindOrCreateSyntheticImageStreamImageNode(g, imagegraph.MakeImageStreamImageObjectMeta(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), from.Name))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		}
	}
	return node
}
示例#11
0
// AddInputOutputEdges links the build config to other nodes for the images and source repositories it depends on.
func AddInputOutputEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) *buildgraph.BuildConfigNode {
	output := node.BuildConfig.Parameters.Output
	to := output.To
	switch {
	case to != nil && len(to.Name) > 0:
		out := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(to.Namespace, node.BuildConfig.Namespace), to.Name, output.Tag))
		g.AddEdge(node, out, BuildOutputEdgeKind)
	case len(output.DockerImageReference) > 0:
		out := imagegraph.EnsureDockerRepositoryNode(g, output.DockerImageReference, output.Tag)
		g.AddEdge(node, out, BuildOutputEdgeKind)
	}

	if in := buildgraph.EnsureSourceRepositoryNode(g, node.BuildConfig.Parameters.Source); in != nil {
		g.AddEdge(in, node, BuildInputEdgeKind)
	}

	from := buildutil.GetImageStreamForStrategy(node.BuildConfig.Parameters.Strategy)
	if from != nil {
		switch from.Kind {
		case "DockerImage":
			if ref, err := imageapi.ParseDockerImageReference(from.Name); err == nil {
				tag := ref.Tag
				ref.Tag = ""
				in := imagegraph.EnsureDockerRepositoryNode(g, ref.String(), tag)
				g.AddEdge(in, node, BuildInputImageEdgeKind)
			}
		case "ImageStream":
			tag := imageapi.DefaultImageTag
			in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), from.Name, tag))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		case "ImageStreamTag":
			name, tag, _ := imageapi.SplitImageStreamTag(from.Name)
			in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), name, tag))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		case "ImageStreamImage":
			glog.V(4).Infof("Ignoring ImageStreamImage reference in BuildConfig %s/%s", node.BuildConfig.Namespace, node.BuildConfig.Name)
		}
	}
	return node
}
示例#12
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")
	for i, trg := range config.Spec.Triggers {
		allErrs = append(allErrs, validateTrigger(&trg, triggersPath.Index(i))...)
		if trg.Type != buildapi.ImageChangeBuildTriggerType || trg.ImageChange == nil {
			continue
		}
		from := trg.ImageChange.From
		if from == nil {
			from = buildutil.GetImageStreamForStrategy(config.Spec.Strategy)
		}
		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)...)

	// validate ImageChangeTriggers of DockerStrategy builds
	strategy := config.Spec.BuildSpec.Strategy
	if strategy.DockerStrategy != nil && strategy.DockerStrategy.From == nil {
		for i, trigger := range config.Spec.Triggers {
			if trigger.Type == buildapi.ImageChangeBuildTriggerType && (trigger.ImageChange == nil || trigger.ImageChange.From == nil) {
				allErrs = append(allErrs, field.Required(triggersPath.Index(i).Child("imageChange", "from")))
			}
		}
	}

	return allErrs
}
示例#13
0
func convert_v1_BuildConfig_To_api_BuildConfig(in *BuildConfig, out *newer.BuildConfig, s conversion.Scope) error {
	if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); 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.GetImageStreamForStrategy(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
}
示例#14
0
文件: generator.go 项目: richm/origin
// 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{
			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
		}
	}

	build.Name = getNextBuildName(bc)
	if build.Annotations == nil {
		build.Annotations = make(map[string]string)
	}
	build.Annotations[buildapi.BuildNumberAnnotation] = strconv.Itoa(bc.Status.LastVersion)
	if build.Labels == nil {
		build.Labels = make(map[string]string)
	}
	build.Labels[buildapi.BuildConfigLabelDeprecated] = bcCopy.Name
	build.Labels[buildapi.BuildConfigLabel] = bcCopy.Name

	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.GetImageStreamForStrategy(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.PullSecret == nil {
			build.Spec.Strategy.SourceStrategy.PullSecret = g.resolveImageSecret(ctx, builderSecrets, &build.Spec.Strategy.SourceStrategy.From, bc.Namespace)
		}
	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
}
示例#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.GetImageStreamForStrategy(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)
				}
			}
		}
	}
}
示例#16
0
// findStreamDeps accepts an image stream and a list of build
// configurations and returns the dependency tree of the specified
// image stream
func findStreamDeps(stream, tag string, buildConfigList []buildapi.BuildConfig) (*Node, error) {
	root := &Node{
		FullName: stream,
		Tags:     []string{tag},
	}

	namespace, name, err := split(stream)
	if err != nil {
		return nil, err
	}

	// Search all build configurations in order to find the image
	// streams depending on the specified image stream
	var childNamespace, childName, childTag string
	for _, cfg := range buildConfigList {
		for _, tr := range cfg.Spec.Triggers {
			from := buildutil.GetImageStreamForStrategy(cfg.Spec.Strategy)
			if from == nil || from.Kind != "ImageStreamTag" || tr.ImageChange == nil {
				continue
			}
			if cfg.Spec.Output.To == nil || len(cfg.Spec.Output.To.Name) == 0 {
				// build has no output image, so the chain ends here.
				continue
			}
			// Setup zeroed fields to their default values
			if from.Namespace == "" {
				from.Namespace = cfg.Namespace
			}
			fromTag := strings.Split(from.Name, ":")[1]
			parentStream := namespace + "/" + name + ":" + tag
			if buildutil.NameFromImageStream("", from, fromTag) == parentStream {
				if cfg.Spec.Output.To.Kind == "ImageStreamTag" {
					bits := strings.Split(cfg.Spec.Output.To.Name, ":")
					if len(bits) != 2 {
						return nil, fmt.Errorf("Invalid ImageStreamTag %s/%s does not contain a :tag", namespace, cfg.Spec.Output.To.Name)
					}
					childName = bits[0]
					childTag = bits[1]
					if cfg.Spec.Output.To.Namespace != "" {
						childNamespace = cfg.Spec.Output.To.Namespace
					} else {
						childNamespace = cfg.Namespace
					}
				} else {
					ref, err := imageapi.ParseDockerImageReference(cfg.Spec.Output.To.Name)
					if err != nil {
						return nil, err
					}
					childName = ref.Name
					childTag = ref.Tag
					childNamespace = cfg.Namespace
				}

				childStream := join(childNamespace, childName)

				// Build all children and their dependency trees recursively
				child, err := findStreamDeps(childStream, childTag, buildConfigList)
				if err != nil {
					return nil, err
				}

				// Add edge between root and child
				cfgFullName := join(cfg.Namespace, cfg.Name)
				root.Edges = append(root.Edges, NewEdge(cfgFullName, child.FullName))

				// If the child depends on root via more than one tag, we have to make sure
				// that only one single instance of the child will make it into root.Children
				cont := false
				for _, stream := range root.Children {
					if stream.FullName == child.FullName {
						// Just pass the tag along and discard the current child
						stream.Tags = append(stream.Tags, child.Tags...)
						cont = true
						break
					}
				}
				if cont {
					// Do not append this child in root.Children. It's already in there
					continue
				}

				root.Children = append(root.Children, child)
			}
		}
	}
	return root, nil
}
// HandleImageRepo processes the next ImageStream event.
func (c *ImageChangeController) HandleImageRepo(repo *imageapi.ImageStream) error {
	glog.V(4).Infof("Build image change controller detected ImageStream change %s", repo.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

	// TODO: this is inefficient
	for _, bc := range c.BuildConfigStore.List() {
		config := bc.(*buildapi.BuildConfig)

		var (
			from           *kapi.ObjectReference
			shouldBuild    = false
			triggeredImage = ""
		)
		// For every ImageChange trigger find the latest tagged image from the image repository 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.GetImageStreamForStrategy(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 repo matches the name and namespace of the 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(repo.Status.DockerImageRepository) == 0 || fromStreamName != repo.Name || fromNamespace != repo.Namespace {
				continue
			}

			// This split is safe because ImageStreamTag names always have the form
			// name:tag.
			latest := imageapi.LatestTaggedImage(repo, tag)
			if latest == nil {
				glog.V(4).Infof("unable to find tagged image: no image recorded for %s/%s:%s", repo.Namespace, repo.Name, tag)
				continue
			}
			glog.V(4).Infof("Found ImageStream %s/%s with tag %s", repo.Namespace, repo.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)
			// instantiate new build
			request := &buildapi.BuildRequest{
				ObjectMeta: kapi.ObjectMeta{
					Name:      config.Name,
					Namespace: config.Namespace,
				},
				TriggeredByImage: &kapi.ObjectReference{
					Kind: "DockerImage",
					Name: triggeredImage,
				},
				From: from,
			}
			if _, err := c.BuildConfigInstantiator.Instantiate(config.Namespace, request); err != nil {
				if kerrors.IsConflict(err) {
					util.HandleError(fmt.Errorf("unable to instantiate Build for BuildConfig %s/%s due to a conflicting update: %v", config.Namespace, config.Name, err))
				} else {
					util.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", repo.Status.DockerImageRepository)
	}
	return nil
}