pkg/cmd/experimental/buildchain/buildchain.go
a2212c3f
 package buildchain
 
 import (
 	"fmt"
121567ee
 	"io"
8f014370
 	"strings"
a2212c3f
 
 	"github.com/golang/glog"
 	"github.com/spf13/cobra"
f638b86d
 	kapi "k8s.io/kubernetes/pkg/api"
49ad7ccd
 	"k8s.io/kubernetes/pkg/api/unversioned"
83c702b4
 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
3dd75654
 	"k8s.io/kubernetes/pkg/util/sets"
a2212c3f
 
121567ee
 	"github.com/openshift/origin/pkg/client"
 	"github.com/openshift/origin/pkg/cmd/cli/describe"
6267dded
 	"github.com/openshift/origin/pkg/cmd/templates"
e30dccdf
 	osutil "github.com/openshift/origin/pkg/cmd/util"
a2212c3f
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
 	imageapi "github.com/openshift/origin/pkg/image/api"
121567ee
 	imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
a2212c3f
 )
 
6267dded
 // BuildChainRecommendedCommandName is the recommended command name
 const BuildChainRecommendedCommandName = "build-chain"
c124965f
 
6267dded
 var (
 	buildChainLong = templates.LongDesc(`
 		Output the inputs and dependencies of your builds
a2212c3f
 
6267dded
 		Supported formats for the generated graph are dot and a human-readable output.
 		Tag and namespace are optional and if they are not specified, 'latest' and the
 		default namespace will be used respectively.`)
a2212c3f
 
6267dded
 	buildChainExample = templates.Examples(`
 		# Build the dependency tree for the 'latest' tag in <image-stream>
 	  %[1]s <image-stream>
a2212c3f
 
6267dded
 	  # Build the dependency tree for 'v2' tag in dot format and visualize it via the dot utility
 	  %[1]s <image-stream>:v2 -o dot | dot -T svg -o deps.svg
a2212c3f
 
6267dded
 	  # Build the dependency tree across all namespaces for the specified image stream tag found in 'test' namespace
 	  %[1]s <image-stream> -n test --all`)
 )
a2212c3f
 
121567ee
 // BuildChainOptions contains all the options needed for build-chain
 type BuildChainOptions struct {
 	name string
a2212c3f
 
121567ee
 	defaultNamespace string
3dd75654
 	namespaces       sets.String
121567ee
 	allNamespaces    bool
603eab74
 	triggerOnly      bool
1e65fd2c
 	reverse          bool
a2212c3f
 
121567ee
 	output string
a2212c3f
 
121567ee
 	c client.BuildConfigsNamespacer
 	t client.ImageStreamTagsNamespacer
a2212c3f
 }
 
13eb76ee
 // NewCmdBuildChain implements the OpenShift experimental build-chain command
121567ee
 func NewCmdBuildChain(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
 	options := &BuildChainOptions{
3dd75654
 		namespaces: sets.NewString(),
121567ee
 	}
a2212c3f
 	cmd := &cobra.Command{
e30dccdf
 		Use:     "build-chain IMAGESTREAMTAG",
121567ee
 		Short:   "Output the inputs and dependencies of your builds",
338c483c
 		Long:    buildChainLong,
121567ee
 		Example: fmt.Sprintf(buildChainExample, fullName),
a2212c3f
 		Run: func(cmd *cobra.Command, args []string) {
121567ee
 			cmdutil.CheckErr(options.Complete(f, cmd, args, out))
 
 			cmdutil.CheckErr(options.Validate())
 
 			cmdutil.CheckErr(options.RunBuildChain())
af00209a
 		},
 	}
a2212c3f
 
121567ee
 	cmd.Flags().BoolVar(&options.allNamespaces, "all", false, "Build dependency tree for the specified image stream tag across all namespaces")
603eab74
 	cmd.Flags().BoolVar(&options.triggerOnly, "trigger-only", true, "If true, only include dependencies based on build triggers. If false, include all dependencies.")
1e65fd2c
 	cmd.Flags().BoolVar(&options.reverse, "reverse", false, "If true, show the istags dependencies instead of its dependants.")
121567ee
 	cmd.Flags().StringVarP(&options.output, "output", "o", "", "Output format of dependency tree")
af00209a
 	return cmd
 }
a2212c3f
 
