diff options
Diffstat (limited to 'cmd')
33 files changed, 241 insertions, 108 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 2b6f9348e..bb5eb9f38 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -363,7 +363,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { ) createFlags.StringVar( &cf.Pull, - "pull", "missing", + "pull", containerConfig.Engine.PullPolicy, `Pull image before creating ("always"|"missing"|"never")`, ) createFlags.BoolVarP( @@ -389,7 +389,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { createFlags.StringVar( &cf.Restart, "restart", "", - `Restart policy to apply when a container exits ("always"|"no"|"on-failure")`, + `Restart policy to apply when a container exits ("always"|"no"|"on-failure"|"unless-stopped")`, ) createFlags.BoolVar( &cf.Rm, @@ -416,6 +416,11 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "Size of /dev/shm "+sizeWithUnitFormat, ) createFlags.StringVar( + &cf.SignaturePolicy, + "signature-policy", "", + "`Pathname` of signature policy file (not usually used)", + ) + createFlags.StringVar( &cf.StopSignal, "stop-signal", "", "Signal to stop a container. Default is SIGTERM", @@ -443,7 +448,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { createFlags.StringSliceVar( &cf.Sysctl, - "sysctl", containerConfig.Sysctls(), + "sysctl", []string{}, "Sysctl options", ) createFlags.StringVar( @@ -504,7 +509,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "volume", "v", containerConfig.Volumes(), "Bind mount a volume into the container", ) - createFlags.StringSliceVar( + createFlags.StringArrayVar( &cf.VolumesFrom, "volumes-from", []string{}, "Mount volumes from the specified container(s)", diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 1b0e64590..83a25f4ab 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -84,6 +84,7 @@ type ContainerCLIOpts struct { SecurityOpt []string SdNotifyMode string ShmSize string + SignaturePolicy string StopSignal string StopTimeout uint StoreageOpt []string diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go index ca0b10765..2a82451e4 100644 --- a/cmd/podman/common/volumes.go +++ b/cmd/podman/common/volumes.go @@ -28,6 +28,7 @@ var ( errDuplicateDest = errors.Errorf("duplicate mount destination") optionArgError = errors.Errorf("must provide an argument for option") noDestError = errors.Errorf("must set volume destination") + errInvalidSyntax = errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]") ) // Parse all volume-related options in the create config into a set of mounts @@ -147,6 +148,27 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo return finalMounts, finalVolumes, finalOverlayVolume, nil } +// findMountType parses the input and extracts the type of the mount type and +// the remaining non-type tokens. +func findMountType(input string) (mountType string, tokens []string, err error) { + // Split by comma, iterate over the slice and look for + // "type=$mountType". Everything else is appended to tokens. + found := false + for _, s := range strings.Split(input, ",") { + kv := strings.Split(s, "=") + if found || !(len(kv) == 2 && kv[0] == "type") { + tokens = append(tokens, s) + continue + } + mountType = kv[1] + found = true + } + if !found { + err = errInvalidSyntax + } + return +} + // getMounts takes user-provided input from the --mount flag and creates OCI // spec mounts and Libpod named volumes. // podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... @@ -156,25 +178,13 @@ func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.N finalMounts := make(map[string]spec.Mount) finalNamedVolumes := make(map[string]*specgen.NamedVolume) - errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]") - - // 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 supported options. for _, mount := range mountFlag { - arr := strings.SplitN(mount, ",", 2) - if len(arr) < 2 { - return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount) + // TODO: Docker defaults to "volume" if no mount type is specified. + mountType, tokens, err := findMountType(mount) + if err != nil { + return nil, nil, err } - kv := strings.Split(arr[0], "=") - // TODO: type is not explicitly required in Docker. - // If not specified, it defaults to "volume". - if len(kv) != 2 || kv[0] != "type" { - return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount) - } - - tokens := strings.Split(arr[1], ",") - switch kv[1] { + switch mountType { case TypeBind: mount, err := getBindMount(tokens) if err != nil { @@ -212,7 +222,7 @@ func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.N } finalNamedVolumes[volume.Dest] = volume default: - return nil, nil, errors.Errorf("invalid filesystem type %q", kv[1]) + return nil, nil, errors.Errorf("invalid filesystem type %q", mountType) } } diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index f9d33a223..d75352848 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -61,6 +61,7 @@ func createFlags(flags *pflag.FlagSet) { flags.AddFlagSet(common.GetNetFlags()) flags.SetNormalizeFunc(utils.AliasFlags) + _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("env-host") @@ -110,7 +111,9 @@ func create(cmd *cobra.Command, args []string) error { } imageName := args[0] + rawImageName := "" if !cliVals.RootFS { + rawImageName = args[0] name, err := pullImage(args[0]) if err != nil { return err @@ -121,6 +124,7 @@ func create(cmd *cobra.Command, args []string) error { if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { return err } + s.RawImageName = rawImageName if _, err := createPodIfNecessary(s, cliVals.Net); err != nil { return err @@ -256,6 +260,8 @@ func pullImage(imageName string) (string, error) { OverrideArch: cliVals.OverrideArch, OverrideOS: cliVals.OverrideOS, OverrideVariant: cliVals.OverrideVariant, + SignaturePolicy: cliVals.SignaturePolicy, + PullPolicy: pullPolicy, }) if pullErr != nil { return "", pullErr diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go index 21c00505f..acc2ab1aa 100644 --- a/cmd/podman/containers/logs.go +++ b/cmd/podman/containers/logs.go @@ -37,7 +37,7 @@ var ( case registry.IsRemote() && len(args) > 1: return errors.New(cmd.Name() + " does not support multiple containers when run remotely") case logsOptions.Latest && len(args) > 0: - return errors.New("no containers can be specified when using 'latest'") + return errors.New("--latest and containers cannot be used together") case !logsOptions.Latest && len(args) < 1: return errors.New("specify at least one container name or ID to log") } diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index 1c358f4a2..f2df5e99e 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -78,6 +78,9 @@ func mount(_ *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) + if len(args) > 0 && mountOpts.Latest { + return errors.Errorf("--latest and containers cannot be used together") + } reports, err := registry.ContainerEngine().ContainerMount(registry.GetContext(), args, mountOpts) if err != nil { return err diff --git a/cmd/podman/containers/pause.go b/cmd/podman/containers/pause.go index c2218bc44..c5171303d 100644 --- a/cmd/podman/containers/pause.go +++ b/cmd/podman/containers/pause.go @@ -6,6 +6,7 @@ import ( "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" + "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/rootless" "github.com/pkg/errors" @@ -64,7 +65,10 @@ func pause(cmd *cobra.Command, args []string) error { errs utils.OutputErrors ) if rootless.IsRootless() && !registry.IsRemote() { - return errors.New("pause is not supported for rootless containers") + cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() + if !cgroupv2 { + return errors.New("pause is not supported for cgroupv1 rootless containers") + } } if len(args) < 1 && !pauseOpts.All { diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go index 90dea2b45..cfe6765ac 100644 --- a/cmd/podman/containers/prune.go +++ b/cmd/podman/containers/prune.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" + "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -25,6 +26,7 @@ var ( Long: pruneDescription, RunE: prune, Example: `podman container prune`, + Args: validate.NoArgs, } force bool filter = []string{} @@ -45,9 +47,6 @@ func prune(cmd *cobra.Command, args []string) error { var ( pruneOptions = entities.ContainerPruneOptions{} ) - if len(args) > 0 { - return errors.Errorf("`%s` takes no arguments", cmd.CommandPath()) - } if !force { reader := bufio.NewReader(os.Stdin) fmt.Println("WARNING! This will remove all non running containers.") diff --git a/cmd/podman/containers/restart.go b/cmd/podman/containers/restart.go index d02097f0e..5f6f9c35c 100644 --- a/cmd/podman/containers/restart.go +++ b/cmd/podman/containers/restart.go @@ -80,6 +80,9 @@ func restart(cmd *cobra.Command, args []string) error { if len(args) < 1 && !restartOptions.Latest && !restartOptions.All { return errors.Wrapf(define.ErrInvalidArg, "you must provide at least one container name or ID") } + if len(args) > 0 && restartOptions.Latest { + return errors.Wrapf(define.ErrInvalidArg, "--latest and containers cannot be used together") + } if cmd.Flag("time").Changed { restartOptions.Timeout = &restartTimeout diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go index 413bcbc87..c996144e3 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -80,7 +80,7 @@ func restore(_ *cobra.Command, args []string) error { } } if (restoreOptions.All || restoreOptions.Latest) && argLen > 0 { - return errors.Errorf("no arguments are needed with --all or --latest") + return errors.Errorf("--all or --latest and containers cannot be used together") } if argLen < 1 && !restoreOptions.All && !restoreOptions.Latest && restoreOptions.Import == "" { return errors.Errorf("you must provide at least one name or id") diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index 8d0c7920f..f8f12234d 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -105,7 +105,7 @@ func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit } responses, err := registry.ContainerEngine().ContainerRm(context.Background(), namesOrIDs, rmOptions) if err != nil { - if setExit && len(namesOrIDs) < 2 { + if setExit { setExitCode(err) } return err @@ -132,7 +132,7 @@ func setExitCode(err error) { switch { case cause == define.ErrNoSuchCtr: registry.SetExitCode(1) - case strings.Contains(cause.Error(), define.ErrNoSuchImage.Error()): + case strings.Contains(cause.Error(), define.ErrNoSuchCtr.Error()): registry.SetExitCode(1) case cause == define.ErrCtrStateInvalid: registry.SetExitCode(2) diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 34eea14e1..8052b033e 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -64,6 +64,7 @@ func runFlags(flags *pflag.FlagSet) { flags.BoolVar(&runRmi, "rmi", false, "Remove container image unless used by other containers") flags.UintVar(&runOpts.PreserveFDs, "preserve-fds", 0, "Pass a number of additional file descriptors into the container") + _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("env-host") @@ -130,7 +131,9 @@ func run(cmd *cobra.Command, args []string) error { } imageName := args[0] + rawImageName := "" if !cliVals.RootFS { + rawImageName = args[0] name, err := pullImage(args[0]) if err != nil { return err @@ -177,6 +180,7 @@ func run(cmd *cobra.Command, args []string) error { if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { return err } + s.RawImageName = rawImageName runOpts.Spec = s if _, err := createPodIfNecessary(s, cliVals.Net); err != nil { diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go index ccbe80317..1e58498b6 100644 --- a/cmd/podman/containers/start.go +++ b/cmd/podman/containers/start.go @@ -74,6 +74,9 @@ func start(cmd *cobra.Command, args []string) error { if len(args) == 0 && !startOptions.Latest { return errors.New("start requires at least one argument") } + if len(args) > 0 && startOptions.Latest { + return errors.Errorf("--latest and containers cannot be used together") + } if len(args) > 1 && startOptions.Attach { return errors.Errorf("you cannot start and attach multiple containers at once") } diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index ddb5f32ef..bbd389bbf 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "strings" - "sync" "text/tabwriter" "text/template" @@ -48,8 +47,18 @@ var ( } ) +// statsOptionsCLI is used for storing CLI arguments. Some fields are later +// used in the backend. +type statsOptionsCLI struct { + All bool + Format string + Latest bool + NoReset bool + NoStream bool +} + var ( - statsOptions entities.ContainerStatsOptions + statsOptions statsOptionsCLI defaultStatsRow = "{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\n" defaultStatsHeader = "ID\tNAME\tCPU %\tMEM USAGE / LIMIT\tMEM %\tNET IO\tBLOCK IO\tPIDS\n" ) @@ -107,32 +116,37 @@ func stats(cmd *cobra.Command, args []string) error { return errors.New("stats is not supported in rootless mode without cgroups v2") } } - statsOptions.StatChan = make(chan []*define.ContainerStats, 1) - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - for reports := range statsOptions.StatChan { - if err := outputStats(reports); err != nil { - logrus.Error(err) - } - } - wg.Done() - }() - err := registry.ContainerEngine().ContainerStats(registry.Context(), args, statsOptions) - wg.Wait() - return err + // Convert to the entities options. We should not leak CLI-only + // options into the backend and separate concerns. + opts := entities.ContainerStatsOptions{ + Latest: statsOptions.Latest, + Stream: !statsOptions.NoStream, + } + statsChan, err := registry.ContainerEngine().ContainerStats(registry.Context(), args, opts) + if err != nil { + return err + } + for report := range statsChan { + if report.Error != nil { + return report.Error + } + if err := outputStats(report.Stats); err != nil { + logrus.Error(err) + } + } + return nil } -func outputStats(reports []*define.ContainerStats) error { +func outputStats(reports []define.ContainerStats) error { if len(statsOptions.Format) < 1 && !statsOptions.NoReset { tm.Clear() tm.MoveCursor(1, 1) tm.Flush() } - stats := make([]*containerStats, 0, len(reports)) + stats := make([]containerStats, 0, len(reports)) for _, r := range reports { - stats = append(stats, &containerStats{r}) + stats = append(stats, containerStats{r}) } if statsOptions.Format == "json" { return outputJSON(stats) @@ -163,7 +177,7 @@ func outputStats(reports []*define.ContainerStats) error { } type containerStats struct { - *define.ContainerStats + define.ContainerStats } func (s *containerStats) ID() string { @@ -213,7 +227,7 @@ func combineHumanValues(a, b uint64) string { return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b))) } -func outputJSON(stats []*containerStats) error { +func outputJSON(stats []containerStats) error { type jstat struct { Id string `json:"id"` //nolint Name string `json:"name"` diff --git a/cmd/podman/containers/unpause.go b/cmd/podman/containers/unpause.go index 50113669c..43eaad72b 100644 --- a/cmd/podman/containers/unpause.go +++ b/cmd/podman/containers/unpause.go @@ -6,6 +6,7 @@ import ( "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" + "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/rootless" "github.com/pkg/errors" @@ -62,7 +63,10 @@ func unpause(cmd *cobra.Command, args []string) error { errs utils.OutputErrors ) if rootless.IsRootless() && !registry.IsRemote() { - return errors.New("unpause is not supported for rootless containers") + cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() + if !cgroupv2 { + return errors.New("unpause is not supported for cgroupv1 rootless containers") + } } if len(args) < 1 && !unPauseOptions.All { return errors.Errorf("you must provide at least one container name or id") diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go index 74ee6073a..4bc3d20e2 100644 --- a/cmd/podman/containers/wait.go +++ b/cmd/podman/containers/wait.go @@ -23,7 +23,6 @@ var ( Short: "Block on one or more containers", Long: waitDescription, RunE: wait, - Args: validate.IDOrLatestArgs, Example: `podman wait --interval 5000 ctrID podman wait ctrID1 ctrID2`, } @@ -33,7 +32,6 @@ var ( Short: waitCommand.Short, Long: waitCommand.Long, RunE: waitCommand.RunE, - Args: validate.IDOrLatestArgs, Example: `podman container wait --interval 5000 ctrID podman container wait ctrID1 ctrID2`, } @@ -76,6 +74,13 @@ func wait(cmd *cobra.Command, args []string) error { return errors.New("interval must be greater then 0") } + if !waitOptions.Latest && len(args) == 0 { + return errors.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath()) + } + if waitOptions.Latest && len(args) > 0 { + return errors.New("--latest and containers cannot be used together") + } + waitOptions.Condition, err = define.StringToContainerStatus(waitCondition) if err != nil { return err diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index ff5c6ec09..d24bb18b6 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -206,14 +206,9 @@ func build(cmd *cobra.Command, args []string) error { } } - ie, err := registry.NewImageEngine(cmd, args) - if err != nil { - return err - } - var logfile *os.File if cmd.Flag("logfile").Changed { - logfile, err = os.OpenFile(buildOpts.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + logfile, err := os.OpenFile(buildOpts.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { return errors.Errorf("error opening logfile %q: %v", buildOpts.Logfile, err) } @@ -225,7 +220,7 @@ func build(cmd *cobra.Command, args []string) error { return err } - _, err = ie.Build(registry.GetContext(), containerFiles, *apiBuildOpts) + _, err = registry.ImageEngine().Build(registry.GetContext(), containerFiles, *apiBuildOpts) return err } @@ -441,6 +436,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil Quiet: flags.Quiet, RemoveIntermediateCtrs: flags.Rm, ReportWriter: reporter, + Runtime: containerConfig.RuntimePath, RuntimeArgs: runtimeFlags, SignBy: flags.SignBy, SignaturePolicyPath: flags.SignaturePolicy, diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go index e605ddfc6..1c234e743 100644 --- a/cmd/podman/images/import.go +++ b/cmd/podman/images/import.go @@ -63,6 +63,8 @@ func importFlags(flags *pflag.FlagSet) { flags.StringArrayVarP(&importOpts.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR") flags.StringVarP(&importOpts.Message, "message", "m", "", "Set commit message for imported image") flags.BoolVarP(&importOpts.Quiet, "quiet", "q", false, "Suppress output") + flags.StringVar(&importOpts.SignaturePolicy, "signature-policy", "", "Path to a signature-policy file") + _ = flags.MarkHidden("signature-policy") } func importCon(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 043871a8c..ffb341fc4 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -184,13 +184,26 @@ func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) { for _, e := range imageS { var h imageReporter if len(e.RepoTags) > 0 { + tagged := []imageReporter{} + untagged := []imageReporter{} for _, tag := range e.RepoTags { h.ImageSummary = *e h.Repository, h.Tag, err = tokenRepoTag(tag) if err != nil { return nil, errors.Wrapf(err, "error parsing repository tag %q:", tag) } - imgs = append(imgs, h) + if h.Tag == "<none>" { + untagged = append(untagged, h) + } else { + tagged = append(tagged, h) + } + } + // Note: we only want to display "<none>" if we + // couldn't find any tagged name in RepoTags. + if len(tagged) > 0 { + imgs = append(imgs, tagged...) + } else { + imgs = append(imgs, untagged[0]) } } else { h.ImageSummary = *e diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go index d86f9800c..448543b4d 100644 --- a/cmd/podman/images/pull.go +++ b/cmd/podman/images/pull.go @@ -44,10 +44,10 @@ var ( // child of the images command. imagesPullCmd = &cobra.Command{ Use: pullCmd.Use, + Args: pullCmd.Args, Short: pullCmd.Short, Long: pullCmd.Long, RunE: pullCmd.RunE, - Args: cobra.ExactArgs(1), Example: `podman image pull imageName podman image pull fedora:latest`, } @@ -77,8 +77,6 @@ func init() { // pullFlags set the flags for the pull command. func pullFlags(flags *pflag.FlagSet) { flags.BoolVar(&pullOptions.AllTags, "all-tags", false, "All tagged images in the repository will be pulled") - flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") - flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&pullOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images") flags.StringVar(&pullOptions.OverrideOS, "override-os", "", "Use `OS` instead of the running OS for choosing images") @@ -86,12 +84,11 @@ func pullFlags(flags *pflag.FlagSet) { flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP") flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.StringVar(&pullOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") - flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") - if registry.IsRemote() { - _ = flags.MarkHidden("authfile") - _ = flags.MarkHidden("cert-dir") - _ = flags.MarkHidden("tls-verify") + if !registry.IsRemote() { + flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go index c57f61221..b164a2534 100644 --- a/cmd/podman/images/save.go +++ b/cmd/podman/images/save.go @@ -94,6 +94,7 @@ func save(cmd *cobra.Command, args []string) (finalErr error) { return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'") } if len(saveOpts.Output) == 0 { + saveOpts.Quiet = true fi := os.Stdout if terminal.IsTerminal(int(fi.Fd())) { return errors.Errorf("refusing to save to terminal. Use -o flag or redirect") @@ -122,12 +123,6 @@ func save(cmd *cobra.Command, args []string) (finalErr error) { tags = args[1:] } - // Decide whether c/image's progress bars should use stderr or stdout. - // If the output is set of stdout, any log message there would corrupt - // the tarfile. - if saveOpts.Output == os.Stdout.Name() { - saveOpts.Quiet = true - } err := registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts) if err == nil { succeeded = true diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index 13c6544bb..f29527412 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -93,7 +93,7 @@ func (i *inspector) inspect(namesOrIDs []string) error { tmpType := i.options.Type if i.options.Latest { if len(namesOrIDs) > 0 { - return errors.New("latest and containers are not allowed") + return errors.New("--latest and containers cannot be used together") } tmpType = ContainerType // -l works with --type=all } diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go index ac49993b7..86aad43cb 100644 --- a/cmd/podman/networks/rm.go +++ b/cmd/podman/networks/rm.go @@ -2,10 +2,13 @@ package network import ( "fmt" + "strings" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -47,14 +50,30 @@ func networkRm(cmd *cobra.Command, args []string) error { responses, err := registry.ContainerEngine().NetworkRm(registry.Context(), args, networkRmOptions) if err != nil { + setExitCode(err) return err } for _, r := range responses { if r.Err == nil { fmt.Println(r.Name) } else { + setExitCode(r.Err) errs = append(errs, r.Err) } } return errs.PrintErrors() } + +func setExitCode(err error) { + cause := errors.Cause(err) + switch { + case cause == define.ErrNoSuchNetwork: + registry.SetExitCode(1) + case strings.Contains(cause.Error(), define.ErrNoSuchNetwork.Error()): + registry.SetExitCode(1) + case cause == define.ErrNetworkInUse: + registry.SetExitCode(2) + case strings.Contains(cause.Error(), define.ErrNetworkInUse.Error()): + registry.SetExitCode(2) + } +} diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 603ddcaca..ac6d83edd 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -55,8 +55,8 @@ func init() { flags.StringVar(&createOptions.CGroupParent, "cgroup-parent", "", "Set parent cgroup for the pod") flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") flags.StringVar(&createOptions.InfraConmonPidFile, "infra-conmon-pidfile", "", "Path to the file that will receive the POD of the infra container's conmon") - flags.StringVar(&createOptions.InfraImage, "infra-image", containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") - flags.StringVar(&createOptions.InfraCommand, "infra-command", containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started") + flags.String("infra-image", containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") + flags.String("infra-command", containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started") flags.StringSliceVar(&labelFile, "label-file", []string{}, "Read in a line delimited file of labels") flags.StringSliceVarP(&labels, "label", "l", []string{}, "Set metadata on pod (default [])") flags.StringVarP(&createOptions.Name, "name", "n", "", "Assign a name to the pod") @@ -92,7 +92,6 @@ func create(cmd *cobra.Command, args []string) error { if cmd.Flag("infra-command").Changed { return errors.New("cannot set infra-command without an infra container") } - createOptions.InfraCommand = "" if cmd.Flag("infra-image").Changed { return errors.New("cannot set infra-image without an infra container") } @@ -104,6 +103,20 @@ func create(cmd *cobra.Command, args []string) error { createOptions.Share = nil } else { createOptions.Share = strings.Split(share, ",") + if cmd.Flag("infra-command").Changed { + // Only send content to server side if user changed defaults + createOptions.InfraCommand, err = cmd.Flags().GetString("infra-command") + if err != nil { + return err + } + } + if cmd.Flag("infra-image").Changed { + // Only send content to server side if user changed defaults + createOptions.InfraImage, err = cmd.Flags().GetString("infra-image") + if err != nil { + return err + } + } } if cmd.Flag("pod-id-file").Changed { @@ -128,14 +141,20 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } + parts := strings.SplitN(netInput, ":", 2) + n := specgen.Namespace{} - switch netInput { - case "bridge": + switch { + case netInput == "bridge": n.NSMode = specgen.Bridge - case "host": + case netInput == "host": n.NSMode = specgen.Host - case "slirp4netns": + case netInput == "slirp4netns", strings.HasPrefix(netInput, "slirp4netns:"): n.NSMode = specgen.Slirp + if len(parts) > 1 { + createOptions.Net.NetworkOptions = make(map[string][]string) + createOptions.Net.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") + } default: // Container and NS mode are presently unsupported n.NSMode = specgen.Bridge diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 8f5aa6062..bc20352b0 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -46,6 +46,9 @@ func inspect(cmd *cobra.Command, args []string) error { if len(args) < 1 && !inspectOptions.Latest { return errors.Errorf("you must provide the name or id of a running pod") } + if len(args) > 0 && inspectOptions.Latest { + return errors.Errorf("--latest and containers cannot be used together") + } if !inspectOptions.Latest { inspectOptions.NameOrID = args[0] diff --git a/cmd/podman/pods/prune.go b/cmd/podman/pods/prune.go index a7347ede5..f13d95ae9 100644 --- a/cmd/podman/pods/prune.go +++ b/cmd/podman/pods/prune.go @@ -9,6 +9,7 @@ import ( "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" + "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -23,6 +24,7 @@ var ( pruneCommand = &cobra.Command{ Use: "prune [flags]", + Args: validate.NoArgs, Short: "Remove all stopped pods and their containers", Long: pruneDescription, RunE: prune, @@ -41,9 +43,6 @@ func init() { } func prune(cmd *cobra.Command, args []string) error { - if len(args) > 0 { - return errors.Errorf("`%s` takes no arguments", cmd.CommandPath()) - } if !pruneOptions.Force { reader := bufio.NewReader(os.Stdin) fmt.Println("WARNING! This will remove all stopped/exited pods..") diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 97e528c7c..7b755cb22 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -73,7 +73,7 @@ func pods(cmd *cobra.Command, _ []string) error { if cmd.Flag("filter").Changed { psInput.Filters = make(map[string][]string) for _, f := range inputFilters { - split := strings.Split(f, "=") + split := strings.SplitN(f, "=", 2) if len(split) < 2 { return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) } diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index 0bb35e1ca..2975db3e8 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -3,12 +3,15 @@ package pods import ( "context" "fmt" + "strings" "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -73,6 +76,7 @@ func removePods(namesOrIDs []string, rmOptions entities.PodRmOptions, printIDs b responses, err := registry.ContainerEngine().PodRm(context.Background(), namesOrIDs, rmOptions) if err != nil { + setExitCode(err) return err } @@ -83,8 +87,19 @@ func removePods(namesOrIDs []string, rmOptions entities.PodRmOptions, printIDs b fmt.Println(r.Id) } } else { + setExitCode(r.Err) errs = append(errs, r.Err) } } return errs.PrintErrors() } + +func setExitCode(err error) { + cause := errors.Cause(err) + switch { + case cause == define.ErrNoSuchPod: + registry.SetExitCode(1) + case strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()): + registry.SetExitCode(1) + } +} diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 60725b111..6424ec12e 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -63,7 +63,7 @@ var ( PersistentPreRunE: persistentPreRunE, RunE: validate.SubCommandExists, PersistentPostRunE: persistentPostRunE, - Version: version.Version, + Version: version.Version.String(), } logLevels = []string{"debug", "info", "warn", "error", "fatal", "panic"} @@ -80,10 +80,6 @@ func init() { ) rootFlags(rootCmd, registry.PodmanConfig()) - - // "version" is a local flag to avoid collisions with sub-commands that use "-v" - var dummyVersion bool - rootCmd.Flags().BoolVarP(&dummyVersion, "version", "v", false, "Version of Podman") } func Execute() { @@ -273,7 +269,6 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { pFlags.StringVar(&opts.RegistriesConf, "registries-conf", "", "Path to a registries.conf to use for image processing") pFlags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") pFlags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") - pFlags.StringArrayVar(&opts.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime") // -s is deprecated due to conflict with -s on subcommands pFlags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") pFlags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") @@ -301,6 +296,7 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { // Only create these flags for ABI connections if !registry.IsRemote() { + pFlags.StringArrayVar(&opts.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime") pFlags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") } } diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index edc860d0e..9da7da54a 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -47,12 +47,9 @@ func version(cmd *cobra.Command, args []string) error { if err != nil { return err } - _, err = io.WriteString(os.Stdout, s) + _, err = io.WriteString(os.Stdout, s+"\n") return err case cmd.Flag("format").Changed: - if !strings.HasSuffix(versionFormat, "\n") { - versionFormat += "\n" - } out := formats.StdoutTemplate{Output: versions, Template: versionFormat} err := out.Out() if err != nil { @@ -86,7 +83,7 @@ func version(cmd *cobra.Command, args []string) error { func formatVersion(writer io.Writer, version *define.Version) { fmt.Fprintf(writer, "Version:\t%s\n", version.Version) - fmt.Fprintf(writer, "API Version:\t%d\n", version.APIVersion) + fmt.Fprintf(writer, "API Version:\t%s\n", version.APIVersion) fmt.Fprintf(writer, "Go Version:\t%s\n", version.GoVersion) if version.GitCommit != "" { fmt.Fprintf(writer, "Git Commit:\t%s\n", version.GitCommit) diff --git a/cmd/podman/validate/args.go b/cmd/podman/validate/args.go index aacb41e69..ab6460e93 100644 --- a/cmd/podman/validate/args.go +++ b/cmd/podman/validate/args.go @@ -37,6 +37,9 @@ func IDOrLatestArgs(cmd *cobra.Command, args []string) error { if len(args) == 0 && !given { return fmt.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath()) } + if len(args) > 0 && given { + return fmt.Errorf("--latest and containers cannot be used together") + } } return nil } @@ -83,7 +86,7 @@ func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool if argLen > 0 { if specifiedLatest { - return errors.Errorf("no arguments are needed with --latest") + return errors.Errorf("--latest and containers cannot be used together") } else if cidfile && (specifiedLatest || specifiedCIDFile) { return errors.Errorf("no arguments are needed with --latest or --cidfile") } @@ -138,7 +141,7 @@ func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bo if argLen > 0 { if specifiedLatest { - return errors.Errorf("no arguments are needed with --latest") + return errors.Errorf("--latest and pods cannot be used together") } else if withIDFile && (specifiedLatest || specifiedPodIDFile) { return errors.Errorf("no arguments are needed with --latest or --pod-id-file") } diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go index 95b47b726..78c258bec 100644 --- a/cmd/podman/volumes/prune.go +++ b/cmd/podman/volumes/prune.go @@ -29,10 +29,6 @@ var ( } ) -var ( - pruneOptions entities.VolumePruneOptions -) - func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, @@ -40,12 +36,16 @@ func init() { Parent: volumeCmd, }) flags := pruneCommand.Flags() - flags.BoolVarP(&pruneOptions.Force, "force", "f", false, "Do not prompt for confirmation") + flags.BoolP("force", "f", false, "Do not prompt for confirmation") } func prune(cmd *cobra.Command, args []string) error { // Prompt for confirmation if --force is not set - if !pruneOptions.Force { + force, err := cmd.Flags().GetBool("force") + if err != nil { + return err + } + if !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] ") @@ -57,7 +57,7 @@ func prune(cmd *cobra.Command, args []string) error { return nil } } - responses, err := registry.ContainerEngine().VolumePrune(context.Background(), pruneOptions) + responses, err := registry.ContainerEngine().VolumePrune(context.Background()) if err != nil { return err } diff --git a/cmd/podman/volumes/rm.go b/cmd/podman/volumes/rm.go index 5b23eb5e6..4c960d4d5 100644 --- a/cmd/podman/volumes/rm.go +++ b/cmd/podman/volumes/rm.go @@ -3,9 +3,11 @@ package volumes import ( "context" "fmt" + "strings" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -51,14 +53,30 @@ func rm(cmd *cobra.Command, args []string) error { } responses, err := registry.ContainerEngine().VolumeRm(context.Background(), args, rmOptions) if err != nil { + setExitCode(err) return err } for _, r := range responses { if r.Err == nil { fmt.Println(r.Id) } else { + setExitCode(r.Err) errs = append(errs, r.Err) } } return errs.PrintErrors() } + +func setExitCode(err error) { + cause := errors.Cause(err) + switch { + case cause == define.ErrNoSuchVolume: + registry.SetExitCode(1) + case strings.Contains(cause.Error(), define.ErrNoSuchVolume.Error()): + registry.SetExitCode(1) + case cause == define.ErrVolumeBeingUsed: + registry.SetExitCode(2) + case strings.Contains(cause.Error(), define.ErrVolumeBeingUsed.Error()): + registry.SetExitCode(2) + } +} |