summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/common/completion.go154
-rw-r--r--cmd/podman/common/completion_test.go2
-rw-r--r--cmd/podman/common/create.go50
-rw-r--r--cmd/podman/common/create_opts.go491
-rw-r--r--cmd/podman/common/default.go3
-rw-r--r--cmd/podman/containers/clone.go1
-rw-r--r--cmd/podman/containers/cp.go21
-rw-r--r--cmd/podman/containers/create.go26
-rw-r--r--cmd/podman/containers/run.go13
-rw-r--r--cmd/podman/containers/stats.go16
-rw-r--r--cmd/podman/images/build.go3
-rw-r--r--cmd/podman/images/load.go2
-rw-r--r--cmd/podman/images/push.go1
-rw-r--r--cmd/podman/inspect/inspect.go4
-rw-r--r--cmd/podman/machine/list.go9
-rw-r--r--cmd/podman/machine/start.go2
-rw-r--r--cmd/podman/parse/net.go23
-rw-r--r--cmd/podman/parse/net_test.go2
-rw-r--r--cmd/podman/pods/clone.go93
-rw-r--r--cmd/podman/pods/create.go1
-rw-r--r--cmd/podman/pods/ps.go4
-rw-r--r--cmd/podman/system/df.go2
-rw-r--r--cmd/podman/system/prune.go6
-rw-r--r--cmd/podman/system/reset.go14
-rw-r--r--cmd/podman/system/service_abi.go4
-rw-r--r--cmd/podman/utils/utils.go15
-rw-r--r--cmd/podman/validate/args.go2
-rw-r--r--cmd/rootlessport/main.go4
-rw-r--r--cmd/rootlessport/wsl_test.go2
29 files changed, 378 insertions, 592 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index 3720e9608..89e53c180 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"os"
+ "path"
"reflect"
"strconv"
"strings"
@@ -21,6 +22,7 @@ import (
"github.com/containers/podman/v4/pkg/signal"
systemdDefine "github.com/containers/podman/v4/pkg/systemd/define"
"github.com/containers/podman/v4/pkg/util"
+ securejoin "github.com/cyphar/filepath-securejoin"
"github.com/spf13/cobra"
)
@@ -69,7 +71,7 @@ func setupImageEngine(cmd *cobra.Command) (entities.ImageEngine, error) {
return nil, err
}
// we also need to set up the container engine since this
- // is required to setup the rootless namespace
+ // is required to set up the rootless namespace
if _, err = setupContainerEngine(cmd); err != nil {
return nil, err
}
@@ -282,6 +284,90 @@ func getNetworks(cmd *cobra.Command, toComplete string, cType completeType) ([]s
return suggestions, cobra.ShellCompDirectiveNoFileComp
}
+func fdIsNotDir(f *os.File) bool {
+ stat, err := f.Stat()
+ if err != nil {
+ cobra.CompErrorln(err.Error())
+ return true
+ }
+ return !stat.IsDir()
+}
+
+func getPathCompletion(root string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ if toComplete == "" {
+ toComplete = "/"
+ }
+ // Important: securejoin is required to make sure we never leave the root mount point
+ userpath, err := securejoin.SecureJoin(root, toComplete)
+ if err != nil {
+ cobra.CompErrorln(err.Error())
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+ var base string
+ f, err := os.Open(userpath)
+ // when error or file is not dir get the parent path to stat
+ if err != nil || fdIsNotDir(f) {
+ // Do not use path.Dir() since this cleans the paths which
+ // then no longer matches the user input.
+ userpath, base = path.Split(userpath)
+ toComplete, _ = path.Split(toComplete)
+ f, err = os.Open(userpath)
+ if err != nil {
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+ }
+
+ if fdIsNotDir(f) {
+ // nothing to complete since it is no dir
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+
+ entries, err := f.ReadDir(-1)
+ if err != nil {
+ cobra.CompErrorln(err.Error())
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+ if len(entries) == 0 {
+ // path is empty dir, just add the trailing slash and no space
+ if !strings.HasSuffix(toComplete, "/") {
+ toComplete += "/"
+ }
+ return []string{toComplete}, cobra.ShellCompDirectiveDefault | cobra.ShellCompDirectiveNoSpace
+ }
+ completions := make([]string, 0, len(entries))
+ count := 0
+ for _, e := range entries {
+ if strings.HasPrefix(e.Name(), base) {
+ suf := ""
+ // When the entry is an directory we add the "/" as suffix and do not want to add space
+ // to match normal shell completion behavior.
+ // Just inc counter again to fake more than one entry in this case and thus get no space.
+ if e.IsDir() {
+ suf = "/"
+ count++
+ }
+ completions = append(completions, simplePathJoinUnix(toComplete, e.Name()+suf))
+ count++
+ }
+ }
+ directive := cobra.ShellCompDirectiveDefault
+ if count > 1 {
+ // when we have more than one match we do not want to add a space after the completion
+ directive |= cobra.ShellCompDirectiveNoSpace
+ }
+ return completions, directive
+}
+
+// simplePathJoinUnix joins to path components by adding a slash only if p1 doesn't end with one.
+// We cannot use path.Join() for the completions logic because this one always calls Clean() on
+// the path which changes it from the input.
+func simplePathJoinUnix(p1, p2 string) string {
+ if p1[len(p1)-1] == '/' {
+ return p1 + p2
+ }
+ return p1 + "/" + p2
+}
+
// validCurrentCmdLine validates the current cmd line
// It utilizes the Args function from the cmd struct
// In most cases the Args function validates the args length but it
@@ -523,8 +609,32 @@ func AutocompleteCreateRun(cmd *cobra.Command, args []string, toComplete string)
}
return getImages(cmd, toComplete)
}
- // TODO: add path completion for files in the image
- return nil, cobra.ShellCompDirectiveDefault
+ // Mount the image and provide path completion
+ engine, err := setupImageEngine(cmd)
+ if err != nil {
+ cobra.CompErrorln(err.Error())
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+
+ resp, err := engine.Mount(registry.Context(), []string{args[0]}, entities.ImageMountOptions{})
+ if err != nil {
+ cobra.CompErrorln(err.Error())
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+ defer func() {
+ _, err := engine.Unmount(registry.Context(), []string{args[0]}, entities.ImageUnmountOptions{})
+ if err != nil {
+ cobra.CompErrorln(err.Error())
+ }
+ }()
+ if len(resp) != 1 {
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+
+ // So this uses ShellCompDirectiveDefault to also still provide normal shell
+ // completion in case no path matches. This is useful if someone tries to get
+ // completion for paths that are not available in the image, e.g. /proc/...
+ return getPathCompletion(resp[0].Path, toComplete)
}
// AutocompleteRegistries - Autocomplete registries.
@@ -572,14 +682,40 @@ func AutocompleteCpCommand(cmd *cobra.Command, args []string, toComplete string)
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) < 2 {
+ if i := strings.IndexByte(toComplete, ':'); i > -1 {
+ // Looks like the user already set the container.
+ // Lets mount it and provide path completion for files in the container.
+ engine, err := setupContainerEngine(cmd)
+ if err != nil {
+ cobra.CompErrorln(err.Error())
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+
+ resp, err := engine.ContainerMount(registry.Context(), []string{toComplete[:i]}, entities.ContainerMountOptions{})
+ if err != nil {
+ cobra.CompErrorln(err.Error())
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+ defer func() {
+ _, err := engine.ContainerUnmount(registry.Context(), []string{toComplete[:i]}, entities.ContainerUnmountOptions{})
+ if err != nil {
+ cobra.CompErrorln(err.Error())
+ }
+ }()
+ if len(resp) != 1 {
+ return nil, cobra.ShellCompDirectiveDefault
+ }
+ comps, directive := getPathCompletion(resp[0].Path, toComplete[i+1:])
+ return prefixSlice(toComplete[:i+1], comps), directive
+ }
+ // Suggest containers when they match the input otherwise normal shell completion is used
containers, _ := getContainers(cmd, toComplete, completeDefault)
for _, container := range containers {
- // TODO: Add path completion for inside the container if possible
if strings.HasPrefix(container, toComplete) {
- return containers, cobra.ShellCompDirectiveNoSpace
+ return suffixCompSlice(":", containers), cobra.ShellCompDirectiveNoSpace
}
}
- // else complete paths
+ // else complete paths on the host
return nil, cobra.ShellCompDirectiveDefault
}
// don't complete more than 2 args
@@ -1471,8 +1607,14 @@ func AutocompleteClone(cmd *cobra.Command, args []string, toComplete string) ([]
}
switch len(args) {
case 0:
+ if cmd.Parent().Name() == "pod" { // needs to be " pod " to exclude 'podman'
+ return getPods(cmd, toComplete, completeDefault)
+ }
return getContainers(cmd, toComplete, completeDefault)
case 2:
+ if cmd.Parent().Name() == "pod" {
+ return nil, cobra.ShellCompDirectiveNoFileComp
+ }
return getImages(cmd, toComplete)
}
return nil, cobra.ShellCompDirectiveNoFileComp
diff --git a/cmd/podman/common/completion_test.go b/cmd/podman/common/completion_test.go
index ae23b02e2..d8be48ad7 100644
--- a/cmd/podman/common/completion_test.go
+++ b/cmd/podman/common/completion_test.go
@@ -50,7 +50,7 @@ func (c *Car) Color() string {
}
// This is for reflect testing required.
-// nolint:unused
+//nolint:unused
func (c Car) internal() int {
return 0
}
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index d28becc8a..e25bdd241 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -12,7 +12,7 @@ import (
"github.com/spf13/cobra"
)
-const sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))"
+const sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))"
var containerConfig = registry.PodmanConfig()
@@ -98,7 +98,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
cgroupsFlagName := "cgroups"
createFlags.StringVar(
&cf.CgroupsMode,
- cgroupsFlagName, cgroupConfig(),
+ cgroupsFlagName, cf.CgroupsMode,
`control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`,
)
_ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode)
@@ -150,7 +150,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
envFlagName := "env"
createFlags.StringArrayP(
- envFlagName, "e", env(),
+ envFlagName, "e", Env(),
"Set environment variables in container",
)
_ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone)
@@ -255,9 +255,8 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
_ = cmd.RegisterFlagCompletionFunc(hostUserFlagName, completion.AutocompleteNone)
imageVolumeFlagName := "image-volume"
- createFlags.StringVar(
- &cf.ImageVolume,
- imageVolumeFlagName, DefaultImageVolume,
+ createFlags.String(
+ imageVolumeFlagName, cf.ImageVolume,
`Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`,
)
_ = cmd.RegisterFlagCompletionFunc(imageVolumeFlagName, AutocompleteImageVolume)
@@ -299,7 +298,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
logDriverFlagName := "log-driver"
createFlags.StringVar(
&cf.LogDriver,
- logDriverFlagName, LogDriver(),
+ logDriverFlagName, cf.LogDriver,
"Logging driver for the container",
)
_ = cmd.RegisterFlagCompletionFunc(logDriverFlagName, AutocompleteLogDriver)
@@ -390,7 +389,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
pullFlagName := "pull"
createFlags.StringVar(
&cf.Pull,
- pullFlagName, policy(),
+ pullFlagName, cf.Pull,
`Pull image before creating ("always"|"missing"|"never")`,
)
_ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption)
@@ -407,7 +406,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
createFlags.BoolVar(
&cf.ReadOnlyTmpFS,
- "read-only-tmpfs", true,
+ "read-only-tmpfs", cf.ReadOnlyTmpFS,
"When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp",
)
requiresFlagName := "requires"
@@ -440,7 +439,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
sdnotifyFlagName := "sdnotify"
createFlags.StringVar(
&cf.SdNotifyMode,
- sdnotifyFlagName, define.SdNotifyModeContainer,
+ sdnotifyFlagName, cf.SdNotifyMode,
`control sd-notify behavior ("container"|"conmon"|"ignore")`,
)
_ = cmd.RegisterFlagCompletionFunc(sdnotifyFlagName, AutocompleteSDNotify)
@@ -453,13 +452,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(secretFlagName, AutocompleteSecrets)
- shmSizeFlagName := "shm-size"
- createFlags.String(
- shmSizeFlagName, shmSize(),
- "Size of /dev/shm "+sizeWithUnitFormat,
- )
- _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone)
-
stopSignalFlagName := "stop-signal"
createFlags.StringVar(
&cf.StopSignal,
@@ -471,7 +463,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
stopTimeoutFlagName := "stop-timeout"
createFlags.UintVar(
&cf.StopTimeout,
- stopTimeoutFlagName, containerConfig.Engine.StopTimeout,
+ stopTimeoutFlagName, cf.StopTimeout,
"Timeout (in seconds) that containers stopped by user command have to exit. If exceeded, the container will be forcibly stopped via SIGKILL.",
)
_ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone)
@@ -479,7 +471,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
systemdFlagName := "systemd"
createFlags.StringVar(
&cf.Systemd,
- systemdFlagName, "true",
+ systemdFlagName, cf.Systemd,
`Run container in systemd mode ("true"|"false"|"always")`,
)
_ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag)
@@ -523,7 +515,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
timezoneFlagName := "tz"
createFlags.StringVar(
&cf.Timezone,
- timezoneFlagName, containerConfig.TZ(),
+ timezoneFlagName, cf.Timezone,
"Set timezone in container",
)
_ = cmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteNone) //TODO: add timezone completion
@@ -531,7 +523,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
umaskFlagName := "umask"
createFlags.StringVar(
&cf.Umask,
- umaskFlagName, containerConfig.Umask(),
+ umaskFlagName, cf.Umask,
"Set umask in container",
)
_ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone)
@@ -539,7 +531,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
ulimitFlagName := "ulimit"
createFlags.StringSliceVar(
&cf.Ulimit,
- ulimitFlagName, ulimits(),
+ ulimitFlagName, cf.Ulimit,
"Ulimit options",
)
_ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone)
@@ -578,7 +570,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
seccompPolicyFlagName := "seccomp-policy"
createFlags.StringVar(
&cf.SeccompPolicy,
- seccompPolicyFlagName, "default",
+ seccompPolicyFlagName, cf.SeccompPolicy,
"Policy for selecting a seccomp profile (experimental)",
)
_ = cmd.RegisterFlagCompletionFunc(seccompPolicyFlagName, completion.AutocompleteDefault)
@@ -629,6 +621,13 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
}
if isInfra || (!clone && !isInfra) { // infra container flags, create should also pick these up
+ shmSizeFlagName := "shm-size"
+ createFlags.String(
+ shmSizeFlagName, shmSize(),
+ "Size of /dev/shm "+sizeWithUnitFormat,
+ )
+ _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone)
+
sysctlFlagName := "sysctl"
createFlags.StringSliceVar(
&cf.Sysctl,
@@ -770,7 +769,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
volumeFlagName := "volume"
createFlags.StringArrayVarP(
&cf.Volume,
- volumeFlagName, "v", volumes(),
+ volumeFlagName, "v", cf.Volume,
volumeDesciption,
)
_ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag)
@@ -891,12 +890,13 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
memorySwappinessFlagName := "memory-swappiness"
createFlags.Int64Var(
&cf.MemorySwappiness,
- memorySwappinessFlagName, -1,
+ memorySwappinessFlagName, cf.MemorySwappiness,
"Tune container memory swappiness (0 to 100, or -1 for system default)",
)
_ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone)
}
// anyone can use these
+
cpusFlagName := "cpus"
createFlags.Float64Var(
&cf.CPUS,
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index c40d1ea51..fb5af8f59 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -1,472 +1,11 @@
package common
import (
- "fmt"
- "net"
- "os"
- "path/filepath"
- "strconv"
- "strings"
-
- "github.com/containers/common/libnetwork/types"
- "github.com/containers/common/pkg/cgroups"
- "github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/libpod/define"
- "github.com/containers/podman/v4/pkg/api/handlers"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/containers/podman/v4/pkg/rootless"
- "github.com/containers/podman/v4/pkg/specgen"
- "github.com/docker/docker/api/types/mount"
- "github.com/pkg/errors"
)
-func stringMaptoArray(m map[string]string) []string {
- a := make([]string, 0, len(m))
- for k, v := range m {
- a = append(a, fmt.Sprintf("%s=%s", k, v))
- }
- return a
-}
-
-// ContainerCreateToContainerCLIOpts converts a compat input struct to cliopts so it can be converted to
-// a specgen spec.
-func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.ContainerCreateOptions, []string, error) {
- var (
- capAdd []string
- cappDrop []string
- entrypoint *string
- init bool
- specPorts []types.PortMapping
- )
-
- if cc.HostConfig.Init != nil {
- init = *cc.HostConfig.Init
- }
-
- // Iterate devices and convert back to string
- devices := make([]string, 0, len(cc.HostConfig.Devices))
- for _, dev := range cc.HostConfig.Devices {
- devices = append(devices, fmt.Sprintf("%s:%s:%s", dev.PathOnHost, dev.PathInContainer, dev.CgroupPermissions))
- }
-
- // iterate blkreaddevicebps
- readBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadBps))
- for _, dev := range cc.HostConfig.BlkioDeviceReadBps {
- readBps = append(readBps, dev.String())
- }
-
- // iterate blkreaddeviceiops
- readIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadIOps))
- for _, dev := range cc.HostConfig.BlkioDeviceReadIOps {
- readIops = append(readIops, dev.String())
- }
-
- // iterate blkwritedevicebps
- writeBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteBps))
- for _, dev := range cc.HostConfig.BlkioDeviceWriteBps {
- writeBps = append(writeBps, dev.String())
- }
-
- // iterate blkwritedeviceiops
- writeIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteIOps))
- for _, dev := range cc.HostConfig.BlkioDeviceWriteIOps {
- writeIops = append(writeIops, dev.String())
- }
-
- // entrypoint
- // can be a string or slice. if it is a slice, we need to
- // marshall it to json; otherwise it should just be the string
- // value
- if len(cc.Config.Entrypoint) > 0 {
- entrypoint = &cc.Config.Entrypoint[0]
- if len(cc.Config.Entrypoint) > 1 {
- b, err := json.Marshal(cc.Config.Entrypoint)
- if err != nil {
- return nil, nil, err
- }
- var jsonString = string(b)
- entrypoint = &jsonString
- }
- }
-
- // expose ports
- expose := make([]string, 0, len(cc.Config.ExposedPorts))
- for p := range cc.Config.ExposedPorts {
- expose = append(expose, fmt.Sprintf("%s/%s", p.Port(), p.Proto()))
- }
-
- // mounts type=tmpfs/bind,source=...,target=...=,opt=val
- volSources := make(map[string]bool)
- volDestinations := make(map[string]bool)
- mounts := make([]string, 0, len(cc.HostConfig.Mounts))
- var builder strings.Builder
- for _, m := range cc.HostConfig.Mounts {
- addField(&builder, "type", string(m.Type))
- addField(&builder, "source", m.Source)
- addField(&builder, "target", m.Target)
-
- // Store source/dest so we don't add duplicates if a volume is
- // also mentioned in cc.Volumes.
- // Which Docker Compose v2.0 does, for unclear reasons...
- volSources[m.Source] = true
- volDestinations[m.Target] = true
-
- if m.ReadOnly {
- addField(&builder, "ro", "true")
- }
- addField(&builder, "consistency", string(m.Consistency))
- // Map any specialized mount options that intersect between *Options and cli options
- switch m.Type {
- case mount.TypeBind:
- if m.BindOptions != nil {
- addField(&builder, "bind-propagation", string(m.BindOptions.Propagation))
- addField(&builder, "bind-nonrecursive", strconv.FormatBool(m.BindOptions.NonRecursive))
- }
- case mount.TypeTmpfs:
- if m.TmpfsOptions != nil {
- addField(&builder, "tmpfs-size", strconv.FormatInt(m.TmpfsOptions.SizeBytes, 10))
- addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 8))
- }
- case mount.TypeVolume:
- // All current VolumeOpts are handled above
- // See vendor/github.com/containers/common/pkg/parse/parse.go:ValidateVolumeOpts()
- }
- mounts = append(mounts, builder.String())
- builder.Reset()
- }
-
- // dns
- dns := make([]net.IP, 0, len(cc.HostConfig.DNS))
- for _, d := range cc.HostConfig.DNS {
- dns = append(dns, net.ParseIP(d))
- }
-
- // publish
- for port, pbs := range cc.HostConfig.PortBindings {
- for _, pb := range pbs {
- var hostport int
- var err error
- if pb.HostPort != "" {
- hostport, err = strconv.Atoi(pb.HostPort)
- }
- if err != nil {
- return nil, nil, err
- }
- tmpPort := types.PortMapping{
- HostIP: pb.HostIP,
- ContainerPort: uint16(port.Int()),
- HostPort: uint16(hostport),
- Range: 0,
- Protocol: port.Proto(),
- }
- specPorts = append(specPorts, tmpPort)
- }
- }
-
- // special case for NetworkMode, the podman default is slirp4netns for
- // rootless but for better docker compat we want bridge.
- netmode := string(cc.HostConfig.NetworkMode)
- if netmode == "" || netmode == "default" {
- netmode = "bridge"
- }
- nsmode, networks, netOpts, err := specgen.ParseNetworkFlag([]string{netmode})
- if err != nil {
- return nil, nil, err
- }
-
- // network
- // Note: we cannot emulate compat exactly here. we only allow specifics of networks to be
- // defined when there is only one network.
- netInfo := entities.NetOptions{
- AddHosts: cc.HostConfig.ExtraHosts,
- DNSOptions: cc.HostConfig.DNSOptions,
- DNSSearch: cc.HostConfig.DNSSearch,
- DNSServers: dns,
- Network: nsmode,
- PublishPorts: specPorts,
- NetworkOptions: netOpts,
- NoHosts: rtc.Containers.NoHosts,
- }
-
- // network names
- switch {
- case len(cc.NetworkingConfig.EndpointsConfig) > 0:
- endpointsConfig := cc.NetworkingConfig.EndpointsConfig
- networks := make(map[string]types.PerNetworkOptions, len(endpointsConfig))
- for netName, endpoint := range endpointsConfig {
- netOpts := types.PerNetworkOptions{}
- if endpoint != nil {
- netOpts.Aliases = endpoint.Aliases
-
- // if IP address is provided
- if len(endpoint.IPAddress) > 0 {
- staticIP := net.ParseIP(endpoint.IPAddress)
- if staticIP == nil {
- return nil, nil, errors.Errorf("failed to parse the ip address %q", endpoint.IPAddress)
- }
- netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
- }
-
- if endpoint.IPAMConfig != nil {
- // if IPAMConfig.IPv4Address is provided
- if len(endpoint.IPAMConfig.IPv4Address) > 0 {
- staticIP := net.ParseIP(endpoint.IPAMConfig.IPv4Address)
- if staticIP == nil {
- return nil, nil, errors.Errorf("failed to parse the ipv4 address %q", endpoint.IPAMConfig.IPv4Address)
- }
- netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
- }
- // if IPAMConfig.IPv6Address is provided
- if len(endpoint.IPAMConfig.IPv6Address) > 0 {
- staticIP := net.ParseIP(endpoint.IPAMConfig.IPv6Address)
- if staticIP == nil {
- return nil, nil, errors.Errorf("failed to parse the ipv6 address %q", endpoint.IPAMConfig.IPv6Address)
- }
- netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
- }
- }
- // If MAC address is provided
- if len(endpoint.MacAddress) > 0 {
- staticMac, err := net.ParseMAC(endpoint.MacAddress)
- if err != nil {
- return nil, nil, errors.Errorf("failed to parse the mac address %q", endpoint.MacAddress)
- }
- netOpts.StaticMAC = types.HardwareAddr(staticMac)
- }
- }
-
- networks[netName] = netOpts
- }
-
- netInfo.Networks = networks
- case len(cc.HostConfig.NetworkMode) > 0:
- netInfo.Networks = networks
- }
-
- parsedTmp := make([]string, 0, len(cc.HostConfig.Tmpfs))
- for path, options := range cc.HostConfig.Tmpfs {
- finalString := path
- if options != "" {
- finalString += ":" + options
- }
- parsedTmp = append(parsedTmp, finalString)
- }
-
- // Note: several options here are marked as "don't need". this is based
- // on speculation by Matt and I. We think that these come into play later
- // like with start. We believe this is just a difference in podman/compat
- cliOpts := entities.ContainerCreateOptions{
- // Attach: nil, // don't need?
- Authfile: "",
- CapAdd: append(capAdd, cc.HostConfig.CapAdd...),
- CapDrop: append(cappDrop, cc.HostConfig.CapDrop...),
- CgroupParent: cc.HostConfig.CgroupParent,
- CIDFile: cc.HostConfig.ContainerIDFile,
- CPUPeriod: uint64(cc.HostConfig.CPUPeriod),
- CPUQuota: cc.HostConfig.CPUQuota,
- CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod),
- CPURTRuntime: cc.HostConfig.CPURealtimeRuntime,
- CPUShares: uint64(cc.HostConfig.CPUShares),
- // CPUS: 0, // don't need?
- CPUSetCPUs: cc.HostConfig.CpusetCpus,
- CPUSetMems: cc.HostConfig.CpusetMems,
- // Detach: false, // don't need
- // DetachKeys: "", // don't need
- Devices: devices,
- DeviceCgroupRule: nil,
- DeviceReadBPs: readBps,
- DeviceReadIOPs: readIops,
- DeviceWriteBPs: writeBps,
- DeviceWriteIOPs: writeIops,
- Entrypoint: entrypoint,
- Env: cc.Config.Env,
- Expose: expose,
- GroupAdd: cc.HostConfig.GroupAdd,
- Hostname: cc.Config.Hostname,
- ImageVolume: "bind",
- Init: init,
- Interactive: cc.Config.OpenStdin,
- IPC: string(cc.HostConfig.IpcMode),
- Label: stringMaptoArray(cc.Config.Labels),
- LogDriver: cc.HostConfig.LogConfig.Type,
- LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config),
- Name: cc.Name,
- OOMScoreAdj: &cc.HostConfig.OomScoreAdj,
- Arch: "",
- OS: "",
- Variant: "",
- PID: string(cc.HostConfig.PidMode),
- PIDsLimit: cc.HostConfig.PidsLimit,
- Privileged: cc.HostConfig.Privileged,
- PublishAll: cc.HostConfig.PublishAllPorts,
- Quiet: false,
- ReadOnly: cc.HostConfig.ReadonlyRootfs,
- ReadOnlyTmpFS: true, // podman default
- Rm: cc.HostConfig.AutoRemove,
- SecurityOpt: cc.HostConfig.SecurityOpt,
- StopSignal: cc.Config.StopSignal,
- StorageOpts: stringMaptoArray(cc.HostConfig.StorageOpt),
- Sysctl: stringMaptoArray(cc.HostConfig.Sysctls),
- Systemd: "true", // podman default
- TmpFS: parsedTmp,
- TTY: cc.Config.Tty,
- UnsetEnv: cc.UnsetEnv,
- UnsetEnvAll: cc.UnsetEnvAll,
- User: cc.Config.User,
- UserNS: string(cc.HostConfig.UsernsMode),
- UTS: string(cc.HostConfig.UTSMode),
- Mount: mounts,
- VolumesFrom: cc.HostConfig.VolumesFrom,
- Workdir: cc.Config.WorkingDir,
- Net: &netInfo,
- HealthInterval: define.DefaultHealthCheckInterval,
- HealthRetries: define.DefaultHealthCheckRetries,
- HealthTimeout: define.DefaultHealthCheckTimeout,
- HealthStartPeriod: define.DefaultHealthCheckStartPeriod,
- }
- if !rootless.IsRootless() {
- var ulimits []string
- if len(cc.HostConfig.Ulimits) > 0 {
- for _, ul := range cc.HostConfig.Ulimits {
- ulimits = append(ulimits, ul.String())
- }
- cliOpts.Ulimit = ulimits
- }
- }
- if cc.HostConfig.Resources.NanoCPUs > 0 {
- if cliOpts.CPUPeriod != 0 || cliOpts.CPUQuota != 0 {
- return nil, nil, errors.Errorf("NanoCpus conflicts with CpuPeriod and CpuQuota")
- }
- cliOpts.CPUPeriod = 100000
- cliOpts.CPUQuota = cc.HostConfig.Resources.NanoCPUs / 10000
- }
-
- // volumes
- for _, vol := range cc.HostConfig.Binds {
- cliOpts.Volume = append(cliOpts.Volume, vol)
- // Extract the destination so we don't add duplicate mounts in
- // the volumes phase.
- splitVol := specgen.SplitVolumeString(vol)
- switch len(splitVol) {
- case 1:
- volDestinations[vol] = true
- default:
- volSources[splitVol[0]] = true
- volDestinations[splitVol[1]] = true
- }
- }
- // Anonymous volumes are added differently from other volumes, in their
- // own special field, for reasons known only to Docker. Still use the
- // format of `-v` so we can just append them in there.
- // Unfortunately, these may be duplicates of existing mounts in Binds.
- // So... We need to catch that.
- // This also handles volumes duplicated between cc.HostConfig.Mounts and
- // cc.Volumes, as seen in compose v2.0.
- for vol := range cc.Volumes {
- if _, ok := volDestinations[filepath.Clean(vol)]; ok {
- continue
- }
- cliOpts.Volume = append(cliOpts.Volume, vol)
- }
- // Make mount points for compat volumes
- for vol := range volSources {
- // This might be a named volume.
- // Assume it is if it's not an absolute path.
- if !filepath.IsAbs(vol) {
- continue
- }
- // If volume already exists, there is nothing to do
- if _, err := os.Stat(vol); err == nil {
- continue
- }
- if err := os.MkdirAll(vol, 0755); err != nil {
- if !os.IsExist(err) {
- return nil, nil, errors.Wrapf(err, "error making volume mountpoint for volume %s", vol)
- }
- }
- }
- if len(cc.HostConfig.BlkioWeightDevice) > 0 {
- devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice))
- for _, d := range cc.HostConfig.BlkioWeightDevice {
- devices = append(devices, d.String())
- }
- cliOpts.BlkIOWeightDevice = devices
- }
- if cc.HostConfig.BlkioWeight > 0 {
- cliOpts.BlkIOWeight = strconv.Itoa(int(cc.HostConfig.BlkioWeight))
- }
-
- if cc.HostConfig.Memory > 0 {
- cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory))
- }
-
- if cc.HostConfig.MemoryReservation > 0 {
- cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation))
- }
-
- cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
- if err != nil {
- return nil, nil, err
- }
- if cc.HostConfig.MemorySwap > 0 && (!rootless.IsRootless() || (rootless.IsRootless() && cgroupsv2)) {
- cliOpts.MemorySwap = strconv.Itoa(int(cc.HostConfig.MemorySwap))
- }
-
- if cc.Config.StopTimeout != nil {
- cliOpts.StopTimeout = uint(*cc.Config.StopTimeout)
- }
-
- if cc.HostConfig.ShmSize > 0 {
- cliOpts.ShmSize = strconv.Itoa(int(cc.HostConfig.ShmSize))
- }
-
- if len(cc.HostConfig.RestartPolicy.Name) > 0 {
- policy := cc.HostConfig.RestartPolicy.Name
- // only add restart count on failure
- if cc.HostConfig.RestartPolicy.IsOnFailure() {
- policy += fmt.Sprintf(":%d", cc.HostConfig.RestartPolicy.MaximumRetryCount)
- }
- cliOpts.Restart = policy
- }
-
- if cc.HostConfig.MemorySwappiness != nil && (!rootless.IsRootless() || rootless.IsRootless() && cgroupsv2 && rtc.Engine.CgroupManager == "systemd") {
- cliOpts.MemorySwappiness = *cc.HostConfig.MemorySwappiness
- } else {
- cliOpts.MemorySwappiness = -1
- }
- if cc.HostConfig.OomKillDisable != nil {
- cliOpts.OOMKillDisable = *cc.HostConfig.OomKillDisable
- }
- if cc.Config.Healthcheck != nil {
- finCmd := ""
- for _, str := range cc.Config.Healthcheck.Test {
- finCmd = finCmd + str + " "
- }
- if len(finCmd) > 1 {
- finCmd = finCmd[:len(finCmd)-1]
- }
- cliOpts.HealthCmd = finCmd
- if cc.Config.Healthcheck.Interval > 0 {
- cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String()
- }
- if cc.Config.Healthcheck.Retries > 0 {
- cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries)
- }
- if cc.Config.Healthcheck.StartPeriod > 0 {
- cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String()
- }
- if cc.Config.Healthcheck.Timeout > 0 {
- cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String()
- }
- }
-
- // specgen assumes the image name is arg[0]
- cmd := []string{cc.Config.Image}
- cmd = append(cmd, cc.Config.Cmd...)
- return &cliOpts, cmd, nil
-}
-
func ulimits() []string {
if !registry.IsRemote() {
return containerConfig.Ulimits()
@@ -488,7 +27,7 @@ func devices() []string {
return nil
}
-func env() []string {
+func Env() []string {
if !registry.IsRemote() {
return containerConfig.Env()
}
@@ -537,16 +76,20 @@ func LogDriver() string {
return ""
}
-// addField is a helper function to populate mount options
-func addField(b *strings.Builder, name string, value string) {
- if value == "" {
- return
- }
-
- if b.Len() > 0 {
- b.WriteRune(',')
- }
- b.WriteString(name)
- b.WriteRune('=')
- b.WriteString(value)
+// DefineCreateDefault is used to initialize ctr create options before flag initialization
+func DefineCreateDefaults(opts *entities.ContainerCreateOptions) {
+ opts.LogDriver = LogDriver()
+ opts.CgroupsMode = cgroupConfig()
+ opts.MemorySwappiness = -1
+ opts.ImageVolume = containerConfig.Engine.ImageVolumeMode
+ opts.Pull = policy()
+ opts.ReadOnlyTmpFS = true
+ opts.SdNotifyMode = define.SdNotifyModeContainer
+ opts.StopTimeout = containerConfig.Engine.StopTimeout
+ opts.Systemd = "true"
+ opts.Timezone = containerConfig.TZ()
+ opts.Umask = containerConfig.Umask()
+ opts.Ulimit = ulimits()
+ opts.SeccompPolicy = "default"
+ opts.Volume = volumes()
}
diff --git a/cmd/podman/common/default.go b/cmd/podman/common/default.go
index 7caec50ff..6f78d3d29 100644
--- a/cmd/podman/common/default.go
+++ b/cmd/podman/common/default.go
@@ -5,9 +5,6 @@ import (
)
var (
-
- // DefaultImageVolume default value
- DefaultImageVolume = "bind"
// Pull in configured json library
json = registry.JSONLibrary()
)
diff --git a/cmd/podman/containers/clone.go b/cmd/podman/containers/clone.go
index 6912da1fc..2763adf3c 100644
--- a/cmd/podman/containers/clone.go
+++ b/cmd/podman/containers/clone.go
@@ -41,6 +41,7 @@ func cloneFlags(cmd *cobra.Command) {
forceFlagName := "force"
flags.BoolVarP(&ctrClone.Force, forceFlagName, "f", false, "force the existing container to be destroyed")
+ common.DefineCreateDefaults(&ctrClone.CreateOpts)
common.DefineCreateFlags(cmd, &ctrClone.CreateOpts, false, true)
}
func init() {
diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go
index eb18dfce4..a5842afc8 100644
--- a/cmd/podman/containers/cp.go
+++ b/cmd/podman/containers/cp.go
@@ -55,10 +55,13 @@ var (
func cpFlags(cmd *cobra.Command) {
flags := cmd.Flags()
- flags.BoolVar(&cpOpts.Extract, "extract", false, "Deprecated...")
- flags.BoolVar(&cpOpts.Pause, "pause", true, "Deprecated")
+ flags.BoolVar(&cpOpts.OverwriteDirNonDir, "overwrite", false, "Allow to overwrite directories with non-directories and vice versa")
flags.BoolVarP(&chown, "archive", "a", true, `Chown copied files to the primary uid/gid of the destination container.`)
+
+ // Deprecated flags (both are NOPs): exist for backwards compat
+ flags.BoolVar(&cpOpts.Extract, "extract", false, "Deprecated...")
_ = flags.MarkHidden("extract")
+ flags.BoolVar(&cpOpts.Pause, "pause", true, "Deprecated")
_ = flags.MarkHidden("pause")
}
@@ -175,7 +178,7 @@ func copyContainerToContainer(sourceContainer string, sourcePath string, destCon
destContainerCopy := func() error {
defer reader.Close()
- copyOptions := entities.CopyOptions{Chown: chown}
+ copyOptions := entities.CopyOptions{Chown: chown, NoOverwriteDirNonDir: !cpOpts.OverwriteDirNonDir}
if (!sourceContainerInfo.IsDir && !destContainerInfo.IsDir) || destResolvedToParentDir {
// If we're having a file-to-file copy, make sure to
// rename accordingly.
@@ -294,9 +297,11 @@ func copyFromContainer(container string, containerPath string, hostPath string)
}
putOptions := buildahCopiah.PutOptions{
- ChownDirs: &idPair,
- ChownFiles: &idPair,
- IgnoreDevices: true,
+ ChownDirs: &idPair,
+ ChownFiles: &idPair,
+ IgnoreDevices: true,
+ NoOverwriteDirNonDir: !cpOpts.OverwriteDirNonDir,
+ NoOverwriteNonDirDir: !cpOpts.OverwriteDirNonDir,
}
if (!containerInfo.IsDir && !hostInfo.IsDir) || resolvedToHostParentDir {
// If we're having a file-to-file copy, make sure to
@@ -429,7 +434,7 @@ func copyToContainer(container string, containerPath string, hostPath string) er
target = filepath.Dir(target)
}
- copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), container, target, reader, entities.CopyOptions{Chown: chown})
+ copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), container, target, reader, entities.CopyOptions{Chown: chown, NoOverwriteDirNonDir: !cpOpts.OverwriteDirNonDir})
if err != nil {
return err
}
@@ -449,7 +454,7 @@ func resolvePathOnDestinationContainer(container string, containerPath string, i
containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
if err == nil {
baseName = filepath.Base(containerInfo.LinkTarget)
- return // nolint: nilerr
+ return //nolint: nilerr
}
if strings.HasSuffix(containerPath, "/") {
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index c62ddd6eb..c021aa031 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -71,6 +71,7 @@ func createFlags(cmd *cobra.Command) {
)
flags.SetInterspersed(false)
+ common.DefineCreateDefaults(&cliVals)
common.DefineCreateFlags(cmd, &cliVals, false, false)
common.DefineNetFlags(cmd)
@@ -102,16 +103,25 @@ func init() {
createFlags(containerCreateCommand)
}
-func create(cmd *cobra.Command, args []string) error {
- var (
- err error
- )
+func commonFlags(cmd *cobra.Command) error {
+ var err error
flags := cmd.Flags()
cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags)
if err != nil {
return err
}
+ if cmd.Flags().Changed("image-volume") {
+ cliVals.ImageVolume = cmd.Flag("image-volume").Value.String()
+ }
+ return nil
+}
+
+func create(cmd *cobra.Command, args []string) error {
+ if err := commonFlags(cmd); err != nil {
+ return err
+ }
+
// Check if initctr is used with --pod and the value is correct
if initctr := InitContainerType; cmd.Flags().Changed("init-ctr") {
if !cmd.Flags().Changed("pod") {
@@ -123,7 +133,7 @@ func create(cmd *cobra.Command, args []string) error {
cliVals.InitContainerType = initctr
}
- cliVals, err = CreateInit(cmd, cliVals, false)
+ cliVals, err := CreateInit(cmd, cliVals, false)
if err != nil {
return err
}
@@ -207,9 +217,6 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra
}
if !isInfra {
- if c.Flag("shm-size").Changed {
- vals.ShmSize = c.Flag("shm-size").Value.String()
- }
if c.Flag("cpu-period").Changed && c.Flag("cpus").Changed {
return vals, errors.Errorf("--cpu-period and --cpus cannot be set together")
}
@@ -273,6 +280,9 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra
return vals, errors.Errorf("--userns and --pod cannot be set together")
}
}
+ if c.Flag("shm-size").Changed {
+ vals.ShmSize = c.Flag("shm-size").Value.String()
+ }
if (c.Flag("dns").Changed || c.Flag("dns-opt").Changed || c.Flag("dns-search").Changed) && vals.Net != nil && (vals.Net.Network.NSMode == specgen.NoNetwork || vals.Net.Network.IsContainer()) {
return vals, errors.Errorf("conflicting options: dns and the network mode: " + string(vals.Net.Network.NSMode))
}
diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go
index 951981293..e49bdaee4 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -61,6 +61,7 @@ func runFlags(cmd *cobra.Command) {
flags := cmd.Flags()
flags.SetInterspersed(false)
+ common.DefineCreateDefaults(&cliVals)
common.DefineCreateFlags(cmd, &cliVals, false, false)
common.DefineNetFlags(cmd)
@@ -109,7 +110,9 @@ func init() {
}
func run(cmd *cobra.Command, args []string) error {
- var err error
+ if err := commonFlags(cmd); err != nil {
+ return err
+ }
// TODO: Breaking change should be made fatal in next major Release
if cliVals.TTY && cliVals.Interactive && !term.IsTerminal(int(os.Stdin.Fd())) {
@@ -122,14 +125,10 @@ func run(cmd *cobra.Command, args []string) error {
}
}
- flags := cmd.Flags()
- cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags)
- if err != nil {
- return err
- }
runOpts.CIDFile = cliVals.CIDFile
runOpts.Rm = cliVals.Rm
- if cliVals, err = CreateInit(cmd, cliVals, false); err != nil {
+ cliVals, err := CreateInit(cmd, cliVals, false)
+ if err != nil {
return err
}
diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go
index 500671d31..89c7f2b08 100644
--- a/cmd/podman/containers/stats.go
+++ b/cmd/podman/containers/stats.go
@@ -214,10 +214,6 @@ func (s *containerStats) BlockIO() string {
}
func (s *containerStats) PIDS() string {
- if s.PIDs == 0 {
- // If things go bazinga, return a safe value
- return "--"
- }
return fmt.Sprintf("%d", s.PIDs)
}
@@ -231,7 +227,7 @@ func (s *containerStats) MemUsageBytes() string {
func floatToPercentString(f float64) string {
strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
- if err != nil || strippedFloat == 0 {
+ if err != nil {
// If things go bazinga, return a safe value
return "--"
}
@@ -239,25 +235,19 @@ func floatToPercentString(f float64) string {
}
func combineHumanValues(a, b uint64) string {
- if a == 0 && b == 0 {
- return "-- / --"
- }
return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b)))
}
func combineBytesValues(a, b uint64) string {
- if a == 0 && b == 0 {
- return "-- / --"
- }
return fmt.Sprintf("%s / %s", units.BytesSize(float64(a)), units.BytesSize(float64(b)))
}
func outputJSON(stats []containerStats) error {
type jstat struct {
- Id string `json:"id"` // nolint
+ Id string `json:"id"` //nolint:revive,stylecheck
Name string `json:"name"`
CPUTime string `json:"cpu_time"`
- CpuPercent string `json:"cpu_percent"` // nolint
+ CpuPercent string `json:"cpu_percent"` //nolint:revive,stylecheck
AverageCPU string `json:"avg_cpu"`
MemUsage string `json:"mem_usage"`
MemPerc string `json:"mem_percent"`
diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go
index 940ea6e42..94b7c43a2 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -191,6 +191,7 @@ func buildFlags(cmd *cobra.Command) {
_ = flags.MarkHidden("compress")
_ = flags.MarkHidden("volume")
_ = flags.MarkHidden("output")
+ _ = flags.MarkHidden("logsplit")
}
}
@@ -546,6 +547,8 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
Labels: flags.Label,
Layers: flags.Layers,
LogRusage: flags.LogRusage,
+ LogFile: flags.Logfile,
+ LogSplitByPlatform: flags.LogSplitByPlatform,
Manifest: flags.Manifest,
MaxPullPushRetries: 3,
NamespaceOptions: nsValues,
diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go
index dbb7c32fa..c18c32387 100644
--- a/cmd/podman/images/load.go
+++ b/cmd/podman/images/load.go
@@ -110,6 +110,6 @@ func load(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
- fmt.Println("Loaded image(s): " + strings.Join(response.Names, ","))
+ fmt.Println("Loaded image: " + strings.Join(response.Names, "\nLoaded image: "))
return nil
}
diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go
index a59bdd93c..1b3419014 100644
--- a/cmd/podman/images/push.go
+++ b/cmd/podman/images/push.go
@@ -117,7 +117,6 @@ func pushFlags(cmd *cobra.Command) {
_ = flags.MarkHidden("compress")
_ = flags.MarkHidden("digestfile")
_ = flags.MarkHidden("quiet")
- _ = flags.MarkHidden("remove-signatures")
_ = flags.MarkHidden("sign-by")
}
if !registry.IsRemote() {
diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go
index f6e3fca06..05a6de699 100644
--- a/cmd/podman/inspect/inspect.go
+++ b/cmd/podman/inspect/inspect.go
@@ -93,7 +93,7 @@ func newInspector(options entities.InspectOptions) (*inspector, error) {
// inspect inspects the specified container/image names or IDs.
func (i *inspector) inspect(namesOrIDs []string) error {
// data - dumping place for inspection results.
- var data []interface{} // nolint
+ var data []interface{}
var errs []error
ctx := context.Background()
@@ -249,7 +249,7 @@ func printTmpl(typ, row string, data []interface{}) error {
}
func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]interface{}, []error, error) {
- var data []interface{} // nolint
+ var data []interface{}
allErrs := []error{}
for _, name := range namesOrIDs {
ctrData, errs, err := i.containerEngine.ContainerInspect(ctx, []string{name}, i.options)
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go
index 5254d50cf..bb14d4a67 100644
--- a/cmd/podman/machine/list.go
+++ b/cmd/podman/machine/list.go
@@ -48,6 +48,7 @@ type ListReporter struct {
Default bool
Created string
Running bool
+ Starting bool
LastUp string
Stream string
VMType string
@@ -224,10 +225,14 @@ func toHumanFormat(vms []*machine.ListResponse) ([]*ListReporter, error) {
} else {
response.Name = vm.Name
}
- if vm.Running {
+ switch {
+ case vm.Running:
response.LastUp = "Currently running"
response.Running = true
- } else {
+ case vm.Starting:
+ response.LastUp = "Currently starting"
+ response.Starting = true
+ default:
response.LastUp = units.HumanDuration(time.Since(vm.LastUp)) + " ago"
}
response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago"
diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go
index c9b99e63b..3bd7f4a25 100644
--- a/cmd/podman/machine/start.go
+++ b/cmd/podman/machine/start.go
@@ -56,7 +56,7 @@ func start(_ *cobra.Command, args []string) error {
if vmName == activeName {
return errors.Wrapf(machine.ErrVMAlreadyRunning, "cannot start VM %s", vmName)
}
- return errors.Wrapf(machine.ErrMultipleActiveVM, "cannot start VM %s. VM %s is currently running", vmName, activeName)
+ return errors.Wrapf(machine.ErrMultipleActiveVM, "cannot start VM %s. VM %s is currently running or starting", vmName, activeName)
}
fmt.Printf("Starting machine %q\n", vmName)
if err := vm.Start(vmName, machine.StartOptions{}); err != nil {
diff --git a/cmd/podman/parse/net.go b/cmd/podman/parse/net.go
index b616e1029..ba70c7ba5 100644
--- a/cmd/podman/parse/net.go
+++ b/cmd/podman/parse/net.go
@@ -1,4 +1,3 @@
-// nolint
// most of these validate and parse functions have been taken from projectatomic/docker
// and modified for cri-o
package parse
@@ -16,26 +15,10 @@ import (
)
const (
- Protocol_TCP Protocol = 0
- Protocol_UDP Protocol = 1
- LabelType string = "label"
- ENVType string = "env"
+ LabelType string = "label"
+ ENVType string = "env"
)
-type Protocol int32
-
-// PortMapping specifies the port mapping configurations of a sandbox.
-type PortMapping struct {
- // Protocol of the port mapping.
- Protocol Protocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=runtime.Protocol" json:"protocol,omitempty"`
- // Port number within the container. Default: 0 (not specified).
- ContainerPort int32 `protobuf:"varint,2,opt,name=container_port,json=containerPort,proto3" json:"container_port,omitempty"`
- // Port number on the host. Default: 0 (not specified).
- HostPort int32 `protobuf:"varint,3,opt,name=host_port,json=hostPort,proto3" json:"host_port,omitempty"`
- // Host IP.
- HostIp string `protobuf:"bytes,4,opt,name=host_ip,json=hostIp,proto3" json:"host_ip,omitempty"`
-}
-
// Note: for flags that are in the form <number><unit>, use the RAMInBytes function
// from the units package in docker/go-units/size.go
@@ -48,7 +31,7 @@ var (
// validateExtraHost validates that the specified string is a valid extrahost and returns it.
// ExtraHost is in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
// for add-host flag
-func ValidateExtraHost(val string) (string, error) { // nolint
+func ValidateExtraHost(val string) (string, error) {
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
arr := strings.SplitN(val, ":", 2)
if len(arr) != 2 || len(arr[0]) == 0 {
diff --git a/cmd/podman/parse/net_test.go b/cmd/podman/parse/net_test.go
index 51c8509df..a11edc2ca 100644
--- a/cmd/podman/parse/net_test.go
+++ b/cmd/podman/parse/net_test.go
@@ -1,4 +1,3 @@
-// nolint
// most of these validate and parse functions have been taken from projectatomic/docker
// and modified for cri-o
package parse
@@ -23,7 +22,6 @@ func createTmpFile(content []byte) (string, error) {
if _, err := tmpfile.Write(content); err != nil {
return "", err
-
}
if err := tmpfile.Close(); err != nil {
return "", err
diff --git a/cmd/podman/pods/clone.go b/cmd/podman/pods/clone.go
new file mode 100644
index 000000000..391af1cf7
--- /dev/null
+++ b/cmd/podman/pods/clone.go
@@ -0,0 +1,93 @@
+package pods
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/common/pkg/completion"
+ "github.com/containers/podman/v4/cmd/podman/common"
+ "github.com/containers/podman/v4/cmd/podman/registry"
+ "github.com/containers/podman/v4/libpod/define"
+ "github.com/containers/podman/v4/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ podCloneDescription = `Create an exact copy of a pod and the containers within it`
+
+ podCloneCommand = &cobra.Command{
+ Use: "clone [options] POD NAME",
+ Short: "Clone an existing pod",
+ Long: podCloneDescription,
+ RunE: clone,
+ Args: cobra.RangeArgs(1, 2),
+ ValidArgsFunction: common.AutocompleteClone,
+ Example: `podman pod clone pod_name new_name`,
+ }
+)
+
+var (
+ podClone entities.PodCloneOptions
+)
+
+func cloneFlags(cmd *cobra.Command) {
+ flags := cmd.Flags()
+
+ destroyFlagName := "destroy"
+ flags.BoolVar(&podClone.Destroy, destroyFlagName, false, "destroy the original pod")
+
+ startFlagName := "start"
+ flags.BoolVar(&podClone.Start, startFlagName, false, "start the new pod")
+
+ nameFlagName := "name"
+ flags.StringVarP(&podClone.CreateOpts.Name, nameFlagName, "n", "", "name the new pod")
+ _ = podCloneCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone)
+
+ common.DefineCreateDefaults(&podClone.InfraOptions)
+ common.DefineCreateFlags(cmd, &podClone.InfraOptions, true, false)
+
+ podClone.InfraOptions.MemorySwappiness = -1 // this is not implemented for pods yet, need to set -1 default manually
+
+ // need to fill an empty ctr create option for each container for sane defaults
+ // for now, these cannot be used. The flag names conflict too much
+ // this makes sense since this is a pod command not a container command
+ // TODO: add support for container specific arguments/flags
+ common.DefineCreateDefaults(&podClone.PerContainerOptions)
+}
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Command: podCloneCommand,
+ Parent: podCmd,
+ })
+
+ cloneFlags(podCloneCommand)
+}
+
+func clone(cmd *cobra.Command, args []string) error {
+ switch len(args) {
+ case 0:
+ return errors.Wrapf(define.ErrInvalidArg, "must specify at least 1 argument")
+ case 2:
+ podClone.CreateOpts.Name = args[1]
+ }
+
+ podClone.ID = args[0]
+
+ if cmd.Flag("shm-size").Changed {
+ podClone.InfraOptions.ShmSize = cmd.Flag("shm-size").Value.String()
+ }
+
+ podClone.PerContainerOptions.IsClone = true
+ rep, err := registry.ContainerEngine().PodClone(context.Background(), podClone)
+ if err != nil {
+ if rep != nil {
+ fmt.Printf("pod %s created but error after creation\n", rep.Id)
+ }
+ return err
+ }
+
+ fmt.Println(rep.Id)
+
+ return nil
+}
diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go
index e2f80bdbc..ca9d60174 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -64,6 +64,7 @@ func init() {
})
flags := createCommand.Flags()
flags.SetInterspersed(false)
+ common.DefineCreateDefaults(&infraOptions)
common.DefineCreateFlags(createCommand, &infraOptions, true, false)
common.DefineNetFlags(createCommand)
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index aa42e1983..c98b4ef4e 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -211,7 +211,7 @@ func (l ListPodReporter) ID() string {
}
// Id returns the Pod id
-func (l ListPodReporter) Id() string { // nolint
+func (l ListPodReporter) Id() string { //nolint:revive,stylecheck
if noTrunc {
return l.ListPodsReport.Id
}
@@ -225,7 +225,7 @@ func (l ListPodReporter) InfraID() string {
// InfraId returns the infra container id for the pod
// depending on trunc
-func (l ListPodReporter) InfraId() string { // nolint
+func (l ListPodReporter) InfraId() string { //nolint:revive,stylecheck
if len(l.ListPodsReport.InfraId) == 0 {
return ""
}
diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go
index dad14df6b..2fcc12feb 100644
--- a/cmd/podman/system/df.go
+++ b/cmd/podman/system/df.go
@@ -150,7 +150,7 @@ func printSummary(cmd *cobra.Command, reports *entities.SystemDfReport) error {
return writeTemplate(rpt, hdrs, dfSummaries)
}
-func printVerbose(cmd *cobra.Command, reports *entities.SystemDfReport) error { // nolint:interfacer
+func printVerbose(cmd *cobra.Command, reports *entities.SystemDfReport) error { //nolint:interfacer
rpt := report.New(os.Stdout, cmd.Name())
defer rpt.Flush()
diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go
index ff78f93bb..1d6ba8155 100644
--- a/cmd/podman/system/prune.go
+++ b/cmd/podman/system/prune.go
@@ -75,6 +75,7 @@ func prune(cmd *cobra.Command, args []string) error {
}
}
+ // Remove all unused pods, containers, images, networks, and volume data.
pruneOptions.Filters, err = parse.FilterArgumentsIntoFilters(filters)
if err != nil {
return err
@@ -106,6 +107,11 @@ func prune(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
+ // Print Network prune results
+ err = utils.PrintNetworkPruneResults(response.NetworkPruneReports, true)
+ if err != nil {
+ return err
+ }
fmt.Printf("Total reclaimed space: %s\n", units.HumanSize((float64)(response.ReclaimedSpace)))
return nil
diff --git a/cmd/podman/system/reset.go b/cmd/podman/system/reset.go
index 176573bf6..20f15a34f 100644
--- a/cmd/podman/system/reset.go
+++ b/cmd/podman/system/reset.go
@@ -91,18 +91,10 @@ func reset(cmd *cobra.Command, args []string) {
registry.ContainerEngine().Shutdown(registry.Context())
registry.ImageEngine().Shutdown(registry.Context())
- engine, err := infra.NewSystemEngine(entities.ResetMode, registry.PodmanConfig())
- if err != nil {
- logrus.Error(err)
- os.Exit(define.ExecErrorCodeGeneric)
- }
- defer engine.Shutdown(registry.Context())
-
- if err := engine.Reset(registry.Context()); err != nil {
+ // Do not try to shut the engine down, as a Reset engine is not valid
+ // after its creation.
+ if _, err := infra.NewSystemEngine(entities.ResetMode, registry.PodmanConfig()); err != nil {
logrus.Error(err)
- // FIXME change this to return the error like other commands
- // defer will never run on os.Exit()
- //nolint:gocritic
os.Exit(define.ExecErrorCodeGeneric)
}
diff --git a/cmd/podman/system/service_abi.go b/cmd/podman/system/service_abi.go
index 9dc9de1c8..7cb1b8084 100644
--- a/cmd/podman/system/service_abi.go
+++ b/cmd/podman/system/service_abi.go
@@ -46,6 +46,10 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
return fmt.Errorf("wrong number of file descriptors for socket activation protocol (%d != 1)", len(listeners))
}
listener = listeners[0]
+ // note that activation.Listeners() returns nil when it cannot listen on the fd (i.e. udp connection)
+ if listener == nil {
+ return fmt.Errorf("unexpected fd received from systemd: cannot listen on it")
+ }
libpodRuntime.SetRemoteURI(listeners[0].Addr().String())
} else {
uri, err := url.Parse(opts.URI)
diff --git a/cmd/podman/utils/utils.go b/cmd/podman/utils/utils.go
index 6fd6647d0..73bb34983 100644
--- a/cmd/podman/utils/utils.go
+++ b/cmd/podman/utils/utils.go
@@ -84,3 +84,18 @@ func PrintImagePruneResults(imagePruneReports []*reports.PruneReport, heading bo
return nil
}
+
+func PrintNetworkPruneResults(networkPruneReport []*reports.PruneReport, heading bool) error {
+ var errs OutputErrors
+ if heading && len(networkPruneReport) > 0 {
+ fmt.Println("Deleted Networks")
+ }
+ for _, r := range networkPruneReport {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podman/validate/args.go b/cmd/podman/validate/args.go
index b9b468d34..4c40581c6 100644
--- a/cmd/podman/validate/args.go
+++ b/cmd/podman/validate/args.go
@@ -27,7 +27,7 @@ func SubCommandExists(cmd *cobra.Command, args []string) error {
}
return errors.Errorf("unrecognized command `%[1]s %[2]s`\n\nDid you mean this?\n\t%[3]s\n\nTry '%[1]s --help' for more information", cmd.CommandPath(), args[0], strings.Join(suggestions, "\n\t"))
}
- cmd.Help() // nolint: errcheck
+ cmd.Help() //nolint: errcheck
return errors.Errorf("missing command '%[1]s COMMAND'", cmd.CommandPath())
}
diff --git a/cmd/rootlessport/main.go b/cmd/rootlessport/main.go
index 5bd35a985..f01b9e4a6 100644
--- a/cmd/rootlessport/main.go
+++ b/cmd/rootlessport/main.go
@@ -226,8 +226,8 @@ outer:
// https://github.com/containers/podman/issues/11248
// Copy /dev/null to stdout and stderr to prevent SIGPIPE errors
if f, err := os.OpenFile("/dev/null", os.O_WRONLY, 0755); err == nil {
- unix.Dup2(int(f.Fd()), 1) // nolint:errcheck
- unix.Dup2(int(f.Fd()), 2) // nolint:errcheck
+ unix.Dup2(int(f.Fd()), 1) //nolint:errcheck
+ unix.Dup2(int(f.Fd()), 2) //nolint:errcheck
f.Close()
}
// write and close ReadyFD (convention is same as slirp4netns --ready-fd)
diff --git a/cmd/rootlessport/wsl_test.go b/cmd/rootlessport/wsl_test.go
index 83d7e3717..2c95251cd 100644
--- a/cmd/rootlessport/wsl_test.go
+++ b/cmd/rootlessport/wsl_test.go
@@ -20,7 +20,7 @@ type SpecData struct {
}
func TestDualStackSplit(t *testing.T) {
- //nolint
+ //nolint:revive,stylecheck
const (
IP4_ALL = "0.0.0.0"
IP4__LO = "127.0.0.1"