diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2019-02-08 20:12:38 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-02-08 20:12:38 +0100 |
commit | afd4d5f4a4b05f421e6f336b4d74a0d808be57ed (patch) | |
tree | 2ccb4a0bd9bda70c1c258dcb1b8aca8961d9ad30 /cmd | |
parent | 962850c6e0dfcee926af31fc0ad24f1f6c26f8ac (diff) | |
parent | 25a3923b61a5ca014318e6d957f68abd03947297 (diff) | |
download | podman-afd4d5f4a4b05f421e6f336b4d74a0d808be57ed.tar.gz podman-afd4d5f4a4b05f421e6f336b4d74a0d808be57ed.tar.bz2 podman-afd4d5f4a4b05f421e6f336b4d74a0d808be57ed.zip |
Merge pull request #2274 from baude/cobraprep
Migrate to cobra CLI
Diffstat (limited to 'cmd')
89 files changed, 4118 insertions, 3762 deletions
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go index bcfee6891..8b0fd7ffd 100644 --- a/cmd/podman/attach.go +++ b/cmd/podman/attach.go @@ -3,57 +3,56 @@ package main import ( "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - attachFlags = []cli.Flag{ - cli.StringFlag{ - Name: "detach-keys", - Usage: "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _.", - }, - cli.BoolFlag{ - Name: "no-stdin", - Usage: "Do not attach STDIN. The default is false.", - }, - cli.BoolTFlag{ - Name: "sig-proxy", - Usage: "Proxy received signals to the process (default true)", - }, - LatestFlag, - } + attachCommand cliconfig.AttachValues attachDescription = "The podman attach command allows you to attach to a running container using the container's ID or name, either to view its ongoing output or to control it interactively." - attachCommand = cli.Command{ - Name: "attach", - Usage: "Attach to a running container", - Description: attachDescription, - Flags: sortFlags(attachFlags), - Action: attachCmd, - ArgsUsage: "", - OnUsageError: usageErrorHandler, + _attachCommand = &cobra.Command{ + Use: "attach", + Short: "Attach to a running container", + Long: attachDescription, + RunE: func(cmd *cobra.Command, args []string) error { + attachCommand.InputArgs = args + attachCommand.GlobalFlags = MainGlobalOpts + return attachCmd(&attachCommand) + }, + Example: "", } ) -func attachCmd(c *cli.Context) error { - args := c.Args() +func init() { + attachCommand.Command = _attachCommand + flags := attachCommand.Flags() + flags.StringVar(&attachCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _") + flags.BoolVar(&attachCommand.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false") + flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true)") + + flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + + rootCmd.AddCommand(attachCommand.Command) +} + +func attachCmd(c *cliconfig.AttachValues) error { + args := c.InputArgs var ctr *libpod.Container - if err := validateFlags(c, attachFlags); err != nil { - return err - } - if len(c.Args()) > 1 || (len(c.Args()) == 0 && !c.Bool("latest")) { + + if len(c.InputArgs) > 1 || (len(c.InputArgs) == 0 && !c.Latest) { return errors.Errorf("attach requires the name or id of one running container or the latest flag") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - if c.Bool("latest") { + if c.Latest { ctr, err = runtime.GetLatestContainer() } else { ctr, err = runtime.LookupContainer(args[0]) @@ -72,11 +71,11 @@ func attachCmd(c *cli.Context) error { } inputStream := os.Stdin - if c.Bool("no-stdin") { + if c.NoStdin { inputStream = nil } - if err := startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.String("detach-keys"), c.BoolT("sig-proxy"), false); err != nil { + if err := startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, c.SigProxy, false); err != nil { return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) } diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 0cd1bfbe3..20f621e84 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -10,38 +10,58 @@ import ( "github.com/containers/buildah/imagebuildah" buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/buildah/pkg/parse" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - layerFlags = []cli.Flag{ - cli.BoolTFlag{ - Name: "force-rm", - Usage: "Always remove intermediate containers after a build, even if the build is unsuccessful. (default true)", - }, - cli.BoolTFlag{ - Name: "layers", - Usage: "Cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override. ", - }, - } + buildCommand cliconfig.BuildValues buildDescription = "Builds an OCI or Docker image using instructions from one\n" + "or more Dockerfiles and a specified build context directory." - buildCommand = cli.Command{ - Name: "build", - Usage: "Build an image using instructions from Dockerfiles", - Description: buildDescription, - Flags: sortFlags(append(append(buildahcli.BudFlags, layerFlags...), buildahcli.FromAndBudFlags...)), - Action: buildCmd, - ArgsUsage: "CONTEXT-DIRECTORY | URL", - SkipArgReorder: true, - OnUsageError: usageErrorHandler, + layerValues buildahcli.LayerResults + budFlagsValues buildahcli.BudResults + fromAndBudValues buildahcli.FromAndBudResults + userNSValues buildahcli.UserNSResults + namespaceValues buildahcli.NameSpaceResults + + _buildCommand = &cobra.Command{ + Use: "build", + Short: "Build an image using instructions from Dockerfiles", + Long: buildDescription, + RunE: func(cmd *cobra.Command, args []string) error { + buildCommand.InputArgs = args + buildCommand.GlobalFlags = MainGlobalOpts + buildCommand.BudResults = &budFlagsValues + buildCommand.UserNSResults = &userNSValues + buildCommand.FromAndBudResults = &fromAndBudValues + buildCommand.LayerResults = &layerValues + buildCommand.NameSpaceResults = &namespaceValues + return buildCmd(&buildCommand) + }, + Example: "CONTEXT-DIRECTORY | URL", } ) +func init() { + buildCommand.Command = _buildCommand + flags := buildCommand.Flags() + flags.SetInterspersed(false) + + flags.BoolVar(&layerValues.ForceRm, "force-rm", true, "Always remove intermediate containers after a build, even if the build is unsuccessful. (default true)") + flags.BoolVar(&layerValues.Layers, "layers", true, "Cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override") + budFlags := buildahcli.GetBudFlags(&budFlagsValues) + fromAndBugFlags := buildahcli.GetFromAndBudFlags(&fromAndBudValues, &userNSValues, &namespaceValues) + + flags.AddFlagSet(&budFlags) + flags.AddFlagSet(&fromAndBugFlags) + + rootCmd.AddCommand(buildCommand.Command) +} + func getDockerfiles(files []string) []string { var dockerfiles []string for _, f := range files { @@ -54,30 +74,30 @@ func getDockerfiles(files []string) []string { return dockerfiles } -func buildCmd(c *cli.Context) error { +func buildCmd(c *cliconfig.BuildValues) error { // The following was taken directly from containers/buildah/cmd/bud.go // TODO Find a away to vendor more of this in rather than copy from bud output := "" tags := []string{} - if c.IsSet("tag") || c.IsSet("t") { - tags = c.StringSlice("tag") + if c.Flag("tag").Changed { + tags = c.Tag if len(tags) > 0 { output = tags[0] tags = tags[1:] } } pullPolicy := imagebuildah.PullNever - if c.BoolT("pull") { + if c.Pull { pullPolicy = imagebuildah.PullIfMissing } - if c.Bool("pull-always") { + if c.PullAlways { pullPolicy = imagebuildah.PullAlways } args := make(map[string]string) - if c.IsSet("build-arg") { - for _, arg := range c.StringSlice("build-arg") { + if c.Flag("build-arg").Changed { + for _, arg := range c.BuildArg { av := strings.SplitN(arg, "=", 2) if len(av) > 1 { args[av[0]] = av[1] @@ -87,15 +107,15 @@ func buildCmd(c *cli.Context) error { } } - dockerfiles := getDockerfiles(c.StringSlice("file")) - format, err := getFormat(c) + dockerfiles := getDockerfiles(c.File) + format, err := getFormat(&c.PodmanCommand) if err != nil { return nil } contextDir := "" - cliArgs := c.Args() + cliArgs := c.InputArgs - layers := c.BoolT("layers") // layers for podman defaults to true + layers := c.Layers // layers for podman defaults to true // Check to see if the BUILDAH_LAYERS environment variable is set and override command-line if _, ok := os.LookupEnv("BUILDAH_LAYERS"); ok { layers = buildahcli.UseLayers() @@ -124,7 +144,7 @@ func buildCmd(c *cli.Context) error { } contextDir = absDir } - cliArgs = cliArgs.Tail() + cliArgs = Tail(cliArgs) } else { // No context directory or URL was specified. Try to use the // home of the first locally-available Dockerfile. @@ -153,17 +173,14 @@ func buildCmd(c *cli.Context) error { if len(dockerfiles) == 0 { dockerfiles = append(dockerfiles, filepath.Join(contextDir, "Dockerfile")) } - if err := parse.ValidateFlags(c, buildahcli.BudFlags); err != nil { - return err - } runtimeFlags := []string{} - for _, arg := range c.StringSlice("runtime-flag") { + for _, arg := range c.RuntimeOpts { runtimeFlags = append(runtimeFlags, "--"+arg) } // end from buildah - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -173,10 +190,10 @@ func buildCmd(c *cli.Context) error { stdout = os.Stdout stderr = os.Stderr reporter = os.Stderr - if c.IsSet("logfile") { - f, err := os.OpenFile(c.String("logfile"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + if c.Flag("logfile").Changed { + f, err := os.OpenFile(c.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { - return errors.Errorf("error opening logfile %q: %v", c.String("logfile"), err) + return errors.Errorf("error opening logfile %q: %v", c.Logfile, err) } defer f.Close() logrus.SetOutput(f) @@ -185,36 +202,36 @@ func buildCmd(c *cli.Context) error { reporter = f } - systemContext, err := parse.SystemContextFromOptions(c) + systemContext, err := parse.SystemContextFromOptions(c.PodmanCommand.Command) if err != nil { return errors.Wrapf(err, "error building system context") } - systemContext.AuthFilePath = getAuthFile(c.String("authfile")) - commonOpts, err := parse.CommonBuildOptions(c) + systemContext.AuthFilePath = getAuthFile(c.Authfile) + commonOpts, err := parse.CommonBuildOptions(c.PodmanCommand.Command) if err != nil { return err } - namespaceOptions, networkPolicy, err := parse.NamespaceOptions(c) + namespaceOptions, networkPolicy, err := parse.NamespaceOptions(c.PodmanCommand.Command) if err != nil { return errors.Wrapf(err, "error parsing namespace-related options") } - usernsOption, idmappingOptions, err := parse.IDMappingOptions(c) + usernsOption, idmappingOptions, err := parse.IDMappingOptions(c.PodmanCommand.Command) if err != nil { return errors.Wrapf(err, "error parsing ID mapping options") } namespaceOptions.AddOrReplace(usernsOption...) ociruntime := runtime.GetOCIRuntimePath() - if c.IsSet("runtime") { - ociruntime = c.String("runtime") + if c.Flag("runtime").Changed { + ociruntime = c.Runtime } options := imagebuildah.BuildOptions{ ContextDirectory: contextDir, PullPolicy: pullPolicy, Compression: imagebuildah.Gzip, - Quiet: c.Bool("quiet"), - SignaturePolicyPath: c.String("signature-policy"), + Quiet: c.Quiet, + SignaturePolicyPath: c.SignaturePolicy, Args: args, Output: output, AdditionalTags: tags, @@ -227,22 +244,22 @@ func buildCmd(c *cli.Context) error { SystemContext: systemContext, NamespaceOptions: namespaceOptions, ConfigureNetwork: networkPolicy, - CNIPluginPath: c.String("cni-plugin-path"), - CNIConfigDir: c.String("cni-config-dir"), + CNIPluginPath: c.CNIPlugInPath, + CNIConfigDir: c.CNIConfigDir, IDMappingOptions: idmappingOptions, CommonBuildOpts: commonOpts, - DefaultMountsFilePath: c.GlobalString("default-mounts-file"), - IIDFile: c.String("iidfile"), - Squash: c.Bool("squash"), - Labels: c.StringSlice("label"), - Annotations: c.StringSlice("annotation"), + DefaultMountsFilePath: c.GlobalFlags.DefaultMountsFile, + IIDFile: c.Iidfile, + Squash: c.Squash, + Labels: c.Label, + Annotations: c.Annotation, Layers: layers, - NoCache: c.Bool("no-cache"), - RemoveIntermediateCtrs: c.BoolT("rm"), - ForceRmIntermediateCtrs: c.BoolT("force-rm"), + NoCache: c.NoCache, + RemoveIntermediateCtrs: c.Rm, + ForceRmIntermediateCtrs: c.ForceRm, } - if c.Bool("quiet") { + if c.Quiet { options.ReportWriter = ioutil.Discard } @@ -252,3 +269,13 @@ func buildCmd(c *cli.Context) error { return runtime.Build(getContext(), options, dockerfiles...) } + +// Tail returns a string slice after the first element unless there are +// not enough elements, then it returns an empty slice. This is to replace +// the urfavecli Tail method for args +func Tail(a []string) []string { + if len(a) >= 2 { + return []string(a)[1:] + } + return []string{} +} diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go index 5c3363147..2a978bea8 100644 --- a/cmd/podman/checkpoint.go +++ b/cmd/podman/checkpoint.go @@ -5,70 +5,67 @@ import ( "fmt" "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + checkpointCommand cliconfig.CheckpointValues checkpointDescription = ` podman container checkpoint Checkpoints one or more running containers. The container name or ID can be used. ` - checkpointFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "keep, k", - Usage: "Keep all temporary checkpoint files", + _checkpointCommand = &cobra.Command{ + Use: "checkpoint", + Short: "Checkpoints one or more containers", + Long: checkpointDescription, + RunE: func(cmd *cobra.Command, args []string) error { + checkpointCommand.InputArgs = args + checkpointCommand.GlobalFlags = MainGlobalOpts + return checkpointCmd(&checkpointCommand) }, - cli.BoolFlag{ - Name: "leave-running, R", - Usage: "Leave the container running after writing checkpoint to disk", - }, - cli.BoolFlag{ - Name: "tcp-established", - Usage: "Checkpoint a container with established TCP connections", - }, - cli.BoolFlag{ - Name: "all, a", - Usage: "Checkpoint all running containers", - }, - LatestFlag, - } - checkpointCommand = cli.Command{ - Name: "checkpoint", - Usage: "Checkpoints one or more containers", - Description: checkpointDescription, - Flags: sortFlags(checkpointFlags), - Action: checkpointCmd, - ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", + Example: "CONTAINER-NAME [CONTAINER-NAME ...]", } ) -func checkpointCmd(c *cli.Context) error { +func init() { + checkpointCommand.Command = _checkpointCommand + + flags := checkpointCommand.Flags() + flags.BoolVarP(&checkpointCommand.Keep, "keep", "k", false, "Keep all temporary checkpoint files") + flags.BoolVarP(&checkpointCommand.LeaveRunning, "leave-running", "R", false, "Leave the container running after writing checkpoint to disk") + flags.BoolVar(&checkpointCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections") + flags.BoolVarP(&checkpointCommand.All, "all", "a", false, "Checkpoint all running containers") + flags.BoolVarP(&checkpointCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") +} + +func checkpointCmd(c *cliconfig.CheckpointValues) error { if rootless.IsRootless() { return errors.New("checkpointing a container requires root") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) options := libpod.ContainerCheckpointOptions{ - Keep: c.Bool("keep"), - KeepRunning: c.Bool("leave-running"), - TCPEstablished: c.Bool("tcp-established"), + Keep: c.Keep, + KeepRunning: c.LeaveRunning, + TCPEstablished: c.TcpEstablished, } - if err := checkAllAndLatest(c); err != nil { + if err := checkAllAndLatest(&c.PodmanCommand); err != nil { return err } - containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") + containers, lastError := getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStateRunning, "running") for _, ctr := range containers { if err = ctr.Checkpoint(context.TODO(), options); err != nil { diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go index bc4af9f50..1a7617d5c 100644 --- a/cmd/podman/cleanup.go +++ b/cmd/podman/cleanup.go @@ -4,50 +4,52 @@ import ( "fmt" "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - cleanupFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Cleans up all containers", - }, - LatestFlag, - } + cleanupCommand cliconfig.CleanupValues cleanupDescription = ` podman container cleanup Cleans up mount points and network stacks on one or more containers from the host. The container name or ID can be used. This command is used internally when running containers, but can also be used if container cleanup has failed when a container exits. ` - cleanupCommand = cli.Command{ - Name: "cleanup", - Usage: "Cleanup network and mountpoints of one or more containers", - Description: cleanupDescription, - Flags: sortFlags(cleanupFlags), - Action: cleanupCmd, - ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", - OnUsageError: usageErrorHandler, + _cleanupCommand = &cobra.Command{ + Use: "cleanup", + Short: "Cleanup network and mountpoints of one or more containers", + Long: cleanupDescription, + RunE: func(cmd *cobra.Command, args []string) error { + cleanupCommand.InputArgs = args + cleanupCommand.GlobalFlags = MainGlobalOpts + return cleanupCmd(&cleanupCommand) + }, + Example: "CONTAINER-NAME [CONTAINER-NAME ...]", } ) -func cleanupCmd(c *cli.Context) error { - if err := validateFlags(c, cleanupFlags); err != nil { - return err - } - runtime, err := libpodruntime.GetRuntime(c) +func init() { + cleanupCommand.Command = _cleanupCommand + flags := cleanupCommand.Flags() + + flags.BoolVarP(&cleanupCommand.All, "all", "a", false, "Cleans up all containers") + flags.BoolVarP(&cleanupCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") +} + +func cleanupCmd(c *cliconfig.CleanupValues) error { + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - if err := checkAllAndLatest(c); err != nil { + if err := checkAllAndLatest(&c.PodmanCommand); err != nil { return err } - cleanupContainers, lastError := getAllOrLatestContainers(c, runtime, -1, "all") + cleanupContainers, lastError := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all") ctx := getContext() diff --git a/cmd/podman/cliconfig/commands.go b/cmd/podman/cliconfig/commands.go new file mode 100644 index 000000000..7d1e762d9 --- /dev/null +++ b/cmd/podman/cliconfig/commands.go @@ -0,0 +1,109 @@ +package cliconfig + +// GlobalIsSet is a compatibility method for urfave +func (p *PodmanCommand) GlobalIsSet(opt string) bool { + flag := p.PersistentFlags().Lookup(opt) + if flag == nil { + return false + } + return flag.Changed +} + +// IsSet is a compatibility method for urfave +func (p *PodmanCommand) IsSet(opt string) bool { + flag := p.Flags().Lookup(opt) + if flag == nil { + return false + } + return flag.Changed +} + +// Bool is a compatibility method for urfave +func (p *PodmanCommand) Bool(opt string) bool { + flag := p.Flags().Lookup(opt) + if flag == nil { + return false + } + val, _ := p.Flags().GetBool(opt) + return val +} + +// String is a compatibility method for urfave +func (p *PodmanCommand) String(opt string) string { + flag := p.Flags().Lookup(opt) + if flag == nil { + return "" + } + val, _ := p.Flags().GetString(opt) + return val +} + +// StringArray is a compatibility method for urfave +func (p *PodmanCommand) StringArray(opt string) []string { + flag := p.Flags().Lookup(opt) + if flag == nil { + return []string{} + } + val, _ := p.Flags().GetStringArray(opt) + return val +} + +// StringSlice is a compatibility method for urfave +func (p *PodmanCommand) StringSlice(opt string) []string { + flag := p.Flags().Lookup(opt) + if flag == nil { + return []string{} + } + val, _ := p.Flags().GetStringSlice(opt) + return val +} + +// Int is a compatibility method for urfave +func (p *PodmanCommand) Int(opt string) int { + flag := p.Flags().Lookup(opt) + if flag == nil { + return 0 + } + val, _ := p.Flags().GetInt(opt) + return val +} + +// Unt is a compatibility method for urfave +func (p *PodmanCommand) Uint(opt string) uint { + flag := p.Flags().Lookup(opt) + if flag == nil { + return 0 + } + val, _ := p.Flags().GetUint(opt) + return val +} + +// Int64 is a compatibility method for urfave +func (p *PodmanCommand) Int64(opt string) int64 { + flag := p.Flags().Lookup(opt) + if flag == nil { + return 0 + } + val, _ := p.Flags().GetInt64(opt) + return val +} + +// Unt64 is a compatibility method for urfave +func (p *PodmanCommand) Uint64(opt string) uint64 { + flag := p.Flags().Lookup(opt) + if flag == nil { + return 0 + } + val, _ := p.Flags().GetUint64(opt) + return val +} + +// Float64 is a compatibility method for urfave +func (p *PodmanCommand) Float64(opt string) float64 { + flag := p.Flags().Lookup(opt) + if flag == nil { + return 0 + } + val, _ := p.Flags().GetFloat64(opt) + return val +} diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go new file mode 100644 index 000000000..b925d29ff --- /dev/null +++ b/cmd/podman/cliconfig/config.go @@ -0,0 +1,541 @@ +package cliconfig + +import ( + "github.com/spf13/cobra" +) + +type PodmanCommand struct { + *cobra.Command + InputArgs []string + GlobalFlags MainFlags +} + +type MainFlags struct { + CGroupManager string + CniConfigDir string + ConmonPath string + DefaultMountsFile string + HooksDir []string + MaxWorks int + Namespace string + Root string + Runroot string + Runtime string + StorageDriver string + StorageOpts []string + Syslog bool + + Config string + CpuProfile string + LogLevel string + TmpDir string +} + +type AttachValues struct { + PodmanCommand + DetachKeys string + Latest bool + NoStdin bool + SigProxy bool +} + +type ImagesValues struct { + PodmanCommand + All bool + Digests bool + Filter []string + Format string + Noheading bool + NoTrunc bool + Quiet bool + Sort string +} + +type TagValues struct { + PodmanCommand +} + +type WaitValues struct { + PodmanCommand + Interval uint + Latest bool +} + +type CheckpointValues struct { + PodmanCommand + Keep bool + LeaveRunning bool + TcpEstablished bool + All bool + Latest bool +} + +type CommitValues struct { + PodmanCommand + Change []string + Format string + Message string + Author string + Pause bool + Quiet bool +} + +type ContainersPrune struct { + PodmanCommand +} + +type DiffValues struct { + PodmanCommand + Archive bool + Format string +} + +type ExecValues struct { + PodmanCommand + Env []string + Privileged bool + Interfactive bool + Tty bool + User string + Latest bool + Workdir string +} + +type ImageExistsValues struct { + PodmanCommand +} + +type ContainerExistsValues struct { + PodmanCommand +} + +type PodExistsValues struct { + PodmanCommand +} + +type ExportValues struct { + PodmanCommand + Output string +} + +type GenerateKubeValues struct { + PodmanCommand + Service bool +} + +type HistoryValues struct { + PodmanCommand + Human bool + NoTrunc bool + Quiet bool + Format string +} +type PruneImagesValues struct { + PodmanCommand + All bool +} + +type ImportValues struct { + PodmanCommand + Change []string + Message string + Quiet bool +} + +type InfoValues struct { + PodmanCommand + Debug bool + Format string +} + +type InspectValues struct { + PodmanCommand + TypeObject string + Format string + Size bool + Latest bool +} + +type KillValues struct { + PodmanCommand + All bool + Signal string + Latest bool +} + +type LoadValues struct { + PodmanCommand + Input string + Quiet bool + SignaturePolicy string +} + +type LoginValues struct { + PodmanCommand + Password string + Username string + Authfile string + CertDir string + GetLogin bool + TlsVerify bool +} + +type LogoutValues struct { + PodmanCommand + Authfile string + All bool +} + +type LogsValues struct { + PodmanCommand + Details bool + Follow bool + Since string + Tail uint64 + Timestamps bool + Latest bool +} + +type MountValues struct { + PodmanCommand + All bool + Format string + NoTrunc bool + Latest bool +} + +type PauseValues struct { + PodmanCommand + All bool +} + +type KubePlayValues struct { + PodmanCommand + Authfile string + CertDir string + Creds string + Quiet bool + SignaturePolicy string + TlsVerify bool +} + +type PodCreateValues struct { + PodmanCommand + CgroupParent string + Infra bool + InfraImage string + InfraCommand string + LabelFile []string + Labels []string + Name string + PodIDFile string + Publish []string + Share string +} + +type PodInspectValues struct { + PodmanCommand + Latest bool +} + +type PodKillValues struct { + PodmanCommand + All bool + Signal string + Latest bool +} + +type PodPauseValues struct { + PodmanCommand + All bool + Latest bool +} + +type PodPsValues struct { + PodmanCommand + CtrNames bool + CtrIDs bool + CtrStatus bool + Filter string + Format string + Latest bool + Namespace bool + NoTrunc bool + Quiet bool + Sort string +} + +type PodRestartValues struct { + PodmanCommand + All bool + Latest bool +} + +type PodRmValues struct { + PodmanCommand + All bool + Force bool + Latest bool +} + +type PodStartValues struct { + PodmanCommand + All bool + Latest bool +} +type PodStatsValues struct { + PodmanCommand + All bool + NoStream bool + NoReset bool + Format string + Latest bool +} + +type PodStopValues struct { + PodmanCommand + All bool + Latest bool + Timeout uint +} + +type PodTopValues struct { + PodmanCommand + Latest bool + ListDescriptors bool +} +type PodUnpauseValues struct { + PodmanCommand + All bool + Latest bool +} + +type PortValues struct { + PodmanCommand + All bool + Latest bool +} + +type PsValues struct { + PodmanCommand + All bool + Filter []string + Format string + Last int + Latest bool + Namespace bool + NoTrunct bool + Pod bool + Quiet bool + Size bool + Sort string + Sync bool +} + +type PullValues struct { + PodmanCommand + Authfile string + CertDir string + Creds string + Quiet bool + SignaturePolicy string + TlsVerify bool +} + +type PushValues struct { + PodmanCommand + Authfile string + CertDir string + Compress bool + Creds string + Format string + Quiet bool + RemoveSignatures bool + SignBy string + SignaturePolicy string + TlsVerify bool +} + +type RefreshValues struct { + PodmanCommand +} + +type RestartValues struct { + PodmanCommand + All bool + Latest bool + Running bool + Timeout uint +} + +type RestoreValues struct { + PodmanCommand + All bool + Keep bool + Latest bool + TcpEstablished bool +} + +type RmValues struct { + PodmanCommand + All bool + Force bool + Latest bool + Volumes bool +} + +type RmiValues struct { + PodmanCommand + All bool + Force bool +} + +type RunlabelValues struct { + PodmanCommand + Authfile string + Display bool + CertDir string + Creds string + Name string + Opt1 string + Opt2 string + Opt3 string + Quiet bool + Pull bool + SignaturePolicy string + TlsVerify bool +} +type SaveValues struct { + PodmanCommand + Compress bool + Format string + Output string + Quiet bool +} + +type SearchValues struct { + PodmanCommand + Authfile string + Filter []string + Format string + Limit int + NoTrunc bool + TlsVerify bool +} + +type SignValues struct { + PodmanCommand + Directory string + SignBy string +} + +type StartValues struct { + PodmanCommand + Attach bool + DetachKeys string + Interactive bool + Latest bool + SigProxy bool +} + +type StatsValues struct { + PodmanCommand + All bool + Format string + Latest bool + NoReset bool + NoStream bool +} + +type StopValues struct { + PodmanCommand + All bool + Latest bool + Timeout uint +} + +type TopValues struct { + PodmanCommand + Latest bool + ListDescriptors bool +} + +type UmountValues struct { + PodmanCommand + All bool + Force bool + Latest bool +} + +type UnpauseValues struct { + PodmanCommand + All bool +} + +type VarlinkValues struct { + PodmanCommand + Timeout int64 +} + +type SetTrustValues struct { + PodmanCommand + PolicyPath string + PubKeysFile []string + TrustType string +} + +type ShowTrustValues struct { + PodmanCommand + Json bool + PolicyPath string + Raw bool + RegistryPath string +} + +type VersionValues struct { + PodmanCommand + Format string +} + +type VolumeCreateValues struct { + PodmanCommand + Driver string + Label []string + Opt []string +} +type VolumeInspectValues struct { + PodmanCommand + All bool + Format string +} + +type VolumeLsValues struct { + PodmanCommand + Filter string + Format string + Quiet bool +} + +type VolumePruneValues struct { + PodmanCommand + Force bool +} + +type VolumeRmValues struct { + PodmanCommand + All bool + Force bool +} + +type CleanupValues struct { + PodmanCommand + All bool + Latest bool +} + +type SystemPruneValues struct { + PodmanCommand + All bool + Force bool + Volume bool +} diff --git a/cmd/podman/cliconfig/create.go b/cmd/podman/cliconfig/create.go new file mode 100644 index 000000000..68ba4d857 --- /dev/null +++ b/cmd/podman/cliconfig/create.go @@ -0,0 +1,22 @@ +package cliconfig + +import ( + buildahcli "github.com/containers/buildah/pkg/cli" +) + +type CreateValues struct { + PodmanCommand +} + +type RunValues struct { + PodmanCommand +} + +type BuildValues struct { + PodmanCommand + *buildahcli.BudResults + *buildahcli.UserNSResults + *buildahcli.FromAndBudResults + *buildahcli.NameSpaceResults + *buildahcli.LayerResults +} diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 57126da4a..e1eba1f31 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -2,151 +2,106 @@ package main -import "github.com/urfave/cli" +import ( + "github.com/spf13/cobra" +) -func getAppCommands() []cli.Command { - return []cli.Command{ - attachCommand, - commitCommand, - buildCommand, - createCommand, - diffCommand, - execCommand, - killCommand, - kubeCommand, - loadCommand, - loginCommand, - logoutCommand, - logsCommand, - mountCommand, - pauseCommand, - psCommand, - podCommand, - portCommand, - pushCommand, - playCommand, - restartCommand, - rmCommand, - runCommand, - saveCommand, - searchCommand, - startCommand, - statsCommand, - stopCommand, - topCommand, - umountCommand, - unpauseCommand, - volumeCommand, - waitCommand, +func getImageSubCommands() []*cobra.Command { + return []*cobra.Command{ + _buildCommand, + _importCommand, + _loadCommand, + _pullCommand, + _rmiCommand, + _saveCommand, + _signCommand, } } -func getImageSubCommands() []cli.Command { - return []cli.Command{ - buildCommand, - importCommand, - loadCommand, - pullCommand, - saveCommand, - trustCommand, - signCommand, +func getContainerSubCommands() []*cobra.Command { + return []*cobra.Command{ + _attachCommand, + _checkpointCommand, + _cleanupCommand, + _containerExistsCommand, + _commitCommand, + _createCommand, + _diffCommand, + _execCommand, + _exportCommand, + _killCommand, + _logsCommand, + _psCommand, + _mountCommand, + _pauseCommand, + _portCommand, + _pruneContainersCommand, + _refreshCommand, + _restartCommand, + _restoreCommand, + _rmCommand, + _runCommmand, + _runlabelCommand, + _startCommand, + _statsCommand, + _stopCommand, + _topCommand, + _umountCommand, + _unpauseCommand, + _waitCommand, } } -func getSystemSubCommands() []cli.Command { - return []cli.Command{infoCommand} +func getPodSubCommands() []*cobra.Command { + return []*cobra.Command{ + _podCreateCommand, + _podExistsCommand, + _podInspectCommand, + _podKillCommand, + _podPauseCommand, + _podPsCommand, + _podRestartCommand, + _podRmCommand, + _podStartCommand, + _podStatsCommand, + _podStopCommand, + _podTopCommand, + _podUnpauseCommand, + } +} + +func getVolumeSubCommands() []*cobra.Command { + return []*cobra.Command{ + _volumeCreateCommand, + _volumeLsCommand, + _volumeRmCommand, + _volumeInspectCommand, + _volumePruneCommand, + } } -func getContainerSubCommands() []cli.Command { - return []cli.Command{ - attachCommand, - checkpointCommand, - cleanupCommand, - containerExistsCommand, - commitCommand, - createCommand, - diffCommand, - execCommand, - exportCommand, - killCommand, - logsCommand, - psCommand, - mountCommand, - pauseCommand, - portCommand, - pruneContainersCommand, - refreshCommand, - restartCommand, - restoreCommand, - rmCommand, - runCommand, - runlabelCommand, - startCommand, - statsCommand, - stopCommand, - topCommand, - umountCommand, - unpauseCommand, - // updateCommand, - waitCommand, +func getGenerateSubCommands() []*cobra.Command { + return []*cobra.Command{ + _containerKubeCommand, } } -func getMainAppFlags() []cli.Flag { - return []cli.Flag{ - cli.StringFlag{ - Name: "cgroup-manager", - Usage: "Cgroup manager to use (cgroupfs or systemd, default systemd)", - }, - cli.StringFlag{ - Name: "cni-config-dir", - Usage: "Path of the configuration directory for CNI networks", - }, - cli.StringFlag{ - Name: "conmon", - Usage: "Path of the conmon binary", - }, - cli.StringFlag{ - Name: "default-mounts-file", - Usage: "Path to default mounts file", - Hidden: true, - }, - cli.StringSliceFlag{ - Name: "hooks-dir", - Usage: "Set the OCI hooks directory path (may be set multiple times)", - }, - cli.IntFlag{ - Name: "max-workers", - Usage: "The maximum number of workers for parallel operations", - Hidden: true, - }, - cli.StringFlag{ - Name: "namespace", - Usage: "Set the libpod namespace, used to create separate views of the containers and pods on the system", - Value: "", - }, - cli.StringFlag{ - Name: "root", - Usage: "Path to the root directory in which data, including images, is stored", - }, - cli.StringFlag{ - Name: "runroot", - Usage: "Path to the 'run directory' where all state information is stored", - }, - cli.StringFlag{ - Name: "runtime", - Usage: "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc", - }, - cli.StringFlag{ - Name: "storage-driver, s", - Usage: "Select which storage driver is used to manage storage of images and containers (default is overlay)", - }, - cli.StringSliceFlag{ - Name: "storage-opt", - Usage: "Used to pass an option to the storage driver", - }, - cli.BoolFlag{ - Name: "syslog", - Usage: "Output logging information to syslog as well as the console", - }, + +func getPlaySubCommands() []*cobra.Command { + return []*cobra.Command{ + _playKubeCommand, + } +} + +func getTrustSubCommands() []*cobra.Command { + return []*cobra.Command{ + _setTrustCommand, + _showTrustCommand, + } +} + +func getSystemSubCommands() []*cobra.Command { + return []*cobra.Command{ + _infoCommand, + _pruneSystemCommand, } } diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go index 0adbd7b4c..80083eab9 100644 --- a/cmd/podman/commands_remoteclient.go +++ b/cmd/podman/commands_remoteclient.go @@ -2,24 +2,47 @@ package main -import "github.com/urfave/cli" +import ( + "github.com/spf13/cobra" +) -func getAppCommands() []cli.Command { - return []cli.Command{} +//import "github.com/urfave/cli" +// +func getAppCommands() []*cobra.Command { + return []*cobra.Command{} } -func getImageSubCommands() []cli.Command { - return []cli.Command{} +func getImageSubCommands() []*cobra.Command { + return []*cobra.Command{} } -func getContainerSubCommands() []cli.Command { - return []cli.Command{} +func getContainerSubCommands() []*cobra.Command { + return []*cobra.Command{} } -func getSystemSubCommands() []cli.Command { - return []cli.Command{} +func getPodSubCommands() []*cobra.Command { + return []*cobra.Command{} } -func getMainAppFlags() []cli.Flag { - return []cli.Flag{} +func getVolumeSubCommands() []*cobra.Command { + return []*cobra.Command{} +} + +func getGenerateSubCommands() []*cobra.Command { + return []*cobra.Command{} +} + +func getPlaySubCommands() []*cobra.Command { + return []*cobra.Command{} +} + +func getTrustSubCommands() []*cobra.Command { + return []*cobra.Command{} +} + +//func getMainAppFlags() []cli.Flag { +// return []cli.Flag{} +//} +func getSystemSubCommands() []*cobra.Command { + return []*cobra.Command{} } diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index 02ede4f73..6fd6b9761 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -2,6 +2,8 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" "io" "os" "strings" @@ -13,57 +15,43 @@ import ( "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" - "github.com/urfave/cli" ) var ( - commitFlags = []cli.Flag{ - cli.StringSliceFlag{ - Name: "change, c", - Usage: fmt.Sprintf("Apply the following possible instructions to the created image (default []): %s", strings.Join(libpod.ChangeCmds, " | ")), - }, - cli.StringFlag{ - Name: "format, f", - Usage: "`format` of the image manifest and metadata", - Value: "oci", - }, - cli.StringFlag{ - Name: "message, m", - Usage: "Set commit message for imported image", - }, - cli.StringFlag{ - Name: "author, a", - Usage: "Set the author for the image committed", - }, - cli.BoolTFlag{ - Name: "pause, p", - Usage: "Pause container during commit", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Suppress output", - }, - } + commitCommand cliconfig.CommitValues commitDescription = `Create an image from a container's changes. Optionally tag the image created, set the author with the --author flag, set the commit message with the --message flag, and make changes to the instructions with the --change flag.` - commitCommand = cli.Command{ - Name: "commit", - Usage: "Create new image based on the changed container", - Description: commitDescription, - Flags: sortFlags(commitFlags), - Action: commitCmd, - ArgsUsage: "CONTAINER [REPOSITORY[:TAG]]", - OnUsageError: usageErrorHandler, + + _commitCommand = &cobra.Command{ + Use: "commit", + Short: "Create new image based on the changed container", + Long: commitDescription, + RunE: func(cmd *cobra.Command, args []string) error { + commitCommand.InputArgs = args + commitCommand.GlobalFlags = MainGlobalOpts + return commitCmd(&commitCommand) + }, + Example: "CONTAINER [REPOSITORY[:TAG]]", } ) -func commitCmd(c *cli.Context) error { - if err := validateFlags(c, commitFlags); err != nil { - return err - } - runtime, err := libpodruntime.GetRuntime(c) +func init() { + commitCommand.Command = _commitCommand + flags := commitCommand.Flags() + flags.StringSliceVarP(&commitCommand.Change, "change", "c", []string{}, fmt.Sprintf("Apply the following possible instructions to the created image (default []): %s", strings.Join(libpod.ChangeCmds, " | "))) + flags.StringVarP(&commitCommand.Format, "format", "f", "oci", "`Format` of the image manifest and metadata") + flags.StringVarP(&commitCommand.Message, "message", "m", "", "Set commit message for imported image") + flags.StringVarP(&commitCommand.Author, "author", "a", "", "Set the author for the image committed") + flags.BoolVarP(&commitCommand.Pause, "pause", "p", false, "Pause container during commit") + flags.BoolVarP(&commitCommand.Quiet, "quiet", "q", false, "Suppress output") + + rootCmd.AddCommand(commitCommand.Command) +} + +func commitCmd(c *cliconfig.CommitValues) error { + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -73,26 +61,26 @@ func commitCmd(c *cli.Context) error { writer io.Writer mimeType string ) - args := c.Args() + args := c.InputArgs if len(args) != 2 { return errors.Errorf("you must provide a container name or ID and a target image name") } - switch c.String("format") { + switch c.Format { case "oci": mimeType = buildah.OCIv1ImageManifest - if c.IsSet("message") || c.IsSet("m") { + if c.Flag("message").Changed { return errors.Errorf("messages are only compatible with the docker image format (-f docker)") } case "docker": mimeType = manifest.DockerV2Schema2MediaType default: - return errors.Errorf("unrecognized image format %q", c.String("format")) + return errors.Errorf("unrecognized image format %q", c.Format) } container := args[0] reference := args[1] - if c.IsSet("change") || c.IsSet("c") { - for _, change := range c.StringSlice("change") { + if c.Flag("change").Changed { + for _, change := range c.Change { splitChange := strings.Split(strings.ToUpper(change), "=") if !util.StringInSlice(splitChange[0], libpod.ChangeCmds) { return errors.Errorf("invalid syntax for --change: %s", change) @@ -100,7 +88,7 @@ func commitCmd(c *cli.Context) error { } } - if !c.Bool("quiet") { + if !c.Quiet { writer = os.Stderr } ctr, err := runtime.LookupContainer(container) @@ -117,10 +105,10 @@ func commitCmd(c *cli.Context) error { } options := libpod.ContainerCommitOptions{ CommitOptions: coptions, - Pause: c.Bool("pause"), - Message: c.String("message"), - Changes: c.StringSlice("change"), - Author: c.String("author"), + Pause: c.Pause, + Message: c.Message, + Changes: c.Change, + Author: c.Author, } newImage, err := ctr.Commit(getContext(), reference, options) if err != nil { diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 82d60d62e..5fbdfce50 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -4,34 +4,19 @@ import ( "context" "fmt" "os" - "reflect" - "regexp" - "sort" "strings" "github.com/containers/buildah" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/containers/storage" "github.com/fatih/camelcase" "github.com/pkg/errors" - "github.com/urfave/cli" ) var ( - stores = make(map[storage.Store]struct{}) - LatestFlag = cli.BoolFlag{ - Name: "latest, l", - Usage: "Act on the latest container podman is aware of", - } - LatestPodFlag = cli.BoolFlag{ - Name: "latest, l", - Usage: "Act on the latest pod podman is aware of", - } - WorkDirFlag = cli.StringFlag{ - Name: "workdir, w", - Usage: "Working directory inside the container", - } + stores = make(map[storage.Store]struct{}) ) const ( @@ -50,53 +35,9 @@ func shortID(id string) string { return id } -func usageErrorHandler(context *cli.Context, err error, _ bool) error { - cmd := context.App.Name - if len(context.Command.Name) > 0 { - cmd = cmd + " " + context.Command.Name - } - return fmt.Errorf("%s\nSee '%s --help'.", err, cmd) -} - -func commandNotFoundHandler(context *cli.Context, command string) { - fmt.Fprintf(os.Stderr, "Command %q not found.\nSee `%s --help`.\n", command, context.App.Name) - os.Exit(exitCode) -} - -// validateFlags searches for StringFlags or StringSlice flags that never had -// a value set. This commonly occurs when the CLI mistakenly takes the next -// option and uses it as a value. -func validateFlags(c *cli.Context, flags []cli.Flag) error { - for _, flag := range flags { - switch reflect.TypeOf(flag).String() { - case "cli.StringSliceFlag": - { - f := flag.(cli.StringSliceFlag) - name := strings.Split(f.Name, ",") - val := c.StringSlice(name[0]) - for _, v := range val { - if ok, _ := regexp.MatchString("^-.+", v); ok { - return errors.Errorf("option --%s requires a value", name[0]) - } - } - } - case "cli.StringFlag": - { - f := flag.(cli.StringFlag) - name := strings.Split(f.Name, ",") - val := c.String(name[0]) - if ok, _ := regexp.MatchString("^-.+", val); ok { - return errors.Errorf("option --%s requires a value", name[0]) - } - } - } - } - return nil -} - // checkAllAndLatest checks that --all and --latest are used correctly -func checkAllAndLatest(c *cli.Context) error { - argLen := len(c.Args()) +func checkAllAndLatest(c *cliconfig.PodmanCommand) error { + argLen := len(c.InputArgs) if (c.Bool("all") || c.Bool("latest")) && argLen > 0 { return errors.Errorf("no arguments are needed with --all or --latest") } @@ -117,7 +58,7 @@ func checkAllAndLatest(c *cli.Context) error { // is desired a -1 can be used to get all containers. For a better // error message, if the filter fails, a corresponding verb can be // specified which will then appear in the error message. -func getAllOrLatestContainers(c *cli.Context, runtime *libpod.Runtime, filterState libpod.ContainerStatus, verb string) ([]*libpod.Container, error) { +func getAllOrLatestContainers(c *cliconfig.PodmanCommand, runtime *libpod.Runtime, filterState libpod.ContainerStatus, verb string) ([]*libpod.Container, error) { var containers []*libpod.Container var lastError error var err error @@ -142,7 +83,7 @@ func getAllOrLatestContainers(c *cli.Context, runtime *libpod.Runtime, filterSta } containers = append(containers, lastCtr) } else { - args := c.Args() + args := c.InputArgs for _, i := range args { container, err := runtime.LookupContainer(i) if err != nil { @@ -173,363 +114,367 @@ func getDefaultNetwork() string { return "bridge" } -// Common flags shared between commands -var createFlags = []cli.Flag{ - cli.StringSliceFlag{ - Name: "add-host", - Usage: "Add a custom host-to-IP mapping (host:ip) (default [])", - }, - cli.StringSliceFlag{ - Name: "annotation", - Usage: "Add annotations to container (key:value) (default [])", - }, - cli.StringSliceFlag{ - Name: "attach, a", - Usage: "Attach to STDIN, STDOUT or STDERR (default [])", - }, - cli.StringFlag{ - Name: "blkio-weight", - Usage: "Block IO weight (relative weight) accepts a weight value between 10 and 1000.", - }, - cli.StringSliceFlag{ - Name: "blkio-weight-device", - Usage: "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)", - }, - cli.StringSliceFlag{ - Name: "cap-add", - Usage: "Add capabilities to the container", - }, - cli.StringSliceFlag{ - Name: "cap-drop", - Usage: "Drop capabilities from the container", - }, - cli.StringFlag{ - Name: "cgroup-parent", - Usage: "Optional parent cgroup for the container", - }, - cli.StringFlag{ - Name: "cidfile", - Usage: "Write the container ID to the file", - }, - cli.StringFlag{ - Name: "conmon-pidfile", - Usage: "Path to the file that will receive the PID of conmon", - }, - cli.Uint64Flag{ - Name: "cpu-period", - Usage: "Limit the CPU CFS (Completely Fair Scheduler) period", - }, - cli.Int64Flag{ - Name: "cpu-quota", - Usage: "Limit the CPU CFS (Completely Fair Scheduler) quota", - }, - cli.Uint64Flag{ - Name: "cpu-rt-period", - Usage: "Limit the CPU real-time period in microseconds", - }, - cli.Int64Flag{ - Name: "cpu-rt-runtime", - Usage: "Limit the CPU real-time runtime in microseconds", - }, - cli.Uint64Flag{ - Name: "cpu-shares", - Usage: "CPU shares (relative weight)", - }, - cli.Float64Flag{ - Name: "cpus", - Usage: "Number of CPUs. The default is 0.000 which means no limit", - }, - cli.StringFlag{ - Name: "cpuset-cpus", - Usage: "CPUs in which to allow execution (0-3, 0,1)", - }, - cli.StringFlag{ - Name: "cpuset-mems", - Usage: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", - }, - cli.BoolFlag{ - Name: "detach, d", - Usage: "Run container in background and print container ID", - }, - cli.StringFlag{ - Name: "detach-keys", - Usage: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`", - }, - cli.StringSliceFlag{ - Name: "device", - Usage: "Add a host device to the container (default [])", - }, - cli.StringSliceFlag{ - Name: "device-read-bps", - Usage: "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)", - }, - cli.StringSliceFlag{ - Name: "device-read-iops", - Usage: "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)", - }, - cli.StringSliceFlag{ - Name: "device-write-bps", - Usage: "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", - }, - cli.StringSliceFlag{ - Name: "device-write-iops", - Usage: "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", - }, - cli.StringSliceFlag{ - Name: "dns", - Usage: "Set custom DNS servers", - }, - cli.StringSliceFlag{ - Name: "dns-opt", - Usage: "Set custom DNS options", - }, - cli.StringSliceFlag{ - Name: "dns-search", - Usage: "Set custom DNS search domains", - }, - cli.StringFlag{ - Name: "entrypoint", - Usage: "Overwrite the default ENTRYPOINT of the image", - }, - cli.StringSliceFlag{ - Name: "env, e", - Usage: "Set environment variables in container", - }, - cli.StringSliceFlag{ - Name: "env-file", - Usage: "Read in a file of environment variables", - }, - cli.StringSliceFlag{ - Name: "expose", - Usage: "Expose a port or a range of ports (default [])", - }, - cli.StringSliceFlag{ - Name: "gidmap", - Usage: "GID map to use for the user namespace", - }, - cli.StringSliceFlag{ - Name: "group-add", - Usage: "Add additional groups to join (default [])", - }, - cli.BoolFlag{ - Name: "help", - Hidden: true, - }, - cli.StringFlag{ - Name: "hostname, h", - Usage: "Set container hostname", - }, - cli.StringFlag{ - Name: "image-volume, builtin-volume", - Usage: "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind')", - Value: "bind", - }, - cli.BoolFlag{ - Name: "init", - Usage: "Run an init binary inside the container that forwards signals and reaps processes", - }, - cli.StringFlag{ - Name: "init-path", +func getCreateFlags(c *cliconfig.PodmanCommand) { + + createFlags := c.Flags() + + createFlags.StringSlice( + "add-host", []string{}, + "Add a custom host-to-IP mapping (host:ip) (default [])", + ) + createFlags.StringSlice( + "annotation", []string{}, + "Add annotations to container (key:value) (default [])", + ) + createFlags.StringSliceP( + "attach", "a", []string{}, + "Attach to STDIN, STDOUT or STDERR (default [])", + ) + createFlags.String( + "blkio-weight", "", + "Block IO weight (relative weight) accepts a weight value between 10 and 1000.", + ) + createFlags.StringSlice( + "blkio-weight-device", []string{}, + "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)", + ) + createFlags.StringSlice( + "cap-add", []string{}, + "Add capabilities to the container", + ) + createFlags.StringSlice( + "cap-drop", []string{}, + "Drop capabilities from the container", + ) + createFlags.String( + "cgroup-parent", "", + "Optional parent cgroup for the container", + ) + createFlags.String( + "cidfile", "", + "Write the container ID to the file", + ) + createFlags.String( + "conmon-pidfile", "", + "Path to the file that will receive the PID of conmon", + ) + createFlags.Uint64( + "cpu-period", 0, + "Limit the CPU CFS (Completely Fair Scheduler) period", + ) + createFlags.Int64( + "cpu-quota", 0, + "Limit the CPU CFS (Completely Fair Scheduler) quota", + ) + createFlags.Uint64( + "cpu-rt-period", 0, + "Limit the CPU real-time period in microseconds", + ) + createFlags.Int64( + "cpu-rt-runtime", 0, + "Limit the CPU real-time runtime in microseconds", + ) + createFlags.Uint64( + "cpu-shares", 0, + "CPU shares (relative weight)", + ) + createFlags.Float64( + "cpus", 0, + "Number of CPUs. The default is 0.000 which means no limit", + ) + createFlags.String( + "cpuset-cpus", "", + "CPUs in which to allow execution (0-3, 0,1)", + ) + createFlags.String( + "cpuset-mems", "", + "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", + ) + createFlags.BoolP( + "detach", "d", false, + "Run container in background and print container ID", + ) + createFlags.String( + "detach-keys", "", + "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`", + ) + createFlags.StringSlice( + "device", []string{}, + "Add a host device to the container (default [])", + ) + createFlags.StringSlice( + "device-read-bps", []string{}, + "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)", + ) + createFlags.StringSlice( + "device-read-iops", []string{}, + "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)", + ) + createFlags.StringSlice( + "device-write-bps", []string{}, + "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", + ) + createFlags.StringSlice( + "device-write-iops", []string{}, + "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", + ) + createFlags.StringSlice( + "dns", []string{}, + "Set custom DNS servers", + ) + createFlags.StringSlice( + "dns-opt", []string{}, + "Set custom DNS options", + ) + createFlags.StringSlice( + "dns-search", []string{}, + "Set custom DNS search domains", + ) + createFlags.String( + "entrypoint", "", + "Overwrite the default ENTRYPOINT of the image", + ) + createFlags.StringSliceP( + "env", "e", []string{}, + "Set environment variables in container", + ) + createFlags.StringSlice( + "env-file", []string{}, + "Read in a file of environment variables", + ) + createFlags.StringSlice( + "expose", []string{}, + "Expose a port or a range of ports (default [])", + ) + createFlags.StringSlice( + "gidmap", []string{}, + "GID map to use for the user namespace", + ) + createFlags.StringSlice( + "group-add", []string{}, + "Add additional groups to join (default [])", + ) + createFlags.Bool( + "help", false, "", + ) + + createFlags.StringP( + "hostname", "h", "", + "Set container hostname", + ) + createFlags.String( + "image-volume", "bind", + "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind')", + ) + createFlags.Bool( + "init", false, + "Run an init binary inside the container that forwards signals and reaps processes", + ) + createFlags.String( + "init-path", "", // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) - Usage: fmt.Sprintf("Path to the container-init binary (default: %q)", libpod.DefaultInitPath), - }, - cli.BoolFlag{ - Name: "interactive, i", - Usage: "Keep STDIN open even if not attached", - }, - cli.StringFlag{ - Name: "ip", - Usage: "Specify a static IPv4 address for the container", - }, - cli.StringFlag{ - Name: "ipc", - Usage: "IPC namespace to use", - }, - cli.StringFlag{ - Name: "kernel-memory", - Usage: "Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)", - }, - cli.StringSliceFlag{ - Name: "label", - Usage: "Set metadata on container (default [])", - }, - cli.StringSliceFlag{ - Name: "label-file", - Usage: "Read in a line delimited file of labels (default [])", - }, - cli.StringFlag{ - Name: "log-driver", - Usage: "Logging driver for the container", - }, - cli.StringSliceFlag{ - Name: "log-opt", - Usage: "Logging driver options (default [])", - }, - cli.StringFlag{ - Name: "mac-address", - Usage: "Container MAC address (e.g. 92:d0:c6:0a:29:33), not currently supported", - }, - cli.StringFlag{ - Name: "memory, m", - Usage: "Memory limit (format: <number>[<unit>], where unit = b, k, m or g)", - }, - cli.StringFlag{ - Name: "memory-reservation", - Usage: "Memory soft limit (format: <number>[<unit>], where unit = b, k, m or g)", - }, - cli.StringFlag{ - Name: "memory-swap", - Usage: "Swap limit equal to memory plus swap: '-1' to enable unlimited swap", - }, - cli.Int64Flag{ - Name: "memory-swappiness", - Usage: "Tune container memory swappiness (0 to 100) (default -1)", - Value: -1, - }, - cli.StringFlag{ - Name: "name", - Usage: "Assign a name to the container", - }, - cli.StringFlag{ - Name: "net, network", - Usage: "Connect a container to a network", - Value: getDefaultNetwork(), - }, - cli.BoolFlag{ - Name: "oom-kill-disable", - Usage: "Disable OOM Killer", - }, - cli.StringFlag{ - Name: "oom-score-adj", - Usage: "Tune the host's OOM preferences (-1000 to 1000)", - }, - cli.StringFlag{ - Name: "pid", - Usage: "PID namespace to use", - }, - cli.Int64Flag{ - Name: "pids-limit", - Usage: "Tune container pids limit (set -1 for unlimited)", - }, - cli.StringFlag{ - Name: "pod", - Usage: "Run container in an existing pod", - }, - cli.BoolFlag{ - Name: "privileged", - Usage: "Give extended privileges to container", - }, - cli.StringSliceFlag{ - Name: "publish, p", - Usage: "Publish a container's port, or a range of ports, to the host (default [])", - }, - cli.BoolFlag{ - Name: "publish-all, P", - Usage: "Publish all exposed ports to random ports on the host interface", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Suppress output information when pulling images", - }, - cli.BoolFlag{ - Name: "read-only", - Usage: "Make containers root filesystem read-only", - }, - cli.StringFlag{ - Name: "restart", - Usage: "Restart is not supported. Please use a systemd unit file for restart", - }, - cli.BoolFlag{ - Name: "rm", - Usage: "Remove container (and pod if created) after exit", - }, - cli.BoolFlag{ - Name: "rootfs", - Usage: "The first argument is not an image but the rootfs to the exploded container", - }, - cli.StringSliceFlag{ - Name: "security-opt", - Usage: "Security Options (default [])", - }, - cli.StringFlag{ - Name: "shm-size", - Usage: "Size of `/dev/shm`. The format is `<number><unit>`.", - Value: "65536k", - }, - cli.StringFlag{ - Name: "stop-signal", - Usage: "Signal to stop a container. Default is SIGTERM", - }, - cli.IntFlag{ - Name: "stop-timeout", - Usage: "Timeout (in seconds) to stop a container. Default is 10", - Value: libpod.CtrRemoveTimeout, - }, - cli.StringSliceFlag{ - Name: "storage-opt", - Usage: "Storage driver options per container (default [])", - }, - cli.StringFlag{ - Name: "subgidname", - Usage: "Name of range listed in /etc/subgid for use in user namespace", - }, - cli.StringFlag{ - Name: "subuidname", - Usage: "Name of range listed in /etc/subuid for use in user namespace", - }, + fmt.Sprintf("Path to the container-init binary (default: %q)", libpod.DefaultInitPath), + ) + createFlags.BoolP( + "interactive", "i", false, + "Keep STDIN open even if not attached", + ) + createFlags.String( + "ip", "", + "Specify a static IPv4 address for the container", + ) + createFlags.String( + "ipc", "", + "IPC namespace to use", + ) + createFlags.String( + "kernel-memory", "", + "Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)", + ) + createFlags.StringSlice( + "label", []string{}, + "Set metadata on container (default [])", + ) + createFlags.StringSlice( + "label-file", []string{}, + "Read in a line delimited file of labels (default [])", + ) + createFlags.String( + "log-driver", "", + "Logging driver for the container", + ) + createFlags.StringSlice( + "log-opt", []string{}, + "Logging driver options (default [])", + ) + createFlags.String( + "mac-address", "", + "Container MAC address (e.g. 92:d0:c6:0a:29:33), not currently supported", + ) + createFlags.StringP( + "memory", "m", "", + "Memory limit (format: <number>[<unit>], where unit = b, k, m or g)", + ) + createFlags.String( + "memory-reservation", "", + "Memory soft limit (format: <number>[<unit>], where unit = b, k, m or g)", + ) + createFlags.String( + "memory-swap", "", + "Swap limit equal to memory plus swap: '-1' to enable unlimited swap", + ) + createFlags.Int64( + "memory-swappiness", -1, + "Tune container memory swappiness (0 to 100) (default -1)", + ) + createFlags.String( + "name", "", + "Assign a name to the container", + ) + createFlags.String( + "net", getDefaultNetwork(), + "Connect a container to a network", + ) + createFlags.String( + "network", getDefaultNetwork(), + "Connect a container to a network", + ) + createFlags.Bool( + "oom-kill-disable", false, + "Disable OOM Killer", + ) + createFlags.Int( + "oom-score-adj", 0, + "Tune the host's OOM preferences (-1000 to 1000)", + ) + createFlags.String( + "pid", "", + "PID namespace to use", + ) + createFlags.Int64( + "pids-limit", 0, + "Tune container pids limit (set -1 for unlimited)", + ) + createFlags.String( + "pod", "", + "Run container in an existing pod", + ) + createFlags.Bool( + "privileged", false, + "Give extended privileges to container", + ) + createFlags.StringSliceP( + "publish", "p", []string{}, + "Publish a container's port, or a range of ports, to the host (default [])", + ) + createFlags.BoolP( + "publish-all", "P", false, + "Publish all exposed ports to random ports on the host interface", + ) + createFlags.BoolP( + "quiet", "q", false, + "Suppress output information when pulling images", + ) + createFlags.Bool( + "read-only", false, + "Make containers root filesystem read-only", + ) + createFlags.String( + "restart", "", + "Restart is not supported. Please use a systemd unit file for restart", + ) + createFlags.Bool( + "rm", false, + "Remove container (and pod if created) after exit", + ) + createFlags.Bool( + "rootfs", false, + "The first argument is not an image but the rootfs to the exploded container", + ) + createFlags.StringArray( + "security-opt", []string{}, + "Security Options (default [])", + ) + createFlags.String( + "shm-size", "65536k", + "Size of `/dev/shm`. The format is `<number><unit>`", + ) + createFlags.String( + "stop-signal", "", + "Signal to stop a container. Default is SIGTERM", + ) + createFlags.Int( + "stop-timeout", libpod.CtrRemoveTimeout, + "Timeout (in seconds) to stop a container. Default is 10", + ) + createFlags.StringSlice( + "storage-opt", []string{}, + "Storage driver options per container (default [])", + ) + createFlags.String( + "subgidname", "", + "Name of range listed in /etc/subgid for use in user namespace", + ) + createFlags.String( + "subuidname", "", + "Name of range listed in /etc/subuid for use in user namespace", + ) - cli.StringSliceFlag{ - Name: "sysctl", - Usage: "Sysctl options (default [])", - }, - cli.BoolTFlag{ - Name: "systemd", - Usage: "Run container in systemd mode if the command executable is systemd or init", - }, - cli.StringSliceFlag{ - Name: "tmpfs", - Usage: "Mount a temporary filesystem (`tmpfs`) into a container (default [])", - }, - cli.BoolFlag{ - Name: "tty, t", - Usage: "Allocate a pseudo-TTY for container", - }, - cli.StringSliceFlag{ - Name: "uidmap", - Usage: "UID map to use for the user namespace", - }, - cli.StringSliceFlag{ - Name: "ulimit", - Usage: "Ulimit options (default [])", - }, - cli.StringFlag{ - Name: "user, u", - Usage: "Username or UID (format: <name|uid>[:<group|gid>])", - }, - cli.StringFlag{ - Name: "userns", - Usage: "User namespace to use", - }, - cli.StringFlag{ - Name: "uts", - Usage: "UTS namespace to use", - }, - cli.StringSliceFlag{ - Name: "mount", - Usage: "Attach a filesystem mount to the container (default [])", - }, - cli.StringSliceFlag{ - Name: "volume, v", - Usage: "Bind mount a volume into the container (default [])", - }, - cli.StringSliceFlag{ - Name: "volumes-from", - Usage: "Mount volumes from the specified container(s) (default [])", - }, - WorkDirFlag, + createFlags.StringSlice( + "sysctl", []string{}, + "Sysctl options (default [])", + ) + createFlags.Bool( + "systemd", true, + "Run container in systemd mode if the command executable is systemd or init", + ) + createFlags.StringSlice( + "tmpfs", []string{}, + "Mount a temporary filesystem (`tmpfs`) into a container (default [])", + ) + createFlags.BoolP( + "tty", "t", false, + "Allocate a pseudo-TTY for container", + ) + createFlags.StringSlice( + "uidmap", []string{}, + "UID map to use for the user namespace", + ) + createFlags.StringSlice( + "ulimit", []string{}, + "Ulimit options (default [])", + ) + createFlags.StringP( + "user", "u", "", + "Username or UID (format: <name|uid>[:<group|gid>])", + ) + createFlags.String( + "userns", "", + "User namespace to use", + ) + createFlags.String( + "uts", "", + "UTS namespace to use", + ) + createFlags.StringArray( + "mount", []string{}, + "Attach a filesystem mount to the container (default [])", + ) + createFlags.StringArrayP( + "volume", "v", []string{}, + "Bind mount a volume into the container (default [])", + ) + createFlags.StringSlice( + "volumes-from", []string{}, + "Mount volumes from the specified container(s) (default [])", + ) + createFlags.StringP( + "workdir", "w", "", + "Working directory inside the container", + ) } -func getFormat(c *cli.Context) (string, error) { +func getFormat(c *cliconfig.PodmanCommand) (string, error) { format := strings.ToLower(c.String("format")) if strings.HasPrefix(format, buildah.OCI) { return buildah.OCIv1ImageManifest, nil @@ -541,13 +486,6 @@ func getFormat(c *cli.Context) (string, error) { return "", errors.Errorf("unrecognized image type %q", format) } -func sortFlags(flags []cli.Flag) []cli.Flag { - sort.Slice(flags, func(i, j int) bool { - return strings.Compare(flags[i].GetName(), flags[j].GetName()) < 0 - }) - return flags -} - func getAuthFile(authfile string) string { if authfile != "" { return authfile diff --git a/cmd/podman/container.go b/cmd/podman/container.go index 29300a6a4..969cb2dc8 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -1,30 +1,21 @@ package main import ( - "sort" - - "github.com/urfave/cli" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" ) -var ( - containerSubCommands = []cli.Command{ - exportCommand, - inspectCommand, - } - containerDescription = "Manage containers" - containerCommand = cli.Command{ - Name: "container", - Usage: "Manage Containers", - Description: containerDescription, - ArgsUsage: "", - Subcommands: getContainerSubCommandsSorted(), - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, - } -) +var containerDescription = "Manage containers" +var containerCommand = cliconfig.PodmanCommand{ + Command: &cobra.Command{ + Use: "container", + Short: "Manage Containers", + Long: containerDescription, + TraverseChildren: true, + }, +} -func getContainerSubCommandsSorted() []cli.Command { - containerSubCommands = append(containerSubCommands, getContainerSubCommands()...) - sort.Sort(commandSortedAlpha{containerSubCommands}) - return containerSubCommands +func init() { + containerCommand.AddCommand(getContainerSubCommands()...) + rootCmd.AddCommand(containerCommand.Command) } diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go index 09141e9a3..3f9b46035 100644 --- a/cmd/podman/containers_prune.go +++ b/cmd/podman/containers_prune.go @@ -3,30 +3,39 @@ package main import ( "context" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + pruneContainersCommand cliconfig.ContainersPrune pruneContainersDescription = ` podman container prune Removes all exited containers ` - pruneContainersCommand = cli.Command{ - Name: "prune", - Usage: "Remove all stopped containers", - Description: pruneContainersDescription, - Action: pruneContainersCmd, - OnUsageError: usageErrorHandler, + _pruneContainersCommand = &cobra.Command{ + Use: "prune", + Short: "Remove all stopped containers", + Long: pruneContainersDescription, + RunE: func(cmd *cobra.Command, args []string) error { + pruneContainersCommand.InputArgs = args + pruneContainersCommand.GlobalFlags = MainGlobalOpts + return pruneContainersCmd(&pruneContainersCommand) + }, } ) +func init() { + pruneContainersCommand.Command = _pruneContainersCommand +} + func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWorkers int, force bool) error { var deleteFuncs []shared.ParallelWorkerInput @@ -60,8 +69,8 @@ func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWork return printParallelOutput(deleteErrors, errCount) } -func pruneContainersCmd(c *cli.Context) error { - runtime, err := adapter.GetRuntime(c) +func pruneContainersCmd(c *cliconfig.ContainersPrune) error { + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -69,7 +78,7 @@ func pruneContainersCmd(c *cli.Context) error { maxWorkers := shared.Parallelize("rm") if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalInt("max-workers") + maxWorkers = c.GlobalFlags.MaxWorks } logrus.Debugf("Setting maximum workers to %d", maxWorkers) diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 2d85abd35..2f8742052 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -12,6 +12,7 @@ import ( "strings" "syscall" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" @@ -29,37 +30,46 @@ import ( "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + createCommand cliconfig.CreateValues + createDescription = "Creates a new container from the given image or" + + " storage and prepares it for running the specified command. The" + + " container ID is then printed to stdout. You can then start it at" + + " any time with the podman start <container_id> command. The container" + + " will be created with the initial state 'created'." + _createCommand = &cobra.Command{ + Use: "create", + Short: "Create but do not start a container", + Long: createDescription, + RunE: func(cmd *cobra.Command, args []string) error { + createCommand.InputArgs = args + createCommand.GlobalFlags = MainGlobalOpts + return createCmd(&createCommand) + }, + Example: "IMAGE [COMMAND [ARG...]]", + } + defaultEnvVariables = map[string]string{ "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM": "xterm", } ) -var createDescription = "Creates a new container from the given image or" + - " storage and prepares it for running the specified command. The" + - " container ID is then printed to stdout. You can then start it at" + - " any time with the podman start <container_id> command. The container" + - " will be created with the initial state 'created'." - -var createCommand = cli.Command{ - Name: "create", - Usage: "Create but do not start a container", - Description: createDescription, - Flags: sortFlags(createFlags), - Action: createCmd, - ArgsUsage: "IMAGE [COMMAND [ARG...]]", - HideHelp: true, - SkipArgReorder: true, - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, +func init() { + createCommand.PodmanCommand.Command = _createCommand + + getCreateFlags(&createCommand.PodmanCommand) + flags := createCommand.Flags() + flags.SetInterspersed(true) + + rootCmd.AddCommand(createCommand.Command) } -func createCmd(c *cli.Context) error { - if err := createInit(c); err != nil { +func createCmd(c *cliconfig.CreateValues) error { + if err := createInit(&c.PodmanCommand); err != nil { return err } @@ -67,13 +77,13 @@ func createCmd(c *cli.Context) error { rootless.SetSkipStorageSetup(true) } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - ctr, _, err := createContainer(c, runtime) + ctr, _, err := createContainer(&c.PodmanCommand, runtime) if err != nil { return err } @@ -82,33 +92,24 @@ func createCmd(c *cli.Context) error { return nil } -func createInit(c *cli.Context) error { - // TODO should allow user to create based off a directory on the host not just image - // Need CLI support for this - +func createInit(c *cliconfig.PodmanCommand) error { // Docker-compatibility: the "-h" flag for run/create is reserved for // the hostname (see https://github.com/containers/libpod/issues/1367). - if c.Bool("help") { - cli.ShowCommandHelpAndExit(c, "run", 0) - } - - if err := validateFlags(c, createFlags); err != nil { - return err - } - if len(c.Args()) < 1 { + if len(c.InputArgs) < 1 { return errors.Errorf("image name or ID is required") } return nil } -func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) { +func createContainer(c *cliconfig.PodmanCommand, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) { + rtc := runtime.GetConfig() ctx := getContext() rootfs := "" if c.Bool("rootfs") { - rootfs = c.Args()[0] + rootfs = c.InputArgs[0] } var err error @@ -134,7 +135,7 @@ func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container writer = os.Stderr } - newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, false, nil) + newImage, err := runtime.ImageRuntime().New(ctx, c.InputArgs[0], rtc.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, false, nil) if err != nil { return nil, nil, err } @@ -264,7 +265,7 @@ func isPortInImagePorts(exposedPorts map[string]struct{}, port string) bool { return false } -func configureEntrypoint(c *cli.Context, data *inspect.ImageData) []string { +func configureEntrypoint(c *cliconfig.PodmanCommand, data *inspect.ImageData) []string { entrypoint := []string{} if c.IsSet("entrypoint") { // Force entrypoint to "" @@ -284,7 +285,7 @@ func configureEntrypoint(c *cli.Context, data *inspect.ImageData) []string { return entrypoint } -func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) { +func configurePod(c *cliconfig.PodmanCommand, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) { pod, err := runtime.LookupPod(podName) if err != nil { return namespaces, err @@ -296,7 +297,7 @@ func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string if (namespaces["pid"] == cc.Pod) || (!c.IsSet("pid") && pod.SharesPID()) { namespaces["pid"] = fmt.Sprintf("container:%s", podInfraID) } - if (namespaces["net"] == cc.Pod) || (!c.IsSet("net") && pod.SharesNet()) { + if (namespaces["net"] == cc.Pod) || (!c.IsSet("net") && !c.IsSet("network") && pod.SharesNet()) { namespaces["net"] = fmt.Sprintf("container:%s", podInfraID) } if (namespaces["user"] == cc.Pod) || (!c.IsSet("user") && pod.SharesUser()) { @@ -313,7 +314,7 @@ func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string // Parses CLI options related to container creation into a config which can be // parsed into an OCI runtime spec -func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*cc.CreateConfig, error) { +func parseCreateOpts(ctx context.Context, c *cliconfig.PodmanCommand, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*cc.CreateConfig, error) { var ( inputCommand, command []string memoryLimit, memoryReservation, memorySwap, memoryKernel int64 @@ -335,14 +336,14 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim imageID := "" - inputCommand = c.Args()[1:] + inputCommand = c.InputArgs[1:] if data != nil { imageID = data.ID } rootfs := "" if c.Bool("rootfs") { - rootfs = c.Args()[0] + rootfs = c.InputArgs[0] } sysctl, err := validateSysctl(c.StringSlice("sysctl")) @@ -382,15 +383,15 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim blkioWeight = uint16(u) } var mountList []spec.Mount - if mountList, err = parseMounts(c.StringSlice("mount")); err != nil { + if mountList, err = parseMounts(c.StringArray("mount")); err != nil { return nil, err } - if err = parseVolumes(c.StringSlice("volume")); err != nil { + if err = parseVolumes(c.StringArray("volume")); err != nil { return nil, err } - if err = parseVolumesFrom(c.StringSlice("volumes-from")); err != nil { + if err = parseVolumesFrom(c.StringArray("volumes-from")); err != nil { return nil, err } @@ -399,10 +400,10 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim if c.Bool("detach") && c.Bool("rm") { return nil, errors.Errorf("--rm and --detach cannot be specified together") } - if c.Int64("cpu-period") != 0 && c.Float64("cpus") > 0 { + if c.Flag("cpu-period").Changed && c.Flag("cpus").Changed { return nil, errors.Errorf("--cpu-period and --cpus cannot be set together") } - if c.Int64("cpu-quota") != 0 && c.Float64("cpus") > 0 { + if c.Flag("cpu-quota").Changed && c.Flag("cpus").Changed { return nil, errors.Errorf("--cpu-quota and --cpus cannot be set together") } @@ -420,9 +421,13 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim // Instead of integrating here, should be done in libpod // However, that also involves setting up security opts // when the pod's namespace is integrated + namespaceNet := c.String("network") + if c.Flag("net").Changed { + namespaceNet = c.String("net") + } namespaces = map[string]string{ "pid": c.String("pid"), - "net": c.String("net"), + "net": namespaceNet, "ipc": c.String("ipc"), "user": c.String("userns"), "uts": c.String("uts"), @@ -654,7 +659,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim } var systemd bool - if command != nil && c.BoolT("systemd") && ((filepath.Base(command[0]) == "init") || (filepath.Base(command[0]) == "systemd")) { + if command != nil && c.Bool("systemd") && ((filepath.Base(command[0]) == "init") || (filepath.Base(command[0]) == "systemd")) { systemd = true if signalString == "" { stopSignal, err = signal.ParseSignal("RTMIN+3") @@ -663,7 +668,17 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim } } } + // This is done because cobra cannot have two aliased flags. So we have to check + // both + network := c.String("network") + if c.Flag("net").Changed { + network = c.String("net") + } + var memorySwappiness int64 + if c.Flags().Lookup("memory-swappiness") != nil { + memorySwappiness, _ = c.Flags().GetInt64("memory-swappiness") + } config := &cc.CreateConfig{ Runtime: runtime, Annotations: annotations, @@ -697,7 +712,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim LogDriverOpt: c.StringSlice("log-opt"), MacAddress: c.String("mac-address"), Name: c.String("name"), - Network: c.String("network"), + Network: network, NetworkAlias: c.StringSlice("network-alias"), IpcMode: ipcMode, NetMode: netMode, @@ -730,12 +745,11 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim Memory: memoryLimit, MemoryReservation: memoryReservation, MemorySwap: memorySwap, - MemorySwappiness: c.Int("memory-swappiness"), + MemorySwappiness: int(memorySwappiness), KernelMemory: memoryKernel, OomScoreAdj: c.Int("oom-score-adj"), - - PidsLimit: c.Int64("pids-limit"), - Ulimit: c.StringSlice("ulimit"), + PidsLimit: c.Int64("pids-limit"), + Ulimit: c.StringSlice("ulimit"), }, Rm: c.Bool("rm"), StopSignal: stopSignal, @@ -747,13 +761,12 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim User: user, UsernsMode: usernsMode, Mounts: mountList, - Volumes: c.StringSlice("volume"), + Volumes: c.StringArray("volume"), WorkDir: workDir, Rootfs: rootfs, VolumesFrom: c.StringSlice("volumes-from"), - Syslog: c.GlobalBool("syslog"), + Syslog: c.GlobalFlags.Syslog, } - if c.Bool("init") { initPath := c.String("init-path") if initPath == "" { @@ -767,11 +780,11 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim if config.Privileged { config.LabelOpts = label.DisableSecOpt() } else { - if err := parseSecurityOpt(config, c.StringSlice("security-opt")); err != nil { + if err := parseSecurityOpt(config, c.StringArray("security-opt")); err != nil { return nil, err } } - config.SecurityOpts = c.StringSlice("security-opt") + config.SecurityOpts = c.StringArray("security-opt") warnings, err := verifyContainerResources(config, false) if err != nil { return nil, err diff --git a/cmd/podman/create_cli.go b/cmd/podman/create_cli.go index 95b9321fd..ae0549687 100644 --- a/cmd/podman/create_cli.go +++ b/cmd/podman/create_cli.go @@ -80,19 +80,22 @@ func addWarning(warnings []string, msg string) []string { // podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... // podman run --mount type=tmpfs,target=/dev/shm .. func parseMounts(mounts []string) ([]spec.Mount, error) { + // TODO(vrothberg): the manual parsing can be replaced with a regular expression + // to allow a more robust parsing of the mount format and to give + // precise errors regarding supported format versus suppored options. var mountList []spec.Mount - errInvalidSyntax := errors.Errorf("incorrect mount format : should be --mount type=<bind|tmpfs>,[src=<host-dir>,]target=<ctr-dir>,[options]") + errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs>,[src=<host-dir>,]target=<ctr-dir>[,options]") for _, mount := range mounts { var tokenCount int var mountInfo spec.Mount arr := strings.SplitN(mount, ",", 2) if len(arr) < 2 { - return nil, errInvalidSyntax + return nil, errors.Wrapf(errInvalidSyntax, "%q", mount) } kv := strings.Split(arr[0], "=") if kv[0] != "type" { - return nil, errInvalidSyntax + return nil, errors.Wrapf(errInvalidSyntax, "%q", mount) } switch kv[1] { case "bind": @@ -168,7 +171,7 @@ func parseVolumes(volumes []string) error { for _, volume := range volumes { arr := strings.SplitN(volume, ":", 3) if len(arr) < 2 { - return errors.Errorf("incorrect volume format %q, should be host-dir:ctr-dir:[option]", volume) + return errors.Errorf("incorrect volume format %q, should be host-dir:ctr-dir[:option]", volume) } if err := validateVolumeHostDir(arr[0]); err != nil { return err diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 5f813699f..04a659ab6 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -2,12 +2,12 @@ package main import ( "fmt" - + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/storage/pkg/archive" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) type diffJSONOutput struct { @@ -33,31 +33,35 @@ func (so stdoutStruct) Out() error { } var ( - diffFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "archive", - Usage: "Save the diff as a tar archive", - Hidden: true, - }, - cli.StringFlag{ - Name: "format", - Usage: "Change the output format.", - }, - } + diffCommand cliconfig.DiffValues diffDescription = fmt.Sprint(`Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer`) - diffCommand = cli.Command{ - Name: "diff", - Usage: "Inspect changes on container's file systems", - Description: diffDescription, - Flags: sortFlags(diffFlags), - Action: diffCmd, - ArgsUsage: "ID-NAME", - OnUsageError: usageErrorHandler, + _diffCommand = &cobra.Command{ + Use: "diff", + Short: "Inspect changes on container's file systems", + Long: diffDescription, + RunE: func(cmd *cobra.Command, args []string) error { + diffCommand.InputArgs = args + diffCommand.GlobalFlags = MainGlobalOpts + return diffCmd(&diffCommand) + }, + Example: "ID-NAME", } ) +func init() { + diffCommand.Command = _diffCommand + flags := diffCommand.Flags() + + flags.BoolVar(&diffCommand.Archive, "archive", true, "Save the diff as a tar archive") + flags.StringVar(&diffCommand.Format, "format", "", "Change the output format") + + flags.MarkHidden("archive") + + rootCmd.AddCommand(diffCommand.Command) + +} func formatJSON(output []diffOutputParams) (diffJSONOutput, error) { jsonStruct := diffJSONOutput{} for _, output := range output { @@ -75,29 +79,25 @@ func formatJSON(output []diffOutputParams) (diffJSONOutput, error) { return jsonStruct, nil } -func diffCmd(c *cli.Context) error { - if err := validateFlags(c, diffFlags); err != nil { - return err - } - - if len(c.Args()) != 1 { +func diffCmd(c *cliconfig.DiffValues) error { + if len(c.InputArgs) != 1 { return errors.Errorf("container, image, or layer name must be specified: podman diff [options [...]] ID-NAME") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - to := c.Args().Get(0) + to := c.InputArgs[0] changes, err := runtime.GetDiff("", to) if err != nil { return errors.Wrapf(err, "could not get changes for %q", to) } diffOutput := []diffOutputParams{} - outputFormat := c.String("format") + outputFormat := c.Format for _, change := range changes { diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index 073e72e64..b4f66bc03 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -2,82 +2,76 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" "os" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" - "github.com/urfave/cli" ) var ( - execFlags = []cli.Flag{ - cli.StringSliceFlag{ - Name: "env, e", - Usage: "Set environment variables", - }, - cli.BoolFlag{ - Name: "privileged", - Usage: "Give the process extended Linux capabilities inside the container. The default is false", - }, - cli.BoolFlag{ - Name: "interactive, i", - Usage: "Not supported. All exec commands are interactive by default.", - }, - cli.BoolFlag{ - Name: "tty, t", - Usage: "Allocate a pseudo-TTY. The default is false", - }, - cli.StringFlag{ - Name: "user, u", - Usage: "Sets the username or UID used and optionally the groupname or GID for the specified command", - }, - LatestFlag, - WorkDirFlag, - } + execCommand cliconfig.ExecValues + execDescription = ` podman exec Run a command in a running container ` - - execCommand = cli.Command{ - Name: "exec", - Usage: "Run a process in a running container", - Description: execDescription, - Flags: sortFlags(execFlags), - Action: execCmd, - ArgsUsage: "CONTAINER-NAME", - SkipArgReorder: true, - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _execCommand = &cobra.Command{ + Use: "exec", + Short: "Run a process in a running container", + Long: execDescription, + RunE: func(cmd *cobra.Command, args []string) error { + execCommand.InputArgs = args + execCommand.GlobalFlags = MainGlobalOpts + return execCmd(&execCommand) + }, + Example: "CONTAINER-NAME", } ) -func execCmd(c *cli.Context) error { - args := c.Args() +func init() { + execCommand.Command = _execCommand + flags := execCommand.Flags() + flags.SetInterspersed(false) + flags.StringSliceVarP(&execCommand.Env, "env", "e", []string{}, "Set environment variables") + flags.BoolVarP(&execCommand.Interfactive, "interactive", "i", false, "Not supported. All exec commands are interactive by default") + flags.BoolVarP(&execCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVar(&execCommand.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false") + flags.BoolVarP(&execCommand.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false") + flags.StringVarP(&execCommand.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command") + + flags.StringVarP(&execCommand.Workdir, "workdir", "w", "", "Working directory inside the container") + + rootCmd.AddCommand(execCommand.Command) +} + +func execCmd(c *cliconfig.ExecValues) error { + args := c.InputArgs var ctr *libpod.Container var err error argStart := 1 - if len(args) < 1 && !c.Bool("latest") { + if len(args) < 1 && !c.Latest { return errors.Errorf("you must provide one container name or id") } - if len(args) < 2 && !c.Bool("latest") { + if len(args) < 2 && !c.Latest { return errors.Errorf("you must provide a command to exec") } - if c.Bool("latest") { + if c.Latest { argStart = 0 } rootless.SetSkipStorageSetup(true) cmd := args[argStart:] - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - if c.Bool("latest") { + if c.Latest { ctr, err = runtime.GetLatestContainer() } else { ctr, err = runtime.LookupContainer(args[0]) @@ -101,7 +95,7 @@ func execCmd(c *cli.Context) error { // ENVIRONMENT VARIABLES env := map[string]string{} - if err := readKVStrings(env, []string{}, c.StringSlice("env")); err != nil { + if err := readKVStrings(env, []string{}, c.Env); err != nil { return errors.Wrapf(err, "unable to process environment variables") } envs := []string{} @@ -109,5 +103,5 @@ func execCmd(c *cli.Context) error { envs = append(envs, fmt.Sprintf("%s=%s", k, v)) } - return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"), c.String("workdir")) + return ctr.Exec(c.Tty, c.Privileged, envs, cmd, c.User, c.Workdir) } diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go index a7601aaa2..a21e8fcf8 100644 --- a/cmd/podman/exists.go +++ b/cmd/podman/exists.go @@ -1,6 +1,8 @@ package main import ( + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" "os" "github.com/containers/libpod/cmd/podman/libpodruntime" @@ -8,66 +10,78 @@ import ( "github.com/containers/libpod/libpod/adapter" "github.com/containers/libpod/libpod/image" "github.com/pkg/errors" - "github.com/urfave/cli" ) var ( + imageExistsCommand cliconfig.ImageExistsValues + containerExistsCommand cliconfig.ContainerExistsValues + podExistsCommand cliconfig.PodExistsValues + imageExistsDescription = ` podman image exists Check if an image exists in local storage ` - - imageExistsCommand = cli.Command{ - Name: "exists", - Usage: "Check if an image exists in local storage", - Description: imageExistsDescription, - Action: imageExistsCmd, - ArgsUsage: "IMAGE-NAME", - OnUsageError: usageErrorHandler, - } -) - -var ( containerExistsDescription = ` podman container exists Check if a container exists in local storage ` - - containerExistsCommand = cli.Command{ - Name: "exists", - Usage: "Check if a container exists in local storage", - Description: containerExistsDescription, - Action: containerExistsCmd, - ArgsUsage: "CONTAINER-NAME", - OnUsageError: usageErrorHandler, - } -) - -var ( podExistsDescription = ` podman pod exists Check if a pod exists in local storage ` + _imageExistsCommand = &cobra.Command{ + Use: "exists", + Short: "Check if an image exists in local storage", + Long: imageExistsDescription, + RunE: func(cmd *cobra.Command, args []string) error { + imageExistsCommand.InputArgs = args + imageExistsCommand.GlobalFlags = MainGlobalOpts + return imageExistsCmd(&imageExistsCommand) + }, + Example: "IMAGE-NAME", + } + + _containerExistsCommand = &cobra.Command{ + Use: "exists", + Short: "Check if a container exists in local storage", + Long: containerExistsDescription, + RunE: func(cmd *cobra.Command, args []string) error { + containerExistsCommand.InputArgs = args + containerExistsCommand.GlobalFlags = MainGlobalOpts + return containerExistsCmd(&containerExistsCommand) - podExistsCommand = cli.Command{ - Name: "exists", - Usage: "Check if a pod exists in local storage", - Description: podExistsDescription, - Action: podExistsCmd, - ArgsUsage: "POD-NAME", - OnUsageError: usageErrorHandler, + }, + Example: "CONTAINER-NAME", + } + + _podExistsCommand = &cobra.Command{ + Use: "exists", + Short: "Check if a pod exists in local storage", + Long: podExistsDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podExistsCommand.InputArgs = args + podExistsCommand.GlobalFlags = MainGlobalOpts + return podExistsCmd(&podExistsCommand) + }, + Example: "POD-NAME", } ) -func imageExistsCmd(c *cli.Context) error { - args := c.Args() +func init() { + imageExistsCommand.Command = _imageExistsCommand + containerExistsCommand.Command = _containerExistsCommand + podExistsCommand.Command = _podExistsCommand +} + +func imageExistsCmd(c *cliconfig.ImageExistsValues) error { + args := c.InputArgs if len(args) > 1 || len(args) < 1 { return errors.New("you may only check for the existence of one image at a time") } - runtime, err := adapter.GetRuntime(c) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -83,12 +97,12 @@ func imageExistsCmd(c *cli.Context) error { return nil } -func containerExistsCmd(c *cli.Context) error { - args := c.Args() +func containerExistsCmd(c *cliconfig.ContainerExistsValues) error { + args := c.InputArgs if len(args) > 1 || len(args) < 1 { return errors.New("you may only check for the existence of one container at a time") } - runtime, err := adapter.GetRuntime(c) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -102,12 +116,12 @@ func containerExistsCmd(c *cli.Context) error { return nil } -func podExistsCmd(c *cli.Context) error { - args := c.Args() +func podExistsCmd(c *cliconfig.PodExistsValues) error { + args := c.InputArgs if len(args) > 1 || len(args) < 1 { return errors.New("you may only check for the existence of one pod at a time") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } diff --git a/cmd/podman/export.go b/cmd/podman/export.go index eaa4e38a2..bd9e38b0c 100644 --- a/cmd/podman/export.go +++ b/cmd/podman/export.go @@ -3,50 +3,52 @@ package main import ( "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/adapter" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - exportFlags = []cli.Flag{ - cli.StringFlag{ - Name: "output, o", - Usage: "Write to a file, default is STDOUT", - Value: "/dev/stdout", - }, - } + exportCommand cliconfig.ExportValues exportDescription = "Exports container's filesystem contents as a tar archive" + " and saves it on the local machine." - exportCommand = cli.Command{ - Name: "export", - Usage: "Export container's filesystem contents as a tar archive", - Description: exportDescription, - Flags: sortFlags(exportFlags), - Action: exportCmd, - ArgsUsage: "CONTAINER", - OnUsageError: usageErrorHandler, + + _exportCommand = &cobra.Command{ + Use: "export", + Short: "Export container's filesystem contents as a tar archive", + Long: exportDescription, + RunE: func(cmd *cobra.Command, args []string) error { + exportCommand.InputArgs = args + exportCommand.GlobalFlags = MainGlobalOpts + return exportCmd(&exportCommand) + }, + Example: "CONTAINER", } ) +func init() { + exportCommand.Command = _exportCommand + flags := exportCommand.Flags() + flags.StringVarP(&exportCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT") + rootCmd.AddCommand(exportCommand.Command) +} + // exportCmd saves a container to a tarball on disk -func exportCmd(c *cli.Context) error { - if err := validateFlags(c, exportFlags); err != nil { - return err - } +func exportCmd(c *cliconfig.ExportValues) error { if os.Geteuid() != 0 { rootless.SetSkipStorageSetup(true) } - runtime, err := adapter.GetRuntime(c) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - args := c.Args() + args := c.InputArgs if len(args) == 0 { return errors.Errorf("container id must be specified") } @@ -54,10 +56,11 @@ func exportCmd(c *cli.Context) error { return errors.Errorf("too many arguments given, need 1 at most.") } - output := c.String("output") + output := c.Output if runtime.Remote && (output == "/dev/stdout" || len(output) == 0) { return errors.New("remote client usage must specify an output file (-o)") } + if output == "/dev/stdout" { file := os.Stdout if logrus.IsTerminal(file) { @@ -68,5 +71,5 @@ func exportCmd(c *cli.Context) error { if err := validateFileName(output); err != nil { return err } - return runtime.Export(args[0], c.String("output")) + return runtime.Export(args[0], output) } diff --git a/cmd/podman/generate.go b/cmd/podman/generate.go index 20a4a61ab..a5e15fdd6 100644 --- a/cmd/podman/generate.go +++ b/cmd/podman/generate.go @@ -1,23 +1,21 @@ package main import ( - "github.com/urfave/cli" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" ) -var ( - generateSubCommands = []cli.Command{ - containerKubeCommand, - } +var generateDescription = "Generate structured data based for a containers and pods" +var generateCommand = cliconfig.PodmanCommand{ - generateDescription = "Generate structured data based for a containers and pods" - kubeCommand = cli.Command{ - Name: "generate", - Usage: "Generate structured data", - Description: generateDescription, - ArgsUsage: "", - Subcommands: generateSubCommands, - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, - Hidden: true, - } -) + Command: &cobra.Command{ + Use: "generate", + Short: "Generated structured data", + Long: generateDescription, + }, +} + +func init() { + generateCommand.AddCommand(getGenerateSubCommands()...) + rootCmd.AddCommand(generateCommand.Command) +} diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go index fc667fb5b..8e3432d12 100644 --- a/cmd/podman/generate_kube.go +++ b/cmd/podman/generate_kube.go @@ -2,38 +2,40 @@ package main import ( "fmt" - + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" podmanVersion "github.com/containers/libpod/version" "github.com/ghodss/yaml" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" "k8s.io/api/core/v1" ) var ( - containerKubeFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "service, s", - Usage: "Generate YAML for kubernetes service object", - }, - } + containerKubeCommand cliconfig.GenerateKubeValues containerKubeDescription = "Generate Kubernetes Pod YAML" - containerKubeCommand = cli.Command{ - Name: "kube", - Usage: "Generate Kubernetes pod YAML for a container or pod", - Description: containerKubeDescription, - Flags: sortFlags(containerKubeFlags), - Action: generateKubeYAMLCmd, - ArgsUsage: "CONTAINER|POD-NAME", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _containerKubeCommand = &cobra.Command{ + Use: "kube", + Short: "Generate Kubernetes pod YAML for a container or pod", + Long: containerKubeDescription, + RunE: func(cmd *cobra.Command, args []string) error { + containerKubeCommand.InputArgs = args + containerKubeCommand.GlobalFlags = MainGlobalOpts + return generateKubeYAMLCmd(&containerKubeCommand) + }, + Example: "CONTAINER|POD-NAME", } ) -func generateKubeYAMLCmd(c *cli.Context) error { +func init() { + containerKubeCommand.Command = _containerKubeCommand + flags := containerKubeCommand.Flags() + flags.BoolVarP(&containerKubeCommand.Service, "service", "s", false, "Generate YAML for kubernetes service object") +} + +func generateKubeYAMLCmd(c *cliconfig.GenerateKubeValues) error { var ( podYAML *v1.Pod container *libpod.Container @@ -48,12 +50,12 @@ func generateKubeYAMLCmd(c *cli.Context) error { if rootless.IsRootless() { return errors.Wrapf(libpod.ErrNotImplemented, "rootless users") } - args := c.Args() + args := c.InputArgs if len(args) > 1 || (len(args) < 1 && !c.Bool("latest")) { return errors.Errorf("you must provide one container|pod ID or name or --latest") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -77,7 +79,7 @@ func generateKubeYAMLCmd(c *cli.Context) error { return err } - if c.Bool("service") { + if c.Service { serviceYAML := libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts) marshalledService, err = yaml.Marshal(serviceYAML) if err != nil { diff --git a/cmd/podman/history.go b/cmd/podman/history.go index 8a9b6cd94..97e501947 100644 --- a/cmd/podman/history.go +++ b/cmd/podman/history.go @@ -6,12 +6,13 @@ import ( "strings" "time" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/libpod/adapter" "github.com/containers/libpod/libpod/image" "github.com/docker/go-units" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) const createdByTruncLength = 45 @@ -34,53 +35,44 @@ type historyOptions struct { } var ( - historyFlags = []cli.Flag{ - cli.BoolTFlag{ - Name: "human, H", - Usage: "Display sizes and dates in human readable format", - }, - cli.BoolFlag{ - Name: "no-trunc, notruncate", - Usage: "Do not truncate the output", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Display the numeric IDs only", - }, - cli.StringFlag{ - Name: "format", - Usage: "Change the output to JSON or a Go template", - }, - } + historyCommand cliconfig.HistoryValues historyDescription = "Displays the history of an image. The information can be printed out in an easy to read, " + "or user specified format, and can be truncated." - historyCommand = cli.Command{ - Name: "history", - Usage: "Show history of a specified image", - Description: historyDescription, - Flags: sortFlags(historyFlags), - Action: historyCmd, - ArgsUsage: "", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _historyCommand = &cobra.Command{ + Use: "history", + Short: "Show history of a specified image", + Long: historyDescription, + RunE: func(cmd *cobra.Command, args []string) error { + historyCommand.InputArgs = args + historyCommand.GlobalFlags = MainGlobalOpts + return historyCmd(&historyCommand) + }, } ) -func historyCmd(c *cli.Context) error { - if err := validateFlags(c, historyFlags); err != nil { - return err - } +func init() { + historyCommand.Command = _historyCommand + flags := historyCommand.Flags() + flags.StringVar(&historyCommand.Format, "format", "", "Change the output to JSON or a Go template") + flags.BoolVarP(&historyCommand.Human, "human", "H", true, "Display sizes and dates in human readable format") + // notrucate needs to be added + flags.BoolVar(&historyCommand.NoTrunc, "no-trunc", false, "Do not truncate the output") + flags.BoolVarP(&historyCommand.Quiet, "quiet", "q", false, "Display the numeric IDs only") - runtime, err := adapter.GetRuntime(c) + rootCmd.AddCommand(historyCommand.Command) + +} +func historyCmd(c *cliconfig.HistoryValues) error { + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - format := genHistoryFormat(c.String("format"), c.Bool("quiet")) + format := genHistoryFormat(c.Format, c.Quiet) - args := c.Args() + args := c.InputArgs if len(args) == 0 { return errors.Errorf("an image name must be specified") } @@ -93,9 +85,9 @@ func historyCmd(c *cli.Context) error { return err } opts := historyOptions{ - human: c.BoolT("human"), - noTrunc: c.Bool("no-trunc"), - quiet: c.Bool("quiet"), + human: c.Human, + noTrunc: c.NoTrunc, + quiet: c.Quiet, format: format, } diff --git a/cmd/podman/image.go b/cmd/podman/image.go index 6b451a1ca..ac7ff4944 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -1,37 +1,35 @@ package main import ( - "sort" - - "github.com/urfave/cli" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" ) var ( - imageSubCommands = []cli.Command{ - importCommand, - historyCommand, - imageExistsCommand, - inspectCommand, - lsImagesCommand, - pruneImagesCommand, - pullCommand, - rmImageCommand, - tagCommand, - } imageDescription = "Manage images" - imageCommand = cli.Command{ - Name: "image", - Usage: "Manage images", - Description: imageDescription, - ArgsUsage: "", - Subcommands: getImageSubCommandsSorted(), - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + imageCommand = cliconfig.PodmanCommand{ + Command: &cobra.Command{ + Use: "image", + Short: "Manage images", + Long: imageDescription, + }, } ) -func getImageSubCommandsSorted() []cli.Command { - imageSubCommands = append(imageSubCommands, getImageSubCommands()...) - sort.Sort(commandSortedAlpha{imageSubCommands}) - return imageSubCommands +//imageSubCommands are implemented both in local and remote clients +var imageSubCommands = []*cobra.Command{ + _historyCommand, + _imageExistsCommand, + _inspectCommand, + _imagesCommand, + _pruneImagesCommand, + _pushCommand, + _rmiCommand, + _tagCommand, +} + +func init() { + imageCommand.AddCommand(imageSubCommands...) + imageCommand.AddCommand(getImageSubCommands()...) + rootCmd.AddCommand(imageCommand.Command) } diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 9fdf0a780..6afc2d042 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -8,6 +8,7 @@ import ( "time" "unicode" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/imagefilters" "github.com/containers/libpod/libpod/adapter" @@ -16,7 +17,7 @@ import ( "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) type imagesTemplateParams struct { @@ -83,108 +84,79 @@ func (a imagesSortedSize) Less(i, j int) bool { } var ( - imagesFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Show all images (default hides intermediate images)", - }, - cli.BoolFlag{ - Name: "digests", - Usage: "Show digests", - }, - cli.StringSliceFlag{ - Name: "filter, f", - Usage: "Filter output based on conditions provided (default [])", - }, - cli.StringFlag{ - Name: "format", - Usage: "Change the output format to JSON or a Go template", - }, - cli.BoolFlag{ - Name: "noheading, n", - Usage: "Do not print column headings", - }, - cli.BoolFlag{ - Name: "no-trunc, notruncate", - Usage: "Do not truncate output", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Display only image IDs", - }, - cli.StringFlag{ - Name: "sort", - Usage: "Sort by created, id, repository, size, or tag", - Value: "Created", - }, - } - + imagesCommand cliconfig.ImagesValues imagesDescription = "lists locally stored images." - imagesCommand = cli.Command{ - Name: "images", - Usage: "List images in local storage", - Description: imagesDescription, - Flags: sortFlags(imagesFlags), - Action: imagesCmd, - ArgsUsage: "", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, - } - lsImagesCommand = cli.Command{ - Name: "list", - Aliases: []string{"ls"}, - Usage: "List images in local storage", - Description: imagesDescription, - Flags: imagesFlags, - Action: imagesCmd, - ArgsUsage: "", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + + _imagesCommand = &cobra.Command{ + Use: "images", + Short: "List images in local storage", + Long: imagesDescription, + RunE: func(cmd *cobra.Command, args []string) error { + imagesCommand.InputArgs = args + imagesCommand.GlobalFlags = MainGlobalOpts + return imagesCmd(&imagesCommand) + }, + Example: "", } ) -func imagesCmd(c *cli.Context) error { +func init() { + imagesCommand.Command = _imagesCommand + flags := imagesCommand.Flags() + flags.BoolVarP(&imagesCommand.All, "all", "a", false, "Show all images (default hides intermediate images)") + flags.BoolVar(&imagesCommand.Digests, "digests", false, "Show digests") + flags.StringSliceVarP(&imagesCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") + flags.StringVar(&imagesCommand.Format, "format", "", "Change the output format to JSON or a Go template") + flags.BoolVarP(&imagesCommand.Noheading, "noheading", "n", false, "Do not print column headings") + // TODO Need to learn how to deal with second name being a string instead of a char. + // This needs to be "no-trunc, notruncate" + flags.BoolVar(&imagesCommand.NoTrunc, "no-trunc", false, "Do not truncate output") + flags.BoolVarP(&imagesCommand.Quiet, "quiet", "q", false, "Display only image IDs") + flags.StringVar(&imagesCommand.Sort, "sort", "created", "Sort by created, id, repository, size, or tag") + + rootCmd.AddCommand(imagesCommand.Command) + +} + +func imagesCmd(c *cliconfig.ImagesValues) error { var ( filterFuncs []imagefilters.ResultFilter newImage *adapter.ContainerImage ) - if err := validateFlags(c, imagesFlags); err != nil { - return err - } - runtime, err := adapter.GetRuntime(c) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "Could not get runtime") } defer runtime.Shutdown(false) - if len(c.Args()) == 1 { - newImage, err = runtime.NewImageFromLocal(c.Args().Get(0)) + if len(c.InputArgs) == 1 { + newImage, err = runtime.NewImageFromLocal(c.InputArgs[0]) if err != nil { return err } } - if len(c.Args()) > 1 { + if len(c.InputArgs) > 1 { return errors.New("'podman images' requires at most 1 argument") } ctx := getContext() - if len(c.StringSlice("filter")) > 0 || newImage != nil { - filterFuncs, err = CreateFilterFuncs(ctx, runtime, c, newImage) + if len(c.Filter) > 0 || newImage != nil { + filterFuncs, err = CreateFilterFuncs(ctx, runtime, c.Filter, newImage) if err != nil { return err } } opts := imagesOptions{ - quiet: c.Bool("quiet"), - noHeading: c.Bool("noheading"), - noTrunc: c.Bool("no-trunc"), - digests: c.Bool("digests"), - format: c.String("format"), - sort: c.String("sort"), - all: c.Bool("all"), + quiet: c.Quiet, + noHeading: c.Noheading, + noTrunc: c.NoTrunc, + digests: c.Digests, + format: c.Format, + sort: c.Sort, + all: c.All, } opts.outputformat = opts.setOutputFormat() @@ -195,7 +167,7 @@ func imagesCmd(c *cli.Context) error { var filteredImages []*adapter.ContainerImage //filter the images - if len(c.StringSlice("filter")) > 0 || newImage != nil { + if len(c.Filter) > 0 || newImage != nil { filteredImages = imagefilters.FilterImages(images, filterFuncs) } else { filteredImages = images @@ -368,9 +340,9 @@ func (i *imagesTemplateParams) HeaderMap() map[string]string { // CreateFilterFuncs returns an array of filter functions based on the user inputs // and is later used to filter images for output -func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, c *cli.Context, img *adapter.ContainerImage) ([]imagefilters.ResultFilter, error) { +func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, filters []string, img *adapter.ContainerImage) ([]imagefilters.ResultFilter, error) { var filterFuncs []imagefilters.ResultFilter - for _, filter := range c.StringSlice("filter") { + for _, filter := range filters { splitFilter := strings.Split(filter, "=") switch splitFilter[0] { case "before": diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go index 844984bb9..ba99c5bde 100644 --- a/cmd/podman/images_prune.go +++ b/cmd/podman/images_prune.go @@ -3,35 +3,39 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + pruneImagesCommand cliconfig.PruneImagesValues pruneImagesDescription = ` podman image prune Removes all unnamed images from local storage ` - pruneImageFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Remove all unused images, not just dangling ones", + _pruneImagesCommand = &cobra.Command{ + Use: "prune", + Short: "Remove unused images", + Long: pruneImagesDescription, + RunE: func(cmd *cobra.Command, args []string) error { + pruneImagesCommand.InputArgs = args + pruneImagesCommand.GlobalFlags = MainGlobalOpts + return pruneImagesCmd(&pruneImagesCommand) }, } - pruneImagesCommand = cli.Command{ - Name: "prune", - Usage: "Remove unused images", - Description: pruneImagesDescription, - Action: pruneImagesCmd, - OnUsageError: usageErrorHandler, - Flags: pruneImageFlags, - } ) -func pruneImagesCmd(c *cli.Context) error { - runtime, err := adapter.GetRuntime(c) +func init() { + pruneImagesCommand.Command = _pruneImagesCommand + flags := pruneImagesCommand.Flags() + flags.BoolVarP(&pruneImagesCommand.All, "all", "a", false, "Remove all unused images, not just dangling ones") +} + +func pruneImagesCmd(c *cliconfig.PruneImagesValues) error { + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -39,7 +43,7 @@ func pruneImagesCmd(c *cli.Context) error { // Call prune; if any cids are returned, print them and then // return err in case an error also came up - pruneCids, err := runtime.PruneImages(c.Bool("all")) + pruneCids, err := runtime.PruneImages(c.All) if len(pruneCids) > 0 { for _, cid := range pruneCids { fmt.Println(cid) diff --git a/cmd/podman/import.go b/cmd/podman/import.go index 661bd5a65..52d144eb3 100644 --- a/cmd/podman/import.go +++ b/cmd/podman/import.go @@ -3,47 +3,44 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - importFlags = []cli.Flag{ - cli.StringSliceFlag{ - Name: "change, c", - Usage: "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR", - }, - cli.StringFlag{ - Name: "message, m", - Usage: "Set commit message for imported image", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Suppress output", - }, - } + importCommand cliconfig.ImportValues + importDescription = `Create a container image from the contents of the specified tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz). Note remote tar balls can be specified, via web address. Optionally tag the image. You can specify the instructions using the --change option. ` - importCommand = cli.Command{ - Name: "import", - Usage: "Import a tarball to create a filesystem image", - Description: importDescription, - Flags: sortFlags(importFlags), - Action: importCmd, - ArgsUsage: "TARBALL [REFERENCE]", - OnUsageError: usageErrorHandler, + _importCommand = &cobra.Command{ + Use: "import", + Short: "Import a tarball to create a filesystem image", + Long: importDescription, + RunE: func(cmd *cobra.Command, args []string) error { + importCommand.InputArgs = args + importCommand.GlobalFlags = MainGlobalOpts + return importCmd(&importCommand) + }, + Example: "TARBALL [REFERENCE]", } ) -func importCmd(c *cli.Context) error { - if err := validateFlags(c, importFlags); err != nil { - return err - } +func init() { + importCommand.Command = _importCommand + flags := importCommand.Flags() + flags.StringSliceVarP(&importCommand.Change, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR") + flags.StringVarP(&importCommand.Message, "message", "m", "", "Set commit message for imported image") + flags.BoolVarP(&importCommand.Quiet, "quiet", "q", false, "Suppress output") + + rootCmd.AddCommand(importCommand.Command) +} - runtime, err := adapter.GetRuntime(c) +func importCmd(c *cliconfig.ImportValues) error { + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -54,7 +51,7 @@ func importCmd(c *cli.Context) error { reference string ) - args := c.Args() + args := c.InputArgs switch len(args) { case 0: return errors.Errorf("need to give the path to the tarball, or must specify a tarball of '-' for stdin") @@ -71,7 +68,7 @@ func importCmd(c *cli.Context) error { return err } - quiet := c.Bool("quiet") + quiet := c.Quiet if runtime.Remote { quiet = false } diff --git a/cmd/podman/info.go b/cmd/podman/info.go index 19078cea5..0896e16a4 100644 --- a/cmd/podman/info.go +++ b/cmd/podman/info.go @@ -4,45 +4,47 @@ import ( "fmt" rt "runtime" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/adapter" "github.com/containers/libpod/version" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + infoCommand cliconfig.InfoValues + infoDescription = "Display podman system information" - infoCommand = cli.Command{ - Name: "info", - Usage: infoDescription, - Description: `Information display here pertain to the host, current storage stats, and build of podman. Useful for the user and when reporting issues.`, - Flags: sortFlags(infoFlags), - Action: infoCmd, - ArgsUsage: "", - OnUsageError: usageErrorHandler, - } - infoFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "debug, D", - Usage: "Display additional debug information", - }, - cli.StringFlag{ - Name: "format, f", - Usage: "Change the output format to JSON or a Go template", + _infoCommand = &cobra.Command{ + Use: "info", + Long: infoDescription, + Short: `Display information pertaining to the host, current storage stats, and build of podman. Useful for the user and when reporting issues.`, + RunE: func(cmd *cobra.Command, args []string) error { + infoCommand.InputArgs = args + infoCommand.GlobalFlags = MainGlobalOpts + return infoCmd(&infoCommand) }, + Example: "", } ) -func infoCmd(c *cli.Context) error { - if err := validateFlags(c, infoFlags); err != nil { - return err - } +func init() { + infoCommand.Command = _infoCommand + flags := infoCommand.Flags() + + flags.BoolVarP(&infoCommand.Debug, "debug", "D", false, "Display additional debug information") + flags.StringVarP(&infoCommand.Format, "format", "f", "", "Change the output format to JSON or a Go template") + + rootCmd.AddCommand(infoCommand.Command) +} + +func infoCmd(c *cliconfig.InfoValues) error { info := map[string]interface{}{} remoteClientInfo := map[string]interface{}{} - runtime, err := adapter.GetRuntime(c) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -59,7 +61,7 @@ func infoCmd(c *cli.Context) error { infoArr = append(infoArr, libpod.InfoData{Type: "client", Data: remoteClientInfo}) } - if !runtime.Remote && c.Bool("debug") { + if !runtime.Remote && c.Debug { debugInfo := debugInfo(c) infoArr = append(infoArr, libpod.InfoData{Type: "debug", Data: debugInfo}) } @@ -69,7 +71,7 @@ func infoCmd(c *cli.Context) error { } var out formats.Writer - infoOutputFormat := c.String("format") + infoOutputFormat := c.Format switch infoOutputFormat { case formats.JSONString: out = formats.JSONStruct{Output: info} @@ -85,11 +87,11 @@ func infoCmd(c *cli.Context) error { } // top-level "debug" info -func debugInfo(c *cli.Context) map[string]interface{} { +func debugInfo(c *cliconfig.InfoValues) map[string]interface{} { info := map[string]interface{}{} info["compiler"] = rt.Compiler info["go version"] = rt.Version() - info["podman version"] = c.App.Version + info["podman version"] = version.Version version, _ := libpod.GetVersion() info["git commit"] = version.GitCommit return info diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index 1346da9fb..b2979c6ae 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -5,13 +5,14 @@ import ( "encoding/json" "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/adapter" cc "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) const ( @@ -21,38 +22,37 @@ const ( ) var ( - inspectFlags = []cli.Flag{ - cli.StringFlag{ - Name: "type, t", - Value: inspectAll, - Usage: "Return JSON for specified type, (e.g image, container or task)", - }, - cli.StringFlag{ - Name: "format, f", - Usage: "Change the output format to a Go template", - }, - cli.BoolFlag{ - Name: "size, s", - Usage: "Display total file size if the type is container", - }, - LatestFlag, - } + inspectCommand cliconfig.InspectValues + inspectDescription = "This displays the low-level information on containers and images identified by name or ID. By default, this will render all results in a JSON array. If the container and image have the same name, this will return container JSON for unspecified type." - inspectCommand = cli.Command{ - Name: "inspect", - Usage: "Display the configuration of a container or image", - Description: inspectDescription, - Flags: sortFlags(inspectFlags), - Action: inspectCmd, - ArgsUsage: "CONTAINER-OR-IMAGE [CONTAINER-OR-IMAGE]...", - OnUsageError: usageErrorHandler, + _inspectCommand = &cobra.Command{ + Use: "inspect", + Short: "Display the configuration of a container or image", + Long: inspectDescription, + RunE: func(cmd *cobra.Command, args []string) error { + inspectCommand.InputArgs = args + inspectCommand.GlobalFlags = MainGlobalOpts + return inspectCmd(&inspectCommand) + }, + Example: "CONTAINER-OR-IMAGE [CONTAINER-OR-IMAGE]...", } ) -func inspectCmd(c *cli.Context) error { - args := c.Args() - inspectType := c.String("type") - latestContainer := c.Bool("latest") +func init() { + inspectCommand.Command = _inspectCommand + flags := inspectCommand.Flags() + flags.StringVarP(&inspectCommand.TypeObject, "type", "t", inspectAll, "Return JSON for specified type, (e.g image, container or task)") + flags.StringVarP(&inspectCommand.Format, "format", "f", "", "Change the output format to a Go template") + flags.BoolVarP(&inspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of if the type is a container") + flags.BoolVarP(&inspectCommand.Size, "size", "s", false, "Display total file size if the type is container") + + rootCmd.AddCommand(inspectCommand.Command) +} + +func inspectCmd(c *cliconfig.InspectValues) error { + args := c.InputArgs + inspectType := c.TypeObject + latestContainer := c.Latest if len(args) == 0 && !latestContainer { return errors.Errorf("container or image name must be specified: podman inspect [options [...]] name") } @@ -60,11 +60,8 @@ func inspectCmd(c *cli.Context) error { if len(args) > 0 && latestContainer { return errors.Errorf("you cannot provide additional arguments with --latest") } - if err := validateFlags(c, inspectFlags); err != nil { - return err - } - runtime, err := adapter.GetRuntime(c) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -74,7 +71,7 @@ func inspectCmd(c *cli.Context) error { return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll) } - outputFormat := c.String("format") + outputFormat := c.Format if strings.Contains(outputFormat, "{{.Id}}") { outputFormat = strings.Replace(outputFormat, "{{.Id}}", formats.IDString, -1) } @@ -87,7 +84,7 @@ func inspectCmd(c *cli.Context) error { inspectType = inspectTypeContainer } - inspectedObjects, iterateErr := iterateInput(getContext(), c, args, runtime, inspectType) + inspectedObjects, iterateErr := iterateInput(getContext(), c.Size, args, runtime, inspectType) if iterateErr != nil { return iterateErr } @@ -105,7 +102,7 @@ func inspectCmd(c *cli.Context) error { } // func iterateInput iterates the images|containers the user has requested and returns the inspect data and error -func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *adapter.LocalRuntime, inspectType string) ([]interface{}, error) { +func iterateInput(ctx context.Context, size bool, args []string, runtime *adapter.LocalRuntime, inspectType string) ([]interface{}, error) { var ( data interface{} inspectedItems []interface{} @@ -120,7 +117,7 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *a inspectError = errors.Wrapf(err, "error looking up container %q", input) break } - libpodInspectData, err := ctr.Inspect(c.Bool("size")) + libpodInspectData, err := ctr.Inspect(size) if err != nil { inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID()) break @@ -160,7 +157,7 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *a break } } else { - libpodInspectData, err := ctr.Inspect(c.Bool("size")) + libpodInspectData, err := ctr.Inspect(size) if err != nil { inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID()) break diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index cfe4b4218..04c0fd531 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -4,6 +4,7 @@ import ( "fmt" "syscall" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" @@ -11,68 +12,66 @@ import ( "github.com/docker/docker/pkg/signal" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - killFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Signal all running containers", - }, - cli.StringFlag{ - Name: "signal, s", - Usage: "Signal to send to the container", - Value: "KILL", - }, - LatestFlag, - } + killCommand cliconfig.KillValues + killDescription = "The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal." - killCommand = cli.Command{ - Name: "kill", - Usage: "Kill one or more running containers with a specific signal", - Description: killDescription, - Flags: sortFlags(killFlags), - Action: killCmd, - ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _killCommand = &cobra.Command{ + Use: "kill", + Short: "Kill one or more running containers with a specific signal", + Long: killDescription, + RunE: func(cmd *cobra.Command, args []string) error { + killCommand.InputArgs = args + killCommand.GlobalFlags = MainGlobalOpts + return killCmd(&killCommand) + }, + Example: "CONTAINER-NAME [CONTAINER-NAME ...]", } ) +func init() { + killCommand.Command = _killCommand + flags := killCommand.Flags() + + flags.BoolVarP(&killCommand.All, "all", "a", false, "Signal all running containers") + flags.StringVarP(&killCommand.Signal, "signal", "s", "KILL", "Signal to send to the container") + flags.BoolVarP(&killCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + + rootCmd.AddCommand(killCommand.Command) +} + // killCmd kills one or more containers with a signal -func killCmd(c *cli.Context) error { +func killCmd(c *cliconfig.KillValues) error { var ( killFuncs []shared.ParallelWorkerInput killSignal uint = uint(syscall.SIGTERM) ) - if err := checkAllAndLatest(c); err != nil { - return err - } - - if err := validateFlags(c, killFlags); err != nil { + if err := checkAllAndLatest(&c.PodmanCommand); err != nil { return err } rootless.SetSkipStorageSetup(true) - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - if c.String("signal") != "" { + if c.Signal != "" { // Check if the signalString provided by the user is valid // Invalid signals will return err - sysSignal, err := signal.ParseSignal(c.String("signal")) + sysSignal, err := signal.ParseSignal(c.Signal) if err != nil { return err } killSignal = uint(sysSignal) } - containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") + containers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStateRunning, "running") if err != nil { if len(containers) == 0 { return err @@ -94,7 +93,7 @@ func killCmd(c *cli.Context) error { maxWorkers := shared.Parallelize("kill") if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalInt("max-workers") + maxWorkers = c.GlobalFlags.MaxWorks } logrus.Debugf("Setting maximum workers to %d", maxWorkers) diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index dca2f5022..60acd1f5b 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -1,15 +1,15 @@ package libpodruntime import ( + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" - "github.com/urfave/cli" ) // GetRuntime generates a new libpod runtime configured by command line options -func GetRuntime(c *cli.Context) (*libpod.Runtime, error) { +func GetRuntime(c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { options := []libpod.RuntimeOption{} storageOpts, volumePath, err := util.GetDefaultStoreOptions() @@ -17,29 +17,39 @@ func GetRuntime(c *cli.Context) (*libpod.Runtime, error) { return nil, err } - if c.IsSet("uidmap") || c.IsSet("gidmap") || c.IsSet("subuidmap") || c.IsSet("subgidmap") { - mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap")) + uidmapFlag := c.Flags().Lookup("uidmap") + gidmapFlag := c.Flags().Lookup("gidmap") + subuidname := c.Flags().Lookup("subuidname") + subgidname := c.Flags().Lookup("subgidname") + if (uidmapFlag != nil && gidmapFlag != nil && subuidname != nil && subgidname != nil) && + (uidmapFlag.Changed || gidmapFlag.Changed || subuidname.Changed || subgidname.Changed) { + uidmapVal, _ := c.Flags().GetStringSlice("uidmap") + gidmapVal, _ := c.Flags().GetStringSlice("gidmap") + subuidVal, _ := c.Flags().GetString("subuidname") + subgidVal, _ := c.Flags().GetString("subgidname") + mappings, err := util.ParseIDMapping(uidmapVal, gidmapVal, subuidVal, subgidVal) if err != nil { return nil, err } storageOpts.UIDMap = mappings.UIDMap storageOpts.GIDMap = mappings.GIDMap + } - if c.GlobalIsSet("root") { - storageOpts.GraphRoot = c.GlobalString("root") + if c.Flags().Changed("root") { + storageOpts.GraphRoot = c.GlobalFlags.Root } - if c.GlobalIsSet("runroot") { - storageOpts.RunRoot = c.GlobalString("runroot") + if c.Flags().Changed("runroot") { + storageOpts.RunRoot = c.GlobalFlags.Runroot } if len(storageOpts.RunRoot) > 50 { return nil, errors.New("the specified runroot is longer than 50 characters") } - if c.GlobalIsSet("storage-driver") { - storageOpts.GraphDriverName = c.GlobalString("storage-driver") + if c.Flags().Changed("storage-driver") { + storageOpts.GraphDriverName = c.GlobalFlags.StorageDriver } - if c.GlobalIsSet("storage-opt") { - storageOpts.GraphDriverOptions = c.GlobalStringSlice("storage-opt") + if c.Flags().Changed("storage-opt") { + storageOpts.GraphDriverOptions = c.GlobalFlags.StorageOpts } options = append(options, libpod.WithStorageConfig(storageOpts)) @@ -47,23 +57,23 @@ func GetRuntime(c *cli.Context) (*libpod.Runtime, error) { // TODO CLI flags for image config? // TODO CLI flag for signature policy? - if c.GlobalIsSet("namespace") { - options = append(options, libpod.WithNamespace(c.GlobalString("namespace"))) + if len(c.GlobalFlags.Namespace) > 0 { + options = append(options, libpod.WithNamespace(c.GlobalFlags.Namespace)) } - if c.GlobalIsSet("runtime") { - options = append(options, libpod.WithOCIRuntime(c.GlobalString("runtime"))) + if c.Flags().Changed("runtime") { + options = append(options, libpod.WithOCIRuntime(c.GlobalFlags.Runtime)) } - if c.GlobalIsSet("conmon") { - options = append(options, libpod.WithConmonPath(c.GlobalString("conmon"))) + if c.Flags().Changed("conmon") { + options = append(options, libpod.WithConmonPath(c.GlobalFlags.ConmonPath)) } - if c.GlobalIsSet("tmpdir") { - options = append(options, libpod.WithTmpDir(c.GlobalString("tmpdir"))) + if c.Flags().Changed("tmpdir") { + options = append(options, libpod.WithTmpDir(c.GlobalFlags.TmpDir)) } - if c.GlobalIsSet("cgroup-manager") { - options = append(options, libpod.WithCgroupManager(c.GlobalString("cgroup-manager"))) + if c.Flags().Changed("cgroup-manager") { + options = append(options, libpod.WithCgroupManager(c.GlobalFlags.CGroupManager)) } else { if rootless.IsRootless() { options = append(options, libpod.WithCgroupManager("cgroupfs")) @@ -73,29 +83,37 @@ func GetRuntime(c *cli.Context) (*libpod.Runtime, error) { // TODO flag to set libpod static dir? // TODO flag to set libpod tmp dir? - if c.GlobalIsSet("cni-config-dir") { - options = append(options, libpod.WithCNIConfigDir(c.GlobalString("cni-config-dir"))) + if c.Flags().Changed("cni-config-dir") { + options = append(options, libpod.WithCNIConfigDir(c.GlobalFlags.CniConfigDir)) } - if c.GlobalIsSet("default-mounts-file") { - options = append(options, libpod.WithDefaultMountsFile(c.GlobalString("default-mounts-file"))) + if c.Flags().Changed("default-mounts-file") { + options = append(options, libpod.WithDefaultMountsFile(c.GlobalFlags.DefaultMountsFile)) } - if c.GlobalIsSet("hooks-dir") { - options = append(options, libpod.WithHooksDir(c.GlobalStringSlice("hooks-dir")...)) + if c.Flags().Changed("hooks-dir") { + options = append(options, libpod.WithHooksDir(c.GlobalFlags.HooksDir...)) } // TODO flag to set CNI plugins dir? + // TODO I dont think these belong here? + // Will follow up with a different PR to address + // // Pod create options - if c.IsSet("infra-image") { - options = append(options, libpod.WithDefaultInfraImage(c.String("infra-image"))) + + infraImageFlag := c.Flags().Lookup("infra-image") + if infraImageFlag != nil && infraImageFlag.Changed { + infraImage, _ := c.Flags().GetString("infra-image") + options = append(options, libpod.WithDefaultInfraImage(infraImage)) } - if c.IsSet("infra-command") { - options = append(options, libpod.WithDefaultInfraCommand(c.String("infra-command"))) + infraCommandFlag := c.Flags().Lookup("infra-command") + if infraCommandFlag != nil && infraImageFlag.Changed { + infraCommand, _ := c.Flags().GetString("infra-command") + options = append(options, libpod.WithDefaultInfraCommand(infraCommand)) } options = append(options, libpod.WithVolumePath(volumePath)) - if c.IsSet("config") { - return libpod.NewRuntimeFromConfig(c.String("config"), options...) + if c.Flags().Changed("config") { + return libpod.NewRuntimeFromConfig(c.GlobalFlags.Config, options...) } return libpod.NewRuntime(options...) } diff --git a/cmd/podman/load.go b/cmd/podman/load.go index f39ee4487..90268ca8f 100644 --- a/cmd/podman/load.go +++ b/cmd/podman/load.go @@ -9,45 +9,44 @@ import ( "github.com/containers/image/directory" dockerarchive "github.com/containers/image/docker/archive" ociarchive "github.com/containers/image/oci/archive" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod/image" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - loadFlags = []cli.Flag{ - cli.StringFlag{ - Name: "input, i", - Usage: "Read from archive file, default is STDIN", - Value: "/dev/stdin", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Suppress the output", - }, - cli.StringFlag{ - Name: "signature-policy", - Usage: "`pathname` of signature policy file (not usually used)", - }, - } + loadCommand cliconfig.LoadValues + loadDescription = "Loads the image from docker-archive stored on the local machine." - loadCommand = cli.Command{ - Name: "load", - Usage: "Load an image from docker archive", - Description: loadDescription, - Flags: sortFlags(loadFlags), - Action: loadCmd, - ArgsUsage: "", - OnUsageError: usageErrorHandler, + _loadCommand = &cobra.Command{ + Use: "load", + Short: "Load an image from docker archive", + Long: loadDescription, + RunE: func(cmd *cobra.Command, args []string) error { + loadCommand.InputArgs = args + loadCommand.GlobalFlags = MainGlobalOpts + return loadCmd(&loadCommand) + }, } ) +func init() { + loadCommand.Command = _loadCommand + flags := loadCommand.Flags() + flags.StringVarP(&loadCommand.Input, "input", "i", "/dev/stdin", "Read from archive file, default is STDIN") + flags.BoolVarP(&loadCommand.Quiet, "quiet", "q", false, "Suppress the output") + flags.StringVar(&loadCommand.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file (not usually used)") + + rootCmd.AddCommand(loadCommand.Command) +} + // loadCmd gets the image/file to be loaded from the command line // and calls loadImage to load the image to containers-storage -func loadCmd(c *cli.Context) error { +func loadCmd(c *cliconfig.LoadValues) error { - args := c.Args() + args := c.InputArgs var imageName string if len(args) == 1 { @@ -56,17 +55,14 @@ func loadCmd(c *cli.Context) error { if len(args) > 1 { return errors.New("too many arguments. Requires exactly 1") } - if err := validateFlags(c, loadFlags); err != nil { - return err - } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - input := c.String("input") + input := c.Input if input == "/dev/stdin" { fi, err := os.Stdin.Stat() @@ -101,7 +97,7 @@ func loadCmd(c *cli.Context) error { } var writer io.Writer - if !c.Bool("quiet") { + if !c.Quiet { writer = os.Stderr } @@ -110,18 +106,18 @@ func loadCmd(c *cli.Context) error { var newImages []*image.Image src, err := dockerarchive.ParseReference(input) // FIXME? We should add dockerarchive.NewReference() if err == nil { - newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.String("signature-policy"), writer) + newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.SignaturePolicy, writer) } if err != nil { // generate full src name with specified image:tag src, err := ociarchive.NewReference(input, imageName) // imageName may be "" if err == nil { - newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.String("signature-policy"), writer) + newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.SignaturePolicy, writer) } if err != nil { src, err := directory.NewReference(input) if err == nil { - newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.String("signature-policy"), writer) + newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.SignaturePolicy, writer) } if err != nil { return errors.Wrapf(err, "error pulling %q", input) diff --git a/cmd/podman/login.go b/cmd/podman/login.go index fc7b39ed8..5ab0713e5 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -9,55 +9,48 @@ import ( "github.com/containers/image/docker" "github.com/containers/image/pkg/docker/config" "github.com/containers/image/types" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/common" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" "golang.org/x/crypto/ssh/terminal" ) var ( - loginFlags = []cli.Flag{ - cli.StringFlag{ - Name: "password, p", - Usage: "Password for registry", - }, - cli.StringFlag{ - Name: "username, u", - Usage: "Username for registry", - }, - cli.StringFlag{ - Name: "authfile", - Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ", - }, - cli.StringFlag{ - Name: "cert-dir", - Usage: "Pathname of a directory containing TLS certificates and keys used to connect to the registry", - }, - cli.BoolTFlag{ - Name: "get-login", - Usage: "Return the current login user for the registry", - }, - cli.BoolTFlag{ - Name: "tls-verify", - Usage: "Require HTTPS and verify certificates when contacting registries (default: true)", - }, - } + loginCommand cliconfig.LoginValues + loginDescription = "Login to a container registry on a specified server." - loginCommand = cli.Command{ - Name: "login", - Usage: "Login to a container registry", - Description: loginDescription, - Flags: sortFlags(loginFlags), - Action: loginCmd, - ArgsUsage: "REGISTRY", - OnUsageError: usageErrorHandler, + _loginCommand = &cobra.Command{ + Use: "login", + Short: "Login to a container registry", + Long: loginDescription, + RunE: func(cmd *cobra.Command, args []string) error { + loginCommand.InputArgs = args + loginCommand.GlobalFlags = MainGlobalOpts + return loginCmd(&loginCommand) + }, + Example: "REGISTRY", } ) +func init() { + loginCommand.Command = _loginCommand + flags := loginCommand.Flags() + + flags.StringVar(&loginCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&loginCommand.CertDir, "cert-dir", "", "Pathname of a directory containing TLS certificates and keys used to connect to the registry") + flags.BoolVar(&loginCommand.GetLogin, "get-login", true, "Return the current login user for the registry") + flags.StringVarP(&loginCommand.Password, "password", "p", "", "Password for registry") + flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.StringVarP(&loginCommand.Username, "username", "u", "", "Username for registry") + + rootCmd.AddCommand(loginCommand.Command) +} + // loginCmd uses the authentication package to store a user's authenticated credentials // in an auth.json file for future use -func loginCmd(c *cli.Context) error { - args := c.Args() +func loginCmd(c *cliconfig.LoginValues) error { + args := c.InputArgs if len(args) > 1 { return errors.Errorf("too many arguments, login takes only 1 argument") } @@ -65,17 +58,17 @@ func loginCmd(c *cli.Context) error { return errors.Errorf("please specify a registry to login to") } server := registryFromFullName(scrubServer(args[0])) - authfile := getAuthFile(c.String("authfile")) + authfile := getAuthFile(c.Authfile) sc := common.GetSystemContext("", authfile, false) - if c.IsSet("tls-verify") { - sc.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify")) + if c.Flag("tls-verify").Changed { + sc.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) } - if c.String("cert-dir") != "" { - sc.DockerCertPath = c.String("cert-dir") + if c.CertDir != "" { + sc.DockerCertPath = c.CertDir } - if c.IsSet("get-login") { + if c.Flag("get-login").Changed { user, err := config.GetUserLoggedIn(sc, server) if err != nil { @@ -98,7 +91,7 @@ func loginCmd(c *cli.Context) error { ctx := getContext() // If no username and no password is specified, try to use existing ones. - if c.String("username") == "" && c.String("password") == "" { + if c.Username == "" && c.Password == "" { fmt.Println("Authenticating with existing credentials...") if err := docker.CheckAuth(ctx, sc, userFromAuthFile, passFromAuthFile, server); err == nil { fmt.Println("Existing credentials are valid. Already logged in to", server) @@ -107,7 +100,7 @@ func loginCmd(c *cli.Context) error { fmt.Println("Existing credentials are invalid, please enter valid username and password") } - username, password, err := getUserAndPass(c.String("username"), c.String("password"), userFromAuthFile) + username, password, err := getUserAndPass(c.Username, c.Password, userFromAuthFile) if err != nil { return errors.Wrapf(err, "error getting username and password") } diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index 3cdb606b5..02bde7857 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -4,53 +4,56 @@ import ( "fmt" "github.com/containers/image/pkg/docker/config" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/common" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - logoutFlags = []cli.Flag{ - cli.StringFlag{ - Name: "authfile", - Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ", - }, - cli.BoolFlag{ - Name: "all, a", - Usage: "Remove the cached credentials for all registries in the auth file", - }, - } + logoutCommand cliconfig.LogoutValues logoutDescription = "Remove the cached username and password for the registry." - logoutCommand = cli.Command{ - Name: "logout", - Usage: "Logout of a container registry", - Description: logoutDescription, - Flags: sortFlags(logoutFlags), - Action: logoutCmd, - ArgsUsage: "REGISTRY", - OnUsageError: usageErrorHandler, + _logoutCommand = &cobra.Command{ + Use: "logout", + Short: "Logout of a container registry", + Long: logoutDescription, + RunE: func(cmd *cobra.Command, args []string) error { + logoutCommand.InputArgs = args + logoutCommand.GlobalFlags = MainGlobalOpts + return logoutCmd(&logoutCommand) + }, + Example: "REGISTRY", } ) +func init() { + logoutCommand.Command = _logoutCommand + flags := logoutCommand.Flags() + flags.BoolVarP(&logoutCommand.All, "all", "a", false, "Remove the cached credentials for all registries in the auth file") + flags.StringVar(&logoutCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") + + rootCmd.AddCommand(logoutCommand.Command) +} + // logoutCmd uses the authentication package to remove the authenticated of a registry // stored in the auth.json file -func logoutCmd(c *cli.Context) error { - args := c.Args() +func logoutCmd(c *cliconfig.LogoutValues) error { + args := c.InputArgs if len(args) > 1 { return errors.Errorf("too many arguments, logout takes at most 1 argument") } - if len(args) == 0 && !c.IsSet("all") { + if len(args) == 0 && !c.All { return errors.Errorf("registry must be given") } var server string if len(args) == 1 { server = scrubServer(args[0]) } - authfile := getAuthFile(c.String("authfile")) + authfile := getAuthFile(c.Authfile) sc := common.GetSystemContext("", authfile, false) - if c.Bool("all") { + if c.All { if err := config.RemoveAllAuthentication(sc); err != nil { return err } diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go index 75947c34e..83f900e63 100644 --- a/cmd/podman/logs.go +++ b/cmd/podman/logs.go @@ -4,91 +4,82 @@ import ( "os" "time" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/logs" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - logsFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "details", - Usage: "Show extra details provided to the logs", - Hidden: true, - }, - cli.BoolFlag{ - Name: "follow, f", - Usage: "Follow log output. The default is false", - }, - cli.StringFlag{ - Name: "since", - Usage: "Show logs since TIMESTAMP", - }, - cli.Uint64Flag{ - Name: "tail", - Usage: "Output the specified number of LINES at the end of the logs. Defaults to 0, which prints all lines", - }, - cli.BoolFlag{ - Name: "timestamps, t", - Usage: "Output the timestamps in the log", - }, - LatestFlag, - } + logsCommand cliconfig.LogsValues logsDescription = "The podman logs command batch-retrieves whatever logs are present for a container at the time of execution. This does not guarantee execution" + "order when combined with podman run (i.e. your run may not have generated any logs at the time you execute podman logs" - logsCommand = cli.Command{ - Name: "logs", - Usage: "Fetch the logs of a container", - Description: logsDescription, - Flags: sortFlags(logsFlags), - Action: logsCmd, - ArgsUsage: "CONTAINER", - SkipArgReorder: true, - OnUsageError: usageErrorHandler, - UseShortOptionHandling: true, + _logsCommand = &cobra.Command{ + Use: "logs", + Short: "Fetch the logs of a container", + Long: logsDescription, + RunE: func(cmd *cobra.Command, args []string) error { + logsCommand.InputArgs = args + logsCommand.GlobalFlags = MainGlobalOpts + return logsCmd(&logsCommand) + }, + Example: "CONTAINER", } ) -func logsCmd(c *cli.Context) error { +func init() { + logsCommand.Command = _logsCommand + flags := logsCommand.Flags() + flags.BoolVar(&logsCommand.Details, "details", false, "Show extra details provided to the logs") + flags.BoolVarP(&logsCommand.Follow, "follow", "f", false, "Follow log output. The default is false") + flags.BoolVarP(&waitCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.StringVar(&logsCommand.Since, "since", "", "Show logs since TIMESTAMP") + flags.Uint64Var(&logsCommand.Tail, "tail", 0, "Output the specified number of LINES at the end of the logs. Defaults to 0, which prints all lines") + flags.BoolVarP(&logsCommand.Timestamps, "timestamps", "t", false, "Output the timestamps in the log") + flags.MarkHidden("details") + + flags.SetInterspersed(false) + + rootCmd.AddCommand(logsCommand.Command) +} + +func logsCmd(c *cliconfig.LogsValues) error { var ctr *libpod.Container var err error - if err := validateFlags(c, logsFlags); err != nil { - return err - } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - args := c.Args() - if len(args) != 1 && !c.Bool("latest") { + args := c.InputArgs + if len(args) != 1 && !c.Latest { return errors.Errorf("'podman logs' requires exactly one container name/ID") } sinceTime := time.Time{} - if c.IsSet("since") { + if c.Flag("since").Changed { // parse time, error out if something is wrong - since, err := parseInputTime(c.String("since")) + since, err := parseInputTime(c.Since) if err != nil { - return errors.Wrapf(err, "could not parse time: %q", c.String("since")) + return errors.Wrapf(err, "could not parse time: %q", c.Since) } sinceTime = since } opts := &logs.LogOptions{ - Details: c.Bool("details"), - Follow: c.Bool("follow"), + Details: c.Details, + Follow: c.Follow, Since: sinceTime, - Tail: c.Uint64("tail"), - Timestamps: c.Bool("timestamps"), + Tail: c.Tail, + Timestamps: c.Timestamps, } - if c.Bool("latest") { + if c.Latest { ctr, err = runtime.GetLatestContainer() } else { ctr, err = runtime.LookupContainer(args[0]) diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 8c08b2bfb..32ecaede7 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -6,9 +6,10 @@ import ( "os" "os/exec" "runtime/pprof" - "sort" + "strings" "syscall" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" _ "github.com/containers/libpod/pkg/hooks/0.1.0" "github.com/containers/libpod/pkg/rootless" @@ -17,7 +18,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" lsyslog "github.com/sirupsen/logrus/hooks/syslog" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) // This is populated by the Makefile from the VERSION file @@ -26,198 +27,159 @@ var ( exitCode = 125 ) -var cmdsNotRequiringRootless = map[string]bool{ - "help": true, - "version": true, - "create": true, - "exec": true, - "export": true, - // `info` must be executed in an user namespace. - // If this change, please also update libpod.refreshRootless() - "login": true, - "logout": true, - "mount": true, - "kill": true, - "pause": true, - "restart": true, - "run": true, - "unpause": true, - "search": true, - "stats": true, - "stop": true, - "top": true, +var cmdsNotRequiringRootless = map[*cobra.Command]bool{ + _versionCommand: true, + _createCommand: true, + _execCommand: true, + _exportCommand: true, + //// `info` must be executed in an user namespace. + //// If this change, please also update libpod.refreshRootless() + _loginCommand: true, + _logoutCommand: true, + _mountCommand: true, + _killCommand: true, + _pauseCommand: true, + _restartCommand: true, + _runCommmand: true, + _unpauseCommand: true, + _searchCommand: true, + _statsCommand: true, + _stopCommand: true, + _topCommand: true, } -type commandSorted []cli.Command - -func (a commandSorted) Len() int { return len(a) } -func (a commandSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type commandSortedAlpha struct{ commandSorted } - -func (a commandSortedAlpha) Less(i, j int) bool { - return a.commandSorted[i].Name < a.commandSorted[j].Name +var rootCmd = &cobra.Command{ + Use: "podman", + Long: "manage pods and images", + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + return before(cmd, args) + }, + PersistentPostRunE: func(cmd *cobra.Command, args []string) error { + return after(cmd, args) + }, + SilenceUsage: true, + SilenceErrors: true, } -type flagSorted []cli.Flag - -func (a flagSorted) Len() int { return len(a) } -func (a flagSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +var MainGlobalOpts cliconfig.MainFlags + +func init() { + cobra.OnInitialize(initConfig) + rootCmd.TraverseChildren = true + rootCmd.Version = version.Version + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", "", "Cgroup manager to use (cgroupfs or systemd, default systemd)") + // -c is deprecated due to conflict with -c on subcommands + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Config, "config", "", "Path of a libpod config file detailing container server configuration options") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConmonPath, "conmon", "", "Path of the conmon binary") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CniConfigDir, "cni-config-dir", "", "Path of the configuration directory for CNI networks") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file") + rootCmd.PersistentFlags().MarkHidden("defaults-mount-file") + rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error (default), fatal or panic") + rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") + rootCmd.PersistentFlags().MarkHidden("max-workers") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", "", "Set the libpod namespace, used to create separate views of the containers and pods on the system") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Root, "root", "", "Path to the root directory in which data, including images, is stored") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runtime, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") + // -s is depracated due to conflict with -s on subcommands + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") + rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") + rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console") + + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory") -type flagSortedAlpha struct{ flagSorted } - -func (a flagSortedAlpha) Less(i, j int) bool { - return a.flagSorted[i].GetName() < a.flagSorted[j].GetName() +} +func initConfig() { + // we can do more stuff in here. } -func main() { - debug := false - cpuProfile := false - - if reexec.Init() { - return - } - - app := cli.NewApp() - app.Name = "podman" - app.Usage = "manage pods and images" - app.OnUsageError = usageErrorHandler - app.CommandNotFound = commandNotFoundHandler - - app.Version = version.Version - - app.Commands = []cli.Command{ - containerCommand, - exportCommand, - historyCommand, - imageCommand, - imagesCommand, - importCommand, - infoCommand, - inspectCommand, - pullCommand, - rmiCommand, - systemCommand, - tagCommand, - versionCommand, - } - - app.Commands = append(app.Commands, getAppCommands()...) - sort.Sort(commandSortedAlpha{app.Commands}) - - if varlinkCommand != nil { - app.Commands = append(app.Commands, *varlinkCommand) +func before(cmd *cobra.Command, args []string) error { + if err := libpod.SetXdgRuntimeDir(""); err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) } - - app.Before = func(c *cli.Context) error { - if err := libpod.SetXdgRuntimeDir(""); err != nil { - logrus.Errorf(err.Error()) - os.Exit(1) - } - args := c.Args() - if args.Present() && rootless.IsRootless() { - if _, notRequireRootless := cmdsNotRequiringRootless[args.First()]; !notRequireRootless { - became, ret, err := rootless.BecomeRootInUserNS() - if err != nil { - logrus.Errorf(err.Error()) - os.Exit(1) - } - if became { - os.Exit(ret) - } + if rootless.IsRootless() { + notRequireRootless := cmdsNotRequiringRootless[cmd] + if !notRequireRootless && !strings.HasPrefix(cmd.Use, "help") { + became, ret, err := rootless.BecomeRootInUserNS() + if err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) } - } - if c.GlobalBool("syslog") { - hook, err := lsyslog.NewSyslogHook("", "", syslog.LOG_INFO, "") - if err == nil { - logrus.AddHook(hook) + if became { + os.Exit(ret) } } - logLevel := c.GlobalString("log-level") - if logLevel != "" { - level, err := logrus.ParseLevel(logLevel) - if err != nil { - return err - } + } - logrus.SetLevel(level) + if MainGlobalOpts.Syslog { + hook, err := lsyslog.NewSyslogHook("", "", syslog.LOG_INFO, "") + if err == nil { + logrus.AddHook(hook) } + } - rlimits := new(syscall.Rlimit) - rlimits.Cur = 1048576 - rlimits.Max = 1048576 - if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { - return errors.Wrapf(err, "error getting rlimits") - } - rlimits.Cur = rlimits.Max - if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { - return errors.Wrapf(err, "error setting new rlimits") - } + // Set log level + level, err := logrus.ParseLevel(MainGlobalOpts.LogLevel) + if err != nil { + return err + } + logrus.SetLevel(level) + + rlimits := new(syscall.Rlimit) + rlimits.Cur = 1048576 + rlimits.Max = 1048576 + if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { + return errors.Wrapf(err, "error getting rlimits") } - - if rootless.IsRootless() { - logrus.Info("running as rootless") + rlimits.Cur = rlimits.Max + if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { + return errors.Wrapf(err, "error setting new rlimits") } + } - // Be sure we can create directories with 0755 mode. - syscall.Umask(0022) + if rootless.IsRootless() { + logrus.Info("running as rootless") + } - if logLevel == "debug" { - debug = true + // Be sure we can create directories with 0755 mode. + syscall.Umask(0022) + if cmd.Flag("cpu-profile").Changed { + f, err := os.Create(MainGlobalOpts.CpuProfile) + if err != nil { + return errors.Wrapf(err, "unable to create cpu profiling file %s", + MainGlobalOpts.CpuProfile) } - if c.GlobalIsSet("cpu-profile") { - f, err := os.Create(c.GlobalString("cpu-profile")) - if err != nil { - return errors.Wrapf(err, "unable to create cpu profiling file %s", - c.GlobalString("cpu-profile")) - } - cpuProfile = true - pprof.StartCPUProfile(f) - } - return nil - } - app.After = func(*cli.Context) error { - // called by Run() when the command handler succeeds - if cpuProfile { - pprof.StopCPUProfile() - } - return nil - } - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "config, c", - Usage: "Path of a libpod config file detailing container server configuration options", - Hidden: true, - }, - cli.StringFlag{ - Name: "cpu-profile", - Usage: "Path for the cpu profiling results", - }, - cli.StringFlag{ - Name: "log-level", - Usage: "Log messages above specified level: debug, info, warn, error (default), fatal or panic", - Value: "error", - }, - cli.StringFlag{ - Name: "tmpdir", - Usage: "Path to the tmp directory", - }, + pprof.StartCPUProfile(f) } + return nil +} - app.Flags = append(app.Flags, getMainAppFlags()...) - sort.Sort(flagSortedAlpha{app.Flags}) +func after(cmd *cobra.Command, args []string) error { + if cmd.Flag("cpu-profile").Changed { + pprof.StopCPUProfile() + } + return nil +} - // Check if /etc/containers/registries.conf exists when running in - // in a local environment. - CheckForRegistries() +func main() { + //debug := false + //cpuProfile := false - if err := app.Run(os.Args); err != nil { - if debug { + if reexec.Init() { + return + } + if err := rootCmd.Execute(); err != nil { + if MainGlobalOpts.LogLevel == "debug" { logrus.Errorf(err.Error()) } else { - // Retrieve the exit error from the exec call, if it exists if ee, ok := err.(*exec.ExitError); ok { if status, ok := ee.Sys().(syscall.WaitStatus); ok { exitCode = status.ExitStatus() @@ -233,6 +195,11 @@ func main() { if exitCode == 125 { exitCode = 0 } + } + + // Check if /etc/containers/registries.conf exists when running in + // in a local environment. + CheckForRegistries() os.Exit(exitCode) } diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go index f71d47434..36ca8bcfb 100644 --- a/cmd/podman/mount.go +++ b/cmd/podman/mount.go @@ -5,15 +5,18 @@ import ( "fmt" "os" + "github.com/containers/libpod/cmd/podman/cliconfig" of "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + mountCommand cliconfig.MountValues + mountDescription = ` podman mount Lists all mounted containers mount points @@ -22,32 +25,29 @@ var ( Mounts the specified container and outputs the mountpoint ` - mountFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Mount all containers", - }, - cli.StringFlag{ - Name: "format", - Usage: "Change the output format to Go template", + _mountCommand = &cobra.Command{ + Use: "mount", + Short: "Mount a working container's root filesystem", + Long: mountDescription, + RunE: func(cmd *cobra.Command, args []string) error { + mountCommand.InputArgs = args + mountCommand.GlobalFlags = MainGlobalOpts + return mountCmd(&mountCommand) }, - cli.BoolFlag{ - Name: "notruncate", - Usage: "Do not truncate output", - }, - LatestFlag, - } - mountCommand = cli.Command{ - Name: "mount", - Usage: "Mount a working container's root filesystem", - Description: mountDescription, - Action: mountCmd, - ArgsUsage: "[CONTAINER-NAME-OR-ID [...]]", - Flags: sortFlags(mountFlags), - OnUsageError: usageErrorHandler, } ) +func init() { + mountCommand.Command = _mountCommand + flags := mountCommand.Flags() + flags.BoolVarP(&mountCommand.All, "all", "a", false, "Mount all containers") + flags.StringVar(&mountCommand.Format, "format", "", "Change the output format to Go template") + flags.BoolVarP(&mountCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVar(&mountCommand.NoTrunc, "notruncate", false, "Do not truncate output") + + rootCmd.AddCommand(mountCommand.Command) +} + // jsonMountPoint stores info about each container type jsonMountPoint struct { ID string `json:"id"` @@ -55,15 +55,12 @@ type jsonMountPoint struct { MountPoint string `json:"mountpoint"` } -func mountCmd(c *cli.Context) error { - if err := validateFlags(c, mountFlags); err != nil { - return err - } +func mountCmd(c *cliconfig.MountValues) error { if os.Geteuid() != 0 { rootless.SetSkipStorageSetup(true) } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -85,11 +82,11 @@ func mountCmd(c *cli.Context) error { } } - if c.Bool("all") && c.Bool("latest") { + if c.All && c.Latest { return errors.Errorf("--all and --latest cannot be used together") } - mountContainers, err := getAllOrLatestContainers(c, runtime, -1, "all") + mountContainers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all") if err != nil { if len(mountContainers) == 0 { return err @@ -102,9 +99,9 @@ func mountCmd(c *cli.Context) error { of.JSONString: true, } - json := c.String("format") == of.JSONString - if !formats[c.String("format")] { - return errors.Errorf("%q is not a supported format", c.String("format")) + json := c.Format == of.JSONString + if !formats[c.Format] { + return errors.Errorf("%q is not a supported format", c.Format) } var lastError error @@ -149,7 +146,7 @@ func mountCmd(c *cli.Context) error { continue } - if c.Bool("notruncate") { + if c.NoTrunc { fmt.Printf("%-64s %s\n", container.ID(), mountPoint) } else { fmt.Printf("%-12.12s %s\n", container.ID(), mountPoint) diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go index 2e7182871..466a3b136 100644 --- a/cmd/podman/pause.go +++ b/cmd/podman/pause.go @@ -3,38 +3,44 @@ package main import ( "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - pauseFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Pause all running containers", - }, - } + pauseCommand cliconfig.PauseValues pauseDescription = ` podman pause Pauses one or more running containers. The container name or ID can be used. ` - pauseCommand = cli.Command{ - Name: "pause", - Usage: "Pause all the processes in one or more containers", - Description: pauseDescription, - Flags: pauseFlags, - Action: pauseCmd, - ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", - OnUsageError: usageErrorHandler, + _pauseCommand = &cobra.Command{ + Use: "pause", + Short: "Pause all the processes in one or more containers", + Long: pauseDescription, + RunE: func(cmd *cobra.Command, args []string) error { + pauseCommand.InputArgs = args + pauseCommand.GlobalFlags = MainGlobalOpts + return pauseCmd(&pauseCommand) + }, + Example: "CONTAINER-NAME [CONTAINER-NAME ...]", } ) -func pauseCmd(c *cli.Context) error { +func init() { + pauseCommand.Command = _pauseCommand + flags := pauseCommand.Flags() + flags.BoolVarP(&pauseCommand.All, "all", "a", false, "Pause all running containers") + + rootCmd.AddCommand(pauseCommand.Command) +} + +func pauseCmd(c *cliconfig.PauseValues) error { var ( pauseContainers []*libpod.Container pauseFuncs []shared.ParallelWorkerInput @@ -43,18 +49,18 @@ func pauseCmd(c *cli.Context) error { return errors.New("pause is not supported for rootless containers") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - args := c.Args() - if len(args) < 1 && !c.Bool("all") { + args := c.InputArgs + if len(args) < 1 && !c.All { return errors.Errorf("you must provide at least one container name or id") } - if c.Bool("all") { - containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") + if c.All { + containers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStateRunning, "running") if err != nil { return err } @@ -84,7 +90,7 @@ func pauseCmd(c *cli.Context) error { maxWorkers := shared.Parallelize("pause") if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalInt("max-workers") + maxWorkers = c.GlobalFlags.MaxWorks } logrus.Debugf("Setting maximum workers to %d", maxWorkers) diff --git a/cmd/podman/play.go b/cmd/podman/play.go index 4e09b2689..ff8320a6b 100644 --- a/cmd/podman/play.go +++ b/cmd/podman/play.go @@ -1,23 +1,23 @@ package main import ( - "github.com/urfave/cli" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" ) -var ( - playSubCommands = []cli.Command{ - playKubeCommand, - } +var playCommand cliconfig.PodmanCommand - playDescription = "Play a pod and its containers from a structured file." - playCommand = cli.Command{ - Name: "play", - Usage: "Play a container or pod", - Description: playDescription, - ArgsUsage: "", - Subcommands: playSubCommands, - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, - Hidden: true, +func init() { + var playDescription = "Play a pod and its containers from a structured file." + playCommand.Command = &cobra.Command{ + Use: "play", + Short: "Play a pod", + Long: playDescription, } -) + +} + +func init() { + playCommand.AddCommand(getPlaySubCommands()...) + rootCmd.AddCommand(playCommand.Command) +} diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go index 2d97e0e95..b314c1278 100644 --- a/cmd/podman/play_kube.go +++ b/cmd/podman/play_kube.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/containers/image/types" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" @@ -20,51 +21,40 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" "k8s.io/api/core/v1" ) var ( - playKubeFlags = []cli.Flag{ - cli.StringFlag{ - Name: "authfile", - Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ", - }, - cli.StringFlag{ - Name: "cert-dir", - Usage: "`Pathname` of a directory containing TLS certificates and keys", - }, - cli.StringFlag{ - Name: "creds", - Usage: "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Suppress output information when pulling images", - }, - cli.StringFlag{ - Name: "signature-policy", - Usage: "`Pathname` of signature policy file (not usually used)", - }, - cli.BoolTFlag{ - Name: "tls-verify", - Usage: "Require HTTPS and verify certificates when contacting registries (default: true)", - }, - } + playKubeCommand cliconfig.KubePlayValues playKubeDescription = "Play a Pod and its containers based on a Kubrernetes YAML" - playKubeCommand = cli.Command{ - Name: "kube", - Usage: "Play a pod based on Kubernetes YAML", - Description: playKubeDescription, - Action: playKubeYAMLCmd, - Flags: sortFlags(playKubeFlags), - ArgsUsage: "Kubernetes YAML file", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _playKubeCommand = &cobra.Command{ + Use: "kube", + Short: "Play a pod based on Kubernetes YAML", + Long: playKubeDescription, + RunE: func(cmd *cobra.Command, args []string) error { + playKubeCommand.InputArgs = args + playKubeCommand.GlobalFlags = MainGlobalOpts + return playKubeYAMLCmd(&playKubeCommand) + }, + Example: "Kubernetes YAML file", } ) -func playKubeYAMLCmd(c *cli.Context) error { +func init() { + playKubeCommand.Command = _playKubeCommand + flags := playKubeCommand.Flags() + flags.StringVar(&playKubeCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&playKubeCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + flags.StringVar(&playKubeCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.BoolVarP(&playKubeCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") + flags.StringVar(&playKubeCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") + flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + + rootCmd.AddCommand(playKubeCommand.Command) +} + +func playKubeYAMLCmd(c *cliconfig.KubePlayValues) error { var ( podOptions []libpod.PodCreateOption podYAML v1.Pod @@ -77,7 +67,7 @@ func playKubeYAMLCmd(c *cli.Context) error { if rootless.IsRootless() { return errors.Wrapf(libpod.ErrNotImplemented, "rootless users") } - args := c.Args() + args := c.InputArgs if len(args) > 1 { return errors.New("you can only play one kubernetes file at a time") } @@ -85,7 +75,7 @@ func playKubeYAMLCmd(c *cli.Context) error { return errors.New("you must supply at least one file") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -133,20 +123,20 @@ func playKubeYAMLCmd(c *cli.Context) error { "ipc": fmt.Sprintf("container:%s", podInfraID), "uts": fmt.Sprintf("container:%s", podInfraID), } - if !c.Bool("quiet") { + if !c.Quiet { writer = os.Stderr } dockerRegistryOptions := image2.DockerRegistryOptions{ DockerRegistryCreds: registryCreds, - DockerCertPath: c.String("cert-dir"), + DockerCertPath: c.CertDir, } - if c.IsSet("tls-verify") { - dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify")) + if c.Flag("tls-verify").Changed { + dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) } for _, container := range podYAML.Spec.Containers { - newImage, err := runtime.ImageRuntime().New(ctx, container.Image, c.String("signature-policy"), c.String("authfile"), writer, &dockerRegistryOptions, image2.SigningOptions{}, false, nil) + newImage, err := runtime.ImageRuntime().New(ctx, container.Image, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image2.SigningOptions{}, false, nil) if err != nil { return err } diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go index a30361134..4104c39c7 100644 --- a/cmd/podman/pod.go +++ b/cmd/podman/pod.go @@ -1,35 +1,24 @@ package main import ( - "github.com/urfave/cli" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" ) var ( podDescription = `Manage container pods. -Pods are a group of one or more containers sharing the same network, pid and ipc namespaces. -` - podSubCommands = []cli.Command{ - podCreateCommand, - podExistsCommand, - podInspectCommand, - podKillCommand, - podPauseCommand, - podPsCommand, - podRestartCommand, - podRmCommand, - podStartCommand, - podStatsCommand, - podStopCommand, - podTopCommand, - podUnpauseCommand, - } - podCommand = cli.Command{ - Name: "pod", - Usage: "Manage pods", - Description: podDescription, - UseShortOptionHandling: true, - Subcommands: podSubCommands, - OnUsageError: usageErrorHandler, - } +Pods are a group of one or more containers sharing the same network, pid and ipc namespaces.` ) +var podCommand = cliconfig.PodmanCommand{ + Command: &cobra.Command{ + Use: "pod", + Short: "Manage pods", + Long: podDescription, + }, +} + +func init() { + podCommand.AddCommand(getPodSubCommands()...) + rootCmd.AddCommand(podCommand.Command) +} diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index 967ce7610..9ac5d94a9 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -5,111 +5,81 @@ import ( "os" "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( // Kernel namespaces shared by default within a pod DefaultKernelNamespaces = "cgroup,ipc,net,uts" + podCreateCommand cliconfig.PodCreateValues + + podCreateDescription = "Creates a new empty pod. The pod ID is then" + + " printed to stdout. You can then start it at any time with the" + + " podman pod start <pod_id> command. The pod will be created with the" + + " initial state 'created'." + + _podCreateCommand = &cobra.Command{ + Use: "create", + Short: "Create a new empty pod", + Long: podCreateDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podCreateCommand.InputArgs = args + podCreateCommand.GlobalFlags = MainGlobalOpts + return podCreateCmd(&podCreateCommand) + }, + } ) -var podCreateDescription = "Creates a new empty pod. The pod ID is then" + - " printed to stdout. You can then start it at any time with the" + - " podman pod start <pod_id> command. The pod will be created with the" + - " initial state 'created'." - -var podCreateFlags = []cli.Flag{ - cli.StringFlag{ - Name: "cgroup-parent", - Usage: "Set parent cgroup for the pod", - }, - cli.BoolTFlag{ - Name: "infra", - Usage: "Create an infra container associated with the pod to share namespaces with", - }, - cli.StringFlag{ - Name: "infra-image", - Usage: "The image of the infra container to associate with the pod", - Value: libpod.DefaultInfraImage, - }, - cli.StringFlag{ - Name: "infra-command", - Usage: "The command to run on the infra container when the pod is started", - Value: libpod.DefaultInfraCommand, - }, - cli.StringSliceFlag{ - Name: "label-file", - Usage: "Read in a line delimited file of labels (default [])", - }, - cli.StringSliceFlag{ - Name: "label, l", - Usage: "Set metadata on pod (default [])", - }, - cli.StringFlag{ - Name: "name, n", - Usage: "Assign a name to the pod", - }, - cli.StringFlag{ - Name: "pod-id-file", - Usage: "Write the pod ID to the file", - }, - cli.StringSliceFlag{ - Name: "publish, p", - Usage: "Publish a container's port, or a range of ports, to the host (default [])", - }, - cli.StringFlag{ - Name: "share", - Usage: "A comma delimited list of kernel namespaces the pod will share", - Value: DefaultKernelNamespaces, - }, -} +func init() { + podCreateCommand.Command = _podCreateCommand + flags := podCreateCommand.Flags() + flags.SetInterspersed(false) + + flags.StringVar(&podCreateCommand.CgroupParent, "cgroup-parent", "", "Set parent cgroup for the pod") + flags.BoolVar(&podCreateCommand.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") + flags.StringVar(&podCreateCommand.InfraImage, "infra-image", libpod.DefaultInfraImage, "The image of the infra container to associate with the pod") + flags.StringVar(&podCreateCommand.InfraCommand, "infra-command", libpod.DefaultInfraCommand, "The command to run on the infra container when the pod is started") + flags.StringSliceVar(&podCreateCommand.LabelFile, "label-file", []string{}, "Read in a line delimited file of labels") + flags.StringSliceVarP(&podCreateCommand.Labels, "label", "l", []string{}, "Set metadata on pod (default [])") + flags.StringVarP(&podCreateCommand.Name, "name", "n", "", "Assign a name to the pod") + flags.StringVar(&podCreateCommand.PodIDFile, "pod-id-file", "", "Write the pod ID to the file") + flags.StringSliceVarP(&podCreateCommand.Publish, "publish", "p", []string{}, "Publish a container's port, or a range of ports, to the host (default [])") + flags.StringVar(&podCreateCommand.Share, "share", DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") -var podCreateCommand = cli.Command{ - Name: "create", - Usage: "Create a new empty pod", - Description: podCreateDescription, - Flags: sortFlags(podCreateFlags), - Action: podCreateCmd, - SkipArgReorder: true, - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, } -func podCreateCmd(c *cli.Context) error { +func podCreateCmd(c *cliconfig.PodCreateValues) error { var options []libpod.PodCreateOption var err error - if err = validateFlags(c, createFlags); err != nil { - return err - } - - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) var podIdFile *os.File - if c.IsSet("pod-id-file") && os.Geteuid() == 0 { - podIdFile, err = libpod.OpenExclusiveFile(c.String("pod-id-file")) + if c.Flag("pod-id-file").Changed && os.Geteuid() == 0 { + podIdFile, err = libpod.OpenExclusiveFile(c.PodIDFile) if err != nil && os.IsExist(err) { - return errors.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", c.String("pod-id-file")) + return errors.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", c.PodIDFile) } if err != nil { - return errors.Errorf("error opening pod-id-file %s", c.String("pod-id-file")) + return errors.Errorf("error opening pod-id-file %s", c.PodIDFile) } defer podIdFile.Close() defer podIdFile.Sync() } - if len(c.StringSlice("publish")) > 0 { - if !c.BoolT("infra") { + if len(c.Publish) > 0 { + if !c.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") } if rootless.IsRootless() { @@ -117,15 +87,15 @@ func podCreateCmd(c *cli.Context) error { } } - if !c.BoolT("infra") && c.IsSet("share") && c.String("share") != "none" && c.String("share") != "" { + if !c.Infra && c.Flag("share").Changed && c.Share != "none" && c.Share != "" { return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container") } - if c.IsSet("cgroup-parent") { - options = append(options, libpod.WithPodCgroupParent(c.String("cgroup-parent"))) + if c.Flag("cgroup-parent").Changed { + options = append(options, libpod.WithPodCgroupParent(c.CgroupParent)) } - labels, err := getAllLabels(c.StringSlice("label-file"), c.StringSlice("label")) + labels, err := getAllLabels(c.LabelFile, c.Labels) if err != nil { return errors.Wrapf(err, "unable to process labels") } @@ -133,21 +103,21 @@ func podCreateCmd(c *cli.Context) error { options = append(options, libpod.WithPodLabels(labels)) } - if c.IsSet("name") { - options = append(options, libpod.WithPodName(c.String("name"))) + if c.Flag("name").Changed { + options = append(options, libpod.WithPodName(c.Name)) } - if c.BoolT("infra") { + if c.Infra { options = append(options, libpod.WithInfraContainer()) - nsOptions, err := shared.GetNamespaceOptions(strings.Split(c.String("share"), ",")) + nsOptions, err := shared.GetNamespaceOptions(strings.Split(c.Share, ",")) if err != nil { return err } options = append(options, nsOptions...) } - if len(c.StringSlice("publish")) > 0 { - portBindings, err := shared.CreatePortBindings(c.StringSlice("publish")) + if len(c.Publish) > 0 { + portBindings, err := shared.CreatePortBindings(c.Publish) if err != nil { return err } diff --git a/cmd/podman/pod_inspect.go b/cmd/podman/pod_inspect.go index c7bbf31cd..f4c357e96 100644 --- a/cmd/podman/pod_inspect.go +++ b/cmd/podman/pod_inspect.go @@ -2,46 +2,53 @@ package main import ( "encoding/json" - "fmt" + + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - podInspectFlags = []cli.Flag{ - LatestPodFlag, - } + podInspectCommand cliconfig.PodInspectValues podInspectDescription = "Display the configuration for a pod by name or id" - podInspectCommand = cli.Command{ - Name: "inspect", - Usage: "Displays a pod configuration", - Description: podInspectDescription, - Flags: sortFlags(podInspectFlags), - Action: podInspectCmd, - UseShortOptionHandling: true, - ArgsUsage: "[POD_NAME_OR_ID]", - OnUsageError: usageErrorHandler, + _podInspectCommand = &cobra.Command{ + Use: "inspect", + Short: "Displays a pod configuration", + Long: podInspectDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podInspectCommand.InputArgs = args + podInspectCommand.GlobalFlags = MainGlobalOpts + return podInspectCmd(&podInspectCommand) + }, + Example: "[POD_NAME_OR_ID]", } ) -func podInspectCmd(c *cli.Context) error { +func init() { + podInspectCommand.Command = _podInspectCommand + flags := podInspectCommand.Flags() + flags.BoolVarP(&podInspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + +} + +func podInspectCmd(c *cliconfig.PodInspectValues) error { var ( pod *libpod.Pod ) - if err := checkMutuallyExclusiveFlags(c); err != nil { + if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil { return err } - args := c.Args() - runtime, err := libpodruntime.GetRuntime(c) + args := c.InputArgs + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - if c.Bool("latest") { + if c.Latest { pod, err = runtime.GetLatestPod() if err != nil { return errors.Wrapf(err, "unable to get latest pod") diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go index c8029eb46..43411a988 100644 --- a/cmd/podman/pod_kill.go +++ b/cmd/podman/pod_kill.go @@ -4,46 +4,45 @@ import ( "fmt" "syscall" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/docker/docker/pkg/signal" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - podKillFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Kill all containers in all pods", - }, - cli.StringFlag{ - Name: "signal, s", - Usage: "Signal to send to the containers in the pod", - Value: "KILL", - }, - LatestPodFlag, - } + podKillCommand cliconfig.PodKillValues podKillDescription = "The main process of each container inside the specified pod will be sent SIGKILL, or any signal specified with option --signal." - podKillCommand = cli.Command{ - Name: "kill", - Usage: "Send the specified signal or SIGKILL to containers in pod", - Description: podKillDescription, - Flags: sortFlags(podKillFlags), - Action: podKillCmd, - ArgsUsage: "[POD_NAME_OR_ID]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _podKillCommand = &cobra.Command{ + Use: "kill", + Short: "Send the specified signal or SIGKILL to containers in pod", + Long: podKillDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podKillCommand.InputArgs = args + podKillCommand.GlobalFlags = MainGlobalOpts + return podKillCmd(&podKillCommand) + }, + Example: "[POD_NAME_OR_ID]", } ) +func init() { + podKillCommand.Command = _podKillCommand + flags := podKillCommand.Flags() + flags.BoolVarP(&podKillCommand.All, "all", "a", false, "Kill all containers in all pods") + flags.BoolVarP(&podKillCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + flags.StringVarP(&podKillCommand.Signal, "signal", "s", "KILL", "Signal to send to the containers in the pod") +} + // podKillCmd kills one or more pods with a signal -func podKillCmd(c *cli.Context) error { - if err := checkMutuallyExclusiveFlags(c); err != nil { +func podKillCmd(c *cliconfig.PodKillValues) error { + if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil { return err } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -51,10 +50,10 @@ func podKillCmd(c *cli.Context) error { var killSignal uint = uint(syscall.SIGTERM) - if c.String("signal") != "" { + if c.Signal != "" { // Check if the signalString provided by the user is valid // Invalid signals will return err - sysSignal, err := signal.ParseSignal(c.String("signal")) + sysSignal, err := signal.ParseSignal(c.Signal) if err != nil { return err } @@ -64,7 +63,7 @@ func podKillCmd(c *cli.Context) error { // getPodsFromContext returns an error when a requested pod // isn't found. The only fatal error scenerio is when there are no pods // in which case the following loop will be skipped. - pods, lastError := getPodsFromContext(c, runtime) + pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime) for _, pod := range pods { ctr_errs, err := pod.Kill(killSignal) diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go index f29a0b8d1..a54783d88 100644 --- a/cmd/podman/pod_pause.go +++ b/cmd/podman/pod_pause.go @@ -2,43 +2,42 @@ package main import ( "fmt" - + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - podPauseFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Pause all running pods", + podPauseCommand cliconfig.PodPauseValues + podPauseDescription = `Pauses one or more pods. The pod name or ID can be used.` + _podPauseCommand = &cobra.Command{ + Use: "pause", + Short: "Pause one or more pods", + Long: podPauseDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podPauseCommand.InputArgs = args + podPauseCommand.GlobalFlags = MainGlobalOpts + return podPauseCmd(&podPauseCommand) }, - LatestPodFlag, - } - podPauseDescription = ` - Pauses one or more pods. The pod name or ID can be used. -` - - podPauseCommand = cli.Command{ - Name: "pause", - Usage: "Pause one or more pods", - Description: podPauseDescription, - Flags: sortFlags(podPauseFlags), - Action: podPauseCmd, - ArgsUsage: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + Example: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]", } ) -func podPauseCmd(c *cli.Context) error { - if err := checkMutuallyExclusiveFlags(c); err != nil { +func init() { + podPauseCommand.Command = _podPauseCommand + flags := podPauseCommand.Flags() + flags.BoolVarP(&podPauseCommand.All, "all", "a", false, "Pause all running pods") + flags.BoolVarP(&podPauseCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") +} + +func podPauseCmd(c *cliconfig.PodPauseValues) error { + if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil { return err } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -47,7 +46,7 @@ func podPauseCmd(c *cli.Context) error { // getPodsFromContext returns an error when a requested pod // isn't found. The only fatal error scenerio is when there are no pods // in which case the following loop will be skipped. - pods, lastError := getPodsFromContext(c, runtime) + pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime) for _, pod := range pods { ctr_errs, err := pod.Pause() diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go index 2030b9b04..991e294bf 100644 --- a/cmd/podman/pod_ps.go +++ b/cmd/podman/pod_ps.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" @@ -15,7 +16,7 @@ import ( "github.com/containers/libpod/pkg/util" "github.com/docker/go-units" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) const ( @@ -112,95 +113,68 @@ func (a podPsSortedStatus) Less(i, j int) bool { } var ( - podPsFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "ctr-names", - Usage: "Display the container names", - }, - cli.BoolFlag{ - Name: "ctr-ids", - Usage: "Display the container UUIDs. If no-trunc is not set they will be truncated", - }, - cli.BoolFlag{ - Name: "ctr-status", - Usage: "Display the container status", - }, - cli.StringFlag{ - Name: "filter, f", - Usage: "Filter output based on conditions given", - }, - cli.StringFlag{ - Name: "format", - Usage: "Pretty-print pods to JSON or using a Go template", - }, - cli.BoolFlag{ - Name: "latest, l", - Usage: "Show the latest pod created", - }, - cli.BoolFlag{ - Name: "namespace, ns", - Usage: "Display namespace information of the pod", - }, - cli.BoolFlag{ - Name: "no-trunc", - Usage: "Do not truncate pod and container IDs", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Print the numeric IDs of the pods only", - }, - cli.StringFlag{ - Name: "sort", - Usage: "Sort output by created, id, name, or number", - Value: "created", - }, - } + podPsCommand cliconfig.PodPsValues + podPsDescription = "List all pods on system including their names, ids and current state." - podPsCommand = cli.Command{ - Name: "ps", - Aliases: []string{"ls", "list"}, - Usage: "List pods", - Description: podPsDescription, - Flags: sortFlags(podPsFlags), - Action: podPsCmd, - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _podPsCommand = &cobra.Command{ + Use: "ps", + Aliases: []string{"ls", "list"}, + Short: "List pods", + Long: podPsDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podPsCommand.InputArgs = args + podPsCommand.GlobalFlags = MainGlobalOpts + return podPsCmd(&podPsCommand) + }, } ) -func podPsCmd(c *cli.Context) error { - if err := validateFlags(c, podPsFlags); err != nil { - return err - } +func init() { + podPsCommand.Command = _podPsCommand + flags := podPsCommand.Flags() + flags.BoolVar(&podPsCommand.CtrNames, "ctr-names", false, "Display the container names") + flags.BoolVar(&podPsCommand.CtrIDs, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated") + flags.BoolVar(&podPsCommand.CtrStatus, "ctr-status", false, "Display the container status") + flags.StringVarP(&podPsCommand.Filter, "filter", "f", "", "Filter output based on conditions given") + flags.StringVar(&podPsCommand.Format, "format", "", "Pretty-print pods to JSON or using a Go template") + flags.BoolVarP(&podPsCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + flags.BoolVar(&podPsCommand.Namespace, "namespace", false, "Display namespace information of the pod") + flags.BoolVar(&podPsCommand.Namespace, "ns", false, "Display namespace information of the pod") + flags.BoolVar(&podPsCommand.NoTrunc, "no-trunc", false, "Do not truncate pod and container IDs") + flags.BoolVarP(&podPsCommand.Quiet, "quiet", "q", false, "Print the numeric IDs of the pods only") + flags.StringVar(&podPsCommand.Sort, "sort", "created", "Sort output by created, id, name, or number") + +} +func podPsCmd(c *cliconfig.PodPsValues) error { if err := podPsCheckFlagsPassed(c); err != nil { return errors.Wrapf(err, "error with flags passed") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - if len(c.Args()) > 0 { + if len(c.InputArgs) > 0 { return errors.Errorf("too many arguments, ps takes no arguments") } opts := podPsOptions{ - NoTrunc: c.Bool("no-trunc"), - Quiet: c.Bool("quiet"), - Sort: c.String("sort"), - IdsOfContainers: c.Bool("ctr-ids"), - NamesOfContainers: c.Bool("ctr-names"), - StatusOfContainers: c.Bool("ctr-status"), + NoTrunc: c.NoTrunc, + Quiet: c.Quiet, + Sort: c.Sort, + IdsOfContainers: c.CtrIDs, + NamesOfContainers: c.CtrNames, + StatusOfContainers: c.CtrStatus, } opts.Format = genPodPsFormat(c) var filterFuncs []libpod.PodFilter - if c.String("filter") != "" { - filters := strings.Split(c.String("filter"), ",") + if c.Filter != "" { + filters := strings.Split(c.Filter, ",") for _, f := range filters { filterSplit := strings.Split(f, "=") if len(filterSplit) < 2 { @@ -215,7 +189,7 @@ func podPsCmd(c *cli.Context) error { } var pods []*libpod.Pod - if c.IsSet("latest") { + if c.Latest { pod, err := runtime.GetLatestPod() if err != nil { return err @@ -244,13 +218,13 @@ func podPsCmd(c *cli.Context) error { } // podPsCheckFlagsPassed checks if mutually exclusive flags are passed together -func podPsCheckFlagsPassed(c *cli.Context) error { +func podPsCheckFlagsPassed(c *cliconfig.PodPsValues) error { // quiet, and format with Go template are mutually exclusive flags := 0 - if c.Bool("quiet") { + if c.Quiet { flags++ } - if c.IsSet("format") && c.String("format") != formats.JSONString { + if c.Flag("format").Changed && c.Format != formats.JSONString { flags++ } if flags > 1 { @@ -342,20 +316,20 @@ func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) } // generate the template based on conditions given -func genPodPsFormat(c *cli.Context) string { +func genPodPsFormat(c *cliconfig.PodPsValues) string { format := "" - if c.String("format") != "" { + if c.Format != "" { // "\t" from the command line is not being recognized as a tab // replacing the string "\t" to a tab character if the user passes in "\t" - format = strings.Replace(c.String("format"), `\t`, "\t", -1) - } else if c.Bool("quiet") { + format = strings.Replace(c.Format, `\t`, "\t", -1) + } else if c.Quiet { format = formats.IDString } else { format = "table {{.ID}}\t{{.Name}}\t{{.Status}}\t{{.Created}}" if c.Bool("namespace") { format += "\t{{.Cgroup}}\t{{.Namespaces}}" } - if c.Bool("ctr-names") || c.Bool("ctr-ids") || c.Bool("ctr-status") { + if c.CtrNames || c.CtrIDs || c.CtrStatus { format += "\t{{.ContainerInfo}}" } else { format += "\t{{.NumberOfContainers}}" diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go index d9800cbf7..4d05205ea 100644 --- a/cmd/podman/pod_restart.go +++ b/cmd/podman/pod_restart.go @@ -3,40 +3,43 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - podRestartFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Restart all pods", - }, - LatestPodFlag, - } + podRestartCommand cliconfig.PodRestartValues podRestartDescription = `Restarts one or more pods. The pod ID or name can be used.` - - podRestartCommand = cli.Command{ - Name: "restart", - Usage: "Restart one or more pods", - Description: podRestartDescription, - Flags: sortFlags(podRestartFlags), - Action: podRestartCmd, - ArgsUsage: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _podRestartCommand = &cobra.Command{ + Use: "restart", + Short: "Restart one or more pods", + Long: podRestartDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podRestartCommand.InputArgs = args + podRestartCommand.GlobalFlags = MainGlobalOpts + return podRestartCmd(&podRestartCommand) + }, + Example: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]", } ) -func podRestartCmd(c *cli.Context) error { - if err := checkMutuallyExclusiveFlags(c); err != nil { +func init() { + podRestartCommand.Command = _podRestartCommand + flags := podRestartCommand.Flags() + flags.BoolVarP(&podRestartCommand.All, "all", "a", false, "Restart all running pods") + flags.BoolVarP(&podRestartCommand.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") + +} + +func podRestartCmd(c *cliconfig.PodRestartValues) error { + if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil { return err } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -45,7 +48,7 @@ func podRestartCmd(c *cli.Context) error { // getPodsFromContext returns an error when a requested pod // isn't found. The only fatal error scenerio is when there are no pods // in which case the following loop will be skipped. - pods, lastError := getPodsFromContext(c, runtime) + pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime) ctx := getContext() for _, pod := range pods { diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go index 49f2104cf..445a641df 100644 --- a/cmd/podman/pod_rm.go +++ b/cmd/podman/pod_rm.go @@ -3,60 +3,61 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - podRmFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Remove all pods", - }, - cli.BoolFlag{ - Name: "force, f", - Usage: "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false", - }, - LatestPodFlag, - } + podRmCommand cliconfig.PodRmValues podRmDescription = fmt.Sprintf(` podman rm will remove one or more pods from the host. The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed. `) - podRmCommand = cli.Command{ - Name: "rm", - Usage: "Remove one or more pods", - Description: podRmDescription, - Flags: sortFlags(podRmFlags), - Action: podRmCmd, - ArgsUsage: "[POD ...]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _podRmCommand = &cobra.Command{ + Use: "rm", + Short: "Remove one or more pods", + Long: podRmDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podRmCommand.InputArgs = args + podRmCommand.GlobalFlags = MainGlobalOpts + return podRmCmd(&podRmCommand) + }, + Example: "[POD ...]", } ) +func init() { + podRmCommand.Command = _podRmCommand + flags := podRmCommand.Flags() + flags.BoolVarP(&podRmCommand.All, "all", "a", false, "Remove all running pods") + flags.BoolVarP(&podRmCommand.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false") + flags.BoolVarP(&podRmCommand.Latest, "latest", "l", false, "Remove the latest pod podman is aware of") + +} + // saveCmd saves the image to either docker-archive or oci -func podRmCmd(c *cli.Context) error { - if err := checkMutuallyExclusiveFlags(c); err != nil { +func podRmCmd(c *cliconfig.PodRmValues) error { + if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil { return err } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) ctx := getContext() - force := c.Bool("force") + force := c.Force // getPodsFromContext returns an error when a requested pod // isn't found. The only fatal error scenerio is when there are no pods // in which case the following loop will be skipped. - pods, lastError := getPodsFromContext(c, runtime) + pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime) for _, pod := range pods { err = runtime.RemovePod(ctx, pod, force, force) diff --git a/cmd/podman/pod_start.go b/cmd/podman/pod_start.go index 2178340a4..8b35d3e04 100644 --- a/cmd/podman/pod_start.go +++ b/cmd/podman/pod_start.go @@ -3,44 +3,46 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - podStartFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Start all running pods", - }, - LatestPodFlag, - } + podStartCommand cliconfig.PodStartValues podStartDescription = ` podman pod start Starts one or more pods. The pod name or ID can be used. ` - - podStartCommand = cli.Command{ - Name: "start", - Usage: "Start one or more pods", - Description: podStartDescription, - Flags: sortFlags(podStartFlags), - Action: podStartCmd, - ArgsUsage: "POD-NAME [POD-NAME ...]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _podStartCommand = &cobra.Command{ + Use: "start", + Short: "Start one or more pods", + Long: podStartDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podStartCommand.InputArgs = args + podStartCommand.GlobalFlags = MainGlobalOpts + return podStartCmd(&podStartCommand) + }, + Example: "POD-NAME [POD-NAME ...]", } ) -func podStartCmd(c *cli.Context) error { - if err := checkMutuallyExclusiveFlags(c); err != nil { +func init() { + podStartCommand.Command = _podStartCommand + flags := podStartCommand.Flags() + flags.BoolVarP(&podStartCommand.All, "all", "a", false, "Start all pods") + flags.BoolVarP(&podStartCommand.Latest, "latest", "l", false, "Start the latest pod podman is aware of") +} + +func podStartCmd(c *cliconfig.PodStartValues) error { + if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil { return err } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -49,7 +51,7 @@ func podStartCmd(c *cli.Context) error { // getPodsFromContext returns an error when a requested pod // isn't found. The only fatal error scenerio is when there are no pods // in which case the following loop will be skipped. - pods, lastError := getPodsFromContext(c, runtime) + pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime) ctx := getContext() for _, pod := range pods { diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go index 0f0e215e6..ccbcf9f7c 100644 --- a/cmd/podman/pod_stats.go +++ b/cmd/podman/pod_stats.go @@ -7,54 +7,49 @@ import ( "encoding/json" tm "github.com/buger/goterm" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/pkg/errors" + "github.com/spf13/cobra" "github.com/ulule/deepcopier" - "github.com/urfave/cli" ) var ( - podStatsFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Show stats for all pods. Only running pods are shown by default.", - }, - cli.BoolFlag{ - Name: "no-stream", - Usage: "Disable streaming stats and only pull the first result, default setting is false", - }, - cli.BoolFlag{ - Name: "no-reset", - Usage: "Disable resetting the screen between intervals", - }, - cli.StringFlag{ - Name: "format", - Usage: "Pretty-print container statistics to JSON or using a Go template", - }, LatestPodFlag, - } + podStatsCommand cliconfig.PodStatsValues podStatsDescription = "Display a live stream of resource usage statistics for the containers in or more pods" - podStatsCommand = cli.Command{ - Name: "stats", - Usage: "Display percentage of CPU, memory, network I/O, block I/O and PIDs for containers in one or more pods", - Description: podStatsDescription, - Flags: sortFlags(podStatsFlags), - Action: podStatsCmd, - ArgsUsage: "[POD_NAME_OR_ID]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _podStatsCommand = &cobra.Command{ + Use: "stats", + Short: "Display percentage of CPU, memory, network I/O, block I/O and PIDs for containers in one or more pods", + Long: podStatsDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podStatsCommand.InputArgs = args + podStatsCommand.GlobalFlags = MainGlobalOpts + return podStatsCmd(&podStatsCommand) + }, + Example: "[POD_NAME_OR_ID]", } ) -func podStatsCmd(c *cli.Context) error { +func init() { + podStatsCommand.Command = _podStatsCommand + flags := podStatsCommand.Flags() + flags.BoolVarP(&podStatsCommand.All, "all", "a", false, "Provide stats for all running pods") + flags.StringVar(&podStatsCommand.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template") + flags.BoolVarP(&podStatsCommand.Latest, "latest", "l", false, "Provide stats on the latest pod podman is aware of") + flags.BoolVar(&podStatsCommand.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false") + flags.BoolVar(&podStatsCommand.NoReset, "no-reset", false, "Disable resetting the screen between intervals") +} + +func podStatsCmd(c *cliconfig.PodStatsValues) error { var ( podFunc func() ([]*libpod.Pod, error) ) - format := c.String("format") - all := c.Bool("all") - latest := c.Bool("latest") + format := c.Format + all := c.All + latest := c.Latest ctr := 0 if all { ctr += 1 @@ -62,7 +57,7 @@ func podStatsCmd(c *cli.Context) error { if latest { ctr += 1 } - if len(c.Args()) > 0 { + if len(c.InputArgs) > 0 { ctr += 1 } @@ -73,19 +68,19 @@ func podStatsCmd(c *cli.Context) error { all = true } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) times := -1 - if c.Bool("no-stream") { + if c.NoStream { times = 1 } - if len(c.Args()) > 0 { - podFunc = func() ([]*libpod.Pod, error) { return getPodsByList(c.Args(), runtime) } + if len(c.InputArgs) > 0 { + podFunc = func() ([]*libpod.Pod, error) { return getPodsByList(c.InputArgs, runtime) } } else if latest { podFunc = func() ([]*libpod.Pod, error) { latestPod, err := runtime.GetLatestPod() @@ -159,7 +154,7 @@ func podStatsCmd(c *cli.Context) error { newStats = append(newStats, &newPod) } //Output - if strings.ToLower(format) != formats.JSONString && !c.Bool("no-reset") { + if strings.ToLower(format) != formats.JSONString && !c.NoReset { tm.Clear() tm.MoveCursor(1, 1) tm.Flush() diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go index 148b4d518..63bd0f5cb 100644 --- a/cmd/podman/pod_stop.go +++ b/cmd/podman/pod_stop.go @@ -2,48 +2,50 @@ package main import ( "fmt" + + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - podStopFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Stop all running pods", - }, - LatestPodFlag, - cli.UintFlag{ - Name: "timeout, time, t", - Usage: "Seconds to wait for pod stop before killing the container", - }, - } + podStopCommand cliconfig.PodStopValues podStopDescription = ` podman pod stop Stops one or more running pods. The pod name or ID can be used. ` - podStopCommand = cli.Command{ - Name: "stop", - Usage: "Stop one or more pods", - Description: podStopDescription, - Flags: sortFlags(podStopFlags), - Action: podStopCmd, - ArgsUsage: "POD-NAME [POD-NAME ...]", - OnUsageError: usageErrorHandler, + _podStopCommand = &cobra.Command{ + Use: "stop", + Short: "Stop one or more pods", + Long: podStopDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podStopCommand.InputArgs = args + podStopCommand.GlobalFlags = MainGlobalOpts + return podStopCmd(&podStopCommand) + }, + Example: "POD-NAME [POD-NAME ...]", } ) -func podStopCmd(c *cli.Context) error { +func init() { + podStopCommand.Command = _podStopCommand + flags := podStopCommand.Flags() + flags.BoolVarP(&podStopCommand.All, "all", "a", false, "Stop all running pods") + flags.BoolVarP(&podStopCommand.Latest, "latest", "l", false, "Stop the latest pod podman is aware of") + flags.UintVarP(&podStopCommand.Timeout, "timeout", "t", 0, "Seconds to wait for pod stop before killing the container") +} + +func podStopCmd(c *cliconfig.PodStopValues) error { timeout := -1 - if err := checkMutuallyExclusiveFlags(c); err != nil { + if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil { return err } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -52,12 +54,12 @@ func podStopCmd(c *cli.Context) error { // getPodsFromContext returns an error when a requested pod // isn't found. The only fatal error scenerio is when there are no pods // in which case the following loop will be skipped. - pods, lastError := getPodsFromContext(c, runtime) + pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime) ctx := getContext() - if c.IsSet("timeout") { - timeout = int(c.Uint("timeout")) + if c.Flag("timeout").Changed { + timeout = int(c.Timeout) } for _, pod := range pods { // set cleanup to true to clean mounts and namespaces diff --git a/cmd/podman/pod_top.go b/cmd/podman/pod_top.go index 1bd1287db..f1db2fac5 100644 --- a/cmd/podman/pod_top.go +++ b/cmd/podman/pod_top.go @@ -6,21 +6,17 @@ import ( "strings" "text/tabwriter" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - podTopFlags = []cli.Flag{ - LatestFlag, - cli.BoolFlag{ - Name: "list-descriptors", - Hidden: true, - }, - } + podTopCommand cliconfig.PodTopValues + podTopDescription = fmt.Sprintf(`Display the running processes containers in a pod. Specify format descriptors to alter the output. You may run "podman pod top -l pid pcpu seccomp" to print the process ID, the CPU percentage and the seccomp mode of each process of @@ -28,24 +24,34 @@ the latest pod. %s `, getDescriptorString()) - podTopCommand = cli.Command{ - Name: "top", - Usage: "Display the running processes of containers in a pod", - Description: podTopDescription, - Flags: sortFlags(podTopFlags), - Action: podTopCmd, - ArgsUsage: "POD-NAME [format descriptors]", - SkipArgReorder: true, - OnUsageError: usageErrorHandler, + _podTopCommand = &cobra.Command{ + Use: "top", + Short: "Display the running processes of containers in a pod", + Long: podTopDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podTopCommand.InputArgs = args + podTopCommand.GlobalFlags = MainGlobalOpts + return podTopCmd(&podTopCommand) + }, + Example: "POD-NAME [format descriptors]", } ) -func podTopCmd(c *cli.Context) error { +func init() { + podTopCommand.Command = _podTopCommand + flags := podTopCommand.Flags() + flags.BoolVarP(&podTopCommand.Latest, "latest,", "l", false, "Act on the latest pod podman is aware of") + flags.BoolVar(&podTopCommand.ListDescriptors, "list-descriptors", false, "") + flags.MarkHidden("list-descriptors") + +} + +func podTopCmd(c *cliconfig.PodTopValues) error { var pod *libpod.Pod var err error - args := c.Args() + args := c.InputArgs - if c.Bool("list-descriptors") { + if c.ListDescriptors { descriptors, err := libpod.GetContainerPidInformationDescriptors() if err != nil { return err @@ -54,21 +60,18 @@ func podTopCmd(c *cli.Context) error { return nil } - if len(args) < 1 && !c.Bool("latest") { + if len(args) < 1 && !c.Latest { return errors.Errorf("you must provide the name or id of a running pod") } - if err := validateFlags(c, podTopFlags); err != nil { - return err - } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) var descriptors []string - if c.Bool("latest") { + if c.Latest { descriptors = args pod, err = runtime.GetLatestPod() } else { diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go index ed1a00cf8..17b771d4a 100644 --- a/cmd/podman/pod_unpause.go +++ b/cmd/podman/pod_unpause.go @@ -3,42 +3,42 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - podUnpauseFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Unpause all paused pods", + podUnpauseCommand cliconfig.PodUnpauseValues + podUnpauseDescription = `Unpauses one or more pods. The pod name or ID can be used.` + _podUnpauseCommand = &cobra.Command{ + Use: "unpause", + Short: "Unpause one or more pods", + Long: podUnpauseDescription, + RunE: func(cmd *cobra.Command, args []string) error { + podUnpauseCommand.InputArgs = args + podUnpauseCommand.GlobalFlags = MainGlobalOpts + return podUnpauseCmd(&podUnpauseCommand) }, - LatestPodFlag, - } - podUnpauseDescription = ` - Unpauses one or more pods. The pod name or ID can be used. -` - - podUnpauseCommand = cli.Command{ - Name: "unpause", - Usage: "Unpause one or more pods", - Description: podUnpauseDescription, - Flags: sortFlags(podUnpauseFlags), - Action: podUnpauseCmd, - ArgsUsage: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + Example: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]", } ) -func podUnpauseCmd(c *cli.Context) error { - if err := checkMutuallyExclusiveFlags(c); err != nil { +func init() { + podUnpauseCommand.Command = _podUnpauseCommand + flags := podUnpauseCommand.Flags() + flags.BoolVarP(&podUnpauseCommand.All, "all", "a", false, "Unpause all running pods") + flags.BoolVarP(&podUnpauseCommand.Latest, "latest", "l", false, "Unpause the latest pod podman is aware of") +} + +func podUnpauseCmd(c *cliconfig.PodUnpauseValues) error { + if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil { return err } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -47,7 +47,7 @@ func podUnpauseCmd(c *cli.Context) error { // getPodsFromContext returns an error when a requested pod // isn't found. The only fatal error scenerio is when there are no pods // in which case the following loop will be skipped. - pods, lastError := getPodsFromContext(c, runtime) + pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime) for _, pod := range pods { ctr_errs, err := pod.Unpause() diff --git a/cmd/podman/port.go b/cmd/podman/port.go index 6875c648a..488ef2ffe 100644 --- a/cmd/podman/port.go +++ b/cmd/podman/port.go @@ -5,38 +5,44 @@ import ( "strconv" "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - portFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Display port information for all containers", - }, - LatestFlag, - } + portCommand cliconfig.PortValues portDescription = ` podman port List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT ` - - portCommand = cli.Command{ - Name: "port", - Usage: "List port mappings or a specific mapping for the container", - Description: portDescription, - Flags: sortFlags(portFlags), - Action: portCmd, - ArgsUsage: "CONTAINER-NAME [mapping]", - OnUsageError: usageErrorHandler, + _portCommand = &cobra.Command{ + Use: "port", + Short: "List port mappings or a specific mapping for the container", + Long: portDescription, + RunE: func(cmd *cobra.Command, args []string) error { + portCommand.InputArgs = args + portCommand.GlobalFlags = MainGlobalOpts + return portCmd(&portCommand) + }, + Example: "CONTAINER-NAME [mapping]", } ) -func portCmd(c *cli.Context) error { +func init() { + portCommand.Command = _portCommand + flags := portCommand.Flags() + + flags.BoolVarP(&portCommand.All, "all", "a", false, "Display port information for all containers") + flags.BoolVarP(&portCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + + rootCmd.AddCommand(portCommand.Command) +} + +func portCmd(c *cliconfig.PortValues) error { var ( userProto, containerName string userPort int @@ -44,29 +50,26 @@ func portCmd(c *cli.Context) error { containers []*libpod.Container ) - args := c.Args() - if err := validateFlags(c, portFlags); err != nil { - return err - } + args := c.InputArgs - if c.Bool("latest") && c.Bool("all") { + if c.Latest && c.All { return errors.Errorf("the 'all' and 'latest' options cannot be used together") } - if c.Bool("all") && len(args) > 0 { + if c.All && len(args) > 0 { return errors.Errorf("no additional arguments can be used with 'all'") } - if len(args) == 0 && !c.Bool("latest") && !c.Bool("all") { + if len(args) == 0 && !c.Latest && !c.All { return errors.Errorf("you must supply a running container name or id") } - if !c.Bool("latest") && !c.Bool("all") { + if !c.Latest && !c.All { containerName = args[0] } port := "" - if len(args) > 1 && !c.Bool("latest") { + if len(args) > 1 && !c.Latest { port = args[1] } - if len(args) == 1 && c.Bool("latest") { + if len(args) == 1 && c.Latest { port = args[0] } if port != "" { @@ -90,19 +93,19 @@ func portCmd(c *cli.Context) error { } } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - if !c.Bool("latest") && !c.Bool("all") { + if !c.Latest && !c.All { container, err = runtime.LookupContainer(containerName) if err != nil { return errors.Wrapf(err, "unable to find container %s", containerName) } containers = append(containers, container) - } else if c.Bool("latest") { + } else if c.Latest { container, err = runtime.GetLatestContainer() if err != nil { return errors.Wrapf(err, "unable to get last created container") @@ -119,7 +122,7 @@ func portCmd(c *cli.Context) error { if state, _ := con.State(); state != libpod.ContainerStateRunning { continue } - if c.Bool("all") { + if c.All { fmt.Println(con.ID()) } // Iterate mappings diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 1708c671c..002975b87 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -12,6 +12,7 @@ import ( "text/tabwriter" "time" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" @@ -21,7 +22,7 @@ import ( "github.com/docker/go-units" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/fields" ) @@ -153,112 +154,78 @@ func (a psSortedSize) Less(i, j int) bool { } var ( - psFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Show all the containers, default is only running containers", - }, - cli.StringSliceFlag{ - Name: "filter, f", - Usage: "Filter output based on conditions given", - }, - cli.StringFlag{ - Name: "format", - Usage: "Pretty-print containers to JSON or using a Go template", - }, - cli.IntFlag{ - Name: "last, n", - Usage: "Print the n last created containers (all states)", - Value: -1, - }, - cli.BoolFlag{ - Name: "latest, l", - Usage: "Show the latest container created (all states)", - }, - cli.BoolFlag{ - Name: "namespace, ns", - Usage: "Display namespace information", - }, - cli.BoolFlag{ - Name: "no-trunc", - Usage: "Display the extended information", - }, - cli.BoolFlag{ - Name: "pod, p", - Usage: "Print the ID and name of the pod the containers are associated with", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Print the numeric IDs of the containers only", - }, - cli.BoolFlag{ - Name: "size, s", - Usage: "Display the total file sizes", - }, - cli.StringFlag{ - Name: "sort", - Usage: "Sort output by command, created, id, image, names, runningfor, size, or status", - Value: "created", - }, - cli.BoolFlag{ - Name: "sync", - Usage: "Sync container state with OCI runtime", - }, - } + psCommand cliconfig.PsValues psDescription = "Prints out information about the containers" - psCommand = cli.Command{ - Name: "list", - Aliases: []string{"ls", "ps"}, - Usage: "List containers", - Description: psDescription, - Flags: sortFlags(psFlags), - Action: psCmd, - ArgsUsage: "", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _psCommand = &cobra.Command{ + Use: "list", + Aliases: []string{"ls", "ps"}, + Short: "List containers", + Long: psDescription, + RunE: func(cmd *cobra.Command, args []string) error { + psCommand.InputArgs = args + psCommand.GlobalFlags = MainGlobalOpts + return psCmd(&psCommand) + }, + Example: "", } ) -func psCmd(c *cli.Context) error { +func init() { + psCommand.Command = _psCommand + flags := psCommand.Flags() + flags.BoolVarP(&psCommand.All, "all", "a", false, "Show all the containers, default is only running containers") + flags.StringSliceVarP(&psCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions given") + flags.StringVar(&psCommand.Format, "format", "", "Pretty-print containers to JSON or using a Go template") + flags.IntVarP(&psCommand.Last, "last", "n", -1, "Print the n last created containers (all states)") + flags.BoolVarP(&psCommand.Latest, "latest", "l", false, "Show the latest container created (all states)") + flags.BoolVar(&psCommand.Namespace, "namespace", false, "Display namespace information") + flags.BoolVar(&psCommand.Namespace, "ns", false, "Display namespace information") + flags.BoolVar(&psCommand.NoTrunct, "no-trunc", false, "Display the extended information") + flags.BoolVarP(&psCommand.Pod, "pod", "p", false, "Print the ID and name of the pod the containers are associated with") + flags.BoolVarP(&psCommand.Quiet, "quiet", "q", false, "Print the numeric IDs of the containers only") + flags.BoolVarP(&psCommand.Size, "size", "s", false, "Display the total file sizes") + flags.StringVar(&psCommand.Sort, "sort", "created", "Sort output by command, created, id, image, names, runningfor, size, or status") + flags.BoolVar(&psCommand.Sync, "sync", false, "Sync container state with OCI runtime") + + rootCmd.AddCommand(psCommand.Command) + +} +func psCmd(c *cliconfig.PsValues) error { var ( filterFuncs []libpod.ContainerFilter outputContainers []*libpod.Container ) - if err := validateFlags(c, psFlags); err != nil { - return err - } - if err := checkFlagsPassed(c); err != nil { return errors.Wrapf(err, "error with flags passed") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - if len(c.Args()) > 0 { + if len(c.InputArgs) > 0 { return errors.Errorf("too many arguments, ps takes no arguments") } opts := shared.PsOptions{ - All: c.Bool("all"), - Format: c.String("format"), - Last: c.Int("last"), - Latest: c.Bool("latest"), - NoTrunc: c.Bool("no-trunc"), - Pod: c.Bool("pod"), - Quiet: c.Bool("quiet"), - Size: c.Bool("size"), - Namespace: c.Bool("namespace"), - Sort: c.String("sort"), - Sync: c.Bool("sync"), - } - - filters := c.StringSlice("filter") + All: c.All, + Format: c.Format, + Last: c.Last, + Latest: c.Latest, + NoTrunc: c.NoTrunct, + Pod: c.Pod, + Quiet: c.Quiet, + Size: c.Size, + Namespace: c.Namespace, + Sort: c.Sort, + Sync: c.Sync, + } + + filters := c.Filter if len(filters) > 0 { for _, f := range filters { filterSplit := strings.SplitN(f, "=", 2) @@ -299,7 +266,7 @@ func psCmd(c *cli.Context) error { maxWorkers := shared.Parallelize("ps") if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalInt("max-workers") + maxWorkers = c.GlobalFlags.MaxWorks } logrus.Debugf("Setting maximum workers to %d", maxWorkers) @@ -384,20 +351,20 @@ func printQuiet(containers []shared.PsContainerOutput) error { } // checkFlagsPassed checks if mutually exclusive flags are passed together -func checkFlagsPassed(c *cli.Context) error { +func checkFlagsPassed(c *cliconfig.PsValues) error { // latest, and last are mutually exclusive. - if c.Int("last") >= 0 && c.Bool("latest") { + if c.Last >= 0 && c.Latest { return errors.Errorf("last and latest are mutually exclusive") } // Quiet conflicts with size, namespace, and format with a Go template - if c.Bool("quiet") { - if c.Bool("size") || c.Bool("namespace") || (c.IsSet("format") && - c.String("format") != formats.JSONString) { + if c.Quiet { + if c.Size || c.Namespace || (c.Flag("format").Changed && + c.Format != formats.JSONString) { return errors.Errorf("quiet conflicts with size, namespace, and format with go template") } } // Size and namespace conflict with each other - if c.Bool("size") && c.Bool("namespace") { + if c.Size && c.Namespace { return errors.Errorf("size and namespace options conflict") } return nil diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 2349265d0..d70719164 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -9,68 +9,58 @@ import ( dockerarchive "github.com/containers/image/docker/archive" "github.com/containers/image/transports/alltransports" "github.com/containers/image/types" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/adapter" image2 "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - pullFlags = []cli.Flag{ - cli.StringFlag{ - Name: "authfile", - Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ", - }, - cli.StringFlag{ - Name: "cert-dir", - Usage: "`pathname` of a directory containing TLS certificates and keys", - }, - cli.StringFlag{ - Name: "creds", - Usage: "`credentials` (USERNAME:PASSWORD) to use for authenticating to a registry", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Suppress output information when pulling images", - }, - cli.StringFlag{ - Name: "signature-policy", - Usage: "`pathname` of signature policy file (not usually used)", - }, - cli.BoolTFlag{ - Name: "tls-verify", - Usage: "Require HTTPS and verify certificates when contacting registries (default: true)", - }, - } - + pullCommand cliconfig.PullValues pullDescription = ` Pulls an image from a registry and stores it locally. An image can be pulled using its tag or digest. If a tag is not specified, the image with the 'latest' tag (if it exists) is pulled ` - pullCommand = cli.Command{ - Name: "pull", - Usage: "Pull an image from a registry", - Description: pullDescription, - Flags: sortFlags(pullFlags), - Action: pullCmd, - ArgsUsage: "", - OnUsageError: usageErrorHandler, + _pullCommand = &cobra.Command{ + Use: "pull", + Short: "Pull an image from a registry", + Long: pullDescription, + RunE: func(cmd *cobra.Command, args []string) error { + pullCommand.InputArgs = args + pullCommand.GlobalFlags = MainGlobalOpts + return pullCmd(&pullCommand) + }, + Example: "", } ) +func init() { + pullCommand.Command = _pullCommand + flags := pullCommand.Flags() + flags.StringVar(&pullCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pullCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + flags.StringVar(&pullCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") + flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") + flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + + rootCmd.AddCommand(pullCommand.Command) +} + // pullCmd gets the data from the command line and calls pullImage // to copy an image from a registry to a local machine -func pullCmd(c *cli.Context) error { - runtime, err := adapter.GetRuntime(c) +func pullCmd(c *cliconfig.PullValues) error { + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - args := c.Args() + args := c.InputArgs if len(args) == 0 { logrus.Errorf("an image name must be specified") return nil @@ -79,15 +69,12 @@ func pullCmd(c *cli.Context) error { logrus.Errorf("too many arguments. Requires exactly 1") return nil } - if err := validateFlags(c, pullFlags); err != nil { - return err - } image := args[0] var registryCreds *types.DockerAuthConfig - if c.IsSet("creds") { - creds, err := util.ParseRegistryCreds(c.String("creds")) + if c.Flag("creds").Changed { + creds, err := util.ParseRegistryCreds(c.Creds) if err != nil { return err } @@ -98,16 +85,16 @@ func pullCmd(c *cli.Context) error { writer io.Writer imgID string ) - if !c.Bool("quiet") { + if !c.Quiet { writer = os.Stderr } dockerRegistryOptions := image2.DockerRegistryOptions{ DockerRegistryCreds: registryCreds, - DockerCertPath: c.String("cert-dir"), + DockerCertPath: c.CertDir, } - if c.IsSet("tls-verify") { - dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify")) + if c.Flag("tls-verify").Changed { + dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) } // Possible for docker-archive to have multiple tags, so use LoadFromArchiveReference instead @@ -116,14 +103,14 @@ func pullCmd(c *cli.Context) error { if err != nil { return errors.Wrapf(err, "error parsing %q", image) } - newImage, err := runtime.LoadFromArchiveReference(getContext(), srcRef, c.String("signature-policy"), writer) + newImage, err := runtime.LoadFromArchiveReference(getContext(), srcRef, c.SignaturePolicy, writer) if err != nil { return errors.Wrapf(err, "error pulling image from %q", image) } imgID = newImage[0].ID() } else { - authfile := getAuthFile(c.String("authfile")) - newImage, err := runtime.New(getContext(), image, c.String("signature-policy"), authfile, writer, &dockerRegistryOptions, image2.SigningOptions{}, true, nil) + authfile := getAuthFile(c.Authfile) + newImage, err := runtime.New(getContext(), image, c.SignaturePolicy, authfile, writer, &dockerRegistryOptions, image2.SigningOptions{}, true, nil) if err != nil { return errors.Wrapf(err, "error pulling image %q", image) } diff --git a/cmd/podman/push.go b/cmd/podman/push.go index 361a25e35..a1ee00a65 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -2,6 +2,8 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" "io" "os" "strings" @@ -14,76 +16,52 @@ import ( "github.com/containers/libpod/pkg/util" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" - "github.com/urfave/cli" ) var ( - pushFlags = []cli.Flag{ - cli.StringFlag{ - Name: "signature-policy", - Usage: "`Pathname` of signature policy file (not usually used)", - Hidden: true, - }, - cli.StringFlag{ - Name: "creds", - Usage: "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry", - }, - cli.StringFlag{ - Name: "cert-dir", - Usage: "`Pathname` of a directory containing TLS certificates and keys", - }, - cli.BoolFlag{ - Name: "compress", - Usage: "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)", - }, - cli.StringFlag{ - Name: "format, f", - Usage: "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir:' transport (default is manifest type of source)", - }, - cli.BoolTFlag{ - Name: "tls-verify", - Usage: "Require HTTPS and verify certificates when contacting registries (default: true)", - }, - cli.BoolFlag{ - Name: "remove-signatures", - Usage: "Discard any pre-existing signatures in the image", - }, - cli.StringFlag{ - Name: "sign-by", - Usage: "Add a signature at the destination using the specified key", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Don't output progress information when pushing images", - }, - cli.StringFlag{ - Name: "authfile", - Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ", - }, - } + pushCommand cliconfig.PushValues pushDescription = fmt.Sprintf(` Pushes an image to a specified location. The Image "DESTINATION" uses a "transport":"details" format. See podman-push(1) section "DESTINATION" for the expected format`) - pushCommand = cli.Command{ - Name: "push", - Usage: "Push an image to a specified destination", - Description: pushDescription, - Flags: sortFlags(pushFlags), - Action: pushCmd, - ArgsUsage: "IMAGE DESTINATION", - OnUsageError: usageErrorHandler, + _pushCommand = &cobra.Command{ + Use: "push", + Short: "Push an image to a specified destination", + Long: pushDescription, + RunE: func(cmd *cobra.Command, args []string) error { + pushCommand.InputArgs = args + pushCommand.GlobalFlags = MainGlobalOpts + return pushCmd(&pushCommand) + }, + Example: "IMAGE DESTINATION", } ) -func pushCmd(c *cli.Context) error { +func init() { + pushCommand.Command = _pushCommand + flags := pushCommand.Flags() + flags.MarkHidden("signature-policy") + flags.StringVar(&pushCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pushCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + flags.BoolVar(&pushCommand.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)") + flags.StringVar(&pushCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.StringVarP(&pushCommand.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir:' transport (default is manifest type of source)") + flags.BoolVarP(&pushCommand.Quiet, "quiet", "q", false, "Don't output progress information when pushing images") + flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image") + flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") + flags.StringVar(&pushCommand.SignBy, "sign-by", "", "Add a signature at the destination using the specified key") + flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + rootCmd.AddCommand(pushCommand.Command) +} + +func pushCmd(c *cliconfig.PushValues) error { var ( registryCreds *types.DockerAuthConfig destName string ) - args := c.Args() + args := c.InputArgs if len(args) == 0 || len(args) > 2 { return errors.New("podman push requires at least one image name, and optionally a second to specify a different destination name") } @@ -94,43 +72,40 @@ func pushCmd(c *cli.Context) error { case 2: destName = args[1] } - if err := validateFlags(c, pushFlags); err != nil { - return err - } // --compress and --format can only be used for the "dir" transport splitArg := strings.SplitN(destName, ":", 2) - if c.IsSet("compress") || c.IsSet("format") { + if c.Flag("compress").Changed || c.Flag("format").Changed { if splitArg[0] != directory.Transport.Name() { return errors.Errorf("--compress and --format can be set only when pushing to a directory using the 'dir' transport") } } - certPath := c.String("cert-dir") - removeSignatures := c.Bool("remove-signatures") - signBy := c.String("sign-by") + certPath := c.CertDir + removeSignatures := c.RemoveSignatures + signBy := c.SignBy - if c.IsSet("creds") { - creds, err := util.ParseRegistryCreds(c.String("creds")) + if c.Flag("creds").Changed { + creds, err := util.ParseRegistryCreds(c.Creds) if err != nil { return err } registryCreds = creds } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not create runtime") } defer runtime.Shutdown(false) var writer io.Writer - if !c.Bool("quiet") { + if !c.Quiet { writer = os.Stderr } var manifestType string - if c.IsSet("format") { + if c.Flag("format").Changed { switch c.String("format") { case "oci": manifestType = imgspecv1.MediaTypeImageManifest @@ -147,8 +122,8 @@ func pushCmd(c *cli.Context) error { DockerRegistryCreds: registryCreds, DockerCertPath: certPath, } - if c.IsSet("tls-verify") { - dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify")) + if c.Flag("tls-verify").Changed { + dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) } so := image.SigningOptions{ @@ -161,7 +136,7 @@ func pushCmd(c *cli.Context) error { return err } - authfile := getAuthFile(c.String("authfile")) + authfile := getAuthFile(c.Authfile) - return newImage.PushImageToHeuristicDestination(getContext(), destName, manifestType, authfile, c.String("signature-policy"), writer, c.Bool("compress"), so, &dockerRegistryOptions, nil) + return newImage.PushImageToHeuristicDestination(getContext(), destName, manifestType, authfile, c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil) } diff --git a/cmd/podman/refresh.go b/cmd/podman/refresh.go index b07376170..3890188b4 100644 --- a/cmd/podman/refresh.go +++ b/cmd/podman/refresh.go @@ -4,37 +4,38 @@ import ( "fmt" "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - refreshFlags = []cli.Flag{} - + refreshCommand cliconfig.RefreshValues refreshDescription = "The refresh command resets the state of all containers to handle database changes after a Podman upgrade. All running containers will be restarted." - - refreshCommand = cli.Command{ - Name: "refresh", - Usage: "Refresh container state", - Description: refreshDescription, - Flags: sortFlags(refreshFlags), - Action: refreshCmd, - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _refreshCommand = &cobra.Command{ + Use: "refresh", + Short: "Refresh container state", + Long: refreshDescription, + RunE: func(cmd *cobra.Command, args []string) error { + refreshCommand.InputArgs = args + refreshCommand.GlobalFlags = MainGlobalOpts + return refreshCmd(&refreshCommand) + }, } ) -func refreshCmd(c *cli.Context) error { - if len(c.Args()) > 0 { - return errors.Errorf("refresh does not accept any arguments") - } +func init() { + refreshCommand.Command = _refreshCommand + rootCmd.AddCommand(refreshCommand.Command) +} - if err := validateFlags(c, refreshFlags); err != nil { - return err +func refreshCmd(c *cliconfig.RefreshValues) error { + if len(c.InputArgs) > 0 { + return errors.Errorf("refresh does not accept any arguments") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go index 2e52ce5e4..f94e745ed 100644 --- a/cmd/podman/restart.go +++ b/cmd/podman/restart.go @@ -4,47 +4,45 @@ import ( "fmt" "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - restartFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Restart all non-running containers", - }, - cli.BoolFlag{ - Name: "running", - Usage: "Restart only running containers when --all is used", - }, - cli.UintFlag{ - Name: "timeout, time, t", - Usage: "Seconds to wait for stop before killing the container", - Value: libpod.CtrRemoveTimeout, - }, - LatestFlag, - } + restartCommand cliconfig.RestartValues restartDescription = `Restarts one or more running containers. The container ID or name can be used. A timeout before forcibly stopping can be set, but defaults to 10 seconds` - - restartCommand = cli.Command{ - Name: "restart", - Usage: "Restart one or more containers", - Description: restartDescription, - Flags: sortFlags(restartFlags), - Action: restartCmd, - ArgsUsage: "CONTAINER [CONTAINER ...]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _restartCommand = &cobra.Command{ + Use: "restart", + Short: "Restart one or more containers", + Long: restartDescription, + RunE: func(cmd *cobra.Command, args []string) error { + restartCommand.InputArgs = args + restartCommand.GlobalFlags = MainGlobalOpts + return restartCmd(&restartCommand) + }, + Example: "CONTAINER [CONTAINER ...]", } ) -func restartCmd(c *cli.Context) error { +func init() { + restartCommand.Command = _restartCommand + flags := restartCommand.Flags() + flags.BoolVarP(&restartCommand.All, "all", "a", false, "Restart all non-running containers") + flags.BoolVarP(&restartCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVar(&restartCommand.Running, "running", false, "Restart only running containers when --all is used") + flags.UintVarP(&restartCommand.Timeout, "timeout", "t", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") + flags.UintVar(&restartCommand.Timeout, "time", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") + + rootCmd.AddCommand(restartCommand.Command) +} + +func restartCmd(c *cliconfig.RestartValues) error { var ( restartFuncs []shared.ParallelWorkerInput containers []*libpod.Container @@ -55,34 +53,31 @@ func restartCmd(c *cli.Context) error { rootless.SetSkipStorageSetup(true) } - args := c.Args() - runOnly := c.Bool("running") - all := c.Bool("all") - if len(args) < 1 && !c.Bool("latest") && !all { + args := c.InputArgs + runOnly := c.Running + all := c.All + if len(args) < 1 && !c.Latest && !all { return errors.Wrapf(libpod.ErrInvalidArg, "you must provide at least one container name or ID") } - if err := validateFlags(c, restartFlags); err != nil { - return err - } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - timeout := c.Uint("timeout") - useTimeout := c.IsSet("timeout") + timeout := c.Timeout + useTimeout := c.Flag("timeout").Changed // Handle --latest - if c.Bool("latest") { + if c.Latest { lastCtr, err := runtime.GetLatestContainer() if err != nil { return errors.Wrapf(err, "unable to get latest container") } restartContainers = append(restartContainers, lastCtr) } else if runOnly { - containers, err = getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") + containers, err = getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStateRunning, "running") if err != nil { return err } @@ -105,7 +100,7 @@ func restartCmd(c *cli.Context) error { maxWorkers := shared.Parallelize("restart") if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalInt("max-workers") + maxWorkers = c.GlobalFlags.MaxWorks } logrus.Debugf("Setting maximum workers to %d", maxWorkers) diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go index 664475e22..daee635f0 100644 --- a/cmd/podman/restore.go +++ b/cmd/podman/restore.go @@ -5,68 +5,67 @@ import ( "fmt" "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + restoreCommand cliconfig.RestoreValues restoreDescription = ` podman container restore Restores a container from a checkpoint. The container name or ID can be used. ` - restoreFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "keep, k", - Usage: "Keep all temporary checkpoint files", + _restoreCommand = &cobra.Command{ + Use: "restore", + Short: "Restores one or more containers from a checkpoint", + Long: restoreDescription, + RunE: func(cmd *cobra.Command, args []string) error { + restoreCommand.InputArgs = args + restoreCommand.GlobalFlags = MainGlobalOpts + return restoreCmd(&restoreCommand) }, - // restore --all would make more sense if there would be - // dedicated state for container which are checkpointed. - // TODO: add ContainerStateCheckpointed - cli.BoolFlag{ - Name: "tcp-established", - Usage: "Checkpoint a container with established TCP connections", - }, - cli.BoolFlag{ - Name: "all, a", - Usage: "Restore all checkpointed containers", - }, - LatestFlag, - } - restoreCommand = cli.Command{ - Name: "restore", - Usage: "Restores one or more containers from a checkpoint", - Description: restoreDescription, - Flags: sortFlags(restoreFlags), - Action: restoreCmd, - ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", + Example: "CONTAINER-NAME [CONTAINER-NAME ...]", } ) -func restoreCmd(c *cli.Context) error { +func init() { + restoreCommand.Command = _restoreCommand + flags := restoreCommand.Flags() + flags.BoolVarP(&restoreCommand.All, "all", "a", false, "Restore all checkpointed containers") + flags.BoolVarP(&restoreCommand.Keep, "keep", "k", false, "Keep all temporary checkpoint files") + flags.BoolVarP(&restoreCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + // TODO: add ContainerStateCheckpointed + flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections") + + rootCmd.AddCommand(restoreCommand.Command) +} + +func restoreCmd(c *cliconfig.RestoreValues) error { if rootless.IsRootless() { return errors.New("restoring a container requires root") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) options := libpod.ContainerCheckpointOptions{ - Keep: c.Bool("keep"), - TCPEstablished: c.Bool("tcp-established"), + Keep: c.Keep, + TCPEstablished: c.TcpEstablished, } - if err := checkAllAndLatest(c); err != nil { + if err := checkAllAndLatest(&c.PodmanCommand); err != nil { return err } - containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateExited, "checkpointed") + containers, lastError := getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStateExited, "checkpointed") for _, ctr := range containers { if err = ctr.Restore(context.TODO(), options); err != nil { diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 7c0569b78..bb9a913c9 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -2,67 +2,64 @@ package main import ( "fmt" + + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - rmFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Remove all containers", - }, - cli.BoolFlag{ - Name: "force, f", - Usage: "Force removal of a running container. The default is false", - }, - LatestFlag, - cli.BoolFlag{ - Name: "volumes, v", - Usage: "Remove the volumes associated with the container (Not implemented yet)", - }, - } + rmCommand cliconfig.RmValues rmDescription = fmt.Sprintf(` Podman rm will remove one or more containers from the host. The container name or ID can be used. This does not remove images. Running containers will not be removed without the -f option. `) - rmCommand = cli.Command{ - Name: "rm", - Usage: "Remove one or more containers", - Description: rmDescription, - Flags: sortFlags(rmFlags), - Action: rmCmd, - ArgsUsage: "", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _rmCommand = &cobra.Command{ + Use: "rm", + Short: "Remove one or more containers", + Long: rmDescription, + RunE: func(cmd *cobra.Command, args []string) error { + rmCommand.InputArgs = args + rmCommand.GlobalFlags = MainGlobalOpts + return rmCmd(&rmCommand) + }, + Example: "", } ) +func init() { + rmCommand.Command = _rmCommand + flags := rmCommand.Flags() + flags.BoolVarP(&rmCommand.All, "all", "a", false, "Remove all containers") + flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running container. The default is false") + flags.BoolVarP(&rmCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container (Not implemented yet)") + + rootCmd.AddCommand(rmCommand.Command) +} + // saveCmd saves the image to either docker-archive or oci -func rmCmd(c *cli.Context) error { +func rmCmd(c *cliconfig.RmValues) error { var ( deleteFuncs []shared.ParallelWorkerInput ) ctx := getContext() - if err := validateFlags(c, rmFlags); err != nil { - return err - } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - if err := checkAllAndLatest(c); err != nil { + if err := checkAllAndLatest(&c.PodmanCommand); err != nil { return err } - delContainers, err := getAllOrLatestContainers(c, runtime, -1, "all") + delContainers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all") if err != nil { if len(delContainers) == 0 { return err @@ -73,7 +70,7 @@ func rmCmd(c *cli.Context) error { for _, container := range delContainers { con := container f := func() error { - return runtime.RemoveContainer(ctx, con, c.Bool("force")) + return runtime.RemoveContainer(ctx, con, c.Force) } deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{ @@ -83,7 +80,7 @@ func rmCmd(c *cli.Context) error { } maxWorkers := shared.Parallelize("rm") if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalInt("max-workers") + maxWorkers = c.GlobalFlags.MaxWorks } logrus.Debugf("Setting maximum workers to %d", maxWorkers) diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go index 39757272e..70f58e844 100644 --- a/cmd/podman/rmi.go +++ b/cmd/podman/rmi.go @@ -4,47 +4,39 @@ import ( "fmt" "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/adapter" "github.com/containers/storage" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + rmiCommand cliconfig.RmiValues rmiDescription = "Removes one or more locally stored images." - rmiFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Remove all images", + _rmiCommand = &cobra.Command{ + Use: "rmi", + Short: "Removes one or more images from local storage", + Long: rmiDescription, + RunE: func(cmd *cobra.Command, args []string) error { + rmiCommand.InputArgs = args + rmiCommand.GlobalFlags = MainGlobalOpts + return rmiCmd(&rmiCommand) }, - cli.BoolFlag{ - Name: "force, f", - Usage: "Force removal of the image", - }, - } - rmiCommand = cli.Command{ - Name: "rmi", - Usage: "Remove one or more images from local storage", - Description: rmiDescription, - Action: rmiCmd, - ArgsUsage: "IMAGE-NAME-OR-ID [...]", - Flags: sortFlags(rmiFlags), - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, - } - rmImageCommand = cli.Command{ - Name: "rm", - Usage: "Removes one or more images from local storage", - Description: rmiDescription, - Action: rmiCmd, - ArgsUsage: "IMAGE-NAME-OR-ID [...]", - Flags: rmiFlags, - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + Example: "IMAGE-NAME-OR-ID [...]", } ) -func rmiCmd(c *cli.Context) error { +func init() { + rmiCommand.Command = _rmiCommand + flags := rmiCommand.Flags() + flags.BoolVarP(&rmiCommand.All, "all", "a", false, "Remove all images") + flags.BoolVarP(&rmiCommand.Force, "force", "f", false, "Force Removal of the image") + + rootCmd.AddCommand(rmiCommand.Command) +} + +func rmiCmd(c *cliconfig.RmiValues) error { var ( lastError error deleted bool @@ -53,17 +45,14 @@ func rmiCmd(c *cli.Context) error { ) ctx := getContext() - if err := validateFlags(c, rmiFlags); err != nil { - return err - } - removeAll := c.Bool("all") - runtime, err := adapter.GetRuntime(c) + removeAll := c.All + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - args := c.Args() + args := c.InputArgs if len(args) == 0 && !removeAll { return errors.Errorf("image name or ID must be specified") } @@ -75,7 +64,7 @@ func rmiCmd(c *cli.Context) error { removeImage := func(img *adapter.ContainerImage) { deleted = true - msg, deleteErr = runtime.RemoveImage(ctx, img, c.Bool("force")) + msg, deleteErr = runtime.RemoveImage(ctx, img, c.Force) if deleteErr != nil { if errors.Cause(deleteErr) == storage.ErrImageUsedByContainer { fmt.Printf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID()) diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 3ef546940..76b29cb84 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -8,49 +8,57 @@ import ( "strconv" "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -var runDescription = "Runs a command in a new container from the given image" - -var runFlags []cli.Flag = append(createFlags, cli.BoolTFlag{ - Name: "sig-proxy", - Usage: "Proxy received signals to the process (default true)", -}) - -var runCommand = cli.Command{ - Name: "run", - Usage: "Run a command in a new container", - Description: runDescription, - Flags: sortFlags(runFlags), - Action: runCmd, - ArgsUsage: "IMAGE [COMMAND [ARG...]]", - HideHelp: true, - SkipArgReorder: true, - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, +var ( + runCommmand cliconfig.RunValues + + runDescription = "Runs a command in a new container from the given image" + _runCommmand = &cobra.Command{ + Use: "run", + Short: "Run a command in a new container", + Long: runDescription, + RunE: func(cmd *cobra.Command, args []string) error { + runCommmand.InputArgs = args + runCommmand.GlobalFlags = MainGlobalOpts + return runCmd(&runCommmand) + }, + Example: "IMAGE [COMMAND [ARG...]]", + } +) + +func init() { + runCommmand.Command = _runCommmand + flags := runCommmand.Flags() + flags.SetInterspersed(false) + flags.Bool("sig-proxy", true, "Proxy received signals to the process (default true)") + getCreateFlags(&runCommmand.PodmanCommand) + + rootCmd.AddCommand(runCommmand.Command) } -func runCmd(c *cli.Context) error { - if err := createInit(c); err != nil { +func runCmd(c *cliconfig.RunValues) error { + if err := createInit(&c.PodmanCommand); err != nil { return err } if os.Geteuid() != 0 { rootless.SetSkipStorageSetup(true) } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - ctr, createConfig, err := createContainer(c, runtime) + ctr, createConfig, err := createContainer(&c.PodmanCommand, runtime) if err != nil { return err } @@ -110,7 +118,7 @@ func runCmd(c *cli.Context) error { } } } - if err := startAttachCtr(ctr, outputStream, errorStream, inputStream, c.String("detach-keys"), c.BoolT("sig-proxy"), true); err != nil { + if err := startAttachCtr(ctr, outputStream, errorStream, inputStream, c.String("detach-keys"), c.Bool("sig-proxy"), true); err != nil { // This means the command did not exist exitCode = 127 if strings.Index(err.Error(), "permission denied") > -1 { diff --git a/cmd/podman/run_test.go b/cmd/podman/run_test.go index 33c0a4bfe..5ea39e457 100644 --- a/cmd/podman/run_test.go +++ b/cmd/podman/run_test.go @@ -4,24 +4,19 @@ import ( "runtime" "testing" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/inspect" cc "github.com/containers/libpod/pkg/spec" - units "github.com/docker/go-units" + "github.com/docker/go-units" ociv1 "github.com/opencontainers/image-spec/specs-go/v1" spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/spf13/cobra" "github.com/stretchr/testify/assert" - "github.com/urfave/cli" ) var ( - cmd = []string{"podman", "test", "alpine"} - CLI *cli.Context - testCommand = cli.Command{ - Name: "test", - Flags: sortFlags(createFlags), - Action: testCmd, - HideHelp: true, - } + cmd = []string{"podman", "test", "alpine"} + CLI *cliconfig.PodmanCommand ) // generates a mocked ImageData structure based on alpine @@ -53,23 +48,29 @@ func generateAlpineImageData() *inspect.ImageData { } // sets a global CLI -func testCmd(c *cli.Context) error { - CLI = c +func testCmd(c *cobra.Command) error { + CLI = &cliconfig.PodmanCommand{Command: c} return nil } // creates the mocked cli pointing to our create flags // global flags like log-level are not implemented -func createCLI() cli.App { - a := cli.App{ - Commands: []cli.Command{ - testCommand, +func createCLI(args []string) *cliconfig.PodmanCommand { + var testCommand = &cliconfig.PodmanCommand{ + Command: &cobra.Command{ + Use: "test", + RunE: func(cmd *cobra.Command, args []string) error { + return testCmd(cmd) + }, }, } - return a + rootCmd := testCommand + getCreateFlags(rootCmd) + rootCmd.ParseFlags(args) + return rootCmd } -func getRuntimeSpec(c *cli.Context) (*spec.Spec, error) { +func getRuntimeSpec(c *cliconfig.PodmanCommand) (*spec.Spec, error) { /* TODO: This test has never worked. Need to install content runtime, err := getRuntime(c) @@ -98,10 +99,11 @@ func TestPIDsLimit(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("seccomp, which is enabled by default, is only supported on Linux") } - a := createCLI() args := []string{"--pids-limit", "22"} - a.Run(append(cmd, args...)) - runtimeSpec, err := getRuntimeSpec(CLI) + a := createCLI(args) + a.InputArgs = args + //a.Run(append(cmd, args...)) + runtimeSpec, err := getRuntimeSpec(a) if err != nil { t.Fatalf(err.Error()) } @@ -116,10 +118,10 @@ func TestBLKIOWeightDevice(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("seccomp, which is enabled by default, is only supported on Linux") } - a := createCLI() args := []string{"--blkio-weight-device", "/dev/zero:100"} - a.Run(append(cmd, args...)) - runtimeSpec, err := getRuntimeSpec(CLI) + a := createCLI(args) + a.InputArgs = args + runtimeSpec, err := getRuntimeSpec(a) if err != nil { t.Fatalf(err.Error()) } @@ -134,10 +136,11 @@ func TestMemorySwap(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("seccomp, which is enabled by default, is only supported on Linux") } - a := createCLI() args := []string{"--memory-swap", "45m", "--memory", "40m"} - a.Run(append(cmd, args...)) - runtimeSpec, err := getRuntimeSpec(CLI) + a := createCLI(args) + a.InputArgs = args + //a.Run(append(cmd, args...)) + runtimeSpec, err := getRuntimeSpec(a) if err != nil { t.Fatalf(err.Error()) } diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go index 38905b5ca..ff8a282d3 100644 --- a/cmd/podman/runlabel.go +++ b/cmd/podman/runlabel.go @@ -7,88 +7,59 @@ import ( "strings" "github.com/containers/image/types" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/utils" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - runlabelFlags = []cli.Flag{ - cli.StringFlag{ - Name: "authfile", - Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ", - }, - cli.BoolFlag{ - Name: "display", - Usage: "Preview the command that the label would run", - }, - cli.StringFlag{ - Name: "cert-dir", - Usage: "`Pathname` of a directory containing TLS certificates and keys", - }, - cli.StringFlag{ - Name: "creds", - Usage: "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry", - }, - cli.StringFlag{ - Name: "name", - Usage: "Assign a name to the container", - }, - cli.StringFlag{ - Name: "opt1", - Usage: "Optional parameter to pass for install", - Hidden: true, - }, - cli.StringFlag{ - Name: "opt2", - Usage: "Optional parameter to pass for install", - Hidden: true, - }, - cli.StringFlag{ - Name: "opt3", - Usage: "Optional parameter to pass for install", - Hidden: true, - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Suppress output information when installing images", - }, - cli.BoolFlag{ - Name: "pull, p", - Usage: "Pull the image if it does not exist locally prior to executing the label contents", - }, - cli.StringFlag{ - Name: "signature-policy", - Usage: "`Pathname` of signature policy file (not usually used)", - }, - cli.BoolTFlag{ - Name: "tls-verify", - Usage: "Require HTTPS and verify certificates when contacting registries (default: true)", - }, - } - + runlabelCommand cliconfig.RunlabelValues runlabelDescription = ` Executes a command as described by a container image label. ` - runlabelCommand = cli.Command{ - Name: "runlabel", - Usage: "Execute the command described by an image label", - Description: runlabelDescription, - Flags: sortFlags(runlabelFlags), - Action: runlabelCmd, - ArgsUsage: "", - SkipArgReorder: true, - OnUsageError: usageErrorHandler, + _runlabelCommand = &cobra.Command{ + Use: "runlabel", + Short: "Execute the command described by an image label", + Long: runlabelDescription, + RunE: func(cmd *cobra.Command, args []string) error { + runlabelCommand.InputArgs = args + runlabelCommand.GlobalFlags = MainGlobalOpts + return runlabelCmd(&runlabelCommand) + }, + Example: "", } ) +func init() { + runlabelCommand.Command = _runlabelCommand + flags := runlabelCommand.Flags() + flags.StringVar(&runlabelCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&runlabelCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + flags.StringVar(&runlabelCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.BoolVar(&runlabelCommand.Display, "display", false, "Preview the command that the label would run") + flags.StringVar(&runlabelCommand.Name, "name", "", "Assign a name to the container") + + flags.StringVar(&runlabelCommand.Opt1, "opt1", "", "Optional parameter to pass for install") + flags.StringVar(&runlabelCommand.Opt2, "opt2", "", "Optional parameter to pass for install") + flags.StringVar(&runlabelCommand.Opt3, "opt3", "", "Optional parameter to pass for install") + flags.MarkHidden("opt1") + flags.MarkHidden("opt3") + flags.MarkHidden("opt3") + + flags.BoolVarP(&runlabelCommand.Pull, "pull", "p", false, "Pull the image if it does not exist locally prior to executing the label contents") + flags.BoolVarP(&runlabelCommand.Quiet, "quiet", "q", false, "Suppress output information when installing images") + flags.StringVar(&runlabelCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") + flags.BoolVar(&runlabelCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") +} + // installCmd gets the data from the command line and calls installImage // to copy an image from a registry to a local machine -func runlabelCmd(c *cli.Context) error { +func runlabelCmd(c *cliconfig.RunlabelValues) error { var ( imageName string stdErr, stdOut io.Writer @@ -105,40 +76,38 @@ func runlabelCmd(c *cli.Context) error { } opts := make(map[string]string) - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - args := c.Args() + args := c.InputArgs if len(args) < 2 { logrus.Errorf("the runlabel command requires at least 2 arguments: LABEL IMAGE") return nil } - if err := validateFlags(c, runlabelFlags); err != nil { - return err - } - if c.Bool("display") && c.Bool("quiet") { + if c.Display && c.Quiet { return errors.Errorf("the display and quiet flags cannot be used together.") } if len(args) > 2 { extraArgs = args[2:] } - pull := c.Bool("pull") + pull := c.Pull label := args[0] runlabelImage := args[1] - if c.IsSet("opt1") { - opts["opt1"] = c.String("opt1") + if c.Flag("opt1").Changed { + opts["opt1"] = c.Opt1 } - if c.IsSet("opt2") { - opts["opt2"] = c.String("opt2") + + if c.Flag("opt2").Changed { + opts["opt2"] = c.Opt2 } - if c.IsSet("opt3") { - opts["opt3"] = c.String("opt3") + if c.Flag("opt3").Changed { + opts["opt3"] = c.Opt3 } ctx := getContext() @@ -147,21 +116,21 @@ func runlabelCmd(c *cli.Context) error { stdOut = os.Stdout stdIn = os.Stdin - if c.Bool("quiet") { + if c.Quiet { stdErr = nil stdOut = nil stdIn = nil } dockerRegistryOptions := image.DockerRegistryOptions{ - DockerCertPath: c.String("cert-dir"), + DockerCertPath: c.CertDir, } - if c.IsSet("tls-verify") { - dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify")) + if c.Flag("tls-verify").Changed { + dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) } - authfile := getAuthFile(c.String("authfile")) - runLabel, imageName, err := shared.GetRunlabel(label, runlabelImage, ctx, runtime, pull, c.String("creds"), dockerRegistryOptions, authfile, c.String("signature-policy"), stdOut) + authfile := getAuthFile(c.Authfile) + runLabel, imageName, err := shared.GetRunlabel(label, runlabelImage, ctx, runtime, pull, c.Creds, dockerRegistryOptions, authfile, c.SignaturePolicy, stdOut) if err != nil { return err } @@ -169,13 +138,13 @@ func runlabelCmd(c *cli.Context) error { return errors.Errorf("%s does not have a label of %s", runlabelImage, label) } - cmd, env, err := shared.GenerateRunlabelCommand(runLabel, imageName, c.String("name"), opts, extraArgs) + cmd, env, err := shared.GenerateRunlabelCommand(runLabel, imageName, c.Name, opts, extraArgs) if err != nil { return err } - if !c.Bool("quiet") { + if !c.Quiet { fmt.Printf("Command: %s\n", strings.Join(cmd, " ")) - if c.Bool("display") { + if c.Display { return nil } } diff --git a/cmd/podman/save.go b/cmd/podman/save.go index 325140b76..766561f1a 100644 --- a/cmd/podman/save.go +++ b/cmd/podman/save.go @@ -12,12 +12,13 @@ import ( "github.com/containers/image/manifest" ociarchive "github.com/containers/image/oci/archive" "github.com/containers/image/types" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" libpodImage "github.com/containers/libpod/libpod/image" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) const ( @@ -26,67 +27,58 @@ const ( ) var ( - saveFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "compress", - Usage: "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)", - }, - cli.StringFlag{ - Name: "output, o", - Usage: "Write to a file, default is STDOUT", - Value: "/dev/stdout", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Suppress the output", - }, - cli.StringFlag{ - Name: "format", - Usage: "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-dir (directory with v2s2 manifest type)", - }, - } + saveCommand cliconfig.SaveValues saveDescription = ` Save an image to docker-archive or oci-archive on the local machine. Default is docker-archive` - saveCommand = cli.Command{ - Name: "save", - Usage: "Save image to an archive", - Description: saveDescription, - Flags: sortFlags(saveFlags), - Action: saveCmd, - ArgsUsage: "", - SkipArgReorder: true, - OnUsageError: usageErrorHandler, + _saveCommand = &cobra.Command{ + Use: "save", + Short: "Save image to an archive", + Long: saveDescription, + RunE: func(cmd *cobra.Command, args []string) error { + saveCommand.InputArgs = args + saveCommand.GlobalFlags = MainGlobalOpts + return saveCmd(&saveCommand) + }, + Example: "", } ) +func init() { + saveCommand.Command = _saveCommand + flags := saveCommand.Flags() + flags.BoolVar(&saveCommand.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)") + flags.StringVar(&saveCommand.Format, "format", "", "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-dir (directory with v2s2 manifest type)") + flags.StringVarP(&saveCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT") + flags.BoolVarP(&saveCommand.Quiet, "quiet", "q", false, "Suppress the output") + + rootCmd.AddCommand(saveCommand.Command) +} + // saveCmd saves the image to either docker-archive or oci -func saveCmd(c *cli.Context) error { - args := c.Args() +func saveCmd(c *cliconfig.SaveValues) error { + args := c.InputArgs if len(args) == 0 { return errors.Errorf("need at least 1 argument") } - if err := validateFlags(c, saveFlags); err != nil { - return err - } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not create runtime") } defer runtime.Shutdown(false) - if c.IsSet("compress") && (c.String("format") != ociManifestDir && c.String("format") != v2s2ManifestDir && c.String("format") == "") { + if c.Flag("compress").Changed && (c.Format != ociManifestDir && c.Format != v2s2ManifestDir && c.Format == "") { return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'") } var writer io.Writer - if !c.Bool("quiet") { + if !c.Quiet { writer = os.Stderr } - output := c.String("output") + output := c.Output if output == "/dev/stdout" { fi := os.Stdout if logrus.IsTerminal(fi) { @@ -105,7 +97,7 @@ func saveCmd(c *cli.Context) error { var destRef types.ImageReference var manifestType string - switch c.String("format") { + switch c.Format { case "oci-archive": destImageName := imageNameForSaveDestination(newImage, source) destRef, err = ociarchive.NewReference(output, destImageName) // destImageName may be "" diff --git a/cmd/podman/search.go b/cmd/podman/search.go index 81469a0f8..b15da1f6d 100644 --- a/cmd/podman/search.go +++ b/cmd/podman/search.go @@ -8,13 +8,14 @@ import ( "github.com/containers/image/docker" "github.com/containers/image/types" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/libpod/common" sysreg "github.com/containers/libpod/pkg/registries" "github.com/docker/distribution/reference" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) const ( @@ -23,46 +24,36 @@ const ( ) var ( - searchFlags = []cli.Flag{ - cli.StringFlag{ - Name: "authfile", - Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ", - }, - cli.StringSliceFlag{ - Name: "filter, f", - Usage: "Filter output based on conditions provided (default [])", - }, - cli.StringFlag{ - Name: "format", - Usage: "Change the output format to a Go template", - }, - cli.IntFlag{ - Name: "limit", - Usage: "Limit the number of results", - }, - cli.BoolFlag{ - Name: "no-trunc", - Usage: "Do not truncate the output", - }, - cli.BoolTFlag{ - Name: "tls-verify", - Usage: "Require HTTPS and verify certificates when contacting registries (default: true)", - }, - } + searchCommand cliconfig.SearchValues searchDescription = ` Search registries for a given image. Can search all the default registries or a specific registry. Can limit the number of results, and filter the output based on certain conditions.` - searchCommand = cli.Command{ - Name: "search", - Usage: "Search registry for image", - Description: searchDescription, - Flags: sortFlags(searchFlags), - Action: searchCmd, - ArgsUsage: "TERM", - OnUsageError: usageErrorHandler, + _searchCommand = &cobra.Command{ + Use: "search", + Short: "Search registry for image", + Long: searchDescription, + RunE: func(cmd *cobra.Command, args []string) error { + searchCommand.InputArgs = args + searchCommand.GlobalFlags = MainGlobalOpts + return searchCmd(&searchCommand) + }, + Example: "TERM", } ) +func init() { + searchCommand.Command = _searchCommand + flags := searchCommand.Flags() + flags.StringVar(&searchCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringSliceVarP(&searchCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") + flags.StringVar(&searchCommand.Format, "format", "", "Change the output format to a Go template") + flags.IntVar(&searchCommand.Limit, "limit", 0, "Limit the number of results") + flags.BoolVar(&searchCommand.NoTrunc, "no-trunc", false, "Do not truncate the output") + flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + + rootCmd.AddCommand(searchCommand.Command) +} + type searchParams struct { Index string Name string @@ -87,8 +78,8 @@ type searchFilterParams struct { isOfficial *bool } -func searchCmd(c *cli.Context) error { - args := c.Args() +func searchCmd(c *cliconfig.SearchValues) error { + args := c.InputArgs if len(args) > 1 { return errors.Errorf("too many arguments. Requires exactly 1") } @@ -106,20 +97,16 @@ func searchCmd(c *cli.Context) error { term = term[len(registry)+1:] } - if err := validateFlags(c, searchFlags); err != nil { - return err - } - - format := genSearchFormat(c.String("format")) + format := genSearchFormat(c.Format) opts := searchOpts{ format: format, - noTrunc: c.Bool("no-trunc"), - limit: c.Int("limit"), - filter: c.StringSlice("filter"), - authfile: getAuthFile(c.String("authfile")), + noTrunc: c.NoTrunc, + limit: c.Limit, + filter: c.Filter, + authfile: getAuthFile(c.Authfile), } - if c.IsSet("tls-verify") { - opts.insecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify")) + if c.Flag("tls-verify").Changed { + opts.insecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) } registries, err := getRegistries(registry) if err != nil { diff --git a/cmd/podman/sign.go b/cmd/podman/sign.go index 22aa07230..a87c12556 100644 --- a/cmd/podman/sign.go +++ b/cmd/podman/sign.go @@ -11,60 +11,63 @@ import ( "github.com/containers/image/signature" "github.com/containers/image/transports" "github.com/containers/image/transports/alltransports" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/trust" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - signFlags = []cli.Flag{ - cli.StringFlag{ - Name: "sign-by", - Usage: "Name of the signing key", - }, - cli.StringFlag{ - Name: "directory, d", - Usage: "Define an alternate directory to store signatures", - }, - } - + signCommand cliconfig.SignValues signDescription = "Create a signature file that can be used later to verify the image" - signCommand = cli.Command{ - Name: "sign", - Usage: "Sign an image", - Description: signDescription, - Flags: sortFlags(signFlags), - Action: signCmd, - ArgsUsage: "IMAGE-NAME [IMAGE-NAME ...]", - OnUsageError: usageErrorHandler, + _signCommand = &cobra.Command{ + Use: "sign", + Short: "Sign an image", + Long: signDescription, + RunE: func(cmd *cobra.Command, args []string) error { + signCommand.InputArgs = args + signCommand.GlobalFlags = MainGlobalOpts + return signCmd(&signCommand) + }, + Example: "IMAGE-NAME [IMAGE-NAME ...]", } ) +func init() { + signCommand.Command = _signCommand + flags := signCommand.Flags() + flags.StringVarP(&signCommand.Directory, "directory", "d", "", "Define an alternate directory to store signatures") + flags.StringVar(&signCommand.SignBy, "sign-by", "", "Name of the signing key") + + rootCmd.AddCommand(signCommand.Command) + +} + // SignatureStoreDir defines default directory to store signatures const SignatureStoreDir = "/var/lib/containers/sigstore" -func signCmd(c *cli.Context) error { - args := c.Args() +func signCmd(c *cliconfig.SignValues) error { + args := c.InputArgs if len(args) < 1 { return errors.Errorf("at least one image name must be specified") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not create runtime") } defer runtime.Shutdown(false) - signby := c.String("sign-by") + signby := c.SignBy if signby == "" { return errors.Errorf("please provide an identity") } var sigStoreDir string - if c.IsSet("directory") { - sigStoreDir = c.String("directory") + if c.Flag("directory").Changed { + sigStoreDir = c.Directory if _, err := os.Stat(sigStoreDir); err != nil { return errors.Wrapf(err, "invalid directory %s", sigStoreDir) } diff --git a/cmd/podman/start.go b/cmd/podman/start.go index f6e1d9882..483ab4081 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -5,84 +5,75 @@ import ( "fmt" "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" cc "github.com/containers/libpod/pkg/spec" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - startFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "attach, a", - Usage: "Attach container's STDOUT and STDERR", - }, - cli.StringFlag{ - Name: "detach-keys", - Usage: "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _.", - }, - cli.BoolFlag{ - Name: "interactive, i", - Usage: "Keep STDIN open even if not attached", - }, - cli.BoolTFlag{ - Name: "sig-proxy", - Usage: "Proxy received signals to the process (default true if attaching, false otherwise)", - }, - LatestFlag, - } + startCommand cliconfig.StartValues startDescription = ` podman start Starts one or more containers. The container name or ID can be used. ` - - startCommand = cli.Command{ - Name: "start", - Usage: "Start one or more containers", - Description: startDescription, - Flags: sortFlags(startFlags), - Action: startCmd, - ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + _startCommand = &cobra.Command{ + Use: "start", + Short: "Start one or more containers", + Long: startDescription, + RunE: func(cmd *cobra.Command, args []string) error { + startCommand.InputArgs = args + startCommand.GlobalFlags = MainGlobalOpts + return startCmd(&startCommand) + }, + Example: "CONTAINER-NAME [CONTAINER-NAME ...]", } ) -func startCmd(c *cli.Context) error { - args := c.Args() - if len(args) < 1 && !c.Bool("latest") { +func init() { + startCommand.Command = _startCommand + flags := startCommand.Flags() + flags.BoolVarP(&startCommand.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR") + flags.StringVar(&startCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _") + flags.BoolVarP(&startCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") + flags.BoolVarP(&startCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVar(&startCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true if attaching, false otherwise)") + + rootCmd.AddCommand(startCommand.Command) +} + +func startCmd(c *cliconfig.StartValues) error { + args := c.InputArgs + if len(args) < 1 && !c.Latest { return errors.Errorf("you must provide at least one container name or id") } - attach := c.Bool("attach") + attach := c.Attach if len(args) > 1 && attach { return errors.Errorf("you cannot start and attach multiple containers at once") } - if err := validateFlags(c, startFlags); err != nil { - return err - } - - sigProxy := c.BoolT("sig-proxy") + sigProxy := c.SigProxy if sigProxy && !attach { - if c.IsSet("sig-proxy") { + if c.Flag("sig-proxy").Changed { return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach") } else { sigProxy = false } } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - if c.Bool("latest") { + if c.Latest { lastCtr, err := runtime.GetLatestContainer() if err != nil { return errors.Wrapf(err, "unable to get latest container") @@ -112,12 +103,12 @@ func startCmd(c *cli.Context) error { if attach { inputStream := os.Stdin - if !c.Bool("interactive") { + if !c.Interactive { inputStream = nil } // attach to the container and also start it not already running - err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.String("detach-keys"), sigProxy, !ctrRunning) + err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, sigProxy, !ctrRunning) if ctrRunning { return err } diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go index 769354b23..8c79ed290 100644 --- a/cmd/podman/stats.go +++ b/cmd/podman/stats.go @@ -8,12 +8,13 @@ import ( "time" tm "github.com/buger/goterm" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/docker/go-units" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) type statsOutputParams struct { @@ -28,48 +29,41 @@ type statsOutputParams struct { } var ( - statsFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Show all containers. Only running containers are shown by default. The default is false", - }, - cli.BoolFlag{ - Name: "no-stream", - Usage: "Disable streaming stats and only pull the first result, default setting is false", - }, - cli.StringFlag{ - Name: "format", - Usage: "Pretty-print container statistics to JSON or using a Go template", - }, - cli.BoolFlag{ - Name: "no-reset", - Usage: "Disable resetting the screen between intervals", - }, LatestFlag, - } + statsCommand cliconfig.StatsValues - statsDescription = "Display a live stream of one or more containers' resource usage statistics" - statsCommand = cli.Command{ - Name: "stats", - Usage: "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers", - Description: statsDescription, - Flags: sortFlags(statsFlags), - Action: statsCmd, - ArgsUsage: "", - OnUsageError: usageErrorHandler, + statsDescription = "display a live stream of one or more containers' resource usage statistics" + _statsCommand = &cobra.Command{ + Use: "stats", + Short: "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers", + Long: statsDescription, + RunE: func(cmd *cobra.Command, args []string) error { + statsCommand.InputArgs = args + statsCommand.GlobalFlags = MainGlobalOpts + return statsCmd(&statsCommand) + }, + Example: "", } ) -func statsCmd(c *cli.Context) error { - if err := validateFlags(c, statsFlags); err != nil { - return err - } +func init() { + statsCommand.Command = _statsCommand + flags := statsCommand.Flags() + flags.BoolVarP(&statsCommand.All, "all", "a", false, "Show all containers. Only running containers are shown by default. The default is false") + flags.StringVar(&statsCommand.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template") + flags.BoolVarP(&statsCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVar(&statsCommand.NoReset, "no-reset", false, "Disable resetting the screen between intervals") + flags.BoolVar(&statsCommand.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false") + + rootCmd.AddCommand(statsCommand.Command) +} +func statsCmd(c *cliconfig.StatsValues) error { if os.Geteuid() != 0 { return errors.New("stats is not supported for rootless containers") } - all := c.Bool("all") - latest := c.Bool("latest") + all := c.All + latest := c.Latest ctr := 0 if all { ctr += 1 @@ -77,7 +71,7 @@ func statsCmd(c *cli.Context) error { if latest { ctr += 1 } - if len(c.Args()) > 0 { + if len(c.InputArgs) > 0 { ctr += 1 } @@ -87,14 +81,14 @@ func statsCmd(c *cli.Context) error { return errors.Errorf("you must specify --all, --latest, or at least one container") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) times := -1 - if c.Bool("no-stream") { + if c.NoStream { times = 1 } @@ -102,8 +96,8 @@ func statsCmd(c *cli.Context) error { var containerFunc func() ([]*libpod.Container, error) containerFunc = runtime.GetRunningContainers - if len(c.Args()) > 0 { - containerFunc = func() ([]*libpod.Container, error) { return runtime.GetContainersByList(c.Args()) } + if len(c.InputArgs) > 0 { + containerFunc = func() ([]*libpod.Container, error) { return runtime.GetContainersByList(c.InputArgs) } } else if latest { containerFunc = func() ([]*libpod.Container, error) { lastCtr, err := runtime.GetLatestContainer() @@ -126,7 +120,7 @@ func statsCmd(c *cli.Context) error { initialStats, err := ctr.GetContainerStats(&libpod.ContainerStats{}) if err != nil { // when doing "all", dont worry about containers that are not running - if c.Bool("all") && errors.Cause(err) == libpod.ErrCtrRemoved || errors.Cause(err) == libpod.ErrNoSuchCtr || errors.Cause(err) == libpod.ErrCtrStateInvalid { + if c.All && errors.Cause(err) == libpod.ErrCtrRemoved || errors.Cause(err) == libpod.ErrNoSuchCtr || errors.Cause(err) == libpod.ErrCtrStateInvalid { continue } return err @@ -134,7 +128,7 @@ func statsCmd(c *cli.Context) error { containerStats[ctr.ID()] = initialStats } - format := genStatsFormat(c.String("format")) + format := genStatsFormat(c.Format) step := 1 if times == -1 { @@ -168,7 +162,7 @@ func statsCmd(c *cli.Context) error { if err != nil { return err } - if strings.ToLower(format) != formats.JSONString && !c.Bool("no-reset") { + if strings.ToLower(format) != formats.JSONString && !c.NoReset { tm.Clear() tm.MoveCursor(1, 1) tm.Flush() diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index 204515c70..e2c16fe20 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -3,27 +3,18 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - stopFlags = []cli.Flag{ - cli.UintFlag{ - Name: "timeout, time, t", - Usage: "Seconds to wait for stop before killing the container", - Value: libpod.CtrRemoveTimeout, - }, - cli.BoolFlag{ - Name: "all, a", - Usage: "Stop all running containers", - }, LatestFlag, - } + stopCommand cliconfig.StopValues stopDescription = ` podman stop @@ -31,36 +22,44 @@ var ( A timeout to forcibly stop the container can also be set but defaults to 10 seconds otherwise. ` - - stopCommand = cli.Command{ - Name: "stop", - Usage: "Stop one or more containers", - Description: stopDescription, - Flags: sortFlags(stopFlags), - Action: stopCmd, - ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", - OnUsageError: usageErrorHandler, + _stopCommand = &cobra.Command{ + Use: "stop", + Short: "Stop one or more containers", + Long: stopDescription, + RunE: func(cmd *cobra.Command, args []string) error { + stopCommand.InputArgs = args + stopCommand.GlobalFlags = MainGlobalOpts + return stopCmd(&stopCommand) + }, + Example: "CONTAINER-NAME [CONTAINER-NAME ...]", } ) -func stopCmd(c *cli.Context) error { +func init() { + stopCommand.Command = _stopCommand + flags := stopCommand.Flags() + flags.BoolVarP(&stopCommand.All, "all", "a", false, "Stop all running containers") + flags.BoolVarP(&stopCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.UintVar(&stopCommand.Timeout, "time", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") + flags.UintVarP(&stopCommand.Timeout, "timeout", "t", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") - if err := checkAllAndLatest(c); err != nil { - return err - } + rootCmd.AddCommand(stopCommand.Command) +} + +func stopCmd(c *cliconfig.StopValues) error { - if err := validateFlags(c, stopFlags); err != nil { + if err := checkAllAndLatest(&c.PodmanCommand); err != nil { return err } rootless.SetSkipStorageSetup(true) - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") + containers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStateRunning, "running") if err != nil { if len(containers) == 0 { return err @@ -72,8 +71,8 @@ func stopCmd(c *cli.Context) error { for _, ctr := range containers { con := ctr var stopTimeout uint - if c.IsSet("timeout") { - stopTimeout = c.Uint("timeout") + if c.Flag("timeout").Changed { + stopTimeout = c.Timeout } else { stopTimeout = ctr.StopTimeout() } @@ -92,7 +91,7 @@ func stopCmd(c *cli.Context) error { maxWorkers := shared.Parallelize("stop") if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalInt("max-workers") + maxWorkers = c.GlobalFlags.MaxWorks } logrus.Debugf("Setting maximum workers to %d", maxWorkers) diff --git a/cmd/podman/system.go b/cmd/podman/system.go index 9596252ad..f6b28fee1 100644 --- a/cmd/podman/system.go +++ b/cmd/podman/system.go @@ -1,29 +1,23 @@ package main import ( - "sort" - - "github.com/urfave/cli" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" ) var ( - systemSubCommands = []cli.Command{ - pruneSystemCommand, - } systemDescription = "Manage podman" - systemCommand = cli.Command{ - Name: "system", - Usage: "Manage podman", - Description: systemDescription, - ArgsUsage: "", - Subcommands: getSystemSubCommandsSorted(), - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, + + systemCommand = cliconfig.PodmanCommand{ + Command: &cobra.Command{ + Use: "system", + Short: "Manage podman", + Long: systemDescription, + }, } ) -func getSystemSubCommandsSorted() []cli.Command { - systemSubCommands = append(systemSubCommands, getSystemSubCommands()...) - sort.Sort(commandSortedAlpha{systemSubCommands}) - return systemSubCommands +func init() { + systemCommand.AddCommand(getSystemSubCommands()...) + rootCmd.AddCommand(systemCommand.Command) } diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go index 64d291560..c918fbe3b 100644 --- a/cmd/podman/system_prune.go +++ b/cmd/podman/system_prune.go @@ -6,50 +6,50 @@ import ( "os" "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + pruneSystemCommand cliconfig.SystemPruneValues pruneSystemDescription = ` podman system prune Remove unused data ` - pruneSystemFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Remove all unused data", - }, - cli.BoolFlag{ - Name: "force, f", - Usage: "Do not prompt for confirmation", - }, - cli.BoolFlag{ - Name: "volumes", - Usage: "Prune volumes", + _pruneSystemCommand = &cobra.Command{ + Use: "prune", + Short: "Remove unused data", + Long: pruneSystemDescription, + RunE: func(cmd *cobra.Command, args []string) error { + pruneSystemCommand.InputArgs = args + pruneSystemCommand.GlobalFlags = MainGlobalOpts + return pruneSystemCmd(&pruneSystemCommand) }, } - pruneSystemCommand = cli.Command{ - Name: "prune", - Usage: "Remove unused data", - Description: pruneSystemDescription, - Action: pruneSystemCmd, - OnUsageError: usageErrorHandler, - Flags: pruneSystemFlags, - } ) -func pruneSystemCmd(c *cli.Context) error { +func init() { + + pruneSystemCommand.Command = _pruneSystemCommand + flags := pruneSystemCommand.Flags() + flags.BoolVarP(&pruneSystemCommand.All, "all", "a", false, "Remove all unused data") + flags.BoolVarP(&pruneSystemCommand.Force, "force", "f", false, "Do not prompt for confirmation") + flags.BoolVar(&pruneSystemCommand.Volume, "volumes", false, "Prune volumes") + +} + +func pruneSystemCmd(c *cliconfig.SystemPruneValues) error { // Prompt for confirmation if --force is not set - if !c.Bool("force") { + if !c.Force { reader := bufio.NewReader(os.Stdin) volumeString := "" - if c.Bool("volumes") { + if c.Volume { volumeString = ` - all volumes not used by at least one container` } @@ -68,7 +68,7 @@ Are you sure you want to continue? [y/N] `, volumeString) } } - runtime, err := adapter.GetRuntime(c) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -90,7 +90,7 @@ Are you sure you want to continue? [y/N] `, volumeString) // Call prune; if any cids are returned, print them and then // return err in case an error also came up - pruneCids, err := runtime.PruneImages(c.Bool("all")) + pruneCids, err := runtime.PruneImages(c.All) if len(pruneCids) > 0 { fmt.Println("Deleted Images") for _, cid := range pruneCids { diff --git a/cmd/podman/tag.go b/cmd/podman/tag.go index d19cf69a2..2c7cf4abb 100644 --- a/cmd/podman/tag.go +++ b/cmd/podman/tag.go @@ -1,29 +1,41 @@ package main import ( + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + tagCommand cliconfig.TagValues + tagDescription = "Adds one or more additional names to locally-stored image" - tagCommand = cli.Command{ - Name: "tag", - Usage: "Add an additional name to a local image", - Description: tagDescription, - Action: tagCmd, - ArgsUsage: "IMAGE-NAME [IMAGE-NAME ...]", - OnUsageError: usageErrorHandler, + _tagCommand = &cobra.Command{ + Use: "tag", + Short: "Add an additional name to a local image", + Long: tagDescription, + RunE: func(cmd *cobra.Command, args []string) error { + tagCommand.InputArgs = args + tagCommand.GlobalFlags = MainGlobalOpts + return tagCmd(&tagCommand) + }, + Example: "IMAGE-NAME [IMAGE-NAME ...]", } ) -func tagCmd(c *cli.Context) error { - args := c.Args() +func init() { + tagCommand.Command = _tagCommand + rootCmd.AddCommand(tagCommand.Command) + +} + +func tagCmd(c *cliconfig.TagValues) error { + args := c.InputArgs if len(args) < 2 { return errors.Errorf("image name and at least one new name must be specified") } - runtime, err := adapter.GetRuntime(c) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not create runtime") } diff --git a/cmd/podman/top.go b/cmd/podman/top.go index 3012265ea..a03830ee5 100644 --- a/cmd/podman/top.go +++ b/cmd/podman/top.go @@ -6,11 +6,12 @@ import ( "strings" "text/tabwriter" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) func getDescriptorString() string { @@ -24,13 +25,7 @@ Format Descriptors: } var ( - topFlags = []cli.Flag{ - LatestFlag, - cli.BoolFlag{ - Name: "list-descriptors", - Hidden: true, - }, - } + topCommand cliconfig.TopValues topDescription = fmt.Sprintf(`Display the running processes of the container. Specify format descriptors to alter the output. You may run "podman top -l pid pcpu seccomp" to print the process ID, the CPU percentage and the seccomp mode of each process of @@ -38,24 +33,35 @@ the latest container. %s `, getDescriptorString()) - topCommand = cli.Command{ - Name: "top", - Usage: "Display the running processes of a container", - Description: topDescription, - Flags: sortFlags(topFlags), - Action: topCmd, - ArgsUsage: "CONTAINER-NAME [format descriptors]", - SkipArgReorder: true, - OnUsageError: usageErrorHandler, + _topCommand = &cobra.Command{ + Use: "top", + Short: "Display the running processes of a container", + Long: topDescription, + RunE: func(cmd *cobra.Command, args []string) error { + topCommand.InputArgs = args + topCommand.GlobalFlags = MainGlobalOpts + return topCmd(&topCommand) + }, + Example: "CONTAINER-NAME [format descriptors]", } ) -func topCmd(c *cli.Context) error { +func init() { + topCommand.Command = _topCommand + flags := topCommand.Flags() + flags.BoolVar(&topCommand.ListDescriptors, "list-descriptors", false, "") + flags.MarkHidden("list-descriptors") + flags.BoolVarP(&topCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + + rootCmd.AddCommand(topCommand.Command) +} + +func topCmd(c *cliconfig.TopValues) error { var container *libpod.Container var err error - args := c.Args() + args := c.InputArgs - if c.Bool("list-descriptors") { + if c.ListDescriptors { descriptors, err := libpod.GetContainerPidInformationDescriptors() if err != nil { return err @@ -64,22 +70,19 @@ func topCmd(c *cli.Context) error { return nil } - if len(args) < 1 && !c.Bool("latest") { + if len(args) < 1 && !c.Latest { return errors.Errorf("you must provide the name or id of a running container") } - if err := validateFlags(c, topFlags); err != nil { - return err - } rootless.SetSkipStorageSetup(true) - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) var descriptors []string - if c.Bool("latest") { + if c.Latest { descriptors = args container, err = runtime.GetLatestContainer() } else { diff --git a/cmd/podman/trust.go b/cmd/podman/trust.go index a99be6ba2..6304586c3 100644 --- a/cmd/podman/trust.go +++ b/cmd/podman/trust.go @@ -1,369 +1,21 @@ package main import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "sort" - "strings" - - "github.com/containers/image/types" - "github.com/containers/libpod/cmd/podman/formats" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod/image" - "github.com/containers/libpod/pkg/trust" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" ) var ( - setTrustFlags = []cli.Flag{ - cli.StringFlag{ - Name: "type, t", - Usage: "Trust type, accept values: signedBy(default), accept, reject.", - Value: "signedBy", - }, - cli.StringSliceFlag{ - Name: "pubkeysfile, f", - Usage: `Path of installed public key(s) to trust for TARGET. - Absolute path to keys is added to policy.json. May - used multiple times to define multiple public keys. - File(s) must exist before using this command.`, - }, - cli.StringFlag{ - Name: "policypath", - Hidden: true, - }, - } - showTrustFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "raw", - Usage: "Output raw policy file", + trustCommand = cliconfig.PodmanCommand{ + Command: &cobra.Command{ + Use: "trust", + Short: "Manage container image trust policy", + Long: "podman image trust command", }, - cli.BoolFlag{ - Name: "json, j", - Usage: "Output as json", - }, - cli.StringFlag{ - Name: "policypath", - Hidden: true, - }, - cli.StringFlag{ - Name: "registrypath", - Hidden: true, - }, - } - - setTrustDescription = "Set default trust policy or add a new trust policy for a registry" - setTrustCommand = cli.Command{ - Name: "set", - Usage: "Set default trust policy or a new trust policy for a registry", - Description: setTrustDescription, - Flags: sortFlags(setTrustFlags), - ArgsUsage: "default | REGISTRY[/REPOSITORY]", - Action: setTrustCmd, - OnUsageError: usageErrorHandler, - } - - showTrustDescription = "Display trust policy for the system" - showTrustCommand = cli.Command{ - Name: "show", - Usage: "Display trust policy for the system", - Description: showTrustDescription, - Flags: sortFlags(showTrustFlags), - Action: showTrustCmd, - ArgsUsage: "", - UseShortOptionHandling: true, - OnUsageError: usageErrorHandler, - } - - trustSubCommands = []cli.Command{ - setTrustCommand, - showTrustCommand, - } - - trustDescription = fmt.Sprintf(`Manages the trust policy of the host system. (%s) - Trust policy describes a registry scope that must be signed by public keys.`, getDefaultPolicyPath()) - trustCommand = cli.Command{ - Name: "trust", - Usage: "Manage container image trust policy", - Description: trustDescription, - ArgsUsage: "{set,show} ...", - Subcommands: trustSubCommands, - OnUsageError: usageErrorHandler, } ) -func showTrustCmd(c *cli.Context) error { - runtime, err := libpodruntime.GetRuntime(c) - if err != nil { - return errors.Wrapf(err, "could not create runtime") - } - - var ( - policyPath string - systemRegistriesDirPath string - outjson interface{} - ) - if c.IsSet("policypath") { - policyPath = c.String("policypath") - } else { - policyPath = trust.DefaultPolicyPath(runtime.SystemContext()) - } - policyContent, err := ioutil.ReadFile(policyPath) - if err != nil { - return errors.Wrapf(err, "unable to read %s", policyPath) - } - if c.IsSet("registrypath") { - systemRegistriesDirPath = c.String("registrypath") - } else { - systemRegistriesDirPath = trust.RegistriesDirPath(runtime.SystemContext()) - } - - if c.Bool("raw") { - _, err := os.Stdout.Write(policyContent) - if err != nil { - return errors.Wrap(err, "could not read trust policies") - } - return nil - } - - policyContentStruct, err := trust.GetPolicy(policyPath) - if err != nil { - return errors.Wrapf(err, "could not read trust policies") - } - - if c.Bool("json") { - policyJSON, err := getPolicyJSON(policyContentStruct, systemRegistriesDirPath) - if err != nil { - return errors.Wrapf(err, "could not show trust policies in JSON format") - } - outjson = policyJSON - out := formats.JSONStruct{Output: outjson} - return formats.Writer(out).Out() - } - - showOutputMap, err := getPolicyShowOutput(policyContentStruct, systemRegistriesDirPath) - if err != nil { - return errors.Wrapf(err, "could not show trust policies") - } - out := formats.StdoutTemplateArray{Output: showOutputMap, Template: "{{.Repo}}\t{{.Trusttype}}\t{{.GPGid}}\t{{.Sigstore}}"} - return formats.Writer(out).Out() -} - -func setTrustCmd(c *cli.Context) error { - runtime, err := libpodruntime.GetRuntime(c) - if err != nil { - return errors.Wrapf(err, "could not create runtime") - } - var ( - policyPath string - policyContentStruct trust.PolicyContent - newReposContent []trust.RepoContent - ) - args := c.Args() - if len(args) != 1 { - return errors.Errorf("default or a registry name must be specified") - } - valid, err := image.IsValidImageURI(args[0]) - if err != nil || !valid { - return errors.Wrapf(err, "invalid image uri %s", args[0]) - } - - trusttype := c.String("type") - if !isValidTrustType(trusttype) { - return errors.Errorf("invalid choice: %s (choose from 'accept', 'reject', 'signedBy')", trusttype) - } - if trusttype == "accept" { - trusttype = "insecureAcceptAnything" - } - - pubkeysfile := c.StringSlice("pubkeysfile") - if len(pubkeysfile) == 0 && trusttype == "signedBy" { - return errors.Errorf("At least one public key must be defined for type 'signedBy'") - } - - if c.IsSet("policypath") { - policyPath = c.String("policypath") - } else { - policyPath = trust.DefaultPolicyPath(runtime.SystemContext()) - } - _, err = os.Stat(policyPath) - if !os.IsNotExist(err) { - policyContent, err := ioutil.ReadFile(policyPath) - if err != nil { - return errors.Wrapf(err, "unable to read %s", policyPath) - } - if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { - return errors.Errorf("could not read trust policies") - } - } - if len(pubkeysfile) != 0 { - for _, filepath := range pubkeysfile { - newReposContent = append(newReposContent, trust.RepoContent{Type: trusttype, KeyType: "GPGKeys", KeyPath: filepath}) - } - } else { - newReposContent = append(newReposContent, trust.RepoContent{Type: trusttype}) - } - if args[0] == "default" { - policyContentStruct.Default = newReposContent - } else { - if len(policyContentStruct.Default) == 0 { - return errors.Errorf("Default trust policy must be set.") - } - registryExists := false - for transport, transportval := range policyContentStruct.Transports { - _, registryExists = transportval[args[0]] - if registryExists { - policyContentStruct.Transports[transport][args[0]] = newReposContent - break - } - } - if !registryExists { - if policyContentStruct.Transports == nil { - policyContentStruct.Transports = make(map[string]trust.RepoMap) - } - if policyContentStruct.Transports["docker"] == nil { - policyContentStruct.Transports["docker"] = make(map[string][]trust.RepoContent) - } - policyContentStruct.Transports["docker"][args[0]] = append(policyContentStruct.Transports["docker"][args[0]], newReposContent...) - } - } - - data, err := json.MarshalIndent(policyContentStruct, "", " ") - if err != nil { - return errors.Wrapf(err, "error setting trust policy") - } - err = ioutil.WriteFile(policyPath, data, 0644) - if err != nil { - return errors.Wrapf(err, "error setting trust policy") - } - return nil -} - -func sortShowOutputMapKey(m map[string]trust.ShowOutput) []string { - keys := make([]string, len(m)) - i := 0 - for k := range m { - keys[i] = k - i++ - } - sort.Strings(keys) - return keys -} - -func isValidTrustType(t string) bool { - if t == "accept" || t == "insecureAcceptAnything" || t == "reject" || t == "signedBy" { - return true - } - return false -} - -func getDefaultPolicyPath() string { - return trust.DefaultPolicyPath(&types.SystemContext{}) -} - -func getPolicyJSON(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, error) { - registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) - if err != nil { - return nil, err - } - - policyJSON := make(map[string]map[string]interface{}) - if len(policyContentStruct.Default) > 0 { - policyJSON["* (default)"] = make(map[string]interface{}) - policyJSON["* (default)"]["type"] = policyContentStruct.Default[0].Type - } - for transname, transval := range policyContentStruct.Transports { - for repo, repoval := range transval { - policyJSON[repo] = make(map[string]interface{}) - policyJSON[repo]["type"] = repoval[0].Type - policyJSON[repo]["transport"] = transname - keyarr := []string{} - uids := []string{} - for _, repoele := range repoval { - if len(repoele.KeyPath) > 0 { - keyarr = append(keyarr, repoele.KeyPath) - uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) - } - if len(repoele.KeyData) > 0 { - keyarr = append(keyarr, string(repoele.KeyData)) - uids = append(uids, trust.GetGPGIdFromKeyData(string(repoele.KeyData))...) - } - } - policyJSON[repo]["keys"] = keyarr - policyJSON[repo]["sigstore"] = "" - registryNamespace := trust.HaveMatchRegistry(repo, registryConfigs) - if registryNamespace != nil { - policyJSON[repo]["sigstore"] = registryNamespace.SigStore - } - } - } - return policyJSON, nil -} - -var typeDescription = map[string]string{"insecureAcceptAnything": "accept", "signedBy": "signed", "reject": "reject"} - -func trustTypeDescription(trustType string) string { - trustDescription, exist := typeDescription[trustType] - if !exist { - logrus.Warnf("invalid trust type %s", trustType) - } - return trustDescription -} - -func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]interface{}, error) { - var output []interface{} - - registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) - if err != nil { - return nil, err - } - - trustShowOutputMap := make(map[string]trust.ShowOutput) - if len(policyContentStruct.Default) > 0 { - defaultPolicyStruct := trust.ShowOutput{ - Repo: "default", - Trusttype: trustTypeDescription(policyContentStruct.Default[0].Type), - } - trustShowOutputMap["* (default)"] = defaultPolicyStruct - } - for _, transval := range policyContentStruct.Transports { - for repo, repoval := range transval { - tempTrustShowOutput := trust.ShowOutput{ - Repo: repo, - Trusttype: repoval[0].Type, - } - keyarr := []string{} - uids := []string{} - for _, repoele := range repoval { - if len(repoele.KeyPath) > 0 { - keyarr = append(keyarr, repoele.KeyPath) - uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) - } - if len(repoele.KeyData) > 0 { - keyarr = append(keyarr, string(repoele.KeyData)) - uids = append(uids, trust.GetGPGIdFromKeyData(string(repoele.KeyData))...) - } - } - tempTrustShowOutput.GPGid = strings.Join(uids, ", ") - - registryNamespace := trust.HaveMatchRegistry(repo, registryConfigs) - if registryNamespace != nil { - tempTrustShowOutput.Sigstore = registryNamespace.SigStore - } - trustShowOutputMap[repo] = tempTrustShowOutput - } - } - - sortedRepos := sortShowOutputMapKey(trustShowOutputMap) - for _, reponame := range sortedRepos { - showOutput, exists := trustShowOutputMap[reponame] - if exists { - output = append(output, interface{}(showOutput)) - } - } - return output, nil +func init() { + trustCommand.AddCommand(getTrustSubCommands()...) + imageCommand.AddCommand(trustCommand.Command) } diff --git a/cmd/podman/trust_set_show.go b/cmd/podman/trust_set_show.go new file mode 100644 index 000000000..cd9438220 --- /dev/null +++ b/cmd/podman/trust_set_show.go @@ -0,0 +1,343 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "os" + "sort" + "strings" + + "github.com/containers/image/types" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/formats" + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/trust" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + setTrustCommand cliconfig.SetTrustValues + showTrustCommand cliconfig.ShowTrustValues + setTrustDescription = "Set default trust policy or add a new trust policy for a registry" + _setTrustCommand = &cobra.Command{ + Use: "set", + Short: "Set default trust policy or a new trust policy for a registry", + Long: setTrustDescription, + Example: "default | REGISTRY[/REPOSITORY]", + RunE: func(cmd *cobra.Command, args []string) error { + setTrustCommand.InputArgs = args + setTrustCommand.GlobalFlags = MainGlobalOpts + return setTrustCmd(&setTrustCommand) + }, + } + + showTrustDescription = "Display trust policy for the system" + _showTrustCommand = &cobra.Command{ + Use: "show", + Short: "Display trust policy for the system", + Long: showTrustDescription, + RunE: func(cmd *cobra.Command, args []string) error { + showTrustCommand.InputArgs = args + showTrustCommand.GlobalFlags = MainGlobalOpts + return showTrustCmd(&showTrustCommand) + }, + Example: "", + } +) + +func init() { + + setTrustCommand.Command = _setTrustCommand + showTrustCommand.Command = _showTrustCommand + setFlags := setTrustCommand.Flags() + setFlags.StringVar(&setTrustCommand.PolicyPath, "policypath", "", "") + setFlags.MarkHidden("policypath") + setFlags.StringSliceVarP(&setTrustCommand.PubKeysFile, "pubkeysfile", "f", []string{}, `Path of installed public key(s) to trust for TARGET. +Absolute path to keys is added to policy.json. May +used multiple times to define multiple public keys. +File(s) must exist before using this command`) + setFlags.StringVarP(&setTrustCommand.TrustType, "type", "t", "signedBy", "Trust type, accept values: signedBy(default), accept, reject") + + showFlags := showTrustCommand.Flags() + showFlags.BoolVarP(&showTrustCommand.Json, "json", "j", false, "Output as json") + showFlags.StringVar(&showTrustCommand.PolicyPath, "policypath", "", "") + showFlags.BoolVar(&showTrustCommand.Raw, "raw", false, "Output raw policy file") + showFlags.MarkHidden("policypath") + showFlags.StringVar(&showTrustCommand.RegistryPath, "registrypath", "", "") + showFlags.MarkHidden("registrypath") +} + +func showTrustCmd(c *cliconfig.ShowTrustValues) error { + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "could not create runtime") + } + + var ( + policyPath string + systemRegistriesDirPath string + outjson interface{} + ) + if c.Flag("policypath").Changed { + policyPath = c.PolicyPath + } else { + policyPath = trust.DefaultPolicyPath(runtime.SystemContext()) + } + policyContent, err := ioutil.ReadFile(policyPath) + if err != nil { + return errors.Wrapf(err, "unable to read %s", policyPath) + } + if c.Flag("registrypath").Changed { + systemRegistriesDirPath = c.RegistryPath + } else { + systemRegistriesDirPath = trust.RegistriesDirPath(runtime.SystemContext()) + } + + if c.Raw { + _, err := os.Stdout.Write(policyContent) + if err != nil { + return errors.Wrap(err, "could not read raw trust policies") + } + return nil + } + + policyContentStruct, err := trust.GetPolicy(policyPath) + if err != nil { + return errors.Wrapf(err, "could not read trust policies") + } + + if c.Json { + policyJSON, err := getPolicyJSON(policyContentStruct, systemRegistriesDirPath) + if err != nil { + return errors.Wrapf(err, "could not show trust policies in JSON format") + } + outjson = policyJSON + out := formats.JSONStruct{Output: outjson} + return formats.Writer(out).Out() + } + + showOutputMap, err := getPolicyShowOutput(policyContentStruct, systemRegistriesDirPath) + if err != nil { + return errors.Wrapf(err, "could not show trust policies") + } + out := formats.StdoutTemplateArray{Output: showOutputMap, Template: "{{.Repo}}\t{{.Trusttype}}\t{{.GPGid}}\t{{.Sigstore}}"} + return formats.Writer(out).Out() +} + +func setTrustCmd(c *cliconfig.SetTrustValues) error { + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "could not create runtime") + } + var ( + policyPath string + policyContentStruct trust.PolicyContent + newReposContent []trust.RepoContent + ) + args := c.InputArgs + if len(args) != 1 { + return errors.Errorf("default or a registry name must be specified") + } + valid, err := image.IsValidImageURI(args[0]) + if err != nil || !valid { + return errors.Wrapf(err, "invalid image uri %s", args[0]) + } + + trusttype := c.TrustType + if !isValidTrustType(trusttype) { + return errors.Errorf("invalid choice: %s (choose from 'accept', 'reject', 'signedBy')", trusttype) + } + if trusttype == "accept" { + trusttype = "insecureAcceptAnything" + } + + pubkeysfile := c.PubKeysFile + if len(pubkeysfile) == 0 && trusttype == "signedBy" { + return errors.Errorf("At least one public key must be defined for type 'signedBy'") + } + + if c.Flag("policypath").Changed { + policyPath = c.PolicyPath + } else { + policyPath = trust.DefaultPolicyPath(runtime.SystemContext()) + } + _, err = os.Stat(policyPath) + if !os.IsNotExist(err) { + policyContent, err := ioutil.ReadFile(policyPath) + if err != nil { + return errors.Wrapf(err, "unable to read %s", policyPath) + } + if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { + return errors.Errorf("could not read trust policies") + } + } + if len(pubkeysfile) != 0 { + for _, filepath := range pubkeysfile { + newReposContent = append(newReposContent, trust.RepoContent{Type: trusttype, KeyType: "GPGKeys", KeyPath: filepath}) + } + } else { + newReposContent = append(newReposContent, trust.RepoContent{Type: trusttype}) + } + if args[0] == "default" { + policyContentStruct.Default = newReposContent + } else { + if len(policyContentStruct.Default) == 0 { + return errors.Errorf("Default trust policy must be set.") + } + registryExists := false + for transport, transportval := range policyContentStruct.Transports { + _, registryExists = transportval[args[0]] + if registryExists { + policyContentStruct.Transports[transport][args[0]] = newReposContent + break + } + } + if !registryExists { + if policyContentStruct.Transports == nil { + policyContentStruct.Transports = make(map[string]trust.RepoMap) + } + if policyContentStruct.Transports["docker"] == nil { + policyContentStruct.Transports["docker"] = make(map[string][]trust.RepoContent) + } + policyContentStruct.Transports["docker"][args[0]] = append(policyContentStruct.Transports["docker"][args[0]], newReposContent...) + } + } + + data, err := json.MarshalIndent(policyContentStruct, "", " ") + if err != nil { + return errors.Wrapf(err, "error setting trust policy") + } + err = ioutil.WriteFile(policyPath, data, 0644) + if err != nil { + return errors.Wrapf(err, "error setting trust policy") + } + return nil +} + +func sortShowOutputMapKey(m map[string]trust.ShowOutput) []string { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +func isValidTrustType(t string) bool { + if t == "accept" || t == "insecureAcceptAnything" || t == "reject" || t == "signedBy" { + return true + } + return false +} + +func getDefaultPolicyPath() string { + return trust.DefaultPolicyPath(&types.SystemContext{}) +} + +func getPolicyJSON(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, error) { + registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) + if err != nil { + return nil, err + } + + policyJSON := make(map[string]map[string]interface{}) + if len(policyContentStruct.Default) > 0 { + policyJSON["* (default)"] = make(map[string]interface{}) + policyJSON["* (default)"]["type"] = policyContentStruct.Default[0].Type + } + for transname, transval := range policyContentStruct.Transports { + for repo, repoval := range transval { + policyJSON[repo] = make(map[string]interface{}) + policyJSON[repo]["type"] = repoval[0].Type + policyJSON[repo]["transport"] = transname + keyarr := []string{} + uids := []string{} + for _, repoele := range repoval { + if len(repoele.KeyPath) > 0 { + keyarr = append(keyarr, repoele.KeyPath) + uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) + } + if len(repoele.KeyData) > 0 { + keyarr = append(keyarr, string(repoele.KeyData)) + uids = append(uids, trust.GetGPGIdFromKeyData(string(repoele.KeyData))...) + } + } + policyJSON[repo]["keys"] = keyarr + policyJSON[repo]["sigstore"] = "" + registryNamespace := trust.HaveMatchRegistry(repo, registryConfigs) + if registryNamespace != nil { + policyJSON[repo]["sigstore"] = registryNamespace.SigStore + } + } + } + return policyJSON, nil +} + +var typeDescription = map[string]string{"insecureAcceptAnything": "accept", "signedBy": "signed", "reject": "reject"} + +func trustTypeDescription(trustType string) string { + trustDescription, exist := typeDescription[trustType] + if !exist { + logrus.Warnf("invalid trust type %s", trustType) + } + return trustDescription +} + +func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]interface{}, error) { + var output []interface{} + + registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) + if err != nil { + return nil, err + } + + trustShowOutputMap := make(map[string]trust.ShowOutput) + if len(policyContentStruct.Default) > 0 { + defaultPolicyStruct := trust.ShowOutput{ + Repo: "default", + Trusttype: trustTypeDescription(policyContentStruct.Default[0].Type), + } + trustShowOutputMap["* (default)"] = defaultPolicyStruct + } + for _, transval := range policyContentStruct.Transports { + for repo, repoval := range transval { + tempTrustShowOutput := trust.ShowOutput{ + Repo: repo, + Trusttype: repoval[0].Type, + } + keyarr := []string{} + uids := []string{} + for _, repoele := range repoval { + if len(repoele.KeyPath) > 0 { + keyarr = append(keyarr, repoele.KeyPath) + uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) + } + if len(repoele.KeyData) > 0 { + keyarr = append(keyarr, string(repoele.KeyData)) + uids = append(uids, trust.GetGPGIdFromKeyData(string(repoele.KeyData))...) + } + } + tempTrustShowOutput.GPGid = strings.Join(uids, ", ") + + registryNamespace := trust.HaveMatchRegistry(repo, registryConfigs) + if registryNamespace != nil { + tempTrustShowOutput.Sigstore = registryNamespace.SigStore + } + trustShowOutputMap[repo] = tempTrustShowOutput + } + } + + sortedRepos := sortShowOutputMapKey(trustShowOutputMap) + for _, reponame := range sortedRepos { + showOutput, exists := trustShowOutputMap[reponame] + if exists { + output = append(output, interface{}(showOutput)) + } + } + return output, nil +} diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go index ab6925e65..4622e8276 100644 --- a/cmd/podman/umount.go +++ b/cmd/podman/umount.go @@ -3,60 +3,62 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/storage" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - umountFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Umount all of the currently mounted containers", - }, - cli.BoolFlag{ - Name: "force, f", - Usage: "Force the complete umount all of the currently mounted containers", - }, - LatestFlag, - } - - description = ` + umountCommand cliconfig.UmountValues + description = ` Container storage increments a mount counter each time a container is mounted. When a container is unmounted, the mount counter is decremented and the container's root filesystem is physically unmounted only when the mount counter reaches zero indicating no other processes are using the mount. An unmount can be forced with the --force flag. ` - umountCommand = cli.Command{ - Name: "umount", - Aliases: []string{"unmount"}, - Usage: "Unmount working container's root filesystem", - Description: description, - Flags: sortFlags(umountFlags), - Action: umountCmd, - ArgsUsage: "CONTAINER-NAME-OR-ID", - OnUsageError: usageErrorHandler, + _umountCommand = &cobra.Command{ + Use: "umount", + Aliases: []string{"unmount"}, + Short: "Unmounts working container's root filesystem", + Long: description, + RunE: func(cmd *cobra.Command, args []string) error { + umountCommand.InputArgs = args + umountCommand.GlobalFlags = MainGlobalOpts + return umountCmd(&umountCommand) + }, + Example: "CONTAINER-NAME-OR-ID", } ) -func umountCmd(c *cli.Context) error { - runtime, err := libpodruntime.GetRuntime(c) +func init() { + umountCommand.Command = _umountCommand + flags := umountCommand.Flags() + flags.BoolVarP(&umountCommand.All, "all", "a", false, "Umount all of the currently mounted containers") + flags.BoolVarP(&umountCommand.Force, "force", "f", false, "Force the complete umount all of the currently mounted containers") + flags.BoolVarP(&umountCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + + rootCmd.AddCommand(umountCommand.Command) +} + +func umountCmd(c *cliconfig.UmountValues) error { + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - force := c.Bool("force") - umountAll := c.Bool("all") - if err := checkAllAndLatest(c); err != nil { + force := c.Force + umountAll := c.All + if err := checkAllAndLatest(&c.PodmanCommand); err != nil { return err } - containers, err := getAllOrLatestContainers(c, runtime, -1, "all") + containers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all") if err != nil { if len(containers) == 0 { return err diff --git a/cmd/podman/unpause.go b/cmd/podman/unpause.go index 91b5fda33..fb3a7b57a 100644 --- a/cmd/podman/unpause.go +++ b/cmd/podman/unpause.go @@ -3,38 +3,45 @@ package main import ( "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( - unpauseFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Unpause all paused containers", - }, - } + unpauseCommand cliconfig.UnpauseValues + unpauseDescription = ` podman unpause Unpauses one or more running containers. The container name or ID can be used. ` - unpauseCommand = cli.Command{ - Name: "unpause", - Usage: "Unpause the processes in one or more containers", - Description: unpauseDescription, - Flags: unpauseFlags, - Action: unpauseCmd, - ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", - OnUsageError: usageErrorHandler, + _unpauseCommand = &cobra.Command{ + Use: "unpause", + Short: "Unpause the processes in one or more containers", + Long: unpauseDescription, + RunE: func(cmd *cobra.Command, args []string) error { + unpauseCommand.InputArgs = args + unpauseCommand.GlobalFlags = MainGlobalOpts + return unpauseCmd(&unpauseCommand) + }, + Example: "CONTAINER-NAME [CONTAINER-NAME ...]", } ) -func unpauseCmd(c *cli.Context) error { +func init() { + unpauseCommand.Command = _unpauseCommand + flags := unpauseCommand.Flags() + flags.BoolVarP(&unpauseCommand.All, "all", "a", false, "Unpause all paused containers") + + rootCmd.AddCommand(unpauseCommand.Command) +} + +func unpauseCmd(c *cliconfig.UnpauseValues) error { var ( unpauseContainers []*libpod.Container unpauseFuncs []shared.ParallelWorkerInput @@ -43,18 +50,18 @@ func unpauseCmd(c *cli.Context) error { return errors.New("unpause is not supported for rootless containers") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - args := c.Args() - if len(args) < 1 && !c.Bool("all") { + args := c.InputArgs + if len(args) < 1 && !c.All { return errors.Errorf("you must provide at least one container name or id") } - if c.Bool("all") { - cs, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStatePaused, "paused") + if c.All { + cs, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStatePaused, "paused") if err != nil { return err } @@ -84,7 +91,7 @@ func unpauseCmd(c *cli.Context) error { maxWorkers := shared.Parallelize("unpause") if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalInt("max-workers") + maxWorkers = c.GlobalFlags.MaxWorks } logrus.Debugf("Setting maximum workers to %d", maxWorkers) diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go index a59535b43..744d010d5 100644 --- a/cmd/podman/utils.go +++ b/cmd/podman/utils.go @@ -6,12 +6,12 @@ import ( "os" gosignal "os/signal" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/term" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" "golang.org/x/crypto/ssh/terminal" "k8s.io/client-go/tools/remotecommand" ) @@ -158,13 +158,10 @@ func (f *RawTtyFormatter) Format(entry *logrus.Entry) ([]byte, error) { return bytes, err } -func checkMutuallyExclusiveFlags(c *cli.Context) error { +func checkMutuallyExclusiveFlags(c *cliconfig.PodmanCommand) error { if err := checkAllAndLatest(c); err != nil { return err } - if err := validateFlags(c, startFlags); err != nil { - return err - } return nil } @@ -174,8 +171,8 @@ func checkMutuallyExclusiveFlags(c *cli.Context) error { // will hold all of the successful pods, and error will hold the last error. // The remaining errors will be logged. On success, pods will hold all pods and // error will be nil. -func getPodsFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Pod, error) { - args := c.Args() +func getPodsFromContext(c *cliconfig.PodmanCommand, r *libpod.Runtime) ([]*libpod.Pod, error) { + args := c.InputArgs var pods []*libpod.Pod var lastError error var err error @@ -209,8 +206,8 @@ func getPodsFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Pod, error return pods, lastError } -func getVolumesFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Volume, error) { - args := c.Args() +func getVolumesFromContext(c *cliconfig.PodmanCommand, r *libpod.Runtime) ([]*libpod.Volume, error) { + args := c.InputArgs var ( vols []*libpod.Volume lastError error diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go index 38ce77415..2b76e034e 100644 --- a/cmd/podman/varlink.go +++ b/cmd/podman/varlink.go @@ -5,55 +5,60 @@ package main import ( "time" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/pkg/varlinkapi" "github.com/containers/libpod/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" "github.com/varlink/go/varlink" ) var ( + varlinkCommand cliconfig.VarlinkValues varlinkDescription = ` podman varlink run varlink interface ` - varlinkFlags = []cli.Flag{ - cli.IntFlag{ - Name: "timeout, t", - Usage: "Time until the varlink session expires in milliseconds. Use 0 to disable the timeout.", - Value: 1000, + _varlinkCommand = &cobra.Command{ + Use: "varlink", + Short: "Run varlink interface", + Long: varlinkDescription, + RunE: func(cmd *cobra.Command, args []string) error { + varlinkCommand.InputArgs = args + varlinkCommand.GlobalFlags = MainGlobalOpts + return varlinkCmd(&varlinkCommand) }, - } - varlinkCommand = &cli.Command{ - Name: "varlink", - Usage: "Run varlink interface", - Description: varlinkDescription, - Flags: sortFlags(varlinkFlags), - Action: varlinkCmd, - ArgsUsage: "VARLINK_URI", - OnUsageError: usageErrorHandler, + Example: "VARLINK_URI", } ) -func varlinkCmd(c *cli.Context) error { - args := c.Args() +func init() { + varlinkCommand.Command = _varlinkCommand + flags := varlinkCommand.Flags() + flags.Int64VarP(&varlinkCommand.Timeout, "timeout", "t", 1000, "Time until the varlink session expires in milliseconds. Use 0 to disable the timeout") + + rootCmd.AddCommand(varlinkCommand.Command) +} + +func varlinkCmd(c *cliconfig.VarlinkValues) error { + args := c.InputArgs if len(args) < 1 { return errors.Errorf("you must provide a varlink URI") } - timeout := time.Duration(c.Int64("timeout")) * time.Millisecond + timeout := time.Duration(c.Timeout) * time.Millisecond // Create a single runtime for varlink - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - var varlinkInterfaces = []*iopodman.VarlinkInterface{varlinkapi.New(c, runtime)} + var varlinkInterfaces = []*iopodman.VarlinkInterface{varlinkapi.New(&c.PodmanCommand, runtime)} // Register varlink service. The metadata can be retrieved with: // $ varlink info [varlink address URI] service, err := varlink.NewService( diff --git a/cmd/podman/varlink_dummy.go b/cmd/podman/varlink_dummy.go index ec4bbb208..8d7a7e8ca 100644 --- a/cmd/podman/varlink_dummy.go +++ b/cmd/podman/varlink_dummy.go @@ -3,7 +3,7 @@ package main import ( - "github.com/urfave/cli" + "github.com/containers/libpod/cmd/podman/cliconfig" ) -var varlinkCommand *cli.Command +var varlinkCommand *cliconfig.PodmanCommand diff --git a/cmd/podman/version.go b/cmd/podman/version.go index ce773ee2e..0e7cd43d5 100644 --- a/cmd/podman/version.go +++ b/cmd/podman/version.go @@ -6,20 +6,41 @@ import ( "text/tabwriter" "time" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/libpod" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) +var ( + versionCommand cliconfig.VersionValues + _versionCommand = &cobra.Command{ + Use: "version", + Short: "Display the Podman Version Information", + RunE: func(cmd *cobra.Command, args []string) error { + versionCommand.InputArgs = args + versionCommand.GlobalFlags = MainGlobalOpts + return versionCmd(&versionCommand) + }, + } +) + +func init() { + versionCommand.Command = _versionCommand + flags := versionCommand.Flags() + flags.StringVarP(&versionCommand.Format, "format", "f", "", "Change the output format to JSON or a Go template") + rootCmd.AddCommand(versionCommand.Command) +} + // versionCmd gets and prints version info for version command -func versionCmd(c *cli.Context) error { +func versionCmd(c *cliconfig.VersionValues) error { output, err := libpod.GetVersion() if err != nil { errors.Wrapf(err, "unable to determine version") } - versionOutputFormat := c.String("format") + versionOutputFormat := c.Format if versionOutputFormat != "" { var out formats.Writer switch versionOutputFormat { @@ -46,19 +67,3 @@ func versionCmd(c *cli.Context) error { fmt.Fprintf(w, "OS/Arch:\t%s\n", output.OsArch) return nil } - -// Cli command to print out the full version of podman -var ( - versionCommand = cli.Command{ - Name: "version", - Usage: "Display the Podman Version Information", - Action: versionCmd, - Flags: versionFlags, - } - versionFlags = []cli.Flag{ - cli.StringFlag{ - Name: "format, f", - Usage: "Change the output format to JSON or a Go template", - }, - } -) diff --git a/cmd/podman/volume.go b/cmd/podman/volume.go index 913592e74..bae778e52 100644 --- a/cmd/podman/volume.go +++ b/cmd/podman/volume.go @@ -1,26 +1,23 @@ package main import ( - "github.com/urfave/cli" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" ) -var ( - volumeDescription = `Manage volumes. +var volumeDescription = `Manage volumes. Volumes are created in and can be shared between containers.` - volumeSubCommands = []cli.Command{ - volumeCreateCommand, - volumeLsCommand, - volumeRmCommand, - volumeInspectCommand, - volumePruneCommand, - } - volumeCommand = cli.Command{ - Name: "volume", - Usage: "Manage volumes", - Description: volumeDescription, - UseShortOptionHandling: true, - Subcommands: volumeSubCommands, - } -) +var volumeCommand = cliconfig.PodmanCommand{ + Command: &cobra.Command{ + Use: "volume", + Short: "Manage volumes", + Long: volumeDescription, + }, +} + +func init() { + volumeCommand.AddCommand(getVolumeSubCommands()...) + rootCmd.AddCommand(volumeCommand.Command) +} diff --git a/cmd/podman/volume_create.go b/cmd/podman/volume_create.go index 0b5f8d1e3..e0ff4c341 100644 --- a/cmd/podman/volume_create.go +++ b/cmd/podman/volume_create.go @@ -3,75 +3,70 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -var volumeCreateDescription = ` +var ( + volumeCreateCommand cliconfig.VolumeCreateValues + volumeCreateDescription = ` podman volume create Creates a new volume. If using the default driver, "local", the volume will be created at.` -var volumeCreateFlags = []cli.Flag{ - cli.StringFlag{ - Name: "driver", - Usage: "Specify volume driver name (default local)", - }, - cli.StringSliceFlag{ - Name: "label, l", - Usage: "Set metadata for a volume (default [])", - }, - cli.StringSliceFlag{ - Name: "opt, o", - Usage: "Set driver specific options (default [])", - }, -} + _volumeCreateCommand = &cobra.Command{ + Use: "create", + Short: "Create a new volume", + Long: volumeCreateDescription, + RunE: func(cmd *cobra.Command, args []string) error { + volumeCreateCommand.InputArgs = args + volumeCreateCommand.GlobalFlags = MainGlobalOpts + return volumeCreateCmd(&volumeCreateCommand) + }, + Example: "[VOLUME-NAME]", + } +) + +func init() { + volumeCreateCommand.Command = _volumeCreateCommand + flags := volumeCreateCommand.Flags() + flags.StringVar(&volumeCreateCommand.Driver, "driver", "", "Specify volume driver name (default local)") + flags.StringSliceVarP(&volumeCreateCommand.Label, "label", "l", []string{}, "Set metadata for a volume (default [])") + flags.StringSliceVarP(&volumeCreateCommand.Opt, "opt", "o", []string{}, "Set driver specific options (default [])") -var volumeCreateCommand = cli.Command{ - Name: "create", - Usage: "Create a new volume", - Description: volumeCreateDescription, - Flags: volumeCreateFlags, - Action: volumeCreateCmd, - SkipArgReorder: true, - ArgsUsage: "[VOLUME-NAME]", - UseShortOptionHandling: true, } -func volumeCreateCmd(c *cli.Context) error { +func volumeCreateCmd(c *cliconfig.VolumeCreateValues) error { var ( options []libpod.VolumeCreateOption err error volName string ) - if err = validateFlags(c, volumeCreateFlags); err != nil { - return err - } - - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - if len(c.Args()) > 1 { + if len(c.InputArgs) > 1 { return errors.Errorf("too many arguments, create takes at most 1 argument") } - if len(c.Args()) > 0 { - volName = c.Args()[0] + if len(c.InputArgs) > 0 { + volName = c.InputArgs[0] options = append(options, libpod.WithVolumeName(volName)) } - if c.IsSet("driver") { + if c.Flag("driver").Changed { options = append(options, libpod.WithVolumeDriver(c.String("driver"))) } - labels, err := getAllLabels([]string{}, c.StringSlice("label")) + labels, err := getAllLabels([]string{}, c.Label) if err != nil { return errors.Wrapf(err, "unable to process labels") } @@ -79,7 +74,7 @@ func volumeCreateCmd(c *cli.Context) error { options = append(options, libpod.WithVolumeLabels(labels)) } - opts, err := getAllLabels([]string{}, c.StringSlice("opt")) + opts, err := getAllLabels([]string{}, c.Opt) if err != nil { return errors.Wrapf(err, "unable to process options") } diff --git a/cmd/podman/volume_inspect.go b/cmd/podman/volume_inspect.go index 152f1d098..7cb5703da 100644 --- a/cmd/podman/volume_inspect.go +++ b/cmd/podman/volume_inspect.go @@ -1,60 +1,56 @@ package main import ( + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -var volumeInspectDescription = ` +var ( + volumeInspectCommand cliconfig.VolumeInspectValues + volumeInspectDescription = ` podman volume inspect Display detailed information on one or more volumes. Can change the format from JSON to a Go template. ` + _volumeInspectCommand = &cobra.Command{ + Use: "inspect", + Short: "Display detailed information on one or more volumes", + Long: volumeInspectDescription, + RunE: func(cmd *cobra.Command, args []string) error { + volumeInspectCommand.InputArgs = args + volumeInspectCommand.GlobalFlags = MainGlobalOpts + return volumeInspectCmd(&volumeInspectCommand) + }, + Example: "[VOLUME-NAME ...]", + } +) -var volumeInspectFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Inspect all volumes", - }, - cli.StringFlag{ - Name: "format, f", - Usage: "Format volume output using Go template", - Value: "json", - }, -} +func init() { + volumeInspectCommand.Command = _volumeInspectCommand + flags := volumeInspectCommand.Flags() + flags.BoolVarP(&volumeInspectCommand.All, "all", "a", false, "Inspect all volumes") + flags.StringVarP(&volumeInspectCommand.Format, "format", "f", "json", "Format volume output using Go template") -var volumeInspectCommand = cli.Command{ - Name: "inspect", - Usage: "Display detailed information on one or more volumes", - Description: volumeInspectDescription, - Flags: volumeInspectFlags, - Action: volumeInspectCmd, - SkipArgReorder: true, - ArgsUsage: "[VOLUME-NAME ...]", - UseShortOptionHandling: true, } -func volumeInspectCmd(c *cli.Context) error { +func volumeInspectCmd(c *cliconfig.VolumeInspectValues) error { var err error - if err = validateFlags(c, volumeInspectFlags); err != nil { - return err - } - - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) opts := volumeLsOptions{ - Format: c.String("format"), + Format: c.Format, } - vols, lastError := getVolumesFromContext(c, runtime) + vols, lastError := getVolumesFromContext(&c.PodmanCommand, runtime) if lastError != nil { logrus.Errorf("%q", lastError) } diff --git a/cmd/podman/volume_ls.go b/cmd/podman/volume_ls.go index 0f94549ee..78fdfed64 100644 --- a/cmd/podman/volume_ls.go +++ b/cmd/podman/volume_ls.go @@ -4,11 +4,12 @@ import ( "reflect" "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) // volumeOptions is the "ls" command options @@ -37,64 +38,57 @@ type volumeLsJSONParams struct { Scope string `json:"scope"` } -var volumeLsDescription = ` +var ( + volumeLsCommand cliconfig.VolumeLsValues + + volumeLsDescription = ` podman volume ls List all available volumes. The output of the volumes can be filtered and the output format can be changed to JSON or a user specified Go template. ` + _volumeLsCommand = &cobra.Command{ + Use: "ls", + Aliases: []string{"list"}, + Short: "List volumes", + Long: volumeLsDescription, + RunE: func(cmd *cobra.Command, args []string) error { + volumeLsCommand.InputArgs = args + volumeLsCommand.GlobalFlags = MainGlobalOpts + return volumeLsCmd(&volumeLsCommand) + }, + } +) -var volumeLsFlags = []cli.Flag{ - cli.StringFlag{ - Name: "filter, f", - Usage: "Filter volume output", - }, - cli.StringFlag{ - Name: "format", - Usage: "Format volume output using Go template", - Value: "table {{.Driver}}\t{{.Name}}", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Print volume output in quiet mode", - }, -} +func init() { + volumeLsCommand.Command = _volumeLsCommand + flags := volumeLsCommand.Flags() -var volumeLsCommand = cli.Command{ - Name: "ls", - Aliases: []string{"list"}, - Usage: "List volumes", - Description: volumeLsDescription, - Flags: volumeLsFlags, - Action: volumeLsCmd, - SkipArgReorder: true, - UseShortOptionHandling: true, + flags.StringVarP(&volumeLsCommand.Filter, "filter", "f", "", "Filter volume output") + flags.StringVar(&volumeLsCommand.Format, "format", "table {{.Driver}}\t{{.Name}}", "Format volume output using Go template") + flags.BoolVarP(&volumeLsCommand.Quiet, "quiet", "q", false, "Print volume output in quiet mode") } -func volumeLsCmd(c *cli.Context) error { - if err := validateFlags(c, volumeLsFlags); err != nil { - return err - } - - runtime, err := libpodruntime.GetRuntime(c) +func volumeLsCmd(c *cliconfig.VolumeLsValues) error { + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - if len(c.Args()) > 0 { + if len(c.InputArgs) > 0 { return errors.Errorf("too many arguments, ls takes no arguments") } opts := volumeLsOptions{ - Quiet: c.Bool("quiet"), + Quiet: c.Quiet, } opts.Format = genVolLsFormat(c) // Get the filter functions based on any filters set var filterFuncs []libpod.VolumeFilter - if c.String("filter") != "" { - filters := strings.Split(c.String("filter"), ",") + if c.Filter != "" { + filters := strings.Split(c.Filter, ",") for _, f := range filters { filterSplit := strings.Split(f, "=") if len(filterSplit) < 2 { @@ -129,14 +123,14 @@ func volumeLsCmd(c *cli.Context) error { } // generate the template based on conditions given -func genVolLsFormat(c *cli.Context) string { +func genVolLsFormat(c *cliconfig.VolumeLsValues) string { var format string - if c.String("format") != "" { + if c.Format != "" { // "\t" from the command line is not being recognized as a tab // replacing the string "\t" to a tab character if the user passes in "\t" - format = strings.Replace(c.String("format"), `\t`, "\t", -1) + format = strings.Replace(c.Format, `\t`, "\t", -1) } - if c.Bool("quiet") { + if c.Quiet { format = "{{.Name}}" } return format diff --git a/cmd/podman/volume_prune.go b/cmd/podman/volume_prune.go index 41d95f9c7..29dc6ead4 100644 --- a/cmd/podman/volume_prune.go +++ b/cmd/podman/volume_prune.go @@ -7,35 +7,39 @@ import ( "os" "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -var volumePruneDescription = ` +var ( + volumePruneCommand cliconfig.VolumePruneValues + volumePruneDescription = ` podman volume prune Remove all unused volumes. Will prompt for confirmation if not using force. ` + _volumePruneCommand = &cobra.Command{ + Use: "prune", + Short: "Remove all unused volumes", + Long: volumePruneDescription, + RunE: func(cmd *cobra.Command, args []string) error { + volumePruneCommand.InputArgs = args + volumePruneCommand.GlobalFlags = MainGlobalOpts + return volumePruneCmd(&volumePruneCommand) + }, + } +) -var volumePruneFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "force, f", - Usage: "Do not prompt for confirmation", - }, -} +func init() { + volumePruneCommand.Command = _volumePruneCommand + flags := volumePruneCommand.Flags() -var volumePruneCommand = cli.Command{ - Name: "prune", - Usage: "Remove all unused volumes", - Description: volumePruneDescription, - Flags: volumePruneFlags, - Action: volumePruneCmd, - SkipArgReorder: true, - UseShortOptionHandling: true, + flags.BoolVarP(&volumePruneCommand.Force, "force", "f", false, "Do not prompt for confirmation") } func volumePrune(runtime *adapter.LocalRuntime, ctx context.Context) error { @@ -60,20 +64,15 @@ func volumePrune(runtime *adapter.LocalRuntime, ctx context.Context) error { return lastError } -func volumePruneCmd(c *cli.Context) error { - - if err := validateFlags(c, volumePruneFlags); err != nil { - return err - } - - runtime, err := adapter.GetRuntime(c) +func volumePruneCmd(c *cliconfig.VolumePruneValues) error { + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) // Prompt for confirmation if --force is not set - if !c.Bool("force") { + if !c.Force { reader := bufio.NewReader(os.Stdin) fmt.Println("WARNING! This will remove all volumes not used by at least one container.") fmt.Print("Are you sure you want to continue? [y/N] ") diff --git a/cmd/podman/volume_rm.go b/cmd/podman/volume_rm.go index 3fb623624..b02a06ed9 100644 --- a/cmd/podman/volume_rm.go +++ b/cmd/podman/volume_rm.go @@ -3,51 +3,47 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -var volumeRmDescription = ` +var ( + volumeRmCommand cliconfig.VolumeRmValues + volumeRmDescription = ` podman volume rm Remove one or more existing volumes. Will only remove volumes that are not being used by any containers. To remove the volumes anyways, use the --force flag. ` + _volumeRmCommand = &cobra.Command{ + Use: "rm", + Aliases: []string{"remove"}, + Short: "Remove one or more volumes", + Long: volumeRmDescription, + RunE: func(cmd *cobra.Command, args []string) error { + volumeRmCommand.InputArgs = args + volumeRmCommand.GlobalFlags = MainGlobalOpts + return volumeRmCmd(&volumeRmCommand) + }, + Example: "[VOLUME-NAME ...]", + } +) -var volumeRmFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "all, a", - Usage: "Remove all volumes", - }, - cli.BoolFlag{ - Name: "force, f", - Usage: "Remove a volume by force, even if it is being used by a container", - }, -} - -var volumeRmCommand = cli.Command{ - Name: "rm", - Aliases: []string{"remove"}, - Usage: "Remove one or more volumes", - Description: volumeRmDescription, - Flags: volumeRmFlags, - Action: volumeRmCmd, - ArgsUsage: "[VOLUME-NAME ...]", - SkipArgReorder: true, - UseShortOptionHandling: true, +func init() { + volumeRmCommand.Command = _volumeRmCommand + flags := volumeRmCommand.Flags() + flags.BoolVarP(&volumeRmCommand.All, "all", "a", false, "Remove all volumes") + flags.BoolVarP(&volumeRmCommand.Force, "force", "f", false, "Remove a volume by force, even if it is being used by a container") } -func volumeRmCmd(c *cli.Context) error { +func volumeRmCmd(c *cliconfig.VolumeRmValues) error { var err error - if err = validateFlags(c, volumeRmFlags); err != nil { - return err - } - - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -55,9 +51,9 @@ func volumeRmCmd(c *cli.Context) error { ctx := getContext() - vols, lastError := getVolumesFromContext(c, runtime) + vols, lastError := getVolumesFromContext(&c.PodmanCommand, runtime) for _, vol := range vols { - err = runtime.RemoveVolume(ctx, vol, c.Bool("force"), false) + err = runtime.RemoveVolume(ctx, vol, c.Force, false) if err != nil { if lastError != nil { logrus.Errorf("%q", lastError) diff --git a/cmd/podman/wait.go b/cmd/podman/wait.go index 35ad7a662..fa195b7ce 100644 --- a/cmd/podman/wait.go +++ b/cmd/podman/wait.go @@ -5,43 +5,49 @@ import ( "os" "time" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) var ( + waitCommand cliconfig.WaitValues + waitDescription = ` podman wait Block until one or more containers stop and then print their exit codes ` - waitFlags = []cli.Flag{ - cli.UintFlag{ - Name: "interval, i", - Usage: "Milliseconds to wait before polling for completion", - Value: 250, + _waitCommand = &cobra.Command{ + Use: "wait", + Short: "Block on one or more containers", + Long: waitDescription, + RunE: func(cmd *cobra.Command, args []string) error { + waitCommand.InputArgs = args + waitCommand.GlobalFlags = MainGlobalOpts + return waitCmd(&waitCommand) }, - LatestFlag, - } - waitCommand = cli.Command{ - Name: "wait", - Usage: "Block on one or more containers", - Description: waitDescription, - Flags: sortFlags(waitFlags), - Action: waitCmd, - ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", - OnUsageError: usageErrorHandler, + Example: "CONTAINER-NAME [CONTAINER-NAME ...]", } ) -func waitCmd(c *cli.Context) error { - args := c.Args() - if len(args) < 1 && !c.Bool("latest") { +func init() { + waitCommand.Command = _waitCommand + flags := waitCommand.Flags() + flags.UintVarP(&waitCommand.Interval, "interval", "i", 250, "Milliseconds to wait before polling for completion") + flags.BoolVarP(&waitCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + + rootCmd.AddCommand(waitCommand.Command) +} + +func waitCmd(c *cliconfig.WaitValues) error { + args := c.InputArgs + if len(args) < 1 && !c.Latest { return errors.Errorf("you must provide at least one container name or id") } - runtime, err := libpodruntime.GetRuntime(c) + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -52,7 +58,7 @@ func waitCmd(c *cli.Context) error { } var lastError error - if c.Bool("latest") { + if c.Latest { latestCtr, err := runtime.GetLatestContainer() if err != nil { return errors.Wrapf(err, "unable to wait on latest container") @@ -65,10 +71,10 @@ func waitCmd(c *cli.Context) error { if err != nil { return errors.Wrapf(err, "unable to find container %s", container) } - if c.Uint("interval") == 0 { + if c.Interval == 0 { return errors.Errorf("interval must be greater then 0") } - returnCode, err := ctr.WaitWithInterval(time.Duration(c.Uint("interval")) * time.Millisecond) + returnCode, err := ctr.WaitWithInterval(time.Duration(c.Interval) * time.Millisecond) if err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) |