diff options
Diffstat (limited to 'cmd')
106 files changed, 1535 insertions, 1424 deletions
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go index 37f8afbad..b78633ed6 100644 --- a/cmd/podman/attach.go +++ b/cmd/podman/attach.go @@ -31,7 +31,7 @@ func init() { attachCommand.SetHelpTemplate(HelpTemplate()) 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.StringVar(&attachCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-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") flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") @@ -51,6 +51,6 @@ func attachCmd(c *cliconfig.AttachValues) error { if err != nil { return errors.Wrapf(err, "error creating runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) return runtime.Attach(getContext(), c) } diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 6e70c6540..14ac51889 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -57,14 +57,20 @@ func init() { budFlags := buildahcli.GetBudFlags(&budFlagsValues) flag := budFlags.Lookup("pull") - flag.Value.Set("true") + if err := flag.Value.Set("true"); err != nil { + logrus.Error("unable to set pull flag to true") + } flag.DefValue = "true" layerFlags := buildahcli.GetLayerFlags(&layerValues) flag = layerFlags.Lookup("layers") - flag.Value.Set(useLayers()) - flag.DefValue = (useLayers()) + if err := flag.Value.Set(useLayers()); err != nil { + logrus.Error("unable to set uselayers") + } + flag.DefValue = useLayers() flag = layerFlags.Lookup("force-rm") - flag.Value.Set("true") + if err := flag.Value.Set("true"); err != nil { + logrus.Error("unable to set force-rm flag to true") + } flag.DefValue = "true" fromAndBugFlags := buildahcli.GetFromAndBudFlags(&fromAndBudValues, &userNSValues, &namespaceValues) @@ -72,7 +78,7 @@ func init() { flags.AddFlagSet(&budFlags) flags.AddFlagSet(&layerFlags) flags.AddFlagSet(&fromAndBugFlags) - flags.MarkHidden("signature-policy") + markFlagHidden(flags, "signature-policy") } func getDockerfiles(files []string) []string { @@ -95,6 +101,10 @@ func getNsValues(c *cliconfig.BuildValues) ([]buildah.NamespaceOption, error) { Name: string(specs.NetworkNamespace), Host: true, }) + } else if c.Network == "container" { + ret = append(ret, buildah.NamespaceOption{ + Name: string(specs.NetworkNamespace), + }) } else if c.Network[0] == '/' { ret = append(ret, buildah.NamespaceOption{ Name: string(specs.NetworkNamespace), @@ -177,7 +187,6 @@ func buildCmd(c *cliconfig.BuildValues) error { } contextDir = absDir } - cliArgs = Tail(cliArgs) } else { // No context directory or URL was specified. Try to use the // home of the first locally-available Dockerfile. @@ -218,7 +227,7 @@ func buildCmd(c *cliconfig.BuildValues) error { } // end from buildah - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) var stdout, stderr, reporter *os.File stdout = os.Stdout @@ -303,16 +312,6 @@ func buildCmd(c *cliconfig.BuildValues) error { return runtime.Build(getContext(), c, options, dockerfiles) } -// Tail returns a string slice after the first element unless there are -// not enough elements, then it returns an empty slice. This is to replace -// the urfavecli Tail method for args -func Tail(a []string) []string { - if len(a) >= 2 { - return []string(a)[1:] - } - return []string{} -} - // useLayers returns false if BUILDAH_LAYERS is set to "0" or "false" // otherwise it returns true func useLayers() string { diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go index 234d683bb..22cdb1f39 100644 --- a/cmd/podman/checkpoint.go +++ b/cmd/podman/checkpoint.go @@ -2,7 +2,6 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" @@ -46,6 +45,8 @@ func init() { flags.BoolVar(&checkpointCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections") flags.BoolVarP(&checkpointCommand.All, "all", "a", false, "Checkpoint all running containers") flags.BoolVarP(&checkpointCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.StringVarP(&checkpointCommand.Export, "export", "e", "", "Export the checkpoint image to a tar.gz") + flags.BoolVar(&checkpointCommand.IgnoreRootfs, "ignore-rootfs", false, "Do not include root file-system changes when exporting") markFlagHiddenForRemoteClient("latest", flags) } @@ -58,12 +59,7 @@ func checkpointCmd(c *cliconfig.CheckpointValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) - options := libpod.ContainerCheckpointOptions{ - Keep: c.Keep, - KeepRunning: c.LeaveRunning, - TCPEstablished: c.TcpEstablished, - } - return runtime.Checkpoint(c, options) + defer runtime.DeferredShutdown(false) + return runtime.Checkpoint(c) } diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go index 4ff744ae5..c00654162 100644 --- a/cmd/podman/cleanup.go +++ b/cmd/podman/cleanup.go @@ -1,3 +1,5 @@ +//+build !remoteclient + package main import ( @@ -50,7 +52,7 @@ func cleanupCmd(c *cliconfig.CleanupValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ok, failures, err := runtime.CleanupContainers(getContext(), c) if err != nil { diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index aaa4513d8..025f40cf6 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -33,9 +33,11 @@ type MainFlags struct { LogLevel string TmpDir string - RemoteUserName string - RemoteHost string - VarlinkAddress string + RemoteUserName string + RemoteHost string + VarlinkAddress string + ConnectionName string + RemoteConfigFilePath string } type AttachValues struct { @@ -89,6 +91,8 @@ type CheckpointValues struct { TcpEstablished bool All bool Latest bool + Export string + IgnoreRootfs bool } type CommitValues struct { @@ -110,18 +114,20 @@ type DiffValues struct { PodmanCommand Archive bool Format string + Latest bool } type ExecValues struct { PodmanCommand - Env []string - Privileged bool - Interfactive bool - Tty bool - User string - Latest bool - Workdir string - PreserveFDs int + DetachKeys string + Env []string + Privileged bool + Interactive bool + Tty bool + User string + Latest bool + Workdir string + PreserveFDs int } type ImageExistsValues struct { @@ -142,7 +148,8 @@ type ExportValues struct { } type GenerateKubeValues struct { PodmanCommand - Service bool + Service bool + Filename string } type GenerateSystemdValues struct { @@ -426,6 +433,9 @@ type RestoreValues struct { Keep bool Latest bool TcpEstablished bool + Import string + Name string + IgnoreRootfs bool } type RmValues struct { @@ -433,6 +443,7 @@ type RmValues struct { All bool Force bool Latest bool + Storage bool Volumes bool } diff --git a/cmd/podman/cliconfig/create.go b/cmd/podman/cliconfig/create.go index 49ab3d827..5fb2eed10 100644 --- a/cmd/podman/cliconfig/create.go +++ b/cmd/podman/cliconfig/create.go @@ -24,4 +24,5 @@ type BuildValues struct { type CpValues struct { PodmanCommand Extract bool + Pause bool } diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 2ac465b9d..27f3fc214 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -11,8 +11,6 @@ const remoteclient = false // Commands that the local client implements func getMainCommands() []*cobra.Command { rootCommands := []*cobra.Command{ - _commitCommand, - _execCommand, _playCommand, _loginCommand, _logoutCommand, @@ -20,6 +18,7 @@ func getMainCommands() []*cobra.Command { _refreshCommand, _searchCommand, _statsCommand, + _umountCommand, _unshareCommand, } @@ -41,8 +40,6 @@ func getContainerSubCommands() []*cobra.Command { return []*cobra.Command{ _cleanupCommand, - _commitCommand, - _execCommand, _mountCommand, _refreshCommand, _runlabelCommand, diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index 2b38bab35..e98b71514 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -2,16 +2,10 @@ package main import ( "fmt" - "io" - "os" "strings" - "github.com/containers/buildah" - "github.com/containers/image/manifest" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod" - "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -31,10 +25,13 @@ var ( commitCommand.Remote = remoteclient return commitCmd(&commitCommand) }, - 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`, + Example: `podman commit -q --message "committing container to image" reverent_golick image-committed + podman commit -q --author "firstName lastName" reverent_golick image-committed + podman commit -q --pause=false containerID image-committed`, } + + // ChangeCmds is the list of valid Changes commands to passed to the Commit call + ChangeCmds = []string{"CMD", "ENTRYPOINT", "ENV", "EXPOSE", "LABEL", "ONBUILD", "STOPSIGNAL", "USER", "VOLUME", "WORKDIR"} ) func init() { @@ -42,7 +39,7 @@ func init() { commitCommand.SetHelpTemplate(HelpTemplate()) commitCommand.SetUsageTemplate(UsageTemplate()) flags := commitCommand.Flags() - flags.StringArrayVarP(&commitCommand.Change, "change", "c", []string{}, fmt.Sprintf("Apply the following possible instructions to the created image (default []): %s", strings.Join(libpod.ChangeCmds, " | "))) + flags.StringArrayVarP(&commitCommand.Change, "change", "c", []string{}, fmt.Sprintf("Apply the following possible instructions to the created image (default []): %s", strings.Join(ChangeCmds, " | "))) flags.StringVarP(&commitCommand.Format, "format", "f", "oci", "`Format` of the image manifest and metadata") flags.StringVarP(&commitCommand.Message, "message", "m", "", "Set commit message for imported image") flags.StringVarP(&commitCommand.Author, "author", "a", "", "Set the author for the image committed") @@ -52,32 +49,17 @@ func init() { } func commitCmd(c *cliconfig.CommitValues) error { - runtime, err := libpodruntime.GetRuntime(getContext(), &c.PodmanCommand) + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) - var ( - writer io.Writer - mimeType string - ) args := c.InputArgs if len(args) != 2 { return errors.Errorf("you must provide a container name or ID and a target image name") } - switch c.Format { - case "oci": - mimeType = buildah.OCIv1ImageManifest - if c.Flag("message").Changed { - return errors.Errorf("messages are only compatible with the docker image format (-f docker)") - } - case "docker": - mimeType = manifest.DockerV2Schema2MediaType - default: - return errors.Errorf("unrecognized image format %q", c.Format) - } container := args[0] reference := args[1] if c.Flag("change").Changed { @@ -86,44 +68,16 @@ func commitCmd(c *cliconfig.CommitValues) error { if len(splitChange) == 1 { splitChange = strings.Split(strings.ToUpper(change), " ") } - if !util.StringInSlice(splitChange[0], libpod.ChangeCmds) { + if !util.StringInSlice(splitChange[0], ChangeCmds) { return errors.Errorf("invalid syntax for --change: %s", change) } } } - if !c.Quiet { - writer = os.Stderr - } - ctr, err := runtime.LookupContainer(container) - if err != nil { - return errors.Wrapf(err, "error looking up container %q", container) - } - - rtc, err := runtime.GetConfig() - if err != nil { - return err - } - - sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false) - coptions := buildah.CommitOptions{ - SignaturePolicyPath: rtc.SignaturePolicyPath, - ReportWriter: writer, - SystemContext: sc, - PreferredManifestType: mimeType, - } - options := libpod.ContainerCommitOptions{ - CommitOptions: coptions, - Pause: c.Pause, - IncludeVolumes: c.IncludeVolumes, - Message: c.Message, - Changes: c.Change, - Author: c.Author, - } - newImage, err := ctr.Commit(getContext(), reference, options) + iid, err := runtime.Commit(getContext(), c, container, reference) if err != nil { return err } - fmt.Println(newImage.ID()) + fmt.Println(iid) return nil } diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 054b01247..1e9092bd6 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -4,14 +4,13 @@ import ( "context" "fmt" "os" - "path/filepath" "strings" "github.com/containers/buildah" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/rootless" - "github.com/containers/storage" "github.com/fatih/camelcase" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -19,8 +18,7 @@ import ( ) var ( - stores = make(map[storage.Store]struct{}) - json = jsoniter.ConfigCompatibleWithStandardLibrary + json = jsoniter.ConfigCompatibleWithStandardLibrary ) const ( @@ -80,58 +78,6 @@ func commandRunE() func(*cobra.Command, []string) error { } } -// getAllOrLatestContainers tries to return the correct list of containers -// depending if --all, --latest or <container-id> is used. -// It requires the Context (c) and the Runtime (runtime). As different -// commands are using different container state for the --all option -// the desired state has to be specified in filterState. If no filter -// is desired a -1 can be used to get all containers. For a better -// error message, if the filter fails, a corresponding verb can be -// specified which will then appear in the error message. -func getAllOrLatestContainers(c *cliconfig.PodmanCommand, runtime *libpod.Runtime, filterState libpod.ContainerStatus, verb string) ([]*libpod.Container, error) { - var containers []*libpod.Container - var lastError error - var err error - if c.Bool("all") { - if filterState != -1 { - var filterFuncs []libpod.ContainerFilter - filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { - state, _ := c.State() - return state == filterState - }) - containers, err = runtime.GetContainers(filterFuncs...) - } else { - containers, err = runtime.GetContainers() - } - if err != nil { - return nil, errors.Wrapf(err, "unable to get %s containers", verb) - } - } else if c.Bool("latest") { - lastCtr, err := runtime.GetLatestContainer() - if err != nil { - return nil, errors.Wrapf(err, "unable to get latest container") - } - containers = append(containers, lastCtr) - } else { - args := c.InputArgs - for _, i := range args { - container, err := runtime.LookupContainer(i) - if err != nil { - if lastError != nil { - fmt.Fprintln(os.Stderr, lastError) - } - lastError = errors.Wrapf(err, "unable to find container %s", i) - } - if container != nil { - // This is here to make sure this does not return [<nil>] but only nil - containers = append(containers, container) - } - } - } - - return containers, lastError -} - // getContext returns a non-nil, empty context func getContext() context.Context { if Ctx != nil { @@ -164,7 +110,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "Attach to STDIN, STDOUT or STDERR (default [])", ) createFlags.String( - "authfile", getAuthFile(""), + "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override", ) createFlags.String( @@ -184,6 +130,10 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "Drop capabilities from the container", ) createFlags.String( + "cgroupns", "host", + "cgroup namespace to use", + ) + createFlags.String( "cgroup-parent", "", "Optional parent cgroup for the container", ) @@ -233,7 +183,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.String( "detach-keys", "", - "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`", + "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`", ) createFlags.StringSlice( "device", []string{}, @@ -275,6 +225,9 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "env", "e", []string{}, "Set environment variables in container", ) + createFlags.Bool( + "env-host", false, "Use all current host environment variables in container", + ) createFlags.StringSlice( "env-file", []string{}, "Read in a file of environment variables", @@ -295,23 +248,23 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "help", false, "", ) createFlags.String( - "healthcheck-command", "", + "health-cmd", "", "set a healthcheck command for the container ('none' disables the existing healthcheck)", ) createFlags.String( - "healthcheck-interval", cliconfig.DefaultHealthCheckInterval, + "health-interval", cliconfig.DefaultHealthCheckInterval, "set an interval for the healthchecks (a value of disable results in no automatic timer setup)", ) createFlags.Uint( - "healthcheck-retries", cliconfig.DefaultHealthCheckRetries, + "health-retries", cliconfig.DefaultHealthCheckRetries, "the number of retries allowed before a healthcheck is considered to be unhealthy", ) createFlags.String( - "healthcheck-start-period", cliconfig.DefaultHealthCheckStartPeriod, + "health-start-period", cliconfig.DefaultHealthCheckStartPeriod, "the initialization time needed for a container to bootstrap", ) createFlags.String( - "healthcheck-timeout", cliconfig.DefaultHealthCheckTimeout, + "health-timeout", cliconfig.DefaultHealthCheckTimeout, "the maximum time allowed to complete the healthcheck before an interval is considered failed", ) createFlags.StringP( @@ -333,7 +286,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { createFlags.String( "init-path", "", // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) - fmt.Sprintf("Path to the container-init binary (default: %q)", libpod.DefaultInitPath), + fmt.Sprintf("Path to the container-init binary (default: %q)", define.DefaultInitPath), ) createFlags.BoolP( "interactive", "i", false, @@ -472,7 +425,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "Signal to stop a container. Default is SIGTERM", ) createFlags.Uint( - "stop-timeout", libpod.CtrRemoveTimeout, + "stop-timeout", define.CtrRemoveTimeout, "Timeout (in seconds) to stop a container. Default is 10", ) createFlags.StringSlice( @@ -496,7 +449,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "systemd", cliconfig.DefaultSystemD, "Run container in systemd mode if the command executable is systemd or init", ) - createFlags.StringSlice( + createFlags.StringArray( "tmpfs", []string{}, "Mount a temporary filesystem (`tmpfs`) into a container (default [])", ) @@ -554,24 +507,6 @@ func getFormat(c *cliconfig.PodmanCommand) (string, error) { return "", errors.Errorf("unrecognized image type %q", format) } -func getAuthFile(authfile string) string { - if authfile != "" { - return authfile - } - if remote { - return "" - } - authfile = os.Getenv("REGISTRY_AUTH_FILE") - if authfile != "" { - return authfile - } - runtimeDir := os.Getenv("XDG_RUNTIME_DIR") - if runtimeDir != "" { - return filepath.Join(runtimeDir, "containers/auth.json") - } - return "" -} - // scrubServer removes 'http://' or 'https://' from the front of the // server/registry string if either is there. This will be mostly used // for user input from 'podman login' and 'podman logout'. diff --git a/cmd/podman/common_libpod.go b/cmd/podman/common_libpod.go new file mode 100644 index 000000000..5deea15d3 --- /dev/null +++ b/cmd/podman/common_libpod.go @@ -0,0 +1,65 @@ +//build !remoteclient + +package main + +import ( + "fmt" + "os" + + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" + "github.com/pkg/errors" +) + +// getAllOrLatestContainers tries to return the correct list of containers +// depending if --all, --latest or <container-id> is used. +// It requires the Context (c) and the Runtime (runtime). As different +// commands are using different container state for the --all option +// the desired state has to be specified in filterState. If no filter +// is desired a -1 can be used to get all containers. For a better +// error message, if the filter fails, a corresponding verb can be +// specified which will then appear in the error message. +func getAllOrLatestContainers(c *cliconfig.PodmanCommand, runtime *libpod.Runtime, filterState define.ContainerStatus, verb string) ([]*libpod.Container, error) { + var containers []*libpod.Container + var lastError error + var err error + if c.Bool("all") { + if filterState != -1 { + var filterFuncs []libpod.ContainerFilter + filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { + state, _ := c.State() + return state == filterState + }) + containers, err = runtime.GetContainers(filterFuncs...) + } else { + containers, err = runtime.GetContainers() + } + if err != nil { + return nil, errors.Wrapf(err, "unable to get %s containers", verb) + } + } else if c.Bool("latest") { + lastCtr, err := runtime.GetLatestContainer() + if err != nil { + return nil, errors.Wrapf(err, "unable to get latest container") + } + containers = append(containers, lastCtr) + } else { + args := c.InputArgs + for _, i := range args { + container, err := runtime.LookupContainer(i) + if err != nil { + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "unable to find container %s", i) + } + if container != nil { + // This is here to make sure this does not return [<nil>] but only nil + containers = append(containers, container) + } + } + } + + return containers, lastError +} diff --git a/cmd/podman/container.go b/cmd/podman/container.go index 839ae3a0e..557f5fafa 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -52,10 +52,12 @@ var ( containerCommands = []*cobra.Command{ _attachCommand, _checkpointCommand, + _commitCommand, _containerExistsCommand, _contInspectSubCommand, _cpCommand, _diffCommand, + _execCommand, _exportCommand, _createCommand, _initCommand, diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go index b052bda36..b8a84a0e3 100644 --- a/cmd/podman/containers_prune.go +++ b/cmd/podman/containers_prune.go @@ -3,7 +3,7 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -43,7 +43,7 @@ func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) maxWorkers := shared.DefaultPoolSize("prune") if c.GlobalIsSet("max-workers") { @@ -51,7 +51,7 @@ func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { } ok, failures, err := runtime.Prune(getContext(), maxWorkers, c.Force) if err != nil { - if errors.Cause(err) == libpod.ErrNoSuchCtr { + if errors.Cause(err) == define.ErrNoSuchCtr { if len(c.InputArgs) > 1 { exitCode = 125 } else { diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go index 8240cc193..bee7d2199 100644 --- a/cmd/podman/cp.go +++ b/cmd/podman/cp.go @@ -13,12 +13,15 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/rootless" "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" + securejoin "github.com/cyphar/filepath-securejoin" + "github.com/opencontainers/go-digest" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -49,6 +52,7 @@ func init() { cpCommand.Command = _cpCommand flags := cpCommand.Flags() flags.BoolVar(&cpCommand.Extract, "extract", false, "Extract the tar file into the destination directory.") + flags.BoolVar(&cpCommand.Pause, "pause", false, "Pause the container while copying") cpCommand.SetHelpTemplate(HelpTemplate()) cpCommand.SetUsageTemplate(UsageTemplate()) rootCmd.AddCommand(cpCommand.Command) @@ -64,13 +68,12 @@ func cpCmd(c *cliconfig.CpValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) - extract := c.Flag("extract").Changed - return copyBetweenHostAndContainer(runtime, args[0], args[1], extract) + return copyBetweenHostAndContainer(runtime, args[0], args[1], c.Extract, c.Pause) } -func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest string, extract bool) error { +func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest string, extract bool, pause bool) error { srcCtr, srcPath := parsePath(runtime, src) destCtr, destPath := parsePath(runtime, dest) @@ -83,7 +86,7 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin return errors.Errorf("invalid arguments %s, %s you must specify paths", src, dest) } ctr := srcCtr - isFromHostToCtr := (ctr == nil) + isFromHostToCtr := ctr == nil if isFromHostToCtr { ctr = destCtr } @@ -92,7 +95,43 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin if err != nil { return err } - defer ctr.Unmount(false) + defer func() { + if err := ctr.Unmount(false); err != nil { + logrus.Errorf("unable to umount container '%s': %q", ctr.ID(), err) + } + }() + + // We can't pause rootless containers. + if pause && rootless.IsRootless() { + state, err := ctr.State() + if err != nil { + return err + } + if state == define.ContainerStateRunning { + return errors.Errorf("cannot copy into running rootless container with pause set - pass --pause=false to force copying") + } + } + + if pause && !rootless.IsRootless() { + if err := ctr.Pause(); err != nil { + // An invalid state error is fine. + // The container isn't running or is already paused. + // TODO: We can potentially start the container while + // the copy is running, which still allows a race where + // malicious code could mess with the symlink. + if errors.Cause(err) != define.ErrCtrStateInvalid { + return err + } + } else { + // Only add the defer if we actually paused + defer func() { + if err := ctr.Unpause(); err != nil { + logrus.Errorf("Error unpausing container after copying: %v", err) + } + }() + } + } + user, err := getUser(mountPoint, ctr.User()) if err != nil { return err @@ -111,20 +150,63 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin var glob []string if isFromHostToCtr { - if filepath.IsAbs(destPath) { - destPath = filepath.Join(mountPoint, destPath) - + if isVol, volDestName, volName := isVolumeDestName(destPath, ctr); isVol { + path, err := pathWithVolumeMount(ctr, runtime, volDestName, volName, destPath) + if err != nil { + return errors.Wrapf(err, "error getting destination path from volume %s", volDestName) + } + destPath = path + } else if isBindMount, mount := isBindMountDestName(destPath, ctr); isBindMount { + path, err := pathWithBindMountSource(mount, destPath) + if err != nil { + return errors.Wrapf(err, "error getting destination path from bind mount %s", mount.Destination) + } + destPath = path + } else if filepath.IsAbs(destPath) { + cleanedPath, err := securejoin.SecureJoin(mountPoint, destPath) + if err != nil { + return err + } + destPath = cleanedPath } else { - if err = idtools.MkdirAllAndChownNew(filepath.Join(mountPoint, ctr.WorkingDir()), 0755, hostOwner); err != nil { + ctrWorkDir, err := securejoin.SecureJoin(mountPoint, ctr.WorkingDir()) + if err != nil { + return err + } + if err = idtools.MkdirAllAndChownNew(ctrWorkDir, 0755, hostOwner); err != nil { return errors.Wrapf(err, "error creating directory %q", destPath) } - destPath = filepath.Join(mountPoint, ctr.WorkingDir(), destPath) + cleanedPath, err := securejoin.SecureJoin(mountPoint, filepath.Join(ctr.WorkingDir(), destPath)) + if err != nil { + return err + } + destPath = cleanedPath } } else { - if filepath.IsAbs(srcPath) { - srcPath = filepath.Join(mountPoint, srcPath) + if isVol, volDestName, volName := isVolumeDestName(srcPath, ctr); isVol { + path, err := pathWithVolumeMount(ctr, runtime, volDestName, volName, srcPath) + if err != nil { + return errors.Wrapf(err, "error getting source path from volume %s", volDestName) + } + srcPath = path + } else if isBindMount, mount := isBindMountDestName(srcPath, ctr); isBindMount { + path, err := pathWithBindMountSource(mount, srcPath) + if err != nil { + return errors.Wrapf(err, "error getting source path from bind moutn %s", mount.Destination) + } + srcPath = path + } else if filepath.IsAbs(srcPath) { + cleanedPath, err := securejoin.SecureJoin(mountPoint, srcPath) + if err != nil { + return err + } + srcPath = cleanedPath } else { - srcPath = filepath.Join(mountPoint, ctr.WorkingDir(), srcPath) + cleanedPath, err := securejoin.SecureJoin(mountPoint, filepath.Join(ctr.WorkingDir(), srcPath)) + if err != nil { + return err + } + srcPath = cleanedPath } } glob, err = filepath.Glob(srcPath) @@ -158,7 +240,7 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin } func getUser(mountPoint string, userspec string) (specs.User, error) { - uid, gid, err := chrootuser.GetUser(mountPoint, userspec) + uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) u := specs.User{ UID: uid, GID: gid, @@ -229,7 +311,7 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch if err != nil && !os.IsNotExist(err) { return errors.Wrapf(err, "error checking directory %q", destdir) } - destDirIsExist := (err == nil) + destDirIsExist := err == nil if err = os.MkdirAll(destdir, 0755); err != nil { return errors.Wrapf(err, "error creating directory %q", destdir) } @@ -249,20 +331,6 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch } 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)) - } - } if extract { // We're extracting an archive into the destination directory. @@ -273,9 +341,16 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch return nil } - if destDirIsExist || strings.HasSuffix(dest, string(os.PathSeparator)) { + 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 { @@ -354,3 +429,69 @@ func streamFileToStdout(srcPath string, srcfi os.FileInfo) error { } return nil } + +func isVolumeDestName(path string, ctr *libpod.Container) (bool, string, string) { + separator := string(os.PathSeparator) + if filepath.IsAbs(path) { + path = strings.TrimPrefix(path, separator) + } + if path == "" { + return false, "", "" + } + for _, vol := range ctr.Config().NamedVolumes { + volNamePath := strings.TrimPrefix(vol.Dest, separator) + if matchVolumePath(path, volNamePath) { + return true, vol.Dest, vol.Name + } + } + return false, "", "" +} + +// if SRCPATH or DESTPATH is from volume mount's destination -v or --mount type=volume, generates the path with volume mount point +func pathWithVolumeMount(ctr *libpod.Container, runtime *libpod.Runtime, volDestName, volName, path string) (string, error) { + destVolume, err := runtime.GetVolume(volName) + if err != nil { + return "", errors.Wrapf(err, "error getting volume destination %s", volName) + } + if !filepath.IsAbs(path) { + path = filepath.Join(string(os.PathSeparator), path) + } + path, err = securejoin.SecureJoin(destVolume.MountPoint(), strings.TrimPrefix(path, volDestName)) + return path, err +} + +func isBindMountDestName(path string, ctr *libpod.Container) (bool, specs.Mount) { + separator := string(os.PathSeparator) + if filepath.IsAbs(path) { + path = strings.TrimPrefix(path, string(os.PathSeparator)) + } + if path == "" { + return false, specs.Mount{} + } + for _, m := range ctr.Config().Spec.Mounts { + if m.Type != "bind" { + continue + } + mDest := strings.TrimPrefix(m.Destination, separator) + if matchVolumePath(path, mDest) { + return true, m + } + } + return false, specs.Mount{} +} + +func matchVolumePath(path, target string) bool { + pathStr := filepath.Clean(path) + target = filepath.Clean(target) + for len(pathStr) > len(target) && strings.Contains(pathStr, string(os.PathSeparator)) { + pathStr = pathStr[:strings.LastIndex(pathStr, string(os.PathSeparator))] + } + return pathStr == target +} + +func pathWithBindMountSource(m specs.Mount, path string) (string, error) { + if !filepath.IsAbs(path) { + path = filepath.Join(string(os.PathSeparator), path) + } + return securejoin.SecureJoin(m.Source, strings.TrimPrefix(path, m.Destination)) +} diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 2351f5860..3c24729c5 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "strings" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" @@ -40,7 +41,7 @@ func init() { getCreateFlags(&createCommand.PodmanCommand) flags := createCommand.Flags() flags.SetInterspersed(false) - + flags.SetNormalizeFunc(aliasFlags) } func createCmd(c *cliconfig.CreateValues) error { @@ -57,7 +58,7 @@ func createCmd(c *cliconfig.CreateValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) cid, err := runtime.CreateContainer(getContext(), c) if err != nil { @@ -77,6 +78,16 @@ func createInit(c *cliconfig.PodmanCommand) error { logrus.Warn("setting security options with --privileged has no effect") } + var setNet string + if c.IsSet("network") { + setNet = c.String("network") + } else if c.IsSet("net") { + setNet = c.String("net") + } + if (c.IsSet("dns") || c.IsSet("dns-opt") || c.IsSet("dns-search")) && (setNet == "none" || strings.HasPrefix(setNet, "container:")) { + return errors.Errorf("conflicting options: dns and the network mode.") + } + // Docker-compatibility: the "-h" flag for run/create is reserved for // the hostname (see https://github.com/containers/libpod/issues/1367). diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 9543113d8..f052b510d 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -60,8 +60,9 @@ func init() { flags.BoolVar(&diffCommand.Archive, "archive", true, "Save the diff as a tar archive") flags.StringVar(&diffCommand.Format, "format", "", "Change the output format") - - flags.MarkHidden("archive") + flags.BoolVarP(&diffCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + markFlagHidden(flags, "archive") + markFlagHiddenForRemoteClient("latest", flags) } @@ -83,7 +84,7 @@ func formatJSON(output []diffOutputParams) (diffJSONOutput, error) { } func diffCmd(c *cliconfig.DiffValues) error { - if len(c.InputArgs) != 1 { + if len(c.InputArgs) != 1 && !c.Latest { return errors.Errorf("container, image, or layer name must be specified: podman diff [options [...]] ID-NAME") } @@ -91,9 +92,18 @@ func diffCmd(c *cliconfig.DiffValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) - to := c.InputArgs[0] + var to string + if c.Latest { + ctr, err := runtime.GetLatestContainer() + if err != nil { + return errors.Wrapf(err, "unable to get latest container") + } + to = ctr.ID() + } else { + to = c.InputArgs[0] + } changes, err := runtime.Diff(c, to) if err != nil { return errors.Wrapf(err, "could not get changes for %q", to) @@ -126,7 +136,5 @@ func diffCmd(c *cliconfig.DiffValues) error { } else { out = stdoutStruct{output: diffOutput} } - formats.Writer(out).Out() - - return nil + return out.Out() } diff --git a/cmd/podman/events.go b/cmd/podman/events.go index 88c1010e3..18126e626 100644 --- a/cmd/podman/events.go +++ b/cmd/podman/events.go @@ -36,7 +36,7 @@ func init() { flags.BoolVar(&eventsCommand.Stream, "stream", true, "stream new events; for testing only") flags.StringVar(&eventsCommand.Since, "since", "", "show all events created since timestamp") flags.StringVar(&eventsCommand.Until, "until", "", "show all events until timestamp") - flags.MarkHidden("stream") + markFlagHidden(flags, "stream") } func eventsCmd(c *cliconfig.EventValues) error { @@ -44,7 +44,7 @@ func eventsCmd(c *cliconfig.EventValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) return runtime.Events(c) } diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index deff44a92..649a7b0db 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -1,15 +1,8 @@ package main import ( - "fmt" - "io/ioutil" - "os" - "strconv" - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/cmd/podman/shared/parse" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -41,8 +34,9 @@ func init() { execCommand.SetUsageTemplate(UsageTemplate()) flags := execCommand.Flags() flags.SetInterspersed(false) + flags.StringVar(&execCommand.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.StringArrayVarP(&execCommand.Env, "env", "e", []string{}, "Set environment variables") - flags.BoolVarP(&execCommand.Interfactive, "interactive", "i", false, "Not supported. All exec commands are interactive by default") + flags.BoolVarP(&execCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVarP(&execCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&execCommand.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false") flags.BoolVarP(&execCommand.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false") @@ -51,79 +45,29 @@ func init() { flags.IntVar(&execCommand.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container") flags.StringVarP(&execCommand.Workdir, "workdir", "w", "", "Working directory inside the container") markFlagHiddenForRemoteClient("latest", flags) + markFlagHiddenForRemoteClient("preserve-fds", flags) } func execCmd(c *cliconfig.ExecValues) error { - args := c.InputArgs - var ctr *libpod.Container - var err error - argStart := 1 - if len(args) < 1 && !c.Latest { - return errors.Errorf("you must provide one container name or id") - } - if len(args) < 2 && !c.Latest { - return errors.Errorf("you must provide a command to exec") - } + argLen := len(c.InputArgs) if c.Latest { - argStart = 0 - } - cmd := args[argStart:] - runtime, err := libpodruntime.GetRuntime(getContext(), &c.PodmanCommand) - if err != nil { - return errors.Wrapf(err, "error creating libpod runtime") - } - defer runtime.Shutdown(false) - - if c.Latest { - ctr, err = runtime.GetLatestContainer() - } else { - ctr, err = runtime.LookupContainer(args[0]) - } - if err != nil { - return errors.Wrapf(err, "unable to exec into %s", args[0]) - } - - if c.PreserveFDs > 0 { - entries, err := ioutil.ReadDir("/proc/self/fd") - if err != nil { - return errors.Wrapf(err, "unable to read /proc/self/fd") + if argLen < 1 { + return errors.Errorf("you must provide a command to exec") } - m := make(map[int]bool) - for _, e := range entries { - i, err := strconv.Atoi(e.Name()) - if err != nil { - if err != nil { - return errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) - } - } - m[i] = true + } else { + if argLen < 1 { + return errors.Errorf("you must provide one container name or id") } - for i := 3; i < 3+c.PreserveFDs; i++ { - if _, found := m[i]; !found { - return errors.New("invalid --preserve-fds=N specified. Not enough FDs available") - } + if argLen < 2 { + return errors.Errorf("you must provide a command to exec") } - } - - // ENVIRONMENT VARIABLES - env := map[string]string{} - - if err := parse.ReadKVStrings(env, []string{}, c.Env); err != nil { - return errors.Wrapf(err, "unable to process environment variables") - } - envs := []string{} - for k, v := range env { - envs = append(envs, fmt.Sprintf("%s=%s", k, v)) + runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "error creating libpod runtime") } + defer runtime.DeferredShutdown(false) - streams := new(libpod.AttachStreams) - streams.OutputStream = os.Stdout - streams.ErrorStream = os.Stderr - streams.InputStream = os.Stdin - streams.AttachOutput = true - streams.AttachError = true - streams.AttachInput = true - - return ctr.Exec(c.Tty, c.Privileged, envs, cmd, c.User, c.Workdir, streams, c.PreserveFDs) + exitCode, err = runtime.ExecContainer(getContext(), c) + return err } diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go index 6619522b6..f8b1f8e59 100644 --- a/cmd/podman/exists.go +++ b/cmd/podman/exists.go @@ -1,14 +1,14 @@ package main import ( - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/spf13/cobra" "os" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" + "github.com/spf13/cobra" ) var ( @@ -90,7 +90,7 @@ func imageExistsCmd(c *cliconfig.ImageExistsValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if _, err := runtime.NewImageFromLocal(args[0]); err != nil { //TODO we need to ask about having varlink defined errors exposed //so we can reuse them @@ -107,13 +107,13 @@ func containerExistsCmd(c *cliconfig.ContainerExistsValues) error { if len(args) > 1 || len(args) < 1 { return errors.New("you may only check for the existence of one container at a time") } - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if _, err := runtime.LookupContainer(args[0]); err != nil { - if errors.Cause(err) == libpod.ErrNoSuchCtr || err.Error() == "io.podman.ContainerNotFound" { + if errors.Cause(err) == define.ErrNoSuchCtr || err.Error() == "io.podman.ContainerNotFound" { os.Exit(1) } return err @@ -126,14 +126,14 @@ func podExistsCmd(c *cliconfig.PodExistsValues) error { if len(args) > 1 || len(args) < 1 { return errors.New("you may only check for the existence of one pod at a time") } - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if _, err := runtime.LookupPod(args[0]); err != nil { - if errors.Cause(err) == libpod.ErrNoSuchPod || err.Error() == "io.podman.PodNotFound" { + if errors.Cause(err) == define.ErrNoSuchPod || err.Error() == "io.podman.PodNotFound" { os.Exit(1) } return err diff --git a/cmd/podman/export.go b/cmd/podman/export.go index 82a4c13e7..27948004c 100644 --- a/cmd/podman/export.go +++ b/cmd/podman/export.go @@ -7,8 +7,8 @@ import ( "github.com/containers/libpod/cmd/podman/shared/parse" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "golang.org/x/crypto/ssh/terminal" ) var ( @@ -45,7 +45,7 @@ func exportCmd(c *cliconfig.ExportValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) args := c.InputArgs if len(args) == 0 { @@ -62,7 +62,7 @@ func exportCmd(c *cliconfig.ExportValues) error { if len(output) == 0 { file := os.Stdout - if logrus.IsTerminal(file) { + if terminal.IsTerminal(int(file.Fd())) { return errors.Errorf("refusing to export to terminal. Use -o flag or redirect") } output = "/dev/stdout" diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go index 318dd0771..6f04d6517 100644 --- a/cmd/podman/generate_kube.go +++ b/cmd/podman/generate_kube.go @@ -2,6 +2,9 @@ package main import ( "fmt" + "io/ioutil" + "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" podmanVersion "github.com/containers/libpod/version" @@ -37,6 +40,7 @@ func init() { containerKubeCommand.SetUsageTemplate(UsageTemplate()) flags := containerKubeCommand.Flags() flags.BoolVarP(&containerKubeCommand.Service, "service", "s", false, "Generate YAML for kubernetes service object") + flags.StringVarP(&containerKubeCommand.Filename, "filename", "f", "", "Filename to output to") } func generateKubeYAMLCmd(c *cliconfig.GenerateKubeValues) error { @@ -58,7 +62,7 @@ func generateKubeYAMLCmd(c *cliconfig.GenerateKubeValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) podYAML, serviceYAML, err := runtime.GenerateKube(c) if err != nil { @@ -88,8 +92,19 @@ func generateKubeYAMLCmd(c *cliconfig.GenerateKubeValues) error { output = append(output, []byte("---\n")...) output = append(output, marshalledService...) } - // Output the v1.Pod with the v1.Container - fmt.Println(string(output)) + + if c.Filename != "" { + if _, err := os.Stat(c.Filename); err == nil { + return errors.Errorf("cannot write to %q - file exists", c.Filename) + } + + if err := ioutil.WriteFile(c.Filename, output, 0644); err != nil { + return err + } + } else { + // Output the v1.Pod with the v1.Container + fmt.Println(string(output)) + } return nil } diff --git a/cmd/podman/generate_systemd.go b/cmd/podman/generate_systemd.go index b4779e512..8be097c83 100644 --- a/cmd/podman/generate_systemd.go +++ b/cmd/podman/generate_systemd.go @@ -50,7 +50,7 @@ func generateSystemdCmd(c *cliconfig.GenerateSystemdValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) // User input stop timeout must be 0 or greater if c.Flag("timeout").Changed && c.StopTimeout < 0 { diff --git a/cmd/podman/healthcheck_run.go b/cmd/podman/healthcheck_run.go index 111318d9c..3a2a8f333 100644 --- a/cmd/podman/healthcheck_run.go +++ b/cmd/podman/healthcheck_run.go @@ -2,8 +2,8 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -42,13 +42,11 @@ func healthCheckCmd(c *cliconfig.HealthCheckValues) error { if err != nil { return errors.Wrap(err, "could not get runtime") } + defer runtime.DeferredShutdown(false) status, err := runtime.HealthCheck(c) - if err != nil { - if status == libpod.HealthCheckFailure { - fmt.Println("\nunhealthy") - } - return err + if err == nil && status == "unhealthy" { + exitCode = 1 } - fmt.Println("healthy") - return nil + fmt.Println(status) + return err } diff --git a/cmd/podman/history.go b/cmd/podman/history.go index cebf99a9f..a16aac8d8 100644 --- a/cmd/podman/history.go +++ b/cmd/podman/history.go @@ -71,7 +71,7 @@ func historyCmd(c *cliconfig.HistoryValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) format := genHistoryFormat(c.Format, c.Quiet) @@ -141,11 +141,12 @@ func (h *historyTemplateParams) headerMap() map[string]string { } // getHistorytemplateOutput gets the modified history information to be printed in human readable format -func getHistoryTemplateOutput(history []*image.History, opts historyOptions) (historyOutput []historyTemplateParams) { +func getHistoryTemplateOutput(history []*image.History, opts historyOptions) []historyTemplateParams { var ( - outputSize string - createdTime string - createdBy string + outputSize string + createdTime string + createdBy string + historyOutput []historyTemplateParams ) for _, hist := range history { imageID := hist.ID @@ -154,7 +155,7 @@ func getHistoryTemplateOutput(history []*image.History, opts historyOptions) (hi } if opts.human { - createdTime = units.HumanDuration(time.Since((*hist.Created))) + " ago" + createdTime = units.HumanDuration(time.Since(*hist.Created)) + " ago" outputSize = units.HumanSize(float64(hist.Size)) } else { createdTime = (hist.Created).Format(time.RFC3339) @@ -175,7 +176,7 @@ func getHistoryTemplateOutput(history []*image.History, opts historyOptions) (hi } historyOutput = append(historyOutput, params) } - return + return historyOutput } // generateHistoryOutput generates the history based on the format given @@ -194,5 +195,5 @@ func generateHistoryOutput(history []*image.History, opts historyOptions) error out = formats.StdoutTemplateArray{Output: historyToGeneric(historyOutput, []*image.History{}), Template: opts.format, Fields: historyOutput[0].headerMap()} } - return formats.Writer(out).Out() + return out.Out() } diff --git a/cmd/podman/imagefilters/filters.go b/cmd/podman/imagefilters/filters.go index aa5776599..0b08314ce 100644 --- a/cmd/podman/imagefilters/filters.go +++ b/cmd/podman/imagefilters/filters.go @@ -46,6 +46,16 @@ func DanglingFilter(danglingImages bool) ResultFilter { } } +// ReadOnlyFilter allows you to filter images based on read/only and read/write +func ReadOnlyFilter(readOnly bool) ResultFilter { + return func(i *adapter.ContainerImage) bool { + if readOnly { + return i.IsReadOnly() + } + return !i.IsReadOnly() + } +} + // LabelFilter allows you to filter by images labels key and/or value func LabelFilter(ctx context.Context, labelfilter string) ResultFilter { // We need to handle both label=key and label=key=value diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 3f755efc1..fe7c89b5c 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -30,14 +30,16 @@ type imagesTemplateParams struct { Created string CreatedTime time.Time Size string + ReadOnly bool } type imagesJSONParams struct { - ID string `json:"id"` - Name []string `json:"names"` - Digest digest.Digest `json:"digest"` - Created time.Time `json:"created"` - Size *uint64 `json:"size"` + ID string `json:"id"` + Name []string `json:"names"` + Digest digest.Digest `json:"digest"` + Created time.Time `json:"created"` + Size *uint64 `json:"size"` + ReadOnly bool `json:"readonly"` } type imagesOptions struct { @@ -138,7 +140,7 @@ func imagesCmd(c *cliconfig.ImagesValues) error { if err != nil { return errors.Wrapf(err, "Could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if len(c.InputArgs) == 1 { image = c.InputArgs[0] } @@ -175,6 +177,13 @@ func imagesCmd(c *cliconfig.ImagesValues) error { return errors.Wrapf(err, "unable to get images") } + for _, image := range images { + if image.IsReadOnly() { + opts.outputformat += "{{.ReadOnly}}\t" + break + } + } + var filteredImages []*adapter.ContainerImage //filter the images if len(c.Filter) > 0 || len(c.InputArgs) == 1 { @@ -238,7 +247,8 @@ func sortImagesOutput(sortBy string, imagesOutput imagesSorted) imagesSorted { } // getImagesTemplateOutput returns the images information to be printed in human readable format -func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) (imagesOutput imagesSorted) { +func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) imagesSorted { + var imagesOutput imagesSorted for _, img := range images { // If all is false and the image doesn't have a name, check to see if the top layer of the image is a parent // to another image's top layer. If it is, then it is an intermediate image so don't print out if the --all flag @@ -280,8 +290,9 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma ID: imageID, Digest: img.Digest(), CreatedTime: createdTime, - Created: units.HumanDuration(time.Since((createdTime))) + " ago", + Created: units.HumanDuration(time.Since(createdTime)) + " ago", Size: sizeStr, + ReadOnly: img.IsReadOnly(), } imagesOutput = append(imagesOutput, params) if opts.quiet { // Show only one image ID when quiet @@ -294,7 +305,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma // Sort images by created time sortImagesOutput(opts.sort, imagesOutput) - return + return imagesOutput } // getImagesJSONOutput returns the images information in its raw form @@ -305,11 +316,12 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) size = nil } params := imagesJSONParams{ - ID: img.ID(), - Name: img.Names(), - Digest: img.Digest(), - Created: img.Created(), - Size: size, + ID: img.ID(), + Name: img.Names(), + Digest: img.Digest(), + Created: img.Created(), + Size: size, + ReadOnly: img.IsReadOnly(), } imagesOutput = append(imagesOutput, params) } @@ -334,7 +346,7 @@ func generateImagesOutput(ctx context.Context, images []*adapter.ContainerImage, imagesOutput := getImagesTemplateOutput(ctx, images, opts) out = formats.StdoutTemplateArray{Output: imagesToGeneric(imagesOutput, []imagesJSONParams{}), Template: opts.outputformat, Fields: templateMap} } - return formats.Writer(out).Out() + return out.Out() } // GenImageOutputMap generates the map used for outputting the images header @@ -351,6 +363,11 @@ func GenImageOutputMap() map[string]string { if value == "ID" { value = "Image" + value } + + if value == "ReadOnly" { + values[key] = "R/O" + continue + } values[key] = strings.ToUpper(splitCamelCase(value)) } return values @@ -378,6 +395,12 @@ func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, filters []s return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1]) } filterFuncs = append(filterFuncs, imagefilters.CreatedAfterFilter(after.Created())) + case "readonly": + readonly, err := strconv.ParseBool(splitFilter[1]) + if err != nil { + return nil, errors.Wrapf(err, "invalid filter readonly=%s", splitFilter[1]) + } + filterFuncs = append(filterFuncs, imagefilters.ReadOnlyFilter(readonly)) case "dangling": danglingImages, err := strconv.ParseBool(splitFilter[1]) if err != nil { diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go index 1ac5bc65d..5745edd6b 100644 --- a/cmd/podman/images_prune.go +++ b/cmd/podman/images_prune.go @@ -41,7 +41,7 @@ func pruneImagesCmd(c *cliconfig.PruneImagesValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) // Call prune; if any cids are returned, print them and then // return err in case an error also came up diff --git a/cmd/podman/import.go b/cmd/podman/import.go index 167d9f2c9..d49792f27 100644 --- a/cmd/podman/import.go +++ b/cmd/podman/import.go @@ -6,6 +6,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared/parse" "github.com/containers/libpod/pkg/adapter" + multierror "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -49,7 +50,7 @@ func importCmd(c *cliconfig.ImportValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) var ( source string @@ -69,8 +70,11 @@ func importCmd(c *cliconfig.ImportValues) error { return errors.Errorf("too many arguments. Usage TARBALL [REFERENCE]") } - if err := parse.ValidateFileName(source); err != nil { - return err + errFileName := parse.ValidateFileName(source) + errURL := parse.ValidURL(source) + + if errFileName != nil && errURL != nil { + return multierror.Append(errFileName, errURL) } quiet := c.Quiet diff --git a/cmd/podman/info.go b/cmd/podman/info.go index 823303354..bf6dd4a8f 100644 --- a/cmd/podman/info.go +++ b/cmd/podman/info.go @@ -6,7 +6,7 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/version" "github.com/pkg/errors" @@ -55,7 +55,7 @@ func infoCmd(c *cliconfig.InfoValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) infoArr, err := runtime.Info() if err != nil { @@ -74,12 +74,12 @@ func infoCmd(c *cliconfig.InfoValues) error { remoteClientInfo["RemoteAPI Version"] = version.RemoteAPIVersion remoteClientInfo["Podman Version"] = version.Version remoteClientInfo["OS Arch"] = fmt.Sprintf("%s/%s", rt.GOOS, rt.GOARCH) - infoArr = append(infoArr, libpod.InfoData{Type: "client", Data: remoteClientInfo}) + infoArr = append(infoArr, define.InfoData{Type: "client", Data: remoteClientInfo}) } if !runtime.Remote && c.Debug { debugInfo := debugInfo(c) - infoArr = append(infoArr, libpod.InfoData{Type: "debug", Data: debugInfo}) + infoArr = append(infoArr, define.InfoData{Type: "debug", Data: debugInfo}) } for _, currInfo := range infoArr { @@ -97,9 +97,7 @@ func infoCmd(c *cliconfig.InfoValues) error { out = formats.StdoutTemplate{Output: info, Template: infoOutputFormat} } - formats.Writer(out).Out() - - return nil + return out.Out() } // top-level "debug" info @@ -108,7 +106,7 @@ func debugInfo(c *cliconfig.InfoValues) map[string]interface{} { info["compiler"] = rt.Compiler info["go version"] = rt.Version() info["podman version"] = version.Version - version, _ := libpod.GetVersion() + version, _ := define.GetVersion() info["git commit"] = version.GitCommit return info } diff --git a/cmd/podman/init.go b/cmd/podman/init.go index 68c80631d..3f97824fc 100644 --- a/cmd/podman/init.go +++ b/cmd/podman/init.go @@ -54,7 +54,7 @@ func initCmd(c *cliconfig.InitValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ok, failures, err := runtime.InitContainers(ctx, c) if err != nil { diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index 4303c149c..cff221cb0 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -6,9 +6,7 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/pkg/adapter" - cc "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -88,7 +86,7 @@ func inspectCmd(c *cliconfig.InspectValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if !util.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) { return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll) @@ -98,6 +96,14 @@ func inspectCmd(c *cliconfig.InspectValues) error { if strings.Contains(outputFormat, "{{.Id}}") { outputFormat = strings.Replace(outputFormat, "{{.Id}}", formats.IDString, -1) } + // These fields were renamed, so we need to provide backward compat for + // the old names. + if strings.Contains(outputFormat, ".Src") { + outputFormat = strings.Replace(outputFormat, ".Src", ".Source", -1) + } + if strings.Contains(outputFormat, ".Dst") { + outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1) + } if latestContainer { lc, err := runtime.GetLatestContainer() if err != nil { @@ -121,7 +127,7 @@ func inspectCmd(c *cliconfig.InspectValues) error { out = formats.JSONStructArray{Output: inspectedObjects} } - return formats.Writer(out).Out() + return out.Out() } // func iterateInput iterates the images|containers the user has requested and returns the inspect data and error @@ -140,19 +146,9 @@ func iterateInput(ctx context.Context, size bool, args []string, runtime *adapte inspectError = errors.Wrapf(err, "error looking up container %q", input) break } - libpodInspectData, err := ctr.Inspect(size) + data, err = ctr.Inspect(size) if err != nil { - inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID()) - break - } - artifact, err := getArtifact(ctr) - if inspectError != nil { - inspectError = err - break - } - data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact) - if err != nil { - inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID()) + inspectError = errors.Wrapf(err, "error inspecting container %s", ctr.ID()) break } case inspectTypeImage: @@ -180,19 +176,9 @@ func iterateInput(ctx context.Context, size bool, args []string, runtime *adapte break } } else { - libpodInspectData, err := ctr.Inspect(size) + data, err = ctr.Inspect(size) if err != nil { - inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID()) - break - } - artifact, inspectError := getArtifact(ctr) - if inspectError != nil { - inspectError = err - break - } - data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact) - if err != nil { - inspectError = errors.Wrapf(err, "error parsing container data %s", ctr.ID()) + inspectError = errors.Wrapf(err, "error inspecting container %s", ctr.ID()) break } } @@ -203,15 +189,3 @@ func iterateInput(ctx context.Context, size bool, args []string, runtime *adapte } return inspectedItems, inspectError } - -func getArtifact(ctr *adapter.Container) (*cc.CreateConfig, error) { - var createArtifact cc.CreateConfig - artifact, err := ctr.GetArtifact("create-config") - if err != nil { - return nil, err - } - if err := json.Unmarshal(artifact, &createArtifact); err != nil { - return nil, err - } - return &createArtifact, nil -} diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index edf69ff2e..d5056d86d 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -63,7 +63,7 @@ func killCmd(c *cliconfig.KillValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ok, failures, err := runtime.KillContainers(getContext(), c, killSignal) if err != nil { diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index 898c81515..570288837 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -5,6 +5,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/cgroups" "github.com/containers/libpod/pkg/namespaces" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/util" @@ -14,20 +15,25 @@ import ( // GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers func GetRuntimeMigrate(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, true) + return getRuntime(ctx, c, false, true, false) } // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber func GetRuntimeRenumber(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, true, false) + return getRuntime(ctx, c, true, false, false) } // GetRuntime generates a new libpod runtime configured by command line options func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, false) + return getRuntime(ctx, c, false, false, false) } -func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber bool, migrate bool) (*libpod.Runtime, error) { +// GetRuntimeNoStore generates a new libpod runtime configured by command line options +func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { + return getRuntime(ctx, c, false, false, true) +} + +func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migrate, noStore bool) (*libpod.Runtime, error) { options := []libpod.RuntimeOption{} storageOpts := storage.StoreOptions{} storageSet := false @@ -67,7 +73,10 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber bool, if c.Flags().Changed("storage-driver") { storageSet = true storageOpts.GraphDriverName = c.GlobalFlags.StorageDriver + // Overriding the default storage driver caused GraphDriverOptions from storage.conf to be ignored + storageOpts.GraphDriverOptions = []string{} } + // This should always be checked after storage-driver is checked if len(c.GlobalFlags.StorageOpts) > 0 { storageSet = true storageOpts.GraphDriverOptions = c.GlobalFlags.StorageOpts @@ -85,6 +94,9 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber bool, options = append(options, libpod.WithStorageConfig(storageOpts)) } + if !storageSet && noStore { + options = append(options, libpod.WithNoStore()) + } // TODO CLI flags for image config? // TODO CLI flag for signature policy? @@ -109,7 +121,7 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber bool, if c.Flags().Changed("cgroup-manager") { options = append(options, libpod.WithCgroupManager(c.GlobalFlags.CGroupManager)) } else { - unified, err := util.IsCgroup2UnifiedMode() + unified, err := cgroups.IsCgroup2UnifiedMode() if err != nil { return nil, err } diff --git a/cmd/podman/load.go b/cmd/podman/load.go index 0c41eb792..ed6a4e5fa 100644 --- a/cmd/podman/load.go +++ b/cmd/podman/load.go @@ -43,7 +43,7 @@ func init() { // Disabled flags for the remote client if !remote { flags.StringVar(&loadCommand.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file (not usually used)") - flags.MarkHidden("signature-policy") + markFlagHidden(flags, "signature-policy") } } @@ -65,7 +65,7 @@ func loadCmd(c *cliconfig.LoadValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if len(c.Input) > 0 { if err := parse.ValidateFileName(c.Input); err != nil { diff --git a/cmd/podman/login.go b/cmd/podman/login.go index 3a78adadc..36262fd4d 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -10,6 +10,7 @@ import ( "github.com/containers/image/pkg/docker/config" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/docker/docker-credential-helpers/credentials" "github.com/pkg/errors" @@ -52,7 +53,7 @@ func init() { flags.BoolVar(&loginCommand.StdinPassword, "password-stdin", false, "Take the password from stdin") // Disabled flags for the remote client if !remote { - flags.StringVar(&loginCommand.Authfile, "authfile", getAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&loginCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&loginCommand.CertDir, "cert-dir", "", "Pathname of a directory containing TLS certificates and keys used to connect to the registry") flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index 5df838bba..66dc82363 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -6,6 +6,7 @@ import ( "github.com/containers/image/docker" "github.com/containers/image/pkg/docker/config" "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -39,7 +40,7 @@ func init() { 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", getAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&logoutCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") markFlagHiddenForRemoteClient("authfile", flags) } diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go index a1ec9f4ee..32605389e 100644 --- a/cmd/podman/logs.go +++ b/cmd/podman/logs.go @@ -4,7 +4,7 @@ import ( "time" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/logs" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" @@ -54,8 +54,7 @@ func init() { flags.StringVar(&logsCommand.Since, "since", "", "Show logs since TIMESTAMP") flags.Uint64Var(&logsCommand.Tail, "tail", 0, "Output the specified number of LINES at the end of the logs. Defaults to 0, which prints all lines") flags.BoolVarP(&logsCommand.Timestamps, "timestamps", "t", false, "Output the timestamps in the log") - flags.MarkHidden("details") - + markFlagHidden(flags, "details") flags.SetInterspersed(false) markFlagHiddenForRemoteClient("latest", flags) @@ -68,7 +67,7 @@ func logsCmd(c *cliconfig.LogsValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) sinceTime := time.Time{} if c.Flag("since").Changed { @@ -80,13 +79,12 @@ func logsCmd(c *cliconfig.LogsValues) error { sinceTime = since } - opts := &libpod.LogOptions{ + options := &logs.LogOptions{ Details: c.Details, Follow: c.Follow, Since: sinceTime, Tail: c.Tail, Timestamps: c.Timestamps, } - - return runtime.Log(c, opts) + return runtime.Log(c, options) } diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 787dd55c0..72d1754ac 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -4,6 +4,7 @@ import ( "context" "io" "os" + "path" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" @@ -30,9 +31,11 @@ var ( var mainCommands = []*cobra.Command{ _attachCommand, _buildCommand, + _commitCommand, _diffCommand, _createCommand, _eventsCommand, + _execCommand, _exportCommand, _generateCommand, _historyCommand, @@ -58,7 +61,6 @@ var mainCommands = []*cobra.Command{ _stopCommand, _tagCommand, _topCommand, - _umountCommand, _unpauseCommand, _versionCommand, _waitCommand, @@ -68,7 +70,7 @@ var mainCommands = []*cobra.Command{ } var rootCmd = &cobra.Command{ - Use: "podman", + Use: path.Base(os.Args[0]), Long: "manage pods and images", RunE: commandRunE(), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { @@ -99,10 +101,11 @@ func initConfig() { } func before(cmd *cobra.Command, args []string) error { - if err := libpod.SetXdgRuntimeDir(""); err != nil { + if err := libpod.SetXdgRuntimeDir(); err != nil { logrus.Errorf(err.Error()) os.Exit(1) } + if err := setupRootless(cmd, args); err != nil { return err } @@ -117,6 +120,9 @@ func before(cmd *cobra.Command, args []string) error { return err } logrus.SetLevel(level) + if err := setSyslog(); err != nil { + return err + } if err := setRLimits(); err != nil { return err diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index b4f21bd0c..e4f521bc4 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -12,6 +12,8 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/cgroups" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/tracing" "github.com/containers/libpod/pkg/util" @@ -25,8 +27,17 @@ import ( const remote = false func init() { - - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", "", "Cgroup manager to use (cgroupfs or systemd, default systemd)") + cgroupManager := libpod.SystemdCgroupsManager + if runtimeConfig, err := libpod.DefaultRuntimeConfig(); err == nil { + cgroupManager = runtimeConfig.CgroupManager + } + cgroupHelp := "Cgroup manager to use (cgroupfs or systemd)" + cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() + if rootless.IsRootless() && !cgroupv2 { + cgroupManager = "" + cgroupHelp = "Cgroup manager is not supported in rootless mode" + } + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", cgroupManager, cgroupHelp) // -c is deprecated due to conflict with -c on subcommands rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Config, "config", "", "Path of a libpod config file detailing container server configuration options") @@ -34,21 +45,25 @@ func init() { rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.NetworkCmdPath, "network-cmd-path", "", "Path to the command for configuring the network") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CniConfigDir, "cni-config-dir", "", "Path of the configuration directory for CNI networks") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file") - rootCmd.PersistentFlags().MarkHidden("defaults-mount-file") + if err := rootCmd.PersistentFlags().MarkHidden("default-mounts-file"); err != nil { + logrus.Error("unable to mark default-mounts-file flag as hidden") + } // Override default --help information of `--help` global flag var dummyHelp bool rootCmd.PersistentFlags().BoolVar(&dummyHelp, "help", false, "Help for podman") rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic") rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") - rootCmd.PersistentFlags().MarkHidden("max-workers") + if err := rootCmd.PersistentFlags().MarkHidden("max-workers"); err != nil { + logrus.Error("unable to mark max-workers flag as hidden") + } rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", "", "Set the libpod namespace, used to create separate views of the containers and pods on the system") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Root, "root", "", "Path to the root directory in which data, including images, is stored") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runtime, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") // -s is depracated due to conflict with -s on subcommands rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") - rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") + rootCmd.PersistentFlags().StringArrayVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory") @@ -107,10 +122,10 @@ func setupRootless(cmd *cobra.Command, args []string) error { return nil } podmanCmd := cliconfig.PodmanCommand{ - cmd, - args, - MainGlobalOpts, - remoteclient, + Command: cmd, + InputArgs: args, + GlobalFlags: MainGlobalOpts, + Remote: remoteclient, } pausePidPath, err := util.GetRootlessPauseProcessPidPath() @@ -137,7 +152,7 @@ func setupRootless(cmd *cobra.Command, args []string) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ctrs, err := runtime.GetRunningContainers() if err != nil { diff --git a/cmd/podman/main_remote.go b/cmd/podman/main_remote.go index c8bb3ad3e..ecbb44d5a 100644 --- a/cmd/podman/main_remote.go +++ b/cmd/podman/main_remote.go @@ -3,20 +3,63 @@ package main import ( + "fmt" + "os" + "os/user" + "path/filepath" + + "github.com/docker/docker/pkg/homedir" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) const remote = true func init() { - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteUserName, "username", "", "username on the remote host") + var username string + if curruser, err := user.Current(); err == nil { + username = curruser.Username + } + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConnectionName, "connection", "", "remote connection name") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteConfigFilePath, "remote-config-path", "", "alternate path for configuration file") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteUserName, "username", username, "username on the remote host") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteHost, "remote-host", "", "remote host") // TODO maybe we allow the altering of this for bridge connections? - //rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.VarlinkAddress, "varlink-address", adapter.DefaultAddress, "address of the varlink socket") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic") + // rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.VarlinkAddress, "varlink-address", adapter.DefaultAddress, "address of the varlink socket") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic. Logged to ~/.config/containers/podman.log") + rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console") } func setSyslog() error { + // Log to file if not using syslog + homeDir := homedir.Get() + path := filepath.Join(homeDir, ".config", "containers") + + if _, err := os.Stat(path); os.IsNotExist(err) { + if err := os.MkdirAll(path, 0750); err != nil { + fmt.Fprintf(os.Stderr, "%v", err) + return err + } + } + + // Update path to include file name + path = filepath.Join(path, "podman.log") + + // Create the log file if doesn't exist. And append to it if it already exists. + file, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640) + if err != nil { + // Cannot open log file. Logging to stderr + fmt.Fprintf(os.Stderr, "%v", err) + return err + } else { + formatter := new(logrus.TextFormatter) + formatter.FullTimestamp = true + logrus.SetFormatter(formatter) + logrus.SetOutput(file) + } + + // Note this message is only logged if --log-level >= Info! + logrus.Infof("Logging level set to %s", logrus.GetLevel().String()) return nil } diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go index 662fb0a28..b14827592 100644 --- a/cmd/podman/mount.go +++ b/cmd/podman/mount.go @@ -65,7 +65,7 @@ func mountCmd(c *cliconfig.MountValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if os.Geteuid() != 0 { rtc, err := runtime.GetConfig() diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go index cd8370082..3a8f4edb5 100644 --- a/cmd/podman/pause.go +++ b/cmd/podman/pause.go @@ -1,11 +1,10 @@ package main import ( - "os" - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -39,7 +38,7 @@ func init() { } func pauseCmd(c *cliconfig.PauseValues) error { - if os.Geteuid() != 0 { + if rootless.IsRootless() && !remoteclient { return errors.New("pause is not supported for rootless containers") } @@ -47,7 +46,7 @@ func pauseCmd(c *cliconfig.PauseValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) args := c.InputArgs if len(args) < 1 && !c.All { @@ -55,7 +54,7 @@ func pauseCmd(c *cliconfig.PauseValues) error { } ok, failures, err := runtime.PauseContainers(getContext(), c) if err != nil { - if errors.Cause(err) == libpod.ErrNoSuchCtr { + if errors.Cause(err) == define.ErrNoSuchCtr { if len(c.InputArgs) > 1 { exitCode = 125 } else { diff --git a/cmd/podman/platform_linux.go b/cmd/podman/platform_linux.go index 2127923ae..eb11867cc 100644 --- a/cmd/podman/platform_linux.go +++ b/cmd/podman/platform_linux.go @@ -4,13 +4,25 @@ package main import ( "os" + "path/filepath" + "github.com/containers/libpod/pkg/rootless" "github.com/sirupsen/logrus" ) +// userRegistriesFile is the path to the per user registry configuration file. +var userRegistriesFile = filepath.Join(os.Getenv("HOME"), ".config/containers/registries.conf") + func CheckForRegistries() { if _, err := os.Stat("/etc/containers/registries.conf"); err != nil { if os.IsNotExist(err) { + // If it is running in rootless mode, also check the user configuration file + if rootless.IsRootless() { + if _, err := os.Stat(userRegistriesFile); err != nil { + logrus.Warnf("unable to find %s. some podman (image shortnames) commands may be limited", userRegistriesFile) + } + return + } logrus.Warn("unable to find /etc/containers/registries.conf. some podman (image shortnames) commands may be limited") } } diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go index b0f4a44eb..9a5cc3ec1 100644 --- a/cmd/podman/play_kube.go +++ b/cmd/podman/play_kube.go @@ -1,35 +1,12 @@ package main import ( - "context" "fmt" - "io" - "io/ioutil" - "os" - "strings" - - "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/libpod" - "github.com/containers/libpod/libpod/image" - ns "github.com/containers/libpod/pkg/namespaces" - "github.com/containers/libpod/pkg/spec" - "github.com/containers/storage" - "github.com/cri-o/ocicni/pkg/ocicni" - "github.com/ghodss/yaml" + "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "k8s.io/api/core/v1" -) - -const ( - // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath - createDirectoryPermission = 0755 - // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath - createFilePermission = 0644 ) var ( @@ -63,11 +40,11 @@ func init() { flags.BoolVarP(&playKubeCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") // Disabled flags for the remote client if !remote { - flags.StringVar(&playKubeCommand.Authfile, "authfile", getAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&playKubeCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&playKubeCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&playKubeCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") - flags.MarkHidden("signature-policy") + markFlagHidden(flags, "signature-policy") } } @@ -81,289 +58,12 @@ func playKubeCmd(c *cliconfig.KubePlayValues) error { } ctx := getContext() - runtime, err := libpodruntime.GetRuntime(ctx, &c.PodmanCommand) + runtime, err := adapter.GetRuntime(ctx, &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) - pod, err := playKubeYAMLCmd(c, ctx, runtime, args[0]) - if err != nil && pod != nil { - if err2 := runtime.RemovePod(ctx, pod, true, true); err2 != nil { - logrus.Errorf("unable to remove pod %s after failing to play kube", pod.ID()) - } - } + _, err = runtime.PlayKubeYAML(ctx, c, args[0]) return err } - -func playKubeYAMLCmd(c *cliconfig.KubePlayValues, ctx context.Context, runtime *libpod.Runtime, yamlFile string) (*libpod.Pod, error) { - var ( - containers []*libpod.Container - pod *libpod.Pod - podOptions []libpod.PodCreateOption - podYAML v1.Pod - registryCreds *types.DockerAuthConfig - writer io.Writer - ) - - content, err := ioutil.ReadFile(yamlFile) - if err != nil { - return nil, err - } - - if err := yaml.Unmarshal(content, &podYAML); err != nil { - return nil, errors.Wrapf(err, "unable to read %s as YAML", yamlFile) - } - - // check for name collision between pod and container - podName := podYAML.ObjectMeta.Name - for _, n := range podYAML.Spec.Containers { - if n.Name == podName { - fmt.Printf("a container exists with the same name (%s) as the pod in your YAML file; changing pod name to %s_pod\n", podName, podName) - podName = fmt.Sprintf("%s_pod", podName) - } - } - - podOptions = append(podOptions, libpod.WithInfraContainer()) - podOptions = append(podOptions, libpod.WithPodName(podName)) - // TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml - - nsOptions, err := shared.GetNamespaceOptions(strings.Split(shared.DefaultKernelNamespaces, ",")) - if err != nil { - return nil, err - } - podOptions = append(podOptions, nsOptions...) - podPorts := getPodPorts(podYAML.Spec.Containers) - podOptions = append(podOptions, libpod.WithInfraContainerPorts(podPorts)) - - // Create the Pod - pod, err = runtime.NewPod(ctx, podOptions...) - if err != nil { - return pod, err - } - - podInfraID, err := pod.InfraContainerID() - if err != nil { - return pod, err - } - - namespaces := map[string]string{ - // Disabled during code review per mheon - //"pid": fmt.Sprintf("container:%s", podInfraID), - "net": fmt.Sprintf("container:%s", podInfraID), - "user": fmt.Sprintf("container:%s", podInfraID), - "ipc": fmt.Sprintf("container:%s", podInfraID), - "uts": fmt.Sprintf("container:%s", podInfraID), - } - if !c.Quiet { - writer = os.Stderr - } - - dockerRegistryOptions := image.DockerRegistryOptions{ - DockerRegistryCreds: registryCreds, - DockerCertPath: c.CertDir, - } - if c.Flag("tls-verify").Changed { - dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) - } - - // map from name to mount point - volumes := make(map[string]string) - for _, volume := range podYAML.Spec.Volumes { - hostPath := volume.VolumeSource.HostPath - if hostPath == nil { - return pod, errors.Errorf("HostPath is currently the only supported VolumeSource") - } - if hostPath.Type != nil { - switch *hostPath.Type { - case v1.HostPathDirectoryOrCreate: - if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { - if err := os.Mkdir(hostPath.Path, createDirectoryPermission); err != nil { - return pod, errors.Errorf("Error creating HostPath %s at %s", volume.Name, hostPath.Path) - } - } - // unconditionally label a newly created volume as private - if err := libpod.LabelVolumePath(hostPath.Path, false); err != nil { - return pod, errors.Wrapf(err, "Error giving %s a label", hostPath.Path) - } - break - case v1.HostPathFileOrCreate: - if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { - f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, createFilePermission) - if err != nil { - return pod, errors.Errorf("Error creating HostPath %s at %s", volume.Name, hostPath.Path) - } - if err := f.Close(); err != nil { - logrus.Warnf("Error in closing newly created HostPath file: %v", err) - } - } - // unconditionally label a newly created volume as private - if err := libpod.LabelVolumePath(hostPath.Path, false); err != nil { - return pod, errors.Wrapf(err, "Error giving %s a label", hostPath.Path) - } - break - case v1.HostPathDirectory: - case v1.HostPathFile: - case v1.HostPathUnset: - // do nothing here because we will verify the path exists in validateVolumeHostDir - break - default: - return pod, errors.Errorf("Directories are the only supported HostPath type") - } - } - - if err := createconfig.ValidateVolumeHostDir(hostPath.Path); err != nil { - return pod, errors.Wrapf(err, "Error in parsing HostPath in YAML") - } - volumes[volume.Name] = hostPath.Path - } - - for _, container := range podYAML.Spec.Containers { - newImage, err := runtime.ImageRuntime().New(ctx, container.Image, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, false, nil) - if err != nil { - return pod, err - } - createConfig, err := kubeContainerToCreateConfig(ctx, container, runtime, newImage, namespaces, volumes, pod.ID()) - if err != nil { - return pod, err - } - ctr, err := shared.CreateContainerFromCreateConfig(runtime, createConfig, ctx, pod) - if err != nil { - return pod, err - } - containers = append(containers, ctr) - } - - // start the containers - for _, ctr := range containers { - if err := ctr.Start(ctx, true); err != nil { - // Making this a hard failure here to avoid a mess - // the other containers are in created status - return pod, err - } - } - - // We've now successfully converted this YAML into a pod - // print our pod and containers, signifying we succeeded - fmt.Printf("Pod:\n%s\n", pod.ID()) - if len(containers) == 1 { - fmt.Printf("Container:\n") - } - if len(containers) > 1 { - fmt.Printf("Containers:\n") - } - for _, ctr := range containers { - fmt.Println(ctr.ID()) - } - - return pod, nil -} - -// getPodPorts converts a slice of kube container descriptions to an -// array of ocicni portmapping descriptions usable in libpod -func getPodPorts(containers []v1.Container) []ocicni.PortMapping { - var infraPorts []ocicni.PortMapping - for _, container := range containers { - for _, p := range container.Ports { - portBinding := ocicni.PortMapping{ - HostPort: p.HostPort, - ContainerPort: p.ContainerPort, - Protocol: strings.ToLower(string(p.Protocol)), - } - if p.HostIP != "" { - logrus.Debug("HostIP on port bindings is not supported") - } - infraPorts = append(infraPorts, portBinding) - } - } - return infraPorts -} - -// kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container -func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID string) (*createconfig.CreateConfig, error) { - var ( - containerConfig createconfig.CreateConfig - ) - - // The default for MemorySwappiness is -1, not 0 - containerConfig.Resources.MemorySwappiness = -1 - - containerConfig.Image = containerYAML.Image - containerConfig.ImageID = newImage.ID() - containerConfig.Name = containerYAML.Name - containerConfig.Tty = containerYAML.TTY - containerConfig.WorkDir = containerYAML.WorkingDir - - containerConfig.Pod = podID - - imageData, _ := newImage.Inspect(ctx) - - containerConfig.User = "0" - if imageData != nil { - containerConfig.User = imageData.Config.User - } - - if containerConfig.SecurityOpts != nil { - if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil { - containerConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem - } - if containerYAML.SecurityContext.Privileged != nil { - containerConfig.Privileged = *containerYAML.SecurityContext.Privileged - } - - if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil { - containerConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation - } - } - - containerConfig.Command = []string{} - if imageData != nil && imageData.Config != nil { - containerConfig.Command = append(containerConfig.Command, imageData.Config.Entrypoint...) - } - if len(containerConfig.Command) != 0 { - containerConfig.Command = append(containerConfig.Command, containerYAML.Command...) - } else if imageData != nil && imageData.Config != nil { - containerConfig.Command = append(containerConfig.Command, imageData.Config.Cmd...) - } - if imageData != nil && len(containerConfig.Command) == 0 { - return nil, errors.Errorf("No command specified in container YAML or as CMD or ENTRYPOINT in this image for %s", containerConfig.Name) - } - - containerConfig.StopSignal = 15 - - // If the user does not pass in ID mappings, just set to basics - if containerConfig.IDMappings == nil { - containerConfig.IDMappings = &storage.IDMappingOptions{} - } - - containerConfig.NetMode = ns.NetworkMode(namespaces["net"]) - containerConfig.IpcMode = ns.IpcMode(namespaces["ipc"]) - containerConfig.UtsMode = ns.UTSMode(namespaces["uts"]) - // disabled in code review per mheon - //containerConfig.PidMode = ns.PidMode(namespaces["pid"]) - containerConfig.UsernsMode = ns.UsernsMode(namespaces["user"]) - if len(containerConfig.WorkDir) == 0 { - containerConfig.WorkDir = "/" - } - - // Set default environment variables and incorporate data from image, if necessary - envs := shared.EnvVariablesFromData(imageData) - - // Environment Variables - for _, e := range containerYAML.Env { - envs[e.Name] = e.Value - } - containerConfig.Env = envs - - for _, volume := range containerYAML.VolumeMounts { - host_path, exists := volumes[volume.Name] - if !exists { - return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) - } - if err := createconfig.ValidateVolumeCtrDir(volume.MountPath); err != nil { - return nil, errors.Wrapf(err, "error in parsing MountPath") - } - containerConfig.Volumes = append(containerConfig.Volumes, fmt.Sprintf("%s:%s", host_path, volume.MountPath)) - } - return &containerConfig, nil -} diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index c891f2c7b..b6154b4db 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -6,8 +6,10 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/errorhandling" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -45,8 +47,8 @@ func init() { flags.StringVar(&podCreateCommand.CgroupParent, "cgroup-parent", "", "Set parent cgroup for the pod") flags.BoolVar(&podCreateCommand.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") - flags.StringVar(&podCreateCommand.InfraImage, "infra-image", libpod.DefaultInfraImage, "The image of the infra container to associate with the pod") - flags.StringVar(&podCreateCommand.InfraCommand, "infra-command", libpod.DefaultInfraCommand, "The command to run on the infra container when the pod is started") + flags.StringVar(&podCreateCommand.InfraImage, "infra-image", define.DefaultInfraImage, "The image of the infra container to associate with the pod") + flags.StringVar(&podCreateCommand.InfraCommand, "infra-command", define.DefaultInfraCommand, "The command to run on the infra container when the pod is started") flags.StringSliceVar(&podCreateCommand.LabelFile, "label-file", []string{}, "Read in a line delimited file of labels") flags.StringSliceVarP(&podCreateCommand.Labels, "label", "l", []string{}, "Set metadata on pod (default [])") flags.StringVarP(&podCreateCommand.Name, "name", "n", "", "Assign a name to the pod") @@ -55,7 +57,6 @@ func init() { flags.StringVar(&podCreateCommand.Share, "share", shared.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") } - func podCreateCmd(c *cliconfig.PodCreateValues) error { var ( err error @@ -66,7 +67,7 @@ func podCreateCmd(c *cliconfig.PodCreateValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if len(c.Publish) > 0 { if !c.Infra { @@ -78,15 +79,15 @@ func podCreateCmd(c *cliconfig.PodCreateValues) error { return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container") } if c.Flag("pod-id-file").Changed && os.Geteuid() == 0 { - podIdFile, err = libpod.OpenExclusiveFile(c.PodIDFile) + podIdFile, err = util.OpenExclusiveFile(c.PodIDFile) if err != nil && os.IsExist(err) { return errors.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", c.PodIDFile) } if err != nil { return errors.Errorf("error opening pod-id-file %s", c.PodIDFile) } - defer podIdFile.Close() - defer podIdFile.Sync() + defer errorhandling.CloseQuiet(podIdFile) + defer errorhandling.SyncQuiet(podIdFile) } labels, err := shared.GetAllLabels(c.LabelFile, c.Labels) diff --git a/cmd/podman/pod_inspect.go b/cmd/podman/pod_inspect.go index a22624078..03b5a8cc4 100644 --- a/cmd/podman/pod_inspect.go +++ b/cmd/podman/pod_inspect.go @@ -53,7 +53,7 @@ func podInspectCmd(c *cliconfig.PodInspectValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if c.Latest { pod, err = runtime.GetLatestPod() diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go index c1ea66126..9bda77471 100644 --- a/cmd/podman/pod_kill.go +++ b/cmd/podman/pod_kill.go @@ -53,9 +53,9 @@ func podKillCmd(c *cliconfig.PodKillValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) - var killSignal uint = uint(syscall.SIGTERM) + killSignal := uint(syscall.SIGTERM) if c.Signal != "" { // Check if the signalString provided by the user is valid diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go index e8574bfdc..45e1319ff 100644 --- a/cmd/podman/pod_pause.go +++ b/cmd/podman/pod_pause.go @@ -49,14 +49,14 @@ func podPauseCmd(c *cliconfig.PodPauseValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) pauseIDs, conErrors, pauseErrors := runtime.PausePods(c) for _, p := range pauseIDs { fmt.Println(p) } - if conErrors != nil && len(conErrors) > 0 { + if len(conErrors) > 0 { for ctr, err := range conErrors { if lastError != nil { logrus.Errorf("%q", lastError) diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go index b9dcbc05d..bda447c57 100644 --- a/cmd/podman/pod_ps.go +++ b/cmd/podman/pod_ps.go @@ -11,7 +11,7 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" "github.com/docker/go-units" @@ -20,7 +20,7 @@ import ( ) const ( - STOPPED = "Stopped" + STOPPED = "Stopped" //nolint RUNNING = "Running" PAUSED = "Paused" EXITED = "Exited" @@ -36,9 +36,9 @@ var ( ) type podPsCtrInfo struct { - Name string `"json:name,omitempty"` - Id string `"json:id,omitempty"` - Status string `"json:status,omitempty"` + Name string `json:"name,omitempty"` + Id string `json:"id,omitempty"` + Status string `json:"status,omitempty"` } type podPsOptions struct { @@ -161,7 +161,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) opts := podPsOptions{ NoTrunc: c.NoTrunc, @@ -282,7 +282,7 @@ func generatePodFilterFuncs(filter, filterValue string) (func(pod *adapter.Pod) } for _, ctr_status := range ctr_statuses { state := ctr_status.String() - if ctr_status == libpod.ContainerStateConfigured { + if ctr_status == define.ContainerStateConfigured { state = "created" } if state == filterValue { @@ -504,15 +504,15 @@ func getAndSortPodJSONParams(pods []*adapter.Pod, opts podPsOptions) ([]podPsJSO } var status string switch batchInfo.ConState { - case libpod.ContainerStateExited: + case define.ContainerStateExited: fallthrough - case libpod.ContainerStateStopped: + case define.ContainerStateStopped: status = EXITED - case libpod.ContainerStateRunning: + case define.ContainerStateRunning: status = RUNNING - case libpod.ContainerStatePaused: + case define.ContainerStatePaused: status = PAUSED - case libpod.ContainerStateCreated, libpod.ContainerStateConfigured: + case define.ContainerStateCreated, define.ContainerStateConfigured: status = CREATED default: status = ERROR @@ -552,9 +552,6 @@ func generatePodPsOutput(pods []*adapter.Pod, opts podPsOptions) error { switch opts.Format { case formats.JSONString: - if err != nil { - return errors.Wrapf(err, "unable to create JSON for output") - } out = formats.JSONStructArray{Output: podPsToGeneric([]podPsTemplateParams{}, psOutput)} default: psOutput, err := getPodTemplateOutput(psOutput, opts) @@ -564,5 +561,5 @@ func generatePodPsOutput(pods []*adapter.Pod, opts podPsOptions) error { out = formats.StdoutTemplateArray{Output: podPsToGeneric(psOutput, []podPsJSONParams{}), Template: opts.Format, Fields: psOutput[0].podHeaderMap()} } - return formats.Writer(out).Out() + return out.Out() } diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go index a1f4c8359..cc090bd6e 100644 --- a/cmd/podman/pod_restart.go +++ b/cmd/podman/pod_restart.go @@ -51,14 +51,14 @@ func podRestartCmd(c *cliconfig.PodRestartValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) restartIDs, conErrors, restartErrors := runtime.RestartPods(getContext(), c) for _, p := range restartIDs { fmt.Println(p) } - if conErrors != nil && len(conErrors) > 0 { + if len(conErrors) > 0 { for ctr, err := range conErrors { if lastError != nil { logrus.Errorf("%q", lastError) diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go index 218ed8154..82d0eb977 100644 --- a/cmd/podman/pod_rm.go +++ b/cmd/podman/pod_rm.go @@ -51,7 +51,7 @@ func podRmCmd(c *cliconfig.PodRmValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) podRmIds, podRmErrors := runtime.RemovePods(getContext(), c) for _, p := range podRmIds { diff --git a/cmd/podman/pod_start.go b/cmd/podman/pod_start.go index 5c9225428..64c951b43 100644 --- a/cmd/podman/pod_start.go +++ b/cmd/podman/pod_start.go @@ -49,7 +49,7 @@ func podStartCmd(c *cliconfig.PodStartValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) podStartIDs, podStartErrors := runtime.StartPods(getContext(), c) for _, p := range podStartIDs { diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go index e0e5ca24e..46cacc026 100644 --- a/cmd/podman/pod_stats.go +++ b/cmd/podman/pod_stats.go @@ -13,6 +13,7 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -73,16 +74,13 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error { if ctr > 1 { return errors.Errorf("--all, --latest and containers cannot be used together") - } else if ctr == 0 { - // If user didn't specify, imply --all - all = true } runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) times := -1 if c.NoStream { @@ -93,24 +91,6 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error { if err != nil { return errors.Wrapf(err, "unable to get a list of pods") } - // First we need to get an initial pass of pod/ctr stats (these are not printed) - var podStats []*adapter.PodContainerStats - for _, p := range pods { - cons, err := p.AllContainersByID() - if err != nil { - return err - } - emptyStats := make(map[string]*libpod.ContainerStats) - // Iterate the pods container ids and make blank stats for them - for _, c := range cons { - emptyStats[c] = &libpod.ContainerStats{} - } - ps := adapter.PodContainerStats{ - Pod: p, - ContainerStats: emptyStats, - } - podStats = append(podStats, &ps) - } // Create empty container stat results for our first pass var previousPodStats []*adapter.PodContainerStats @@ -153,7 +133,7 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error { for _, p := range pods { prevStat := getPreviousPodContainerStats(p.ID(), previousPodStats) newPodStats, err := p.GetPodStats(prevStat) - if errors.Cause(err) == libpod.ErrNoSuchPod { + if errors.Cause(err) == define.ErrNoSuchPod { continue } if err != nil { @@ -172,7 +152,9 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error { tm.Flush() } if strings.ToLower(format) == formats.JSONString { - outputJson(newStats) + if err := outputJson(newStats); err != nil { + return err + } } else { results := podContainerStatsToPodStatOut(newStats) @@ -270,7 +252,7 @@ func printPSFormat(format string, stats []*podStatOut, headerNames map[string]st 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") + 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 { @@ -299,17 +281,3 @@ func outputJson(stats []*adapter.PodContainerStats) error { fmt.Println(string(b)) return nil } - -func getPodsByList(podList []string, r *libpod.Runtime) ([]*libpod.Pod, error) { - var ( - pods []*libpod.Pod - ) - for _, p := range podList { - pod, err := r.LookupPod(p) - if err != nil { - return nil, err - } - pods = append(pods, pod) - } - return pods, nil -} diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go index b4b1718d9..edda99550 100644 --- a/cmd/podman/pod_stop.go +++ b/cmd/podman/pod_stop.go @@ -51,7 +51,7 @@ func podStopCmd(c *cliconfig.PodStopValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) podStopIds, podStopErrors := runtime.StopPods(getContext(), c) for _, p := range podStopIds { diff --git a/cmd/podman/pod_top.go b/cmd/podman/pod_top.go index 64e32318e..fcd9c4f3c 100644 --- a/cmd/podman/pod_top.go +++ b/cmd/podman/pod_top.go @@ -2,13 +2,13 @@ package main import ( "fmt" - "github.com/containers/libpod/pkg/adapter" "os" "strings" "text/tabwriter" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -44,8 +44,7 @@ func init() { flags := podTopCommand.Flags() flags.BoolVarP(&podTopCommand.Latest, "latest,", "l", false, "Act on the latest pod podman is aware of") flags.BoolVar(&podTopCommand.ListDescriptors, "list-descriptors", false, "") - flags.MarkHidden("list-descriptors") - + markFlagHidden(flags, "list-descriptors") } func podTopCmd(c *cliconfig.PodTopValues) error { @@ -55,7 +54,7 @@ func podTopCmd(c *cliconfig.PodTopValues) error { args := c.InputArgs if c.ListDescriptors { - descriptors, err := libpod.GetContainerPidInformationDescriptors() + descriptors, err := util.GetContainerPidInformationDescriptors() if err != nil { return err } @@ -71,7 +70,7 @@ func podTopCmd(c *cliconfig.PodTopValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if c.Latest { descriptors = args @@ -85,8 +84,9 @@ func podTopCmd(c *cliconfig.PodTopValues) error { return err } for _, proc := range psOutput { - fmt.Fprintln(w, proc) + if _, err := fmt.Fprintln(w, proc); err != nil { + return err + } } - w.Flush() - return nil + return w.Flush() } diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go index c5b7e6a18..833434c3f 100644 --- a/cmd/podman/pod_unpause.go +++ b/cmd/podman/pod_unpause.go @@ -50,14 +50,14 @@ func podUnpauseCmd(c *cliconfig.PodUnpauseValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) unpauseIDs, conErrors, unpauseErrors := runtime.UnpausePods(c) for _, p := range unpauseIDs { fmt.Println(p) } - if conErrors != nil && len(conErrors) > 0 { + if len(conErrors) > 0 { for ctr, err := range conErrors { if lastError != nil { logrus.Errorf("%q", lastError) diff --git a/cmd/podman/pods_prune.go b/cmd/podman/pods_prune.go index bdd75f9de..d40e37bdb 100644 --- a/cmd/podman/pods_prune.go +++ b/cmd/podman/pods_prune.go @@ -40,8 +40,11 @@ func podPruneCmd(c *cliconfig.PodPruneValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ok, failures, err := runtime.PrunePods(getContext(), c) + if err != nil { + return err + } return printCmdResults(ok, failures) } diff --git a/cmd/podman/port.go b/cmd/podman/port.go index 1bd2d623e..5753c8e56 100644 --- a/cmd/podman/port.go +++ b/cmd/podman/port.go @@ -95,9 +95,12 @@ func portCmd(c *cliconfig.PortValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) containers, err := runtime.Port(c) + if err != nil { + return err + } for _, con := range containers { portmappings, err := con.PortMappings() if err != nil { diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index eb5181126..9fad0ea65 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -15,87 +15,32 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/pkg/adapter" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/fields" ) const ( - mountTruncLength = 12 - hid = "CONTAINER ID" - himage = "IMAGE" - hcommand = "COMMAND" - hcreated = "CREATED" - hstatus = "STATUS" - hports = "PORTS" - hnames = "NAMES" - hsize = "SIZE" - hinfra = "IS INFRA" - hpod = "POD" - nspid = "PID" - nscgroup = "CGROUPNS" - nsipc = "IPC" - nsmnt = "MNT" - nsnet = "NET" - nspidns = "PIDNS" - nsuserns = "USERNS" - nsuts = "UTS" + hid = "CONTAINER ID" + himage = "IMAGE" + hcommand = "COMMAND" + hcreated = "CREATED" + hstatus = "STATUS" + hports = "PORTS" + hnames = "NAMES" + hsize = "SIZE" + hinfra = "IS INFRA" //nolint + hpod = "POD" + nspid = "PID" + nscgroup = "CGROUPNS" + nsipc = "IPC" + nsmnt = "MNT" + nsnet = "NET" + nspidns = "PIDNS" + nsuserns = "USERNS" + nsuts = "UTS" ) -type psTemplateParams struct { - ID string - Image string - Command string - CreatedAtTime time.Time - Created string - Status string - Ports string - Size string - Names string - Labels string - Mounts string - PID int - CGROUPNS string - IPC string - MNT string - NET string - PIDNS string - USERNS string - UTS string - Pod string - IsInfra bool -} - -// psJSONParams is used as a base structure for the psParams -// If template output is requested, psJSONParams will be converted to -// psTemplateParams. -// psJSONParams will be populated by data from libpod.Container, -// the members of the struct are the sama data types as their sources. -type psJSONParams struct { - ID string `json:"id"` - Image string `json:"image"` - ImageID string `json:"image_id"` - Command []string `json:"command"` - ExitCode int32 `json:"exitCode"` - Exited bool `json:"exited"` - CreatedAt time.Time `json:"createdAt"` - StartedAt time.Time `json:"startedAt"` - ExitedAt time.Time `json:"exitedAt"` - Status string `json:"status"` - PID int `json:"PID"` - Ports []ocicni.PortMapping `json:"ports"` - Size *shared.ContainerSize `json:"size,omitempty"` - Names string `json:"names"` - Labels fields.Set `json:"labels"` - Mounts []string `json:"mounts"` - ContainerRunning bool `json:"ctrRunning"` - Namespaces *shared.Namespace `json:"namespace,omitempty"` - Pod string `json:"pod,omitempty"` - IsInfra bool `json:"infra"` -} - // Type declaration and functions for sorting the PS output type psSorted []shared.PsContainerOutput @@ -197,7 +142,11 @@ func init() { } func psCmd(c *cliconfig.PsValues) error { - var watch bool + var ( + watch bool + runtime *adapter.LocalRuntime + err error + ) if c.Watch > 0 { watch = true @@ -210,13 +159,16 @@ func psCmd(c *cliconfig.PsValues) error { if err := checkFlagsPassed(c); err != nil { return errors.Wrapf(err, "error with flags passed") } - - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + if !c.Size { + runtime, err = adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) + } else { + runtime, err = adapter.GetRuntime(getContext(), &c.PodmanCommand) + } if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if !watch { if err := psDisplay(c, runtime); err != nil { @@ -266,22 +218,6 @@ func checkFlagsPassed(c *cliconfig.PsValues) error { return nil } -// generate the accurate header based on template given -func (p *psTemplateParams) headerMap() map[string]string { - v := reflect.Indirect(reflect.ValueOf(p)) - values := make(map[string]string) - - for i := 0; i < v.NumField(); i++ { - key := v.Type().Field(i).Name - value := key - if value == "ID" { - value = "Container" + value - } - values[key] = strings.ToUpper(splitCamelCase(value)) - } - return values -} - func sortPsOutput(sortBy string, psOutput psSorted) (psSorted, error) { switch sortBy { case "id": @@ -308,57 +244,6 @@ func sortPsOutput(sortBy string, psOutput psSorted) (psSorted, error) { return psOutput, nil } -// getLabels converts the labels to a string of the form "key=value, key2=value2" -func formatLabels(labels map[string]string) string { - var arr []string - if len(labels) > 0 { - for key, val := range labels { - temp := key + "=" + val - arr = append(arr, temp) - } - return strings.Join(arr, ",") - } - return "" -} - -// getMounts converts the volumes mounted to a string of the form "mount1, mount2" -// it truncates it if noTrunc is false -func getMounts(mounts []string, noTrunc bool) string { - return strings.Join(getMountsArray(mounts, noTrunc), ",") -} - -func getMountsArray(mounts []string, noTrunc bool) []string { - var arr []string - if len(mounts) == 0 { - return mounts - } - for _, mount := range mounts { - splitArr := strings.Split(mount, ":") - if len(splitArr[0]) > mountTruncLength && !noTrunc { - arr = append(arr, splitArr[0][:mountTruncLength]+"...") - continue - } - arr = append(arr, splitArr[0]) - } - return arr -} - -// portsToString converts the ports used to a string of the from "port1, port2" -func portsToString(ports []ocicni.PortMapping) string { - var portDisplay []string - if len(ports) == 0 { - return "" - } - for _, v := range ports { - hostIP := v.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" - } - portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) - } - return strings.Join(portDisplay, ", ") -} - func printFormat(format string, containers []shared.PsContainerOutput) error { // return immediately if no containers are present if len(containers) == 0 { @@ -434,6 +319,9 @@ func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error { } pss, err := runtime.Ps(c, opts) + if err != nil { + return err + } // Here and down if opts.Sort != "" { pss, err = sortPsOutput(opts.Sort, pss) @@ -491,8 +379,8 @@ func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error { size = units.HumanSizeWithPrecision(0, 0) } else { size = units.HumanSizeWithPrecision(float64(container.Size.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(container.Size.RootFsSize), 3) + ")" - fmt.Fprintf(w, "\t%s", size) } + fmt.Fprintf(w, "\t%s", size) } } else { diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 115f437d8..0eee51e79 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -11,10 +11,11 @@ import ( "github.com/containers/image/transports/alltransports" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" - opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -55,11 +56,11 @@ func init() { flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") // Disabled flags for the remote client if !remote { - flags.StringVar(&pullCommand.Authfile, "authfile", getAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pullCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&pullCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&pullCommand.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") - flags.MarkHidden("signature-policy") + markFlagHidden(flags, "signature-policy") } } @@ -81,7 +82,7 @@ func pullCmd(c *cliconfig.PullValues) (retError error) { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) args := c.InputArgs if len(args) == 0 { diff --git a/cmd/podman/push.go b/cmd/podman/push.go index 497820156..43df8c2de 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -10,6 +10,7 @@ import ( "github.com/containers/image/manifest" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" @@ -57,12 +58,12 @@ func init() { // Disabled flags for the remote client if !remote { - flags.StringVar(&pushCommand.Authfile, "authfile", getAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pushCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&pushCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.BoolVar(&pushCommand.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)") flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") - flags.MarkHidden("signature-policy") + markFlagHidden(flags, "signature-policy") } } @@ -108,7 +109,7 @@ func pushCmd(c *cliconfig.PushValues) error { if err != nil { return errors.Wrapf(err, "could not create runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) var writer io.Writer if !c.Quiet { diff --git a/cmd/podman/refresh.go b/cmd/podman/refresh.go index 9f9cbf908..b21a4ff79 100644 --- a/cmd/podman/refresh.go +++ b/cmd/podman/refresh.go @@ -42,7 +42,7 @@ func refreshCmd(c *cliconfig.RefreshValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) allCtrs, err := runtime.GetAllContainers() if err != nil { diff --git a/cmd/podman/remoteclientconfig/config.go b/cmd/podman/remoteclientconfig/config.go new file mode 100644 index 000000000..01f293ec3 --- /dev/null +++ b/cmd/podman/remoteclientconfig/config.go @@ -0,0 +1,21 @@ +package remoteclientconfig + +const remoteConfigFileName string = "podman-remote.conf" + +// RemoteConfig describes the podman remote configuration file +type RemoteConfig struct { + Connections map[string]RemoteConnection +} + +// RemoteConnection describes the attributes of a podman-remote endpoint +type RemoteConnection struct { + Destination string `toml:"destination"` + Username string `toml:"username"` + IsDefault bool `toml:"default"` +} + +// GetConfigFilePath is a simple helper to export the configuration file's +// path based on arch, etc +func GetConfigFilePath() string { + return getConfigFilePath() +} diff --git a/cmd/podman/remoteclientconfig/config_darwin.go b/cmd/podman/remoteclientconfig/config_darwin.go new file mode 100644 index 000000000..b94941381 --- /dev/null +++ b/cmd/podman/remoteclientconfig/config_darwin.go @@ -0,0 +1,12 @@ +package remoteclientconfig + +import ( + "path/filepath" + + "github.com/docker/docker/pkg/homedir" +) + +func getConfigFilePath() string { + homeDir := homedir.Get() + return filepath.Join(homeDir, ".config", "containers", remoteConfigFileName) +} diff --git a/cmd/podman/remoteclientconfig/config_linux.go b/cmd/podman/remoteclientconfig/config_linux.go new file mode 100644 index 000000000..b94941381 --- /dev/null +++ b/cmd/podman/remoteclientconfig/config_linux.go @@ -0,0 +1,12 @@ +package remoteclientconfig + +import ( + "path/filepath" + + "github.com/docker/docker/pkg/homedir" +) + +func getConfigFilePath() string { + homeDir := homedir.Get() + return filepath.Join(homeDir, ".config", "containers", remoteConfigFileName) +} diff --git a/cmd/podman/remoteclientconfig/config_windows.go b/cmd/podman/remoteclientconfig/config_windows.go new file mode 100644 index 000000000..fa6ffca63 --- /dev/null +++ b/cmd/podman/remoteclientconfig/config_windows.go @@ -0,0 +1,12 @@ +package remoteclientconfig + +import ( + "path/filepath" + + "github.com/docker/docker/pkg/homedir" +) + +func getConfigFilePath() string { + homeDir := homedir.Get() + return filepath.Join(homeDir, "AppData", "podman", remoteConfigFileName) +} diff --git a/cmd/podman/remoteclientconfig/configfile.go b/cmd/podman/remoteclientconfig/configfile.go new file mode 100644 index 000000000..56a868733 --- /dev/null +++ b/cmd/podman/remoteclientconfig/configfile.go @@ -0,0 +1,64 @@ +package remoteclientconfig + +import ( + "io" + + "github.com/BurntSushi/toml" + "github.com/pkg/errors" +) + +// ReadRemoteConfig takes an io.Reader representing the remote configuration +// file and returns a remoteconfig +func ReadRemoteConfig(reader io.Reader) (*RemoteConfig, error) { + var remoteConfig RemoteConfig + // the configuration file does not exist + if reader == nil { + return &remoteConfig, ErrNoConfigationFile + } + _, err := toml.DecodeReader(reader, &remoteConfig) + if err != nil { + return nil, err + } + // We need to validate each remote connection has fields filled out + for name, conn := range remoteConfig.Connections { + if len(conn.Destination) < 1 { + return nil, errors.Errorf("connection %q has no destination defined", name) + } + } + return &remoteConfig, err +} + +// GetDefault returns the default RemoteConnection. If there is only one +// connection, we assume it is the default as well +func (r *RemoteConfig) GetDefault() (*RemoteConnection, error) { + if len(r.Connections) == 0 { + return nil, ErrNoDefinedConnections + } + for _, v := range r.Connections { + v := v + if len(r.Connections) == 1 { + // if there is only one defined connection, we assume it is + // the default whether tagged as such or not + return &v, nil + } + if v.IsDefault { + return &v, nil + } + } + return nil, ErrNoDefaultConnection +} + +// GetRemoteConnection "looks up" a remote connection by name and returns it in the +// form of a RemoteConnection +func (r *RemoteConfig) GetRemoteConnection(name string) (*RemoteConnection, error) { + if len(r.Connections) == 0 { + return nil, ErrNoDefinedConnections + } + for k, v := range r.Connections { + v := v + if k == name { + return &v, nil + } + } + return nil, errors.Wrap(ErrConnectionNotFound, name) +} diff --git a/cmd/podman/remoteclientconfig/configfile_test.go b/cmd/podman/remoteclientconfig/configfile_test.go new file mode 100644 index 000000000..66e0a4693 --- /dev/null +++ b/cmd/podman/remoteclientconfig/configfile_test.go @@ -0,0 +1,201 @@ +package remoteclientconfig + +import ( + "io" + "reflect" + "strings" + "testing" +) + +var goodConfig = ` +[connections] + +[connections.homer] +destination = "192.168.1.1" +username = "myuser" +default = true + +[connections.bart] +destination = "foobar.com" +username = "root" +` +var noDest = ` +[connections] + +[connections.homer] +destination = "192.168.1.1" +username = "myuser" +default = true + +[connections.bart] +username = "root" +` + +var noUser = ` +[connections] + +[connections.homer] +destination = "192.168.1.1" +` + +func makeGoodResult() *RemoteConfig { + var goodConnections = make(map[string]RemoteConnection) + goodConnections["homer"] = RemoteConnection{ + Destination: "192.168.1.1", + Username: "myuser", + IsDefault: true, + } + goodConnections["bart"] = RemoteConnection{ + Destination: "foobar.com", + Username: "root", + } + var goodResult = RemoteConfig{ + Connections: goodConnections, + } + return &goodResult +} + +func makeNoUserResult() *RemoteConfig { + var goodConnections = make(map[string]RemoteConnection) + goodConnections["homer"] = RemoteConnection{ + Destination: "192.168.1.1", + } + var goodResult = RemoteConfig{ + Connections: goodConnections, + } + return &goodResult +} + +func TestReadRemoteConfig(t *testing.T) { + type args struct { + reader io.Reader + } + tests := []struct { + name string + args args + want *RemoteConfig + wantErr bool + }{ + // good test should pass + {"good", args{reader: strings.NewReader(goodConfig)}, makeGoodResult(), false}, + // a connection with no destination is an error + {"nodest", args{reader: strings.NewReader(noDest)}, nil, true}, + // a connnection with no user is OK + {"nouser", args{reader: strings.NewReader(noUser)}, makeNoUserResult(), false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ReadRemoteConfig(tt.args.reader) + if (err != nil) != tt.wantErr { + t.Errorf("ReadRemoteConfig() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ReadRemoteConfig() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRemoteConfig_GetDefault(t *testing.T) { + good := make(map[string]RemoteConnection) + good["homer"] = RemoteConnection{ + Username: "myuser", + Destination: "192.168.1.1", + IsDefault: true, + } + good["bart"] = RemoteConnection{ + Username: "root", + Destination: "foobar.com", + } + noDefault := make(map[string]RemoteConnection) + noDefault["homer"] = RemoteConnection{ + Username: "myuser", + Destination: "192.168.1.1", + } + noDefault["bart"] = RemoteConnection{ + Username: "root", + Destination: "foobar.com", + } + single := make(map[string]RemoteConnection) + single["homer"] = RemoteConnection{ + Username: "myuser", + Destination: "192.168.1.1", + } + + none := make(map[string]RemoteConnection) + + type fields struct { + Connections map[string]RemoteConnection + } + tests := []struct { + name string + fields fields + want *RemoteConnection + wantErr bool + }{ + // A good toml should return the connection that is marked isDefault + {"good", fields{Connections: makeGoodResult().Connections}, &RemoteConnection{"192.168.1.1", "myuser", true}, false}, + // If nothing is marked as isDefault and there is more than one connection, error should occur + {"nodefault", fields{Connections: noDefault}, nil, true}, + // if nothing is marked as isDefault but there is only one connection, the one connection is considered the default + {"single", fields{Connections: none}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &RemoteConfig{ + Connections: tt.fields.Connections, + } + got, err := r.GetDefault() + if (err != nil) != tt.wantErr { + t.Errorf("RemoteConfig.GetDefault() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("RemoteConfig.GetDefault() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRemoteConfig_GetRemoteConnection(t *testing.T) { + type fields struct { + Connections map[string]RemoteConnection + } + type args struct { + name string + } + + blank := make(map[string]RemoteConnection) + tests := []struct { + name string + fields fields + args args + want *RemoteConnection + wantErr bool + }{ + // Good connection + {"goodhomer", fields{Connections: makeGoodResult().Connections}, args{name: "homer"}, &RemoteConnection{"192.168.1.1", "myuser", true}, false}, + // Good connection + {"goodbart", fields{Connections: makeGoodResult().Connections}, args{name: "bart"}, &RemoteConnection{"foobar.com", "root", false}, false}, + // Getting an unknown connection should result in error + {"noexist", fields{Connections: makeGoodResult().Connections}, args{name: "foobar"}, nil, true}, + // Getting a connection when there are none should result in an error + {"none", fields{Connections: blank}, args{name: "foobar"}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &RemoteConfig{ + Connections: tt.fields.Connections, + } + got, err := r.GetRemoteConnection(tt.args.name) + if (err != nil) != tt.wantErr { + t.Errorf("RemoteConfig.GetRemoteConnection() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("RemoteConfig.GetRemoteConnection() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/podman/remoteclientconfig/errors.go b/cmd/podman/remoteclientconfig/errors.go new file mode 100644 index 000000000..2689d3b49 --- /dev/null +++ b/cmd/podman/remoteclientconfig/errors.go @@ -0,0 +1,14 @@ +package remoteclientconfig + +import "errors" + +var ( + // ErrNoDefaultConnection no default connection is defined in the podman-remote.conf file + ErrNoDefaultConnection = errors.New("no default connection is defined") + // ErrNoDefinedConnections no connections are defined in the podman-remote.conf file + ErrNoDefinedConnections = errors.New("no remote connections have been defined") + // ErrConnectionNotFound unable to lookup connection by name + ErrConnectionNotFound = errors.New("remote connection not found by name") + // ErrNoConfigationFile no config file found + ErrNoConfigationFile = errors.New("no configuration file found") +) diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go index 437676eef..494a9ec06 100644 --- a/cmd/podman/restart.go +++ b/cmd/podman/restart.go @@ -2,7 +2,7 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -39,8 +39,8 @@ func init() { flags.BoolVarP(&restartCommand.All, "all", "a", false, "Restart all non-running containers") flags.BoolVarP(&restartCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&restartCommand.Running, "running", false, "Restart only running containers when --all is used") - flags.UintVarP(&restartCommand.Timeout, "timeout", "t", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") - flags.UintVar(&restartCommand.Timeout, "time", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") + flags.UintVarP(&restartCommand.Timeout, "timeout", "t", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") + flags.UintVar(&restartCommand.Timeout, "time", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") markFlagHiddenForRemoteClient("latest", flags) } @@ -48,18 +48,18 @@ func init() { func restartCmd(c *cliconfig.RestartValues) error { all := c.All if len(c.InputArgs) < 1 && !c.Latest && !all { - return errors.Wrapf(libpod.ErrInvalidArg, "you must provide at least one container name or ID") + return errors.Wrapf(define.ErrInvalidArg, "you must provide at least one container name or ID") } runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ok, failures, err := runtime.Restart(getContext(), c) if err != nil { - if errors.Cause(err) == libpod.ErrNoSuchCtr { + if errors.Cause(err) == define.ErrNoSuchCtr { if len(c.InputArgs) > 1 { exitCode = 125 } else { diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go index 8cfd5ca0d..3ae141d41 100644 --- a/cmd/podman/restore.go +++ b/cmd/podman/restore.go @@ -2,7 +2,6 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" @@ -24,10 +23,10 @@ var ( restoreCommand.InputArgs = args restoreCommand.GlobalFlags = MainGlobalOpts restoreCommand.Remote = remoteclient - return restoreCmd(&restoreCommand) + return restoreCmd(&restoreCommand, cmd) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllAndLatest(cmd, args, true) }, Example: `podman container restore ctrID podman container restore --latest @@ -43,13 +42,15 @@ func init() { flags.BoolVarP(&restoreCommand.All, "all", "a", false, "Restore all checkpointed containers") flags.BoolVarP(&restoreCommand.Keep, "keep", "k", false, "Keep all temporary checkpoint files") flags.BoolVarP(&restoreCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - // TODO: add ContainerStateCheckpointed - flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections") + flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Restore a container with established TCP connections") + flags.StringVarP(&restoreCommand.Import, "import", "i", "", "Restore from exported checkpoint archive (tar.gz)") + flags.StringVarP(&restoreCommand.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)") + flags.BoolVar(&restoreCommand.IgnoreRootfs, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint") markFlagHiddenForRemoteClient("latest", flags) } -func restoreCmd(c *cliconfig.RestoreValues) error { +func restoreCmd(c *cliconfig.RestoreValues, cmd *cobra.Command) error { if rootless.IsRootless() { return errors.New("restoring a container requires root") } @@ -58,11 +59,36 @@ func restoreCmd(c *cliconfig.RestoreValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) - options := libpod.ContainerCheckpointOptions{ - Keep: c.Keep, - TCPEstablished: c.TcpEstablished, + if c.Import == "" && c.IgnoreRootfs { + return errors.Errorf("--ignore-rootfs can only be used with --import") } - return runtime.Restore(c, options) + + if c.Import == "" && c.Name != "" { + return errors.Errorf("--name can only be used with --import") + } + + if c.Name != "" && c.TcpEstablished { + return errors.Errorf("--tcp-established cannot be used with --name") + } + + argLen := len(c.InputArgs) + if c.Import != "" { + if c.All || c.Latest { + return errors.Errorf("Cannot use --import with --all or --latest") + } + if argLen > 0 { + return errors.Errorf("Cannot use --import with positional arguments") + } + } + + if (c.All || c.Latest) && argLen > 0 { + return errors.Errorf("no arguments are needed with --all or --latest") + } + if argLen < 1 && !c.All && !c.Latest && c.Import == "" { + return errors.Errorf("you must provide at least one name or id") + } + + return runtime.Restore(getContext(), c) } diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 1bf56b782..958ca1c60 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -42,7 +42,9 @@ func init() { 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.BoolVar(&rmCommand.Storage, "storage", false, "Remove container from storage library") flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container") + markFlagHiddenForRemoteClient("storage", flags) markFlagHiddenForRemoteClient("latest", flags) } @@ -52,11 +54,18 @@ func rmCmd(c *cliconfig.RmValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) + + // Storage conflicts with --all/--latest/--volumes + if c.Storage { + if c.All || c.Latest || c.Volumes { + return errors.Errorf("--storage conflicts with --volumes, --all, and --latest") + } + } ok, failures, err := runtime.RemoveContainers(getContext(), c) if err != nil { - if errors.Cause(err) == libpod.ErrNoSuchCtr { + if errors.Cause(err) == define.ErrNoSuchCtr { if len(c.InputArgs) > 1 { exitCode = 125 } else { diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go index 4c41a3ad5..57e78c34a 100644 --- a/cmd/podman/rmi.go +++ b/cmd/podman/rmi.go @@ -55,7 +55,7 @@ func rmiCmd(c *cliconfig.RmiValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) args := c.InputArgs if len(args) == 0 && !removeAll { @@ -87,7 +87,7 @@ func rmiCmd(c *cliconfig.RmiValues) error { if removeAll { var imagesToDelete []*adapter.ContainerImage - imagesToDelete, err = runtime.GetImages() + imagesToDelete, err = runtime.GetRWImages() if err != nil { return errors.Wrapf(err, "unable to query local images") } @@ -107,7 +107,7 @@ func rmiCmd(c *cliconfig.RmiValues) error { removeImage(i) } lastNumberofImages = len(imagesToDelete) - imagesToDelete, err = runtime.GetImages() + imagesToDelete, err = runtime.GetRWImages() if err != nil { return err } diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 7d84d716b..4836c99dc 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -3,7 +3,7 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" - opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -34,10 +34,10 @@ func init() { runCommand.SetUsageTemplate(UsageTemplate()) flags := runCommand.Flags() flags.SetInterspersed(false) + flags.SetNormalizeFunc(aliasFlags) flags.Bool("sig-proxy", true, "Proxy received signals to the process") getCreateFlags(&runCommand.PodmanCommand) markFlagHiddenForRemoteClient("authfile", flags) - flags.MarkHidden("signature-policy") } func runCmd(c *cliconfig.RunValues) error { @@ -54,7 +54,7 @@ func runCmd(c *cliconfig.RunValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) exitCode, err = runtime.Run(getContext(), c, exitCode) return err diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go index 59cbc7aa4..db6d390d5 100644 --- a/cmd/podman/runlabel.go +++ b/cmd/podman/runlabel.go @@ -10,7 +10,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/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/util" "github.com/containers/libpod/utils" @@ -53,23 +53,23 @@ func init() { flags.StringVar(&runlabelCommand.Opt1, "opt1", "", "Optional parameter to pass for install") flags.StringVar(&runlabelCommand.Opt2, "opt2", "", "Optional parameter to pass for install") flags.StringVar(&runlabelCommand.Opt3, "opt3", "", "Optional parameter to pass for install") - flags.MarkHidden("opt1") - flags.MarkHidden("opt2") - flags.MarkHidden("opt3") - + markFlagHidden(flags, "opt1") + markFlagHidden(flags, "opt2") + markFlagHidden(flags, "opt3") flags.BoolP("pull", "p", false, "Pull the image if it does not exist locally prior to executing the label contents") flags.BoolVarP(&runlabelCommand.Quiet, "quiet", "q", false, "Suppress output information when installing images") // Disabled flags for the remote client if !remote { - flags.StringVar(&runlabelCommand.Authfile, "authfile", getAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&runlabelCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&runlabelCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&runlabelCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.BoolVar(&runlabelCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") - flags.MarkDeprecated("pull", "podman will pull if not found in local storage") - flags.MarkHidden("signature-policy") + if err := flags.MarkDeprecated("pull", "podman will pull if not found in local storage"); err != nil { + logrus.Error("unable to mark pull flag deprecated") + } + markFlagHidden(flags, "signature-policy") } - markFlagHiddenForRemoteClient("authfile", flags) } // installCmd gets the data from the command line and calls installImage @@ -95,7 +95,7 @@ func runlabelCmd(c *cliconfig.RunlabelValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) args := c.InputArgs if len(args) < 2 { @@ -169,7 +169,7 @@ func runlabelCmd(c *cliconfig.RunlabelValues) error { name := cmd[i+1] ctr, err := runtime.LookupContainer(name) if err != nil { - if errors.Cause(err) != libpod.ErrNoSuchCtr { + if errors.Cause(err) != define.ErrNoSuchCtr { logrus.Debugf("Error occurred searching for container %s: %s", name, err.Error()) return err } diff --git a/cmd/podman/save.go b/cmd/podman/save.go index 4d204337e..237ebde03 100644 --- a/cmd/podman/save.go +++ b/cmd/podman/save.go @@ -9,8 +9,8 @@ import ( "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "golang.org/x/crypto/ssh/terminal" ) const ( @@ -74,7 +74,7 @@ func saveCmd(c *cliconfig.SaveValues) error { if err != nil { return errors.Wrapf(err, "could not create runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if c.Flag("compress").Changed && (c.Format != ociManifestDir && c.Format != v2s2ManifestDir && c.Format == "") { return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'") @@ -82,7 +82,7 @@ func saveCmd(c *cliconfig.SaveValues) error { if len(c.Output) == 0 { fi := os.Stdout - if logrus.IsTerminal(fi) { + if terminal.IsTerminal(int(fi.Fd())) { return errors.Errorf("refusing to save to terminal. Use -o flag or redirect") } c.Output = "/dev/stdout" diff --git a/cmd/podman/search.go b/cmd/podman/search.go index ba04002f6..f4c51bff1 100644 --- a/cmd/podman/search.go +++ b/cmd/podman/search.go @@ -7,16 +7,12 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/pkg/errors" "github.com/spf13/cobra" ) -const ( - descriptionTruncLength = 44 - maxQueries = 25 -) - var ( searchCommand cliconfig.SearchValues searchDescription = `Search registries for a given image. Can search all the default registries or a specific registry. @@ -49,7 +45,7 @@ func init() { flags.BoolVar(&searchCommand.NoTrunc, "no-trunc", false, "Do not truncate the output") // Disabled flags for the remote client if !remote { - flags.StringVar(&searchCommand.Authfile, "authfile", getAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&searchCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } } @@ -88,8 +84,7 @@ func searchCmd(c *cliconfig.SearchValues) error { return nil } out := formats.StdoutTemplateArray{Output: searchToGeneric(results), Template: format, Fields: searchHeaderMap()} - formats.Writer(out).Out() - return nil + return out.Out() } // searchHeaderMap returns the headers of a SearchResult. diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index 55cc529e0..7f53f5ec9 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - v1 "k8s.io/api/core/v1" "os" "path/filepath" "regexp" @@ -16,16 +15,15 @@ import ( "github.com/containers/image/types" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" - "github.com/containers/libpod/pkg/inspect" - cc "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/util" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-units" "github.com/google/shlex" - "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" ) const ( @@ -34,7 +32,7 @@ const ( cmdTruncLength = 17 ) -// PsOptions describes the struct being formed for ps +// PsOptions describes the struct being formed for ps. type PsOptions struct { All bool Format string @@ -49,11 +47,11 @@ type PsOptions struct { Sync bool } -// BatchContainerStruct is the return obkect from BatchContainer and contains -// container related information +// BatchContainerStruct is the return object from BatchContainer and contains +// container related information. type BatchContainerStruct struct { ConConfig *libpod.ContainerConfig - ConState libpod.ContainerStatus + ConState define.ContainerStatus ExitCode int32 Exited bool Pid int @@ -63,7 +61,7 @@ type BatchContainerStruct struct { } // PsContainerOutput is the struct being returned from a parallel -// Batch operation +// batch operation. type PsContainerOutput struct { ID string Image string @@ -73,7 +71,7 @@ type PsContainerOutput struct { Names string IsInfra bool Status string - State libpod.ContainerStatus + State define.ContainerStatus Pid int Size *ContainerSize Pod string @@ -92,7 +90,7 @@ type PsContainerOutput struct { Mounts string } -// Namespace describes output for ps namespace +// Namespace describes output for ps namespace. type Namespace struct { PID string `json:"pid,omitempty"` Cgroup string `json:"cgroup,omitempty"` @@ -105,17 +103,17 @@ type Namespace struct { } // ContainerSize holds the size of the container's root filesystem and top -// read-write layer +// read-write layer. type ContainerSize struct { RootFsSize int64 `json:"rootFsSize"` RwSize int64 `json:"rwSize"` } // NewBatchContainer runs a batch process under one lock to get container information and only -// be called in PBatch +// be called in PBatch. func NewBatchContainer(ctr *libpod.Container, opts PsOptions) (PsContainerOutput, error) { var ( - conState libpod.ContainerStatus + conState define.ContainerStatus command string created string status string @@ -186,16 +184,16 @@ func NewBatchContainer(ctr *libpod.Container, opts PsOptions) (PsContainerOutput } switch conState.String() { - case libpod.ContainerStateExited.String(): + case define.ContainerStateExited.String(): fallthrough - case libpod.ContainerStateStopped.String(): + case define.ContainerStateStopped.String(): exitedSince := units.HumanDuration(time.Since(exitedAt)) status = fmt.Sprintf("Exited (%d) %s ago", exitCode, exitedSince) - case libpod.ContainerStateRunning.String(): + case define.ContainerStateRunning.String(): status = "Up " + units.HumanDuration(time.Since(startedAt)) + " ago" - case libpod.ContainerStatePaused.String(): + case define.ContainerStatePaused.String(): status = "Paused" - case libpod.ContainerStateCreated.String(), libpod.ContainerStateConfigured.String(): + case define.ContainerStateCreated.String(), define.ContainerStateConfigured.String(): status = "Created" default: status = "Error" @@ -259,15 +257,15 @@ type workerInput struct { job int } -// worker is a "threaded" worker that takes jobs from the channel "queue" +// worker is a "threaded" worker that takes jobs from the channel "queue". func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContainerOutput, errors chan<- error) { for j := range jobs { r, err := j.parallelFunc() - // If we find an error, we return just the error + // If we find an error, we return just the error. if err != nil { errors <- err } else { - // Return the result + // Return the result. results <- r } wg.Done() @@ -281,8 +279,8 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) return strings.Contains(c.ID(), filterValue) }, nil case "label": - var filterArray []string = strings.SplitN(filterValue, "=", 2) - var filterKey string = filterArray[0] + var filterArray = strings.SplitN(filterValue, "=", 2) + var filterKey = filterArray[0] if len(filterArray) > 1 { filterValue = filterArray[1] } else { @@ -298,7 +296,11 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) }, nil case "name": return func(c *libpod.Container) bool { - return strings.Contains(c.Name(), filterValue) + match, err := regexp.MatchString(filterValue, c.Name()) + if err != nil { + return false + } + return match }, nil case "exited": exitCode, err := strconv.ParseInt(filterValue, 10, 32) @@ -307,7 +309,7 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) } return func(c *libpod.Container) bool { ec, exited, err := c.ExitCode() - if ec == int32(exitCode) && err == nil && exited == true { + if ec == int32(exitCode) && err == nil && exited { return true } return false @@ -325,9 +327,9 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) filterValue = "exited" } state := status.String() - if status == libpod.ContainerStateConfigured { + if status == define.ContainerStateConfigured { state = "created" - } else if status == libpod.ContainerStateStopped { + } else if status == define.ContainerStateStopped { state = "exited" } return state == filterValue @@ -396,7 +398,7 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) return nil, errors.Errorf("%s is an invalid filter", filter) } -// GetPsContainerOutput returns a slice of containers specifically for ps output +// GetPsContainerOutput returns a slice of containers specifically for ps output. func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, maxWorkers int) ([]PsContainerOutput, error) { var ( filterFuncs []libpod.ContainerFilter @@ -417,21 +419,21 @@ func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, m } } if !opts.Latest { - // Get all containers + // Get all containers. containers, err := r.GetContainers(filterFuncs...) if err != nil { return nil, err } - // We only want the last few containers + // We only want the last few containers. if opts.Last > 0 && opts.Last <= len(containers) { return nil, errors.Errorf("--last not yet supported") } else { outputContainers = containers } } else { - // Get just the latest container - // Ignore filters + // Get just the latest container. + // Ignore filters. latestCtr, err := r.GetLatestContainer() if err != nil { return nil, err @@ -444,8 +446,8 @@ func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, m return pss, nil } -// PBatch is performs batch operations on a container in parallel. It spawns the number of workers -// relative to the the number of parallel operations desired. +// PBatch performs batch operations on a container in parallel. It spawns the +// number of workers relative to the number of parallel operations desired. func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsContainerOutput { var ( wg sync.WaitGroup @@ -453,7 +455,7 @@ func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsCon ) // If the number of containers in question is less than the number of - // proposed parallel operations, we shouldnt spawn so many workers + // proposed parallel operations, we shouldnt spawn so many workers. if workers > len(containers) { workers = len(containers) } @@ -462,12 +464,12 @@ func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsCon results := make(chan PsContainerOutput, len(containers)) batchErrors := make(chan error, len(containers)) - // Create the workers + // Create the workers. for w := 1; w <= workers; w++ { go worker(&wg, jobs, results, batchErrors) } - // Add jobs to the workers + // Add jobs to the workers. for i, j := range containers { j := j wg.Add(1) @@ -492,7 +494,7 @@ func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsCon // We sort out running vs non-running here to save lots of copying // later. if !opts.All && !opts.Latest && opts.Last < 1 { - if !res.IsInfra && res.State == libpod.ContainerStateRunning { + if !res.IsInfra && res.State == define.ContainerStateRunning { psResults = append(psResults, res) } } else { @@ -502,12 +504,12 @@ func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsCon return psResults } -// BatchContainer is used in ps to reduce performance hits by "batching" +// BatchContainerOp is used in ps to reduce performance hits by "batching" // locks. func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) { var ( conConfig *libpod.ContainerConfig - conState libpod.ContainerStatus + conState define.ContainerStatus err error exitCode int32 exited bool @@ -580,7 +582,7 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru }, nil } -// GetNamespaces returns a populated namespace struct +// GetNamespaces returns a populated namespace struct. func GetNamespaces(pid int) *Namespace { ctrPID := strconv.Itoa(pid) cgroup, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup")) @@ -611,9 +613,9 @@ func getNamespaceInfo(path string) (string, error) { return getStrFromSquareBrackets(val), nil } -// getStrFromSquareBrackets gets the string inside [] from a string +// getStrFromSquareBrackets gets the string inside [] from a string. func getStrFromSquareBrackets(cmd string) string { - reg, err := regexp.Compile(".*\\[|\\].*") + reg, err := regexp.Compile(`.*\[|\].*`) if err != nil { return "" } @@ -621,145 +623,6 @@ func getStrFromSquareBrackets(cmd string) string { return strings.Join(arr, ",") } -// GetCtrInspectInfo takes container inspect data and collects all its info into a ContainerData -// structure for inspection related methods -func GetCtrInspectInfo(config *libpod.ContainerConfig, ctrInspectData *inspect.ContainerInspectData, createArtifact *cc.CreateConfig) (*inspect.ContainerData, error) { - spec := config.Spec - - cpus, mems, period, quota, realtimePeriod, realtimeRuntime, shares := getCPUInfo(spec) - blkioWeight, blkioWeightDevice, blkioReadBps, blkioWriteBps, blkioReadIOPS, blkioeWriteIOPS := getBLKIOInfo(spec) - memKernel, memReservation, memSwap, memSwappiness, memDisableOOMKiller := getMemoryInfo(spec) - pidsLimit := getPidsInfo(spec) - cgroup := getCgroup(spec) - logConfig := inspect.LogConfig{ - config.LogDriver, - make(map[string]string), - } - - data := &inspect.ContainerData{ - ctrInspectData, - &inspect.HostConfig{ - ConsoleSize: spec.Process.ConsoleSize, - OomScoreAdj: spec.Process.OOMScoreAdj, - CPUShares: shares, - BlkioWeight: blkioWeight, - BlkioWeightDevice: blkioWeightDevice, - BlkioDeviceReadBps: blkioReadBps, - BlkioDeviceWriteBps: blkioWriteBps, - BlkioDeviceReadIOps: blkioReadIOPS, - BlkioDeviceWriteIOps: blkioeWriteIOPS, - CPUPeriod: period, - CPUQuota: quota, - CPURealtimePeriod: realtimePeriod, - CPURealtimeRuntime: realtimeRuntime, - CPUSetCPUs: cpus, - CPUSetMems: mems, - Devices: spec.Linux.Devices, - KernelMemory: memKernel, - MemoryReservation: memReservation, - MemorySwap: memSwap, - MemorySwappiness: memSwappiness, - OomKillDisable: memDisableOOMKiller, - PidsLimit: pidsLimit, - Privileged: config.Privileged, - ReadOnlyRootfs: spec.Root.Readonly, - ReadOnlyTmpfs: createArtifact.ReadOnlyTmpfs, - Runtime: config.OCIRuntime, - NetworkMode: string(createArtifact.NetMode), - IpcMode: string(createArtifact.IpcMode), - Cgroup: cgroup, - UTSMode: string(createArtifact.UtsMode), - UsernsMode: string(createArtifact.UsernsMode), - GroupAdd: spec.Process.User.AdditionalGids, - ContainerIDFile: createArtifact.CidFile, - AutoRemove: createArtifact.Rm, - CapAdd: createArtifact.CapAdd, - CapDrop: createArtifact.CapDrop, - DNS: createArtifact.DNSServers, - DNSOptions: createArtifact.DNSOpt, - DNSSearch: createArtifact.DNSSearch, - PidMode: string(createArtifact.PidMode), - CgroupParent: createArtifact.CgroupParent, - ShmSize: createArtifact.Resources.ShmSize, - Memory: createArtifact.Resources.Memory, - Ulimits: createArtifact.Resources.Ulimit, - SecurityOpt: createArtifact.SecurityOpts, - Tmpfs: createArtifact.Tmpfs, - LogConfig: &logConfig, - }, - &inspect.CtrConfig{ - Hostname: spec.Hostname, - User: spec.Process.User, - Env: spec.Process.Env, - Image: config.RootfsImageName, - WorkingDir: spec.Process.Cwd, - Labels: config.Labels, - Annotations: spec.Annotations, - Tty: spec.Process.Terminal, - OpenStdin: config.Stdin, - StopSignal: config.StopSignal, - Cmd: config.Spec.Process.Args, - Entrypoint: strings.Join(createArtifact.Entrypoint, " "), - Healthcheck: config.HealthCheckConfig, - }, - } - return data, nil -} - -func getCPUInfo(spec *specs.Spec) (string, string, *uint64, *int64, *uint64, *int64, *uint64) { - if spec.Linux.Resources == nil { - return "", "", nil, nil, nil, nil, nil - } - cpu := spec.Linux.Resources.CPU - if cpu == nil { - return "", "", nil, nil, nil, nil, nil - } - return cpu.Cpus, cpu.Mems, cpu.Period, cpu.Quota, cpu.RealtimePeriod, cpu.RealtimeRuntime, cpu.Shares -} - -func getBLKIOInfo(spec *specs.Spec) (*uint16, []specs.LinuxWeightDevice, []specs.LinuxThrottleDevice, []specs.LinuxThrottleDevice, []specs.LinuxThrottleDevice, []specs.LinuxThrottleDevice) { - if spec.Linux.Resources == nil { - return nil, nil, nil, nil, nil, nil - } - blkio := spec.Linux.Resources.BlockIO - if blkio == nil { - return nil, nil, nil, nil, nil, nil - } - return blkio.Weight, blkio.WeightDevice, blkio.ThrottleReadBpsDevice, blkio.ThrottleWriteBpsDevice, blkio.ThrottleReadIOPSDevice, blkio.ThrottleWriteIOPSDevice -} - -func getMemoryInfo(spec *specs.Spec) (*int64, *int64, *int64, *uint64, *bool) { - if spec.Linux.Resources == nil { - return nil, nil, nil, nil, nil - } - memory := spec.Linux.Resources.Memory - if memory == nil { - return nil, nil, nil, nil, nil - } - return memory.Kernel, memory.Reservation, memory.Swap, memory.Swappiness, memory.DisableOOMKiller -} - -func getPidsInfo(spec *specs.Spec) *int64 { - if spec.Linux.Resources == nil { - return nil - } - pids := spec.Linux.Resources.Pids - if pids == nil { - return nil - } - return &pids.Limit -} - -func getCgroup(spec *specs.Spec) string { - cgroup := "host" - for _, ns := range spec.Linux.Namespaces { - if ns.Type == specs.CgroupNamespace && ns.Path != "" { - cgroup = "container" - } - } - return cgroup -} - func comparePorts(i, j ocicni.PortMapping) bool { if i.ContainerPort != j.ContainerPort { return i.ContainerPort < j.ContainerPort @@ -776,8 +639,8 @@ func comparePorts(i, j ocicni.PortMapping) bool { return i.Protocol < j.Protocol } -// returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto> -// e.g 0.0.0.0:1000-1006->1000-1006/tcp +// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto> +// e.g 0.0.0.0:1000-1006->1000-1006/tcp. func formatGroup(key string, start, last int32) string { parts := strings.Split(key, "/") groupType := parts[0] @@ -797,7 +660,7 @@ func formatGroup(key string, start, last int32) string { } // portsToString converts the ports used to a string of the from "port1, port2" -// also groups continuous list of ports in readable format. +// and also groups continuous list of ports in readable format. func portsToString(ports []ocicni.PortMapping) string { type portGroup struct { first int32 @@ -812,7 +675,7 @@ func portsToString(ports []ocicni.PortMapping) string { return comparePorts(ports[i], ports[j]) }) - // portGroupMap is used for grouping continuous ports + // portGroupMap is used for grouping continuous ports. portGroupMap := make(map[string]*portGroup) var groupKeyList []string @@ -822,7 +685,7 @@ func portsToString(ports []ocicni.PortMapping) string { if hostIP == "" { hostIP = "0.0.0.0" } - // if hostPort and containerPort are not same, consider as individual port. + // If hostPort and containerPort are not same, consider as individual port. if v.ContainerPort != v.HostPort { portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) continue @@ -833,7 +696,7 @@ func portsToString(ports []ocicni.PortMapping) string { portgroup, ok := portGroupMap[portMapKey] if !ok { portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort} - // this list is required to travese portGroupMap + // This list is required to travese portGroupMap. groupKeyList = append(groupKeyList, portMapKey) continue } @@ -843,7 +706,7 @@ func portsToString(ports []ocicni.PortMapping) string { continue } } - // for each portMapKey, format group list and appned to output string + // For each portMapKey, format group list and appned to output string. for _, portKey := range groupKeyList { group := portGroupMap[portKey] portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last)) @@ -852,7 +715,7 @@ func portsToString(ports []ocicni.PortMapping) string { } // GetRunlabel is a helper function for runlabel; it gets the image if needed and begins the -// contruction of the runlabel output and environment variables +// construction of the runlabel output and environment variables. func GetRunlabel(label string, runlabelImage string, ctx context.Context, runtime *libpod.Runtime, pull bool, inputCreds string, dockerRegistryOptions image.DockerRegistryOptions, authfile string, signaturePolicyPath string, output io.Writer) (string, string, error) { var ( newImage *image.Image @@ -887,9 +750,9 @@ func GetRunlabel(label string, runlabelImage string, ctx context.Context, runtim return runLabel, imageName, err } -// GenerateRunlabelCommand generates the command that will eventually be execucted by podman +// GenerateRunlabelCommand generates the command that will eventually be execucted by podman. func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]string, extraArgs []string, globalOpts string) ([]string, []string, error) { - // If no name is provided, we use the image's basename instead + // If no name is provided, we use the image's basename instead. if name == "" { baseName, err := image.GetImageBaseName(imageName) if err != nil { @@ -897,7 +760,7 @@ func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]s } name = baseName } - // The user provided extra arguments that need to be tacked onto the label's command + // The user provided extra arguments that need to be tacked onto the label's command. if len(extraArgs) > 0 { runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(extraArgs, " ")) } @@ -919,7 +782,7 @@ func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]s case "OPT3": return envmap["OPT3"] case "PWD": - // I would prefer to use os.getenv but it appears PWD is not in the os env list + // I would prefer to use os.getenv but it appears PWD is not in the os env list. d, err := os.Getwd() if err != nil { logrus.Error("unable to determine current working directory") @@ -956,7 +819,7 @@ func GenerateKube(name string, service bool, r *libpod.Runtime) (*v1.Pod, *v1.Se servicePorts []v1.ServicePort serviceYAML v1.Service ) - // Get the container in question + // Get the container in question. container, err = r.LookupContainer(name) if err != nil { pod, err = r.LookupPod(name) @@ -966,7 +829,7 @@ func GenerateKube(name string, service bool, r *libpod.Runtime) (*v1.Pod, *v1.Se podYAML, servicePorts, err = pod.GenerateForKube() } else { if len(container.Dependencies()) > 0 { - return nil, nil, errors.Wrapf(libpod.ErrNotImplemented, "containers with dependencies") + return nil, nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies") } podYAML, err = container.GenerateForKube() } diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 7cf230605..9578eb17d 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/containers/libpod/pkg/errorhandling" "io" "os" "path/filepath" @@ -25,7 +26,6 @@ import ( "github.com/docker/docker/pkg/signal" "github.com/docker/go-connections/nat" "github.com/docker/go-units" - "github.com/google/shlex" "github.com/opencontainers/selinux/go-selinux/label" "github.com/opentracing/opentracing-go" "github.com/pkg/errors" @@ -56,15 +56,15 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. } if c.IsSet("cidfile") && os.Geteuid() == 0 { - cidFile, err = libpod.OpenExclusiveFile(c.String("cidfile")) + cidFile, err = util.OpenExclusiveFile(c.String("cidfile")) if err != nil && os.IsExist(err) { return nil, nil, errors.Errorf("container id file exists. Ensure another container is not using it or delete %s", c.String("cidfile")) } if err != nil { return nil, nil, errors.Errorf("error opening cidfile %s", c.String("cidfile")) } - defer cidFile.Close() - defer cidFile.Sync() + defer errorhandling.CloseQuiet(cidFile) + defer errorhandling.SyncQuiet(cidFile) } imageName := "" @@ -77,11 +77,14 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. writer = os.Stderr } - newImage, err := runtime.ImageRuntime().New(ctx, c.InputArgs[0], rtc.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, false, nil) + newImage, err := runtime.ImageRuntime().New(ctx, c.InputArgs[0], rtc.SignaturePolicyPath, GetAuthFile(""), writer, nil, image.SigningOptions{}, false, nil) if err != nil { return nil, nil, err } data, err = newImage.Inspect(ctx) + if err != nil { + return nil, nil, err + } names := newImage.Names() if len(names) > 0 { imageName = names[0] @@ -89,9 +92,8 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. imageName = newImage.ID() } - var healthCheckCommandInput string // if the user disabled the healthcheck with "none", we skip adding it - healthCheckCommandInput = c.String("healthcheck-command") + healthCheckCommandInput := c.String("healthcheck-command") // the user didnt disable the healthcheck but did pass in a healthcheck command // now we need to make a healthcheck from the commandline input @@ -113,6 +115,30 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. if err != nil { return nil, nil, errors.Wrapf(err, "unable to get healthcheck for %s", c.InputArgs[0]) } + + if healthCheck != nil { + hcCommand := healthCheck.Test + if len(hcCommand) < 1 || hcCommand[0] == "" || hcCommand[0] == "NONE" { + // disable health check + healthCheck = nil + } else { + // apply defaults if image doesn't override them + if healthCheck.Interval == 0 { + healthCheck.Interval = 30 * time.Second + } + if healthCheck.Timeout == 0 { + healthCheck.Timeout = 30 * time.Second + } + /* Docker default is 0s, so the following would be a no-op + if healthCheck.StartPeriod == 0 { + healthCheck.StartPeriod = 0 * time.Second + } + */ + if healthCheck.Retries == 0 { + healthCheck.Retries = 3 + } + } + } } } } @@ -191,7 +217,7 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string, runtime *l } else { con := strings.SplitN(opt, "=", 2) if len(con) != 2 { - return fmt.Errorf("Invalid --security-opt 1: %q", opt) + return fmt.Errorf("invalid --security-opt 1: %q", opt) } switch con[0] { @@ -202,7 +228,7 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string, runtime *l case "seccomp": config.SeccompProfilePath = con[1] default: - return fmt.Errorf("Invalid --security-opt 2: %q", opt) + return fmt.Errorf("invalid --security-opt 2: %q", opt) } } } @@ -256,13 +282,26 @@ func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[ if err != nil { return namespaces, err } + hasUserns := false + if podInfraID != "" { + podCtr, err := runtime.GetContainer(podInfraID) + if err != nil { + return namespaces, err + } + mappings, err := podCtr.IDMappings() + if err != nil { + return namespaces, err + } + hasUserns = len(mappings.UIDMap) > 0 + } + if (namespaces["pid"] == cc.Pod) || (!c.IsSet("pid") && pod.SharesPID()) { namespaces["pid"] = fmt.Sprintf("container:%s", podInfraID) } if (namespaces["net"] == cc.Pod) || (!c.IsSet("net") && !c.IsSet("network") && pod.SharesNet()) { namespaces["net"] = fmt.Sprintf("container:%s", podInfraID) } - if (namespaces["user"] == cc.Pod) || (!c.IsSet("user") && pod.SharesUser()) { + if hasUserns && (namespaces["user"] == cc.Pod) || (!c.IsSet("user") && pod.SharesUser()) { namespaces["user"] = fmt.Sprintf("container:%s", podInfraID) } if (namespaces["ipc"] == cc.Pod) || (!c.IsSet("ipc") && pod.SharesIPC()) { @@ -374,11 +413,12 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. namespaceNet = c.String("net") } namespaces = map[string]string{ - "pid": c.String("pid"), - "net": namespaceNet, - "ipc": c.String("ipc"), - "user": c.String("userns"), - "uts": c.String("uts"), + "cgroup": c.String("cgroupns"), + "pid": c.String("pid"), + "net": namespaceNet, + "ipc": c.String("ipc"), + "user": c.String("userns"), + "uts": c.String("uts"), } originalPodName := c.String("pod") @@ -436,6 +476,11 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. return nil, errors.Errorf("--uts %q is not valid", namespaces["uts"]) } + cgroupMode := ns.CgroupMode(namespaces["cgroup"]) + if !cgroupMode.Valid() { + return nil, errors.Errorf("--cgroup %q is not valid", namespaces["cgroup"]) + } + ipcMode := ns.IpcMode(namespaces["ipc"]) if !cc.Valid(string(ipcMode), ipcMode) { return nil, errors.Errorf("--ipc %q is not valid", ipcMode) @@ -479,6 +524,16 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. // ENVIRONMENT VARIABLES env := EnvVariablesFromData(data) + if c.Bool("env-host") { + for _, e := range os.Environ() { + pair := strings.SplitN(e, "=", 2) + if _, ok := env[pair[0]]; !ok { + if len(pair) > 1 { + env[pair[0]] = pair[1] + } + } + } + } if err := parse.ReadKVStrings(env, c.StringSlice("env-file"), c.StringArray("env")); err != nil { return nil, errors.Wrapf(err, "unable to process environment variables") } @@ -615,6 +670,8 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. ImageVolumeType: c.String("image-volume"), CapAdd: c.StringSlice("cap-add"), CapDrop: c.StringSlice("cap-drop"), + CidFile: c.String("cidfile"), + Cgroupns: c.String("cgroupns"), CgroupParent: c.String("cgroup-parent"), Command: command, Detach: c.Bool("detach"), @@ -650,6 +707,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. NetMode: netMode, UtsMode: utsMode, PidMode: pidMode, + CgroupMode: cgroupMode, Pod: podName, Privileged: c.Bool("privileged"), Publish: c.StringSlice("publish"), @@ -690,7 +748,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. StopTimeout: c.Uint("stop-timeout"), Sysctl: sysctl, Systemd: systemd, - Tmpfs: c.StringSlice("tmpfs"), + Tmpfs: c.StringArray("tmpfs"), Tty: tty, User: user, UsernsMode: usernsMode, @@ -730,14 +788,6 @@ func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateC if err != nil { return nil, err } - - createConfigJSON, err := json.Marshal(createConfig) - if err != nil { - return nil, err - } - if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil { - return nil, err - } return ctr, nil } @@ -775,9 +825,12 @@ func makeHealthCheckFromCli(c *GenericCLIResults) (*manifest.Schema2HealthConfig return nil, errors.New("Must define a healthcheck command for all healthchecks") } - cmd, err := shlex.Split(inCommand) + // first try to parse option value as JSON array of strings... + cmd := []string{} + err := json.Unmarshal([]byte(inCommand), &cmd) if err != nil { - return nil, errors.Wrap(err, "failed to parse healthcheck command") + // ...otherwise pass it to "/bin/sh -c" inside the container + cmd = []string{"CMD-SHELL", inCommand} } hc := manifest.Schema2HealthConfig{ Test: cmd, @@ -801,7 +854,7 @@ func makeHealthCheckFromCli(c *GenericCLIResults) (*manifest.Schema2HealthConfig if err != nil { return nil, errors.Wrapf(err, "invalid healthcheck-timeout %s", inTimeout) } - if timeoutDuration < time.Duration(time.Second*1) { + if timeoutDuration < time.Duration(1) { return nil, errors.New("healthcheck-timeout must be at least 1 second") } hc.Timeout = timeoutDuration @@ -811,7 +864,7 @@ func makeHealthCheckFromCli(c *GenericCLIResults) (*manifest.Schema2HealthConfig return nil, errors.Wrapf(err, "invalid healthcheck-start-period %s", inStartPeriod) } if startPeriodDuration < time.Duration(0) { - return nil, errors.New("healthcheck-start-period must be a 0 seconds or greater") + return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") } hc.StartPeriod = startPeriodDuration diff --git a/cmd/podman/shared/create_cli.go b/cmd/podman/shared/create_cli.go index 7f158b09a..08a40b206 100644 --- a/cmd/podman/shared/create_cli.go +++ b/cmd/podman/shared/create_cli.go @@ -5,9 +5,9 @@ import ( "strings" "github.com/containers/libpod/cmd/podman/shared/parse" + "github.com/containers/libpod/pkg/cgroups" cc "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/sysinfo" - "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -78,7 +78,7 @@ func addWarning(warnings []string, msg string) []string { func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) { warnings := []string{} - cgroup2, err := util.IsCgroup2UnifiedMode() + cgroup2, err := cgroups.IsCgroup2UnifiedMode() if err != nil || cgroup2 { return warnings, err } @@ -133,7 +133,7 @@ func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, e if config.Resources.KernelMemory > 0 && config.Resources.KernelMemory < linuxMinMemory { return warnings, fmt.Errorf("minimum kernel memory limit allowed is 4MB") } - if config.Resources.DisableOomKiller == true && !sysInfo.OomKillDisable { + if config.Resources.DisableOomKiller && !sysInfo.OomKillDisable { // only produce warnings if the setting wasn't to *disable* the OOM Kill; no point // warning the caller if they already wanted the feature to be off warnings = addWarning(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.") diff --git a/cmd/podman/shared/funcs.go b/cmd/podman/shared/funcs.go index c189cceeb..2ceb9cdcb 100644 --- a/cmd/podman/shared/funcs.go +++ b/cmd/podman/shared/funcs.go @@ -9,6 +9,21 @@ import ( "github.com/google/shlex" ) +func GetAuthFile(authfile string) string { + if authfile != "" { + return authfile + } + authfile = os.Getenv("REGISTRY_AUTH_FILE") + if authfile != "" { + return authfile + } + runtimeDir := os.Getenv("XDG_RUNTIME_DIR") + if runtimeDir != "" { + return filepath.Join(runtimeDir, "containers/auth.json") + } + return "" +} + func substituteCommand(cmd string) (string, error) { var ( newCommand string diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index a38e4d47a..4062ac48a 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -370,6 +370,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["blkio-weight-device"] = newCRStringSlice(c, "blkio-weight-device") m["cap-add"] = newCRStringSlice(c, "cap-add") m["cap-drop"] = newCRStringSlice(c, "cap-drop") + m["cgroupns"] = newCRString(c, "cgroupns") m["cgroup-parent"] = newCRString(c, "cgroup-parent") m["cidfile"] = newCRString(c, "cidfile") m["conmon-pidfile"] = newCRString(c, "conmon-pidfile") @@ -393,16 +394,17 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["dns-search"] = newCRStringSlice(c, "dns-search") m["entrypoint"] = newCRString(c, "entrypoint") m["env"] = newCRStringArray(c, "env") + m["env-host"] = newCRBool(c, "env-host") m["env-file"] = newCRStringSlice(c, "env-file") m["expose"] = newCRStringSlice(c, "expose") m["gidmap"] = newCRStringSlice(c, "gidmap") m["group-add"] = newCRStringSlice(c, "group-add") m["help"] = newCRBool(c, "help") - m["healthcheck-command"] = newCRString(c, "healthcheck-command") - m["healthcheck-interval"] = newCRString(c, "healthcheck-interval") - m["healthcheck-retries"] = newCRUint(c, "healthcheck-retries") - m["healthcheck-start-period"] = newCRString(c, "healthcheck-start-period") - m["healthcheck-timeout"] = newCRString(c, "healthcheck-timeout") + m["healthcheck-command"] = newCRString(c, "health-cmd") + m["healthcheck-interval"] = newCRString(c, "health-interval") + m["healthcheck-retries"] = newCRUint(c, "health-retries") + m["healthcheck-start-period"] = newCRString(c, "health-start-period") + m["healthcheck-timeout"] = newCRString(c, "health-timeout") m["hostname"] = newCRString(c, "hostname") m["http-proxy"] = newCRBool(c, "http-proxy") m["image-volume"] = newCRString(c, "image-volume") @@ -448,7 +450,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["subuidname"] = newCRString(c, "subuidname") m["sysctl"] = newCRStringSlice(c, "sysctl") m["systemd"] = newCRBool(c, "systemd") - m["tmpfs"] = newCRStringSlice(c, "tmpfs") + m["tmpfs"] = newCRStringArray(c, "tmpfs") m["tty"] = newCRBool(c, "tty") m["uidmap"] = newCRStringSlice(c, "uidmap") m["ulimit"] = newCRStringSlice(c, "ulimit") diff --git a/cmd/podman/shared/parse/parse.go b/cmd/podman/shared/parse/parse.go index 7bc2652cb..9fbc92fc3 100644 --- a/cmd/podman/shared/parse/parse.go +++ b/cmd/podman/shared/parse/parse.go @@ -7,6 +7,7 @@ import ( "bufio" "fmt" "net" + "net/url" "os" "regexp" "strings" @@ -112,9 +113,22 @@ func parseEnv(env map[string]string, line string) error { if len(data) > 1 { env[name] = data[1] } else { - // if only a pass-through variable is given, clean it up. - val, _ := os.LookupEnv(name) - env[name] = val + if strings.HasSuffix(name, "*") { + name = strings.TrimSuffix(name, "*") + for _, e := range os.Environ() { + part := strings.SplitN(e, "=", 2) + if len(part) < 2 { + continue + } + if strings.HasPrefix(part[0], name) { + env[part[0]] = part[1] + } + } + } else { + // if only a pass-through variable is given, clean it up. + val, _ := os.LookupEnv(name) + env[name] = val + } } return nil } @@ -149,3 +163,12 @@ func ValidateFileName(filename string) error { } return nil } + +// ValidURL checks a string urlStr is a url or not +func ValidURL(urlStr string) error { + _, err := url.ParseRequestURI(urlStr) + if err != nil { + return errors.Wrapf(err, "invalid url path: %q", urlStr) + } + return nil +} diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go index 3f4cb0312..ab6d1f144 100644 --- a/cmd/podman/shared/pod.go +++ b/cmd/podman/shared/pod.go @@ -4,6 +4,7 @@ import ( "strconv" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-connections/nat" "github.com/pkg/errors" @@ -29,7 +30,7 @@ func GetPodStatus(pod *libpod.Pod) (string, error) { return CreatePodStatusResults(ctrStatuses) } -func CreatePodStatusResults(ctrStatuses map[string]libpod.ContainerStatus) (string, error) { +func CreatePodStatusResults(ctrStatuses map[string]define.ContainerStatus) (string, error) { ctrNum := len(ctrStatuses) if ctrNum == 0 { return PodStateCreated, nil @@ -43,15 +44,15 @@ func CreatePodStatusResults(ctrStatuses map[string]libpod.ContainerStatus) (stri } for _, ctrStatus := range ctrStatuses { switch ctrStatus { - case libpod.ContainerStateExited: + case define.ContainerStateExited: fallthrough - case libpod.ContainerStateStopped: + case define.ContainerStateStopped: statuses[PodStateStopped]++ - case libpod.ContainerStateRunning: + case define.ContainerStateRunning: statuses[PodStateRunning]++ - case libpod.ContainerStatePaused: + case define.ContainerStatePaused: statuses[PodStatePaused]++ - case libpod.ContainerStateCreated, libpod.ContainerStateConfigured: + case define.ContainerStateCreated, define.ContainerStateConfigured: statuses[PodStateCreated]++ default: statuses[PodStateErrored]++ diff --git a/cmd/podman/sign.go b/cmd/podman/sign.go index 0c25eec62..1333cf441 100644 --- a/cmd/podman/sign.go +++ b/cmd/podman/sign.go @@ -60,7 +60,7 @@ func signCmd(c *cliconfig.SignValues) error { if err != nil { return errors.Wrapf(err, "could not create runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) signby := c.SignBy if signby == "" { diff --git a/cmd/podman/start.go b/cmd/podman/start.go index bd34010f2..737a6d9f1 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -2,7 +2,7 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/opentracing/opentracing-go" "github.com/pkg/errors" @@ -35,7 +35,7 @@ func init() { 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.StringVar(&startCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-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", false, "Proxy received signals to the process (default true if attaching, false otherwise)") @@ -62,14 +62,14 @@ func startCmd(c *cliconfig.StartValues) error { sigProxy := c.SigProxy || attach if sigProxy && !attach { - return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach") + return errors.Wrapf(define.ErrInvalidArg, "you cannot use sig-proxy without --attach") } runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) exitCode, err = runtime.Start(getContext(), c, sigProxy) return err } diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go index c2b2a688c..3accae1b6 100644 --- a/cmd/podman/stats.go +++ b/cmd/podman/stats.go @@ -12,6 +12,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -92,7 +93,7 @@ func statsCmd(c *cliconfig.StatsValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) times := -1 if c.NoStream { @@ -100,9 +101,8 @@ func statsCmd(c *cliconfig.StatsValues) error { } var ctrs []*libpod.Container - var containerFunc func() ([]*libpod.Container, error) - containerFunc = runtime.GetRunningContainers + containerFunc := runtime.GetRunningContainers if len(c.InputArgs) > 0 { containerFunc = func() ([]*libpod.Container, error) { return runtime.GetContainersByList(c.InputArgs) } } else if latest { @@ -127,7 +127,7 @@ func statsCmd(c *cliconfig.StatsValues) error { initialStats, err := ctr.GetContainerStats(&libpod.ContainerStats{}) if err != nil { // when doing "all", dont worry about containers that are not running - if c.All && errors.Cause(err) == libpod.ErrCtrRemoved || errors.Cause(err) == libpod.ErrNoSuchCtr || errors.Cause(err) == libpod.ErrCtrStateInvalid { + if c.All && errors.Cause(err) == define.ErrCtrRemoved || errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrStateInvalid { continue } return err @@ -148,7 +148,7 @@ func statsCmd(c *cliconfig.StatsValues) error { id := ctr.ID() if _, ok := containerStats[ctr.ID()]; !ok { initialStats, err := ctr.GetContainerStats(&libpod.ContainerStats{}) - if errors.Cause(err) == libpod.ErrCtrRemoved || errors.Cause(err) == libpod.ErrNoSuchCtr || errors.Cause(err) == libpod.ErrCtrStateInvalid { + if errors.Cause(err) == define.ErrCtrRemoved || errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrStateInvalid { // skip dealing with a container that is gone continue } @@ -158,7 +158,7 @@ func statsCmd(c *cliconfig.StatsValues) error { containerStats[id] = initialStats } stats, err := ctr.GetContainerStats(containerStats[id]) - if err != nil && errors.Cause(err) != libpod.ErrNoSuchCtr { + if err != nil && errors.Cause(err) != define.ErrNoSuchCtr { return err } // replace the previous measurement with the current one @@ -174,7 +174,9 @@ func statsCmd(c *cliconfig.StatsValues) error { tm.MoveCursor(1, 1) tm.Flush() } - outputStats(reportStats, format) + if err := outputStats(reportStats, format); err != nil { + return err + } time.Sleep(time.Second) } return nil @@ -198,7 +200,7 @@ func outputStats(stats []*libpod.ContainerStats, format string) error { } out = formats.StdoutTemplateArray{Output: statsToGeneric(outputStats, []statsOutputParams{}), Template: format, Fields: mapOfHeaders} } - return formats.Writer(out).Out() + return out.Out() } func genStatsFormat(format string) string { diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index d88c90deb..e04d8a12b 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -2,7 +2,7 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/opentracing/opentracing-go" "github.com/pkg/errors" @@ -40,8 +40,8 @@ func init() { 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") + flags.UintVar(&stopCommand.Timeout, "time", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") + flags.UintVarP(&stopCommand.Timeout, "timeout", "t", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") markFlagHiddenForRemoteClient("latest", flags) } @@ -60,7 +60,7 @@ func stopCmd(c *cliconfig.StopValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ok, failures, err := runtime.StopContainers(getContext(), c) if err != nil { diff --git a/cmd/podman/system_df.go b/cmd/podman/system_df.go index 840916547..6b9824a79 100644 --- a/cmd/podman/system_df.go +++ b/cmd/podman/system_df.go @@ -1,3 +1,5 @@ +//+build !remoteclient + package main import ( @@ -12,8 +14,9 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" - units "github.com/docker/go-units" + "github.com/docker/go-units" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -103,7 +106,7 @@ func dfSystemCmd(c *cliconfig.SystemDfValues) error { if err != nil { return errors.Wrapf(err, "Could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ctx := getContext() @@ -128,11 +131,10 @@ func dfSystemCmd(c *cliconfig.SystemDfValues) error { if c.Format != "" { format = strings.Replace(c.Format, `\t`, "\t", -1) } - generateSysDfOutput(systemDfDiskUsages, format) - return nil + return generateSysDfOutput(systemDfDiskUsages, format) } -func generateSysDfOutput(systemDfDiskUsages []systemDfDiskUsage, format string) { +func generateSysDfOutput(systemDfDiskUsages []systemDfDiskUsage, format string) error { var systemDfHeader = map[string]string{ "Type": "TYPE", "Total": "TOTAL", @@ -141,7 +143,7 @@ func generateSysDfOutput(systemDfDiskUsages []systemDfDiskUsage, format string) "Reclaimable": "RECLAIMABLE", } out := formats.StdoutTemplateArray{Output: systemDfDiskUsageToGeneric(systemDfDiskUsages), Template: format, Fields: systemDfHeader} - formats.Writer(out).Out() + return out.Out() } func getDiskUsage(ctx context.Context, runtime *libpod.Runtime, metaData dfMetaData) ([]systemDfDiskUsage, error) { @@ -358,7 +360,7 @@ func ctrIsActive(ctr *libpod.Container) (bool, error) { if err != nil { return false, err } - return state == libpod.ContainerStatePaused || state == libpod.ContainerStateRunning, nil + return state == define.ContainerStatePaused || state == define.ContainerStateRunning, nil } func activeContainers(containers []*libpod.Container) (map[string]*libpod.Container, error) { @@ -544,17 +546,18 @@ func imagesVerboseOutput(ctx context.Context, metaData dfMetaData) error { "Created": "CREATED", "Size": "SIZE", "SharedSize": "SHARED SIZE", - "UniqueSize": "UNQUE SIZE", + "UniqueSize": "UNIQUE SIZE", "Containers": "CONTAINERS", } imagesVerboseDiskUsage, err := getImageVerboseDiskUsage(ctx, metaData.images, metaData.imagesUsedbyCtrMap) if err != nil { return errors.Wrapf(err, "error getting verbose output of images") } - os.Stderr.WriteString("Images space usage:\n\n") + if _, err := os.Stderr.WriteString("Images space usage:\n\n"); err != nil { + return err + } out := formats.StdoutTemplateArray{Output: systemDfImageVerboseDiskUsageToGeneric(imagesVerboseDiskUsage), Template: imageVerboseFormat, Fields: imageVerboseHeader} - formats.Writer(out).Out() - return nil + return out.Out() } func containersVerboseOutput(ctx context.Context, metaData dfMetaData) error { @@ -572,10 +575,12 @@ func containersVerboseOutput(ctx context.Context, metaData dfMetaData) error { if err != nil { return errors.Wrapf(err, "error getting verbose output of containers") } - os.Stderr.WriteString("\nContainers space usage:\n\n") + if _, err := os.Stderr.WriteString("\nContainers space usage:\n\n"); err != nil { + return err + } out := formats.StdoutTemplateArray{Output: systemDfContainerVerboseDiskUsageToGeneric(containersVerboseDiskUsage), Template: containerVerboseFormat, Fields: containerVerboseHeader} - formats.Writer(out).Out() - return nil + return out.Out() + } func volumesVerboseOutput(ctx context.Context, metaData dfMetaData) error { @@ -586,12 +591,13 @@ func volumesVerboseOutput(ctx context.Context, metaData dfMetaData) error { } volumesVerboseDiskUsage, err := getVolumeVerboseDiskUsage(metaData.volumes, metaData.volumeUsedByContainerMap) if err != nil { - return errors.Wrapf(err, "error getting verbose ouput of volumes") + return errors.Wrapf(err, "error getting verbose output of volumes") + } + if _, err := os.Stderr.WriteString("\nLocal Volumes space usage:\n\n"); err != nil { + return err } - os.Stderr.WriteString("\nLocal Volumes space usage:\n\n") out := formats.StdoutTemplateArray{Output: systemDfVolumeVerboseDiskUsageToGeneric(volumesVerboseDiskUsage), Template: volumeVerboseFormat, Fields: volumeVerboseHeader} - formats.Writer(out).Out() - return nil + return out.Out() } func verboseOutput(ctx context.Context, metaData dfMetaData) error { diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go index d5b218cd8..b499d8dd2 100644 --- a/cmd/podman/system_prune.go +++ b/cmd/podman/system_prune.go @@ -76,27 +76,33 @@ Are you sure you want to continue? [y/N] `, volumeString) if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) - - rmWorkers := shared.Parallelize("rm") - ctx := getContext() - fmt.Println("Deleted Containers") - ok, failures, lasterr := runtime.Prune(ctx, rmWorkers, false) - printCmdResults(ok, failures) + defer runtime.DeferredShutdown(false) + // We must clean out pods first because if they may have infra containers fmt.Println("Deleted Pods") pruneValues := cliconfig.PodPruneValues{ PodmanCommand: c.PodmanCommand, Force: c.Force, } - ok, failures, err = runtime.PrunePods(ctx, &pruneValues) + ctx := getContext() + ok, failures, lasterr := runtime.PrunePods(ctx, &pruneValues) + + if err := printCmdResults(ok, failures); err != nil { + return err + } + + rmWorkers := shared.Parallelize("rm") + fmt.Println("Deleted Containers") + ok, failures, err = runtime.Prune(ctx, rmWorkers, false) if err != nil { if lasterr != nil { - logrus.Errorf("%q", lasterr) + logrus.Errorf("%q", err) } lasterr = err } - printCmdResults(ok, failures) + if err := printCmdResults(ok, failures); err != nil { + return err + } if c.Bool("volumes") { fmt.Println("Deleted Volumes") diff --git a/cmd/podman/tag.go b/cmd/podman/tag.go index 58f221e26..eb43d695c 100644 --- a/cmd/podman/tag.go +++ b/cmd/podman/tag.go @@ -42,7 +42,7 @@ func tagCmd(c *cliconfig.TagValues) error { if err != nil { return errors.Wrapf(err, "could not create runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) newImage, err := runtime.NewImageFromLocal(args[0]) if err != nil { diff --git a/cmd/podman/top.go b/cmd/podman/top.go index 8583eccb5..bfba90fc0 100644 --- a/cmd/podman/top.go +++ b/cmd/podman/top.go @@ -7,14 +7,14 @@ import ( "text/tabwriter" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" ) func getDescriptorString() string { - descriptors, err := libpod.GetContainerPidInformationDescriptors() + descriptors, err := util.GetContainerPidInformationDescriptors() if err == nil { return fmt.Sprintf(` Format Descriptors: @@ -57,7 +57,7 @@ func init() { flags := topCommand.Flags() flags.SetInterspersed(false) flags.BoolVar(&topCommand.ListDescriptors, "list-descriptors", false, "") - flags.MarkHidden("list-descriptors") + markFlagHidden(flags, "list-descriptors") flags.BoolVarP(&topCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") markFlagHiddenForRemoteClient("latest", flags) } @@ -67,7 +67,7 @@ func topCmd(c *cliconfig.TopValues) error { args := c.InputArgs if c.ListDescriptors { - descriptors, err := libpod.GetContainerPidInformationDescriptors() + descriptors, err := util.GetContainerPidInformationDescriptors() if err != nil { return err } @@ -83,7 +83,7 @@ func topCmd(c *cliconfig.TopValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) psOutput, err := runtime.Top(c) if err != nil { @@ -91,8 +91,9 @@ func topCmd(c *cliconfig.TopValues) error { } w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0) for _, proc := range psOutput { - fmt.Fprintln(w, proc) + if _, err := fmt.Fprintln(w, proc); err != nil { + return err + } } - w.Flush() - return nil + return w.Flush() } diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go index 6490c609d..904a0d375 100644 --- a/cmd/podman/tree.go +++ b/cmd/podman/tree.go @@ -55,7 +55,7 @@ func treeCmd(c *cliconfig.TreeValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) imageInfo, layerInfoMap, img, err := runtime.Tree(c) if err != nil { return err @@ -72,7 +72,11 @@ func printTree(imageInfo *image.InfoImage, layerInfoMap map[string]*image.LayerI fmt.Printf("Image ID: %s\n", imageInfo.ID[:12]) fmt.Printf("Tags:\t %s\n", imageInfo.Tags) fmt.Printf("Size:\t %v\n", units.HumanSizeWithPrecision(float64(*size), 4)) - fmt.Printf(fmt.Sprintf("Image Layers\n")) + if img.TopLayer() != "" { + fmt.Printf("Image Layers\n") + } else { + fmt.Printf("No Image Layers\n") + } if !whatRequires { // fill imageInfo with layers associated with image. @@ -103,7 +107,7 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr if !ok { return fmt.Errorf("lookup error: layerid %s, not found", layerID) } - fmt.Printf(prefix) + fmt.Print(prefix) //initialize intend with middleItem to reduce middleItem checks. intend := middleItem @@ -123,7 +127,7 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr } fmt.Printf("%sID: %s Size: %7v%s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags) for count, childID := range ll.ChildID { - if err := printImageChildren(layerMap, childID, prefix, (count == len(ll.ChildID)-1)); err != nil { + if err := printImageChildren(layerMap, childID, prefix, count == len(ll.ChildID)-1); err != nil { return err } } diff --git a/cmd/podman/trust_set_show.go b/cmd/podman/trust_set_show.go index b615f6266..7d2a5ddc3 100644 --- a/cmd/podman/trust_set_show.go +++ b/cmd/podman/trust_set_show.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/containers/buildah/pkg/formats" - "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod/image" @@ -57,7 +56,7 @@ func init() { showTrustCommand.SetUsageTemplate(UsageTemplate()) setFlags := setTrustCommand.Flags() setFlags.StringVar(&setTrustCommand.PolicyPath, "policypath", "", "") - setFlags.MarkHidden("policypath") + markFlagHidden(setFlags, "policypath") setFlags.StringSliceVarP(&setTrustCommand.PubKeysFile, "pubkeysfile", "f", []string{}, `Path of installed public key(s) to trust for TARGET. Absolute path to keys is added to policy.json. May used multiple times to define multiple public keys. @@ -68,9 +67,9 @@ File(s) must exist before using this command`) showFlags.BoolVarP(&showTrustCommand.Json, "json", "j", false, "Output as json") showFlags.StringVar(&showTrustCommand.PolicyPath, "policypath", "", "") showFlags.BoolVar(&showTrustCommand.Raw, "raw", false, "Output raw policy file") - showFlags.MarkHidden("policypath") + markFlagHidden(showFlags, "policypath") showFlags.StringVar(&showTrustCommand.RegistryPath, "registrypath", "", "") - showFlags.MarkHidden("registrypath") + markFlagHidden(showFlags, "registrypath") } func showTrustCmd(c *cliconfig.ShowTrustValues) error { @@ -119,7 +118,7 @@ func showTrustCmd(c *cliconfig.ShowTrustValues) error { } outjson = policyJSON out := formats.JSONStruct{Output: outjson} - return formats.Writer(out).Out() + return out.Out() } showOutputMap, err := getPolicyShowOutput(policyContentStruct, systemRegistriesDirPath) @@ -127,7 +126,7 @@ func showTrustCmd(c *cliconfig.ShowTrustValues) error { return errors.Wrapf(err, "could not show trust policies") } out := formats.StdoutTemplateArray{Output: showOutputMap, Template: "{{.Repo}}\t{{.Trusttype}}\t{{.GPGid}}\t{{.Sigstore}}"} - return formats.Writer(out).Out() + return out.Out() } func setTrustCmd(c *cliconfig.SetTrustValues) error { @@ -238,10 +237,6 @@ func isValidTrustType(t string) bool { return false } -func getDefaultPolicyPath() string { - return trust.DefaultPolicyPath(&types.SystemContext{}) -} - func getPolicyJSON(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, error) { registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) if err != nil { @@ -259,15 +254,12 @@ func getPolicyJSON(policyContentStruct trust.PolicyContent, systemRegistriesDirP policyJSON[repo]["type"] = repoval[0].Type policyJSON[repo]["transport"] = transname keyarr := []string{} - uids := []string{} for _, repoele := range repoval { if len(repoele.KeyPath) > 0 { keyarr = append(keyarr, repoele.KeyPath) - uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) } if len(repoele.KeyData) > 0 { - keyarr = append(keyarr, string(repoele.KeyData)) - uids = append(uids, trust.GetGPGIdFromKeyData(string(repoele.KeyData))...) + keyarr = append(keyarr, repoele.KeyData) } } policyJSON[repo]["keys"] = keyarr @@ -313,16 +305,17 @@ func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistri Repo: repo, Trusttype: repoval[0].Type, } - keyarr := []string{} + // TODO - keyarr is not used and I don't know its intent; commenting out for now for someone to fix later + //keyarr := []string{} uids := []string{} for _, repoele := range repoval { if len(repoele.KeyPath) > 0 { - keyarr = append(keyarr, repoele.KeyPath) + //keyarr = append(keyarr, repoele.KeyPath) uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) } if len(repoele.KeyData) > 0 { - keyarr = append(keyarr, string(repoele.KeyData)) - uids = append(uids, trust.GetGPGIdFromKeyData(string(repoele.KeyData))...) + //keyarr = append(keyarr, string(repoele.KeyData)) + uids = append(uids, trust.GetGPGIdFromKeyData(repoele.KeyData)...) } } tempTrustShowOutput.GPGid = strings.Join(uids, ", ") diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go index ddbd00bd5..c3d81d3a8 100644 --- a/cmd/podman/umount.go +++ b/cmd/podman/umount.go @@ -52,7 +52,7 @@ func umountCmd(c *cliconfig.UmountValues) error { if err != nil { return errors.Wrapf(err, "error creating runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ok, failures, err := runtime.UmountRootFilesystems(getContext(), c) if err != nil { diff --git a/cmd/podman/unpause.go b/cmd/podman/unpause.go index 2cd6846fe..382b64e97 100644 --- a/cmd/podman/unpause.go +++ b/cmd/podman/unpause.go @@ -1,11 +1,10 @@ package main import ( - "os" - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -38,7 +37,7 @@ func init() { } func unpauseCmd(c *cliconfig.UnpauseValues) error { - if os.Geteuid() != 0 { + if rootless.IsRootless() && !remoteclient { return errors.New("unpause is not supported for rootless containers") } @@ -46,7 +45,7 @@ func unpauseCmd(c *cliconfig.UnpauseValues) error { if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) args := c.InputArgs if len(args) < 1 && !c.All { @@ -54,7 +53,7 @@ func unpauseCmd(c *cliconfig.UnpauseValues) error { } ok, failures, err := runtime.UnpauseContainers(getContext(), c) if err != nil { - if errors.Cause(err) == libpod.ErrNoSuchCtr { + if errors.Cause(err) == define.ErrNoSuchCtr { if len(c.InputArgs) > 1 { exitCode = 125 } else { diff --git a/cmd/podman/unshare.go b/cmd/podman/unshare.go index 4a4e371db..31ce441f4 100644 --- a/cmd/podman/unshare.go +++ b/cmd/podman/unshare.go @@ -1,4 +1,4 @@ -// +build linux +// +build !remoteclient package main @@ -8,8 +8,7 @@ import ( "os/exec" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -41,10 +40,10 @@ func init() { flags.SetInterspersed(false) } -func unshareEnv(config *libpod.RuntimeConfig) []string { +func unshareEnv(graphroot, runroot string) []string { return append(os.Environ(), "_CONTAINERS_USERNS_CONFIGURED=done", - fmt.Sprintf("CONTAINERS_GRAPHROOT=%s", config.StorageConfig.GraphRoot), - fmt.Sprintf("CONTAINERS_RUNROOT=%s", config.StorageConfig.RunRoot)) + fmt.Sprintf("CONTAINERS_GRAPHROOT=%s", graphroot), + fmt.Sprintf("CONTAINERS_RUNROOT=%s", runroot)) } // unshareCmd execs whatever using the ID mappings that we want to use for ourselves @@ -63,7 +62,7 @@ func unshareCmd(c *cliconfig.PodmanCommand) error { c.InputArgs = []string{shell} } - runtime, err := libpodruntime.GetRuntime(getContext(), c) + runtime, err := adapter.GetRuntime(getContext(), c) if err != nil { return err } @@ -73,7 +72,7 @@ func unshareCmd(c *cliconfig.PodmanCommand) error { } cmd := exec.Command(c.InputArgs[0], c.InputArgs[1:]...) - cmd.Env = unshareEnv(runtimeConfig) + cmd.Env = unshareEnv(runtimeConfig.StorageConfig.GraphRoot, runtimeConfig.StorageConfig.RunRoot) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go index 986db469e..c0ddaba4e 100644 --- a/cmd/podman/utils.go +++ b/cmd/podman/utils.go @@ -3,27 +3,12 @@ package main import ( "fmt" "reflect" + "runtime/debug" + "github.com/sirupsen/logrus" "github.com/spf13/pflag" ) -// printParallelOutput takes the map of parallel worker results and outputs them -// to stdout -func printParallelOutput(m map[string]error, errCount int) error { - var lastError error - for cid, result := range m { - if result != nil { - if errCount > 1 { - fmt.Println(result.Error()) - } - lastError = result - continue - } - fmt.Println(cid) - } - return lastError -} - // print results from CLI command func printCmdResults(ok []string, failures map[string]error) error { for _, id := range ok { @@ -48,6 +33,33 @@ func printCmdResults(ok []string, failures map[string]error) error { // on the remote-client func markFlagHiddenForRemoteClient(flagName string, flags *pflag.FlagSet) { if remoteclient { - flags.MarkHidden(flagName) + if err := flags.MarkHidden(flagName); err != nil { + debug.PrintStack() + logrus.Errorf("unable to mark %s as hidden in the remote-client", flagName) + } + } +} + +// markFlagHidden is a helper function to log an error if marking +// a flag as hidden happens to fail +func markFlagHidden(flags *pflag.FlagSet, flag string) { + if err := flags.MarkHidden(flag); err != nil { + logrus.Errorf("unable to mark flag '%s' as hidden: %q", flag, err) + } +} + +func aliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName { + switch name { + case "healthcheck-command": + name = "health-cmd" + case "healthcheck-interval": + name = "health-interval" + case "healthcheck-retries": + name = "health-retries" + case "healthcheck-start-period": + name = "health-start-period" + case "healthcheck-timeout": + name = "health-timeout" } + return pflag.NormalizedName(name) } diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go index 698a30d84..92315cd6b 100644 --- a/cmd/podman/varlink.go +++ b/cmd/podman/varlink.go @@ -83,7 +83,7 @@ func varlinkCmd(c *cliconfig.VarlinkValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) var varlinkInterfaces = []*iopodman.VarlinkInterface{varlinkapi.New(&c.PodmanCommand, runtime)} // Register varlink service. The metadata can be retrieved with: diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index ed7b49c68..f5f3250f7 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -69,7 +69,8 @@ type Image ( containers: int, labels: [string]string, isParent: bool, - topLayer: string + topLayer: string, + readOnly: bool ) # ImageHistory describes the returned structure from ImageHistory. @@ -144,10 +145,11 @@ type PsOpts ( last: ?int, latest: ?bool, noTrunc: ?bool, - pod: ?bool, - quiet: ?bool, - sort: ?string, - sync: ?bool + pod: ?bool, + quiet: ?bool, + size: ?bool, + sort: ?string, + sync: ?bool ) type PsContainer ( @@ -207,7 +209,7 @@ type ContainerNameSpace ( ipc: string ) -# InfoDistribution describes the the host's distribution +# InfoDistribution describes the host's distribution type InfoDistribution ( distribution: string, version: string @@ -498,6 +500,25 @@ type DiffInfo( changeType: string ) +type ExecOpts( + # container name or id + name: string, + # Create pseudo tty + tty: bool, + # privileged access in container + privileged: bool, + # command to execute in container + cmd: []string, + # user to use in container + user: ?string, + # workdir to run command in container + workdir: ?string, + # slice of keyword=value environment variables + env: ?[]string, + # string of detach keys + detachKeys: ?string +) + # GetVersion returns version and build information of the podman service method GetVersion() -> ( version: string, @@ -671,7 +692,7 @@ method PauseContainer(name: string) -> (container: string) # See also [PauseContainer](#PauseContainer). method UnpauseContainer(name: string) -> (container: string) -# Attach takes the name or ID of a container and sets up a the ability to remotely attach to its console. The start +# Attach takes the name or ID of a container and sets up the ability to remotely attach to its console. The start # bool is whether you wish to start the container in question first. method Attach(name: string, detachKeys: string, start: bool) -> () @@ -744,7 +765,7 @@ method BuildImage(build: BuildInfo) -> (image: MoreResponse) # This function is not implemented yet. # method CreateImage() -> (notimplemented: NotImplemented) -# InspectImage takes the name or ID of an image and returns a string respresentation of data associated with the +# InspectImage takes the name or ID of an image and returns a string representation of data associated with the #image. You must serialize the string into JSON to use it further. An [ImageNotFound](#ImageNotFound) error will # be returned if the image cannot be found. method InspectImage(name: string) -> (image: string) @@ -802,15 +823,15 @@ method DeleteUnusedImages() -> (images: []string) # attributes: _CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, ONBUILD, STOPSIGNAL, USER, VOLUME, and WORKDIR_. To pause the # container while it is being committed, pass a _true_ bool for the pause argument. If the container cannot # be found by the ID or name provided, a (ContainerNotFound)[#ContainerNotFound] error will be returned; otherwise, -# the resulting image's ID will be returned as a string. -method Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) -> (image: string) +# the resulting image's ID will be returned as a string inside a MoreResponse. +method Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) -> (reply: MoreResponse) # ImportImage imports an image from a source (like tarball) into local storage. The image can have additional # descriptions added to it using the message and changes options. See also [ExportImage](ExportImage). method ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) -> (image: string) # ExportImage takes the name or ID of an image and exports it to a destination like a tarball. There is also -# a booleon option to force compression. It also takes in a string array of tags to be able to save multiple +# a boolean option to force compression. It also takes in a string array of tags to be able to save multiple # tags of the same image to a tarball (each tag should be of the form <image>:<tag>). Upon completion, the ID # of the image is returned. If the image cannot be found in local storage, an [ImageNotFound](#ImageNotFound) # error will be returned. See also [ImportImage](ImportImage). @@ -915,7 +936,7 @@ method ListPods() -> (pods: []ListPodData) # ~~~ method GetPod(name: string) -> (pod: ListPodData) -# InspectPod takes the name or ID of an image and returns a string respresentation of data associated with the +# InspectPod takes the name or ID of an image and returns a string representation of data associated with the # pod. You must serialize the string into JSON to use it further. A [PodNotFound](#PodNotFound) error will # be returned if the pod cannot be found. method InspectPod(name: string) -> (pod: string) @@ -1098,6 +1119,9 @@ method ContainerRestore(name: string, keep: bool, tcpEstablished: bool) -> (id: # ContainerRunlabel runs executes a command as described by a given container image label. method ContainerRunlabel(runlabel: Runlabel) -> () +# ExecContainer executes a command in the given container. +method ExecContainer(opts: ExecOpts) -> () + # ListContainerMounts gathers all the mounted container mount points and returns them as an array # of strings # #### Example diff --git a/cmd/podman/version.go b/cmd/podman/version.go index 52a518db8..314b2e266 100644 --- a/cmd/podman/version.go +++ b/cmd/podman/version.go @@ -10,7 +10,7 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -40,9 +40,9 @@ func init() { // versionCmd gets and prints version info for version command func versionCmd(c *cliconfig.VersionValues) error { - clientVersion, err := libpod.GetVersion() + clientVersion, err := define.GetVersion() if err != nil { - errors.Wrapf(err, "unable to determine version") + return errors.Wrapf(err, "unable to determine version") } versionOutputFormat := c.Format @@ -57,24 +57,28 @@ func versionCmd(c *cliconfig.VersionValues) error { default: out = formats.StdoutTemplate{Output: clientVersion, Template: versionOutputFormat} } - return formats.Writer(out).Out() + return out.Out() } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) defer w.Flush() if remote { - fmt.Fprintf(w, "Client:\n") + if _, err := fmt.Fprintf(w, "Client:\n"); err != nil { + return err + } } formatVersion(w, clientVersion) if remote { - fmt.Fprintf(w, "\nService:\n") + if _, err := fmt.Fprintf(w, "\nService:\n"); err != nil { + return err + } runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) serviceVersion, err := runtime.GetVersion() if err != nil { @@ -85,7 +89,7 @@ func versionCmd(c *cliconfig.VersionValues) error { return nil } -func formatVersion(writer io.Writer, version libpod.Version) { +func formatVersion(writer io.Writer, version define.Version) { fmt.Fprintf(writer, "Version:\t%s\n", version.Version) fmt.Fprintf(writer, "RemoteAPI Version:\t%d\n", version.RemoteAPIVersion) fmt.Fprintf(writer, "Go Version:\t%s\n", version.GoVersion) diff --git a/cmd/podman/volume_create.go b/cmd/podman/volume_create.go index 84f6bba94..0897ab705 100644 --- a/cmd/podman/volume_create.go +++ b/cmd/podman/volume_create.go @@ -46,7 +46,7 @@ func volumeCreateCmd(c *cliconfig.VolumeCreateValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) if len(c.InputArgs) > 1 { return errors.Errorf("too many arguments, create takes at most 1 argument") diff --git a/cmd/podman/volume_inspect.go b/cmd/podman/volume_inspect.go index e4b05f96a..1ebc5ce60 100644 --- a/cmd/podman/volume_inspect.go +++ b/cmd/podman/volume_inspect.go @@ -47,7 +47,7 @@ func volumeInspectCmd(c *cliconfig.VolumeInspectValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) vols, err := runtime.InspectVolumes(getContext(), c) if err != nil { diff --git a/cmd/podman/volume_ls.go b/cmd/podman/volume_ls.go index 581e595cb..eda5685cf 100644 --- a/cmd/podman/volume_ls.go +++ b/cmd/podman/volume_ls.go @@ -76,7 +76,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) opts := volumeLsOptions{ Quiet: c.Quiet, @@ -238,7 +238,7 @@ func generateVolLsOutput(volumes []*adapter.Volume, opts volumeLsOptions) error } out = formats.StdoutTemplateArray{Output: volLsToGeneric(lsOutput, []volumeLsJSONParams{}), Template: opts.Format, Fields: lsOutput[0].volHeaderMap()} } - return formats.Writer(out).Out() + return out.Out() } // generateVolumeFilterFuncs returns the true if the volume matches the filter set, otherwise it returns false. diff --git a/cmd/podman/volume_prune.go b/cmd/podman/volume_prune.go index 6dc9e2403..daea5a4d2 100644 --- a/cmd/podman/volume_prune.go +++ b/cmd/podman/volume_prune.go @@ -67,7 +67,7 @@ func volumePruneCmd(c *cliconfig.VolumePruneValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) // Prompt for confirmation if --force is not set if !c.Force { diff --git a/cmd/podman/volume_rm.go b/cmd/podman/volume_rm.go index 77137eb7a..0141d06da 100644 --- a/cmd/podman/volume_rm.go +++ b/cmd/podman/volume_rm.go @@ -51,7 +51,7 @@ func volumeRmCmd(c *cliconfig.VolumeRmValues) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) deletedVolumeNames, err := runtime.RemoveVolumes(getContext(), c) if err != nil { if len(deletedVolumeNames) > 0 { diff --git a/cmd/podman/wait.go b/cmd/podman/wait.go index 380e861ed..d6a707bb8 100644 --- a/cmd/podman/wait.go +++ b/cmd/podman/wait.go @@ -55,7 +55,7 @@ func waitCmd(c *cliconfig.WaitValues) error { if err != nil { return errors.Wrapf(err, "error creating runtime") } - defer runtime.Shutdown(false) + defer runtime.DeferredShutdown(false) ok, failures, err := runtime.WaitOnContainers(getContext(), c, interval) if err != nil { |