121567ee
 // Complete completes the required options for build-chain
 func (o *BuildChainOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string, out io.Writer) error {
 	if len(args) != 1 {
e30dccdf
 		return cmdutil.UsageError(cmd, "Must pass an image stream tag. If only an image stream name is specified, 'latest' will be used for the tag.")
af00209a
 	}
a2212c3f
 
121567ee
 	// Setup client
97e6f1de
 	oc, _, _, err := f.Clients()
af00209a
 	if err != nil {
 		return err
 	}
121567ee
 	o.c, o.t = oc, oc
a2212c3f
 
49ad7ccd
 	resource := unversioned.GroupResource{}
59e6f9d2
 	mapper, _ := f.Object(false)
49ad7ccd
 	resource, o.name, err = osutil.ResolveResource(imageapi.Resource("imagestreamtags"), args[0], mapper)
121567ee
 	if err != nil {
e30dccdf
 		return err
 	}
 
 	switch resource {
49ad7ccd
 	case imageapi.Resource("imagestreamtags"):
e30dccdf
 		o.name = imageapi.NormalizeImageStreamTag(o.name)
 		glog.V(4).Infof("Using %q as the image stream tag to look dependencies for", o.name)
 	default:
49ad7ccd
 		return fmt.Errorf("invalid resource provided: %v", resource)
af00209a
 	}
121567ee
 
 	// Setup namespace
 	if o.allNamespaces {
 		// TODO: Handle different uses of build-chain; user and admin
f638b86d
 		projectList, err := oc.Projects().List(kapi.ListOptions{})
af00209a
 		if err != nil {
 			return err
 		}
8f014370
 		for _, project := range projectList.Items {
121567ee
 			glog.V(4).Infof("Found namespace %q", project.Name)
 			o.namespaces.Insert(project.Name)
af00209a
 		}
 	}
a2212c3f
 
121567ee
 	namespace, _, err := f.DefaultNamespace()
 	if err != nil {
 		return err
af00209a
 	}
a2212c3f
 
121567ee
 	o.defaultNamespace = namespace
e30dccdf
 	glog.V(4).Infof("Using %q as the namespace for %q", o.defaultNamespace, o.name)
121567ee
 	o.namespaces.Insert(namespace)
 	glog.V(4).Infof("Will look for deps in %s", strings.Join(o.namespaces.List(), ","))
af00209a
 
 	return nil
a2212c3f
 }
 
121567ee
 // Validate returns validation errors regarding build-chain
 func (o *BuildChainOptions) Validate() error {
 	if len(o.name) == 0 {
e30dccdf
 		return fmt.Errorf("image stream tag cannot be empty")
a2212c3f
 	}
121567ee
 	if len(o.defaultNamespace) == 0 {
 		return fmt.Errorf("default namespace cannot be empty")
a2212c3f
 	}
121567ee
 	if o.output != "" && o.output != "dot" {
 		return fmt.Errorf("output must be either empty or 'dot'")
a2212c3f
 	}
121567ee
 	if o.c == nil {
 		return fmt.Errorf("buildConfig client must not be nil")
af00209a
 	}
121567ee
 	if o.t == nil {
 		return fmt.Errorf("imageStreamTag client must not be nil")
a2212c3f
 	}
121567ee
 	return nil
 }
a2212c3f
 
121567ee
 // RunBuildChain contains all the necessary functionality for the OpenShift
 // experimental build-chain command
 func (o *BuildChainOptions) RunBuildChain() error {
e30dccdf
 	ist := imagegraph.MakeImageStreamTagObjectMeta2(o.defaultNamespace, o.name)
 
1e65fd2c
 	desc, err := describe.NewChainDescriber(o.c, o.namespaces, o.output).Describe(ist, !o.triggerOnly, o.reverse)
121567ee
 	if err != nil {
 		if _, isNotFoundErr := err.(describe.NotFoundErr); isNotFoundErr {
e30dccdf
 			name, tag, _ := imageapi.SplitImageStreamTag(o.name)
121567ee
 			// Try to get the imageStreamTag via a direct GET
e30dccdf
 			if _, getErr := o.t.ImageStreamTags(o.defaultNamespace).Get(name, tag); getErr != nil {
121567ee
 				return getErr
a2212c3f
 			}
e30dccdf
 			fmt.Printf("Image stream tag %q in %q doesn't have any dependencies.\n", o.name, o.defaultNamespace)
121567ee
 			return nil
a2212c3f
 		}
121567ee
 		return err
a2212c3f
 	}
 
121567ee
 	fmt.Println(desc)
a2212c3f
 
121567ee
 	return nil
 }