diff options
Diffstat (limited to 'cmd')
88 files changed, 994 insertions, 498 deletions
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go index 8b0fd7ffd..ed175bdf4 100644 --- a/cmd/podman/attach.go +++ b/cmd/podman/attach.go @@ -28,14 +28,13 @@ var ( func init() { attachCommand.Command = _attachCommand + attachCommand.SetUsageTemplate(UsageTemplate()) flags := attachCommand.Flags() flags.StringVar(&attachCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _") flags.BoolVar(&attachCommand.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false") flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true)") flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - - rootCmd.AddCommand(attachCommand.Command) } func attachCmd(c *cliconfig.AttachValues) error { @@ -75,7 +74,7 @@ func attachCmd(c *cliconfig.AttachValues) error { inputStream = nil } - if err := startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, c.SigProxy, false); err != nil { + if err := startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, c.SigProxy, false); err != nil && errors.Cause(err) != libpod.ErrDetach { return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) } diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 20f621e84..30a734377 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "path/filepath" "strings" @@ -9,10 +8,9 @@ import ( "github.com/containers/buildah" "github.com/containers/buildah/imagebuildah" buildahcli "github.com/containers/buildah/pkg/cli" - "github.com/containers/buildah/pkg/parse" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/libpod/adapter" + "github.com/docker/go-units" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -48,6 +46,7 @@ var ( func init() { buildCommand.Command = _buildCommand + buildCommand.SetUsageTemplate(UsageTemplate()) flags := buildCommand.Flags() flags.SetInterspersed(false) @@ -58,8 +57,6 @@ func init() { flags.AddFlagSet(&budFlags) flags.AddFlagSet(&fromAndBugFlags) - - rootCmd.AddCommand(buildCommand.Command) } func getDockerfiles(files []string) []string { @@ -77,7 +74,6 @@ func getDockerfiles(files []string) []string { func buildCmd(c *cliconfig.BuildValues) error { // The following was taken directly from containers/buildah/cmd/bud.go // TODO Find a away to vendor more of this in rather than copy from bud - output := "" tags := []string{} if c.Flag("tag").Changed { @@ -87,6 +83,7 @@ func buildCmd(c *cliconfig.BuildValues) error { tags = tags[1:] } } + pullPolicy := imagebuildah.PullNever if c.Pull { pullPolicy = imagebuildah.PullIfMissing @@ -174,16 +171,17 @@ func buildCmd(c *cliconfig.BuildValues) error { dockerfiles = append(dockerfiles, filepath.Join(contextDir, "Dockerfile")) } + runtime, err := adapter.GetRuntime(&c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + runtimeFlags := []string{} for _, arg := range c.RuntimeOpts { runtimeFlags = append(runtimeFlags, "--"+arg) } // end from buildah - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) - if err != nil { - return errors.Wrapf(err, "could not get runtime") - } defer runtime.Shutdown(false) var stdout, stderr, reporter *os.File @@ -202,72 +200,64 @@ func buildCmd(c *cliconfig.BuildValues) error { reporter = f } - systemContext, err := parse.SystemContextFromOptions(c.PodmanCommand.Command) - if err != nil { - return errors.Wrapf(err, "error building system context") - } - systemContext.AuthFilePath = getAuthFile(c.Authfile) - commonOpts, err := parse.CommonBuildOptions(c.PodmanCommand.Command) - if err != nil { - return err + var memoryLimit, memorySwap int64 + if c.Flags().Changed("memory") { + memoryLimit, err = units.RAMInBytes(c.Memory) + if err != nil { + return err + } } - namespaceOptions, networkPolicy, err := parse.NamespaceOptions(c.PodmanCommand.Command) - if err != nil { - return errors.Wrapf(err, "error parsing namespace-related options") - } - usernsOption, idmappingOptions, err := parse.IDMappingOptions(c.PodmanCommand.Command) - if err != nil { - return errors.Wrapf(err, "error parsing ID mapping options") + if c.Flags().Changed("memory-swap") { + memorySwap, err = units.RAMInBytes(c.MemorySwap) + if err != nil { + return err + } } - namespaceOptions.AddOrReplace(usernsOption...) - ociruntime := runtime.GetOCIRuntimePath() - if c.Flag("runtime").Changed { - ociruntime = c.Runtime + buildOpts := buildah.CommonBuildOptions{ + AddHost: c.AddHost, + CgroupParent: c.CgroupParent, + CPUPeriod: c.CPUPeriod, + CPUQuota: c.CPUQuota, + CPUShares: c.CPUShares, + CPUSetCPUs: c.CPUSetCPUs, + CPUSetMems: c.CPUSetMems, + Memory: memoryLimit, + MemorySwap: memorySwap, + ShmSize: c.ShmSize, + Ulimit: c.Ulimit, + Volumes: c.Volume, } + options := imagebuildah.BuildOptions{ - ContextDirectory: contextDir, - PullPolicy: pullPolicy, - Compression: imagebuildah.Gzip, - Quiet: c.Quiet, - SignaturePolicyPath: c.SignaturePolicy, - Args: args, - Output: output, + CommonBuildOpts: &buildOpts, AdditionalTags: tags, - Out: stdout, - Err: stderr, - ReportWriter: reporter, - Runtime: ociruntime, - RuntimeArgs: runtimeFlags, - OutputFormat: format, - SystemContext: systemContext, - NamespaceOptions: namespaceOptions, - ConfigureNetwork: networkPolicy, - CNIPluginPath: c.CNIPlugInPath, + Annotations: c.Annotation, + Args: args, CNIConfigDir: c.CNIConfigDir, - IDMappingOptions: idmappingOptions, - CommonBuildOpts: commonOpts, + CNIPluginPath: c.CNIPlugInPath, + Compression: imagebuildah.Gzip, + ContextDirectory: contextDir, DefaultMountsFilePath: c.GlobalFlags.DefaultMountsFile, + Err: stderr, + ForceRmIntermediateCtrs: c.ForceRm, IIDFile: c.Iidfile, - Squash: c.Squash, Labels: c.Label, - Annotations: c.Annotation, Layers: layers, NoCache: c.NoCache, + Out: stdout, + Output: output, + OutputFormat: format, + PullPolicy: pullPolicy, + Quiet: c.Quiet, RemoveIntermediateCtrs: c.Rm, - ForceRmIntermediateCtrs: c.ForceRm, - } - - if c.Quiet { - options.ReportWriter = ioutil.Discard - } - - if rootless.IsRootless() { - options.Isolation = buildah.IsolationOCIRootless + ReportWriter: reporter, + RuntimeArgs: runtimeFlags, + SignaturePolicyPath: c.SignaturePolicy, + Squash: c.Squash, } - - return runtime.Build(getContext(), options, dockerfiles...) + return runtime.Build(getContext(), c, options, dockerfiles) } // Tail returns a string slice after the first element unless there are diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go index 2a978bea8..aa4034ccd 100644 --- a/cmd/podman/checkpoint.go +++ b/cmd/podman/checkpoint.go @@ -35,6 +35,7 @@ var ( func init() { checkpointCommand.Command = _checkpointCommand + checkpointCommand.SetUsageTemplate(UsageTemplate()) flags := checkpointCommand.Flags() flags.BoolVarP(&checkpointCommand.Keep, "keep", "k", false, "Keep all temporary checkpoint files") diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go index 1a7617d5c..e465a30e6 100644 --- a/cmd/podman/cleanup.go +++ b/cmd/podman/cleanup.go @@ -32,10 +32,12 @@ var ( func init() { cleanupCommand.Command = _cleanupCommand + cleanupCommand.SetUsageTemplate(UsageTemplate()) flags := cleanupCommand.Flags() flags.BoolVarP(&cleanupCommand.All, "all", "a", false, "Cleans up all containers") flags.BoolVarP(&cleanupCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVar(&cleanupCommand.Remove, "rm", false, "After cleanup, remove the container entirely") } func cleanupCmd(c *cliconfig.CleanupValues) error { @@ -54,12 +56,25 @@ func cleanupCmd(c *cliconfig.CleanupValues) error { ctx := getContext() for _, ctr := range cleanupContainers { - if err = ctr.Cleanup(ctx); err != nil { - if lastError != nil { - fmt.Fprintln(os.Stderr, lastError) + hadError := false + if c.Remove { + if err := runtime.RemoveContainer(ctx, ctr, false, false); err != nil { + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID()) + hadError = true } - lastError = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID()) } else { + if err := ctr.Cleanup(ctx); err != nil { + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID()) + hadError = true + } + } + if !hadError { fmt.Println(ctr.ID()) } } diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index a4c1bf0c0..9c9be3618 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -135,6 +135,11 @@ type PruneImagesValues struct { All bool } +type PruneContainersValues struct { + PodmanCommand + Force bool +} + type ImportValues struct { PodmanCommand Change []string @@ -172,12 +177,13 @@ type LoadValues struct { type LoginValues struct { PodmanCommand - Password string - Username string - Authfile string - CertDir string - GetLogin bool - TlsVerify bool + Password string + StdinPassword bool + Username string + Authfile string + CertDir string + GetLogin bool + TlsVerify bool } type LogoutValues struct { @@ -532,6 +538,7 @@ type CleanupValues struct { PodmanCommand All bool Latest bool + Remove bool } type SystemPruneValues struct { diff --git a/cmd/podman/cliconfig/create.go b/cmd/podman/cliconfig/create.go index 68ba4d857..b5ca1be9c 100644 --- a/cmd/podman/cliconfig/create.go +++ b/cmd/podman/cliconfig/create.go @@ -20,3 +20,7 @@ type BuildValues struct { *buildahcli.NameSpaceResults *buildahcli.LayerResults } + +type CpValues struct { + PodmanCommand +} diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index e1eba1f31..fa3839a53 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -6,24 +6,65 @@ import ( "github.com/spf13/cobra" ) +// Commands that the local client implements +func getMainCommands() []*cobra.Command { + rootCommands := []*cobra.Command{ + _attachCommand, + _commitCommand, + _createCommand, + _diffCommand, + _execCommand, + _killCommand, + generateCommand.Command, + podCommand.Command, + _containerKubeCommand, + _psCommand, + _loadCommand, + _loginCommand, + _logoutCommand, + _logsCommand, + _mountCommand, + _pauseCommand, + _portCommand, + _refreshCommand, + _restartCommand, + _restoreCommand, + _rmCommand, + _runCommand, + _saveCommand, + _searchCommand, + _signCommand, + _startCommand, + _statsCommand, + _stopCommand, + _topCommand, + _umountCommand, + _unpauseCommand, + volumeCommand.Command, + _waitCommand, + } + + if len(_varlinkCommand.Use) > 0 { + rootCommands = append(rootCommands, _varlinkCommand) + } + return rootCommands +} + +// Commands that the local client implements func getImageSubCommands() []*cobra.Command { return []*cobra.Command{ - _buildCommand, - _importCommand, _loadCommand, - _pullCommand, - _rmiCommand, _saveCommand, _signCommand, } } +// Commands that the local client implements func getContainerSubCommands() []*cobra.Command { return []*cobra.Command{ _attachCommand, _checkpointCommand, _cleanupCommand, - _containerExistsCommand, _commitCommand, _createCommand, _diffCommand, @@ -40,7 +81,7 @@ func getContainerSubCommands() []*cobra.Command { _restartCommand, _restoreCommand, _rmCommand, - _runCommmand, + _runCommand, _runlabelCommand, _startCommand, _statsCommand, @@ -52,6 +93,7 @@ func getContainerSubCommands() []*cobra.Command { } } +// Commands that the local client implements func getPodSubCommands() []*cobra.Command { return []*cobra.Command{ _podCreateCommand, @@ -70,28 +112,20 @@ func getPodSubCommands() []*cobra.Command { } } -func getVolumeSubCommands() []*cobra.Command { - return []*cobra.Command{ - _volumeCreateCommand, - _volumeLsCommand, - _volumeRmCommand, - _volumeInspectCommand, - _volumePruneCommand, - } -} - func getGenerateSubCommands() []*cobra.Command { return []*cobra.Command{ _containerKubeCommand, } } +// Commands that the local client implements func getPlaySubCommands() []*cobra.Command { return []*cobra.Command{ _playKubeCommand, } } +// Commands that the local client implements func getTrustSubCommands() []*cobra.Command { return []*cobra.Command{ _setTrustCommand, @@ -99,9 +133,9 @@ func getTrustSubCommands() []*cobra.Command { } } +// Commands that the local client implements func getSystemSubCommands() []*cobra.Command { return []*cobra.Command{ - _infoCommand, _pruneSystemCommand, } } diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go index 80083eab9..ba0a4d47e 100644 --- a/cmd/podman/commands_remoteclient.go +++ b/cmd/podman/commands_remoteclient.go @@ -6,43 +6,47 @@ import ( "github.com/spf13/cobra" ) -//import "github.com/urfave/cli" -// +// commands that only the remoteclient implements +func getMainCommands() []*cobra.Command { + return []*cobra.Command{} +} + +// commands that only the remoteclient implements func getAppCommands() []*cobra.Command { return []*cobra.Command{} } +// commands that only the remoteclient implements func getImageSubCommands() []*cobra.Command { return []*cobra.Command{} } +// commands that only the remoteclient implements func getContainerSubCommands() []*cobra.Command { return []*cobra.Command{} } +// commands that only the remoteclient implements func getPodSubCommands() []*cobra.Command { return []*cobra.Command{} } -func getVolumeSubCommands() []*cobra.Command { - return []*cobra.Command{} -} - +// commands that only the remoteclient implements func getGenerateSubCommands() []*cobra.Command { return []*cobra.Command{} } +// commands that only the remoteclient implements func getPlaySubCommands() []*cobra.Command { return []*cobra.Command{} } +// commands that only the remoteclient implements func getTrustSubCommands() []*cobra.Command { return []*cobra.Command{} } -//func getMainAppFlags() []cli.Flag { -// return []cli.Flag{} -//} +// commands that only the remoteclient implements func getSystemSubCommands() []*cobra.Command { return []*cobra.Command{} } diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index 6fd6b9761..d8ced0e36 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -33,12 +33,15 @@ var ( commitCommand.GlobalFlags = MainGlobalOpts return commitCmd(&commitCommand) }, - Example: "CONTAINER [REPOSITORY[:TAG]]", + Example: `podman commit -q --message "committing container to image" reverent_golick image-commited + podman commit -q --author "firstName lastName" reverent_golick image-commited + podman commit -q --pause=false containerID image-commited`, } ) func init() { commitCommand.Command = _commitCommand + commitCommand.SetUsageTemplate(UsageTemplate()) flags := commitCommand.Flags() flags.StringSliceVarP(&commitCommand.Change, "change", "c", []string{}, fmt.Sprintf("Apply the following possible instructions to the created image (default []): %s", strings.Join(libpod.ChangeCmds, " | "))) flags.StringVarP(&commitCommand.Format, "format", "f", "oci", "`Format` of the image manifest and metadata") @@ -47,7 +50,6 @@ func init() { flags.BoolVarP(&commitCommand.Pause, "pause", "p", false, "Pause container during commit") flags.BoolVarP(&commitCommand.Quiet, "quiet", "q", false, "Suppress output") - rootCmd.AddCommand(commitCommand.Command) } func commitCmd(c *cliconfig.CommitValues) error { diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 5fbdfce50..ec755c4a8 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -500,3 +500,27 @@ func scrubServer(server string) string { server = strings.TrimPrefix(server, "https://") return strings.TrimPrefix(server, "http://") } + +// UsageTemplate returns the usage template for podman commands +// This blocks the desplaying of the global options. The main podman +// command should not use this. +func UsageTemplate() string { + return `Usage:{{if .Runnable}} + {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} + + {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} + +Aliases: + {{.NameAndAliases}}{{end}}{{if .HasExample}} + +Examples: + {{.Example}}{{end}}{{if .HasAvailableSubCommands}} + +Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + +Flags: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} +{{end}} +` +} diff --git a/cmd/podman/common_test.go b/cmd/podman/common_test.go index 042568d7e..a24173003 100644 --- a/cmd/podman/common_test.go +++ b/cmd/podman/common_test.go @@ -3,34 +3,8 @@ package main import ( "os/user" "testing" - - "flag" - - "github.com/urfave/cli" ) -func TestGetStore(t *testing.T) { - t.Skip("FIX THIS!") - - //cmd/podman/common_test.go:27: cannot use c (type *cli.Context) as type *libkpod.Config in argument to getStore - - // Make sure the tests are running as root - skipTestIfNotRoot(t) - - set := flag.NewFlagSet("test", 0) - globalSet := flag.NewFlagSet("test", 0) - globalSet.String("root", "", "path to the root directory in which data, including images, is stored") - globalCtx := cli.NewContext(nil, globalSet, nil) - command := cli.Command{Name: "imagesCommand"} - c := cli.NewContext(nil, set, globalCtx) - c.Command = command - - //_, err := getStore(c) - //if err != nil { - //t.Error(err) - //} -} - func skipTestIfNotRoot(t *testing.T) { u, err := user.Current() if err != nil { diff --git a/cmd/podman/container.go b/cmd/podman/container.go index 969cb2dc8..d2450fdd3 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -15,7 +15,14 @@ var containerCommand = cliconfig.PodmanCommand{ }, } +// Commands that are universally implemented. +var containerCommands = []*cobra.Command{ + _containerExistsCommand, +} + func init() { + containerCommand.AddCommand(containerCommands...) containerCommand.AddCommand(getContainerSubCommands()...) + containerCommand.SetUsageTemplate(UsageTemplate()) rootCmd.AddCommand(containerCommand.Command) } diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go index 3f9b46035..bae578e1d 100644 --- a/cmd/podman/containers_prune.go +++ b/cmd/podman/containers_prune.go @@ -13,13 +13,12 @@ import ( ) var ( - pruneContainersCommand cliconfig.ContainersPrune + pruneContainersCommand cliconfig.PruneContainersValues pruneContainersDescription = ` podman container prune Removes all exited containers ` - _pruneContainersCommand = &cobra.Command{ Use: "prune", Short: "Remove all stopped containers", @@ -34,9 +33,12 @@ var ( func init() { pruneContainersCommand.Command = _pruneContainersCommand + pruneContainersCommand.SetUsageTemplate(UsageTemplate()) + flags := pruneContainersCommand.Flags() + flags.BoolVarP(&pruneContainersCommand.Force, "force", "f", false, "Force removal of a running container. The default is false") } -func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWorkers int, force bool) error { +func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWorkers int, force, volumes bool) error { var deleteFuncs []shared.ParallelWorkerInput filter := func(c *libpod.Container) bool { @@ -56,7 +58,7 @@ func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWork for _, container := range delContainers { con := container f := func() error { - return runtime.RemoveContainer(ctx, con, force) + return runtime.RemoveContainer(ctx, con, force, volumes) } deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{ @@ -69,7 +71,7 @@ func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWork return printParallelOutput(deleteErrors, errCount) } -func pruneContainersCmd(c *cliconfig.ContainersPrune) error { +func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") @@ -82,5 +84,5 @@ func pruneContainersCmd(c *cliconfig.ContainersPrune) error { } logrus.Debugf("Setting maximum workers to %d", maxWorkers) - return pruneContainers(runtime, getContext(), maxWorkers, c.Bool("force")) + return pruneContainers(runtime, getContext(), maxWorkers, c.Bool("force"), c.Bool("volumes")) } diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go new file mode 100644 index 000000000..89114fda1 --- /dev/null +++ b/cmd/podman/cp.go @@ -0,0 +1,257 @@ +package main + +import ( + "os" + "path/filepath" + "strings" + + "github.com/containers/buildah/util" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/chrootuser" + "github.com/containers/storage" + "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/chrootarchive" + "github.com/containers/storage/pkg/idtools" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + cpCommand cliconfig.CpValues + + cpDescription = "Copy files/folders between a container and the local filesystem" + _cpCommand = &cobra.Command{ + Use: "cp", + Short: "Copy files/folders between a container and the local filesystem", + Long: cpDescription, + RunE: func(cmd *cobra.Command, args []string) error { + cpCommand.InputArgs = args + cpCommand.GlobalFlags = MainGlobalOpts + return cpCmd(&cpCommand) + }, + Example: "[CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", + } +) + +func init() { + cpCommand.Command = _cpCommand + rootCmd.AddCommand(cpCommand.Command) +} + +func cpCmd(c *cliconfig.CpValues) error { + args := c.InputArgs + if len(args) != 2 { + return errors.Errorf("you must provide a source path and a destination path") + } + + runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + return copyBetweenHostAndContainer(runtime, args[0], args[1]) +} + +func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest string) error { + + srcCtr, srcPath := parsePath(runtime, src) + destCtr, destPath := parsePath(runtime, dest) + + if (srcCtr == nil && destCtr == nil) || (srcCtr != nil && destCtr != nil) { + return errors.Errorf("invalid arguments %s, %s you must use just one container", src, dest) + } + + if len(srcPath) == 0 || len(destPath) == 0 { + return errors.Errorf("invalid arguments %s, %s you must specify paths", src, dest) + } + ctr := srcCtr + isFromHostToCtr := (ctr == nil) + if isFromHostToCtr { + ctr = destCtr + } + + mountPoint, err := ctr.Mount() + if err != nil { + return err + } + defer ctr.Unmount(false) + user, err := getUser(mountPoint, ctr.User()) + if err != nil { + return err + } + idMappingOpts, err := ctr.IDMappings() + if err != nil { + return errors.Wrapf(err, "error getting IDMappingOptions") + } + containerOwner := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} + hostUID, hostGID, err := util.GetHostIDs(convertIDMap(idMappingOpts.UIDMap), convertIDMap(idMappingOpts.GIDMap), user.UID, user.GID) + if err != nil { + return err + } + + hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)} + + var glob []string + if isFromHostToCtr { + if filepath.IsAbs(destPath) { + destPath = filepath.Join(mountPoint, destPath) + + } else { + if err = idtools.MkdirAllAndChownNew(filepath.Join(mountPoint, ctr.WorkingDir()), 0755, hostOwner); err != nil { + return errors.Wrapf(err, "error creating directory %q", destPath) + } + destPath = filepath.Join(mountPoint, ctr.WorkingDir(), destPath) + } + } else { + if filepath.IsAbs(srcPath) { + srcPath = filepath.Join(mountPoint, srcPath) + } else { + srcPath = filepath.Join(mountPoint, ctr.WorkingDir(), srcPath) + } + } + glob, err = filepath.Glob(srcPath) + if err != nil { + return errors.Wrapf(err, "invalid glob %q", srcPath) + } + if len(glob) == 0 { + glob = append(glob, srcPath) + } + if !filepath.IsAbs(destPath) { + dir, err := os.Getwd() + if err != nil { + return errors.Wrapf(err, "err getting current working directory") + } + destPath = filepath.Join(dir, destPath) + } + + var lastError error + for _, src := range glob { + err := copy(src, destPath, dest, idMappingOpts, &containerOwner) + if lastError != nil { + logrus.Error(lastError) + } + lastError = err + } + return lastError +} + +func getUser(mountPoint string, userspec string) (specs.User, error) { + uid, gid, err := chrootuser.GetUser(mountPoint, userspec) + u := specs.User{ + UID: uid, + GID: gid, + Username: userspec, + } + if !strings.Contains(userspec, ":") { + groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) + if err2 != nil { + if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil { + err = err2 + } + } else { + u.AdditionalGids = groups + } + + } + return u, err +} + +func parsePath(runtime *libpod.Runtime, path string) (*libpod.Container, string) { + pathArr := strings.SplitN(path, ":", 2) + if len(pathArr) == 2 { + ctr, err := runtime.LookupContainer(pathArr[0]) + if err == nil { + return ctr, pathArr[1] + } + } + return nil, path +} + +func getPathInfo(path string) (string, os.FileInfo, error) { + path, err := filepath.EvalSymlinks(path) + if err != nil { + return "", nil, errors.Wrapf(err, "error evaluating symlinks %q", path) + } + srcfi, err := os.Stat(path) + if err != nil { + return "", nil, errors.Wrapf(err, "error reading path %q", path) + } + return path, srcfi, nil +} + +func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair) error { + srcPath, err := filepath.EvalSymlinks(src) + if err != nil { + return errors.Wrapf(err, "error evaluating symlinks %q", srcPath) + } + + srcPath, srcfi, err := getPathInfo(srcPath) + if err != nil { + return err + } + destdir := destPath + if !srcfi.IsDir() && !strings.HasSuffix(dest, string(os.PathSeparator)) { + destdir = filepath.Dir(destPath) + } + if err = os.MkdirAll(destdir, 0755); err != nil { + return errors.Wrapf(err, "error creating directory %q", destdir) + } + + // return functions for copying items + copyFileWithTar := chrootarchive.CopyFileWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) + copyWithTar := chrootarchive.CopyWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) + untarPath := chrootarchive.UntarPathAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) + + if srcfi.IsDir() { + + logrus.Debugf("copying %q to %q", srcPath+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*") + if err = copyWithTar(srcPath, destPath); err != nil { + return errors.Wrapf(err, "error copying %q to %q", srcPath, dest) + } + return nil + } + if !archive.IsArchivePath(srcPath) { + // This srcPath is a file, and either it's not an + // archive, or we don't care whether or not it's an + // archive. + destfi, err := os.Stat(destPath) + if err != nil { + if !os.IsNotExist(err) { + return errors.Wrapf(err, "failed to get stat of dest path %s", destPath) + } + } + if destfi != nil && destfi.IsDir() { + destPath = filepath.Join(destPath, filepath.Base(srcPath)) + } + // Copy the file, preserving attributes. + logrus.Debugf("copying %q to %q", srcPath, destPath) + if err = copyFileWithTar(srcPath, destPath); err != nil { + return errors.Wrapf(err, "error copying %q to %q", srcPath, destPath) + } + return nil + } + // We're extracting an archive into the destination directory. + logrus.Debugf("extracting contents of %q into %q", srcPath, destPath) + if err = untarPath(srcPath, destPath); err != nil { + return errors.Wrapf(err, "error extracting %q into %q", srcPath, destPath) + } + return nil +} + +func convertIDMap(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) { + for _, idmap := range idMaps { + tempIDMap := specs.LinuxIDMapping{ + ContainerID: uint32(idmap.ContainerID), + HostID: uint32(idmap.HostID), + Size: uint32(idmap.Size), + } + convertedIDMap = append(convertedIDMap, tempIDMap) + } + return convertedIDMap +} diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 2f8742052..7bfb070c7 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -49,7 +49,9 @@ var ( createCommand.GlobalFlags = MainGlobalOpts return createCmd(&createCommand) }, - Example: "IMAGE [COMMAND [ARG...]]", + Example: `podman create alpine ls + podman create --annotation HELLO=WORLD alpine ls + podman create -t -i --name myctr alpine ls`, } defaultEnvVariables = map[string]string{ @@ -60,12 +62,12 @@ var ( func init() { createCommand.PodmanCommand.Command = _createCommand + createCommand.SetUsageTemplate(UsageTemplate()) getCreateFlags(&createCommand.PodmanCommand) flags := createCommand.Flags() flags.SetInterspersed(true) - rootCmd.AddCommand(createCommand.Command) } func createCmd(c *cliconfig.CreateValues) error { @@ -646,9 +648,10 @@ func parseCreateOpts(ctx context.Context, c *cliconfig.PodmanCommand, runtime *l } var ImageVolumes map[string]struct{} - if data != nil { + if data != nil && c.String("image-volume") != "ignore" { ImageVolumes = data.Config.Volumes } + var imageVolType = map[string]string{ "bind": "", "tmpfs": "", diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 04a659ab6..7d4cc1b58 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -52,6 +52,7 @@ var ( func init() { diffCommand.Command = _diffCommand + diffCommand.SetUsageTemplate(UsageTemplate()) flags := diffCommand.Flags() flags.BoolVar(&diffCommand.Archive, "archive", true, "Save the diff as a tar archive") @@ -59,9 +60,8 @@ func init() { flags.MarkHidden("archive") - rootCmd.AddCommand(diffCommand.Command) - } + func formatJSON(output []diffOutputParams) (diffJSONOutput, error) { jsonStruct := diffJSONOutput{} for _, output := range output { diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index b4f66bc03..74808768e 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -35,6 +35,7 @@ var ( func init() { execCommand.Command = _execCommand + execCommand.SetUsageTemplate(UsageTemplate()) flags := execCommand.Flags() flags.SetInterspersed(false) flags.StringSliceVarP(&execCommand.Env, "env", "e", []string{}, "Set environment variables") @@ -46,7 +47,6 @@ func init() { flags.StringVarP(&execCommand.Workdir, "workdir", "w", "", "Working directory inside the container") - rootCmd.AddCommand(execCommand.Command) } func execCmd(c *cliconfig.ExecValues) error { diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go index a21e8fcf8..7645bb716 100644 --- a/cmd/podman/exists.go +++ b/cmd/podman/exists.go @@ -41,7 +41,7 @@ var ( imageExistsCommand.GlobalFlags = MainGlobalOpts return imageExistsCmd(&imageExistsCommand) }, - Example: "IMAGE-NAME", + Example: `podman image exists imageID`, } _containerExistsCommand = &cobra.Command{ @@ -54,7 +54,7 @@ var ( return containerExistsCmd(&containerExistsCommand) }, - Example: "CONTAINER-NAME", + Example: `podman container exists containerID`, } _podExistsCommand = &cobra.Command{ @@ -66,14 +66,17 @@ var ( podExistsCommand.GlobalFlags = MainGlobalOpts return podExistsCmd(&podExistsCommand) }, - Example: "POD-NAME", + Example: `podman pod exists podID`, } ) func init() { imageExistsCommand.Command = _imageExistsCommand + imageExistsCommand.SetUsageTemplate(UsageTemplate()) containerExistsCommand.Command = _containerExistsCommand + containerExistsCommand.SetUsageTemplate(UsageTemplate()) podExistsCommand.Command = _podExistsCommand + podExistsCommand.SetUsageTemplate(UsageTemplate()) } func imageExistsCmd(c *cliconfig.ImageExistsValues) error { diff --git a/cmd/podman/export.go b/cmd/podman/export.go index bd9e38b0c..2ce8186a1 100644 --- a/cmd/podman/export.go +++ b/cmd/podman/export.go @@ -31,9 +31,9 @@ var ( func init() { exportCommand.Command = _exportCommand + exportCommand.SetUsageTemplate(UsageTemplate()) flags := exportCommand.Flags() flags.StringVarP(&exportCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT") - rootCmd.AddCommand(exportCommand.Command) } // exportCmd saves a container to a tarball on disk diff --git a/cmd/podman/generate.go b/cmd/podman/generate.go index a5e15fdd6..66cb7a465 100644 --- a/cmd/podman/generate.go +++ b/cmd/podman/generate.go @@ -17,5 +17,5 @@ var generateCommand = cliconfig.PodmanCommand{ func init() { generateCommand.AddCommand(getGenerateSubCommands()...) - rootCmd.AddCommand(generateCommand.Command) + generateCommand.SetUsageTemplate(UsageTemplate()) } diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go index 8e3432d12..ddb2daa34 100644 --- a/cmd/podman/generate_kube.go +++ b/cmd/podman/generate_kube.go @@ -31,6 +31,7 @@ var ( func init() { containerKubeCommand.Command = _containerKubeCommand + containerKubeCommand.SetUsageTemplate(UsageTemplate()) flags := containerKubeCommand.Flags() flags.BoolVarP(&containerKubeCommand.Service, "service", "s", false, "Generate YAML for kubernetes service object") } diff --git a/cmd/podman/history.go b/cmd/podman/history.go index 97e501947..6791257d9 100644 --- a/cmd/podman/history.go +++ b/cmd/podman/history.go @@ -53,6 +53,7 @@ var ( func init() { historyCommand.Command = _historyCommand + historyCommand.SetUsageTemplate(UsageTemplate()) flags := historyCommand.Flags() flags.StringVar(&historyCommand.Format, "format", "", "Change the output to JSON or a Go template") flags.BoolVarP(&historyCommand.Human, "human", "H", true, "Display sizes and dates in human readable format") @@ -60,9 +61,8 @@ func init() { flags.BoolVar(&historyCommand.NoTrunc, "no-trunc", false, "Do not truncate the output") flags.BoolVarP(&historyCommand.Quiet, "quiet", "q", false, "Display the numeric IDs only") - rootCmd.AddCommand(historyCommand.Command) - } + func historyCmd(c *cliconfig.HistoryValues) error { runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { diff --git a/cmd/podman/image.go b/cmd/podman/image.go index ac7ff4944..4f9c7cd6a 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -18,18 +18,21 @@ var ( //imageSubCommands are implemented both in local and remote clients var imageSubCommands = []*cobra.Command{ + _buildCommand, _historyCommand, _imageExistsCommand, - _inspectCommand, _imagesCommand, + _importCommand, + _inspectCommand, _pruneImagesCommand, + _pullCommand, _pushCommand, _rmiCommand, _tagCommand, } func init() { + imageCommand.SetUsageTemplate(UsageTemplate()) imageCommand.AddCommand(imageSubCommands...) imageCommand.AddCommand(getImageSubCommands()...) - rootCmd.AddCommand(imageCommand.Command) } diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 6f5a3e9f1..b269f6440 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -102,6 +102,7 @@ var ( func init() { imagesCommand.Command = _imagesCommand + imagesCommand.SetUsageTemplate(UsageTemplate()) flags := imagesCommand.Flags() flags.BoolVarP(&imagesCommand.All, "all", "a", false, "Show all images (default hides intermediate images)") flags.BoolVar(&imagesCommand.Digests, "digests", false, "Show digests") @@ -114,8 +115,6 @@ func init() { flags.BoolVarP(&imagesCommand.Quiet, "quiet", "q", false, "Display only image IDs") flags.StringVar(&imagesCommand.Sort, "sort", "created", "Sort by created, id, repository, size, or tag") - rootCmd.AddCommand(imagesCommand.Command) - } func imagesCmd(c *cliconfig.ImagesValues) error { @@ -248,8 +247,12 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma } // get all specified repo:tag pairs and print them separately + repopairs, err := image.ReposToMap(img.Names()) + if err != nil { + logrus.Errorf("error finding tag/digest for %s", img.ID()) + } outer: - for repo, tags := range image.ReposToMap(img.Names()) { + for repo, tags := range repopairs { for _, tag := range tags { size, err := img.Size(ctx) var sizeStr string diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go index ba99c5bde..cc0dcb99a 100644 --- a/cmd/podman/images_prune.go +++ b/cmd/podman/images_prune.go @@ -30,6 +30,7 @@ var ( func init() { pruneImagesCommand.Command = _pruneImagesCommand + pruneImagesCommand.SetUsageTemplate(UsageTemplate()) flags := pruneImagesCommand.Flags() flags.BoolVarP(&pruneImagesCommand.All, "all", "a", false, "Remove all unused images, not just dangling ones") } diff --git a/cmd/podman/import.go b/cmd/podman/import.go index 52d144eb3..32f79757b 100644 --- a/cmd/podman/import.go +++ b/cmd/podman/import.go @@ -31,12 +31,12 @@ var ( func init() { importCommand.Command = _importCommand + importCommand.SetUsageTemplate(UsageTemplate()) flags := importCommand.Flags() flags.StringSliceVarP(&importCommand.Change, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR") flags.StringVarP(&importCommand.Message, "message", "m", "", "Set commit message for imported image") flags.BoolVarP(&importCommand.Quiet, "quiet", "q", false, "Suppress output") - rootCmd.AddCommand(importCommand.Command) } func importCmd(c *cliconfig.ImportValues) error { diff --git a/cmd/podman/info.go b/cmd/podman/info.go index 0896e16a4..06dbbd748 100644 --- a/cmd/podman/info.go +++ b/cmd/podman/info.go @@ -26,18 +26,18 @@ var ( infoCommand.GlobalFlags = MainGlobalOpts return infoCmd(&infoCommand) }, - Example: "", + Example: `podman info`, } ) func init() { infoCommand.Command = _infoCommand + infoCommand.SetUsageTemplate(UsageTemplate()) flags := infoCommand.Flags() flags.BoolVarP(&infoCommand.Debug, "debug", "D", false, "Display additional debug information") flags.StringVarP(&infoCommand.Format, "format", "f", "", "Change the output format to JSON or a Go template") - rootCmd.AddCommand(infoCommand.Command) } func infoCmd(c *cliconfig.InfoValues) error { diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index b2979c6ae..a1f3ef81f 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -34,19 +34,21 @@ var ( inspectCommand.GlobalFlags = MainGlobalOpts return inspectCmd(&inspectCommand) }, - Example: "CONTAINER-OR-IMAGE [CONTAINER-OR-IMAGE]...", + Example: `podman inspect alpine + podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine + podman inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`, } ) func init() { inspectCommand.Command = _inspectCommand + inspectCommand.SetUsageTemplate(UsageTemplate()) flags := inspectCommand.Flags() flags.StringVarP(&inspectCommand.TypeObject, "type", "t", inspectAll, "Return JSON for specified type, (e.g image, container or task)") flags.StringVarP(&inspectCommand.Format, "format", "f", "", "Change the output format to a Go template") flags.BoolVarP(&inspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of if the type is a container") flags.BoolVarP(&inspectCommand.Size, "size", "s", false, "Display total file size if the type is container") - rootCmd.AddCommand(inspectCommand.Command) } func inspectCmd(c *cliconfig.InspectValues) error { diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index 04c0fd531..d922cf721 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -34,13 +34,13 @@ var ( func init() { killCommand.Command = _killCommand + killCommand.SetUsageTemplate(UsageTemplate()) flags := killCommand.Flags() flags.BoolVarP(&killCommand.All, "all", "a", false, "Signal all running containers") flags.StringVarP(&killCommand.Signal, "signal", "s", "KILL", "Signal to send to the container") flags.BoolVarP(&killCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - rootCmd.AddCommand(killCommand.Command) } // killCmd kills one or more containers with a signal diff --git a/cmd/podman/load.go b/cmd/podman/load.go index 90268ca8f..34a51cd0d 100644 --- a/cmd/podman/load.go +++ b/cmd/podman/load.go @@ -34,12 +34,12 @@ var ( func init() { loadCommand.Command = _loadCommand + loadCommand.SetUsageTemplate(UsageTemplate()) flags := loadCommand.Flags() flags.StringVarP(&loadCommand.Input, "input", "i", "/dev/stdin", "Read from archive file, default is STDIN") flags.BoolVarP(&loadCommand.Quiet, "quiet", "q", false, "Suppress the output") flags.StringVar(&loadCommand.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file (not usually used)") - rootCmd.AddCommand(loadCommand.Command) } // loadCmd gets the image/file to be loaded from the command line diff --git a/cmd/podman/login.go b/cmd/podman/login.go index 5ab0713e5..3eacab54a 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -29,12 +29,15 @@ var ( loginCommand.GlobalFlags = MainGlobalOpts return loginCmd(&loginCommand) }, - Example: "REGISTRY", + Example: `podman login -u testuser -p testpassword localhost:5000 + podman login --authfile authdir/myauths.json quay.io + podman login -u testuser -p testpassword localhost:5000`, } ) func init() { loginCommand.Command = _loginCommand + loginCommand.SetUsageTemplate(UsageTemplate()) flags := loginCommand.Flags() flags.StringVar(&loginCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") @@ -43,8 +46,8 @@ func init() { flags.StringVarP(&loginCommand.Password, "password", "p", "", "Password for registry") flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") flags.StringVarP(&loginCommand.Username, "username", "u", "", "Username for registry") + flags.BoolVar(&loginCommand.StdinPassword, "password-stdin", false, "Take the password from stdin") - rootCmd.AddCommand(loginCommand.Command) } // loginCmd uses the authentication package to store a user's authenticated credentials @@ -90,8 +93,26 @@ func loginCmd(c *cliconfig.LoginValues) error { } ctx := getContext() + + password := c.Password + + if c.Flag("password-stdin").Changed { + var stdinPasswordStrBuilder strings.Builder + if c.Password != "" { + return errors.Errorf("Can't specify both --password-stdin and --password") + } + if c.Username == "" { + return errors.Errorf("Must provide --username with --password-stdin") + } + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + fmt.Fprint(&stdinPasswordStrBuilder, scanner.Text()) + } + password = stdinPasswordStrBuilder.String() + } + // If no username and no password is specified, try to use existing ones. - if c.Username == "" && c.Password == "" { + if c.Username == "" && password == "" { fmt.Println("Authenticating with existing credentials...") if err := docker.CheckAuth(ctx, sc, userFromAuthFile, passFromAuthFile, server); err == nil { fmt.Println("Existing credentials are valid. Already logged in to", server) @@ -100,7 +121,7 @@ func loginCmd(c *cliconfig.LoginValues) error { fmt.Println("Existing credentials are invalid, please enter valid username and password") } - username, password, err := getUserAndPass(c.Username, c.Password, userFromAuthFile) + username, password, err := getUserAndPass(c.Username, password, userFromAuthFile) if err != nil { return errors.Wrapf(err, "error getting username and password") } diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index 02bde7857..5fecf814e 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -28,11 +28,11 @@ var ( func init() { logoutCommand.Command = _logoutCommand + logoutCommand.SetUsageTemplate(UsageTemplate()) flags := logoutCommand.Flags() flags.BoolVarP(&logoutCommand.All, "all", "a", false, "Remove the cached credentials for all registries in the auth file") flags.StringVar(&logoutCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") - rootCmd.AddCommand(logoutCommand.Command) } // logoutCmd uses the authentication package to remove the authenticated of a registry diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go index 83f900e63..6962a1f6d 100644 --- a/cmd/podman/logs.go +++ b/cmd/podman/logs.go @@ -32,6 +32,7 @@ var ( func init() { logsCommand.Command = _logsCommand + logsCommand.SetUsageTemplate(UsageTemplate()) flags := logsCommand.Flags() flags.BoolVar(&logsCommand.Details, "details", false, "Show extra details provided to the logs") flags.BoolVarP(&logsCommand.Follow, "follow", "f", false, "Follow log output. The default is false") @@ -43,7 +44,6 @@ func init() { flags.SetInterspersed(false) - rootCmd.AddCommand(logsCommand.Command) } func logsCmd(c *cliconfig.LogsValues) error { diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 32ecaede7..f9820c075 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -27,6 +27,26 @@ var ( exitCode = 125 ) +// Commands that the remote and local client have +// implemented. +var mainCommands = []*cobra.Command{ + _buildCommand, + _exportCommand, + _historyCommand, + _imagesCommand, + _importCommand, + _infoCommand, + _inspectCommand, + _killCommand, + _pullCommand, + _pushCommand, + _rmiCommand, + _tagCommand, + _versionCommand, + imageCommand.Command, + systemCommand.Command, +} + var cmdsNotRequiringRootless = map[*cobra.Command]bool{ _versionCommand: true, _createCommand: true, @@ -40,7 +60,7 @@ var cmdsNotRequiringRootless = map[*cobra.Command]bool{ _killCommand: true, _pauseCommand: true, _restartCommand: true, - _runCommmand: true, + _runCommand: true, _unpauseCommand: true, _searchCommand: true, _statsCommand: true, @@ -92,6 +112,8 @@ func init() { rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory") + rootCmd.AddCommand(mainCommands...) + rootCmd.AddCommand(getMainCommands()...) } func initConfig() { @@ -185,7 +207,7 @@ func main() { exitCode = status.ExitStatus() } } - fmt.Fprintln(os.Stderr, err.Error()) + fmt.Fprintln(os.Stderr, "Error:", err.Error()) } } else { // The exitCode modified from 125, indicates an application diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go index 36ca8bcfb..ce7c22d60 100644 --- a/cmd/podman/mount.go +++ b/cmd/podman/mount.go @@ -39,13 +39,13 @@ var ( func init() { mountCommand.Command = _mountCommand + mountCommand.SetUsageTemplate(UsageTemplate()) flags := mountCommand.Flags() flags.BoolVarP(&mountCommand.All, "all", "a", false, "Mount all containers") flags.StringVar(&mountCommand.Format, "format", "", "Change the output format to Go template") flags.BoolVarP(&mountCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&mountCommand.NoTrunc, "notruncate", false, "Do not truncate output") - rootCmd.AddCommand(mountCommand.Command) } // jsonMountPoint stores info about each container diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go index 466a3b136..a840cbe49 100644 --- a/cmd/podman/pause.go +++ b/cmd/podman/pause.go @@ -34,10 +34,10 @@ var ( func init() { pauseCommand.Command = _pauseCommand + pauseCommand.SetUsageTemplate(UsageTemplate()) flags := pauseCommand.Flags() flags.BoolVarP(&pauseCommand.All, "all", "a", false, "Pause all running containers") - rootCmd.AddCommand(pauseCommand.Command) } func pauseCmd(c *cliconfig.PauseValues) error { diff --git a/cmd/podman/play.go b/cmd/podman/play.go index ff8320a6b..495a1f170 100644 --- a/cmd/podman/play.go +++ b/cmd/podman/play.go @@ -5,19 +5,18 @@ import ( "github.com/spf13/cobra" ) -var playCommand cliconfig.PodmanCommand - -func init() { - var playDescription = "Play a pod and its containers from a structured file." - playCommand.Command = &cobra.Command{ +var ( + playCommand cliconfig.PodmanCommand + playDescription = "Play a pod and its containers from a structured file." + _playCommand = &cobra.Command{ Use: "play", Short: "Play a pod", Long: playDescription, } - -} +) func init() { + playCommand.Command = _playCommand + playCommand.SetUsageTemplate(UsageTemplate()) playCommand.AddCommand(getPlaySubCommands()...) - rootCmd.AddCommand(playCommand.Command) } diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go index b314c1278..4ecd30cd4 100644 --- a/cmd/podman/play_kube.go +++ b/cmd/podman/play_kube.go @@ -43,6 +43,7 @@ var ( func init() { playKubeCommand.Command = _playKubeCommand + playKubeCommand.SetUsageTemplate(UsageTemplate()) flags := playKubeCommand.Flags() flags.StringVar(&playKubeCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&playKubeCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go index 4104c39c7..e988875ab 100644 --- a/cmd/podman/pod.go +++ b/cmd/podman/pod.go @@ -20,5 +20,5 @@ var podCommand = cliconfig.PodmanCommand{ func init() { podCommand.AddCommand(getPodSubCommands()...) - rootCmd.AddCommand(podCommand.Command) + podCommand.SetUsageTemplate(UsageTemplate()) } diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index 9ac5d94a9..0a0b86aab 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -9,7 +9,6 @@ import ( "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" - "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -39,6 +38,7 @@ var ( func init() { podCreateCommand.Command = _podCreateCommand + podCreateCommand.SetUsageTemplate(UsageTemplate()) flags := podCreateCommand.Flags() flags.SetInterspersed(false) @@ -82,9 +82,6 @@ func podCreateCmd(c *cliconfig.PodCreateValues) error { if !c.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") } - if rootless.IsRootless() { - return errors.Errorf("rootless networking does not allow port binding to the host") - } } if !c.Infra && c.Flag("share").Changed && c.Share != "none" && c.Share != "" { diff --git a/cmd/podman/pod_inspect.go b/cmd/podman/pod_inspect.go index f4c357e96..58b15328e 100644 --- a/cmd/podman/pod_inspect.go +++ b/cmd/podman/pod_inspect.go @@ -23,12 +23,13 @@ var ( podInspectCommand.GlobalFlags = MainGlobalOpts return podInspectCmd(&podInspectCommand) }, - Example: "[POD_NAME_OR_ID]", + Example: `podman pod inspect podID`, } ) func init() { podInspectCommand.Command = _podInspectCommand + podInspectCommand.SetUsageTemplate(UsageTemplate()) flags := podInspectCommand.Flags() flags.BoolVarP(&podInspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go index 43411a988..febc820cd 100644 --- a/cmd/podman/pod_kill.go +++ b/cmd/podman/pod_kill.go @@ -30,6 +30,7 @@ var ( func init() { podKillCommand.Command = _podKillCommand + podKillCommand.SetUsageTemplate(UsageTemplate()) flags := podKillCommand.Flags() flags.BoolVarP(&podKillCommand.All, "all", "a", false, "Kill all containers in all pods") flags.BoolVarP(&podKillCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go index a54783d88..2059727ae 100644 --- a/cmd/podman/pod_pause.go +++ b/cmd/podman/pod_pause.go @@ -27,6 +27,7 @@ var ( func init() { podPauseCommand.Command = _podPauseCommand + podPauseCommand.SetUsageTemplate(UsageTemplate()) flags := podPauseCommand.Flags() flags.BoolVarP(&podPauseCommand.All, "all", "a", false, "Pause all running pods") flags.BoolVarP(&podPauseCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go index 991e294bf..49af91a1e 100644 --- a/cmd/podman/pod_ps.go +++ b/cmd/podman/pod_ps.go @@ -131,6 +131,7 @@ var ( func init() { podPsCommand.Command = _podPsCommand + podPsCommand.SetUsageTemplate(UsageTemplate()) flags := podPsCommand.Flags() flags.BoolVar(&podPsCommand.CtrNames, "ctr-names", false, "Display the container names") flags.BoolVar(&podPsCommand.CtrIDs, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated") diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go index 4d05205ea..f75d84956 100644 --- a/cmd/podman/pod_restart.go +++ b/cmd/podman/pod_restart.go @@ -28,6 +28,7 @@ var ( func init() { podRestartCommand.Command = _podRestartCommand + podRestartCommand.SetUsageTemplate(UsageTemplate()) flags := podRestartCommand.Flags() flags.BoolVarP(&podRestartCommand.All, "all", "a", false, "Restart all running pods") flags.BoolVarP(&podRestartCommand.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go index 445a641df..54cee2a50 100644 --- a/cmd/podman/pod_rm.go +++ b/cmd/podman/pod_rm.go @@ -26,12 +26,15 @@ If --force is specified, all containers will be stopped, then removed. podRmCommand.GlobalFlags = MainGlobalOpts return podRmCmd(&podRmCommand) }, - Example: "[POD ...]", + Example: `podman pod rm mywebserverpod + podman pod rm -f 860a4b23 + podman pod rm -f -a`, } ) func init() { podRmCommand.Command = _podRmCommand + podRmCommand.SetUsageTemplate(UsageTemplate()) flags := podRmCommand.Flags() flags.BoolVarP(&podRmCommand.All, "all", "a", false, "Remove all running pods") flags.BoolVarP(&podRmCommand.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false") diff --git a/cmd/podman/pod_start.go b/cmd/podman/pod_start.go index 8b35d3e04..829fe2107 100644 --- a/cmd/podman/pod_start.go +++ b/cmd/podman/pod_start.go @@ -32,6 +32,7 @@ var ( func init() { podStartCommand.Command = _podStartCommand + podStartCommand.SetUsageTemplate(UsageTemplate()) flags := podStartCommand.Flags() flags.BoolVarP(&podStartCommand.All, "all", "a", false, "Start all pods") flags.BoolVarP(&podStartCommand.Latest, "latest", "l", false, "Start the latest pod podman is aware of") diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go index ccbcf9f7c..7afab6be9 100644 --- a/cmd/podman/pod_stats.go +++ b/cmd/podman/pod_stats.go @@ -2,7 +2,11 @@ package main import ( "fmt" + "html/template" + "os" + "reflect" "strings" + "text/tabwriter" "time" "encoding/json" @@ -34,6 +38,7 @@ var ( func init() { podStatsCommand.Command = _podStatsCommand + podStatsCommand.SetUsageTemplate(UsageTemplate()) flags := podStatsCommand.Flags() flags.BoolVarP(&podStatsCommand.All, "all", "a", false, "Provide stats for all running pods") flags.StringVar(&podStatsCommand.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template") @@ -136,6 +141,25 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error { step = 0 } + headerNames := make(map[string]string) + if c.Format != "" { + // Make a map of the field names for the headers + v := reflect.ValueOf(podStatOut{}) + t := v.Type() + for i := 0; i < t.NumField(); i++ { + value := strings.ToUpper(splitCamelCase(t.Field(i).Name)) + switch value { + case "CPU": + value = value + " %" + case "MEM": + value = value + " %" + case "MEM USAGE": + value = "MEM USAGE / LIMIT" + } + headerNames[t.Field(i).Name] = value + } + } + for i := 0; i < times; i += step { var newStats []*libpod.PodContainerStats for _, p := range pods { @@ -163,7 +187,14 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error { outputJson(newStats) } else { - outputToStdOut(newStats) + results := podContainerStatsToPodStatOut(newStats) + if len(format) == 0 { + outputToStdOut(results) + } else { + if err := printPSFormat(c.Format, results, headerNames); err != nil { + return err + } + } } time.Sleep(time.Second) previousPodStats := new([]*libpod.PodContainerStats) @@ -177,28 +208,88 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error { return nil } -func outputToStdOut(stats []*libpod.PodContainerStats) { - outFormat := ("%-14s %-14s %-12s %-6s %-19s %-6s %-19s %-19s %-4s\n") - fmt.Printf(outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS") - for _, i := range stats { - if len(i.ContainerStats) == 0 { - fmt.Printf(outFormat, i.Pod.ID()[:12], "--", "--", "--", "--", "--", "--", "--", "--") - } - for _, c := range i.ContainerStats { - cpu := floatToPercentString(c.CPU) - memUsage := combineHumanValues(c.MemUsage, c.MemLimit) - memPerc := floatToPercentString(c.MemPerc) - netIO := combineHumanValues(c.NetInput, c.NetOutput) - blockIO := combineHumanValues(c.BlockInput, c.BlockOutput) - pids := pidsToString(c.PIDs) - containerName := c.Name - if len(c.Name) > 10 { - containerName = containerName[:10] +func podContainerStatsToPodStatOut(stats []*libpod.PodContainerStats) []*podStatOut { + var out []*podStatOut + for _, p := range stats { + for _, c := range p.ContainerStats { + o := podStatOut{ + CPU: floatToPercentString(c.CPU), + MemUsage: combineHumanValues(c.MemUsage, c.MemLimit), + Mem: floatToPercentString(c.MemPerc), + NetIO: combineHumanValues(c.NetInput, c.NetOutput), + BlockIO: combineHumanValues(c.BlockInput, c.BlockOutput), + PIDS: pidsToString(c.PIDs), + CID: c.ContainerID[:12], + Name: c.Name, + Pod: p.Pod.ID()[:12], } - fmt.Printf(outFormat, i.Pod.ID()[:12], c.ContainerID[:12], containerName, cpu, memUsage, memPerc, netIO, blockIO, pids) + out = append(out, &o) + } + } + return out +} + +type podStatOut struct { + CPU string + MemUsage string + Mem string + NetIO string + BlockIO string + PIDS string + Pod string + CID string + Name string +} + +func printPSFormat(format string, stats []*podStatOut, headerNames map[string]string) error { + if len(stats) == 0 { + return nil + } + + // Use a tabwriter to align column format + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) + // Spit out the header if "table" is present in the format + if strings.HasPrefix(format, "table") { + hformat := strings.Replace(strings.TrimSpace(format[5:]), " ", "\t", -1) + format = hformat + headerTmpl, err := template.New("header").Parse(hformat) + if err != nil { + return err + } + if err := headerTmpl.Execute(w, headerNames); err != nil { + return err + } + fmt.Fprintln(w, "") + } + + // Spit out the data rows now + dataTmpl, err := template.New("data").Parse(format) + if err != nil { + return err + } + for _, container := range stats { + if err := dataTmpl.Execute(w, container); err != nil { + return err + } + fmt.Fprintln(w, "") + } + // Flush the writer + return w.Flush() + +} + +func outputToStdOut(stats []*podStatOut) { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + outFormat := ("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n") + fmt.Fprintf(w, outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS") + for _, i := range stats { + if len(stats) == 0 { + fmt.Fprintf(w, outFormat, i.Pod, "--", "--", "--", "--", "--", "--", "--", "--") + } else { + fmt.Fprintf(w, outFormat, i.Pod, i.CID, i.Name, i.CPU, i.MemUsage, i.Mem, i.NetIO, i.BlockIO, i.PIDS) } } - fmt.Println() + w.Flush() } func getPreviousPodContainerStats(podID string, prev []*libpod.PodContainerStats) map[string]*libpod.ContainerStats { diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go index 63bd0f5cb..80082045d 100644 --- a/cmd/podman/pod_stop.go +++ b/cmd/podman/pod_stop.go @@ -33,6 +33,7 @@ var ( func init() { podStopCommand.Command = _podStopCommand + podStopCommand.SetUsageTemplate(UsageTemplate()) flags := podStopCommand.Flags() flags.BoolVarP(&podStopCommand.All, "all", "a", false, "Stop all running pods") flags.BoolVarP(&podStopCommand.Latest, "latest", "l", false, "Stop the latest pod podman is aware of") diff --git a/cmd/podman/pod_top.go b/cmd/podman/pod_top.go index f1db2fac5..411c782bd 100644 --- a/cmd/podman/pod_top.go +++ b/cmd/podman/pod_top.go @@ -39,6 +39,7 @@ the latest pod. func init() { podTopCommand.Command = _podTopCommand + podTopCommand.SetUsageTemplate(UsageTemplate()) flags := podTopCommand.Flags() flags.BoolVarP(&podTopCommand.Latest, "latest,", "l", false, "Act on the latest pod podman is aware of") flags.BoolVar(&podTopCommand.ListDescriptors, "list-descriptors", false, "") diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go index 17b771d4a..a917919c3 100644 --- a/cmd/podman/pod_unpause.go +++ b/cmd/podman/pod_unpause.go @@ -28,6 +28,7 @@ var ( func init() { podUnpauseCommand.Command = _podUnpauseCommand + podUnpauseCommand.SetUsageTemplate(UsageTemplate()) flags := podUnpauseCommand.Flags() flags.BoolVarP(&podUnpauseCommand.All, "all", "a", false, "Unpause all running pods") flags.BoolVarP(&podUnpauseCommand.Latest, "latest", "l", false, "Unpause the latest pod podman is aware of") diff --git a/cmd/podman/port.go b/cmd/podman/port.go index 488ef2ffe..be84da065 100644 --- a/cmd/podman/port.go +++ b/cmd/podman/port.go @@ -34,12 +34,12 @@ var ( func init() { portCommand.Command = _portCommand + portCommand.SetUsageTemplate(UsageTemplate()) flags := portCommand.Flags() flags.BoolVarP(&portCommand.All, "all", "a", false, "Display port information for all containers") flags.BoolVarP(&portCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - rootCmd.AddCommand(portCommand.Command) } func portCmd(c *cliconfig.PortValues) error { @@ -125,8 +125,13 @@ func portCmd(c *cliconfig.PortValues) error { if c.All { fmt.Println(con.ID()) } + + portmappings, err := con.PortMappings() + if err != nil { + return err + } // Iterate mappings - for _, v := range con.Config().PortMappings { + for _, v := range portmappings { hostIP := v.HostIP // Set host IP to 0.0.0.0 if blank if hostIP == "" { diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 002975b87..949f78a34 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -172,6 +172,7 @@ var ( func init() { psCommand.Command = _psCommand + psCommand.SetUsageTemplate(UsageTemplate()) flags := psCommand.Flags() flags.BoolVarP(&psCommand.All, "all", "a", false, "Show all the containers, default is only running containers") flags.StringSliceVarP(&psCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions given") @@ -187,9 +188,8 @@ func init() { flags.StringVar(&psCommand.Sort, "sort", "created", "Sort output by command, created, id, image, names, runningfor, size, or status") flags.BoolVar(&psCommand.Sync, "sync", false, "Sync container state with OCI runtime") - rootCmd.AddCommand(psCommand.Command) - } + func psCmd(c *cliconfig.PsValues) error { var ( filterFuncs []libpod.ContainerFilter diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index b9ef3f96b..6e060d6f5 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -42,6 +42,7 @@ specified, the image with the 'latest' tag (if it exists) is pulled func init() { pullCommand.Command = _pullCommand + pullCommand.SetUsageTemplate(UsageTemplate()) flags := pullCommand.Flags() flags.BoolVar(&pullCommand.AllTags, "all-tags", false, "All tagged images inthe repository will be pulled") flags.StringVar(&pullCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") @@ -51,7 +52,6 @@ func init() { flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") - rootCmd.AddCommand(pullCommand.Command) } // pullCmd gets the data from the command line and calls pullImage diff --git a/cmd/podman/push.go b/cmd/podman/push.go index a1ee00a65..bbe8a4027 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -2,8 +2,6 @@ package main import ( "fmt" - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/spf13/cobra" "io" "os" "strings" @@ -11,11 +9,13 @@ import ( "github.com/containers/image/directory" "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/libpod/adapter" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/util" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + "github.com/spf13/cobra" ) var ( @@ -40,6 +40,7 @@ var ( func init() { pushCommand.Command = _pushCommand + pushCommand.SetUsageTemplate(UsageTemplate()) flags := pushCommand.Flags() flags.MarkHidden("signature-policy") flags.StringVar(&pushCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") @@ -52,7 +53,6 @@ func init() { flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.StringVar(&pushCommand.SignBy, "sign-by", "", "Add a signature at the destination using the specified key") flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") - rootCmd.AddCommand(pushCommand.Command) } func pushCmd(c *cliconfig.PushValues) error { @@ -93,7 +93,7 @@ func pushCmd(c *cliconfig.PushValues) error { registryCreds = creds } - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not create runtime") } @@ -131,12 +131,7 @@ func pushCmd(c *cliconfig.PushValues) error { SignBy: signBy, } - newImage, err := runtime.ImageRuntime().NewFromLocal(srcName) - if err != nil { - return err - } - authfile := getAuthFile(c.Authfile) - return newImage.PushImageToHeuristicDestination(getContext(), destName, manifestType, authfile, c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil) + return runtime.Push(getContext(), srcName, destName, manifestType, authfile, c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil) } diff --git a/cmd/podman/refresh.go b/cmd/podman/refresh.go index 3890188b4..641748452 100644 --- a/cmd/podman/refresh.go +++ b/cmd/podman/refresh.go @@ -27,7 +27,7 @@ var ( func init() { refreshCommand.Command = _refreshCommand - rootCmd.AddCommand(refreshCommand.Command) + refreshCommand.SetUsageTemplate(UsageTemplate()) } func refreshCmd(c *cliconfig.RefreshValues) error { diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go index f94e745ed..235107b5c 100644 --- a/cmd/podman/restart.go +++ b/cmd/podman/restart.go @@ -32,6 +32,7 @@ var ( func init() { restartCommand.Command = _restartCommand + restartCommand.SetUsageTemplate(UsageTemplate()) flags := restartCommand.Flags() flags.BoolVarP(&restartCommand.All, "all", "a", false, "Restart all non-running containers") flags.BoolVarP(&restartCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") @@ -39,7 +40,6 @@ func init() { flags.UintVarP(&restartCommand.Timeout, "timeout", "t", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") flags.UintVar(&restartCommand.Timeout, "time", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") - rootCmd.AddCommand(restartCommand.Command) } func restartCmd(c *cliconfig.RestartValues) error { diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go index daee635f0..556cdb774 100644 --- a/cmd/podman/restore.go +++ b/cmd/podman/restore.go @@ -35,6 +35,7 @@ var ( func init() { restoreCommand.Command = _restoreCommand + restoreCommand.SetUsageTemplate(UsageTemplate()) flags := restoreCommand.Flags() flags.BoolVarP(&restoreCommand.All, "all", "a", false, "Restore all checkpointed containers") flags.BoolVarP(&restoreCommand.Keep, "keep", "k", false, "Keep all temporary checkpoint files") @@ -42,7 +43,6 @@ func init() { // TODO: add ContainerStateCheckpointed flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections") - rootCmd.AddCommand(restoreCommand.Command) } func restoreCmd(c *cliconfig.RestoreValues) error { diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index bb9a913c9..d170e5357 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -6,6 +6,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/libpod" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -33,13 +34,12 @@ Running containers will not be removed without the -f option. func init() { rmCommand.Command = _rmCommand + rmCommand.SetUsageTemplate(UsageTemplate()) flags := rmCommand.Flags() flags.BoolVarP(&rmCommand.All, "all", "a", false, "Remove all containers") flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running container. The default is false") flags.BoolVarP(&rmCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container (Not implemented yet)") - - rootCmd.AddCommand(rmCommand.Command) + flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container") } // saveCmd saves the image to either docker-archive or oci @@ -61,16 +61,24 @@ func rmCmd(c *cliconfig.RmValues) error { delContainers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all") if err != nil { + if c.Force && len(c.InputArgs) > 0 { + if errors.Cause(err) == libpod.ErrNoSuchCtr { + err = nil + } + runtime.RemoveContainersFromStorage(c.InputArgs) + } if len(delContainers) == 0 { return err } - fmt.Println(err.Error()) + if err != nil { + fmt.Println(err.Error()) + } } for _, container := range delContainers { con := container f := func() error { - return runtime.RemoveContainer(ctx, con, c.Force) + return runtime.RemoveContainer(ctx, con, c.Force, c.Volumes) } deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{ diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go index 70f58e844..c5bd1e190 100644 --- a/cmd/podman/rmi.go +++ b/cmd/podman/rmi.go @@ -29,11 +29,10 @@ var ( func init() { rmiCommand.Command = _rmiCommand + rmiCommand.SetUsageTemplate(UsageTemplate()) flags := rmiCommand.Flags() flags.BoolVarP(&rmiCommand.All, "all", "a", false, "Remove all images") flags.BoolVarP(&rmiCommand.Force, "force", "f", false, "Force Removal of the image") - - rootCmd.AddCommand(rmiCommand.Command) } func rmiCmd(c *cliconfig.RmiValues) error { diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 76b29cb84..64f8b6856 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -18,30 +18,29 @@ import ( ) var ( - runCommmand cliconfig.RunValues + runCommand cliconfig.RunValues runDescription = "Runs a command in a new container from the given image" - _runCommmand = &cobra.Command{ + _runCommand = &cobra.Command{ Use: "run", Short: "Run a command in a new container", Long: runDescription, RunE: func(cmd *cobra.Command, args []string) error { - runCommmand.InputArgs = args - runCommmand.GlobalFlags = MainGlobalOpts - return runCmd(&runCommmand) + runCommand.InputArgs = args + runCommand.GlobalFlags = MainGlobalOpts + return runCmd(&runCommand) }, Example: "IMAGE [COMMAND [ARG...]]", } ) func init() { - runCommmand.Command = _runCommmand - flags := runCommmand.Flags() + runCommand.Command = _runCommand + runCommand.SetUsageTemplate(UsageTemplate()) + flags := runCommand.Flags() flags.SetInterspersed(false) flags.Bool("sig-proxy", true, "Proxy received signals to the process (default true)") - getCreateFlags(&runCommmand.PodmanCommand) - - rootCmd.AddCommand(runCommmand.Command) + getCreateFlags(&runCommand.PodmanCommand) } func runCmd(c *cliconfig.RunValues) error { @@ -119,13 +118,21 @@ func runCmd(c *cliconfig.RunValues) error { } } if err := startAttachCtr(ctr, outputStream, errorStream, inputStream, c.String("detach-keys"), c.Bool("sig-proxy"), true); err != nil { + // We've manually detached from the container + // Do not perform cleanup, or wait for container exit code + // Just exit immediately + if errors.Cause(err) == libpod.ErrDetach { + exitCode = 0 + return nil + } + // This means the command did not exist exitCode = 127 if strings.Index(err.Error(), "permission denied") > -1 { exitCode = 126 } if c.IsSet("rm") { - if deleteError := runtime.RemoveContainer(ctx, ctr, true); deleteError != nil { + if deleteError := runtime.RemoveContainer(ctx, ctr, true, false); deleteError != nil { logrus.Errorf("unable to remove container %s after failing to start and attach to it", ctr.ID()) } } @@ -148,28 +155,12 @@ func runCmd(c *cliconfig.RunValues) error { exitCode = int(ecode) } - if createConfig.Rm { - return runtime.RemoveContainer(ctx, ctr, true) - } - - if err := ctr.Cleanup(ctx); err != nil { - // If the container has been removed already, no need to error on cleanup - // Also, if it was restarted, don't error either - if errors.Cause(err) == libpod.ErrNoSuchCtr || - errors.Cause(err) == libpod.ErrCtrRemoved || - errors.Cause(err) == libpod.ErrCtrStateInvalid { - return nil - } - - return err - } - return nil } // Read a container's exit file func readExitFile(runtimeTmp, ctrID string) (int, error) { - exitFile := filepath.Join(runtimeTmp, "exits", ctrID) + exitFile := filepath.Join(runtimeTmp, "exits", fmt.Sprintf("%s-old", ctrID)) logrus.Debugf("Attempting to read container %s exit code from file %s", ctrID, exitFile) diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go index ff8a282d3..f1c61ebda 100644 --- a/cmd/podman/runlabel.go +++ b/cmd/podman/runlabel.go @@ -37,6 +37,7 @@ Executes a command as described by a container image label. func init() { runlabelCommand.Command = _runlabelCommand + runlabelCommand.SetUsageTemplate(UsageTemplate()) flags := runlabelCommand.Flags() flags.StringVar(&runlabelCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&runlabelCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") diff --git a/cmd/podman/save.go b/cmd/podman/save.go index 766561f1a..1ae1bc872 100644 --- a/cmd/podman/save.go +++ b/cmd/podman/save.go @@ -47,13 +47,12 @@ var ( func init() { saveCommand.Command = _saveCommand + saveCommand.SetUsageTemplate(UsageTemplate()) flags := saveCommand.Flags() flags.BoolVar(&saveCommand.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)") flags.StringVar(&saveCommand.Format, "format", "", "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-dir (directory with v2s2 manifest type)") flags.StringVarP(&saveCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT") flags.BoolVarP(&saveCommand.Quiet, "quiet", "q", false, "Suppress the output") - - rootCmd.AddCommand(saveCommand.Command) } // saveCmd saves the image to either docker-archive or oci diff --git a/cmd/podman/search.go b/cmd/podman/search.go index b15da1f6d..2febee689 100644 --- a/cmd/podman/search.go +++ b/cmd/podman/search.go @@ -43,6 +43,7 @@ var ( func init() { searchCommand.Command = _searchCommand + searchCommand.SetUsageTemplate(UsageTemplate()) flags := searchCommand.Flags() flags.StringVar(&searchCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringSliceVarP(&searchCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") @@ -50,8 +51,6 @@ func init() { flags.IntVar(&searchCommand.Limit, "limit", 0, "Limit the number of results") flags.BoolVar(&searchCommand.NoTrunc, "no-trunc", false, "Do not truncate the output") flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") - - rootCmd.AddCommand(searchCommand.Command) } type searchParams struct { diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index c74d8fdce..81811e0f2 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -213,11 +213,16 @@ func NewBatchContainer(ctr *libpod.Container, opts PsOptions) (PsContainerOutput } } + ports, err := ctr.PortMappings() + if err != nil { + logrus.Errorf("unable to lookup namespace container for %s", ctr.ID()) + } + pso.ID = cid pso.Image = imageName pso.Command = command pso.Created = created - pso.Ports = portsToString(ctr.PortMappings()) + pso.Ports = portsToString(ports) pso.Names = ctr.Name() pso.IsInfra = ctr.IsInfra() pso.Status = status diff --git a/cmd/podman/sign.go b/cmd/podman/sign.go index a87c12556..ac0d985f5 100644 --- a/cmd/podman/sign.go +++ b/cmd/podman/sign.go @@ -38,12 +38,11 @@ var ( func init() { signCommand.Command = _signCommand + signCommand.SetUsageTemplate(UsageTemplate()) flags := signCommand.Flags() flags.StringVarP(&signCommand.Directory, "directory", "d", "", "Define an alternate directory to store signatures") flags.StringVar(&signCommand.SignBy, "sign-by", "", "Name of the signing key") - rootCmd.AddCommand(signCommand.Command) - } // SignatureStoreDir defines default directory to store signatures diff --git a/cmd/podman/start.go b/cmd/podman/start.go index 483ab4081..3a606d662 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -36,14 +36,13 @@ var ( func init() { startCommand.Command = _startCommand + startCommand.SetUsageTemplate(UsageTemplate()) flags := startCommand.Flags() flags.BoolVarP(&startCommand.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR") flags.StringVar(&startCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _") flags.BoolVarP(&startCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVarP(&startCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&startCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true if attaching, false otherwise)") - - rootCmd.AddCommand(startCommand.Command) } func startCmd(c *cliconfig.StartValues) error { @@ -109,6 +108,13 @@ func startCmd(c *cliconfig.StartValues) error { // attach to the container and also start it not already running err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, sigProxy, !ctrRunning) + if errors.Cause(err) == libpod.ErrDetach { + // User manually detached + // Exit cleanly immediately + exitCode = 0 + return nil + } + if ctrRunning { return err } @@ -138,7 +144,7 @@ func startCmd(c *cliconfig.StartValues) error { logrus.Errorf("unable to detect if container %s should be deleted", ctr.ID()) } if createArtifact.Rm { - if rmErr := runtime.RemoveContainer(ctx, ctr, true); rmErr != nil { + if rmErr := runtime.RemoveContainer(ctx, ctr, true, false); rmErr != nil { logrus.Errorf("unable to remove container %s after it failed to start", ctr.ID()) } } diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go index 8c79ed290..af9bbad0e 100644 --- a/cmd/podman/stats.go +++ b/cmd/podman/stats.go @@ -47,14 +47,13 @@ var ( func init() { statsCommand.Command = _statsCommand + statsCommand.SetUsageTemplate(UsageTemplate()) flags := statsCommand.Flags() flags.BoolVarP(&statsCommand.All, "all", "a", false, "Show all containers. Only running containers are shown by default. The default is false") flags.StringVar(&statsCommand.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template") flags.BoolVarP(&statsCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&statsCommand.NoReset, "no-reset", false, "Disable resetting the screen between intervals") flags.BoolVar(&statsCommand.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false") - - rootCmd.AddCommand(statsCommand.Command) } func statsCmd(c *cliconfig.StatsValues) error { diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index e2c16fe20..134d8a069 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -37,13 +37,12 @@ var ( func init() { stopCommand.Command = _stopCommand + stopCommand.SetUsageTemplate(UsageTemplate()) flags := stopCommand.Flags() flags.BoolVarP(&stopCommand.All, "all", "a", false, "Stop all running containers") flags.BoolVarP(&stopCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.UintVar(&stopCommand.Timeout, "time", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") flags.UintVarP(&stopCommand.Timeout, "timeout", "t", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") - - rootCmd.AddCommand(stopCommand.Command) } func stopCmd(c *cliconfig.StopValues) error { diff --git a/cmd/podman/system.go b/cmd/podman/system.go index f6b28fee1..741b79da5 100644 --- a/cmd/podman/system.go +++ b/cmd/podman/system.go @@ -17,7 +17,12 @@ var ( } ) +var systemCommands = []*cobra.Command{ + _infoCommand, +} + func init() { + systemCommand.AddCommand(systemCommands...) systemCommand.AddCommand(getSystemSubCommands()...) - rootCmd.AddCommand(systemCommand.Command) + systemCommand.SetUsageTemplate(UsageTemplate()) } diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go index c918fbe3b..a91d7bf0a 100644 --- a/cmd/podman/system_prune.go +++ b/cmd/podman/system_prune.go @@ -34,8 +34,8 @@ var ( ) func init() { - pruneSystemCommand.Command = _pruneSystemCommand + pruneSystemCommand.SetUsageTemplate(UsageTemplate()) flags := pruneSystemCommand.Flags() flags.BoolVarP(&pruneSystemCommand.All, "all", "a", false, "Remove all unused data") flags.BoolVarP(&pruneSystemCommand.Force, "force", "f", false, "Do not prompt for confirmation") @@ -76,7 +76,7 @@ Are you sure you want to continue? [y/N] `, volumeString) ctx := getContext() fmt.Println("Deleted Containers") - lasterr := pruneContainers(runtime, ctx, shared.Parallelize("rm"), false) + lasterr := pruneContainers(runtime, ctx, shared.Parallelize("rm"), false, false) if c.Bool("volumes") { fmt.Println("Deleted Volumes") err := volumePrune(runtime, getContext()) diff --git a/cmd/podman/tag.go b/cmd/podman/tag.go index 2c7cf4abb..135c2f7a0 100644 --- a/cmd/podman/tag.go +++ b/cmd/podman/tag.go @@ -26,8 +26,7 @@ var ( func init() { tagCommand.Command = _tagCommand - rootCmd.AddCommand(tagCommand.Command) - + tagCommand.SetUsageTemplate(UsageTemplate()) } func tagCmd(c *cliconfig.TagValues) error { diff --git a/cmd/podman/top.go b/cmd/podman/top.go index a03830ee5..51b813e5a 100644 --- a/cmd/podman/top.go +++ b/cmd/podman/top.go @@ -48,12 +48,11 @@ the latest container. func init() { topCommand.Command = _topCommand + topCommand.SetUsageTemplate(UsageTemplate()) flags := topCommand.Flags() flags.BoolVar(&topCommand.ListDescriptors, "list-descriptors", false, "") flags.MarkHidden("list-descriptors") flags.BoolVarP(&topCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - - rootCmd.AddCommand(topCommand.Command) } func topCmd(c *cliconfig.TopValues) error { diff --git a/cmd/podman/trust.go b/cmd/podman/trust.go index 6304586c3..8b02dcdc6 100644 --- a/cmd/podman/trust.go +++ b/cmd/podman/trust.go @@ -16,6 +16,7 @@ var ( ) func init() { + trustCommand.SetUsageTemplate(UsageTemplate()) trustCommand.AddCommand(getTrustSubCommands()...) imageCommand.AddCommand(trustCommand.Command) } diff --git a/cmd/podman/trust_set_show.go b/cmd/podman/trust_set_show.go index cd9438220..f3d1cadce 100644 --- a/cmd/podman/trust_set_show.go +++ b/cmd/podman/trust_set_show.go @@ -49,9 +49,10 @@ var ( ) func init() { - setTrustCommand.Command = _setTrustCommand + setTrustCommand.SetUsageTemplate(UsageTemplate()) showTrustCommand.Command = _showTrustCommand + showTrustCommand.SetUsageTemplate(UsageTemplate()) setFlags := setTrustCommand.Flags() setFlags.StringVar(&setTrustCommand.PolicyPath, "policypath", "", "") setFlags.MarkHidden("policypath") diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go index 4622e8276..20ea410c2 100644 --- a/cmd/podman/umount.go +++ b/cmd/podman/umount.go @@ -37,12 +37,11 @@ An unmount can be forced with the --force flag. func init() { umountCommand.Command = _umountCommand + umountCommand.SetUsageTemplate(UsageTemplate()) flags := umountCommand.Flags() flags.BoolVarP(&umountCommand.All, "all", "a", false, "Umount all of the currently mounted containers") flags.BoolVarP(&umountCommand.Force, "force", "f", false, "Force the complete umount all of the currently mounted containers") flags.BoolVarP(&umountCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - - rootCmd.AddCommand(umountCommand.Command) } func umountCmd(c *cliconfig.UmountValues) error { diff --git a/cmd/podman/unpause.go b/cmd/podman/unpause.go index fb3a7b57a..c8f85cfd3 100644 --- a/cmd/podman/unpause.go +++ b/cmd/podman/unpause.go @@ -35,10 +35,9 @@ var ( func init() { unpauseCommand.Command = _unpauseCommand + unpauseCommand.SetUsageTemplate(UsageTemplate()) flags := unpauseCommand.Flags() flags.BoolVarP(&unpauseCommand.All, "all", "a", false, "Unpause all paused containers") - - rootCmd.AddCommand(unpauseCommand.Command) } func unpauseCmd(c *cliconfig.UnpauseValues) error { diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go index 2b76e034e..ce54cfa85 100644 --- a/cmd/podman/varlink.go +++ b/cmd/podman/varlink.go @@ -1,4 +1,4 @@ -// +build varlink +// +build varlink,!remoteclient package main @@ -38,10 +38,9 @@ var ( func init() { varlinkCommand.Command = _varlinkCommand + varlinkCommand.SetUsageTemplate(UsageTemplate()) flags := varlinkCommand.Flags() flags.Int64VarP(&varlinkCommand.Timeout, "timeout", "t", 1000, "Time until the varlink session expires in milliseconds. Use 0 to disable the timeout") - - rootCmd.AddCommand(varlinkCommand.Command) } func varlinkCmd(c *cliconfig.VarlinkValues) error { @@ -84,7 +83,7 @@ func varlinkCmd(c *cliconfig.VarlinkValues) error { logrus.Infof("varlink service expired (use --timeout to increase session time beyond %d ms, 0 means never timeout)", c.Int64("timeout")) return nil default: - return errors.Errorf("unable to start varlink service") + return errors.Wrapf(err, "unable to start varlink service") } } diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 101232b0c..697d9ed90 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -2,15 +2,13 @@ # in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in the upstream libpod repository. interface io.podman - -# Version is the structure returned by GetVersion -type Version ( - version: string, - go_version: string, - git_commit: string, - built: int, - os_arch: string, - remote_api_version: int +type Volume ( + name: string, + labels: [string]string, + mountPoint: string, + driver: string, + options: [string]string, + scope: string ) type NotImplemented ( @@ -20,6 +18,7 @@ type NotImplemented ( type StringResponse ( message: string ) + # ContainerChanges describes the return struct for ListContainerChanges type ContainerChanges ( changed: []string, @@ -27,14 +26,25 @@ type ContainerChanges ( deleted: []string ) -# ImageInList describes the structure that is returned in -# ListImages. -type ImageInList ( +type VolumeCreateOpts ( + volumeName: string, + driver: string, + labels: [string]string, + options: [string]string +) + +type VolumeRemoveOpts ( + volumes: []string, + all: bool, + force: bool +) + +type Image ( id: string, parentId: string, repoTags: []string, repoDigests: []string, - created: string, + created: string, # as RFC3339 size: int, virtualSize: int, containers: int, @@ -45,16 +55,15 @@ type ImageInList ( # ImageHistory describes the returned structure from ImageHistory. type ImageHistory ( id: string, - created: string, + created: string, # as RFC3339 createdBy: string, tags: []string, size: int, comment: string ) -# ImageSearch is the returned structure for SearchImage. It is returned -# in array form. -type ImageSearch ( +# Represents a single search result from SearchImages +type ImageSearchResult ( description: string, is_official: bool, is_automated: bool, @@ -62,13 +71,12 @@ type ImageSearch ( star_count: int ) -# ListContainerData is the returned struct for an individual container -type ListContainerData ( +type Container ( id: string, image: string, imageid: string, command: []string, - createdat: string, + createdat: string, # as RFC3339 runningfor: string, status: string, ports: []ContainerPortMappings, @@ -303,37 +311,54 @@ type IDMap ( size: int ) +# BuildOptions are are used to describe describe physical attributes of the build +type BuildOptions ( + addHosts: []string, + cgroupParent: string, + cpuPeriod: int, + cpuQuota: int, + cpuShares: int, + cpusetCpus: string, + cpusetMems: string, + memory: int, + memorySwap: int, + shmSize: string, + ulimit: []string, + volume: []string +) + # BuildInfo is used to describe user input for building images type BuildInfo ( - # paths to one or more dockerfiles - dockerfile: []string, - tags: []string, - add_hosts: []string, - cgroup_parent: string, - cpu_period: int, - cpu_quota: int, - cpu_shares: int, - cpuset_cpus: string, - cpuset_mems: string, - memory: string, - memory_swap: string, - security_opts: []string, - shm_size: string, - ulimit: []string, - volume: []string, - squash: bool, - pull: bool, - pull_always: bool, - force_rm: bool, - rm: bool, - label: []string, + additionalTags: []string, annotations: []string, - build_args: [string]string, - image_format: string + buildArgs: [string]string, + buildOptions: BuildOptions, + cniConfigDir: string, + cniPluginDir: string, + compression: string, + contextDir: string, + defaultsMountFilePath: string, + dockerfiles: []string, + err: string, + forceRmIntermediateCtrs: bool, + iidfile: string, + label: []string, + layers: bool, + nocache: bool, + out: string, + output: string, + outputFormat: string, + pullPolicy: string, + quiet: bool, + remoteIntermediateCtrs: bool, + reportWriter: string, + runtimeArgs: []string, + signaturePolicyPath: string, + squash: bool ) -# BuildResponse is used to describe the responses for building images -type BuildResponse ( +# MoreResponse is a struct for when responses from varlink requires longer output +type MoreResponse ( logs: []string, id: string ) @@ -387,40 +412,35 @@ type Runlabel( name: string, pull: bool, signaturePolicyPath: string, - tlsVerify: bool, + tlsVerify: ?bool, label: string, extraArgs: []string, opts: [string]string ) -# Ping provides a response for developers to ensure their varlink setup is working. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.Ping -# { -# "ping": { -# "message": "OK" -# } -# } -# ~~~ -method Ping() -> (ping: StringResponse) - -# GetVersion returns a Version structure describing the libpod setup on their -# system. -method GetVersion() -> (version: Version) +# GetVersion returns version and build information of the podman service +method GetVersion() -> ( + version: string, + go_version: string, + git_commit: string, + built: string, # as RFC3339 + os_arch: string, + remote_api_version: int +) # GetInfo returns a [PodmanInfo](#PodmanInfo) struct that describes podman and its host such as storage stats, # build information of Podman, and system-wide registries. method GetInfo() -> (info: PodmanInfo) -# ListContainers returns a list of containers in no particular order. There are -# returned as an array of ListContainerData structs. See also [GetContainer](#GetContainer). -method ListContainers() -> (containers: []ListContainerData) +# ListContainers returns information about all containers. +# See also [GetContainer](#GetContainer). +method ListContainers() -> (containers: []Container) -# GetContainer takes a name or ID of a container and returns single ListContainerData -# structure. A [ContainerNotFound](#ContainerNotFound) error will be returned if the container cannot be found. -# See also [ListContainers](ListContainers) and [InspectContainer](#InspectContainer). -method GetContainer(name: string) -> (container: ListContainerData) +# GetContainer returns information about a single container. If a container +# with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound) +# error will be returned. See also [ListContainers](ListContainers) and +# [InspectContainer](#InspectContainer). +method GetContainer(id: string) -> (container: Container) # CreateContainer creates a new container from an image. It uses a [Create](#Create) type for input. The minimum # input required for CreateContainer is an image name. If the image name is not found, an [ImageNotFound](#ImageNotFound) @@ -508,7 +528,7 @@ method ExportContainer(name: string, path: string) -> (tarfile: string) method GetContainerStats(name: string) -> (container: ContainerStats) # This method has not be implemented yet. -method ResizeContainerTty() -> (notimplemented: NotImplemented) +# method ResizeContainerTty() -> (notimplemented: NotImplemented) # StartContainer starts a created or stopped container. It takes the name or ID of container. It returns # the container ID once started. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) @@ -540,10 +560,10 @@ method RestartContainer(name: string, timeout: int) -> (container: string) method KillContainer(name: string, signal: int) -> (container: string) # This method has not be implemented yet. -method UpdateContainer() -> (notimplemented: NotImplemented) +# method UpdateContainer() -> (notimplemented: NotImplemented) # This method has not be implemented yet. -method RenameContainer() -> (notimplemented: NotImplemented) +# method RenameContainer() -> (notimplemented: NotImplemented) # PauseContainer takes the name or ID of container and pauses it. If the container cannot be found, # a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise the ID of the container is returned. @@ -556,7 +576,7 @@ method PauseContainer(name: string) -> (container: string) method UnpauseContainer(name: string) -> (container: string) # This method has not be implemented yet. -method AttachToContainer() -> (notimplemented: NotImplemented) +# method AttachToContainer() -> (notimplemented: NotImplemented) # GetAttachSockets takes the name or ID of an existing container. It returns file paths for two sockets needed # to properly communicate with a container. The first is the actual I/O socket that the container uses. The @@ -580,7 +600,7 @@ method GetAttachSockets(name: string) -> (sockets: Sockets) # a [ContainerNotFound](#ContainerNotFound) error is returned. method WaitContainer(name: string) -> (exitcode: int) -# RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running +# RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running and a boolean indicating whether to remove builtin volumes # container can be stopped and removed. Upon successful removal of the container, its ID is returned. If the # container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned. # #### Example @@ -590,7 +610,7 @@ method WaitContainer(name: string) -> (exitcode: int) # "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20" # } # ~~~ -method RemoveContainer(name: string, force: bool) -> (container: string) +method RemoveContainer(name: string, force: bool, removeVolumes: bool) -> (container: string) # DeleteStoppedContainers will delete all containers that are not running. It will return a list the deleted # container IDs. See also [RemoveContainer](RemoveContainer). @@ -608,21 +628,21 @@ method RemoveContainer(name: string, force: bool) -> (container: string) # ~~~ method DeleteStoppedContainers() -> (containers: []string) -# ListImages returns an array of ImageInList structures which provide basic information about -# an image currently in storage. See also [InspectImage](InspectImage). -method ListImages() -> (images: []ImageInList) +# ListImages returns information about the images that are currently in storage. +# See also [InspectImage](InspectImage). +method ListImages() -> (images: []Image) -# GetImage returns a single image in an [ImageInList](#ImageInList) struct. You must supply an image name as a string. -# If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. -method GetImage(name: string) -> (image: ImageInList) +# GetImage returns information about a single image in storage. +# If the image caGetImage returns be found, [ImageNotFound](#ImageNotFound) will be returned. +method GetImage(id: string) -> (image: Image) # BuildImage takes a [BuildInfo](#BuildInfo) structure and builds an image. At a minimum, you must provide the -# 'dockerfile' and 'tags' options in the BuildInfo structure. It will return a [BuildResponse](#BuildResponse) structure +# 'dockerfile' and 'tags' options in the BuildInfo structure. It will return a [MoreResponse](#MoreResponse) structure # that contains the build logs and resulting image ID. -method BuildImage(build: BuildInfo) -> (image: BuildResponse) +method BuildImage(build: BuildInfo) -> (image: MoreResponse) # This function is not implemented yet. -method CreateImage() -> (notimplemented: NotImplemented) +# method CreateImage() -> (notimplemented: NotImplemented) # InspectImage takes the name or ID of an image and returns a string respresentation of data associated with the #image. You must serialize the string into JSON to use it further. An [ImageNotFound](#ImageNotFound) error will @@ -637,8 +657,8 @@ method HistoryImage(name: string) -> (history: []ImageHistory) # PushImage takes three input arguments: the name or ID of an image, the fully-qualified destination name of the image, # and a boolean as to whether tls-verify should be used (with false disabling TLS, not affecting the default behavior). # It will return an [ImageNotFound](#ImageNotFound) error if -# the image cannot be found in local storage; otherwise the ID of the image will be returned on success. -method PushImage(name: string, tag: string, tlsverify: bool, signaturePolicy: string, creds: string, certDir: string, compress: bool, format: string, removeSignatures: bool, signBy: string) -> (image: string) +# the image cannot be found in local storage; otherwise it will return a [MoreResponse](#MoreResponse) +method PushImage(name: string, tag: string, tlsverify: ?bool, signaturePolicy: string, creds: string, certDir: string, compress: bool, format: string, removeSignatures: bool, signBy: string) -> (reply: MoreResponse) # TagImage takes the name or ID of an image in local storage as well as the desired tag name. If the image cannot # be found, an [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of the image is returned on success. @@ -656,10 +676,10 @@ method TagImage(name: string, tagged: string) -> (image: string) # ~~~ method RemoveImage(name: string, force: bool) -> (image: string) -# SearchImage takes the string of an image name and a limit of searches from each registries to be returned. SearchImage -# will then use a glob-like match to find the image you are searching for. The images are returned in an array of -# ImageSearch structures which contain information about the image as well as its fully-qualified name. -method SearchImage(name: string, limit: int) -> (images: []ImageSearch) +# SearchImages searches available registries for images that contain the +# contents of "query" in their name. If "limit" is given, limits the amount of +# search results per registry. +method SearchImages(query: string, limit: ?int, tlsVerify: ?bool) -> (results: []ImageSearchResult) # DeleteUnusedImages deletes any images not associated with a container. The IDs of the deleted images are returned # in a string array. @@ -706,7 +726,7 @@ method ExportImage(name: string, destination: string, compress: bool, tags: []st # "id": "426866d6fa419873f97e5cbd320eeb22778244c1dfffa01c944db3114f55772e" # } # ~~~ -method PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: bool) -> (id: string) +method PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: ?bool) -> (id: string) # CreatePod creates a new empty pod. It uses a [PodCreate](#PodCreate) type for input. # On success, the ID of the newly created pod will be returned. @@ -913,10 +933,10 @@ method UnpausePod(name: string) -> (pod: string) method RemovePod(name: string, force: bool) -> (pod: string) # This method has not be implemented yet. -method WaitPod() -> (notimplemented: NotImplemented) +# method WaitPod() -> (notimplemented: NotImplemented) # This method has not been implemented yet. -method TopPod() -> (notimplemented: NotImplemented) +# method TopPod() -> (notimplemented: NotImplemented) # GetPodStats takes the name or ID of a pod and returns a pod name and slice of ContainerStats structure which # contains attributes like memory and cpu usage. If the pod cannot be found, a [PodNotFound](#PodNotFound) @@ -1020,19 +1040,19 @@ method UnmountContainer(name: string, force: bool) -> () method ImagesPrune(all: bool) -> (pruned: []string) # This function is not implemented yet. -method ListContainerPorts(name: string) -> (notimplemented: NotImplemented) +# method ListContainerPorts(name: string) -> (notimplemented: NotImplemented) # GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod # and its containers. The description is in YAML. See also [ReplayKube](ReplayKube). -method GenerateKube() -> (notimplemented: NotImplemented) +# method GenerateKube() -> (notimplemented: NotImplemented) # GenerateKubeService generates a Kubernetes v1 Service description of a Podman container or pod # and its containers. The description is in YAML. See also [GenerateKube](GenerateKube). -method GenerateKubeService() -> (notimplemented: NotImplemented) +# method GenerateKubeService() -> (notimplemented: NotImplemented) # ReplayKube recreates a pod and its containers based on a Kubernetes v1 Pod description (in YAML) # like that created by GenerateKube. See also [GenerateKube](GenerateKube). -method ReplayKube() -> (notimplemented: NotImplemented) +# method ReplayKube() -> (notimplemented: NotImplemented) # ContainerConfig returns a container's config in string form. This call is for # development of Podman only and generally should not be used. @@ -1050,14 +1070,29 @@ method ContainerInspectData(name: string) -> (config: string) # development of Podman only and generally should not be used. method ContainerStateData(name: string) -> (config: string) +# Sendfile allows a remote client to send a file to the host method SendFile(type: string, length: int) -> (file_handle: string) + +# ReceiveFile allows the host to send a remote client a file method ReceiveFile(path: string, delete: bool) -> (len: int) +# VolumeCreate creates a volume on a remote host +method VolumeCreate(options: VolumeCreateOpts) -> (volumeName: string) + +# VolumeRemove removes a volume on a remote host +method VolumeRemove(options: VolumeRemoveOpts) -> (volumeNames: []string) + +# GetVolumes gets slice of the volumes on a remote host +method GetVolumes(args: []string, all: bool) -> (volumes: []Volume) + +# VolumesPrune removes unused volumes on the host +method VolumesPrune() -> (prunedNames: []string, prunedErrors: []string) + # ImageNotFound means the image could not be found by the provided name or ID in local storage. -error ImageNotFound (name: string) +error ImageNotFound (id: string) # ContainerNotFound means the container could not be found by the provided name or ID in local storage. -error ContainerNotFound (name: string) +error ContainerNotFound (id: string) # NoContainerRunning means none of the containers requested are running in a command that requires a running container. error NoContainerRunning () diff --git a/cmd/podman/varlink_dummy.go b/cmd/podman/varlink_dummy.go index 8d7a7e8ca..430511d72 100644 --- a/cmd/podman/varlink_dummy.go +++ b/cmd/podman/varlink_dummy.go @@ -2,8 +2,10 @@ package main -import ( - "github.com/containers/libpod/cmd/podman/cliconfig" -) +import "github.com/spf13/cobra" -var varlinkCommand *cliconfig.PodmanCommand +var ( + _varlinkCommand = &cobra.Command{ + Use: "", + } +) diff --git a/cmd/podman/version.go b/cmd/podman/version.go index 0e7cd43d5..c65ba94f9 100644 --- a/cmd/podman/version.go +++ b/cmd/podman/version.go @@ -28,9 +28,9 @@ var ( func init() { versionCommand.Command = _versionCommand + versionCommand.SetUsageTemplate(UsageTemplate()) flags := versionCommand.Flags() flags.StringVarP(&versionCommand.Format, "format", "f", "", "Change the output format to JSON or a Go template") - rootCmd.AddCommand(versionCommand.Command) } // versionCmd gets and prints version info for version command diff --git a/cmd/podman/volume.go b/cmd/podman/volume.go index bae778e52..8a8664151 100644 --- a/cmd/podman/volume.go +++ b/cmd/podman/volume.go @@ -16,8 +16,16 @@ var volumeCommand = cliconfig.PodmanCommand{ Long: volumeDescription, }, } +var volumeSubcommands = []*cobra.Command{ + _volumeCreateCommand, + _volumeLsCommand, + _volumeRmCommand, + _volumeInspectCommand, + _volumePruneCommand, +} func init() { - volumeCommand.AddCommand(getVolumeSubCommands()...) + volumeCommand.SetUsageTemplate(UsageTemplate()) + volumeCommand.AddCommand(volumeSubcommands...) rootCmd.AddCommand(volumeCommand.Command) } diff --git a/cmd/podman/volume_create.go b/cmd/podman/volume_create.go index e0ff4c341..fe5d69e0b 100644 --- a/cmd/podman/volume_create.go +++ b/cmd/podman/volume_create.go @@ -4,8 +4,7 @@ import ( "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -33,6 +32,7 @@ be created at.` func init() { volumeCreateCommand.Command = _volumeCreateCommand + volumeCreateCommand.SetUsageTemplate(UsageTemplate()) flags := volumeCreateCommand.Flags() flags.StringVar(&volumeCreateCommand.Driver, "driver", "", "Specify volume driver name (default local)") flags.StringSliceVarP(&volumeCreateCommand.Label, "label", "l", []string{}, "Set metadata for a volume (default [])") @@ -41,13 +41,7 @@ func init() { } func volumeCreateCmd(c *cliconfig.VolumeCreateValues) error { - var ( - options []libpod.VolumeCreateOption - err error - volName string - ) - - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -57,36 +51,19 @@ func volumeCreateCmd(c *cliconfig.VolumeCreateValues) error { return errors.Errorf("too many arguments, create takes at most 1 argument") } - if len(c.InputArgs) > 0 { - volName = c.InputArgs[0] - options = append(options, libpod.WithVolumeName(volName)) - } - - if c.Flag("driver").Changed { - options = append(options, libpod.WithVolumeDriver(c.String("driver"))) - } - labels, err := getAllLabels([]string{}, c.Label) if err != nil { return errors.Wrapf(err, "unable to process labels") } - if len(labels) != 0 { - options = append(options, libpod.WithVolumeLabels(labels)) - } opts, err := getAllLabels([]string{}, c.Opt) if err != nil { return errors.Wrapf(err, "unable to process options") } - if len(options) != 0 { - options = append(options, libpod.WithVolumeOptions(opts)) - } - vol, err := runtime.NewVolume(getContext(), options...) - if err != nil { - return err + volumeName, err := runtime.CreateVolume(getContext(), c, labels, opts) + if err == nil { + fmt.Println(volumeName) } - fmt.Printf("%s\n", vol.Name()) - - return nil + return err } diff --git a/cmd/podman/volume_inspect.go b/cmd/podman/volume_inspect.go index 7cb5703da..928ef37d0 100644 --- a/cmd/podman/volume_inspect.go +++ b/cmd/podman/volume_inspect.go @@ -2,9 +2,8 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -31,6 +30,7 @@ from JSON to a Go template. func init() { volumeInspectCommand.Command = _volumeInspectCommand + volumeInspectCommand.SetUsageTemplate(UsageTemplate()) flags := volumeInspectCommand.Flags() flags.BoolVarP(&volumeInspectCommand.All, "all", "a", false, "Inspect all volumes") flags.StringVarP(&volumeInspectCommand.Format, "format", "f", "json", "Format volume output using Go template") @@ -38,22 +38,19 @@ func init() { } func volumeInspectCmd(c *cliconfig.VolumeInspectValues) error { - var err error + if (c.All && len(c.InputArgs) > 0) || (!c.All && len(c.InputArgs) < 1) { + return errors.New("provide one or more volume names or use --all") + } - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - opts := volumeLsOptions{ - Format: c.Format, - } - - vols, lastError := getVolumesFromContext(&c.PodmanCommand, runtime) - if lastError != nil { - logrus.Errorf("%q", lastError) + vols, err := runtime.InspectVolumes(getContext(), c) + if err != nil { + return err } - - return generateVolLsOutput(vols, opts, runtime) + return generateVolLsOutput(vols, volumeLsOptions{Format: c.Format}) } diff --git a/cmd/podman/volume_ls.go b/cmd/podman/volume_ls.go index 78fdfed64..0edadc5ac 100644 --- a/cmd/podman/volume_ls.go +++ b/cmd/podman/volume_ls.go @@ -6,8 +6,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -62,6 +61,7 @@ and the output format can be changed to JSON or a user specified Go template. func init() { volumeLsCommand.Command = _volumeLsCommand + volumeLsCommand.SetUsageTemplate(UsageTemplate()) flags := volumeLsCommand.Flags() flags.StringVarP(&volumeLsCommand.Filter, "filter", "f", "", "Filter volume output") @@ -70,7 +70,7 @@ func init() { } func volumeLsCmd(c *cliconfig.VolumeLsValues) error { - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -86,7 +86,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { opts.Format = genVolLsFormat(c) // Get the filter functions based on any filters set - var filterFuncs []libpod.VolumeFilter + var filterFuncs []adapter.VolumeFilter if c.Filter != "" { filters := strings.Split(c.Filter, ",") for _, f := range filters { @@ -94,7 +94,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { if len(filterSplit) < 2 { return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) } - generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1], runtime) + generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1]) if err != nil { return errors.Wrapf(err, "invalid filter") } @@ -102,13 +102,12 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { } } - volumes, err := runtime.GetAllVolumes() + volumes, err := runtime.Volumes(getContext()) if err != nil { return err } - // Get the volumes that match the filter - volsFiltered := make([]*libpod.Volume, 0, len(volumes)) + volsFiltered := make([]*adapter.Volume, 0, len(volumes)) for _, vol := range volumes { include := true for _, filter := range filterFuncs { @@ -119,7 +118,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { volsFiltered = append(volsFiltered, vol) } } - return generateVolLsOutput(volsFiltered, opts, runtime) + return generateVolLsOutput(volsFiltered, opts) } // generate the template based on conditions given @@ -205,7 +204,7 @@ func getVolTemplateOutput(lsParams []volumeLsJSONParams, opts volumeLsOptions) ( } // getVolJSONParams returns the volumes in JSON format -func getVolJSONParams(volumes []*libpod.Volume, opts volumeLsOptions, runtime *libpod.Runtime) ([]volumeLsJSONParams, error) { +func getVolJSONParams(volumes []*adapter.Volume) []volumeLsJSONParams { var lsOutput []volumeLsJSONParams for _, volume := range volumes { @@ -220,25 +219,19 @@ func getVolJSONParams(volumes []*libpod.Volume, opts volumeLsOptions, runtime *l lsOutput = append(lsOutput, params) } - return lsOutput, nil + return lsOutput } // generateVolLsOutput generates the output based on the format, JSON or Go Template, and prints it out -func generateVolLsOutput(volumes []*libpod.Volume, opts volumeLsOptions, runtime *libpod.Runtime) error { +func generateVolLsOutput(volumes []*adapter.Volume, opts volumeLsOptions) error { if len(volumes) == 0 && opts.Format != formats.JSONString { return nil } - lsOutput, err := getVolJSONParams(volumes, opts, runtime) - if err != nil { - return err - } + lsOutput := getVolJSONParams(volumes) var out formats.Writer switch opts.Format { case formats.JSONString: - if err != nil { - return errors.Wrapf(err, "unable to create JSON for volume output") - } out = formats.JSONStructArray{Output: volLsToGeneric([]volumeLsTemplateParams{}, lsOutput)} default: lsOutput, err := getVolTemplateOutput(lsOutput, opts) @@ -251,18 +244,18 @@ func generateVolLsOutput(volumes []*libpod.Volume, opts volumeLsOptions, runtime } // generateVolumeFilterFuncs returns the true if the volume matches the filter set, otherwise it returns false. -func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (func(volume *libpod.Volume) bool, error) { +func generateVolumeFilterFuncs(filter, filterValue string) (func(volume *adapter.Volume) bool, error) { switch filter { case "name": - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { return strings.Contains(v.Name(), filterValue) }, nil case "driver": - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { return v.Driver() == filterValue }, nil case "scope": - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { return v.Scope() == filterValue }, nil case "label": @@ -273,7 +266,7 @@ func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runti } else { filterValue = "" } - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { for labelKey, labelValue := range v.Labels() { if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { return true @@ -289,7 +282,7 @@ func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runti } else { filterValue = "" } - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { for labelKey, labelValue := range v.Options() { if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { return true diff --git a/cmd/podman/volume_prune.go b/cmd/podman/volume_prune.go index 29dc6ead4..a2205140f 100644 --- a/cmd/podman/volume_prune.go +++ b/cmd/podman/volume_prune.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -37,29 +36,27 @@ using force. func init() { volumePruneCommand.Command = _volumePruneCommand + volumePruneCommand.SetUsageTemplate(UsageTemplate()) flags := volumePruneCommand.Flags() flags.BoolVarP(&volumePruneCommand.Force, "force", "f", false, "Do not prompt for confirmation") } func volumePrune(runtime *adapter.LocalRuntime, ctx context.Context) error { - var lastError error - - volumes, err := runtime.GetAllVolumes() - if err != nil { - return err + prunedNames, prunedErrors := runtime.PruneVolumes(ctx) + for _, name := range prunedNames { + fmt.Println(name) + } + if len(prunedErrors) == 0 { + return nil } + // Grab the last error + lastError := prunedErrors[len(prunedErrors)-1] + // Remove the last error from the error slice + prunedErrors = prunedErrors[:len(prunedErrors)-1] - for _, vol := range volumes { - err = runtime.RemoveVolume(ctx, vol, false, true) - if err == nil { - fmt.Println(vol.Name()) - } else if err != libpod.ErrVolumeBeingUsed { - if lastError != nil { - logrus.Errorf("%q", lastError) - } - lastError = errors.Wrapf(err, "failed to remove volume %q", vol.Name()) - } + for _, err := range prunedErrors { + logrus.Errorf("%q", err) } return lastError } @@ -84,6 +81,5 @@ func volumePruneCmd(c *cliconfig.VolumePruneValues) error { return nil } } - return volumePrune(runtime, getContext()) } diff --git a/cmd/podman/volume_rm.go b/cmd/podman/volume_rm.go index b02a06ed9..f301749e9 100644 --- a/cmd/podman/volume_rm.go +++ b/cmd/podman/volume_rm.go @@ -4,9 +4,8 @@ import ( "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -35,6 +34,7 @@ not being used by any containers. To remove the volumes anyways, use the func init() { volumeRmCommand.Command = _volumeRmCommand + volumeRmCommand.SetUsageTemplate(UsageTemplate()) flags := volumeRmCommand.Flags() flags.BoolVarP(&volumeRmCommand.All, "all", "a", false, "Remove all volumes") flags.BoolVarP(&volumeRmCommand.Force, "force", "f", false, "Remove a volume by force, even if it is being used by a container") @@ -43,25 +43,28 @@ func init() { func volumeRmCmd(c *cliconfig.VolumeRmValues) error { var err error - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + if (len(c.InputArgs) > 0 && c.All) || (len(c.InputArgs) < 1 && !c.All) { + return errors.New("choose either one or more volumes or all") + } + + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - - ctx := getContext() - - vols, lastError := getVolumesFromContext(&c.PodmanCommand, runtime) - for _, vol := range vols { - err = runtime.RemoveVolume(ctx, vol, c.Force, false) - if err != nil { - if lastError != nil { - logrus.Errorf("%q", lastError) - } - lastError = errors.Wrapf(err, "failed to remove volume %q", vol.Name()) - } else { - fmt.Println(vol.Name()) + deletedVolumeNames, err := runtime.RemoveVolumes(getContext(), c) + if err != nil { + if len(deletedVolumeNames) > 0 { + printDeleteVolumes(deletedVolumeNames) + return err } } - return lastError + printDeleteVolumes(deletedVolumeNames) + return err +} + +func printDeleteVolumes(volumes []string) { + for _, v := range volumes { + fmt.Println(v) + } } diff --git a/cmd/podman/wait.go b/cmd/podman/wait.go index fa195b7ce..616c8feb5 100644 --- a/cmd/podman/wait.go +++ b/cmd/podman/wait.go @@ -34,11 +34,10 @@ var ( func init() { waitCommand.Command = _waitCommand + waitCommand.SetUsageTemplate(UsageTemplate()) flags := waitCommand.Flags() flags.UintVarP(&waitCommand.Interval, "interval", "i", 250, "Milliseconds to wait before polling for completion") flags.BoolVarP(&waitCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - - rootCmd.AddCommand(waitCommand.Command) } func waitCmd(c *cliconfig.WaitValues) error { |