Exemplo n.º 1
0
// 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
}
Exemplo n.º 2
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.Triggers {
			glog.V(1).Infof("Scanning trigger %v", tr)
			from := buildutil.GetImageStreamForStrategy(cfg.Parameters.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
}
Exemplo n.º 3
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.Triggers {
			from := buildutil.GetImageStreamForStrategy(cfg.Parameters.Strategy)
			if from == nil || from.Kind != "ImageStreamTag" || tr.ImageChange == nil {
				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 {
				// Either To & Tag or DockerImageReference will be used as output
				if cfg.Parameters.Output.To != nil && cfg.Parameters.Output.To.Name != "" {
					childName = cfg.Parameters.Output.To.Name
					childTag = cfg.Parameters.Output.Tag
					if cfg.Parameters.Output.To.Namespace != "" {
						childNamespace = cfg.Parameters.Output.To.Namespace
					} else {
						childNamespace = cfg.Namespace
					}
				} else {
					ref, err := imageapi.ParseDockerImageReference(cfg.Parameters.Output.DockerImageReference)
					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)

		from := buildutil.GetImageStreamForStrategy(config.Parameters.Strategy)
		if from == nil || from.Kind != "ImageStreamTag" {
			continue
		}

		shouldBuild := false
		triggeredImage := ""
		// For every ImageChange trigger find the latest tagged image from the image repository and replace that value
		// throughout the build strategies. 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 _, trigger := range config.Triggers {
			if trigger.Type != buildapi.ImageChangeBuildTriggerType {
				continue
			}
			fromStreamName := getImageStreamNameFromReference(from)

			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.
			tag := strings.Split(from.Name, ":")[1]
			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,
				},
			}
			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
}