예제 #1
0
// NewCmdWhoCan implements the OpenShift cli who-can command
func NewCmdWhoCan(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
	options := &whoCanOptions{}

	cmd := &cobra.Command{
		Use:   "who-can VERB RESOURCE",
		Short: "List who can perform the specified action on a resource",
		Long:  "List who can perform the specified action on a resource",
		Run: func(cmd *cobra.Command, args []string) {
			if err := options.complete(args); err != nil {
				kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error()))
			}

			var err error
			if options.client, _, err = f.Clients(); err != nil {
				kcmdutil.CheckErr(err)
			}
			if options.bindingNamespace, err = f.DefaultNamespace(); err != nil {
				kcmdutil.CheckErr(err)
			}
			if err := options.run(); err != nil {
				kcmdutil.CheckErr(err)
			}
		},
	}

	return cmd
}
예제 #2
0
func (n *NodeOptions) Complete(f *clientcmd.Factory, c *cobra.Command, args []string, out io.Writer) error {
	defaultNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}
	_, kc, err := f.Clients()
	if err != nil {
		return err
	}
	cmdPrinter, output, err := kcmdutil.PrinterForCommand(c)
	if err != nil {
		return err
	}
	mapper, typer := f.Object()

	n.DefaultNamespace = defaultNamespace
	n.Kclient = kc
	n.Writer = out
	n.Mapper = mapper
	n.Typer = typer
	n.RESTClientFactory = f.Factory.RESTClient
	n.Printer = f.Printer
	n.NodeNames = []string{}
	n.CmdPrinter = cmdPrinter
	n.CmdPrinterOutput = false

	if output {
		n.CmdPrinterOutput = true
	}
	if len(args) != 0 {
		n.NodeNames = append(n.NodeNames, args...)
	}
	return nil
}
예제 #3
0
func (o *RoleModificationOptions) Complete(f *clientcmd.Factory, args []string, target *[]string, targetName string, isNamespaced bool) error {
	if len(args) < 2 {
		return fmt.Errorf("You must specify at least two arguments: <role> <%s> [%s]...", targetName, targetName)
	}

	o.RoleName = args[0]
	*target = append(*target, args[1:]...)

	osClient, _, err := f.Clients()
	if err != nil {
		return err
	}

	if isNamespaced {
		roleBindingNamespace, err := f.DefaultNamespace()
		if err != nil {
			return err
		}
		o.RoleBindingAccessor = NewLocalRoleBindingAccessor(roleBindingNamespace, osClient)

	} else {
		o.RoleBindingAccessor = NewClusterRoleBindingAccessor(osClient)

	}

	return nil
}
예제 #4
0
func setupAppConfig(f *clientcmd.Factory, c *cobra.Command, args []string, config *newcmd.AppConfig) error {
	namespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	dockerClient, _, err := dockerutil.NewHelper().GetClient()
	if err == nil {
		if err = dockerClient.Ping(); err == nil {
			config.SetDockerClient(dockerClient)
		}
	}
	if err != nil {
		glog.V(2).Infof("No local Docker daemon detected: %v", err)
	}

	osclient, _, err := f.Clients()
	if err != nil {
		return err
	}
	config.SetOpenShiftClient(osclient, namespace)

	unknown := config.AddArguments(args)
	if len(unknown) != 0 {
		return cmdutil.UsageError(c, "Did not recognize the following arguments: %v", unknown)
	}

	return nil
}
예제 #5
0
func (v *VolumeOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, out io.Writer) error {
	clientConfig, err := f.ClientConfig()
	if err != nil {
		return err
	}
	v.OutputVersion = kcmdutil.OutputVersion(cmd, clientConfig.Version)

	cmdNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}
	mapper, typer := f.Object()

	v.DefaultNamespace = cmdNamespace
	v.Writer = out
	v.Mapper = mapper
	v.Typer = typer
	v.RESTClientFactory = f.Factory.RESTClient
	v.UpdatePodSpecForObject = f.UpdatePodSpecForObject

	if v.Add && len(v.Name) == 0 {
		v.Name = string(kutil.NewUUID())
		if len(v.Output) == 0 {
			fmt.Fprintf(v.Writer, "Generated volume name: %s\n", v.Name)
		}
	}
	// In case of volume source ignore the default volume type
	if len(v.AddOpts.Source) > 0 {
		v.AddOpts.Type = ""
	}
	return nil
}
예제 #6
0
// RunImportImage contains all the necessary functionality for the OpenShift cli import-image command.
func RunImportImage(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
	if len(args) == 0 || len(args[0]) == 0 {
		return cmdutil.UsageError(cmd, "you must specify the name of an image stream.")
	}

	streamName := args[0]
	namespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	osClient, _, err := f.Clients()
	if err != nil {
		return err
	}

	imageStreamClient := osClient.ImageStreams(namespace)
	stream, err := imageStreamClient.Get(streamName)
	if err != nil {
		return err
	}

	if len(stream.Spec.DockerImageRepository) == 0 {
		return errors.New("only image streams with spec.dockerImageRepository set may have images imported")
	}

	if stream.Annotations == nil {
		stream.Annotations = make(map[string]string)
	}
	stream.Annotations[imageapi.DockerImageRepositoryCheckAnnotation] = ""

	updatedStream, err := imageStreamClient.Update(stream)
	if err != nil {
		return err
	}

	resourceVersion := updatedStream.ResourceVersion

	fmt.Fprintln(cmd.Out(), "Waiting for the import to complete, CTRL+C to stop waiting.")

	updatedStream, err = waitForImport(imageStreamClient, stream.Name, resourceVersion)
	if err != nil {
		return fmt.Errorf("unable to determine if the import completed successfully - please run 'oc describe -n %s imagestream/%s' to see if the tags were updated as expected: %v", stream.Namespace, stream.Name, err)
	}

	fmt.Fprintln(cmd.Out(), "The import completed successfully.\n")

	d := describe.ImageStreamDescriber{osClient}
	info, err := d.Describe(updatedStream.Namespace, updatedStream.Name)
	if err != nil {
		return err
	}

	fmt.Fprintln(out, info)
	return nil
}
func (o *RemoveFromProjectOptions) Complete(f *clientcmd.Factory, args []string, target *[]string, targetName string) error {
	if len(args) < 1 {
		return fmt.Errorf("You must specify at least one argument: <%s> [%s]...", targetName, targetName)
	}

	*target = append(*target, args...)

	var err error
	if o.Client, _, err = f.Clients(); err != nil {
		return err
	}
	if o.BindingNamespace, err = f.DefaultNamespace(); err != nil {
		return err
	}

	return nil
}
예제 #8
0
func (o *DeployOptions) Complete(f *clientcmd.Factory, args []string, out io.Writer) error {
	var err error

	o.osClient, o.kubeClient, err = f.Clients()
	if err != nil {
		return err
	}
	o.namespace, err = f.DefaultNamespace()
	if err != nil {
		return err
	}

	o.out = out

	if len(args) > 0 {
		o.deploymentConfigName = args[0]
	}

	return nil
}
예제 #9
0
// RunBuildLogs contains all the necessary functionality for the OpenShift cli build-logs command
func RunBuildLogs(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, opts api.BuildLogOptions, args []string) error {
	if len(args) != 1 {
		return cmdutil.UsageError(cmd, "A build name is required")
	}

	namespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	c, _, err := f.Clients()
	if err != nil {
		return err
	}

	readCloser, err := c.BuildLogs(namespace).Get(args[0], opts).Stream()
	if err != nil {
		return err
	}
	defer readCloser.Close()

	_, err = io.Copy(out, readCloser)
	return err
}
예제 #10
0
// RunBuildChain contains all the necessary functionality for the OpenShift
// experimental build-chain command
func RunBuildChain(f *clientcmd.Factory, cmd *cobra.Command, args []string) error {
	all := cmdutil.GetFlagBool(cmd, "all")
	allTags := cmdutil.GetFlagBool(cmd, "all-tags")
	if len(args) > 1 ||
		(len(args) == 1 && all) ||
		(len(args) == 0 && allTags) ||
		(all && allTags) {
		return cmdutil.UsageError(cmd, "Must pass nothing, an ImageStream name:tag combination, or specify the --all flag")
	}

	oc, _, err := f.Clients()
	if err != nil {
		return err
	}

	// Retrieve namespace(s)
	namespace := cmdutil.GetFlagString(cmd, "namespace")
	if len(namespace) == 0 {
		namespace, err = f.DefaultNamespace()
		if err != nil {
			return err
		}
	}
	namespaces := make([]string, 0)
	if all {
		projectList, err := oc.Projects().List(labels.Everything(), fields.Everything())
		if err != nil {
			return err
		}
		for _, project := range projectList.Items {
			glog.V(4).Infof("Found namespace %s", project.Name)
			namespaces = append(namespaces, project.Name)
		}
	}
	if len(namespaces) == 0 {
		namespaces = append(namespaces, namespace)
	}

	// Get all build configurations
	buildConfigList := make([]buildapi.BuildConfig, 0)
	for _, namespace := range namespaces {
		cfgList, err := oc.BuildConfigs(namespace).List(labels.Everything(), fields.Everything())
		if err != nil {
			return err
		}
		buildConfigList = append(buildConfigList, cfgList.Items...)
	}

	// Parse user input and validate specified image stream
	streams := make(map[string][]string)
	if !all && len(args) != 0 {
		name, specifiedTag, err := parseTag(args[0])
		if err != nil {
			return err
		}

		// Validate the specified image stream
		is, err := oc.ImageStreams(namespace).Get(name)
		if err != nil {
			return err
		}
		stream := join(namespace, name)

		// Validate specified tag
		tags := make([]string, 0)
		exists := false
		for tag := range is.Status.Tags {
			tags = append(tags, tag)
			if specifiedTag == tag {
				exists = true
			}
		}
		if !exists && !allTags {
			// The specified tag isn't a part of our image stream
			return fmt.Errorf("no tag %s exists in %s", specifiedTag, stream)
		} else if !allTags {
			// Use only the specified tag
			tags = []string{specifiedTag}
		}

		// Set the specified stream as the only one to output dependencies for
		streams[stream] = tags
	} else {
		streams = getStreams(buildConfigList)
	}

	if len(streams) == 0 {
		return fmt.Errorf("no ImageStream available for building its dependency tree")
	}

	output := cmdutil.GetFlagString(cmd, "output")
	for stream, tags := range streams {
		for _, tag := range tags {
			glog.V(4).Infof("Checking dependencies of stream %s tag %s", stream, tag)
			root, err := findStreamDeps(stream, tag, buildConfigList)
			if err != nil {
				return err
			}

			// Check if the given image stream doesn't have any dependencies
			if treeSize(root) < 2 {
				glog.Infof("%s:%s has no dependencies\n", root.FullName, tag)
				continue
			}

			switch output {
			case "json":
				jsonDump, err := json.MarshalIndent(root, "", "\t")
				if err != nil {
					return err
				}
				fmt.Println(string(jsonDump))
			case "dot":
				g := dot.NewGraph()
				_, name, err := split(stream)
				if err != nil {
					return err
				}
				graphName := validDOT(name)
				g.SetName(graphName)
				// Directed graph since we illustrate dependencies
				g.SetDir(true)
				// Explicitly allow multiple pairs of edges between
				// the same pair of nodes
				g.SetStrict(false)
				out, err := dotDump(root, g, graphName)
				if err != nil {
					return err
				}
				fmt.Println(out)
			case "ast":
				fmt.Println(root)
			default:
				return cmdutil.UsageError(cmd, "Wrong output format specified: %s", output)
			}
		}
	}
	return nil
}
예제 #11
0
// validate adds one layer of validation prior to calling the upstream
// expose command. Used only for validating services that are about
// to be exposed as routes.
func validate(cmd *cobra.Command, f *clientcmd.Factory, args []string) error {
	if cmdutil.GetFlagString(cmd, "generator") != "route/v1" {
		if len(cmdutil.GetFlagString(cmd, "hostname")) > 0 {
			return fmt.Errorf("cannot use --hostname without generating a route")
		}
		return nil
	}
	if err := validateFlags(cmd); err != nil {
		return err
	}

	namespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	_, kc, err := f.Clients()
	if err != nil {
		return err
	}

	mapper, typer := f.Object()
	r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
		ContinueOnError().
		NamespaceParam(namespace).DefaultNamespace().
		ResourceTypeOrNameArgs(false, args...).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}
	mapping, err := r.ResourceMapping()
	if err != nil {
		return err
	}
	infos, err := r.Infos()
	if err != nil {
		return err
	}
	if len(infos) > 1 {
		return fmt.Errorf("multiple resources provided: %v", args)
	}
	info := infos[0]

	switch mapping.Kind {
	case "Service":
		svc, err := kc.Services(info.Namespace).Get(info.Name)
		if err != nil {
			return err
		}

		supportsTCP := false
		for _, port := range svc.Spec.Ports {
			if port.Protocol == kapi.ProtocolTCP {
				supportsTCP = true
				break
			}
		}
		if !supportsTCP {
			return fmt.Errorf("service %s doesn't support TCP", info.Name)
		}
	default:
		return fmt.Errorf("cannot expose a %s as a route", mapping.Kind)
	}

	return nil
}
예제 #12
0
// RunCancelBuild contains all the necessary functionality for the OpenShift cli cancel-build command
func RunCancelBuild(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
	if len(args) == 0 || len(args[0]) == 0 {
		return cmdutil.UsageError(cmd, "You must specify the name of a build to cancel.")
	}

	buildName := args[0]
	namespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	client, _, err := f.Clients()
	if err != nil {
		return err
	}
	buildClient := client.Builds(namespace)
	build, err := buildClient.Get(buildName)
	if err != nil {
		return err
	}

	if !isBuildCancellable(build) {
		return nil
	}

	// Print build logs before cancelling build.
	if cmdutil.GetFlagBool(cmd, "dump-logs") {
		opts := buildapi.BuildLogOptions{
			NoWait: true,
			Follow: false,
		}
		response, err := client.BuildLogs(namespace).Get(buildName, opts).Do().Raw()
		if err != nil {
			glog.Errorf("Could not fetch build logs for %s: %v", buildName, err)
		} else {
			glog.Infof("Build logs for %s:\n%v", buildName, string(response))
		}
	}

	// Mark build to be cancelled.
	for {
		build.Cancelled = true
		if _, err = buildClient.Update(build); err != nil && errors.IsConflict(err) {
			build, err = buildClient.Get(buildName)
			if err != nil {
				return err
			}
			continue
		}
		if err != nil {
			return err
		}
		break
	}
	glog.V(2).Infof("Build %s was cancelled.", buildName)

	// Create a new build with the same configuration.
	if cmdutil.GetFlagBool(cmd, "restart") {
		request := &buildapi.BuildRequest{
			ObjectMeta: kapi.ObjectMeta{Name: build.Name},
		}
		newBuild, err := client.Builds(namespace).Clone(request)
		if err != nil {
			return err
		}
		glog.V(2).Infof("Restarted build %s.", buildName)
		fmt.Fprintf(out, "%s\n", newBuild.Name)
	} else {
		fmt.Fprintf(out, "%s\n", build.Name)
	}
	return nil
}
예제 #13
0
// NewCmdRollback creates a CLI rollback command.
func NewCmdRollback(fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
	rollback := &deployapi.DeploymentConfigRollback{
		Spec: deployapi.DeploymentConfigRollbackSpec{
			IncludeTemplate: true,
		},
	}

	cmd := &cobra.Command{
		Use:     "rollback DEPLOYMENT",
		Short:   "Revert part of an application back to a previous deployment",
		Long:    rollbackLong,
		Example: fmt.Sprintf(rollbackExample, fullName),
		Run: func(cmd *cobra.Command, args []string) {
			// Validate arguments
			if len(args) == 0 || len(args[0]) == 0 {
				cmdutil.CheckErr(cmdutil.UsageError(cmd, "A deployment name is required."))
			}

			// Extract arguments
			format := cmdutil.GetFlagString(cmd, "output")
			template := cmdutil.GetFlagString(cmd, "template")
			dryRun := cmdutil.GetFlagBool(cmd, "dry-run")

			// Get globally provided stuff
			namespace, err := f.DefaultNamespace()
			cmdutil.CheckErr(err)
			oClient, kClient, err := f.Clients()
			cmdutil.CheckErr(err)

			// Set up the rollback config
			rollback.Spec.From.Name = args[0]

			// Make a helper and generate a rolled back config
			helper := newHelper(oClient, kClient)
			config, err := helper.Generate(namespace, rollback)
			cmdutil.CheckErr(err)

			// If this is a dry run, print and exit
			if dryRun {
				err := helper.Describe(config, out)
				cmdutil.CheckErr(err)
				return
			}

			// If an output format is specified, print and exit
			if len(format) > 0 {
				err := helper.Print(config, format, template, out)
				cmdutil.CheckErr(err)
				return
			}

			// Perform the rollback
			rolledback, err := helper.Update(config)
			cmdutil.CheckErr(err)

			// Notify the user of any disabled image triggers
			fmt.Fprintf(out, "#%d rolled back to %s\n", rolledback.LatestVersion, rollback.Spec.From.Name)
			for _, trigger := range rolledback.Triggers {
				disabled := []string{}
				if trigger.Type == deployapi.DeploymentTriggerOnImageChange && !trigger.ImageChangeParams.Automatic {
					disabled = append(disabled, trigger.ImageChangeParams.From.Name)
				}
				if len(disabled) > 0 {
					reenable := fmt.Sprintf("%s deploy %s --enable-triggers", fullName, rolledback.Name)
					fmt.Fprintf(cmd.Out(), "Warning: the following images triggers were disabled: %s\n  You can re-enable them with: %s\n", strings.Join(disabled, ","), reenable)
				}
			}
		},
	}

	cmd.Flags().BoolVar(&rollback.Spec.IncludeTriggers, "change-triggers", false, "Include the previous deployment's triggers in the rollback")
	cmd.Flags().BoolVar(&rollback.Spec.IncludeStrategy, "change-strategy", false, "Include the previous deployment's strategy in the rollback")
	cmd.Flags().BoolVar(&rollback.Spec.IncludeReplicationMeta, "change-scaling-settings", false, "Include the previous deployment's replicationController replica count and selector in the rollback")
	cmd.Flags().BoolP("dry-run", "d", false, "Instead of performing the rollback, describe what the rollback will look like in human-readable form")
	cmd.Flags().StringP("output", "o", "", "Instead of performing the rollback, print the updated deployment configuration in the specified format (json|yaml|template|templatefile)")
	cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile.")

	return cmd
}
예제 #14
0
// RunProject contains all the necessary functionality for the OpenShift cli process command
func RunProcess(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
	storedTemplate := ""
	if len(args) > 0 {
		storedTemplate = args[0]
	}

	filename := kcmdutil.GetFlagString(cmd, "filename")
	if len(storedTemplate) == 0 && len(filename) == 0 {
		return kcmdutil.UsageError(cmd, "Must pass a filename or name of stored template")
	}

	namespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	mapper, typer := f.Object()

	client, _, err := f.Clients()
	if err != nil {
		return err
	}

	var (
		objects []runtime.Object
		infos   []*resource.Info
		mapping *meta.RESTMapping
	)

	version, kind, err := mapper.VersionAndKindForResource("template")
	if mapping, err = mapper.RESTMapping(kind, version); err != nil {
		return err
	}

	// When storedTemplate is not empty, then we fetch the template from the
	// server, otherwise we require to set the `-f` parameter.
	if len(storedTemplate) > 0 {
		templateObj, err := client.Templates(namespace).Get(storedTemplate)
		if err != nil {
			if errors.IsNotFound(err) {
				return fmt.Errorf("template %q could not be found", storedTemplate)
			}
			return err
		}
		templateObj.CreationTimestamp = util.Now()
		infos = append(infos, &resource.Info{Object: templateObj})
	} else {
		infos, err = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
			NamespaceParam(namespace).RequireNamespace().
			FilenameParam(filename).
			Do().
			Infos()
		if err != nil {
			return err
		}
	}

	outputFormat := kcmdutil.GetFlagString(cmd, "output")

	for i := range infos {
		obj, ok := infos[i].Object.(*api.Template)
		if !ok {
			sourceName := filename
			if len(storedTemplate) > 0 {
				sourceName = namespace + "/" + storedTemplate
			}
			fmt.Fprintf(cmd.Out(), "unable to parse %q, not a valid Template but %s\n", sourceName, reflect.TypeOf(infos[i].Object))
			continue
		}

		// If 'parameters' flag is set it does not do processing but only print
		// the template parameters to console for inspection.
		// If multiple templates are passed, this will print combined output for all
		// templates.
		if kcmdutil.GetFlagBool(cmd, "parameters") {
			if len(infos) > 1 {
				fmt.Fprintf(out, "\n%s:\n", obj.Name)
			}
			if err := describe.PrintTemplateParameters(obj.Parameters, out); err != nil {
				fmt.Fprintf(cmd.Out(), "error printing parameters for %q: %v\n", obj.Name, err)
			}
			continue
		}

		if label := kcmdutil.GetFlagString(cmd, "labels"); len(label) > 0 {
			lbl, err := kubectl.ParseLabels(label)
			if err != nil {
				fmt.Fprintf(cmd.Out(), "error parsing labels: %v\n", err)
				continue
			}
			if obj.ObjectLabels == nil {
				obj.ObjectLabels = make(map[string]string)
			}
			for key, value := range lbl {
				obj.ObjectLabels[key] = value
			}
		}

		// Override the values for the current template parameters
		// when user specify the --value
		if cmd.Flag("value").Changed {
			injectUserVars(cmd, obj)
		}

		resultObj, err := client.TemplateConfigs(namespace).Create(obj)
		if err != nil {
			fmt.Fprintf(cmd.Out(), "error processing the template %q: %v\n", obj.Name, err)
			continue
		}

		if outputFormat == "describe" {
			if s, err := (&describe.TemplateDescriber{
				MetadataAccessor: meta.NewAccessor(),
				ObjectTyper:      kapi.Scheme,
				ObjectDescriber:  nil,
			}).DescribeTemplate(resultObj); err != nil {
				fmt.Fprintf(cmd.Out(), "error describing %q: %v\n", obj.Name, err)
			} else {
				fmt.Fprintf(out, s)
			}
			continue
		}
		objects = append(objects, resultObj.Objects...)
	}

	// Do not print the processed templates when asked to only show parameters or
	// describe.
	if kcmdutil.GetFlagBool(cmd, "parameters") || outputFormat == "describe" {
		return nil
	}

	p, _, err := kubectl.GetPrinter(outputFormat, "")
	if err != nil {
		return err
	}
	p = kubectl.NewVersionedPrinter(p, kapi.Scheme, kcmdutil.OutputVersion(cmd, mapping.APIVersion))

	// use generic output
	if kcmdutil.GetFlagBool(cmd, "raw") {
		for i := range objects {
			p.PrintObj(objects[i], out)
		}
		return nil
	}

	return p.PrintObj(&kapi.List{
		ListMeta: kapi.ListMeta{},
		Items:    objects,
	}, out)
}
예제 #15
0
// RunListBuildWebHooks prints the webhooks for the provided build config.
func RunListBuildWebHooks(f *clientcmd.Factory, out, errOut io.Writer, name string, isBuild bool, webhookFilter string) error {
	generic, github := false, false
	prefix := false
	switch webhookFilter {
	case "all":
		generic, github = true, true
		prefix = true
	case "generic":
		generic = true
	case "github":
		github = true
	default:
		return fmt.Errorf("--list-webhooks must be 'all', 'generic', or 'github'")
	}
	client, _, err := f.Clients()
	if err != nil {
		return err
	}
	namespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	if isBuild {
		build, err := client.Builds(namespace).Get(name)
		if err != nil {
			return err
		}
		ref := build.Config
		if ref == nil {
			return fmt.Errorf("the provided Build %q was not created from a BuildConfig and cannot have webhooks", name)
		}
		if len(ref.Namespace) > 0 {
			namespace = ref.Namespace
		}
		name = ref.Name
	}
	config, err := client.BuildConfigs(namespace).Get(name)
	if err != nil {
		return err
	}

	for _, t := range config.Triggers {
		hookType := ""
		switch {
		case t.GenericWebHook != nil && generic:
			if prefix {
				hookType = "generic "
			}
		case t.GitHubWebHook != nil && github:
			if prefix {
				hookType = "github "
			}
		default:
			continue
		}
		url, err := client.BuildConfigs(namespace).WebHookURL(name, &t)
		if err != nil {
			if err != osclient.ErrTriggerIsNotAWebHook {
				fmt.Fprintf(errOut, "error: unable to get webhook for %s: %v", name, err)
			}
			continue
		}
		fmt.Fprintf(out, "%s%s\n", hookType, url.String())
	}
	return nil
}
예제 #16
0
// RunTag contains all the necessary functionality for the OpenShift cli tag command.
func RunTag(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string, sourceKind string) error {
	if len(args) < 2 {
		return cmdutil.UsageError(cmd, "you must specify a source and at least one destination")
	}

	original := sourceKind
	if len(sourceKind) > 0 {
		sourceKind = determineSourceKind(f, sourceKind)
	}
	if len(sourceKind) > 0 {
		validSources := util.NewStringSet("imagestreamtag", "istag", "imagestreamimage", "isimage", "docker", "dockerimage")
		if !validSources.Has(strings.ToLower(sourceKind)) {
			cmdutil.CheckErr(cmdutil.UsageError(cmd, "invalid source %q; valid values are %v", original, strings.Join(validSources.List(), ", ")))
		}
	}

	namespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	sourceNamespace, sourceNameAndRef, err := parseStreamName(args[0], namespace)
	if err != nil {
		return err
	}

	osClient, _, err := f.Clients()
	if err != nil {
		return err
	}

	if len(sourceKind) == 0 {
		if sourceName, sourceTag, ok := imageapi.SplitImageStreamTag(sourceNameAndRef); ok {
			if _, err := osClient.ImageStreamTags(sourceNamespace).Get(sourceName, sourceTag); err == nil {
				sourceKind = "ImageStreamTag"
			}
		}
	}

	if len(sourceKind) == 0 {
		if sourceName, sourceID, err := imagestreamimage.ParseNameAndID(sourceNameAndRef); err == nil {
			if _, err := osClient.ImageStreamImages(sourceNamespace).Get(sourceName, sourceID); err == nil {
				sourceKind = "ImageStreamImage"
			}
		}
	}

	if len(sourceKind) == 0 {
		sourceKind = "DockerImage"
	}

	for _, arg := range args[1:] {
		destNamespace, destNameAndTag, err := parseStreamName(arg, namespace)
		if err != nil {
			return err
		}

		destName, destTag, ok := imageapi.SplitImageStreamTag(destNameAndTag)
		if !ok {
			return fmt.Errorf("%q must be of the form <namespace>/<stream name>:<tag>", arg)
		}

		isc := osClient.ImageStreams(destNamespace)

		target, err := isc.Get(destName)
		if err != nil {
			if !kerrors.IsNotFound(err) {
				return err
			}

			// try to create the target if it doesn't exist
			target = &imageapi.ImageStream{
				ObjectMeta: kapi.ObjectMeta{
					Name: destName,
				},
			}
			target, err = isc.Create(target)
			if err != nil {
				return nil
			}
		}

		if target.Spec.Tags == nil {
			target.Spec.Tags = make(map[string]imageapi.TagReference)
		}

		targetRef, ok := target.Spec.Tags[destTag]
		if !ok {
			targetRef = imageapi.TagReference{}
		}

		targetRef.From = &kapi.ObjectReference{
			Kind: sourceKind,
		}
		switch sourceKind {
		case "DockerImage":
			targetRef.From.Name = args[0]
		default:
			targetRef.From.Namespace = sourceNamespace
			targetRef.From.Name = sourceNameAndRef
		}

		target.Spec.Tags[destTag] = targetRef

		if _, err = isc.Update(target); err != nil {
			return err
		}
	}

	return nil
}
예제 #17
0
// RunStartBuild contains all the necessary functionality for the OpenShift cli start-build command
func RunStartBuild(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string, webhooks util.StringFlag) error {
	webhook := cmdutil.GetFlagString(cmd, "from-webhook")
	buildName := cmdutil.GetFlagString(cmd, "from-build")
	follow := cmdutil.GetFlagBool(cmd, "follow")

	switch {
	case len(webhook) > 0:
		if len(args) > 0 || len(buildName) > 0 {
			return cmdutil.UsageError(cmd, "The '--from-webhook' flag is incompatible with arguments or '--from-build'")
		}
		path := cmdutil.GetFlagString(cmd, "git-repository")
		postReceivePath := cmdutil.GetFlagString(cmd, "git-post-receive")
		repo := git.NewRepository()
		return RunStartBuildWebHook(f, out, webhook, path, postReceivePath, repo)
	case len(args) != 1 && len(buildName) == 0:
		return cmdutil.UsageError(cmd, "Must pass a name of a BuildConfig or specify build name with '--from-build' flag")
	}

	name := buildName
	isBuild := true
	if len(name) == 0 {
		name = args[0]
		isBuild = false
	}

	if webhooks.Provided() {
		return RunListBuildWebHooks(f, out, cmd.Out(), name, isBuild, webhooks.String())
	}

	client, _, err := f.Clients()
	if err != nil {
		return err
	}

	namespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	request := &buildapi.BuildRequest{
		ObjectMeta: kapi.ObjectMeta{Name: name},
	}
	var newBuild *buildapi.Build
	if isBuild {
		if newBuild, err = client.Builds(namespace).Clone(request); err != nil {
			return err
		}
	} else {
		if newBuild, err = client.BuildConfigs(namespace).Instantiate(request); err != nil {
			return err
		}
	}
	fmt.Fprintf(out, "%s\n", newBuild.Name)

	if follow {
		opts := buildapi.BuildLogOptions{
			Follow: true,
			NoWait: false,
		}
		rd, err := client.BuildLogs(namespace).Get(newBuild.Name, opts).Stream()
		if err != nil {
			return fmt.Errorf("error getting logs: %v", err)
		}
		defer rd.Close()
		_, err = io.Copy(out, rd)
		if err != nil {
			return fmt.Errorf("error streaming logs: %v", err)
		}
	}
	return nil
}
예제 #18
0
func RunExport(f *clientcmd.Factory, exporter Exporter, in io.Reader, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error {
	selector := cmdutil.GetFlagString(cmd, "selector")
	all := cmdutil.GetFlagBool(cmd, "all")
	exact := cmdutil.GetFlagBool(cmd, "exact")
	asTemplate := cmdutil.GetFlagString(cmd, "as-template")
	raw := cmdutil.GetFlagBool(cmd, "raw")
	if exact && raw {
		return cmdutil.UsageError(cmd, "--exact and --raw may not both be specified")
	}

	clientConfig, err := f.ClientConfig()
	if err != nil {
		return err
	}
	outputVersion := cmdutil.OutputVersion(cmd, clientConfig.Version)

	cmdNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	mapper, typer := f.Object()
	b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(filenames...).
		SelectorParam(selector).
		ResourceTypeOrNameArgs(all, args...).
		Flatten()

	one := false
	infos, err := b.Do().IntoSingular(&one).Infos()
	if err != nil {
		return err
	}

	if len(infos) == 0 {
		return fmt.Errorf("no resources found - nothing to export")
	}

	if !raw {
		newInfos := []*resource.Info{}
		errs := []error{}
		for _, info := range infos {
			if err := exporter.Export(info.Object, exact); err != nil {
				if err == ErrExportOmit {
					continue
				}
				errs = append(errs, err)
			}
			newInfos = append(newInfos, info)
		}
		if len(errs) > 0 {
			return utilerrors.NewAggregate(errs)
		}
		infos = newInfos
	}

	var result runtime.Object
	if len(asTemplate) > 0 {
		objects, err := resource.AsVersionedObjects(infos, outputVersion)
		if err != nil {
			return err
		}
		template := &templateapi.Template{
			Objects: objects,
		}
		template.Name = asTemplate
		result, err = kapi.Scheme.ConvertToVersion(template, outputVersion)
		if err != nil {
			return err
		}
	} else {
		object, err := resource.AsVersionedObject(infos, !one, outputVersion)
		if err != nil {
			return err
		}
		result = object
	}

	// use YAML as the default format
	outputFormat := cmdutil.GetFlagString(cmd, "output")
	templateFile := cmdutil.GetFlagString(cmd, "template")
	if len(outputFormat) == 0 && len(templateFile) != 0 {
		outputFormat = "template"
	}
	if len(outputFormat) == 0 {
		outputFormat = "yaml"
	}
	p, _, err := kubectl.GetPrinter(outputFormat, templateFile)
	if err != nil {
		return err
	}
	return p.PrintObj(result, out)
}
예제 #19
0
// RunEnv contains all the necessary functionality for the OpenShift cli env command
func RunEnv(f *clientcmd.Factory, in io.Reader, out io.Writer, cmd *cobra.Command, args []string, envParams, filenames kutil.StringList) error {
	resources, envArgs := []string{}, []string{}
	first := true
	for _, s := range args {
		isEnv := strings.Contains(s, "=") || strings.HasSuffix(s, "-")
		switch {
		case first && isEnv:
			first = false
			fallthrough
		case !first && isEnv:
			envArgs = append(envArgs, s)
		case first && !isEnv:
			resources = append(resources, s)
		case !first && !isEnv:
			return cmdutil.UsageError(cmd, "all resources must be specified before environment changes: %s", s)
		}
	}
	if len(filenames) == 0 && len(resources) < 1 {
		return cmdutil.UsageError(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>")
	}

	containerMatch := cmdutil.GetFlagString(cmd, "containers")
	list := cmdutil.GetFlagBool(cmd, "list")
	selector := cmdutil.GetFlagString(cmd, "selector")
	all := cmdutil.GetFlagBool(cmd, "all")
	//overwrite := cmdutil.GetFlagBool(cmd, "overwrite")
	resourceVersion := cmdutil.GetFlagString(cmd, "resource-version")
	outputFormat := cmdutil.GetFlagString(cmd, "output")

	if list && len(outputFormat) > 0 {
		return cmdutil.UsageError(cmd, "--list and --output may not be specified together")
	}

	clientConfig, err := f.ClientConfig()
	if err != nil {
		return err
	}
	outputVersion := cmdutil.OutputVersion(cmd, clientConfig.Version)

	cmdNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	env, remove, err := parseEnv(append(envParams, envArgs...), in)
	if err != nil {
		return err
	}

	mapper, typer := f.Object()
	b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(filenames...).
		SelectorParam(selector).
		ResourceTypeOrNameArgs(all, resources...).
		Flatten()

	one := false
	infos, err := b.Do().IntoSingular(&one).Infos()
	if err != nil {
		return err
	}

	// only apply resource version locking on a single resource
	if !one && len(resourceVersion) > 0 {
		return cmdutil.UsageError(cmd, "--resource-version may only be used with a single resource")
	}

	skipped := 0
	for _, info := range infos {
		ok, err := f.UpdatePodSpecForObject(info.Object, func(spec *kapi.PodSpec) error {
			containers, _ := selectContainers(spec.Containers, containerMatch)
			if len(containers) == 0 {
				fmt.Fprintf(cmd.Out(), "warning: %s/%s does not have any containers matching %q\n", info.Mapping.Resource, info.Name, containerMatch)
				return nil
			}
			for _, c := range containers {
				c.Env = updateEnv(c.Env, env, remove)

				if list {
					fmt.Fprintf(out, "# %s %s, container %s\n", info.Mapping.Resource, info.Name, c.Name)
					for _, env := range c.Env {
						// if env.ValueFrom != nil && env.ValueFrom.FieldRef != nil {
						// 	fmt.Fprintf(cmd.Out(), "%s= # calculated from pod %s %s\n", env.Name, env.ValueFrom.FieldRef.FieldPath, env.ValueFrom.FieldRef.APIVersion)
						// 	continue
						// }
						fmt.Fprintf(out, "%s=%s\n", env.Name, env.Value)

					}
				}
			}
			return nil
		})
		if !ok {
			skipped++
			continue
		}
		if err != nil {
			fmt.Fprintf(cmd.Out(), "error: %s/%s %v\n", info.Mapping.Resource, info.Name, err)
			continue
		}
	}
	if one && skipped == len(infos) {
		return fmt.Errorf("the %s %s is not a pod or does not have a pod template", infos[0].Mapping.Resource, infos[0].Name)
	}

	if list {
		return nil
	}

	objects, err := resource.AsVersionedObject(infos, false, outputVersion)
	if err != nil {
		return err
	}

	if len(outputFormat) != 0 {
		p, _, err := kubectl.GetPrinter(outputFormat, "")
		if err != nil {
			return err
		}
		return p.PrintObj(objects, out)
	}

	failed := false
	for _, info := range infos {
		data, err := info.Mapping.Codec.Encode(info.Object)
		if err != nil {
			fmt.Fprintf(cmd.Out(), "Error: %v\n", err)
			failed = true
			continue
		}
		obj, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, true, data)
		if err != nil {
			handlePodUpdateError(cmd.Out(), err, "environment variables")
			failed = true
			continue
		}
		info.Refresh(obj, true)
		fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name)
	}
	if failed {
		return errExit
	}
	return nil
}
예제 #20
0
func RunEdit(fullName string, f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error {
	var printer kubectl.ResourcePrinter
	var ext string
	switch format := cmdutil.GetFlagString(cmd, "output"); format {
	case "json":
		printer = &kubectl.JSONPrinter{}
		ext = ".json"
	case "yaml":
		printer = &kubectl.YAMLPrinter{}
		ext = ".yaml"
	default:
		return cmdutil.UsageError(cmd, "The flag 'output' must be one of yaml|json")
	}

	cmdNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	mapper, typer := f.Object()
	rmap := &resource.Mapper{
		ObjectTyper:  typer,
		RESTMapper:   mapper,
		ClientMapper: f.ClientMapperForCommand(),
	}

	b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(filenames...).
		//SelectorParam(selector).
		ResourceTypeOrNameArgs(true, args...).
		Latest()
	if err != nil {
		return err
	}

	clientConfig, err := f.ClientConfig()
	if err != nil {
		return err
	}

	r := b.Flatten().Do()
	infos, err := r.Infos()
	if err != nil {
		return err
	}

	defaultVersion := cmdutil.OutputVersion(cmd, clientConfig.Version)
	results := editResults{}
	for {
		obj, err := resource.AsVersionedObject(infos, false, defaultVersion)
		if err != nil {
			return preservedFile(err, results.file, cmd.Out())
		}

		// TODO: add an annotating YAML printer that can print inline comments on each field,
		//   including descriptions or validation errors

		// generate the file to edit
		buf := &bytes.Buffer{}
		if err := results.header.WriteTo(buf); err != nil {
			return preservedFile(err, results.file, cmd.Out())
		}
		if err := printer.PrintObj(obj, buf); err != nil {
			return preservedFile(err, results.file, cmd.Out())
		}
		original := buf.Bytes()

		// launch the editor
		edit := editor.NewDefaultEditor()
		edited, file, err := edit.LaunchTempFile("oc-edit-", ext, buf)
		if err != nil {
			return preservedFile(err, results.file, cmd.Out())
		}

		// cleanup any file from the previous pass
		if len(results.file) > 0 {
			os.Remove(results.file)
		}

		glog.V(4).Infof("User edited:\n%s", string(edited))
		lines, err := hasLines(bytes.NewBuffer(edited))
		if err != nil {
			return preservedFile(err, file, cmd.Out())
		}
		if bytes.Equal(original, edited) {
			if len(results.edit) > 0 {
				preservedFile(nil, file, cmd.Out())
			} else {
				os.Remove(file)
			}
			fmt.Fprintln(cmd.Out(), "Edit cancelled, no changes made.")
			return nil
		}
		if !lines {
			if len(results.edit) > 0 {
				preservedFile(nil, file, cmd.Out())
			} else {
				os.Remove(file)
			}
			fmt.Fprintln(cmd.Out(), "Edit cancelled, saved file was empty.")
			return nil
		}

		results = editResults{
			file: file,
		}

		// parse the edited file
		updates, err := rmap.InfoForData(edited, "edited-file")
		if err != nil {
			results.header.reasons = append(results.header.reasons, editReason{
				head: fmt.Sprintf("The edited file had a syntax error: %v", err),
			})
			continue
		}

		visitor := resource.NewFlattenListVisitor(updates, rmap)

		// need to make sure the original namespace wasn't changed while editing
		if err = visitor.Visit(resource.RequireNamespace(cmdNamespace)); err != nil {
			return preservedFile(err, file, cmd.Out())
		}

		// attempt to calculate a delta for merging conflicts
		delta, err := jsonmerge.NewDelta(original, edited)
		if err != nil {
			glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
			delta = nil
		} else {
			delta.AddPreconditions(jsonmerge.RequireKeyUnchanged("apiVersion"))
			results.delta = delta
			results.version = defaultVersion
		}

		err = visitor.Visit(func(info *resource.Info) error {
			data, err := info.Mapping.Codec.Encode(info.Object)
			if err != nil {
				return err
			}
			updated, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, false, data)
			if err != nil {
				fmt.Fprintln(cmd.Out(), results.AddError(err, info))
				return nil
			}
			info.Refresh(updated, true)
			fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name)
			return nil
		})
		if err != nil {
			return preservedFile(err, file, cmd.Out())
		}

		if results.retryable > 0 {
			fmt.Fprintf(cmd.Out(), "You can run `%s update -f %s` to try this update again.\n", fullName, file)
			return errExit
		}
		if results.conflict > 0 {
			fmt.Fprintf(cmd.Out(), "You must update your local resource version and run `%s update -f %s` to overwrite the remote changes.\n", fullName, file)
			return errExit
		}
		if len(results.edit) == 0 {
			if results.notfound == 0 {
				os.Remove(file)
			} else {
				fmt.Fprintf(cmd.Out(), "The edits you made on deleted resources have been saved to %q\n", file)
			}
			return nil
		}

		// loop again and edit the remaining items
		infos = results.edit
	}
	return nil
}