pkg/cmd/cli/cmd/rollout/latest.go
3180f050
 package rollout
 
 import (
 	"errors"
 	"fmt"
 	"io"
 
6267dded
 	"github.com/openshift/origin/pkg/cmd/templates"
3180f050
 	"github.com/spf13/cobra"
 	kerrors "k8s.io/kubernetes/pkg/api/errors"
 	"k8s.io/kubernetes/pkg/api/meta"
 	kclient "k8s.io/kubernetes/pkg/client/unversioned"
 	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
 	"k8s.io/kubernetes/pkg/kubectl/resource"
 	"k8s.io/kubernetes/pkg/runtime"
 
 	"github.com/openshift/origin/pkg/client"
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
 	deployutil "github.com/openshift/origin/pkg/deploy/util"
 )
 
6267dded
 var (
 	rolloutLatestLong = templates.LongDesc(`
 		Start a new rollout for a deployment config with the latest state from its triggers
3180f050
 
6267dded
 		This command is appropriate for running manual rollouts. If you want full control over
 		running new rollouts, use "oc set triggers --manual" to disable all triggers in your
 		deployment config and then whenever you want to run a new deployment process, use this
 		command in order to pick up the latest images found in the cluster that are pointed by
 		your image change triggers.`)
3180f050
 
6267dded
 	rolloutLatestExample = templates.Examples(`
 		# Start a new rollout based on the latest images defined in the image change triggers.
   	%[1]s rollout latest dc/nginx`)
3180f050
 )
 
 // RolloutLatestOptions holds all the options for the `rollout latest` command.
 type RolloutLatestOptions struct {
 	mapper meta.RESTMapper
 	typer  runtime.ObjectTyper
 	infos  []*resource.Info
 
7a2339f4
 	DryRun bool
14b40ee5
 	out    io.Writer
 	output string
 	again  bool
3180f050
 
 	oc              client.Interface
 	kc              kclient.Interface
 	baseCommandName string
 }
 
 // NewCmdRolloutLatest implements the oc rollout latest subcommand.
 func NewCmdRolloutLatest(fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
 	opts := &RolloutLatestOptions{
 		baseCommandName: fullName,
 	}
 
 	cmd := &cobra.Command{
 		Use:     "latest DEPLOYMENTCONFIG",
 		Short:   "Start a new rollout for a deployment config with the latest state from its triggers",
 		Long:    rolloutLatestLong,
 		Example: fmt.Sprintf(rolloutLatestExample, fullName),
 		Run: func(cmd *cobra.Command, args []string) {
 			err := opts.Complete(f, cmd, args, out)
 			kcmdutil.CheckErr(err)
 
 			if err := opts.Validate(); err != nil {
 				kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error()))
 			}
 
 			err = opts.RunRolloutLatest()
 			kcmdutil.CheckErr(err)
 		},
442adafd
 		ValidArgs: []string{"deploymentconfig"},
3180f050
 	}
 
7a2339f4
 	kcmdutil.AddPrinterFlags(cmd)
 	kcmdutil.AddDryRunFlag(cmd)
cc7cbc20
 	cmd.Flags().Bool("again", false, "Deploy the current pod template without updating state from triggers")
3180f050
 
 	return cmd
 }
 
 func (o *RolloutLatestOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string, out io.Writer) error {
 	if len(args) != 1 {
 		return errors.New("one deployment config name is needed as argument.")
 	}
 
 	namespace, _, err := f.DefaultNamespace()
 	if err != nil {
 		return err
 	}
 
7a2339f4
 	o.DryRun = kcmdutil.GetFlagBool(cmd, "dry-run")
 
3180f050
 	o.oc, o.kc, err = f.Clients()
 	if err != nil {
 		return err
 	}
 
 	o.mapper, o.typer = f.Object(false)
 	o.infos, err = resource.NewBuilder(o.mapper, o.typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
 		ContinueOnError().
 		NamespaceParam(namespace).
 		ResourceNames("deploymentconfigs", args[0]).
 		SingleResourceType().
 		Do().Infos()
 	if err != nil {
 		return err
 	}
 
 	o.out = out
14b40ee5
 	o.output = kcmdutil.GetFlagString(cmd, "output")
cc7cbc20
 	o.again = kcmdutil.GetFlagBool(cmd, "again")
3180f050
 
 	return nil
 }
 
 func (o RolloutLatestOptions) Validate() error {
 	if len(o.infos) != 1 {
 		return errors.New("a deployment config name is required.")
 	}
 	return nil
 }
 
 func (o RolloutLatestOptions) RunRolloutLatest() error {
 	info := o.infos[0]
 	config, ok := info.Object.(*deployapi.DeploymentConfig)
 	if !ok {
 		return fmt.Errorf("%s is not a deployment config", info.Name)
 	}
 
 	// TODO: Consider allowing one-off deployments for paused configs
 	// See https://github.com/openshift/origin/issues/9903
 	if config.Spec.Paused {
 		return fmt.Errorf("cannot deploy a paused deployment config")
 	}
 
 	deploymentName := deployutil.LatestDeploymentNameForConfig(config)
 	deployment, err := o.kc.ReplicationControllers(config.Namespace).Get(deploymentName)
 	switch {
 	case err == nil:
 		// Reject attempts to start a concurrent deployment.
 		if !deployutil.IsTerminatedDeployment(deployment) {
 			status := deployutil.DeploymentStatusFor(deployment)
 			return fmt.Errorf("#%d is already in progress (%s).", config.Status.LatestVersion, status)
 		}
 	case !kerrors.IsNotFound(err):
 		return err
 	}
 
7a2339f4
 	dc := config
 	if !o.DryRun {
 		request := &deployapi.DeploymentRequest{
 			Name:   config.Name,
 			Latest: !o.again,
 			Force:  true,
 		}
3180f050
 
7a2339f4
 		dc, err = o.oc.DeploymentConfigs(config.Namespace).Instantiate(request)
 
 		// Pre 1.4 servers don't support the instantiate endpoint. Fallback to incrementing
 		// latestVersion on them.
 		if kerrors.IsNotFound(err) || kerrors.IsForbidden(err) {
 			config.Status.LatestVersion++
 			dc, err = o.oc.DeploymentConfigs(config.Namespace).Update(config)
 		}
3180f050
 
7a2339f4
 		if err != nil {
 			return err
 		}
 
 		info.Refresh(dc, true)
 	}
3180f050
 
14b40ee5
 	if o.output == "revision" {
 		fmt.Fprintf(o.out, fmt.Sprintf("%d", dc.Status.LatestVersion))
 		return nil
 	}
 
7a2339f4
 	kcmdutil.PrintSuccess(o.mapper, o.output == "name", o.out, info.Mapping.Resource, info.Name, o.DryRun, "rolled out")
3180f050
 	return nil
 }