diff options
Diffstat (limited to 'cmd/podman')
108 files changed, 888 insertions, 625 deletions
diff --git a/cmd/podman/auto-update.go b/cmd/podman/auto-update.go index 677266c83..76bff0c70 100644 --- a/cmd/podman/auto-update.go +++ b/cmd/podman/auto-update.go @@ -18,9 +18,9 @@ var ( Auto-update policies are specified with the "io.containers.autoupdate" label. Containers are expected to run in systemd units created with "podman-generate-systemd --new", or similar units that create new containers in order to run the updated images. - Note that this command is experimental. Please refer to the podman-auto-update(1) man page for details.` + Please refer to the podman-auto-update(1) man page for details.` autoUpdateCommand = &cobra.Command{ - Use: "auto-update [flags]", + Use: "auto-update [options]", Short: "Auto update containers according to their auto-update policy", Long: autoUpdateDescription, RunE: autoUpdate, diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 0ec422313..60f4e526c 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -115,16 +115,6 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "cpuset-mems", "", "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", ) - createFlags.BoolVarP( - &cf.Detach, - "detach", "d", false, - "Run container in background and print container ID", - ) - createFlags.StringVar( - &cf.DetachKeys, - "detach-keys", containerConfig.DetachKeys(), - "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`", - ) createFlags.StringSliceVar( &cf.Devices, "device", containerConfig.Devices(), diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 83a25f4ab..05bb9de13 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -1,6 +1,15 @@ package common -import "github.com/containers/podman/v2/pkg/domain/entities" +import ( + "fmt" + "net" + "strconv" + "strings" + + "github.com/containers/podman/v2/pkg/api/handlers" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/specgen" +) type ContainerCLIOpts struct { Annotation []string @@ -23,8 +32,6 @@ type ContainerCLIOpts struct { CPUS float64 CPUSetCPUs string CPUSetMems string - Detach bool - DetachKeys string Devices []string DeviceCGroupRule []string DeviceReadBPs []string @@ -111,3 +118,305 @@ type ContainerCLIOpts struct { CgroupConf []string } + +func stringMaptoArray(m map[string]string) []string { + a := make([]string, 0, len(m)) + for k, v := range m { + a = append(a, fmt.Sprintf("%s=%s", k, v)) + } + return a +} + +// ContainerCreateToContainerCLIOpts converts a compat input struct to cliopts so it can be converted to +// a specgen spec. +func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig) (*ContainerCLIOpts, []string, error) { + var ( + capAdd []string + cappDrop []string + entrypoint string + init bool + specPorts []specgen.PortMapping + ) + + if cc.HostConfig.Init != nil { + init = *cc.HostConfig.Init + } + + // Iterate devices and convert back to string + devices := make([]string, 0, len(cc.HostConfig.Devices)) + for _, dev := range cc.HostConfig.Devices { + devices = append(devices, fmt.Sprintf("%s:%s:%s", dev.PathOnHost, dev.PathInContainer, dev.CgroupPermissions)) + } + + // iterate blkreaddevicebps + readBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadBps)) + for _, dev := range cc.HostConfig.BlkioDeviceReadBps { + readBps = append(readBps, dev.String()) + } + + // iterate blkreaddeviceiops + readIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadIOps)) + for _, dev := range cc.HostConfig.BlkioDeviceReadIOps { + readIops = append(readIops, dev.String()) + } + + // iterate blkwritedevicebps + writeBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteBps)) + for _, dev := range cc.HostConfig.BlkioDeviceWriteBps { + writeBps = append(writeBps, dev.String()) + } + + // iterate blkwritedeviceiops + writeIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteIOps)) + for _, dev := range cc.HostConfig.BlkioDeviceWriteIOps { + writeIops = append(writeIops, dev.String()) + } + + // entrypoint + // can be a string or slice. if it is a slice, we need to + // marshall it to json; otherwise it should just be the string + // value + if len(cc.Config.Entrypoint) > 0 { + entrypoint = cc.Config.Entrypoint[0] + if len(cc.Config.Entrypoint) > 1 { + b, err := json.Marshal(cc.Config.Entrypoint) + if err != nil { + return nil, nil, err + } + entrypoint = string(b) + } + } + + // expose ports + expose := make([]string, 0, len(cc.Config.ExposedPorts)) + for p := range cc.Config.ExposedPorts { + expose = append(expose, fmt.Sprintf("%s/%s", p.Port(), p.Proto())) + } + + // mounts type=tmpfs/bind,source=,dest=,opt=val + // TODO options + mounts := make([]string, 0, len(cc.HostConfig.Mounts)) + for _, m := range cc.HostConfig.Mounts { + mount := fmt.Sprintf("type=%s", m.Type) + if len(m.Source) > 0 { + mount += fmt.Sprintf("source=%s", m.Source) + } + if len(m.Target) > 0 { + mount += fmt.Sprintf("dest=%s", m.Target) + } + mounts = append(mounts, mount) + } + + //volumes + volumes := make([]string, 0, len(cc.Config.Volumes)) + for v := range cc.Config.Volumes { + volumes = append(volumes, v) + } + + // dns + dns := make([]net.IP, 0, len(cc.HostConfig.DNS)) + for _, d := range cc.HostConfig.DNS { + dns = append(dns, net.ParseIP(d)) + } + + // publish + for port, pbs := range cc.HostConfig.PortBindings { + for _, pb := range pbs { + hostport, err := strconv.Atoi(pb.HostPort) + if err != nil { + return nil, nil, err + } + tmpPort := specgen.PortMapping{ + HostIP: pb.HostIP, + ContainerPort: uint16(port.Int()), + HostPort: uint16(hostport), + Range: 0, + Protocol: port.Proto(), + } + specPorts = append(specPorts, tmpPort) + } + } + + // network names + endpointsConfig := cc.NetworkingConfig.EndpointsConfig + cniNetworks := make([]string, 0, len(endpointsConfig)) + for netName := range endpointsConfig { + cniNetworks = append(cniNetworks, netName) + } + + // netMode + nsmode, _, err := specgen.ParseNetworkNamespace(cc.HostConfig.NetworkMode.NetworkName()) + if err != nil { + return nil, nil, err + } + + netNS := specgen.Namespace{ + NSMode: nsmode.NSMode, + Value: nsmode.Value, + } + + // network + // Note: we cannot emulate compat exactly here. we only allow specifics of networks to be + // defined when there is only one network. + netInfo := entities.NetOptions{ + AddHosts: cc.HostConfig.ExtraHosts, + CNINetworks: cniNetworks, + DNSOptions: cc.HostConfig.DNSOptions, + DNSSearch: cc.HostConfig.DNSSearch, + DNSServers: dns, + Network: netNS, + PublishPorts: specPorts, + } + + // static IP and MAC + if len(endpointsConfig) == 1 { + for _, ep := range endpointsConfig { + // if IP address is provided + if len(ep.IPAddress) > 0 { + staticIP := net.ParseIP(ep.IPAddress) + netInfo.StaticIP = &staticIP + } + // If MAC address is provided + if len(ep.MacAddress) > 0 { + staticMac, err := net.ParseMAC(ep.MacAddress) + if err != nil { + return nil, nil, err + } + netInfo.StaticMAC = &staticMac + } + break + } + } + + // Note: several options here are marked as "don't need". this is based + // on speculation by Matt and I. We think that these come into play later + // like with start. We believe this is just a difference in podman/compat + cliOpts := ContainerCLIOpts{ + //Attach: nil, // dont need? + Authfile: "", + CapAdd: append(capAdd, cc.HostConfig.CapAdd...), + CapDrop: append(cappDrop, cc.HostConfig.CapDrop...), + CGroupParent: cc.HostConfig.CgroupParent, + CIDFile: cc.HostConfig.ContainerIDFile, + CPUPeriod: uint64(cc.HostConfig.CPUPeriod), + CPUQuota: cc.HostConfig.CPUQuota, + CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod), + CPURTRuntime: cc.HostConfig.CPURealtimeRuntime, + CPUShares: uint64(cc.HostConfig.CPUShares), + //CPUS: 0, // dont need? + CPUSetCPUs: cc.HostConfig.CpusetCpus, + CPUSetMems: cc.HostConfig.CpusetMems, + //Detach: false, // dont need + //DetachKeys: "", // dont need + Devices: devices, + DeviceCGroupRule: nil, + DeviceReadBPs: readBps, + DeviceReadIOPs: readIops, + DeviceWriteBPs: writeBps, + DeviceWriteIOPs: writeIops, + Entrypoint: &entrypoint, + Env: cc.Config.Env, + Expose: expose, + GroupAdd: cc.HostConfig.GroupAdd, + Hostname: cc.Config.Hostname, + ImageVolume: "bind", + Init: init, + Interactive: cc.Config.OpenStdin, + IPC: string(cc.HostConfig.IpcMode), + Label: stringMaptoArray(cc.Config.Labels), + LogDriver: cc.HostConfig.LogConfig.Type, + LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config), + Name: cc.Name, + OOMScoreAdj: cc.HostConfig.OomScoreAdj, + OverrideArch: "", + OverrideOS: "", + OverrideVariant: "", + PID: string(cc.HostConfig.PidMode), + PIDsLimit: cc.HostConfig.PidsLimit, + Privileged: cc.HostConfig.Privileged, + PublishAll: cc.HostConfig.PublishAllPorts, + Quiet: false, + ReadOnly: cc.HostConfig.ReadonlyRootfs, + ReadOnlyTmpFS: true, // podman default + Rm: cc.HostConfig.AutoRemove, + SecurityOpt: cc.HostConfig.SecurityOpt, + StopSignal: cc.Config.StopSignal, + StoreageOpt: stringMaptoArray(cc.HostConfig.StorageOpt), + Sysctl: stringMaptoArray(cc.HostConfig.Sysctls), + Systemd: "true", // podman default + TmpFS: stringMaptoArray(cc.HostConfig.Tmpfs), + TTY: cc.Config.Tty, + //Ulimit: cc.HostConfig.Ulimits, // ask dan, no documented format + Ulimit: []string{"nproc=4194304:4194304"}, + User: cc.Config.User, + UserNS: string(cc.HostConfig.UsernsMode), + UTS: string(cc.HostConfig.UTSMode), + Mount: mounts, + Volume: volumes, + VolumesFrom: cc.HostConfig.VolumesFrom, + Workdir: cc.Config.WorkingDir, + Net: &netInfo, + } + + if len(cc.HostConfig.BlkioWeightDevice) > 0 { + devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice)) + for _, d := range cc.HostConfig.BlkioWeightDevice { + devices = append(devices, d.String()) + } + cliOpts.BlkIOWeightDevice = devices + } + if cc.HostConfig.BlkioWeight > 0 { + cliOpts.BlkIOWeight = strconv.Itoa(int(cc.HostConfig.BlkioWeight)) + } + + if cc.HostConfig.Memory > 0 { + cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory)) + } + + if cc.HostConfig.MemoryReservation > 0 { + cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation)) + } + + if cc.HostConfig.MemorySwap > 0 { + cliOpts.MemorySwap = strconv.Itoa(int(cc.HostConfig.MemorySwap)) + } + + if cc.Config.StopTimeout != nil { + cliOpts.StopTimeout = uint(*cc.Config.StopTimeout) + } + + if cc.HostConfig.ShmSize > 0 { + cliOpts.ShmSize = strconv.Itoa(int(cc.HostConfig.ShmSize)) + } + + if cc.HostConfig.KernelMemory > 0 { + cliOpts.KernelMemory = strconv.Itoa(int(cc.HostConfig.KernelMemory)) + } + if len(cc.HostConfig.RestartPolicy.Name) > 0 { + policy := cc.HostConfig.RestartPolicy.Name + // only add restart count on failure + if cc.HostConfig.RestartPolicy.IsOnFailure() { + policy += fmt.Sprintf(":%d", cc.HostConfig.RestartPolicy.MaximumRetryCount) + } + cliOpts.Restart = policy + } + + if cc.HostConfig.MemorySwappiness != nil { + cliOpts.MemorySwappiness = *cc.HostConfig.MemorySwappiness + } + if cc.HostConfig.OomKillDisable != nil { + cliOpts.OOMKillDisable = *cc.HostConfig.OomKillDisable + } + if cc.Config.Healthcheck != nil { + cliOpts.HealthCmd = strings.Join(cc.Config.Healthcheck.Test, " ") + cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String() + cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries) + cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String() + cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String() + } + + // specgen assumes the image name is arg[0] + cmd := []string{cc.Image} + cmd = append(cmd, cc.Config.Cmd...) + return &cliOpts, cmd, nil +} diff --git a/cmd/podman/report/diff.go b/cmd/podman/common/diffChanges.go index edd324bfe..4aa485acc 100644 --- a/cmd/podman/report/diff.go +++ b/cmd/podman/common/diffChanges.go @@ -1,4 +1,4 @@ -package report +package common import ( "fmt" diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go index 2a82451e4..71f897264 100644 --- a/cmd/podman/common/volumes.go +++ b/cmd/podman/common/volumes.go @@ -238,7 +238,7 @@ func getBindMount(args []string) (spec.Mount, error) { var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "bind-nonrecursive": newMount.Options = append(newMount.Options, "bind") @@ -366,7 +366,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) { var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "tmpcopyup", "notmpcopyup": if setTmpcopyup { @@ -441,7 +441,7 @@ func getDevptsMount(args []string) (spec.Mount, error) { var setDest bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "target", "dst", "destination": if len(kv) == 1 { @@ -473,7 +473,7 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { var setSource, setDest, setRORW, setSuid, setDev, setExec bool for _, val := range args { - kv := strings.Split(val, "=") + kv := strings.SplitN(val, "=", 2) switch kv[0] { case "ro", "rw": if setRORW { diff --git a/cmd/podman/containers/attach.go b/cmd/podman/containers/attach.go index cfe7df441..0db7c22d0 100644 --- a/cmd/podman/containers/attach.go +++ b/cmd/podman/containers/attach.go @@ -14,7 +14,7 @@ import ( var ( 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 = &cobra.Command{ - Use: "attach [flags] CONTAINER", + Use: "attach [options] CONTAINER", Short: "Attach to a running container", Long: attachDescription, RunE: attach, diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go index 284393dc0..2606f62c5 100644 --- a/cmd/podman/containers/checkpoint.go +++ b/cmd/podman/containers/checkpoint.go @@ -20,7 +20,7 @@ var ( Checkpoints one or more running containers. The container name or ID can be used. ` checkpointCommand = &cobra.Command{ - Use: "checkpoint [flags] CONTAINER [CONTAINER...]", + Use: "checkpoint [options] CONTAINER [CONTAINER...]", Short: "Checkpoints one or more containers", Long: checkpointDescription, RunE: checkpoint, diff --git a/cmd/podman/containers/cleanup.go b/cmd/podman/containers/cleanup.go index 7913f5a10..54bc64a60 100644 --- a/cmd/podman/containers/cleanup.go +++ b/cmd/podman/containers/cleanup.go @@ -19,7 +19,7 @@ var ( 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 = &cobra.Command{ - Use: "cleanup [flags] CONTAINER [CONTAINER...]", + Use: "cleanup [options] CONTAINER [CONTAINER...]", Short: "Cleanup network and mountpoints of one or more containers", Long: cleanupDescription, RunE: cleanup, diff --git a/cmd/podman/containers/commit.go b/cmd/podman/containers/commit.go index 31294c66d..1b33d221d 100644 --- a/cmd/podman/containers/commit.go +++ b/cmd/podman/containers/commit.go @@ -18,7 +18,7 @@ var ( 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 = &cobra.Command{ - Use: "commit [flags] CONTAINER [IMAGE]", + Use: "commit [options] CONTAINER [IMAGE]", Short: "Create new image based on the changed container", Long: commitDescription, RunE: commit, diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go index 28a18e83e..5dfe43ded 100644 --- a/cmd/podman/containers/cp.go +++ b/cmd/podman/containers/cp.go @@ -16,7 +16,7 @@ var ( You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container. If "-" is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. The CONTAINER can be a running or stopped container. The SRC_PATH or DEST_PATH can be a file or directory. ` cpCommand = &cobra.Command{ - Use: "cp [flags] SRC_PATH DEST_PATH", + Use: "cp [options] SRC_PATH DEST_PATH", Short: "Copy files/folders between a container and the local filesystem", Long: cpDescription, Args: cobra.ExactArgs(2), diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 809162e76..b7b2a364f 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -15,11 +15,9 @@ import ( "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/containers/podman/v2/pkg/errorhandling" "github.com/containers/podman/v2/pkg/specgen" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -29,7 +27,7 @@ var ( 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 [flags] IMAGE [COMMAND [ARG...]]", + Use: "create [options] IMAGE [COMMAND [ARG...]]", Short: "Create but do not start a container", Long: createDescription, RunE: create, @@ -63,7 +61,6 @@ func createFlags(flags *pflag.FlagSet) { _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("http-proxy") } } @@ -95,15 +92,6 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } - cidFile, err := openCidFile(cliVals.CIDFile) - if err != nil { - return err - } - - if cidFile != nil { - defer errorhandling.CloseQuiet(cidFile) - defer errorhandling.SyncQuiet(cidFile) - } if err := createInit(cmd); err != nil { return err @@ -140,10 +128,9 @@ func create(cmd *cobra.Command, args []string) error { return err } - if cidFile != nil { - _, err = cidFile.WriteString(report.Id) - if err != nil { - logrus.Error(err) + if cliVals.CIDFile != "" { + if err := util.CreateCidFile(cliVals.CIDFile, report.Id); err != nil { + return err } } @@ -270,20 +257,6 @@ func pullImage(imageName string) (string, error) { return imageName, nil } -func openCidFile(cidfile string) (*os.File, error) { - if cidfile == "" { - return nil, nil - } - cidFile, err := util.OpenExclusiveFile(cidfile) - if err != nil && os.IsExist(err) { - return nil, errors.Errorf("container id file exists. Ensure another container is not using it or delete %s", cidfile) - } - if err != nil { - return nil, errors.Errorf("error opening cidfile %s", cidfile) - } - return cidFile, nil -} - // createPodIfNecessary automatically creates a pod when requested. if the pod name // has the form new:ID, the pod ID is created and the name in the spec generator is replaced // with ID. diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go index a3ca6edf9..caf7e9955 100644 --- a/cmd/podman/containers/diff.go +++ b/cmd/podman/containers/diff.go @@ -1,9 +1,9 @@ package containers import ( - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -13,7 +13,7 @@ import ( var ( // podman container _diff_ diffCmd = &cobra.Command{ - Use: "diff [flags] CONTAINER", + Use: "diff [options] CONTAINER", Args: validate.IDOrLatestArgs, Short: "Inspect changes to the container's file systems", Long: `Displays changes to the container filesystem's'. The container will be compared to its parent layer.`, @@ -54,10 +54,10 @@ func diff(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(diffOpts.Format): - return report.ChangesToJSON(results) + case report.IsJSON(diffOpts.Format): + return common.ChangesToJSON(results) case diffOpts.Format == "": - return report.ChangesToTable(results) + return common.ChangesToTable(results) default: return errors.New("only supported value for '--format' is 'json'") } diff --git a/cmd/podman/containers/exec.go b/cmd/podman/containers/exec.go index e301ca588..88851f619 100644 --- a/cmd/podman/containers/exec.go +++ b/cmd/podman/containers/exec.go @@ -20,7 +20,7 @@ var ( execDescription = `Execute the specified command inside a running container. ` execCommand = &cobra.Command{ - Use: "exec [flags] CONTAINER [COMMAND [ARG...]]", + Use: "exec [options] CONTAINER [COMMAND [ARG...]]", Short: "Run a process in a running container", Long: execDescription, RunE: exec, diff --git a/cmd/podman/containers/exists.go b/cmd/podman/containers/exists.go index 283f6df18..70b8af159 100644 --- a/cmd/podman/containers/exists.go +++ b/cmd/podman/containers/exists.go @@ -12,10 +12,10 @@ var ( containerExistsDescription = `If the named container exists in local storage, podman container exists exits with 0, otherwise the exit code will be 1.` existsCommand = &cobra.Command{ - Use: "exists CONTAINER", + Use: "exists [options] CONTAINER", Short: "Check if a container exists in local storage", Long: containerExistsDescription, - Example: `podman container exists containerID + Example: `podman container exists --external containerID podman container exists myctr || podman run --name myctr [etc...]`, RunE: exists, Args: cobra.ExactArgs(1), @@ -29,10 +29,19 @@ func init() { Command: existsCommand, Parent: containerCmd, }) + flags := existsCommand.Flags() + flags.Bool("external", false, "Check external storage containers as well as Podman containers") } func exists(cmd *cobra.Command, args []string) error { - response, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0]) + external, err := cmd.Flags().GetBool("external") + if err != nil { + return err + } + options := entities.ContainerExistsOptions{ + External: external, + } + response, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0], options) if err != nil { return err } diff --git a/cmd/podman/containers/export.go b/cmd/podman/containers/export.go index 1a59b7fff..f5e02d134 100644 --- a/cmd/podman/containers/export.go +++ b/cmd/podman/containers/export.go @@ -18,7 +18,7 @@ var ( " and saves it on the local machine." exportCommand = &cobra.Command{ - Use: "export [flags] CONTAINER", + Use: "export [options] CONTAINER", Short: "Export container's filesystem contents as a tar archive", Long: exportDescription, RunE: export, diff --git a/cmd/podman/containers/init.go b/cmd/podman/containers/init.go index 5a826bc59..983c0e4e8 100644 --- a/cmd/podman/containers/init.go +++ b/cmd/podman/containers/init.go @@ -15,7 +15,7 @@ var ( initDescription = `Initialize one or more containers, creating the OCI spec and mounts for inspection. Container names or IDs can be used.` initCommand = &cobra.Command{ - Use: "init [flags] CONTAINER [CONTAINER...]", + Use: "init [options] CONTAINER [CONTAINER...]", Short: "Initialize one or more containers", Long: initDescription, RunE: initContainer, diff --git a/cmd/podman/containers/inspect.go b/cmd/podman/containers/inspect.go index 1c208b513..b4e1feccb 100644 --- a/cmd/podman/containers/inspect.go +++ b/cmd/podman/containers/inspect.go @@ -11,7 +11,7 @@ import ( var ( // podman container _inspect_ inspectCmd = &cobra.Command{ - Use: "inspect [flags] CONTAINER [CONTAINER...]", + Use: "inspect [options] CONTAINER [CONTAINER...]", Short: "Display the configuration of a container", Long: `Displays the low-level information on a container identified by name or ID.`, RunE: inspectExec, diff --git a/cmd/podman/containers/kill.go b/cmd/podman/containers/kill.go index a4d536098..1bb071b6d 100644 --- a/cmd/podman/containers/kill.go +++ b/cmd/podman/containers/kill.go @@ -17,7 +17,7 @@ import ( var ( killDescription = "The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal." killCommand = &cobra.Command{ - Use: "kill [flags] CONTAINER [CONTAINER...]", + Use: "kill [options] CONTAINER [CONTAINER...]", Short: "Kill one or more running containers with a specific signal", Long: killDescription, RunE: kill, diff --git a/cmd/podman/containers/list.go b/cmd/podman/containers/list.go index daf03a51b..78a15559f 100644 --- a/cmd/podman/containers/list.go +++ b/cmd/podman/containers/list.go @@ -10,7 +10,7 @@ import ( var ( // podman container _list_ listCmd = &cobra.Command{ - Use: "list", + Use: "list [options]", Aliases: []string{"ls"}, Args: validate.NoArgs, Short: "List containers", diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go index acc2ab1aa..8ad2d7e16 100644 --- a/cmd/podman/containers/logs.go +++ b/cmd/podman/containers/logs.go @@ -27,7 +27,7 @@ var ( 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 = &cobra.Command{ - Use: "logs [flags] CONTAINER [CONTAINER...]", + Use: "logs [options] CONTAINER [CONTAINER...]", Short: "Fetch the logs of one or more containers", Long: logsDescription, Args: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index c4dfb513f..335367e18 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -6,7 +6,7 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -25,7 +25,7 @@ var ( ` mountCommand = &cobra.Command{ - Use: "mount [flags] [CONTAINER...]", + Use: "mount [options] [CONTAINER...]", Short: "Mount a working container's root filesystem", Long: mountDescription, RunE: mount, @@ -97,7 +97,7 @@ func mount(_ *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(mountOpts.Format): + case report.IsJSON(mountOpts.Format): return printJSON(reports) case mountOpts.Format == "": break // print defaults diff --git a/cmd/podman/containers/pause.go b/cmd/podman/containers/pause.go index c5171303d..89a76ab25 100644 --- a/cmd/podman/containers/pause.go +++ b/cmd/podman/containers/pause.go @@ -17,7 +17,7 @@ import ( var ( pauseDescription = `Pauses one or more running containers. The container name or ID can be used.` pauseCommand = &cobra.Command{ - Use: "pause [flags] CONTAINER [CONTAINER...]", + Use: "pause [options] CONTAINER [CONTAINER...]", Short: "Pause all the processes in one or more containers", Long: pauseDescription, RunE: pause, diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go index 347f06bba..a895f24b1 100644 --- a/cmd/podman/containers/port.go +++ b/cmd/podman/containers/port.go @@ -18,7 +18,7 @@ var ( portDescription = `List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT ` portCommand = &cobra.Command{ - Use: "port [flags] CONTAINER [PORT]", + Use: "port [options] CONTAINER [PORT]", Short: "List port mappings or a specific mapping for the container", Long: portDescription, RunE: port, @@ -31,7 +31,7 @@ var ( } containerPortCommand = &cobra.Command{ - Use: "port [flags] CONTAINER [PORT]", + Use: "port [options] CONTAINER [PORT]", Short: portCommand.Short, Long: portDescription, RunE: portCommand.RunE, diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go index cfe6765ac..bfdace086 100644 --- a/cmd/podman/containers/prune.go +++ b/cmd/podman/containers/prune.go @@ -21,7 +21,7 @@ var ( Removes all non running containers`) pruneCommand = &cobra.Command{ - Use: "prune [flags]", + Use: "prune [options]", Short: "Remove all non running containers", Long: pruneDescription, RunE: prune, diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 8082a74c2..90f4db19c 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -11,10 +11,8 @@ import ( "time" tm "github.com/buger/goterm" - "github.com/containers/buildah/pkg/formats" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -28,7 +26,7 @@ import ( var ( psDescription = "Prints out information about the containers" psCommand = &cobra.Command{ - Use: "ps", + Use: "ps [options]", Args: validate.NoArgs, Short: "List containers", Long: psDescription, @@ -59,7 +57,7 @@ func init() { func listFlagSet(flags *pflag.FlagSet) { flags.BoolVarP(&listOpts.All, "all", "a", false, "Show all the containers, default is only running containers") flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given") - flags.BoolVar(&listOpts.Storage, "storage", false, "Show containers in storage not controlled by Podman") + flags.BoolVar(&listOpts.Storage, "external", false, "Show containers in storage not controlled by Podman") flags.StringVar(&listOpts.Format, "format", "", "Pretty-print containers to JSON or using a Go template") flags.IntVarP(&listOpts.Last, "last", "n", -1, "Print the n last created containers (all states)") flags.BoolVar(&listOpts.Namespace, "ns", false, "Display namespace information") @@ -93,7 +91,7 @@ func checkFlags(c *cobra.Command) error { if listOpts.Size || listOpts.Namespace { return errors.Errorf("quiet conflicts with size and namespace") } - if c.Flag("format").Changed && listOpts.Format != formats.JSONString { + if c.Flag("format").Changed && !report.IsJSON(listOpts.Format) { // Quiet is overridden by Go template output. listOpts.Quiet = false } @@ -180,7 +178,7 @@ func ps(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(listOpts.Format): + case report.IsJSON(listOpts.Format): return jsonOut(listContainers) case listOpts.Quiet: return quietOut(listContainers) @@ -372,12 +370,6 @@ func (l psReporter) CreatedHuman() string { // portsToString converts the ports used to a string of the from "port1, port2" // and also groups a continuous list of ports into a readable format. func portsToString(ports []ocicni.PortMapping) string { - type portGroup struct { - first int32 - last int32 - } - portDisplay := []string{} - if len(ports) == 0 { return "" } @@ -386,41 +378,124 @@ func portsToString(ports []ocicni.PortMapping) string { return comparePorts(ports[i], ports[j]) }) - // portGroupMap is used for grouping continuous ports. - portGroupMap := make(map[string]*portGroup) - var groupKeyList []string + portGroups := [][]ocicni.PortMapping{} + currentGroup := []ocicni.PortMapping{} + for i, v := range ports { + var prevPort, nextPort *int32 + if i > 0 { + prevPort = &ports[i-1].ContainerPort + } + if i+1 < len(ports) { + nextPort = &ports[i+1].ContainerPort + } - for _, v := range ports { + port := v.ContainerPort - hostIP := v.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" + // Helper functions + addToCurrentGroup := func(x ocicni.PortMapping) { + currentGroup = append(currentGroup, x) } - // If hostPort and containerPort are not same, consider as individual port. - if v.ContainerPort != v.HostPort { - portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) - continue + + addToPortGroup := func(x ocicni.PortMapping) { + portGroups = append(portGroups, []ocicni.PortMapping{x}) + } + + finishCurrentGroup := func() { + portGroups = append(portGroups, currentGroup) + currentGroup = []ocicni.PortMapping{} } - portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol) + // Single entry slice + if prevPort == nil && nextPort == nil { + addToPortGroup(v) + } + + // Start of the slice with len > 0 + if prevPort == nil && nextPort != nil { + isGroup := *nextPort-1 == port + + if isGroup { + // Start with a group + addToCurrentGroup(v) + } else { + // Start with single item + addToPortGroup(v) + } - portgroup, ok := portGroupMap[portMapKey] - if !ok { - portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort} - // This list is required to traverse portGroupMap. - groupKeyList = append(groupKeyList, portMapKey) continue } - if portgroup.last == (v.ContainerPort - 1) { - portgroup.last = v.ContainerPort + // Middle of the slice with len > 0 + if prevPort != nil && nextPort != nil { + currentIsGroup := *prevPort+1 == port + nextIsGroup := *nextPort-1 == port + + if currentIsGroup { + // Maybe in the middle of a group + addToCurrentGroup(v) + + if !nextIsGroup { + // End of a group + finishCurrentGroup() + } + } else if nextIsGroup { + // Start of a new group + addToCurrentGroup(v) + } else { + // No group at all + addToPortGroup(v) + } + continue } + + // End of the slice with len > 0 + if prevPort != nil && nextPort == nil { + isGroup := *prevPort+1 == port + + if isGroup { + // End group + addToCurrentGroup(v) + finishCurrentGroup() + } else { + // End single item + addToPortGroup(v) + } + } } - // For each portMapKey, format group list and append to output string. - for _, portKey := range groupKeyList { - group := portGroupMap[portKey] - portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last)) + + portDisplay := []string{} + for _, group := range portGroups { + if len(group) == 0 { + // Usually should not happen, but better do not crash. + continue + } + + first := group[0] + + hostIP := first.HostIP + if hostIP == "" { + hostIP = "0.0.0.0" + } + + // Single mappings + if len(group) == 1 { + portDisplay = append(portDisplay, + fmt.Sprintf( + "%s:%d->%d/%s", + hostIP, first.HostPort, first.ContainerPort, first.Protocol, + ), + ) + continue + } + + // Group mappings + last := group[len(group)-1] + portDisplay = append(portDisplay, formatGroup( + fmt.Sprintf("%s/%s", hostIP, first.Protocol), + first.HostPort, last.HostPort, + first.ContainerPort, last.ContainerPort, + )) } return strings.Join(portDisplay, ", ") } @@ -441,9 +516,10 @@ func comparePorts(i, j ocicni.PortMapping) bool { return i.Protocol < j.Protocol } -// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto> -// e.g 0.0.0.0:1000-1006->1000-1006/tcp. -func formatGroup(key string, start, last int32) string { +// formatGroup returns the group in the format: +// <IP:firstHost:lastHost->firstCtr:lastCtr/Proto> +// e.g 0.0.0.0:1000-1006->2000-2006/tcp. +func formatGroup(key string, firstHost, lastHost, firstCtr, lastCtr int32) string { parts := strings.Split(key, "/") groupType := parts[0] var ip string @@ -451,12 +527,16 @@ func formatGroup(key string, start, last int32) string { ip = parts[0] groupType = parts[1] } - group := strconv.Itoa(int(start)) - if start != last { - group = fmt.Sprintf("%s-%d", group, last) - } - if ip != "" { - group = fmt.Sprintf("%s:%s->%s", ip, group, group) + + group := func(first, last int32) string { + group := strconv.Itoa(int(first)) + if first != last { + group = fmt.Sprintf("%s-%d", group, last) + } + return group } - return fmt.Sprintf("%s/%s", group, groupType) + hostGroup := group(firstHost, lastHost) + ctrGroup := group(firstCtr, lastCtr) + + return fmt.Sprintf("%s:%s->%s/%s", ip, hostGroup, ctrGroup, groupType) } diff --git a/cmd/podman/containers/restart.go b/cmd/podman/containers/restart.go index 5f6f9c35c..1cc28c20d 100644 --- a/cmd/podman/containers/restart.go +++ b/cmd/podman/containers/restart.go @@ -20,7 +20,7 @@ var ( A timeout before forcibly stopping can be set, but defaults to %d seconds.`, containerConfig.Engine.StopTimeout) restartCommand = &cobra.Command{ - Use: "restart [flags] CONTAINER [CONTAINER...]", + Use: "restart [options] CONTAINER [CONTAINER...]", Short: "Restart one or more containers", Long: restartDescription, RunE: restart, diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go index c996144e3..314bf7564 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -20,7 +20,7 @@ var ( Restores a container from a checkpoint. The container name or ID can be used. ` restoreCommand = &cobra.Command{ - Use: "restore [flags] CONTAINER [CONTAINER...]", + Use: "restore [options] CONTAINER [CONTAINER...]", Short: "Restores one or more containers from a checkpoint", Long: restoreDescription, RunE: restore, diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index a7739b3ba..ccdd2ef05 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -21,7 +21,7 @@ var ( Command does not remove images. Running or unusable containers will not be removed without the -f option.` rmCommand = &cobra.Command{ - Use: "rm [flags] CONTAINER [CONTAINER...]", + Use: "rm [options] CONTAINER [CONTAINER...]", Short: "Remove one or more containers", Long: rmDescription, RunE: rm, diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 19c984bbf..6cadbc7ec 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -25,7 +25,7 @@ var ( runDescription = "Runs a command in a new container from the given image" runCommand = &cobra.Command{ Args: cobra.MinimumNArgs(1), - Use: "run [flags] IMAGE [COMMAND [ARG...]]", + Use: "run [options] IMAGE [COMMAND [ARG...]]", Short: "Run a command in a new container", Long: runDescription, RunE: run, @@ -63,10 +63,11 @@ func runFlags(flags *pflag.FlagSet) { flags.BoolVar(&runOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process") 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.BoolVarP(&runOpts.Detach, "detach", "d", false, "Run container in background and print container ID") + flags.StringVar(&runOpts.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("http-proxy") _ = flags.MarkHidden("preserve-fds") } @@ -110,15 +111,8 @@ func run(cmd *cobra.Command, args []string) error { return errors.Wrapf(err, "error checking authfile path %s", af) } } - cidFile, err := openCidFile(cliVals.CIDFile) - if err != nil { - return err - } - if cidFile != nil { - defer errorhandling.CloseQuiet(cidFile) - defer errorhandling.SyncQuiet(cidFile) - } + runOpts.CIDFile = cliVals.CIDFile runOpts.Rm = cliVals.Rm if err := createInit(cmd); err != nil { return err @@ -172,8 +166,6 @@ func run(cmd *cobra.Command, args []string) error { } } } - runOpts.Detach = cliVals.Detach - runOpts.DetachKeys = cliVals.DetachKeys cliVals.PreserveFDs = runOpts.PreserveFDs s := specgen.NewSpecGenerator(imageName, cliVals.RootFS) if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { @@ -194,14 +186,8 @@ func run(cmd *cobra.Command, args []string) error { if err != nil { return err } - if cidFile != nil { - _, err = cidFile.WriteString(report.Id) - if err != nil { - logrus.Error(err) - } - } - if cliVals.Detach { + if runOpts.Detach { fmt.Println(report.Id) return nil } diff --git a/cmd/podman/containers/runlabel.go b/cmd/podman/containers/runlabel.go index 5ee8c9d6c..b49af36ab 100644 --- a/cmd/podman/containers/runlabel.go +++ b/cmd/podman/containers/runlabel.go @@ -24,7 +24,7 @@ var ( runlabelOptions = runlabelOptionsWrapper{} runlabelDescription = "Executes a command as described by a container image label." runlabelCommand = &cobra.Command{ - Use: "runlabel [flags] LABEL IMAGE [ARG...]", + Use: "runlabel [options] LABEL IMAGE [ARG...]", Short: "Execute the command described by an image label", Long: runlabelDescription, RunE: runlabel, diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go index 1e58498b6..21f31d360 100644 --- a/cmd/podman/containers/start.go +++ b/cmd/podman/containers/start.go @@ -17,7 +17,7 @@ import ( var ( startDescription = `Starts one or more containers. The container name or ID can be used.` startCommand = &cobra.Command{ - Use: "start [flags] CONTAINER [CONTAINER...]", + Use: "start [options] CONTAINER [CONTAINER...]", Short: "Start one or more containers", Long: startDescription, RunE: start, diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index c30ea52ec..85e7a1e82 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -7,9 +7,8 @@ import ( "text/template" tm "github.com/buger/goterm" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/cgroups" @@ -26,7 +25,7 @@ import ( var ( statsDescription = "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers." statsCommand = &cobra.Command{ - Use: "stats [flags] [CONTAINER...]", + Use: "stats [options] [CONTAINER...]", Short: "Display a live stream of container resource usage statistics", Long: statsDescription, RunE: stats, @@ -157,7 +156,7 @@ func outputStats(reports []define.ContainerStats) error { for _, r := range reports { stats = append(stats, containerStats{r}) } - if parse.MatchesJSONFormat(statsOptions.Format) { + if report.IsJSON(statsOptions.Format) { return outputJSON(stats) } format := defaultStatsRow @@ -240,9 +239,9 @@ func combineHumanValues(a, b uint64) string { func outputJSON(stats []containerStats) error { type jstat struct { - Id string `json:"id"` //nolint + Id string `json:"id"` // nolint Name string `json:"name"` - CpuPercent string `json:"cpu_percent"` //nolint + CpuPercent string `json:"cpu_percent"` // nolint MemUsage string `json:"mem_usage"` MemPerc string `json:"mem_percent"` NetIO string `json:"net_io"` diff --git a/cmd/podman/containers/stop.go b/cmd/podman/containers/stop.go index 1c0a5efe4..7c8c1b50e 100644 --- a/cmd/podman/containers/stop.go +++ b/cmd/podman/containers/stop.go @@ -17,7 +17,7 @@ var ( A timeout to forcibly stop the container can also be set but defaults to %d seconds otherwise.`, containerConfig.Engine.StopTimeout) stopCommand = &cobra.Command{ - Use: "stop [flags] CONTAINER [CONTAINER...]", + Use: "stop [options] CONTAINER [CONTAINER...]", Short: "Stop one or more containers", Long: stopDescription, RunE: stop, diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go index f2632d57a..361d30516 100644 --- a/cmd/podman/containers/top.go +++ b/cmd/podman/containers/top.go @@ -26,7 +26,7 @@ var ( topOptions = entities.TopOptions{} topCommand = &cobra.Command{ - Use: "top [flags] CONTAINER [FORMAT-DESCRIPTORS|ARGS...]", + Use: "top [options] CONTAINER [FORMAT-DESCRIPTORS|ARGS...]", Short: "Display the running processes of a container", Long: topDescription, RunE: top, diff --git a/cmd/podman/containers/unmount.go b/cmd/podman/containers/unmount.go index 424d4cedc..c3159cfed 100644 --- a/cmd/podman/containers/unmount.go +++ b/cmd/podman/containers/unmount.go @@ -19,7 +19,7 @@ var ( An unmount can be forced with the --force flag. ` unmountCommand = &cobra.Command{ - Use: "unmount [flags] CONTAINER [CONTAINER...]", + Use: "unmount [options] CONTAINER [CONTAINER...]", Aliases: []string{"umount"}, Short: "Unmounts working container's root filesystem", Long: description, diff --git a/cmd/podman/containers/unpause.go b/cmd/podman/containers/unpause.go index 43eaad72b..8927fc426 100644 --- a/cmd/podman/containers/unpause.go +++ b/cmd/podman/containers/unpause.go @@ -17,7 +17,7 @@ import ( var ( unpauseDescription = `Unpauses one or more previously paused containers. The container name or ID can be used.` unpauseCommand = &cobra.Command{ - Use: "unpause [flags] CONTAINER [CONTAINER...]", + Use: "unpause [options] CONTAINER [CONTAINER...]", Short: "Unpause the processes in one or more containers", Long: unpauseDescription, RunE: unpause, diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go index 4bc3d20e2..b4986143b 100644 --- a/cmd/podman/containers/wait.go +++ b/cmd/podman/containers/wait.go @@ -19,11 +19,11 @@ var ( waitDescription = `Block until one or more containers stop and then print their exit codes. ` waitCommand = &cobra.Command{ - Use: "wait [flags] CONTAINER [CONTAINER...]", + Use: "wait [options] CONTAINER [CONTAINER...]", Short: "Block on one or more containers", Long: waitDescription, RunE: wait, - Example: `podman wait --interval 5000 ctrID + Example: `podman wait --interval 5s ctrID podman wait ctrID1 ctrID2`, } @@ -32,7 +32,7 @@ var ( Short: waitCommand.Short, Long: waitCommand.Long, RunE: waitCommand.RunE, - Example: `podman container wait --interval 5000 ctrID + Example: `podman container wait --interval 5s ctrID podman container wait ctrID1 ctrID2`, } ) @@ -40,10 +40,11 @@ var ( var ( waitOptions = entities.WaitOptions{} waitCondition string + waitInterval string ) func waitFlags(flags *pflag.FlagSet) { - flags.DurationVarP(&waitOptions.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion") + flags.StringVarP(&waitInterval, "interval", "i", "250ns", "Time Interval to wait before polling for completion") flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on") } @@ -70,8 +71,11 @@ func wait(cmd *cobra.Command, args []string) error { err error errs utils.OutputErrors ) - if waitOptions.Interval == 0 { - return errors.New("interval must be greater then 0") + if waitOptions.Interval, err = time.ParseDuration(waitInterval); err != nil { + var err1 error + if waitOptions.Interval, err1 = time.ParseDuration(waitInterval + "ms"); err1 != nil { + return err + } } if !waitOptions.Latest && len(args) == 0 { diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 51a04bf46..bad31a4a2 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -17,7 +17,7 @@ var ( // Command: podman _diff_ Object_ID diffDescription = `Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer.` diffCmd = &cobra.Command{ - Use: "diff [flags] {CONTAINER_ID | IMAGE_ID}", + Use: "diff [options] {CONTAINER_ID | IMAGE_ID}", Args: validate.IDOrLatestArgs, Short: "Display the changes to the object's file system", Long: diffDescription, @@ -48,7 +48,10 @@ func diff(cmd *cobra.Command, args []string) error { return containers.Diff(cmd, args, diffOpts) } - if found, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), args[0]); err != nil { + options := entities.ContainerExistsOptions{ + External: true, + } + if found, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), args[0], options); err != nil { return err } else if found.Value { return containers.Diff(cmd, args, diffOpts) diff --git a/cmd/podman/generate/kube.go b/cmd/podman/generate/kube.go index 2f63ba590..4935fc60c 100644 --- a/cmd/podman/generate/kube.go +++ b/cmd/podman/generate/kube.go @@ -20,7 +20,7 @@ var ( Whether the input is for a container or pod, Podman will always generate the specification as a pod.` kubeCmd = &cobra.Command{ - Use: "kube [flags] CONTAINER | POD", + Use: "kube [options] CONTAINER | POD", Short: "Generate Kubernetes YAML from a container or pod.", Long: kubeDescription, RunE: kube, diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go index 02e826549..8e937fa90 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -6,7 +6,7 @@ import ( "os" "path/filepath" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -24,7 +24,7 @@ var ( The generated units can later be controlled via systemctl(1).` systemdCmd = &cobra.Command{ - Use: "systemd [flags] CTR|POD", + Use: "systemd [options] CTR|POD", Short: "Generate systemd units.", Long: systemdDescription, RunE: systemd, @@ -63,7 +63,7 @@ func systemd(cmd *cobra.Command, args []string) error { logrus.Warnln("The generated units should be placed on your remote system") } - report, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions) + reports, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions) if err != nil { return err } @@ -73,7 +73,7 @@ func systemd(cmd *cobra.Command, args []string) error { if err != nil { return errors.Wrap(err, "error getting current working directory") } - for name, content := range report.Units { + for name, content := range reports.Units { path := filepath.Join(cwd, fmt.Sprintf("%s.service", name)) f, err := os.Create(path) if err != nil { @@ -94,15 +94,15 @@ func systemd(cmd *cobra.Command, args []string) error { } // modify in place so we can print the // paths when --files is set - report.Units[name] = path + reports.Units[name] = path } } switch { - case parse.MatchesJSONFormat(format): - return printJSON(report.Units) + case report.IsJSON(format): + return printJSON(reports.Units) case format == "": - return printDefault(report.Units) + return printDefault(reports.Units) default: return errors.Errorf("unknown --format argument: %s", format) } diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index ac1b2c848..18c31313b 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -40,7 +40,7 @@ var ( // Command: podman _diff_ Object_ID buildDescription = "Builds an OCI or Docker image using instructions from one or more Containerfiles and a specified build context directory." buildCmd = &cobra.Command{ - Use: "build [flags] [CONTEXT]", + Use: "build [options] [CONTEXT]", Short: "Build an image using instructions from Containerfiles", Long: buildDescription, Args: cobra.MaximumNArgs(1), @@ -282,8 +282,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil flags.Layers = false } - var stdin, stdout, stderr, reporter *os.File - stdin = os.Stdin + var stdout, stderr, reporter *os.File stdout = os.Stdout stderr = os.Stderr reporter = os.Stderr @@ -422,7 +421,6 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil ForceRmIntermediateCtrs: flags.ForceRm, IDMappingOptions: idmappingOptions, IIDFile: flags.Iidfile, - In: stdin, Isolation: isolation, Labels: flags.Label, Layers: flags.Layers, diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go index 05a05fa04..b7722e5e5 100644 --- a/cmd/podman/images/diff.go +++ b/cmd/podman/images/diff.go @@ -1,9 +1,9 @@ package images import ( - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -13,7 +13,7 @@ import ( var ( // podman container _inspect_ diffCmd = &cobra.Command{ - Use: "diff [flags] IMAGE", + Use: "diff [options] IMAGE", Args: cobra.ExactArgs(1), Short: "Inspect changes to the image's file systems", Long: `Displays changes to the image's filesystem. The image will be compared to its parent layer.`, @@ -51,10 +51,10 @@ func diff(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(diffOpts.Format): - return report.ChangesToJSON(results) + case report.IsJSON(diffOpts.Format): + return common.ChangesToJSON(results) case diffOpts.Format == "": - return report.ChangesToTable(results) + return common.ChangesToTable(results) default: return errors.New("only supported value for '--format' is 'json'") } diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go index fa4b368c6..3075218d1 100644 --- a/cmd/podman/images/history.go +++ b/cmd/podman/images/history.go @@ -10,9 +10,8 @@ import ( "time" "unicode" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" "github.com/pkg/errors" @@ -27,7 +26,7 @@ var ( // podman _history_ historyCmd = &cobra.Command{ - Use: "history [flags] IMAGE", + Use: "history [options] IMAGE", Short: "Show history of a specified image", Long: long, Args: cobra.ExactArgs(1), @@ -81,7 +80,7 @@ func history(cmd *cobra.Command, args []string) error { return err } - if parse.MatchesJSONFormat(opts.format) { + if report.IsJSON(opts.format) { var err error if len(results.Layers) == 0 { _, err = fmt.Fprintf(os.Stdout, "[]\n") diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go index 1c234e743..e3545da69 100644 --- a/cmd/podman/images/import.go +++ b/cmd/podman/images/import.go @@ -19,7 +19,7 @@ var ( Note remote tar balls can be specified, via web address. Optionally tag the image. You can specify the instructions using the --change option.` importCommand = &cobra.Command{ - Use: "import [flags] PATH [REFERENCE]", + Use: "import [options] PATH [REFERENCE]", Short: "Import a tarball to create a filesystem image", Long: importDescription, RunE: importCon, diff --git a/cmd/podman/images/inspect.go b/cmd/podman/images/inspect.go index 065dfaed2..8f005553d 100644 --- a/cmd/podman/images/inspect.go +++ b/cmd/podman/images/inspect.go @@ -10,7 +10,7 @@ import ( var ( // Command: podman image _inspect_ inspectCmd = &cobra.Command{ - Use: "inspect [flags] IMAGE [IMAGE...]", + Use: "inspect [options] IMAGE [IMAGE...]", Short: "Display the configuration of an image", Long: `Displays the low-level information of an image identified by name or ID.`, RunE: inspectExec, diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 239da9d28..489b15086 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -10,10 +10,9 @@ import ( "time" "unicode" + "github.com/containers/common/pkg/report" "github.com/containers/image/v5/docker/reference" - "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" "github.com/pkg/errors" @@ -35,7 +34,7 @@ type listFlagType struct { var ( // Command: podman image _list_ listCmd = &cobra.Command{ - Use: "list [flags] [IMAGE]", + Use: "list [options] [IMAGE]", Aliases: []string{"ls"}, Args: cobra.MaximumNArgs(1), Short: "List images in local storage", @@ -108,7 +107,7 @@ func images(cmd *cobra.Command, args []string) error { switch { case listFlag.quiet: return writeID(imgs) - case parse.MatchesJSONFormat(listFlag.format): + case report.IsJSON(listFlag.format): return writeJSON(imgs) default: if cmd.Flag("format").Changed { diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go index cc8e71814..02f1b3b39 100644 --- a/cmd/podman/images/load.go +++ b/cmd/podman/images/load.go @@ -22,7 +22,7 @@ import ( var ( loadDescription = "Loads an image from a locally stored archive (tar file) into container storage." loadCommand = &cobra.Command{ - Use: "load [flags] [NAME[:TAG]]", + Use: "load [options] [NAME[:TAG]]", Short: "Load an image from container archive", Long: loadDescription, RunE: load, diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go index 0a972ea81..28e9264ee 100644 --- a/cmd/podman/images/mount.go +++ b/cmd/podman/images/mount.go @@ -6,7 +6,7 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -24,7 +24,7 @@ var ( ` mountCommand = &cobra.Command{ - Use: "mount [flags] [IMAGE...]", + Use: "mount [options] [IMAGE...]", Short: "Mount an image's root filesystem", Long: mountDescription, RunE: mount, @@ -80,7 +80,7 @@ func mount(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(mountOpts.Format): + case report.IsJSON(mountOpts.Format): return printJSON(reports) case mountOpts.Format == "": break // default format diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go index 8dc203ead..b6e6b9562 100644 --- a/cmd/podman/images/prune.go +++ b/cmd/podman/images/prune.go @@ -19,7 +19,7 @@ var ( If an image is not being used by a container, it will be removed from the system.` pruneCmd = &cobra.Command{ - Use: "prune", + Use: "prune [options]", Args: validate.NoArgs, Short: "Remove unused images", Long: pruneDescription, diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go index 448543b4d..35ef80f3c 100644 --- a/cmd/podman/images/pull.go +++ b/cmd/podman/images/pull.go @@ -30,7 +30,7 @@ var ( // Command: podman pull pullCmd = &cobra.Command{ - Use: "pull [flags] IMAGE", + Use: "pull [options] IMAGE", Args: cobra.ExactArgs(1), Short: "Pull an image from a registry", Long: pullDescription, @@ -84,11 +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") + flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") 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/push.go b/cmd/podman/images/push.go index 2943cccc9..718bd4e8c 100644 --- a/cmd/podman/images/push.go +++ b/cmd/podman/images/push.go @@ -29,7 +29,7 @@ var ( // Command: podman push pushCmd = &cobra.Command{ - Use: "push [flags] SOURCE [DESTINATION]", + Use: "push [options] SOURCE [DESTINATION]", Short: "Push an image to a specified destination", Long: pushDescription, RunE: imagePush, @@ -88,11 +88,9 @@ func pushFlags(flags *pflag.FlagSet) { flags.BoolVar(&pushOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("cert-dir") _ = flags.MarkHidden("compress") _ = flags.MarkHidden("quiet") - _ = flags.MarkHidden("tls-verify") } _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/images/rm.go b/cmd/podman/images/rm.go index f6e52a49e..9dddef48f 100644 --- a/cmd/podman/images/rm.go +++ b/cmd/podman/images/rm.go @@ -14,7 +14,7 @@ import ( var ( rmDescription = "Removes one or more previously pulled or locally created images." rmCmd = &cobra.Command{ - Use: "rm [flags] IMAGE [IMAGE...]", + Use: "rm [options] IMAGE [IMAGE...]", Short: "Removes one or more images from local storage", Long: rmDescription, RunE: rm, diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go index b164a2534..db1fa7159 100644 --- a/cmd/podman/images/save.go +++ b/cmd/podman/images/save.go @@ -25,7 +25,7 @@ var ( saveDescription = `Save an image to docker-archive or oci-archive on the local machine. Default is docker-archive.` saveCommand = &cobra.Command{ - Use: "save [flags] IMAGE [IMAGE...]", + Use: "save [options] IMAGE [IMAGE...]", Short: "Save image(s) to an archive", Long: saveDescription, RunE: save, diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index 8edd776ce..b1a1442a6 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -6,9 +6,9 @@ import ( "text/template" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/report" "github.com/containers/image/v5/types" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -32,7 +32,7 @@ var ( // Command: podman search searchCmd = &cobra.Command{ - Use: "search [flags] TERM", + Use: "search [options] TERM", Short: "Search registry for image", Long: searchDescription, RunE: imageSearch, diff --git a/cmd/podman/images/sign.go b/cmd/podman/images/sign.go index e331a64df..f6c1f9856 100644 --- a/cmd/podman/images/sign.go +++ b/cmd/podman/images/sign.go @@ -12,7 +12,7 @@ import ( var ( signDescription = "Create a signature file that can be used later to verify the image." signCommand = &cobra.Command{ - Use: "sign [flags] IMAGE [IMAGE...]", + Use: "sign [options] IMAGE [IMAGE...]", Short: "Sign an image", Long: signDescription, RunE: sign, diff --git a/cmd/podman/images/tree.go b/cmd/podman/images/tree.go index 0b79c2a4b..237a2ab91 100644 --- a/cmd/podman/images/tree.go +++ b/cmd/podman/images/tree.go @@ -11,7 +11,7 @@ import ( var ( treeDescription = "Prints layer hierarchy of an image in a tree format" treeCmd = &cobra.Command{ - Use: "tree [flags] IMAGE", + Use: "tree [options] IMAGE", Args: cobra.ExactArgs(1), Short: treeDescription, Long: treeDescription, diff --git a/cmd/podman/images/trust_set.go b/cmd/podman/images/trust_set.go index 878ffeea6..2e4b4fe17 100644 --- a/cmd/podman/images/trust_set.go +++ b/cmd/podman/images/trust_set.go @@ -12,7 +12,7 @@ import ( var ( setTrustDescription = "Set default trust policy or add a new trust policy for a registry" setTrustCommand = &cobra.Command{ - Use: "set [flags] REGISTRY", + Use: "set [options] REGISTRY", Short: "Set default trust policy or a new trust policy for a registry", Long: setTrustDescription, Example: "", diff --git a/cmd/podman/images/trust_show.go b/cmd/podman/images/trust_show.go index d968f6d80..ba3b4e7fb 100644 --- a/cmd/podman/images/trust_show.go +++ b/cmd/podman/images/trust_show.go @@ -14,7 +14,7 @@ import ( var ( showTrustDescription = "Display trust policy for the system" showTrustCommand = &cobra.Command{ - Use: "show [flags] [REGISTRY]", + Use: "show [options] [REGISTRY]", Short: "Display trust policy for the system", Long: showTrustDescription, RunE: showTrust, @@ -49,7 +49,7 @@ func showTrust(cmd *cobra.Command, args []string) error { return err } if showTrustOptions.Raw { - fmt.Println(report.Raw) + fmt.Println(string(report.Raw)) return nil } if showTrustOptions.JSON { diff --git a/cmd/podman/images/unmount.go b/cmd/podman/images/unmount.go index f7f6cf8e5..50dc972e8 100644 --- a/cmd/podman/images/unmount.go +++ b/cmd/podman/images/unmount.go @@ -19,7 +19,7 @@ var ( An unmount can be forced with the --force flag. ` unmountCommand = &cobra.Command{ - Use: "unmount [flags] IMAGE [IMAGE...]", + Use: "unmount [options] IMAGE [IMAGE...]", Aliases: []string{"umount"}, Short: "Unmount an image's root filesystem", Long: description, diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index 85050a497..f1d673a21 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -19,7 +19,7 @@ var ( // Command: podman _inspect_ Object_ID inspectCmd = &cobra.Command{ - Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID} [...]", + Use: "inspect [options] {CONTAINER_ID | IMAGE_ID} [...]", Short: "Display the configuration of object denoted by ID", RunE: inspectExec, Long: inspectDescription, diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index 3d1ef72aa..9c400d506 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -9,10 +9,10 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "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/sirupsen/logrus" @@ -20,12 +20,18 @@ import ( ) const ( - // ImageType is the image type. - ImageType = "image" - // ContainerType is the container type. - ContainerType = "container" // AllType can be of type ImageType or ContainerType. AllType = "all" + // ContainerType is the container type. + ContainerType = "container" + // ImageType is the image type. + ImageType = "image" + //NetworkType is the network type + NetworkType = "network" + //PodType is the pod type. + PodType = "pod" + //VolumeType is the volume type + VolumeType = "volume" ) // Pull in configured json library @@ -59,15 +65,16 @@ type inspector struct { containerEngine entities.ContainerEngine imageEngine entities.ImageEngine options entities.InspectOptions + podOptions entities.PodInspectOptions } // newInspector creates a new inspector based on the specified options. func newInspector(options entities.InspectOptions) (*inspector, error) { switch options.Type { - case ImageType, ContainerType, AllType: + case ImageType, ContainerType, AllType, PodType, NetworkType, VolumeType: // Valid types. default: - return nil, errors.Errorf("invalid type %q: must be %q, %q or %q", options.Type, ImageType, ContainerType, AllType) + return nil, errors.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", options.Type, ImageType, ContainerType, PodType, NetworkType, VolumeType, AllType) } if options.Type == ImageType { if options.Latest { @@ -77,10 +84,18 @@ func newInspector(options entities.InspectOptions) (*inspector, error) { return nil, errors.Errorf("size is not supported for type %q", ImageType) } } + if options.Type == PodType && options.Size { + return nil, errors.Errorf("size is not supported for type %q", PodType) + } + podOpts := entities.PodInspectOptions{ + Latest: options.Latest, + Format: options.Format, + } return &inspector{ containerEngine: registry.ContainerEngine(), imageEngine: registry.ImageEngine(), options: options, + podOptions: podOpts, }, nil } @@ -92,17 +107,19 @@ func (i *inspector) inspect(namesOrIDs []string) error { ctx := context.Background() if len(namesOrIDs) == 0 { - if !i.options.Latest { - return errors.New("no containers or images specified") + if !i.options.Latest && !i.options.All { + return errors.New("no names or ids specified") } } tmpType := i.options.Type if i.options.Latest { if len(namesOrIDs) > 0 { - return errors.New("--latest and containers cannot be used together") + return errors.New("--latest and arguments cannot be used together") + } + if i.options.Type == AllType { + tmpType = ContainerType // -l works with --type=all, defaults to containertype } - tmpType = ContainerType // -l works with --type=all } // Inspect - note that AllType requires us to expensively query one-by-one. @@ -132,10 +149,57 @@ func (i *inspector) inspect(namesOrIDs []string) error { for i := range ctrData { data = append(data, ctrData[i]) } + case PodType: + for _, pod := range namesOrIDs { + i.podOptions.NameOrID = pod + podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + if err != nil { + cause := errors.Cause(err) + if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) { + errs = []error{err} + } else { + return err + } + } else { + errs = nil + data = append(data, podData) + } + } + if i.podOptions.Latest { //latest means there are no names in the namesOrID array + podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + if err != nil { + cause := errors.Cause(err) + if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) { + errs = []error{err} + } else { + return err + } + } else { + errs = nil + data = append(data, podData) + } + } + case NetworkType: + networkData, allErrs, err := registry.ContainerEngine().NetworkInspect(ctx, namesOrIDs, i.options) + if err != nil { + return err + } + errs = allErrs + for i := range networkData { + data = append(data, networkData[i]) + } + case VolumeType: + volumeData, allErrs, err := i.containerEngine.VolumeInspect(ctx, namesOrIDs, i.options) + if err != nil { + return err + } + errs = allErrs + for i := range volumeData { + data = append(data, volumeData[i]) + } default: - return errors.Errorf("invalid type %q: must be %q, %q or %q", i.options.Type, ImageType, ContainerType, AllType) + return errors.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", i.options.Type, ImageType, ContainerType, PodType, NetworkType, VolumeType, AllType) } - // Always print an empty array if data == nil { data = []interface{}{} @@ -143,7 +207,7 @@ func (i *inspector) inspect(namesOrIDs []string) error { var err error switch { - case parse.MatchesJSONFormat(i.options.Format) || i.options.Format == "": + case report.IsJSON(i.options.Format) || i.options.Format == "": err = printJSON(data) default: row := inspectNormalize(i.options.Format) @@ -196,11 +260,41 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]inte if err != nil { return nil, nil, err } + if len(errs) == 0 { + data = append(data, imgData[0]) + continue + } + volumeData, errs, err := i.containerEngine.VolumeInspect(ctx, []string{name}, i.options) + if err != nil { + return nil, nil, err + } + if len(errs) == 0 { + data = append(data, volumeData[0]) + continue + } + networkData, errs, err := registry.ContainerEngine().NetworkInspect(ctx, namesOrIDs, i.options) + if err != nil { + return nil, nil, err + } + if len(errs) == 0 { + data = append(data, networkData[0]) + continue + } + i.podOptions.NameOrID = name + podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + if err != nil { + cause := errors.Cause(err) + if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) { + return nil, nil, err + } + } else { + data = append(data, podData) + continue + } if len(errs) > 0 { allErrs = append(allErrs, errors.Errorf("no such object: %q", name)) continue } - data = append(data, imgData[0]) } return data, allErrs, nil } diff --git a/cmd/podman/login.go b/cmd/podman/login.go index 1556b0dcf..a789cef33 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -20,7 +20,7 @@ type loginOptionsWrapper struct { var ( loginOptions = loginOptionsWrapper{} loginCommand = &cobra.Command{ - Use: "login [flags] [REGISTRY]", + Use: "login [options] [REGISTRY]", Short: "Login to a container registry", Long: "Login to a container registry on a specified server.", RunE: login, diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index 1c6fdfb2a..7b5615d30 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -14,7 +14,7 @@ import ( var ( logoutOptions = auth.LogoutOptions{} logoutCommand = &cobra.Command{ - Use: "logout [flags] [REGISTRY]", + Use: "logout [options] [REGISTRY]", Short: "Logout of a container registry", Long: "Remove the cached username and password for the registry.", RunE: logout, diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 007a8716c..9747769c7 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -66,6 +66,7 @@ func main() { // - rootCmd uses cobra default template not ours c.Command.SetHelpTemplate(helpTemplate) c.Command.SetUsageTemplate(usageTemplate) + c.Command.DisableFlagsInUseLine = true } } } diff --git a/cmd/podman/manifest/add.go b/cmd/podman/manifest/add.go index 128bf66a7..4b85f4c2a 100644 --- a/cmd/podman/manifest/add.go +++ b/cmd/podman/manifest/add.go @@ -25,7 +25,7 @@ type manifestAddOptsWrapper struct { var ( manifestAddOpts = manifestAddOptsWrapper{} addCmd = &cobra.Command{ - Use: "add [flags] LIST LIST", + Use: "add [options] LIST LIST", Short: "Add images to a manifest list or image index", Long: "Adds an image to a manifest list or image index.", RunE: add, @@ -56,9 +56,7 @@ func init() { flags.StringVar(&manifestAddOpts.Variant, "variant", "", "override the `Variant` of the specified image") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("cert-dir") - _ = flags.MarkHidden("tls-verify") } } diff --git a/cmd/podman/manifest/annotate.go b/cmd/podman/manifest/annotate.go index 1b720aa39..861e94034 100644 --- a/cmd/podman/manifest/annotate.go +++ b/cmd/podman/manifest/annotate.go @@ -13,7 +13,7 @@ import ( var ( manifestAnnotateOpts = entities.ManifestAnnotateOptions{} annotateCmd = &cobra.Command{ - Use: "annotate [flags] LIST IMAGE", + Use: "annotate [options] LIST IMAGE", Short: "Add or update information about an entry in a manifest list or image index", Long: "Adds or updates information about an entry in a manifest list or image index.", RunE: annotate, diff --git a/cmd/podman/manifest/create.go b/cmd/podman/manifest/create.go index b5352d24f..956946f9d 100644 --- a/cmd/podman/manifest/create.go +++ b/cmd/podman/manifest/create.go @@ -13,7 +13,7 @@ import ( var ( manifestCreateOpts = entities.ManifestCreateOptions{} createCmd = &cobra.Command{ - Use: "create [flags] LIST [IMAGE]", + Use: "create [options] LIST [IMAGE]", Short: "Create manifest list or image index", Long: "Creates manifest lists or image indexes.", RunE: create, diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go index b43e4531a..91881c1b3 100644 --- a/cmd/podman/manifest/push.go +++ b/cmd/podman/manifest/push.go @@ -22,7 +22,7 @@ type manifestPushOptsWrapper struct { var ( manifestPushOpts = manifestPushOptsWrapper{} pushCmd = &cobra.Command{ - Use: "push [flags] SOURCE DESTINATION", + Use: "push [options] SOURCE DESTINATION", Short: "Push a manifest list or image index to a registry", Long: "Pushes manifest lists and image indexes to registries.", RunE: push, @@ -51,9 +51,7 @@ func init() { flags.BoolVarP(&manifestPushOpts.Quiet, "quiet", "q", false, "don't output progress information when pushing lists") if registry.IsRemote() { - _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("cert-dir") - _ = flags.MarkHidden("tls-verify") } } diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go index 17f39bd8b..74646090d 100644 --- a/cmd/podman/networks/create.go +++ b/cmd/podman/networks/create.go @@ -14,7 +14,7 @@ import ( var ( networkCreateDescription = `create CNI networks for containers and pods` networkCreateCommand = &cobra.Command{ - Use: "create [flags] [NETWORK]", + Use: "create [options] [NETWORK]", Short: "network create", Long: networkCreateDescription, RunE: networkCreate, @@ -34,9 +34,9 @@ func networkCreateFlags(flags *pflag.FlagSet) { flags.IPNetVar(&networkCreateOptions.Range, "ip-range", net.IPNet{}, "allocate container IP from range") flags.StringVar(&networkCreateOptions.MacVLAN, "macvlan", "", "create a Macvlan connection based on this device") // TODO not supported yet - //flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver") + // flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver") // TODO enable when IPv6 is working - //flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking") + // flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking") flags.IPNetVar(&networkCreateOptions.Subnet, "subnet", net.IPNet{}, "subnet in CIDR format") flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin") } diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go index c36125948..25ee7e574 100644 --- a/cmd/podman/networks/inspect.go +++ b/cmd/podman/networks/inspect.go @@ -1,15 +1,8 @@ package network import ( - "encoding/json" - "fmt" - "os" - "text/tabwriter" - "text/template" - - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -17,17 +10,14 @@ import ( var ( networkinspectDescription = `Inspect network` networkinspectCommand = &cobra.Command{ - Use: "inspect [flags] NETWORK [NETWORK...]", + Use: "inspect [options] NETWORK [NETWORK...]", Short: "network inspect", Long: networkinspectDescription, RunE: networkInspect, Example: `podman network inspect podman`, Args: cobra.MinimumNArgs(1), } -) - -var ( - networkInspectOptions entities.NetworkInspectOptions + inspectOpts *entities.InspectOptions ) func init() { @@ -36,36 +26,13 @@ func init() { Command: networkinspectCommand, Parent: networkCmd, }) + inspectOpts = new(entities.InspectOptions) flags := networkinspectCommand.Flags() - flags.StringVarP(&networkInspectOptions.Format, "format", "f", "", "Pretty-print network to JSON or using a Go template") + flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Pretty-print network to JSON or using a Go template") } func networkInspect(_ *cobra.Command, args []string) error { - responses, err := registry.ContainerEngine().NetworkInspect(registry.Context(), args, entities.NetworkInspectOptions{}) - if err != nil { - return err - } - - switch { - case parse.MatchesJSONFormat(networkInspectOptions.Format) || networkInspectOptions.Format == "": - b, err := json.MarshalIndent(responses, "", " ") - if err != nil { - return err - } - fmt.Println(string(b)) - default: - row := report.NormalizeFormat(networkInspectOptions.Format) - // There can be more than 1 in the inspect output. - row = "{{range . }}" + row + "{{end}}" - tmpl, err := template.New("inspectNetworks").Parse(row) - if err != nil { - return err - } + inspectOpts.Type = inspect.NetworkType + return inspect.Inspect(args, *inspectOpts) - w := tabwriter.NewWriter(os.Stdout, 8, 2, 0, ' ', 0) - defer w.Flush() - - return tmpl.Execute(w, responses) - } - return nil } diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index c53f50c9f..532af631e 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -19,7 +19,7 @@ import ( var ( networklistDescription = `List networks` networklistCommand = &cobra.Command{ - Use: "ls", + Use: "ls [options]", Args: validate.NoArgs, Short: "network list", Long: networklistDescription, @@ -36,7 +36,7 @@ var ( func networkListFlags(flags *pflag.FlagSet) { // TODO enable filters based on something - //flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers") + // flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers") flags.StringVarP(&networkListOptions.Format, "format", "f", "", "Pretty-print networks to JSON or using a Go template") flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names") flags.StringVarP(&networkListOptions.Filter, "filter", "", "", "Provide filter values (e.g. 'name=podman')") diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go index 86aad43cb..3d7db941a 100644 --- a/cmd/podman/networks/rm.go +++ b/cmd/podman/networks/rm.go @@ -16,7 +16,7 @@ import ( var ( networkrmDescription = `Remove networks` networkrmCommand = &cobra.Command{ - Use: "rm [flags] NETWORK [NETWORK...]", + Use: "rm [options] NETWORK [NETWORK...]", Short: "network rm", Long: networkrmDescription, RunE: networkRm, diff --git a/cmd/podman/parse/json.go b/cmd/podman/parse/json.go index 40ac415db..d7486d0b1 100644 --- a/cmd/podman/parse/json.go +++ b/cmd/podman/parse/json.go @@ -2,7 +2,7 @@ package parse import "regexp" -var jsonFormatRegex = regexp.MustCompile(`^\s*(json|{{\s*json\s*( \.)?\s*}})\s*$`) +var jsonFormatRegex = regexp.MustCompile(`^\s*(json|{{\s*json\s*(\.)?\s*}})\s*$`) // MatchesJSONFormat test CLI --format string to be a JSON request func MatchesJSONFormat(s string) bool { diff --git a/cmd/podman/parse/json_test.go b/cmd/podman/parse/json_test.go index ec3b5664b..b3b29c93c 100644 --- a/cmd/podman/parse/json_test.go +++ b/cmd/podman/parse/json_test.go @@ -27,7 +27,7 @@ func TestMatchesJSONFormat(t *testing.T) { {"json . }}", false}, {"{{.ID }} json .", false}, {"json .", false}, - {"{{json.}}", false}, + {"{{json.}}", true}, } for _, tt := range tests { diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index 976d720ee..6072ea80c 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -32,7 +32,7 @@ var ( It creates the pod and containers described in the YAML. The containers within the pod are then started and the ID of the new Pod is output.` kubeCmd = &cobra.Command{ - Use: "kube [flags] KUBEFILE", + Use: "kube [options] KUBEFILE", Short: "Play a pod based on Kubernetes YAML.", Long: kubeDescription, RunE: kube, @@ -54,10 +54,10 @@ func init() { flags.StringVar(&kubeOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.StringVar(&kubeOptions.Network, "network", "", "Connect pod to CNI network(s)") flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") + flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + flags.StringVar(&kubeOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") if !registry.IsRemote() { - flags.StringVar(&kubeOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&kubeOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") - flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") flags.StringVar(&kubeOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.StringVar(&kubeOptions.SeccompProfileRoot, "seccomp-profile-root", defaultSeccompRoot, "Directory path for seccomp profiles") flags.StringSliceVar(&kubeOptions.ConfigMaps, "configmap", []string{}, "`Pathname` of a YAML file containing a kubernetes configmap") diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index ac6d83edd..efa84dcb4 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -27,7 +27,7 @@ var ( 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'.` createCommand = &cobra.Command{ - Use: "create", + Use: "create [options]", Args: validate.NoArgs, Short: "Create a new empty pod", Long: podCreateDescription, diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index cad15d10f..7f81ba8fb 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -7,9 +7,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -26,7 +25,7 @@ var ( By default, this will render all results in a JSON array.`) inspectCmd = &cobra.Command{ - Use: "inspect [flags] POD [POD...]", + Use: "inspect [options] POD [POD...]", Short: "Displays a pod configuration", Long: inspectDescription, RunE: inspect, @@ -62,8 +61,9 @@ func inspect(cmd *cobra.Command, args []string) error { return err } - if parse.MatchesJSONFormat(inspectOptions.Format) { + if report.IsJSON(inspectOptions.Format) { enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") return enc.Encode(responses) } diff --git a/cmd/podman/pods/kill.go b/cmd/podman/pods/kill.go index d666d7537..1902a2c80 100644 --- a/cmd/podman/pods/kill.go +++ b/cmd/podman/pods/kill.go @@ -16,7 +16,7 @@ var ( The default signal is SIGKILL, or any signal specified with option --signal.` killCommand = &cobra.Command{ - Use: "kill [flags] POD [POD...]", + Use: "kill [options] POD [POD...]", Short: "Send the specified signal or SIGKILL to containers in pod", Long: podKillDescription, RunE: kill, diff --git a/cmd/podman/pods/pause.go b/cmd/podman/pods/pause.go index 591378983..bba26f90d 100644 --- a/cmd/podman/pods/pause.go +++ b/cmd/podman/pods/pause.go @@ -16,7 +16,7 @@ var ( All running containers within each specified pod will then be paused.` pauseCommand = &cobra.Command{ - Use: "pause [flags] POD [POD...]", + Use: "pause [options] POD [POD...]", Short: "Pause one or more pods", Long: podPauseDescription, RunE: pause, diff --git a/cmd/podman/pods/prune.go b/cmd/podman/pods/prune.go index f13d95ae9..e3eae3f71 100644 --- a/cmd/podman/pods/prune.go +++ b/cmd/podman/pods/prune.go @@ -23,7 +23,7 @@ var ( pruneDescription = fmt.Sprintf(`podman pod prune Removes all exited pods`) pruneCommand = &cobra.Command{ - Use: "prune [flags]", + Use: "prune [options]", Args: validate.NoArgs, Short: "Remove all stopped pods and their containers", Long: pruneDescription, diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 0013cca02..688108c1a 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -10,9 +10,8 @@ import ( "text/template" "time" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" @@ -25,7 +24,7 @@ var ( // Command: podman pod _ps_ psCmd = &cobra.Command{ - Use: "ps", + Use: "ps [options]", Aliases: []string{"ls", "list"}, Short: "List pods", Long: psDescription, @@ -85,7 +84,7 @@ func pods(cmd *cobra.Command, _ []string) error { } switch { - case parse.MatchesJSONFormat(psInput.Format): + case report.IsJSON(psInput.Format): b, err := json.MarshalIndent(responses, "", " ") if err != nil { return err diff --git a/cmd/podman/pods/restart.go b/cmd/podman/pods/restart.go index 11e8b4ebc..119b4ddee 100644 --- a/cmd/podman/pods/restart.go +++ b/cmd/podman/pods/restart.go @@ -16,7 +16,7 @@ var ( All of the containers within each of the specified pods will be restarted. If a container in a pod is not currently running it will be started.` restartCommand = &cobra.Command{ - Use: "restart [flags] POD [POD...]", + Use: "restart [options] POD [POD...]", Short: "Restart one or more pods", Long: podRestartDescription, RunE: restart, diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index 2975db3e8..714e075e2 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -28,7 +28,7 @@ var ( 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.`) rmCommand = &cobra.Command{ - Use: "rm [flags] POD [POD...]", + Use: "rm [options] POD [POD...]", Short: "Remove one or more pods", Long: podRmDescription, RunE: rm, diff --git a/cmd/podman/pods/start.go b/cmd/podman/pods/start.go index e2ca3fd41..28ee4769a 100644 --- a/cmd/podman/pods/start.go +++ b/cmd/podman/pods/start.go @@ -24,7 +24,7 @@ var ( All containers defined in the pod will be started.` startCommand = &cobra.Command{ - Use: "start [flags] POD [POD...]", + Use: "start [options] POD [POD...]", Short: "Start one or more pods", Long: podStartDescription, RunE: start, diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index 2f59e4e47..338f13d3e 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -9,9 +9,8 @@ import ( "time" "github.com/buger/goterm" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -33,7 +32,7 @@ var ( statsDescription = `Display the containers' resource-usage statistics of one or more running pod` // Command: podman pod _pod_ statsCmd = &cobra.Command{ - Use: "stats [flags] [POD...]", + Use: "stats [options] [POD...]", Short: "Display a live stream of resource usage statistics for the containers in one or more pods", Long: statsDescription, RunE: stats, @@ -66,7 +65,7 @@ func stats(cmd *cobra.Command, args []string) error { } row := report.NormalizeFormat(statsOptions.Format) - doJSON := parse.MatchesJSONFormat(row) + doJSON := report.IsJSON(row) headers := report.Headers(entities.PodStatsReport{}, map[string]string{ "CPU": "CPU %", diff --git a/cmd/podman/pods/stop.go b/cmd/podman/pods/stop.go index 20c3f59bf..a2a9b0b57 100644 --- a/cmd/podman/pods/stop.go +++ b/cmd/podman/pods/stop.go @@ -29,7 +29,7 @@ var ( This command will stop all running containers in each of the specified pods.` stopCommand = &cobra.Command{ - Use: "stop [flags] POD [POD...]", + Use: "stop [options] POD [POD...]", Short: "Stop one or more pods", Long: podStopDescription, RunE: stop, diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go index 9877db404..0ffa724da 100644 --- a/cmd/podman/pods/top.go +++ b/cmd/podman/pods/top.go @@ -23,7 +23,7 @@ var ( topOptions = entities.PodTopOptions{} topCommand = &cobra.Command{ - Use: "top [flags] POD [FORMAT-DESCRIPTORS|ARGS...]", + Use: "top [options] POD [FORMAT-DESCRIPTORS|ARGS...]", Short: "Display the running processes of containers in a pod", Long: topDescription, RunE: top, diff --git a/cmd/podman/pods/unpause.go b/cmd/podman/pods/unpause.go index 8474da57e..15b30db14 100644 --- a/cmd/podman/pods/unpause.go +++ b/cmd/podman/pods/unpause.go @@ -16,7 +16,7 @@ var ( The pod name or ID can be used.` unpauseCommand = &cobra.Command{ - Use: "unpause [flags] POD [POD...]", + Use: "unpause [options] POD [POD...]", Short: "Unpause one or more pods", Long: podUnpauseDescription, RunE: unpause, diff --git a/cmd/podman/report/format.go b/cmd/podman/report/format.go deleted file mode 100644 index 32d92bec5..000000000 --- a/cmd/podman/report/format.go +++ /dev/null @@ -1,68 +0,0 @@ -package report - -import ( - "reflect" - "strings" -) - -// tableReplacer will remove 'table ' prefix and clean up tabs -var tableReplacer = strings.NewReplacer( - "table ", "", - `\t`, "\t", - `\n`, "\n", - " ", "\t", -) - -// escapedReplacer will clean up escaped characters from CLI -var escapedReplacer = strings.NewReplacer( - `\t`, "\t", - `\n`, "\n", -) - -// NormalizeFormat reads given go template format provided by CLI and munges it into what we need -func NormalizeFormat(format string) string { - f := format - // two replacers used so we only remove the prefix keyword `table` - if strings.HasPrefix(f, "table ") { - f = tableReplacer.Replace(f) - } else { - f = escapedReplacer.Replace(format) - } - - if !strings.HasSuffix(f, "\n") { - f += "\n" - } - - return f -} - -// Headers queries the interface for field names -func Headers(object interface{}, overrides map[string]string) []map[string]string { - value := reflect.ValueOf(object) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - // Column header will be field name upper-cased. - headers := make(map[string]string, value.NumField()) - for i := 0; i < value.Type().NumField(); i++ { - field := value.Type().Field(i) - // Recurse to find field names from promoted structs - if field.Type.Kind() == reflect.Struct && field.Anonymous { - h := Headers(reflect.New(field.Type).Interface(), nil) - for k, v := range h[0] { - headers[k] = v - } - continue - } - headers[field.Name] = strings.ToUpper(field.Name) - } - - if len(overrides) > 0 { - // Override column header as provided - for k, v := range overrides { - headers[k] = strings.ToUpper(v) - } - } - return []map[string]string{headers} -} diff --git a/cmd/podman/report/format_test.go b/cmd/podman/report/format_test.go deleted file mode 100644 index 7dd62e899..000000000 --- a/cmd/podman/report/format_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package report - -import ( - "strings" - "testing" -) - -func TestNormalizeFormat(t *testing.T) { - cases := []struct { - format string - expected string - }{ - {"table {{.ID}}", "{{.ID}}\n"}, - {"table {{.ID}} {{.C}}", "{{.ID}}\t{{.C}}\n"}, - {"{{.ID}}", "{{.ID}}\n"}, - {"{{.ID}}\n", "{{.ID}}\n"}, - {"{{.ID}} {{.C}}", "{{.ID}} {{.C}}\n"}, - {"\t{{.ID}}", "\t{{.ID}}\n"}, - {`\t` + "{{.ID}}", "\t{{.ID}}\n"}, - {"table {{.ID}}\t{{.C}}", "{{.ID}}\t{{.C}}\n"}, - {"{{.ID}} table {{.C}}", "{{.ID}} table {{.C}}\n"}, - } - for _, tc := range cases { - tc := tc - - label := strings.ReplaceAll(tc.format, " ", "<sp>") - t.Run("NormalizeFormat/"+label, func(t *testing.T) { - t.Parallel() - actual := NormalizeFormat(tc.format) - if actual != tc.expected { - t.Errorf("Expected %q, actual %q", tc.expected, actual) - } - }) - } -} diff --git a/cmd/podman/report/report.go b/cmd/podman/report/report.go deleted file mode 100644 index 2c4f2e1fd..000000000 --- a/cmd/podman/report/report.go +++ /dev/null @@ -1,6 +0,0 @@ -package report - -import "github.com/containers/podman/v2/cmd/podman/registry" - -// Pull in configured json library -var json = registry.JSONLibrary() diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 6293fa17d..b59b8341a 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -38,7 +38,7 @@ Description: // command should not use this. const usageTemplate = `Usage:{{if (and .Runnable (not .HasAvailableSubCommands))}} {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} - {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} + {{.UseLine}} [command]{{end}}{{if gt (len .Aliases) 0}} Aliases: {{.NameAndAliases}}{{end}}{{if .HasExample}} @@ -49,24 +49,24 @@ Examples: Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} -Flags: +Options: {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} {{end}} ` var ( rootCmd = &cobra.Command{ - Use: path.Base(os.Args[0]), - Long: "Manage pods, containers and images", - SilenceUsage: true, - SilenceErrors: true, - TraverseChildren: true, - PersistentPreRunE: persistentPreRunE, - RunE: validate.SubCommandExists, - PersistentPostRunE: persistentPostRunE, - Version: version.Version.String(), + Use: path.Base(os.Args[0]) + " [options]", + Long: "Manage pods, containers and images", + SilenceUsage: true, + SilenceErrors: true, + TraverseChildren: true, + PersistentPreRunE: persistentPreRunE, + RunE: validate.SubCommandExists, + PersistentPostRunE: persistentPostRunE, + Version: version.Version.String(), + DisableFlagsInUseLine: true, } - logLevels = []string{"debug", "info", "warn", "error", "fatal", "panic"} logLevel = "error" useSyslog bool @@ -81,6 +81,7 @@ func init() { ) rootFlags(rootCmd, registry.PodmanConfig()) + rootCmd.SetUsageTemplate(usageTemplate) } func Execute() { diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go index df036af1a..0d81a64ca 100644 --- a/cmd/podman/system/connection/add.go +++ b/cmd/podman/system/connection/add.go @@ -27,7 +27,7 @@ const schemaPattern = "^[A-Za-z][A-Za-z0-9+.-]*:" var ( addCmd = &cobra.Command{ - Use: "add [flags] NAME DESTINATION", + Use: "add [options] NAME DESTINATION", Args: cobra.ExactArgs(2), Short: "Record destination for the Podman service", Long: `Add destination to podman configuration. diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go index b262c8478..b11167938 100644 --- a/cmd/podman/system/df.go +++ b/cmd/podman/system/df.go @@ -2,14 +2,13 @@ package system import ( "fmt" - "io" "os" - "strconv" "strings" "text/tabwriter" "text/template" "time" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -24,7 +23,7 @@ var ( Show podman disk usage ` dfSystemCommand = &cobra.Command{ - Use: "df", + Use: "df [options]", Args: validate.NoArgs, Short: "Show podman disk usage", Long: dfSystemDescription, @@ -52,35 +51,21 @@ func df(cmd *cobra.Command, args []string) error { if err != nil { return err } + + w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + if dfOptions.Verbose { - return printVerbose(reports) + return printVerbose(cmd, w, reports) } - return printSummary(reports, dfOptions.Format) + return printSummary(w, cmd, reports) } -func printSummary(reports *entities.SystemDfReport, userFormat string) error { - +func printSummary(w *tabwriter.Writer, cmd *cobra.Command, reports *entities.SystemDfReport) error { var ( dfSummaries []*dfSummary active int size, reclaimable int64 - format = "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n" - w io.Writer = os.Stdout ) - - // Images - if len(userFormat) > 0 { - if !strings.HasSuffix(userFormat, `\n`) { - userFormat += `\n` - } - // should be Unquoto from cmd line - userFormat, err := strconv.Unquote(`"` + userFormat + `"`) - if err != nil { - return err - } - format = userFormat - } - for _, i := range reports.Images { if i.Containers > 0 { active++ @@ -90,7 +75,6 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error { reclaimable += i.Size } } - imageSummary := dfSummary{ Type: "Images", Total: len(reports.Images), @@ -101,7 +85,6 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error { dfSummaries = append(dfSummaries, &imageSummary) // Containers - var ( conActive int conSize, conReclaimable int64 @@ -114,7 +97,6 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error { } conSize += c.RWSize } - containerSummary := dfSummary{ Type: "Containers", Total: len(reports.Containers), @@ -122,7 +104,6 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error { size: conSize, reclaimable: conReclaimable, } - dfSummaries = append(dfSummaries, &containerSummary) // Volumes @@ -143,78 +124,94 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error { size: volumesSize, reclaimable: volumesReclaimable, } - dfSummaries = append(dfSummaries, &volumeSummary) - headers := "TYPE\tTOTAL\tACTIVE\tSIZE\tRECLAIMABLE\n" - format = "{{range . }}" + format + "{{end}}" - if len(userFormat) == 0 { - format = headers + format + // need to give un-exported fields + hdrs := report.Headers(dfSummary{}, map[string]string{ + "Size": "SIZE", + "Reclaimable": "RECLAIMABLE", + }) + + row := "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n" + if cmd.Flags().Changed("format") { + row = report.NormalizeFormat(dfOptions.Format) } - return writeTemplate(w, format, dfSummaries) + row = "{{range . }}" + row + "{{end}}" + + return writeTemplate(cmd, w, hdrs, row, dfSummaries) } -func printVerbose(reports *entities.SystemDfReport) error { - var ( - w io.Writer = os.Stdout - ) +func printVerbose(cmd *cobra.Command, w *tabwriter.Writer, reports *entities.SystemDfReport) error { + defer w.Flush() // Images - fmt.Print("\nImages space usage:\n\n") + fmt.Fprint(w, "Images space usage:\n\n") // convert to dfImage for output dfImages := make([]*dfImage, 0, len(reports.Images)) for _, d := range reports.Images { dfImages = append(dfImages, &dfImage{SystemDfImageReport: d}) } - imageHeaders := "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE\tSHARED SIZE\tUNIQUE SIZE\tCONTAINERS\n" + hdrs := report.Headers(entities.SystemDfImageReport{}, map[string]string{ + "ImageID": "IMAGE ID", + "SharedSize": "SHARED SIZE", + "UniqueSize": "UNIQUE SIZE", + }) imageRow := "{{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}\n" - format := imageHeaders + "{{range . }}" + imageRow + "{{end}}" - if err := writeTemplate(w, format, dfImages); err != nil { + format := "{{range . }}" + imageRow + "{{end}}" + if err := writeTemplate(cmd, w, hdrs, format, dfImages); err != nil { return nil } // Containers - fmt.Print("\nContainers space usage:\n\n") + fmt.Fprint(w, "\nContainers space usage:\n\n") // convert to dfContainers for output dfContainers := make([]*dfContainer, 0, len(reports.Containers)) for _, d := range reports.Containers { dfContainers = append(dfContainers, &dfContainer{SystemDfContainerReport: d}) } - containerHeaders := "CONTAINER ID\tIMAGE\tCOMMAND\tLOCAL VOLUMES\tSIZE\tCREATED\tSTATUS\tNAMES\n" + hdrs = report.Headers(entities.SystemDfContainerReport{}, map[string]string{ + "ContainerID": "CONTAINER ID", + "LocalVolumes": "LOCAL VOLUMES", + "RWSize": "SIZE", + }) containerRow := "{{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.RWSize}}\t{{.Created}}\t{{.Status}}\t{{.Names}}\n" - format = containerHeaders + "{{range . }}" + containerRow + "{{end}}" - if err := writeTemplate(w, format, dfContainers); err != nil { + format = "{{range . }}" + containerRow + "{{end}}" + if err := writeTemplate(cmd, w, hdrs, format, dfContainers); err != nil { return nil } // Volumes - fmt.Print("\nLocal Volumes space usage:\n\n") + fmt.Fprint(w, "\nLocal Volumes space usage:\n\n") dfVolumes := make([]*dfVolume, 0, len(reports.Volumes)) // convert to dfVolume for output for _, d := range reports.Volumes { dfVolumes = append(dfVolumes, &dfVolume{SystemDfVolumeReport: d}) } - volumeHeaders := "VOLUME NAME\tLINKS\tSIZE\n" + hdrs = report.Headers(entities.SystemDfVolumeReport{}, map[string]string{ + "VolumeName": "VOLUME NAME", + }) volumeRow := "{{.VolumeName}}\t{{.Links}}\t{{.Size}}\n" - format = volumeHeaders + "{{range . }}" + volumeRow + "{{end}}" - return writeTemplate(w, format, dfVolumes) + format = "{{range . }}" + volumeRow + "{{end}}" + return writeTemplate(cmd, w, hdrs, format, dfVolumes) } -func writeTemplate(w io.Writer, format string, output interface{}) error { - tmpl, err := template.New("dfout").Parse(format) +func writeTemplate(cmd *cobra.Command, w *tabwriter.Writer, hdrs []map[string]string, format string, + output interface{}) error { + defer w.Flush() + + tmpl, err := template.New("df").Parse(format) if err != nil { return err } - w = tabwriter.NewWriter(w, 8, 2, 2, ' ', 0) //nolint - if err := tmpl.Execute(w, output); err != nil { - return err - } - if flusher, ok := w.(interface{ Flush() error }); ok { - return flusher.Flush() + + if !cmd.Flags().Changed("format") { + if err := tmpl.Execute(w, hdrs); err != nil { + return err + } } - return nil + return tmpl.Execute(w, output) } type dfImage struct { diff --git a/cmd/podman/system/events.go b/cmd/podman/system/events.go index 04e948f30..368cd41a6 100644 --- a/cmd/podman/system/events.go +++ b/cmd/podman/system/events.go @@ -1,13 +1,12 @@ package system import ( - "bufio" "context" + "fmt" "os" - "strings" "text/template" - "github.com/containers/buildah/pkg/formats" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/events" @@ -21,13 +20,14 @@ var ( By default, streaming mode is used, printing new events as they occur. Previous events can be listed via --since and --until.` eventsCommand = &cobra.Command{ - Use: "events", + Use: "events [options]", Args: validate.NoArgs, Short: "Show podman events", Long: eventsDescription, RunE: eventsCmd, Example: `podman events podman events --filter event=create + podman events --format {{.Image}} podman events --since 1h30s`, } ) @@ -51,60 +51,54 @@ func init() { _ = flags.MarkHidden("stream") } -func eventsCmd(cmd *cobra.Command, args []string) error { - var ( - err error - eventsError error - tmpl *template.Template - ) - if strings.Join(strings.Fields(eventFormat), "") == "{{json.}}" { - eventFormat = formats.JSONString - } - if eventFormat != formats.JSONString { - tmpl, err = template.New("events").Parse(eventFormat) - if err != nil { - return err - } - } +func eventsCmd(cmd *cobra.Command, _ []string) error { if len(eventOptions.Since) > 0 || len(eventOptions.Until) > 0 { eventOptions.FromStart = true } - eventChannel := make(chan *events.Event) + eventChannel := make(chan *events.Event, 1) eventOptions.EventChan = eventChannel + errChannel := make(chan error) + + var ( + tmpl *template.Template + doJSON bool + ) + + if cmd.Flags().Changed("format") { + doJSON = report.IsJSON(eventFormat) + if !doJSON { + var err error + tmpl, err = template.New("events").Parse(eventFormat) + if err != nil { + return err + } + } + } go func() { - eventsError = registry.ContainerEngine().Events(context.Background(), eventOptions) + err := registry.ContainerEngine().Events(context.Background(), eventOptions) + errChannel <- err }() - if eventsError != nil { - return eventsError - } - w := bufio.NewWriter(os.Stdout) for event := range eventChannel { switch { - case eventFormat == formats.JSONString: + case event == nil: + // no-op + case doJSON: jsonStr, err := event.ToJSONString() if err != nil { return errors.Wrapf(err, "unable to format json") } - if _, err := w.Write([]byte(jsonStr)); err != nil { - return err - } - case len(eventFormat) > 0: - if err := tmpl.Execute(w, event); err != nil { + fmt.Println(jsonStr) + case cmd.Flags().Changed("format"): + if err := tmpl.Execute(os.Stdout, event); err != nil { return err } + fmt.Println("") default: - if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil { - return err - } - } - if _, err := w.Write([]byte("\n")); err != nil { - return err - } - if err := w.Flush(); err != nil { - return err + fmt.Println(event.ToHumanReadable()) } } - return nil + + return <-errChannel } diff --git a/cmd/podman/system/info.go b/cmd/podman/system/info.go index 3e3c99488..dece6b37e 100644 --- a/cmd/podman/system/info.go +++ b/cmd/podman/system/info.go @@ -5,7 +5,7 @@ import ( "os" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -20,7 +20,7 @@ var ( Useful for the user and when reporting issues. ` infoCommand = &cobra.Command{ - Use: "info", + Use: "info [options]", Args: validate.NoArgs, Long: infoDescription, Short: "Display podman system information", @@ -69,26 +69,25 @@ func info(cmd *cobra.Command, args []string) error { return err } - if parse.MatchesJSONFormat(inFormat) { + switch { + case report.IsJSON(inFormat): b, err := json.MarshalIndent(info, "", " ") if err != nil { return err } fmt.Println(string(b)) - return nil - } - if !cmd.Flag("format").Changed { + case cmd.Flags().Changed("format"): + tmpl, err := template.New("info").Parse(inFormat) + if err != nil { + return err + } + return tmpl.Execute(os.Stdout, info) + default: b, err := yaml.Marshal(info) if err != nil { return err } fmt.Println(string(b)) - return nil - } - tmpl, err := template.New("info").Parse(inFormat) - if err != nil { - return err } - err = tmpl.Execute(os.Stdout, info) - return err + return nil } diff --git a/cmd/podman/system/migrate.go b/cmd/podman/system/migrate.go index 018701fc9..7870df60b 100644 --- a/cmd/podman/system/migrate.go +++ b/cmd/podman/system/migrate.go @@ -21,7 +21,7 @@ var ( ` migrateCommand = &cobra.Command{ - Use: "migrate", + Use: "migrate [options]", Args: validate.NoArgs, Short: "Migrate containers", Long: migrateDescription, diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go index f34df3698..a229b06b0 100644 --- a/cmd/podman/system/prune.go +++ b/cmd/podman/system/prune.go @@ -25,7 +25,7 @@ var ( `) pruneCommand = &cobra.Command{ - Use: "prune [flags]", + Use: "prune [options]", Short: "Remove unused data", Args: validate.NoArgs, Long: pruneDescription, diff --git a/cmd/podman/system/reset.go b/cmd/podman/system/reset.go index a7a42ea58..0b04c6ee0 100644 --- a/cmd/podman/system/reset.go +++ b/cmd/podman/system/reset.go @@ -22,7 +22,7 @@ var ( All containers will be stopped and removed, and all images, volumes and container content will be removed. ` systemResetCommand = &cobra.Command{ - Use: "reset", + Use: "reset [options]", Args: validate.NoArgs, Short: "Reset podman storage", Long: systemResetDescription, diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index 7c692b07e..2a2b1984f 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -26,7 +26,7 @@ Enable a listening service for API access to Podman commands. ` srvCmd = &cobra.Command{ - Use: "service [flags] [URI]", + Use: "service [options] [URI]", Args: cobra.MaximumNArgs(1), Short: "Run API service", Long: srvDescription, diff --git a/cmd/podman/system/varlink.go b/cmd/podman/system/varlink.go index 57e7e4ba6..89669d51a 100644 --- a/cmd/podman/system/varlink.go +++ b/cmd/podman/system/varlink.go @@ -16,7 +16,7 @@ var ( Tools speaking varlink protocol can remotely manage pods, containers and images. ` varlinkCmd = &cobra.Command{ - Use: "varlink [flags] [URI]", + Use: "varlink [options] [URI]", Args: cobra.MinimumNArgs(1), Short: "Run varlink interface", Long: varlinkDescription, diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index 9da7da54a..b790a7511 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -6,9 +6,9 @@ import ( "os" "strings" "text/tabwriter" + "text/template" - "github.com/containers/buildah/pkg/formats" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" @@ -18,7 +18,7 @@ import ( var ( versionCommand = &cobra.Command{ - Use: "version", + Use: "version [options]", Args: validate.NoArgs, Short: "Display the Podman Version Information", RunE: version, @@ -41,31 +41,38 @@ func version(cmd *cobra.Command, args []string) error { return err } - switch { - case parse.MatchesJSONFormat(versionFormat): + if report.IsJSON(versionFormat) { s, err := json.MarshalToString(versions) if err != nil { return err } - _, err = io.WriteString(os.Stdout, s+"\n") - return err - case cmd.Flag("format").Changed: - out := formats.StdoutTemplate{Output: versions, Template: versionFormat} - err := out.Out() + fmt.Println(s) + return nil + } + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + defer w.Flush() + + if cmd.Flag("format").Changed { + row := report.NormalizeFormat(versionFormat) + tmpl, err := template.New("version 2.0.0").Parse(row) if err != nil { + return err + } + if err := tmpl.Execute(w, versions); err != nil { // On Failure, assume user is using older version of podman version --format and check client - versionFormat = strings.Replace(versionFormat, ".Server.", ".", 1) - out = formats.StdoutTemplate{Output: versions.Client, Template: versionFormat} - if err1 := out.Out(); err1 != nil { + row = strings.Replace(row, ".Server.", ".", 1) + tmpl, err := template.New("version 1.0.0").Parse(row) + if err != nil { + return err + } + if err := tmpl.Execute(w, versions.Client); err != nil { return err } } return nil } - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - defer w.Flush() - if versions.Server != nil { if _, err := fmt.Fprintf(w, "Client:\n"); err != nil { return err @@ -81,13 +88,13 @@ func version(cmd *cobra.Command, args []string) error { return nil } -func formatVersion(writer io.Writer, version *define.Version) { - fmt.Fprintf(writer, "Version:\t%s\n", version.Version) - fmt.Fprintf(writer, "API Version:\t%s\n", version.APIVersion) - fmt.Fprintf(writer, "Go Version:\t%s\n", version.GoVersion) +func formatVersion(w io.Writer, version *define.Version) { + fmt.Fprintf(w, "Version:\t%s\n", version.Version) + fmt.Fprintf(w, "API Version:\t%s\n", version.APIVersion) + fmt.Fprintf(w, "Go Version:\t%s\n", version.GoVersion) if version.GitCommit != "" { - fmt.Fprintf(writer, "Git Commit:\t%s\n", version.GitCommit) + fmt.Fprintf(w, "Git Commit:\t%s\n", version.GitCommit) } - fmt.Fprintf(writer, "Built:\t%s\n", version.BuiltTime) - fmt.Fprintf(writer, "OS/Arch:\t%s\n", version.OsArch) + fmt.Fprintf(w, "Built:\t%s\n", version.BuiltTime) + fmt.Fprintf(w, "OS/Arch:\t%s\n", version.OsArch) } diff --git a/cmd/podman/utils/alias.go b/cmd/podman/utils/alias.go index ff31e82ea..10b96fa98 100644 --- a/cmd/podman/utils/alias.go +++ b/cmd/podman/utils/alias.go @@ -21,6 +21,8 @@ func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName { name = "time" case "namespace": name = "ns" + case "storage": + name = "external" } return pflag.NormalizedName(name) } diff --git a/cmd/podman/volumes/create.go b/cmd/podman/volumes/create.go index 934a552dc..a54530183 100644 --- a/cmd/podman/volumes/create.go +++ b/cmd/podman/volumes/create.go @@ -15,7 +15,7 @@ var ( createDescription = `If using the default driver, "local", the volume will be created on the host in the volumes directory under container storage.` createCommand = &cobra.Command{ - Use: "create [flags] [NAME]", + Use: "create [options] [NAME]", Short: "Create a new volume", Long: createDescription, RunE: create, diff --git a/cmd/podman/volumes/inspect.go b/cmd/podman/volumes/inspect.go index ce24ac4e5..c6edcf809 100644 --- a/cmd/podman/volumes/inspect.go +++ b/cmd/podman/volumes/inspect.go @@ -1,17 +1,11 @@ package volumes import ( - "fmt" - "os" - "strings" - "text/template" - - "github.com/containers/buildah/pkg/formats" + "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "golang.org/x/net/context" ) var ( @@ -19,10 +13,10 @@ var ( Use a Go template to change the format from JSON.` inspectCommand = &cobra.Command{ - Use: "inspect [flags] VOLUME [VOLUME...]", + Use: "inspect [options] VOLUME [VOLUME...]", Short: "Display detailed information on one or more volumes", Long: volumeInspectDescription, - RunE: inspect, + RunE: volumeInspect, Example: `podman volume inspect myvol podman volume inspect --all podman volume inspect --format "{{.Driver}} {{.Scope}}" myvol`, @@ -30,8 +24,7 @@ var ( ) var ( - inspectOpts = entities.VolumeInspectOptions{} - inspectFormat string + inspectOpts *entities.InspectOptions ) func init() { @@ -40,39 +33,16 @@ func init() { Command: inspectCommand, Parent: volumeCmd, }) + inspectOpts = new(entities.InspectOptions) flags := inspectCommand.Flags() flags.BoolVarP(&inspectOpts.All, "all", "a", false, "Inspect all volumes") - flags.StringVarP(&inspectFormat, "format", "f", "json", "Format volume output using Go template") + flags.StringVarP(&inspectOpts.Format, "format", "f", "json", "Format volume output using Go template") } -func inspect(cmd *cobra.Command, args []string) error { +func volumeInspect(cmd *cobra.Command, args []string) error { if (inspectOpts.All && len(args) > 0) || (!inspectOpts.All && len(args) < 1) { return errors.New("provide one or more volume names or use --all") } - responses, err := registry.ContainerEngine().VolumeInspect(context.Background(), args, inspectOpts) - if err != nil { - return err - } - switch inspectFormat { - case "", formats.JSONString: - jsonOut, err := json.MarshalIndent(responses, "", " ") - if err != nil { - return errors.Wrapf(err, "error marshalling inspect JSON") - } - fmt.Println(string(jsonOut)) - default: - if !strings.HasSuffix(inspectFormat, "\n") { - inspectFormat += "\n" - } - format := "{{range . }}" + inspectFormat + "{{end}}" - tmpl, err := template.New("volumeInspect").Parse(format) - if err != nil { - return err - } - if err := tmpl.Execute(os.Stdout, responses); err != nil { - return err - } - } - return nil - + inspectOpts.Type = inspect.VolumeType + return inspect.Inspect(args, *inspectOpts) } diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go index 18765a499..b3b2b8ea1 100644 --- a/cmd/podman/volumes/list.go +++ b/cmd/podman/volumes/list.go @@ -8,9 +8,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -24,7 +23,7 @@ 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.` lsCommand = &cobra.Command{ - Use: "ls", + Use: "ls [options]", Aliases: []string{"list"}, Args: validate.NoArgs, Short: "List volumes", @@ -75,7 +74,7 @@ func list(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(cliOpts.Format): + case report.IsJSON(cliOpts.Format): return outputJSON(responses) case len(responses) < 1: return nil diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go index 78c258bec..79e6f1a54 100644 --- a/cmd/podman/volumes/prune.go +++ b/cmd/podman/volumes/prune.go @@ -21,7 +21,7 @@ var ( The command prompts for confirmation which can be overridden with the --force flag. Note all data will be destroyed.` pruneCommand = &cobra.Command{ - Use: "prune", + Use: "prune [options]", Args: validate.NoArgs, Short: "Remove all unused volumes", Long: volumePruneDescription, diff --git a/cmd/podman/volumes/rm.go b/cmd/podman/volumes/rm.go index 4c960d4d5..4026764ac 100644 --- a/cmd/podman/volumes/rm.go +++ b/cmd/podman/volumes/rm.go @@ -18,7 +18,7 @@ var ( By default only volumes that are not being used by any containers will be removed. To remove the volumes anyways, use the --force flag.` rmCommand = &cobra.Command{ - Use: "rm [flags] VOLUME [VOLUME...]", + Use: "rm [options] VOLUME [VOLUME...]", Aliases: []string{"remove"}, Short: "Remove one or more volumes", Long: volumeRmDescription, |