package cli import ( "fmt" "io" "os" "runtime" "strings" "github.com/golang/glog" "github.com/spf13/cobra" "github.com/spf13/pflag" kubecmd "k8s.io/kubernetes/pkg/kubectl/cmd" "github.com/openshift/origin/pkg/cmd/admin" "github.com/openshift/origin/pkg/cmd/cli/cmd" "github.com/openshift/origin/pkg/cmd/cli/cmd/rsync" "github.com/openshift/origin/pkg/cmd/cli/cmd/set" "github.com/openshift/origin/pkg/cmd/cli/policy" "github.com/openshift/origin/pkg/cmd/cli/secrets" "github.com/openshift/origin/pkg/cmd/flagtypes" "github.com/openshift/origin/pkg/cmd/templates" cmdutil "github.com/openshift/origin/pkg/cmd/util" "github.com/openshift/origin/pkg/cmd/util/clientcmd" "github.com/openshift/origin/pkg/version" ) const cliLong = ` Developer and Administrator Client This client exposes commands for managing your applications, as well as lower level tools to interact with each component of your system. To create a new application, you can use the example app source. Login to your server and then run new-app: $ %[1]s login $ %[1]s new-app centos/ruby-22-centos7~https://github.com/openshift/ruby-hello-world.git This will create an application based on the Docker image 'centos/ruby-22-centos7' that builds the source code at 'github.com/openshift/ruby-hello-world.git'. A build will start automatically and a deployment will start as soon as the build finishes. Once your application is deployed, use the status, get, and describe commands to see more about the created components: $ %[1]s status $ %[1]s describe deploymentconfig ruby-hello-world $ %[1]s get pods You'll be able to view the deployed application on the IP and port of the service that new-app created for you. You can easily switch between multiple projects using '%[1]s project '.` func NewCommandCLI(name, fullName string, in io.Reader, out, errout io.Writer) *cobra.Command { // Main command cmds := &cobra.Command{ Use: name, Short: "Command line tools for managing applications", Long: fmt.Sprintf(cliLong, fullName), Run: cmdutil.DefaultSubCommandRun(out), BashCompletionFunction: bashCompletionFunc, } f := clientcmd.New(cmds.PersistentFlags()) loginCmd := cmd.NewCmdLogin(fullName, f, in, out) groups := templates.CommandGroups{ { Message: "Basic Commands:", Commands: []*cobra.Command{ cmd.NewCmdTypes(fullName, f, out), loginCmd, cmd.NewCmdRequestProject(fullName, "new-project", fullName+" login", fullName+" project", f, out), cmd.NewCmdNewApplication(fullName, f, out), cmd.NewCmdStatus(cmd.StatusRecommendedName, fullName+" "+cmd.StatusRecommendedName, f, out), cmd.NewCmdProject(fullName+" project", f, out), }, }, { Message: "Build and Deploy Commands:", Commands: []*cobra.Command{ cmd.NewCmdStartBuild(fullName, f, in, out), cmd.NewCmdBuildLogs(fullName, f, out), cmd.NewCmdDeploy(fullName, f, out), cmd.NewCmdRollback(fullName, f, out), cmd.NewCmdNewBuild(fullName, f, in, out), cmd.NewCmdCancelBuild(fullName, f, out), cmd.NewCmdImportImage(fullName, f, out), cmd.NewCmdScale(fullName, f, out), cmd.NewCmdTag(fullName, f, out), }, }, { Message: "Application Modification Commands:", Commands: []*cobra.Command{ cmd.NewCmdGet(fullName, f, out), cmd.NewCmdDescribe(fullName, f, out), cmd.NewCmdEdit(fullName, f, out), set.NewCmdSet(fullName, f, in, out, errout), cmd.NewCmdLabel(fullName, f, out), cmd.NewCmdAnnotate(fullName, f, out), cmd.NewCmdExpose(fullName, f, out), cmd.NewCmdDelete(fullName, f, out), }, }, { Message: "Troubleshooting and Debugging Commands:", Commands: []*cobra.Command{ cmd.NewCmdExplain(fullName, f, out), cmd.NewCmdLogs(cmd.LogsRecommendedName, fullName, f, out), cmd.NewCmdRsh(cmd.RshRecommendedName, fullName, f, in, out, errout), rsync.NewCmdRsync(rsync.RsyncRecommendedName, fullName, f, out, errout), cmd.NewCmdExec(fullName, f, in, out, errout), cmd.NewCmdPortForward(fullName, f), cmd.NewCmdProxy(fullName, f, out), }, }, { Message: "Advanced Commands:", Commands: []*cobra.Command{ admin.NewCommandAdmin("adm", fullName+" "+"adm", out), cmd.NewCmdCreate(fullName, f, out), cmd.NewCmdReplace(fullName, f, out), cmd.NewCmdApply(fullName, f, out), cmd.NewCmdPatch(fullName, f, out), cmd.NewCmdProcess(fullName, f, out), cmd.NewCmdExport(fullName, f, in, out), cmd.NewCmdRun(fullName, f, in, out, errout), cmd.NewCmdAttach(fullName, f, in, out, errout), policy.NewCmdPolicy(policy.PolicyRecommendedName, fullName+" "+policy.PolicyRecommendedName, f, out), secrets.NewCmdSecrets(secrets.SecretsRecommendedName, fullName+" "+secrets.SecretsRecommendedName, f, in, out, fullName+" edit"), cmd.NewCmdConvert(fullName, f, out), cmd.NewCmdAutoscale(fullName, f, out), }, }, { Message: "Settings Commands:", Commands: []*cobra.Command{ cmd.NewCmdLogout("logout", fullName+" logout", fullName+" login", f, in, out), cmd.NewCmdConfig(fullName, "config"), cmd.NewCmdWhoAmI(cmd.WhoAmIRecommendedCommandName, fullName+" "+cmd.WhoAmIRecommendedCommandName, f, out), }, }, } groups.Add(cmds) filters := []string{ "options", // These commands are deprecated and should not appear in help moved(fullName, "set env", cmds, set.NewCmdEnv(fullName, f, in, out)), moved(fullName, "set volume", cmds, set.NewCmdVolume(fullName, f, out, errout)), } changeSharedFlagDefaults(cmds) templates.ActsAsRootCommand(cmds, filters, groups...). ExposeFlags(loginCmd, "certificate-authority", "insecure-skip-tls-verify", "token") if name == fullName { cmds.AddCommand(version.NewVersionCommand(fullName, false)) } cmds.AddCommand(cmd.NewCmdOptions(out)) return cmds } func moved(fullName, to string, parent, cmd *cobra.Command) string { cmd.Long = fmt.Sprintf("DEPRECATED: This command has been moved to \"%s %s\"", fullName, to) cmd.Short = fmt.Sprintf("DEPRECATED: %s", to) parent.AddCommand(cmd) return cmd.Name() } // changeSharedFlagDefaults changes values of shared flags that we disagree with. This can't be done in godep code because // that would change behavior in our `kubectl` symlink. Defend each change. // 1. show-all - the most interesting pods are terminated/failed pods. We don't want to exclude them from printing func changeSharedFlagDefaults(rootCmd *cobra.Command) { cmds := []*cobra.Command{rootCmd} for i := 0; i < len(cmds); i++ { currCmd := cmds[i] cmds = append(cmds, currCmd.Commands()...) if showAllFlag := currCmd.Flags().Lookup("show-all"); showAllFlag != nil { showAllFlag.DefValue = "true" showAllFlag.Value.Set("true") showAllFlag.Changed = false showAllFlag.Usage = "When printing, show all resources (false means hide terminated pods.)" } // we want to disable the --validate flag by default when we're running kube commands from oc. We want to make sure // that we're only getting the upstream --validate flags, so check both the flag and the usage if validateFlag := currCmd.Flags().Lookup("validate"); (validateFlag != nil) && (validateFlag.Usage == "If true, use a schema to validate the input before sending it") { validateFlag.DefValue = "false" validateFlag.Value.Set("false") validateFlag.Changed = false } } } // NewCmdKubectl provides exactly the functionality from Kubernetes, // but with support for OpenShift resources func NewCmdKubectl(name string, out io.Writer) *cobra.Command { flags := pflag.NewFlagSet("", pflag.ContinueOnError) f := clientcmd.New(flags) cmds := kubecmd.NewKubectlCommand(f.Factory, os.Stdin, out, os.Stderr) cmds.Aliases = []string{"kubectl"} cmds.Use = name cmds.Short = "Kubernetes cluster management via kubectl" flags.VisitAll(func(flag *pflag.Flag) { if f := cmds.PersistentFlags().Lookup(flag.Name); f == nil { cmds.PersistentFlags().AddFlag(flag) } else { glog.V(5).Infof("already registered flag %s", flag.Name) } }) cmds.PersistentFlags().Var(flags.Lookup("config").Value, "kubeconfig", "Specify a kubeconfig file to define the configuration") templates.ActsAsRootCommand(cmds, []string{"options"}) cmds.AddCommand(cmd.NewCmdOptions(out)) return cmds } // CommandFor returns the appropriate command for this base name, // or the OpenShift CLI command. func CommandFor(basename string) *cobra.Command { var cmd *cobra.Command in, out, errout := os.Stdin, os.Stdout, os.Stderr // Make case-insensitive and strip executable suffix if present if runtime.GOOS == "windows" { basename = strings.ToLower(basename) basename = strings.TrimSuffix(basename, ".exe") } switch basename { case "kubectl": cmd = NewCmdKubectl(basename, out) default: cmd = NewCommandCLI(basename, basename, in, out, errout) } if cmd.UsageFunc() == nil { templates.ActsAsRootCommand(cmd, []string{"options"}) } flagtypes.GLog(cmd.PersistentFlags()) return cmd }