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
} |