summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/auto-update.go99
-rw-r--r--cmd/podman/common/completion.go4
-rw-r--r--cmd/podman/common/create.go9
-rw-r--r--cmd/podman/common/specgen.go63
-rw-r--r--cmd/podman/containers/cp.go177
-rw-r--r--cmd/podman/containers/create.go3
-rw-r--r--cmd/podman/containers/logs.go14
-rw-r--r--cmd/podman/containers/restore.go6
-rw-r--r--cmd/podman/images/import.go19
-rw-r--r--cmd/podman/manifest/create.go5
-rw-r--r--cmd/podman/parse/net.go7
-rw-r--r--cmd/podman/pods/create.go13
-rw-r--r--cmd/podman/pods/ps.go5
-rw-r--r--cmd/podman/registry/config.go3
-rw-r--r--cmd/podman/root.go11
-rw-r--r--cmd/podman/system/migrate.go5
16 files changed, 376 insertions, 67 deletions
diff --git a/cmd/podman/auto-update.go b/cmd/podman/auto-update.go
index 99226790f..9622088d5 100644
--- a/cmd/podman/auto-update.go
+++ b/cmd/podman/auto-update.go
@@ -1,10 +1,15 @@
package main
import (
+ "encoding/json"
"fmt"
+ "os"
+ "strings"
"github.com/containers/common/pkg/auth"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/report"
+ "github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/errorhandling"
@@ -12,8 +17,13 @@ import (
"github.com/spf13/cobra"
)
+type cliAutoUpdateOptions struct {
+ entities.AutoUpdateOptions
+ format string
+}
+
var (
- autoUpdateOptions = entities.AutoUpdateOptions{}
+ autoUpdateOptions = cliAutoUpdateOptions{}
autoUpdateDescription = `Auto update containers according to their auto-update policy.
Auto-update policies are specified with the "io.containers.autoupdate" label.
@@ -42,6 +52,11 @@ func init() {
authfileFlagName := "authfile"
flags.StringVar(&autoUpdateOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path to the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
_ = autoUpdateCommand.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault)
+
+ flags.BoolVar(&autoUpdateOptions.DryRun, "dry-run", false, "Check for pending updates")
+
+ flags.StringVar(&autoUpdateOptions.format, "format", "", "Change the output format to JSON or a Go template")
+ _ = autoUpdateCommand.RegisterFlagCompletionFunc("format", common.AutocompleteFormat(autoUpdateOutput{}))
}
func autoUpdate(cmd *cobra.Command, args []string) error {
@@ -49,11 +64,83 @@ func autoUpdate(cmd *cobra.Command, args []string) error {
// Backwards compat. System tests expect this error string.
return errors.Errorf("`%s` takes no arguments", cmd.CommandPath())
}
- report, failures := registry.ContainerEngine().AutoUpdate(registry.GetContext(), autoUpdateOptions)
- if report != nil {
- for _, unit := range report.Units {
- fmt.Println(unit)
- }
+
+ allReports, failures := registry.ContainerEngine().AutoUpdate(registry.GetContext(), autoUpdateOptions.AutoUpdateOptions)
+ if allReports == nil {
+ return errorhandling.JoinErrors(failures)
+ }
+
+ if err := writeTemplate(allReports, autoUpdateOptions.format); err != nil {
+ failures = append(failures, err)
}
+
return errorhandling.JoinErrors(failures)
}
+
+type autoUpdateOutput struct {
+ Unit string
+ Container string
+ ContainerName string
+ ContainerID string
+ Image string
+ Policy string
+ Updated string
+}
+
+func reportsToOutput(allReports []*entities.AutoUpdateReport) []autoUpdateOutput {
+ output := make([]autoUpdateOutput, len(allReports))
+ for i, r := range allReports {
+ output[i] = autoUpdateOutput{
+ Unit: r.SystemdUnit,
+ Container: fmt.Sprintf("%s (%s)", r.ContainerID[:12], r.ContainerName),
+ ContainerName: r.ContainerName,
+ ContainerID: r.ContainerID,
+ Image: r.ImageName,
+ Policy: r.Policy,
+ Updated: r.Updated,
+ }
+ }
+ return output
+}
+
+func writeTemplate(allReports []*entities.AutoUpdateReport, inputFormat string) error {
+ var format string
+ var printHeader bool
+
+ output := reportsToOutput(allReports)
+ switch inputFormat {
+ case "":
+ rows := []string{"{{.Unit}}", "{{.Container}}", "{{.Image}}", "{{.Policy}}", "{{.Updated}}"}
+ format = "{{range . }}" + strings.Join(rows, "\t") + "\n{{end -}}"
+ printHeader = true
+ case "json":
+ prettyJSON, err := json.MarshalIndent(output, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(prettyJSON))
+ return nil
+ default:
+ format = "{{range . }}" + inputFormat + "\n{{end -}}"
+ }
+
+ tmpl, err := report.NewTemplate("auto-update").Parse(format)
+ if err != nil {
+ return err
+ }
+
+ w, err := report.NewWriterDefault(os.Stdout)
+ if err != nil {
+ return err
+ }
+ defer w.Flush()
+
+ if printHeader {
+ headers := report.Headers(autoUpdateOutput{}, nil)
+ if err := tmpl.Execute(w, headers); err != nil {
+ return err
+ }
+ }
+
+ return tmpl.Execute(w, output)
+}
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index 177d094aa..08b2f6235 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -46,7 +46,9 @@ func setupContainerEngine(cmd *cobra.Command) (entities.ContainerEngine, error)
return nil, err
}
if !registry.IsRemote() && rootless.IsRootless() {
- err := containerEngine.SetupRootless(registry.Context(), cmd)
+ _, noMoveProcess := cmd.Annotations[registry.NoMoveProcess]
+
+ err := containerEngine.SetupRootless(registry.Context(), noMoveProcess)
if err != nil {
return nil, err
}
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index 64d1956eb..96414add4 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -655,15 +655,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
)
_ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone)
- storageOptFlagName := "storage-opt"
- createFlags.StringSliceVar(
- &cf.StorageOpt,
- storageOptFlagName, []string{},
- "Storage driver options per container",
- )
- //FIXME: What should we suggest here? The flag is not in the man page.
- _ = cmd.RegisterFlagCompletionFunc(storageOptFlagName, completion.AutocompleteNone)
-
subgidnameFlagName := "subgidname"
createFlags.StringVar(
&cf.SubUIDName,
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 149869237..42f515ace 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -565,6 +565,14 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev})
}
+ for _, rule := range c.DeviceCGroupRule {
+ dev, err := parseLinuxResourcesDeviceAccess(rule)
+ if err != nil {
+ return err
+ }
+ s.DeviceCGroupRule = append(s.DeviceCGroupRule, dev)
+ }
+
s.Init = c.Init
s.InitPath = c.InitPath
s.Stdin = c.Interactive
@@ -899,3 +907,58 @@ func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error)
}
return mount, envs, nil
}
+
+var cgroupDeviceType = map[string]bool{
+ "a": true, // all
+ "b": true, // block device
+ "c": true, // character device
+}
+
+var cgroupDeviceAccess = map[string]bool{
+ "r": true, //read
+ "w": true, //write
+ "m": true, //mknod
+}
+
+// parseLinuxResourcesDeviceAccess parses the raw string passed with the --device-access-add flag
+func parseLinuxResourcesDeviceAccess(device string) (specs.LinuxDeviceCgroup, error) {
+ var devType, access string
+ var major, minor *int64
+
+ value := strings.Split(device, " ")
+ if len(value) != 3 {
+ return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device cgroup rule requires type, major:Minor, and access rules: %q", device)
+ }
+
+ devType = value[0]
+ if !cgroupDeviceType[devType] {
+ return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device type in device-access-add: %s", devType)
+ }
+
+ number := strings.SplitN(value[1], ":", 2)
+ i, err := strconv.ParseInt(number[0], 10, 64)
+ if err != nil {
+ return specs.LinuxDeviceCgroup{}, err
+ }
+ major = &i
+ if len(number) == 2 && number[1] != "*" {
+ i, err := strconv.ParseInt(number[1], 10, 64)
+ if err != nil {
+ return specs.LinuxDeviceCgroup{}, err
+ }
+ minor = &i
+ }
+ access = value[2]
+ for _, c := range strings.Split(access, "") {
+ if !cgroupDeviceAccess[c] {
+ return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device access in device-access-add: %s", c)
+ }
+ }
+ return specs.LinuxDeviceCgroup{
+ Allow: true,
+ Type: devType,
+ Major: major,
+ Minor: minor,
+ Access: access,
+ }, nil
+}
diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go
index c1f1e27f5..7b5846a35 100644
--- a/cmd/podman/containers/cp.go
+++ b/cmd/podman/containers/cp.go
@@ -82,7 +82,9 @@ func cp(cmd *cobra.Command, args []string) error {
return err
}
- if len(sourceContainerStr) > 0 {
+ if len(sourceContainerStr) > 0 && len(destContainerStr) > 0 {
+ return copyContainerToContainer(sourceContainerStr, sourcePath, destContainerStr, destPath)
+ } else if len(sourceContainerStr) > 0 {
return copyFromContainer(sourceContainerStr, sourcePath, destPath)
}
@@ -115,6 +117,84 @@ func doCopy(funcA func() error, funcB func() error) error {
return errorhandling.JoinErrors(copyErrors)
}
+func copyContainerToContainer(sourceContainer string, sourcePath string, destContainer string, destPath string) error {
+ if err := containerMustExist(sourceContainer); err != nil {
+ return err
+ }
+
+ if err := containerMustExist(destContainer); err != nil {
+ return err
+ }
+
+ sourceContainerInfo, err := registry.ContainerEngine().ContainerStat(registry.GetContext(), sourceContainer, sourcePath)
+ if err != nil {
+ return errors.Wrapf(err, "%q could not be found on container %s", sourcePath, sourceContainer)
+ }
+
+ destContainerBaseName, destContainerInfo, destResolvedToParentDir, err := resolvePathOnDestinationContainer(destContainer, destPath, false)
+ if err != nil {
+ return err
+ }
+
+ if sourceContainerInfo.IsDir && !destContainerInfo.IsDir {
+ return errors.New("destination must be a directory when copying a directory")
+ }
+
+ sourceContainerTarget := sourceContainerInfo.LinkTarget
+ destContainerTarget := destContainerInfo.LinkTarget
+ if !destContainerInfo.IsDir {
+ destContainerTarget = filepath.Dir(destPath)
+ }
+
+ // If we copy a directory via the "." notation and the container path
+ // does not exist, we need to make sure that the destination on the
+ // container gets created; otherwise the contents of the source
+ // directory will be written to the destination's parent directory.
+ //
+ // Hence, whenever "." is the source and the destination does not
+ // exist, we copy the source's parent and let the copier package create
+ // the destination via the Rename option.
+ if destResolvedToParentDir && sourceContainerInfo.IsDir && strings.HasSuffix(sourcePath, ".") {
+ sourceContainerTarget = filepath.Dir(sourceContainerTarget)
+ }
+
+ reader, writer := io.Pipe()
+
+ sourceContainerCopy := func() error {
+ defer writer.Close()
+ copyFunc, err := registry.ContainerEngine().ContainerCopyToArchive(registry.GetContext(), sourceContainer, sourceContainerTarget, writer)
+ if err != nil {
+ return err
+ }
+ if err := copyFunc(); err != nil {
+ return errors.Wrap(err, "error copying from container")
+ }
+ return nil
+ }
+
+ destContainerCopy := func() error {
+ defer reader.Close()
+
+ copyOptions := entities.CopyOptions{Chown: chown}
+ if (!sourceContainerInfo.IsDir && !destContainerInfo.IsDir) || destResolvedToParentDir {
+ // If we're having a file-to-file copy, make sure to
+ // rename accordingly.
+ copyOptions.Rename = map[string]string{filepath.Base(sourceContainerTarget): destContainerBaseName}
+ }
+
+ copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), destContainer, destContainerTarget, reader, copyOptions)
+ if err != nil {
+ return err
+ }
+ if err := copyFunc(); err != nil {
+ return errors.Wrap(err, "error copying to container")
+ }
+ return nil
+ }
+
+ return doCopy(sourceContainerCopy, destContainerCopy)
+}
+
// copyFromContainer copies from the containerPath on the container to hostPath.
func copyFromContainer(container string, containerPath string, hostPath string) error {
if err := containerMustExist(container); err != nil {
@@ -133,6 +213,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
}
var hostBaseName string
+ var resolvedToHostParentDir bool
hostInfo, hostInfoErr := copy.ResolveHostPath(hostPath)
if hostInfoErr != nil {
if strings.HasSuffix(hostPath, "/") {
@@ -148,6 +229,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
// it'll be created while copying. Hence, we use it as the
// base path.
hostBaseName = filepath.Base(hostPath)
+ resolvedToHostParentDir = true
} else {
// If the specified path exists on the host, we must use its
// base path as it may have changed due to symlink evaluations.
@@ -175,7 +257,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
// we copy the source's parent and let the copier package create the
// destination via the Rename option.
containerTarget := containerInfo.LinkTarget
- if hostInfoErr != nil && containerInfo.IsDir && strings.HasSuffix(containerTarget, ".") {
+ if resolvedToHostParentDir && containerInfo.IsDir && strings.HasSuffix(containerTarget, ".") {
containerTarget = filepath.Dir(containerTarget)
}
@@ -216,7 +298,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
ChownFiles: &idPair,
IgnoreDevices: true,
}
- if (!containerInfo.IsDir && !hostInfo.IsDir) || hostInfoErr != nil {
+ if (!containerInfo.IsDir && !hostInfo.IsDir) || resolvedToHostParentDir {
// If we're having a file-to-file copy, make sure to
// rename accordingly.
putOptions.Rename = map[string]string{filepath.Base(containerTarget): hostBaseName}
@@ -263,42 +345,9 @@ func copyToContainer(container string, containerPath string, hostPath string) er
return errors.Wrapf(err, "%q could not be found on the host", hostPath)
}
- // If the path on the container does not exist. We need to make sure
- // that it's parent directory exists. The destination may be created
- // while copying.
- var containerBaseName string
- containerInfo, containerInfoErr := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
- if containerInfoErr != nil {
- if strings.HasSuffix(containerPath, "/") {
- return errors.Wrapf(containerInfoErr, "%q could not be found on container %s", containerPath, container)
- }
- if isStdin {
- return errors.New("destination must be a directory when copying from stdin")
- }
- // NOTE: containerInfo may actually be set. That happens when
- // the container path is a symlink into nirvana. In that case,
- // we must use the symlinked path instead.
- path := containerPath
- if containerInfo != nil {
- containerBaseName = filepath.Base(containerInfo.LinkTarget)
- path = containerInfo.LinkTarget
- } else {
- containerBaseName = filepath.Base(containerPath)
- }
-
- parentDir, err := containerParentDir(container, path)
- if err != nil {
- return errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container)
- }
- containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, parentDir)
- if err != nil {
- return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
- }
- } else {
- // If the specified path exists on the container, we must use
- // its base path as it may have changed due to symlink
- // evaluations.
- containerBaseName = filepath.Base(containerInfo.LinkTarget)
+ containerBaseName, containerInfo, containerResolvedToParentDir, err := resolvePathOnDestinationContainer(container, containerPath, isStdin)
+ if err != nil {
+ return err
}
// If we copy a directory via the "." notation and the container path
@@ -310,7 +359,7 @@ func copyToContainer(container string, containerPath string, hostPath string) er
// exist, we copy the source's parent and let the copier package create
// the destination via the Rename option.
hostTarget := hostInfo.LinkTarget
- if containerInfoErr != nil && hostInfo.IsDir && strings.HasSuffix(hostTarget, ".") {
+ if containerResolvedToParentDir && hostInfo.IsDir && strings.HasSuffix(hostTarget, ".") {
hostTarget = filepath.Dir(hostTarget)
}
@@ -362,7 +411,7 @@ func copyToContainer(container string, containerPath string, hostPath string) er
// copy the base directory.
KeepDirectoryNames: hostInfo.IsDir && filepath.Base(hostTarget) != ".",
}
- if (!hostInfo.IsDir && !containerInfo.IsDir) || containerInfoErr != nil {
+ if (!hostInfo.IsDir && !containerInfo.IsDir) || containerResolvedToParentDir {
// If we're having a file-to-file copy, make sure to
// rename accordingly.
getOptions.Rename = map[string]string{filepath.Base(hostTarget): containerBaseName}
@@ -393,6 +442,52 @@ func copyToContainer(container string, containerPath string, hostPath string) er
return doCopy(hostCopy, containerCopy)
}
+// resolvePathOnDestinationContainer resolves the specified path on the
+// container. If the path does not exist, it attempts to use the parent
+// directory.
+func resolvePathOnDestinationContainer(container string, containerPath string, isStdin bool) (baseName string, containerInfo *entities.ContainerStatReport, resolvedToParentDir bool, err error) {
+ containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
+ if err == nil {
+ baseName = filepath.Base(containerInfo.LinkTarget)
+ return
+ }
+
+ if strings.HasSuffix(containerPath, "/") {
+ err = errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
+ return
+ }
+ if isStdin {
+ err = errors.New("destination must be a directory when copying from stdin")
+ return
+ }
+
+ // NOTE: containerInfo may actually be set. That happens when
+ // the container path is a symlink into nirvana. In that case,
+ // we must use the symlinked path instead.
+ path := containerPath
+ if containerInfo != nil {
+ baseName = filepath.Base(containerInfo.LinkTarget)
+ path = containerInfo.LinkTarget
+ } else {
+ baseName = filepath.Base(containerPath)
+ }
+
+ parentDir, err := containerParentDir(container, path)
+ if err != nil {
+ err = errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container)
+ return
+ }
+
+ containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, parentDir)
+ if err != nil {
+ err = errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
+ return
+ }
+
+ resolvedToParentDir = true
+ return baseName, containerInfo, resolvedToParentDir, nil
+}
+
// containerParentDir returns the parent directory of the specified path on the
// container. If the path is relative, it will be resolved relative to the
// container's working directory (or "/" if the work dir isn't set).
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index 1e11c53d5..c63c074f7 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -146,6 +146,8 @@ func replaceContainer(name string) error {
}
func createInit(c *cobra.Command) error {
+ cliVals.StorageOpt = registry.PodmanConfig().StorageOpts
+
if c.Flag("shm-size").Changed {
cliVals.ShmSize = c.Flag("shm-size").Value.String()
}
@@ -293,6 +295,7 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions
Hostname: s.ContainerBasicConfig.Hostname,
Cpus: cliVals.CPUS,
CpusetCpus: cliVals.CPUSetCPUs,
+ Pid: cliVals.PID,
}
// Unset config values we passed to the pod to prevent them being used twice for the container and pod.
s.ContainerBasicConfig.Hostname = ""
diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go
index 0d745291e..00a8d4b52 100644
--- a/cmd/podman/containers/logs.go
+++ b/cmd/podman/containers/logs.go
@@ -19,6 +19,8 @@ type logsOptionsWrapper struct {
entities.ContainerLogsOptions
SinceRaw string
+
+ UntilRaw string
}
var (
@@ -101,6 +103,10 @@ func logsFlags(cmd *cobra.Command) {
flags.StringVar(&logsOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP")
_ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone)
+ untilFlagName := "until"
+ flags.StringVar(&logsOptions.UntilRaw, untilFlagName, "", "Show logs until TIMESTAMP")
+ _ = cmd.RegisterFlagCompletionFunc(untilFlagName, completion.AutocompleteNone)
+
tailFlagName := "tail"
flags.Int64Var(&logsOptions.Tail, tailFlagName, -1, "Output the specified number of LINES at the end of the logs. Defaults to -1, which prints all lines")
_ = cmd.RegisterFlagCompletionFunc(tailFlagName, completion.AutocompleteNone)
@@ -120,6 +126,14 @@ func logs(_ *cobra.Command, args []string) error {
}
logsOptions.Since = since
}
+ if logsOptions.UntilRaw != "" {
+ // parse time, error out if something is wrong
+ until, err := util.ParseInputTime(logsOptions.UntilRaw)
+ if err != nil {
+ return errors.Wrapf(err, "error parsing --until %q", logsOptions.UntilRaw)
+ }
+ logsOptions.Until = until
+ }
logsOptions.StdoutWriter = os.Stdout
logsOptions.StderrWriter = os.Stderr
return registry.ContainerEngine().ContainerLogs(registry.GetContext(), args, logsOptions.ContainerLogsOptions)
diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go
index b908ea493..3b6f74efa 100644
--- a/cmd/podman/containers/restore.go
+++ b/cmd/podman/containers/restore.go
@@ -71,6 +71,9 @@ func init() {
)
_ = restoreCommand.RegisterFlagCompletionFunc("publish", completion.AutocompleteNone)
+ flags.StringVar(&restoreOptions.Pod, "pod", "", "Restore container into existing Pod (only works with --import)")
+ _ = restoreCommand.RegisterFlagCompletionFunc("pod", common.AutocompletePodsRunning)
+
validate.AddLatestFlag(restoreCommand, &restoreOptions.Latest)
}
@@ -91,6 +94,9 @@ func restore(cmd *cobra.Command, args []string) error {
if restoreOptions.Import == "" && restoreOptions.Name != "" {
return errors.Errorf("--name can only be used with --import")
}
+ if restoreOptions.Import == "" && restoreOptions.Pod != "" {
+ return errors.Errorf("--pod can only be used with --import")
+ }
if restoreOptions.Name != "" && restoreOptions.TCPEstablished {
return errors.Errorf("--tcp-established cannot be used with --name")
}
diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go
index bed2d4105..bc80417cc 100644
--- a/cmd/podman/images/import.go
+++ b/cmd/podman/images/import.go
@@ -3,6 +3,9 @@ package images
import (
"context"
"fmt"
+ "io"
+ "io/ioutil"
+ "os"
"strings"
"github.com/containers/common/pkg/completion"
@@ -97,6 +100,22 @@ func importCon(cmd *cobra.Command, args []string) error {
default:
return errors.Errorf("too many arguments. Usage TARBALL [REFERENCE]")
}
+
+ if source == "-" {
+ outFile, err := ioutil.TempFile("", "podman")
+ if err != nil {
+ return errors.Errorf("error creating file %v", err)
+ }
+ defer os.Remove(outFile.Name())
+ defer outFile.Close()
+
+ _, err = io.Copy(outFile, os.Stdin)
+ if err != nil {
+ return errors.Errorf("error copying file %v", err)
+ }
+ source = outFile.Name()
+ }
+
errFileName := parse.ValidateFileName(source)
errURL := parse.ValidURL(source)
if errURL == nil {
diff --git a/cmd/podman/manifest/create.go b/cmd/podman/manifest/create.go
index 9f7d74d14..95c9f89b0 100644
--- a/cmd/podman/manifest/create.go
+++ b/cmd/podman/manifest/create.go
@@ -13,15 +13,16 @@ import (
var (
manifestCreateOpts = entities.ManifestCreateOptions{}
createCmd = &cobra.Command{
- Use: "create [options] LIST [IMAGE]",
+ Use: "create [options] LIST [IMAGE...]",
Short: "Create manifest list or image index",
Long: "Creates manifest lists or image indexes.",
RunE: create,
ValidArgsFunction: common.AutocompleteImages,
Example: `podman manifest create mylist:v1.11
podman manifest create mylist:v1.11 arch-specific-image-to-add
+ podman manifest create mylist:v1.11 arch-specific-image-to-add another-arch-specific-image-to-add
podman manifest create --all mylist:v1.11 transport:tagged-image-to-add`,
- Args: cobra.RangeArgs(1, 2),
+ Args: cobra.MinimumNArgs(1),
}
)
diff --git a/cmd/podman/parse/net.go b/cmd/podman/parse/net.go
index f93c4ab1e..870690db3 100644
--- a/cmd/podman/parse/net.go
+++ b/cmd/podman/parse/net.go
@@ -180,9 +180,12 @@ func ValidateFileName(filename string) error {
// ValidURL checks a string urlStr is a url or not
func ValidURL(urlStr string) error {
- _, err := url.ParseRequestURI(urlStr)
+ url, err := url.ParseRequestURI(urlStr)
if err != nil {
- return errors.Wrapf(err, "invalid url path: %q", urlStr)
+ return errors.Wrapf(err, "invalid url %q", urlStr)
+ }
+ if url.Scheme == "" {
+ return errors.Errorf("invalid url %q: missing scheme", urlStr)
}
return nil
}
diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go
index 03e3ffaa0..abc47164b 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -86,6 +86,10 @@ func init() {
flags.String(infraCommandFlagName, containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started")
_ = createCommand.RegisterFlagCompletionFunc(infraCommandFlagName, completion.AutocompleteNone)
+ infraNameFlagName := "infra-name"
+ flags.StringVarP(&createOptions.InfraName, infraNameFlagName, "", "", "The name used as infra container name")
+ _ = createCommand.RegisterFlagCompletionFunc(infraNameFlagName, completion.AutocompleteNone)
+
labelFileFlagName := "label-file"
flags.StringSliceVar(&labelFile, labelFileFlagName, []string{}, "Read in a line delimited file of labels")
_ = createCommand.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault)
@@ -102,6 +106,10 @@ func init() {
flags.StringVarP(&createOptions.Hostname, hostnameFlagName, "", "", "Set a hostname to the pod")
_ = createCommand.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone)
+ pidFlagName := "pid"
+ flags.StringVar(&createOptions.Pid, pidFlagName, "", "PID namespace to use")
+ _ = createCommand.RegisterFlagCompletionFunc(pidFlagName, common.AutocompleteNamespace)
+
podIDFileFlagName := "pod-id-file"
flags.StringVar(&podIDFile, podIDFileFlagName, "", "Write the pod ID to the file")
_ = createCommand.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault)
@@ -144,6 +152,9 @@ func create(cmd *cobra.Command, args []string) error {
return errors.New("cannot set infra-image without an infra container")
}
createOptions.InfraImage = ""
+ if createOptions.InfraName != "" {
+ return errors.New("cannot set infra-name without an infra container")
+ }
if cmd.Flag("share").Changed && share != "none" && share != "" {
return fmt.Errorf("cannot set share(%s) namespaces without an infra container", cmd.Flag("share").Value)
@@ -179,6 +190,8 @@ func create(cmd *cobra.Command, args []string) error {
defer errorhandling.SyncQuiet(podIDFD)
}
+ createOptions.Pid = cmd.Flag("pid").Value.String()
+
createOptions.Net, err = common.NetFlagsToNetOptions(cmd, createOptions.Infra)
if err != nil {
return err
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index 3e5ab86f4..14e3e2ea9 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -124,6 +124,11 @@ func pods(cmd *cobra.Command, _ []string) error {
"NumberOfContainers": "# OF CONTAINERS",
"Created": "CREATED",
"InfraID": "INFRA ID",
+ "ContainerIds": "IDS",
+ "ContainerNames": "NAMES",
+ "ContainerStatuses": "STATUS",
+ "Cgroup": "CGROUP",
+ "Namespace": "NAMESPACES",
})
renderHeaders := true
row := podPsFormat()
diff --git a/cmd/podman/registry/config.go b/cmd/podman/registry/config.go
index 25139a3de..b512ba341 100644
--- a/cmd/podman/registry/config.go
+++ b/cmd/podman/registry/config.go
@@ -15,6 +15,9 @@ import (
)
const (
+ // NoMoveProcess used as cobra.Annotation when command doesn't need Podman to be moved to a separate cgroup
+ NoMoveProcess = "NoMoveProcess"
+
// ParentNSRequired used as cobra.Annotation when command requires root access
ParentNSRequired = "ParentNSRequired"
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 9e5d2a236..dc4ebb952 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -208,7 +208,8 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
// 3) command doesn't require Parent Namespace
_, found := cmd.Annotations[registry.ParentNSRequired]
if !registry.IsRemote() && rootless.IsRootless() && !found {
- err := registry.ContainerEngine().SetupRootless(registry.Context(), cmd)
+ _, noMoveProcess := cmd.Annotations[registry.NoMoveProcess]
+ err := registry.ContainerEngine().SetupRootless(registry.Context(), noMoveProcess)
if err != nil {
return err
}
@@ -342,10 +343,6 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
pFlags.StringVar(&opts.StorageDriver, storageDriverFlagName, "", "Select which storage driver is used to manage storage of images and containers (default is overlay)")
_ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone) //TODO: what can we recommend here?
- storageOptFlagName := "storage-opt"
- pFlags.StringArrayVar(&opts.StorageOpts, storageOptFlagName, []string{}, "Used to pass an option to the storage driver")
- _ = cmd.RegisterFlagCompletionFunc(storageOptFlagName, completion.AutocompleteNone)
-
tmpdirFlagName := "tmpdir"
pFlags.StringVar(&opts.Engine.TmpDir, tmpdirFlagName, "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n")
_ = cmd.RegisterFlagCompletionFunc(tmpdirFlagName, completion.AutocompleteDefault)
@@ -365,6 +362,10 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
}
}
}
+ storageOptFlagName := "storage-opt"
+ pFlags.StringArrayVar(&opts.StorageOpts, storageOptFlagName, []string{}, "Used to pass an option to the storage driver")
+ _ = cmd.RegisterFlagCompletionFunc(storageOptFlagName, completion.AutocompleteNone)
+
// Override default --help information of `--help` global flag
var dummyHelp bool
pFlags.BoolVar(&dummyHelp, "help", false, "Help for podman")
diff --git a/cmd/podman/system/migrate.go b/cmd/podman/system/migrate.go
index 9940cd063..b9dc272d7 100644
--- a/cmd/podman/system/migrate.go
+++ b/cmd/podman/system/migrate.go
@@ -22,7 +22,10 @@ var (
`
migrateCommand = &cobra.Command{
- Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
+ Annotations: map[string]string{
+ registry.EngineMode: registry.ABIMode,
+ registry.NoMoveProcess: registry.NoMoveProcess,
+ },
Use: "migrate [options]",
Args: validate.NoArgs,
Short: "Migrate containers",