summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman-mac-helper/install.go18
-rw-r--r--cmd/podman-mac-helper/main.go8
-rw-r--r--cmd/podman-mac-helper/uninstall.go5
-rw-r--r--cmd/podman/auto-update.go3
-rw-r--r--cmd/podman/common/completion.go347
-rw-r--r--cmd/podman/common/completion_test.go72
-rw-r--r--cmd/podman/common/create.go83
-rw-r--r--cmd/podman/common/create_opts.go491
-rw-r--r--cmd/podman/common/default.go3
-rw-r--r--cmd/podman/common/diffChanges.go3
-rw-r--r--cmd/podman/common/netflags.go27
-rw-r--r--cmd/podman/containers/attach.go4
-rw-r--r--cmd/podman/containers/checkpoint.go14
-rw-r--r--cmd/podman/containers/cleanup.go12
-rw-r--r--cmd/podman/containers/clone.go8
-rw-r--r--cmd/podman/containers/commit.go3
-rw-r--r--cmd/podman/containers/cp.go61
-rw-r--r--cmd/podman/containers/create.go57
-rw-r--r--cmd/podman/containers/diff.go9
-rw-r--r--cmd/podman/containers/exec.go6
-rw-r--r--cmd/podman/containers/export.go4
-rw-r--r--cmd/podman/containers/init.go2
-rw-r--r--cmd/podman/containers/kill.go8
-rw-r--r--cmd/podman/containers/logs.go7
-rw-r--r--cmd/podman/containers/mount.go8
-rw-r--r--cmd/podman/containers/pause.go4
-rw-r--r--cmd/podman/containers/port.go16
-rw-r--r--cmd/podman/containers/ps.go10
-rw-r--r--cmd/podman/containers/rename.go5
-rw-r--r--cmd/podman/containers/restart.go7
-rw-r--r--cmd/podman/containers/restore.go2
-rw-r--r--cmd/podman/containers/rm.go23
-rw-r--r--cmd/podman/containers/run.go25
-rw-r--r--cmd/podman/containers/start.go16
-rw-r--r--cmd/podman/containers/stats.go20
-rw-r--r--cmd/podman/containers/stop.go7
-rw-r--r--cmd/podman/containers/top.go4
-rw-r--r--cmd/podman/containers/unmount.go4
-rw-r--r--cmd/podman/containers/unpause.go4
-rw-r--r--cmd/podman/containers/wait.go4
-rw-r--r--cmd/podman/diff.go3
-rw-r--r--cmd/podman/diff/diff.go10
-rw-r--r--cmd/podman/early_init_linux.go5
-rw-r--r--cmd/podman/generate/kube.go5
-rw-r--r--cmd/podman/generate/systemd.go6
-rw-r--r--cmd/podman/images/build.go77
-rw-r--r--cmd/podman/images/diff.go4
-rw-r--r--cmd/podman/images/history.go3
-rw-r--r--cmd/podman/images/import.go10
-rw-r--r--cmd/podman/images/list.go4
-rw-r--r--cmd/podman/images/load.go10
-rw-r--r--cmd/podman/images/mount.go19
-rw-r--r--cmd/podman/images/pull.go4
-rw-r--r--cmd/podman/images/push.go1
-rw-r--r--cmd/podman/images/rm.go6
-rw-r--r--cmd/podman/images/save.go13
-rw-r--r--cmd/podman/images/scp.go302
-rw-r--r--cmd/podman/images/scp_test.go46
-rw-r--r--cmd/podman/images/scp_utils.go88
-rw-r--r--cmd/podman/images/search.go13
-rw-r--r--cmd/podman/images/sign.go4
-rw-r--r--cmd/podman/images/trust_set.go12
-rw-r--r--cmd/podman/images/unmount.go2
-rw-r--r--cmd/podman/images/utils_linux.go4
-rw-r--r--cmd/podman/inspect/inspect.go31
-rw-r--r--cmd/podman/machine/info.go182
-rw-r--r--cmd/podman/machine/init.go28
-rw-r--r--cmd/podman/machine/inspect.go1
-rw-r--r--cmd/podman/machine/list.go52
-rw-r--r--cmd/podman/machine/machine.go20
-rw-r--r--cmd/podman/machine/machine_unix.go11
-rw-r--r--cmd/podman/machine/machine_windows.go9
-rw-r--r--cmd/podman/machine/rm.go1
-rw-r--r--cmd/podman/machine/set.go1
-rw-r--r--cmd/podman/machine/ssh.go17
-rw-r--r--cmd/podman/machine/start.go6
-rw-r--r--cmd/podman/machine/stop.go1
-rw-r--r--cmd/podman/manifest/push.go6
-rw-r--r--cmd/podman/manifest/remove.go3
-rw-r--r--cmd/podman/networks/create.go10
-rw-r--r--cmd/podman/networks/reload.go2
-rw-r--r--cmd/podman/networks/rm.go12
-rw-r--r--cmd/podman/parse/filters.go5
-rw-r--r--cmd/podman/parse/net.go50
-rw-r--r--cmd/podman/parse/net_test.go2
-rw-r--r--cmd/podman/play/kube.go12
-rw-r--r--cmd/podman/pods/clone.go92
-rw-r--r--cmd/podman/pods/create.go40
-rw-r--r--cmd/podman/pods/inspect.go6
-rw-r--r--cmd/podman/pods/kill.go2
-rw-r--r--cmd/podman/pods/logs.go9
-rw-r--r--cmd/podman/pods/pause.go2
-rw-r--r--cmd/podman/pods/ps.go13
-rw-r--r--cmd/podman/pods/restart.go2
-rw-r--r--cmd/podman/pods/rm.go10
-rw-r--r--cmd/podman/pods/start.go2
-rw-r--r--cmd/podman/pods/stop.go2
-rw-r--r--cmd/podman/pods/top.go4
-rw-r--r--cmd/podman/pods/unpause.go8
-rw-r--r--cmd/podman/registry/config.go9
-rw-r--r--cmd/podman/root.go44
-rw-r--r--cmd/podman/root_test.go4
-rw-r--r--cmd/podman/secrets/create.go4
-rw-r--r--cmd/podman/secrets/inspect.go3
-rw-r--r--cmd/podman/secrets/list.go4
-rw-r--r--cmd/podman/system/connection/add.go125
-rw-r--r--cmd/podman/system/connection/remove.go3
-rw-r--r--cmd/podman/system/connection/shared.go27
-rw-r--r--cmd/podman/system/df.go50
-rw-r--r--cmd/podman/system/dial_stdio.go12
-rw-r--r--cmd/podman/system/prune.go6
-rw-r--r--cmd/podman/system/reset.go14
-rw-r--r--cmd/podman/system/service_abi.go38
-rw-r--r--cmd/podman/system/unshare.go27
-rw-r--r--cmd/podman/utils/error.go32
-rw-r--r--cmd/podman/utils/signals_linux.go15
-rw-r--r--cmd/podman/utils/signals_windows.go15
-rw-r--r--cmd/podman/utils/utils.go19
-rw-r--r--cmd/podman/validate/args.go113
-rw-r--r--cmd/podman/volumes/create.go9
-rw-r--r--cmd/podman/volumes/export.go8
-rw-r--r--cmd/podman/volumes/import.go9
-rw-r--r--cmd/podman/volumes/inspect.go3
-rw-r--r--cmd/podman/volumes/list.go4
-rw-r--r--cmd/podman/volumes/reload.go52
-rw-r--r--cmd/podman/volumes/rm.go12
-rw-r--r--cmd/rootlessport/main.go14
-rw-r--r--cmd/rootlessport/wsl_test.go2
-rw-r--r--cmd/winpath/main.go20
129 files changed, 1507 insertions, 1909 deletions
diff --git a/cmd/podman-mac-helper/install.go b/cmd/podman-mac-helper/install.go
index a1b99e66c..713bdfcdf 100644
--- a/cmd/podman-mac-helper/install.go
+++ b/cmd/podman-mac-helper/install.go
@@ -5,6 +5,7 @@ package main
import (
"bytes"
+ "errors"
"fmt"
"io"
"io/fs"
@@ -14,7 +15,6 @@ import (
"syscall"
"text/template"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -111,7 +111,7 @@ func install(cmd *cobra.Command, args []string) error {
file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_EXCL, rw_r_r)
if err != nil {
- return errors.Wrap(err, "error creating helper plist file")
+ return fmt.Errorf("creating helper plist file: %w", err)
}
defer file.Close()
_, err = buf.WriteTo(file)
@@ -120,7 +120,7 @@ func install(cmd *cobra.Command, args []string) error {
}
if err = runDetectErr("launchctl", "load", fileName); err != nil {
- return errors.Wrap(err, "launchctl failed loading service")
+ return fmt.Errorf("launchctl failed loading service: %w", err)
}
return nil
@@ -133,13 +133,13 @@ func restrictRecursive(targetDir string, until string) error {
return err
}
if info.Mode()&fs.ModeSymlink != 0 {
- return errors.Errorf("symlinks not allowed in helper paths (remove them and rerun): %s", targetDir)
+ return fmt.Errorf("symlinks not allowed in helper paths (remove them and rerun): %s", targetDir)
}
if err = os.Chown(targetDir, 0, 0); err != nil {
- return errors.Wrap(err, "could not update ownership of helper path")
+ return fmt.Errorf("could not update ownership of helper path: %w", err)
}
if err = os.Chmod(targetDir, rwx_rx_rx|fs.ModeSticky); err != nil {
- return errors.Wrap(err, "could not update permissions of helper path")
+ return fmt.Errorf("could not update permissions of helper path: %w", err)
}
targetDir = filepath.Dir(targetDir)
}
@@ -162,7 +162,7 @@ func verifyRootDeep(path string) error {
stat := info.Sys().(*syscall.Stat_t)
if stat.Uid != 0 {
- return errors.Errorf("installation target path must be solely owned by root: %s is not", current)
+ return fmt.Errorf("installation target path must be solely owned by root: %s is not", current)
}
if info.Mode()&fs.ModeSymlink != 0 {
@@ -193,7 +193,7 @@ func verifyRootDeep(path string) error {
func installExecutable(user string) (string, error) {
// Since the installed executable runs as root, as a precaution verify root ownership of
- // the entire installation path, and utilize sticky + read only perms for the helper path
+ // the entire installation path, and utilize sticky + read-only perms for the helper path
// suffix. The goal is to help users harden against privilege escalation from loose
// filesystem permissions.
//
@@ -206,7 +206,7 @@ func installExecutable(user string) (string, error) {
targetDir := filepath.Join(installPrefix, "podman", "helper", user)
if err := os.MkdirAll(targetDir, rwx_rx_rx); err != nil {
- return "", errors.Wrap(err, "could not create helper directory structure")
+ return "", fmt.Errorf("could not create helper directory structure: %w", err)
}
// Correct any incorrect perms on previously existing directories and verify no symlinks
diff --git a/cmd/podman-mac-helper/main.go b/cmd/podman-mac-helper/main.go
index 8d995519f..937cb8433 100644
--- a/cmd/podman-mac-helper/main.go
+++ b/cmd/podman-mac-helper/main.go
@@ -4,6 +4,7 @@
package main
import (
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -13,7 +14,6 @@ import (
"strconv"
"strings"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -73,7 +73,7 @@ func getUserInfo(name string) (string, string, string, error) {
entry := readCapped(output)
elements := strings.Split(entry, ":")
if len(elements) < 9 || elements[0] != name {
- return "", "", "", errors.New("Could not lookup user")
+ return "", "", "", errors.New("Could not look up user")
}
return elements[0], elements[2], elements[8], nil
@@ -90,14 +90,14 @@ func getUser() (string, string, string, error) {
_, uid, home, err := getUserInfo(name)
if err != nil {
- return "", "", "", fmt.Errorf("could not lookup user: %s", name)
+ return "", "", "", fmt.Errorf("could not look up user: %s", name)
}
id, err := strconv.Atoi(uid)
if err != nil {
return "", "", "", fmt.Errorf("invalid uid for user: %s", name)
}
if id == 0 {
- return "", "", "", fmt.Errorf("unexpected root user")
+ return "", "", "", errors.New("unexpected root user")
}
return name, uid, home, nil
diff --git a/cmd/podman-mac-helper/uninstall.go b/cmd/podman-mac-helper/uninstall.go
index f72d0efd1..0c21b27e9 100644
--- a/cmd/podman-mac-helper/uninstall.go
+++ b/cmd/podman-mac-helper/uninstall.go
@@ -9,7 +9,6 @@ import (
"os/exec"
"path/filepath"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -48,13 +47,13 @@ func uninstall(cmd *cobra.Command, args []string) error {
if err := os.Remove(fileName); err != nil {
if !os.IsNotExist(err) {
- return errors.Errorf("could not remove plist file: %s", fileName)
+ return fmt.Errorf("could not remove plist file: %s", fileName)
}
}
helperPath := filepath.Join(installPrefix, "podman", "helper", userName)
if err := os.RemoveAll(helperPath); err != nil {
- return errors.Errorf("could not remove helper binary path: %s", helperPath)
+ return fmt.Errorf("could not remove helper binary path: %s", helperPath)
}
return nil
}
diff --git a/cmd/podman/auto-update.go b/cmd/podman/auto-update.go
index 1dc29530e..88ef0ec88 100644
--- a/cmd/podman/auto-update.go
+++ b/cmd/podman/auto-update.go
@@ -13,7 +13,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/errorhandling"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -63,7 +62,7 @@ func init() {
func autoUpdate(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
// Backwards compat. System tests expect this error string.
- return errors.Errorf("`%s` takes no arguments", cmd.CommandPath())
+ return fmt.Errorf("`%s` takes no arguments", cmd.CommandPath())
}
allReports, failures := registry.ContainerEngine().AutoUpdate(registry.GetContext(), autoUpdateOptions.AutoUpdateOptions)
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index 58dff3578..6e6c33f9b 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -4,9 +4,13 @@ import (
"bufio"
"fmt"
"os"
+ "path"
"reflect"
+ "strconv"
"strings"
+ "unicode"
+ libimageDefine "github.com/containers/common/libimage/define"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/pkg/sysregistriesv2"
@@ -15,8 +19,10 @@ import (
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
+ "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"
)
@@ -65,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
}
@@ -278,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
@@ -430,6 +520,16 @@ func AutocompletePodsRunning(cmd *cobra.Command, args []string, toComplete strin
return getPods(cmd, toComplete, completeDefault, "running", "degraded")
}
+// AutoCompletePodsPause - Autocomplete only paused pod names
+// When a pod has a few containers paused, that ends up in degraded state
+// So autocomplete degraded pod names as well
+func AutoCompletePodsPause(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ if !validCurrentCmdLine(cmd, args, toComplete) {
+ return nil, cobra.ShellCompDirectiveNoFileComp
+ }
+ return getPods(cmd, toComplete, completeDefault, "paused", "degraded")
+}
+
// AutocompleteForKube - Autocomplete all Podman objects supported by kube generate.
func AutocompleteForKube(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
@@ -495,6 +595,11 @@ func AutocompleteImages(cmd *cobra.Command, args []string, toComplete string) ([
return getImages(cmd, toComplete)
}
+// AutocompleteImageSearchFilters - Autocomplate `search --filter`.
+func AutocompleteImageSearchFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ return libimageDefine.SearchFilters, cobra.ShellCompDirectiveNoFileComp
+}
+
// AutocompletePodExitPolicy - Autocomplete pod exit policy.
func AutocompletePodExitPolicy(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return config.PodExitPolicies, cobra.ShellCompDirectiveNoFileComp
@@ -514,8 +619,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.
@@ -563,14 +692,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
@@ -594,7 +749,9 @@ func AutocompleteRunlabelCommand(cmd *cobra.Command, args []string, toComplete s
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 0 {
- // FIXME: What labels can we recommend here?
+ // This is unfortunate because the argument order is label followed by image.
+ // If it would be the other way around we could inspect the first arg and get
+ // all labels from it to suggest them.
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 1 {
@@ -799,8 +956,7 @@ func AutocompleteLogDriver(cmd *cobra.Command, args []string, toComplete string)
// AutocompleteLogOpt - Autocomplete log-opt options.
// -> "path=", "tag="
func AutocompleteLogOpt(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- // FIXME: are these the only one? the man page states these but in the current shell completion they are more options
- logOptions := []string{"path=", "tag="}
+ logOptions := []string{"path=", "tag=", "max-size="}
if strings.HasPrefix(toComplete, "path=") {
return nil, cobra.ShellCompDirectiveDefault
}
@@ -810,7 +966,7 @@ func AutocompleteLogOpt(cmd *cobra.Command, args []string, toComplete string) ([
// AutocompletePullOption - Autocomplete pull options for create and run command.
// -> "always", "missing", "never"
func AutocompletePullOption(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- pullOptions := []string{"always", "missing", "never"}
+ pullOptions := []string{"always", "missing", "never", "newer"}
return pullOptions, cobra.ShellCompDirectiveNoFileComp
}
@@ -839,10 +995,26 @@ func AutocompleteSecurityOption(cmd *cobra.Command, args []string, toComplete st
}
// AutocompleteStopSignal - Autocomplete stop signal options.
-// -> "SIGHUP", "SIGINT", "SIGKILL", "SIGTERM"
+// Autocompletes signals both lower or uppercase depending on the user input.
func AutocompleteStopSignal(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- // FIXME: add more/different signals?
- stopSignals := []string{"SIGHUP", "SIGINT", "SIGKILL", "SIGTERM"}
+ // convertCase will convert a string to lowercase only if the user input is lowercase
+ convertCase := func(s string) string { return s }
+ if len(toComplete) > 0 && unicode.IsLower(rune(toComplete[0])) {
+ convertCase = strings.ToLower
+ }
+
+ prefix := ""
+ // if input starts with "SI" we have to add SIG in front
+ // since the signal map does not have this prefix but the option
+ // allows signals with and without SIG prefix
+ if strings.HasPrefix(toComplete, convertCase("SI")) {
+ prefix = "SIG"
+ }
+
+ stopSignals := make([]string, 0, len(signal.SignalMap))
+ for sig := range signal.SignalMap {
+ stopSignals = append(stopSignals, convertCase(prefix+sig))
+ }
return stopSignals, cobra.ShellCompDirectiveNoFileComp
}
@@ -963,9 +1135,22 @@ func AutocompleteNetworkFlag(cmd *cobra.Command, args []string, toComplete strin
return append(networks, suggestions...), dir
}
+type formatSuggestion struct {
+ fieldname string
+ suffix string
+}
+
+func convertFormatSuggestions(suggestions []formatSuggestion) []string {
+ completions := make([]string, 0, len(suggestions))
+ for _, f := range suggestions {
+ completions = append(completions, f.fieldname+f.suffix)
+ }
+ return completions
+}
+
// AutocompleteFormat - Autocomplete json or a given struct to use for a go template.
// The input can be nil, In this case only json will be autocompleted.
-// This function will only work for structs other types are not supported.
+// This function will only work for pointer to structs other types are not supported.
// When "{{." is typed the field and method names of the given struct will be completed.
// This also works recursive for nested structs.
func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
@@ -994,6 +1179,12 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
// split this into it struct field names
fields := strings.Split(field[len(field)-1], ".")
f := reflect.ValueOf(o)
+ if f.Kind() != reflect.Ptr {
+ // We panic here to make sure that all callers pass the value by reference.
+ // If someone passes a by value then all podman commands will panic since
+ // this function is run at init time.
+ panic("AutocompleteFormat: passed value must be a pointer to a struct")
+ }
for i := 1; i < len(fields); i++ {
// last field get all names to suggest
if i == len(fields)-1 {
@@ -1002,61 +1193,83 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
toCompArr := strings.Split(toComplete, ".")
toCompArr[len(toCompArr)-1] = ""
toComplete = strings.Join(toCompArr, ".")
- return prefixSlice(toComplete, suggestions), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
+ return prefixSlice(toComplete, convertFormatSuggestions(suggestions)), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
- val := getActualStructType(f)
- if val == nil {
- // no struct return nothing to complete
+ // first follow pointer and create element when it is nil
+ f = actualReflectValue(f)
+ switch f.Kind() {
+ case reflect.Struct:
+ for j := 0; j < f.NumField(); j++ {
+ field := f.Type().Field(j)
+ // ok this is a bit weird but when we have an embedded nil struct
+ // calling FieldByName on a name which is present on this struct will panic
+ // Therefore we have to init them (non nil ptr), https://github.com/containers/podman/issues/14223
+ if field.Anonymous && f.Field(j).Type().Kind() == reflect.Ptr {
+ f.Field(j).Set(reflect.New(f.Field(j).Type().Elem()))
+ }
+ }
+ // set the next struct field
+ f = f.FieldByName(fields[i])
+ case reflect.Map:
+ rtype := f.Type().Elem()
+ if rtype.Kind() == reflect.Ptr {
+ rtype = rtype.Elem()
+ }
+ f = reflect.New(rtype)
+ case reflect.Func:
+ if f.Type().NumOut() != 1 {
+ // unsupported type return nothing
+ return nil, cobra.ShellCompDirectiveNoFileComp
+ }
+ f = reflect.New(f.Type().Out(0))
+ default:
+ // unsupported type return nothing
return nil, cobra.ShellCompDirectiveNoFileComp
}
- f = *val
-
- // set the next struct field
- f = f.FieldByName(fields[i])
}
return nil, cobra.ShellCompDirectiveNoFileComp
}
}
-// getActualStructType take the value and check if it is a struct,
+// actualReflectValue takes the value,
// if it is pointer it will dereference it and when it is nil,
-// it will create a new value from it to get the actual struct
-// returns nil when type is not a struct
-func getActualStructType(f reflect.Value) *reflect.Value {
+// it will create a new value from it
+func actualReflectValue(f reflect.Value) reflect.Value {
// follow the pointer first
if f.Kind() == reflect.Ptr {
// if the pointer is nil we create a new value from the elements type
- // this allows us to follow nil pointers and get the actual struct fields
+ // this allows us to follow nil pointers and get the actual type
if f.IsNil() {
f = reflect.New(f.Type().Elem())
}
f = f.Elem()
}
- // we only support structs
- if f.Kind() != reflect.Struct {
- return nil
- }
- return &f
+ return f
}
// getStructFields reads all struct field names and method names and returns them.
-func getStructFields(f reflect.Value, prefix string) []string {
- var suggestions []string
+func getStructFields(f reflect.Value, prefix string) []formatSuggestion {
+ var suggestions []formatSuggestion
if f.IsValid() {
suggestions = append(suggestions, getMethodNames(f, prefix)...)
}
- val := getActualStructType(f)
- if val == nil {
- // no struct return nothing to complete
+ f = actualReflectValue(f)
+ // we only support structs
+ if f.Kind() != reflect.Struct {
return suggestions
}
- f = *val
+ var anonymous []formatSuggestion
// loop over all field names
for j := 0; j < f.NumField(); j++ {
field := f.Type().Field(j)
+ // check if struct field is not exported, templates only use exported fields
+ // PkgPath is always empty for exported fields
+ if field.PkgPath != "" {
+ continue
+ }
fname := field.Name
suffix := "}}"
kind := field.Type.Kind()
@@ -1065,27 +1278,63 @@ func getStructFields(f reflect.Value, prefix string) []string {
kind = field.Type.Elem().Kind()
}
// when we have a nested struct do not append braces instead append a dot
- if kind == reflect.Struct {
+ if kind == reflect.Struct || kind == reflect.Map {
suffix = "."
}
// if field is anonymous add the child fields as well
if field.Anonymous {
- suggestions = append(suggestions, getStructFields(f.Field(j), prefix)...)
- } else if strings.HasPrefix(fname, prefix) {
+ anonymous = append(anonymous, getStructFields(f.Field(j), prefix)...)
+ }
+ if strings.HasPrefix(fname, prefix) {
// add field name with suffix
- suggestions = append(suggestions, fname+suffix)
+ suggestions = append(suggestions, formatSuggestion{fieldname: fname, suffix: suffix})
+ }
+ }
+outer:
+ for _, ano := range anonymous {
+ // we should only add anonymous child fields if they are not already present.
+ for _, sug := range suggestions {
+ if ano.fieldname == sug.fieldname {
+ continue outer
+ }
}
+ suggestions = append(suggestions, ano)
}
return suggestions
}
-func getMethodNames(f reflect.Value, prefix string) []string {
- suggestions := make([]string, 0, f.NumMethod())
+func getMethodNames(f reflect.Value, prefix string) []formatSuggestion {
+ suggestions := make([]formatSuggestion, 0, f.NumMethod())
for j := 0; j < f.NumMethod(); j++ {
- fname := f.Type().Method(j).Name
+ method := f.Type().Method(j)
+ // in a template we can only run functions with one return value
+ if method.Func.Type().NumOut() != 1 {
+ continue
+ }
+ // when we have a nested struct do not append braces instead append a dot
+ kind := method.Func.Type().Out(0).Kind()
+ suffix := "}}"
+ if kind == reflect.Struct || kind == reflect.Map {
+ suffix = "."
+ }
+ // From a template users POV it is not important when the use a struct field or method.
+ // They only notice the difference when the function requires arguments.
+ // So lets be nice and let the user know that this method requires arguments via the help text.
+ // Note since this is actually a method on a type the first argument is always fix so we should skip it.
+ num := method.Func.Type().NumIn() - 1
+ if num > 0 {
+ // everything after tab will the completion scripts show as help when enabled
+ // overwrite the suffix because it expects the args
+ suffix = "\tThis is a function and requires " + strconv.Itoa(num) + " argument"
+ if num > 1 {
+ // add plural s
+ suffix += "s"
+ }
+ }
+ fname := method.Name
if strings.HasPrefix(fname, prefix) {
// add method name with closing braces
- suggestions = append(suggestions, fname+"}}")
+ suggestions = append(suggestions, formatSuggestion{fieldname: fname, suffix: suffix})
}
}
return suggestions
@@ -1368,8 +1617,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 13f45a662..d8be48ad7 100644
--- a/cmd/podman/common/completion_test.go
+++ b/cmd/podman/common/completion_test.go
@@ -14,11 +14,29 @@ type Car struct {
HP *int
Displacement int
}
- Extras map[string]string
+ Extras map[string]Extra
+ // also ensure it will work with pointers
+ Extras2 map[string]*Extra
+}
+
+type Extra struct {
+ Name1 string
+ Name2 string
}
type Anonymous struct {
Hello string
+ // The name should match the testStruct Name below. This is used to make
+ // sure the logic uses the actual struct fields before the embedded ones.
+ Name struct {
+ Suffix string
+ Prefix string
+ }
+}
+
+// The name should match the testStruct Age name below.
+func (a Anonymous) Age() int {
+ return 0
}
func (c Car) Type() string {
@@ -31,6 +49,20 @@ func (c *Car) Color() string {
return ""
}
+// This is for reflect testing required.
+//nolint:unused
+func (c Car) internal() int {
+ return 0
+}
+
+func (c Car) TwoOut() (string, string) {
+ return "", ""
+}
+
+func (c Car) Struct() Car {
+ return Car{}
+}
+
func TestAutocompleteFormat(t *testing.T) {
testStruct := struct {
Name string
@@ -38,10 +70,10 @@ func TestAutocompleteFormat(t *testing.T) {
Car *Car
Car2 *Car
*Anonymous
+ private int
}{}
testStruct.Car = &Car{}
- testStruct.Car.Extras = map[string]string{"test": "1"}
tests := []struct {
name string
@@ -76,17 +108,17 @@ func TestAutocompleteFormat(t *testing.T) {
{
"invalid completion",
"{{ ..",
- nil,
+ []string{},
},
{
"fist level struct field name",
"{{.",
- []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Car2.", "{{.Hello}}"},
+ []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Car2.", "{{.Anonymous.", "{{.Hello}}"},
},
{
"fist level struct field name",
"{{ .",
- []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Car2.", "{{ .Hello}}"},
+ []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Car2.", "{{ .Anonymous.", "{{ .Hello}}"},
},
{
"fist level struct field name",
@@ -96,7 +128,7 @@ func TestAutocompleteFormat(t *testing.T) {
{
"second level struct field name",
"{{ .Car.",
- []string{"{{ .Car.Color}}", "{{ .Car.Type}}", "{{ .Car.Brand}}", "{{ .Car.Stats.", "{{ .Car.Extras}}"},
+ []string{"{{ .Car.Color}}", "{{ .Car.Struct.", "{{ .Car.Type}}", "{{ .Car.Brand}}", "{{ .Car.Stats.", "{{ .Car.Extras.", "{{ .Car.Extras2."},
},
{
"second level struct field name",
@@ -106,7 +138,7 @@ func TestAutocompleteFormat(t *testing.T) {
{
"second level nil struct field name",
"{{ .Car2.",
- []string{"{{ .Car2.Color}}", "{{ .Car2.Type}}", "{{ .Car2.Brand}}", "{{ .Car2.Stats.", "{{ .Car2.Extras}}"},
+ []string{"{{ .Car2.Color}}", "{{ .Car2.Struct.", "{{ .Car2.Type}}", "{{ .Car2.Brand}}", "{{ .Car2.Stats.", "{{ .Car2.Extras.", "{{ .Car2.Extras2."},
},
{
"three level struct field name",
@@ -126,28 +158,44 @@ func TestAutocompleteFormat(t *testing.T) {
{
"invalid field name",
"{{ .Ca.B",
- nil,
+ []string{},
},
{
"map key names don't work",
"{{ .Car.Extras.",
- nil,
+ []string{},
+ },
+ {
+ "map values work",
+ "{{ .Car.Extras.somekey.",
+ []string{"{{ .Car.Extras.somekey.Name1}}", "{{ .Car.Extras.somekey.Name2}}"},
+ },
+ {
+ "map values work with ptr",
+ "{{ .Car.Extras2.somekey.",
+ []string{"{{ .Car.Extras2.somekey.Name1}}", "{{ .Car.Extras2.somekey.Name2}}"},
},
{
"two variables struct field name",
"{{ .Car.Brand }} {{ .Car.",
- []string{"{{ .Car.Brand }} {{ .Car.Color}}", "{{ .Car.Brand }} {{ .Car.Type}}", "{{ .Car.Brand }} {{ .Car.Brand}}",
- "{{ .Car.Brand }} {{ .Car.Stats.", "{{ .Car.Brand }} {{ .Car.Extras}}"},
+ []string{"{{ .Car.Brand }} {{ .Car.Color}}", "{{ .Car.Brand }} {{ .Car.Struct.", "{{ .Car.Brand }} {{ .Car.Type}}",
+ "{{ .Car.Brand }} {{ .Car.Brand}}", "{{ .Car.Brand }} {{ .Car.Stats.", "{{ .Car.Brand }} {{ .Car.Extras.",
+ "{{ .Car.Brand }} {{ .Car.Extras2."},
},
{
"only dot without variable",
".",
nil,
},
+ {
+ "access embedded nil struct field",
+ "{{.Hello.",
+ []string{},
+ },
}
for _, test := range tests {
- completion, directive := common.AutocompleteFormat(testStruct)(nil, nil, test.toComplete)
+ completion, directive := common.AutocompleteFormat(&testStruct)(nil, nil, test.toComplete)
// directive should always be greater than ShellCompDirectiveNoFileComp
assert.GreaterOrEqual(t, directive, cobra.ShellCompDirectiveNoFileComp, "unexpected ShellCompDirective")
assert.Equal(t, test.expected, completion, test.name)
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index d28becc8a..923d0517f 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,8 +389,8 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
pullFlagName := "pull"
createFlags.StringVar(
&cf.Pull,
- pullFlagName, policy(),
- `Pull image before creating ("always"|"missing"|"never")`,
+ pullFlagName, cf.Pull,
+ `Pull image policy`,
)
_ = 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)
@@ -552,13 +544,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag)
- utsFlagName := "uts"
- createFlags.String(
- utsFlagName, "",
- "UTS namespace to use",
- )
- _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace)
-
mountFlagName := "mount"
createFlags.StringArrayVar(
&cf.Mount,
@@ -578,7 +563,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 +614,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,
@@ -685,6 +677,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(usernsFlagName, AutocompleteUserNamespace)
+ utsFlagName := "uts"
+ createFlags.StringVar(
+ &cf.UTS,
+ utsFlagName, "",
+ "UTS namespace to use",
+ )
+ _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace)
+
cgroupParentFlagName := "cgroup-parent"
createFlags.StringVar(
&cf.CgroupParent,
@@ -770,7 +770,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)
@@ -864,14 +864,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(cpusetMemsFlagName, completion.AutocompleteNone)
- memoryFlagName := "memory"
- createFlags.StringVarP(
- &cf.Memory,
- memoryFlagName, "m", "",
- "Memory limit "+sizeWithUnitFormat,
- )
- _ = cmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone)
-
memoryReservationFlagName := "memory-reservation"
createFlags.StringVar(
&cf.MemoryReservation,
@@ -891,12 +883,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,
@@ -912,4 +905,12 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
"CPUs in which to allow execution (0-3, 0,1)",
)
_ = cmd.RegisterFlagCompletionFunc(cpusetCpusFlagName, completion.AutocompleteNone)
+
+ memoryFlagName := "memory"
+ createFlags.StringVarP(
+ &cf.Memory,
+ memoryFlagName, "m", "",
+ "Memory limit "+sizeWithUnitFormat,
+ )
+ _ = cmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone)
}
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/common/diffChanges.go b/cmd/podman/common/diffChanges.go
index 99b5f1dcd..6d4c7154a 100644
--- a/cmd/podman/common/diffChanges.go
+++ b/cmd/podman/common/diffChanges.go
@@ -6,7 +6,6 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/storage/pkg/archive"
- "github.com/pkg/errors"
)
type ChangesReportJSON struct {
@@ -26,7 +25,7 @@ func ChangesToJSON(diffs *entities.DiffReport) error {
case archive.ChangeModify:
body.Changed = append(body.Changed, row.Path)
default:
- return errors.Errorf("output kind %q not recognized", row.Kind)
+ return fmt.Errorf("output kind %q not recognized", row.Kind)
}
}
diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go
index 9dfe81d62..e7aa265d1 100644
--- a/cmd/podman/common/netflags.go
+++ b/cmd/podman/common/netflags.go
@@ -1,6 +1,8 @@
package common
import (
+ "errors"
+ "fmt"
"net"
"github.com/containers/common/libnetwork/types"
@@ -10,7 +12,6 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/podman/v4/pkg/specgenutil"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -125,13 +126,13 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*enti
if d == "none" {
opts.UseImageResolvConf = true
if len(servers) > 1 {
- return nil, errors.Errorf("%s is not allowed to be specified with other DNS ip addresses", d)
+ return nil, fmt.Errorf("%s is not allowed to be specified with other DNS ip addresses", d)
}
break
}
dns := net.ParseIP(d)
if dns == nil {
- return nil, errors.Errorf("%s is not an ip address", d)
+ return nil, fmt.Errorf("%s is not an ip address", d)
}
opts.DNSServers = append(opts.DNSServers, dns)
}
@@ -154,7 +155,7 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*enti
for _, dom := range dnsSearches {
if dom == "." {
if len(dnsSearches) > 1 {
- return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
+ return nil, errors.New("cannot pass additional search domains when also specifying '.'")
}
continue
}
@@ -218,18 +219,18 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*enti
if ip != "" {
// if pod create --infra=false
if infra, err := flags.GetBool("infra"); err == nil && !infra {
- return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set --%s without infra container", ipFlagName)
+ return nil, fmt.Errorf("cannot set --%s without infra container: %w", ipFlagName, define.ErrInvalidArg)
}
staticIP := net.ParseIP(ip)
if staticIP == nil {
- return nil, errors.Errorf("%q is not an ip address", ip)
+ return nil, fmt.Errorf("%q is not an ip address", ip)
}
if !opts.Network.IsBridge() && !opts.Network.IsDefault() {
- return nil, errors.Wrapf(define.ErrInvalidArg, "--%s can only be set when the network mode is bridge", ipFlagName)
+ return nil, fmt.Errorf("--%s can only be set when the network mode is bridge: %w", ipFlagName, define.ErrInvalidArg)
}
if len(opts.Networks) != 1 {
- return nil, errors.Wrapf(define.ErrInvalidArg, "--%s can only be set for a single network", ipFlagName)
+ return nil, fmt.Errorf("--%s can only be set for a single network: %w", ipFlagName, define.ErrInvalidArg)
}
for name, netOpts := range opts.Networks {
netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
@@ -245,17 +246,17 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*enti
if len(m) > 0 {
// if pod create --infra=false
if infra, err := flags.GetBool("infra"); err == nil && !infra {
- return nil, errors.Wrap(define.ErrInvalidArg, "cannot set --mac without infra container")
+ return nil, fmt.Errorf("cannot set --mac without infra container: %w", define.ErrInvalidArg)
}
mac, err := net.ParseMAC(m)
if err != nil {
return nil, err
}
if !opts.Network.IsBridge() && !opts.Network.IsDefault() {
- return nil, errors.Wrap(define.ErrInvalidArg, "--mac-address can only be set when the network mode is bridge")
+ return nil, fmt.Errorf("--mac-address can only be set when the network mode is bridge: %w", define.ErrInvalidArg)
}
if len(opts.Networks) != 1 {
- return nil, errors.Wrap(define.ErrInvalidArg, "--mac-address can only be set for a single network")
+ return nil, fmt.Errorf("--mac-address can only be set for a single network: %w", define.ErrInvalidArg)
}
for name, netOpts := range opts.Networks {
netOpts.StaticMAC = types.HardwareAddr(mac)
@@ -270,10 +271,10 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*enti
if len(aliases) > 0 {
// if pod create --infra=false
if infra, err := flags.GetBool("infra"); err == nil && !infra {
- return nil, errors.Wrap(define.ErrInvalidArg, "cannot set --network-alias without infra container")
+ return nil, fmt.Errorf("cannot set --network-alias without infra container: %w", define.ErrInvalidArg)
}
if !opts.Network.IsBridge() && !opts.Network.IsDefault() {
- return nil, errors.Wrap(define.ErrInvalidArg, "--network-alias can only be set when the network mode is bridge")
+ return nil, fmt.Errorf("--network-alias can only be set when the network mode is bridge: %w", define.ErrInvalidArg)
}
for name, netOpts := range opts.Networks {
netOpts.Aliases = aliases
diff --git a/cmd/podman/containers/attach.go b/cmd/podman/containers/attach.go
index 1a05ea5c3..52adb1fcb 100644
--- a/cmd/podman/containers/attach.go
+++ b/cmd/podman/containers/attach.go
@@ -1,13 +1,13 @@
package containers
import (
+ "errors"
"os"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -70,7 +70,7 @@ func init() {
func attach(cmd *cobra.Command, args []string) error {
if len(args) > 1 || (len(args) == 0 && !attachOpts.Latest) {
- return errors.Errorf("attach requires the name or id of one running container or the latest flag")
+ return errors.New("attach requires the name or id of one running container or the latest flag")
}
var name string
diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go
index 40d689c4d..0eb0db394 100644
--- a/cmd/podman/containers/checkpoint.go
+++ b/cmd/podman/containers/checkpoint.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "errors"
"fmt"
"strings"
"time"
@@ -15,7 +16,6 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/storage/pkg/archive"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -31,7 +31,7 @@ var (
Long: checkpointDescription,
RunE: checkpoint,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainersRunning,
Example: `podman container checkpoint --keep ctrID
@@ -90,7 +90,7 @@ func checkpoint(cmd *cobra.Command, args []string) error {
podmanStart := time.Now()
if cmd.Flags().Changed("compress") {
if checkpointOptions.Export == "" {
- return errors.Errorf("--compress can only be used with --export")
+ return errors.New("--compress can only be used with --export")
}
compress, _ := cmd.Flags().GetString("compress")
switch strings.ToLower(compress) {
@@ -101,7 +101,7 @@ func checkpoint(cmd *cobra.Command, args []string) error {
case "zstd":
checkpointOptions.Compression = archive.Zstd
default:
- return errors.Errorf("Selected compression algorithm (%q) not supported. Please select one from: gzip, none, zstd", compress)
+ return fmt.Errorf("selected compression algorithm (%q) not supported. Please select one from: gzip, none, zstd", compress)
}
} else {
checkpointOptions.Compression = archive.Zstd
@@ -110,13 +110,13 @@ func checkpoint(cmd *cobra.Command, args []string) error {
return errors.New("checkpointing a container requires root")
}
if checkpointOptions.Export == "" && checkpointOptions.IgnoreRootFS {
- return errors.Errorf("--ignore-rootfs can only be used with --export")
+ return errors.New("--ignore-rootfs can only be used with --export")
}
if checkpointOptions.Export == "" && checkpointOptions.IgnoreVolumes {
- return errors.Errorf("--ignore-volumes can only be used with --export")
+ return errors.New("--ignore-volumes can only be used with --export")
}
if checkpointOptions.WithPrevious && checkpointOptions.PreCheckPoint {
- return errors.Errorf("--with-previous can not be used with --pre-checkpoint")
+ return errors.New("--with-previous can not be used with --pre-checkpoint")
}
if (checkpointOptions.WithPrevious || checkpointOptions.PreCheckPoint) && !criu.MemTrack() {
return errors.New("system (architecture/kernel/CRIU) does not support memory tracking")
diff --git a/cmd/podman/containers/cleanup.go b/cmd/podman/containers/cleanup.go
index aa2734607..c9a5cb28b 100644
--- a/cmd/podman/containers/cleanup.go
+++ b/cmd/podman/containers/cleanup.go
@@ -1,6 +1,7 @@
package containers
import (
+ "errors"
"fmt"
"github.com/containers/common/pkg/completion"
@@ -9,7 +10,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -23,11 +23,11 @@ var (
cleanupCommand = &cobra.Command{
Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
Use: "cleanup [options] CONTAINER [CONTAINER...]",
- Short: "Cleanup network and mountpoints of one or more containers",
+ Short: "Clean up network and mountpoints of one or more containers",
Long: cleanupDescription,
RunE: cleanup,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainersExited,
Example: `podman container cleanup --latest
@@ -65,11 +65,11 @@ func cleanup(cmd *cobra.Command, args []string) error {
if cleanupOptions.Exec != "" {
switch {
case cleanupOptions.All:
- return errors.Errorf("exec and all options conflict")
+ return errors.New("exec and all options conflict")
case len(args) > 1:
- return errors.Errorf("cannot use exec option when more than one container is given")
+ return errors.New("cannot use exec option when more than one container is given")
case cleanupOptions.RemoveImage:
- return errors.Errorf("exec and rmi options conflict")
+ return errors.New("exec and rmi options conflict")
}
}
diff --git a/cmd/podman/containers/clone.go b/cmd/podman/containers/clone.go
index 6912da1fc..9881a791c 100644
--- a/cmd/podman/containers/clone.go
+++ b/cmd/podman/containers/clone.go
@@ -7,7 +7,6 @@ import (
"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"
)
@@ -41,6 +40,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() {
@@ -55,7 +55,7 @@ func init() {
func clone(cmd *cobra.Command, args []string) error {
switch len(args) {
case 0:
- return errors.Wrapf(define.ErrInvalidArg, "must specify at least 1 argument")
+ return fmt.Errorf("must specify at least 1 argument: %w", define.ErrInvalidArg)
case 2:
ctrClone.CreateOpts.Name = args[1]
case 3:
@@ -63,7 +63,7 @@ func clone(cmd *cobra.Command, args []string) error {
ctrClone.Image = args[2]
if !cliVals.RootFS {
rawImageName := args[0]
- name, err := PullImage(ctrClone.Image, ctrClone.CreateOpts)
+ name, err := PullImage(ctrClone.Image, &ctrClone.CreateOpts)
if err != nil {
return err
}
@@ -72,7 +72,7 @@ func clone(cmd *cobra.Command, args []string) error {
}
}
if ctrClone.Force && !ctrClone.Destroy {
- return errors.Wrapf(define.ErrInvalidArg, "cannot set --force without --destroy")
+ return fmt.Errorf("cannot set --force without --destroy: %w", define.ErrInvalidArg)
}
ctrClone.ID = args[0]
diff --git a/cmd/podman/containers/commit.go b/cmd/podman/containers/commit.go
index e0cadd5b0..fb6dccad4 100644
--- a/cmd/podman/containers/commit.go
+++ b/cmd/podman/containers/commit.go
@@ -11,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -109,7 +108,7 @@ func commit(cmd *cobra.Command, args []string) error {
}
if len(iidFile) > 0 {
if err = ioutil.WriteFile(iidFile, []byte(response.Id), 0644); err != nil {
- return errors.Wrap(err, "failed to write image ID")
+ return fmt.Errorf("failed to write image ID: %w", err)
}
}
fmt.Println(response.Id)
diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go
index eb18dfce4..c38cba66e 100644
--- a/cmd/podman/containers/cp.go
+++ b/cmd/podman/containers/cp.go
@@ -1,6 +1,7 @@
package containers
import (
+ "fmt"
"io"
"io/ioutil"
"os"
@@ -9,6 +10,8 @@ import (
"strconv"
"strings"
+ "errors"
+
buildahCopiah "github.com/containers/buildah/copier"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
@@ -17,7 +20,6 @@ import (
"github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/idtools"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -55,10 +57,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")
}
@@ -99,7 +104,7 @@ func containerMustExist(container string) error {
return err
}
if !exists.Value {
- return errors.Errorf("container %q does not exist", container)
+ return fmt.Errorf("container %q does not exist", container)
}
return nil
}
@@ -128,7 +133,7 @@ func copyContainerToContainer(sourceContainer string, sourcePath string, destCon
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)
+ return fmt.Errorf("%q could not be found on container %s: %w", sourcePath, sourceContainer, err)
}
destContainerBaseName, destContainerInfo, destResolvedToParentDir, err := resolvePathOnDestinationContainer(destContainer, destPath, false)
@@ -167,7 +172,7 @@ func copyContainerToContainer(sourceContainer string, sourcePath string, destCon
return err
}
if err := copyFunc(); err != nil {
- return errors.Wrap(err, "error copying from container")
+ return fmt.Errorf("error copying from container: %w", err)
}
return nil
}
@@ -175,7 +180,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.
@@ -187,7 +192,7 @@ func copyContainerToContainer(sourceContainer string, sourcePath string, destCon
return err
}
if err := copyFunc(); err != nil {
- return errors.Wrap(err, "error copying to container")
+ return fmt.Errorf("error copying to container: %w", err)
}
return nil
}
@@ -209,7 +214,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
containerInfo, err := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
if err != nil {
- return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
+ return fmt.Errorf("%q could not be found on container %s: %w", containerPath, container, err)
}
var hostBaseName string
@@ -217,13 +222,13 @@ func copyFromContainer(container string, containerPath string, hostPath string)
hostInfo, hostInfoErr := copy.ResolveHostPath(hostPath)
if hostInfoErr != nil {
if strings.HasSuffix(hostPath, "/") {
- return errors.Wrapf(hostInfoErr, "%q could not be found on the host", hostPath)
+ return fmt.Errorf("%q could not be found on the host: %w", hostPath, hostInfoErr)
}
// If it doesn't exist, then let's have a look at the parent dir.
parentDir := filepath.Dir(hostPath)
hostInfo, err = copy.ResolveHostPath(parentDir)
if err != nil {
- return errors.Wrapf(hostInfoErr, "%q could not be found on the host", hostPath)
+ return fmt.Errorf("%q could not be found on the host: %w", hostPath, hostInfoErr)
}
// If the specified path does not exist, we need to assume that
// it'll be created while copying. Hence, we use it as the
@@ -238,7 +243,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
if !isStdout {
if err := validateFileInfo(hostInfo); err != nil {
- return errors.Wrap(err, "invalid destination")
+ return fmt.Errorf("invalid destination: %w", err)
}
}
@@ -294,9 +299,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
@@ -308,7 +315,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
dir = filepath.Dir(dir)
}
if err := buildahCopiah.Put(dir, "", putOptions, reader); err != nil {
- return errors.Wrap(err, "error copying to host")
+ return fmt.Errorf("error copying to host: %w", err)
}
return nil
}
@@ -320,7 +327,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
return err
}
if err := copyFunc(); err != nil {
- return errors.Wrap(err, "error copying from container")
+ return fmt.Errorf("error copying from container: %w", err)
}
return nil
}
@@ -342,7 +349,7 @@ func copyToContainer(container string, containerPath string, hostPath string) er
// Make sure that host path exists.
hostInfo, err := copy.ResolveHostPath(hostPath)
if err != nil {
- return errors.Wrapf(err, "%q could not be found on the host", hostPath)
+ return fmt.Errorf("%q could not be found on the host: %w", hostPath, err)
}
containerBaseName, containerInfo, containerResolvedToParentDir, err := resolvePathOnDestinationContainer(container, containerPath, isStdin)
@@ -417,7 +424,7 @@ func copyToContainer(container string, containerPath string, hostPath string) er
getOptions.Rename = map[string]string{filepath.Base(hostTarget): containerBaseName}
}
if err := buildahCopiah.Get("/", "", getOptions, []string{hostTarget}, writer); err != nil {
- return errors.Wrap(err, "error copying from host")
+ return fmt.Errorf("error copying from host: %w", err)
}
return nil
}
@@ -429,12 +436,12 @@ 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
}
if err := copyFunc(); err != nil {
- return errors.Wrap(err, "error copying to container")
+ return fmt.Errorf("error copying to container: %w", err)
}
return nil
}
@@ -449,11 +456,11 @@ 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, "/") {
- err = errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
+ err = fmt.Errorf("%q could not be found on container %s: %w", containerPath, container, err)
return
}
if isStdin {
@@ -474,13 +481,13 @@ func resolvePathOnDestinationContainer(container string, containerPath string, i
parentDir, err := containerParentDir(container, path)
if err != nil {
- err = errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container)
+ err = fmt.Errorf("could not determine parent dir of %q on container %s: %w", path, container, err)
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)
+ err = fmt.Errorf("%q could not be found on container %s: %w", containerPath, container, err)
return
}
@@ -500,7 +507,7 @@ func containerParentDir(container string, containerPath string) (string, error)
return "", err
}
if len(inspectData) != 1 {
- return "", errors.Errorf("inspecting container %q: expected 1 data item but got %d", container, len(inspectData))
+ return "", fmt.Errorf("inspecting container %q: expected 1 data item but got %d", container, len(inspectData))
}
workDir := filepath.Join("/", inspectData[0].Config.WorkingDir)
workDir = filepath.Join(workDir, containerPath)
@@ -513,5 +520,5 @@ func validateFileInfo(info *copy.FileInfo) error {
if info.Mode.IsDir() || info.Mode.IsRegular() {
return nil
}
- return errors.Errorf("%q must be a directory or a regular file", info.LinkTarget)
+ return fmt.Errorf("%q must be a directory or a regular file", info.LinkTarget)
}
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index 29e138e30..7d0f4d9ae 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "errors"
"fmt"
"os"
"strconv"
@@ -9,6 +10,7 @@ import (
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
+ cutil "github.com/containers/common/pkg/util"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/cmd/podman/common"
@@ -20,7 +22,6 @@ import (
"github.com/containers/podman/v4/pkg/specgenutil"
"github.com/containers/podman/v4/pkg/util"
"github.com/mattn/go-isatty"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -70,6 +71,7 @@ func createFlags(cmd *cobra.Command) {
)
flags.SetInterspersed(false)
+ common.DefineCreateDefaults(&cliVals)
common.DefineCreateFlags(cmd, &cliVals, false, false)
common.DefineNetFlags(cmd)
@@ -101,28 +103,37 @@ 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") {
return errors.New("must specify pod value with init-ctr")
}
- if !util.StringInSlice(initctr, []string{define.AlwaysInitContainer, define.OneShotInitContainer}) {
- return errors.Errorf("init-ctr value must be '%s' or '%s'", define.AlwaysInitContainer, define.OneShotInitContainer)
+ if !cutil.StringInSlice(initctr, []string{define.AlwaysInitContainer, define.OneShotInitContainer}) {
+ return fmt.Errorf("init-ctr value must be '%s' or '%s'", define.AlwaysInitContainer, define.OneShotInitContainer)
}
cliVals.InitContainerType = initctr
}
- cliVals, err = CreateInit(cmd, cliVals, false)
+ cliVals, err := CreateInit(cmd, cliVals, false)
if err != nil {
return err
}
@@ -130,7 +141,7 @@ func create(cmd *cobra.Command, args []string) error {
rawImageName := ""
if !cliVals.RootFS {
rawImageName = args[0]
- name, err := PullImage(args[0], cliVals)
+ name, err := PullImage(args[0], &cliVals)
if err != nil {
return err
}
@@ -206,17 +217,13 @@ 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")
+ return vals, errors.New("--cpu-period and --cpus cannot be set together")
}
if c.Flag("cpu-quota").Changed && c.Flag("cpus").Changed {
- return vals, errors.Errorf("--cpu-quota and --cpus cannot be set together")
+ return vals, errors.New("--cpu-quota and --cpus cannot be set together")
}
vals.IPC = c.Flag("ipc").Value.String()
- vals.UTS = c.Flag("uts").Value.String()
vals.PID = c.Flag("pid").Value.String()
vals.CgroupNS = c.Flag("cgroupns").Value.String()
@@ -260,27 +267,30 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra
if c.Flags().Changed("env") {
env, err := c.Flags().GetStringArray("env")
if err != nil {
- return vals, errors.Wrapf(err, "retrieve env flag")
+ return vals, fmt.Errorf("retrieve env flag: %w", err)
}
vals.Env = env
}
if c.Flag("cgroups").Changed && vals.CgroupsMode == "split" && registry.IsRemote() {
- return vals, errors.Errorf("the option --cgroups=%q is not supported in remote mode", vals.CgroupsMode)
+ return vals, fmt.Errorf("the option --cgroups=%q is not supported in remote mode", vals.CgroupsMode)
}
if c.Flag("pod").Changed && !strings.HasPrefix(c.Flag("pod").Value.String(), "new:") && c.Flag("userns").Changed {
- return vals, errors.Errorf("--userns and --pod cannot be set together")
+ return vals, errors.New("--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))
+ return vals, fmt.Errorf("conflicting options: dns and the network mode: " + string(vals.Net.Network.NSMode))
}
noHosts, err := c.Flags().GetBool("no-hosts")
if err != nil {
return vals, err
}
if noHosts && c.Flag("add-host").Changed {
- return vals, errors.Errorf("--no-hosts and --add-host cannot be set together")
+ return vals, errors.New("--no-hosts and --add-host cannot be set together")
}
if !isInfra && c.Flag("entrypoint").Changed {
@@ -294,7 +304,8 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra
return vals, nil
}
-func PullImage(imageName string, cliVals entities.ContainerCreateOptions) (string, error) {
+// Pulls image if any also parses and populates OS, Arch and Variant in specified container create options
+func PullImage(imageName string, cliVals *entities.ContainerCreateOptions) (string, error) {
pullPolicy, err := config.ParsePullPolicy(cliVals.Pull)
if err != nil {
return "", err
@@ -303,7 +314,7 @@ func PullImage(imageName string, cliVals entities.ContainerCreateOptions) (strin
if cliVals.Platform != "" || cliVals.Arch != "" || cliVals.OS != "" {
if cliVals.Platform != "" {
if cliVals.Arch != "" || cliVals.OS != "" {
- return "", errors.Errorf("--platform option can not be specified with --arch or --os")
+ return "", errors.New("--platform option can not be specified with --arch or --os")
}
split := strings.SplitN(cliVals.Platform, "/", 2)
cliVals.OS = split[0]
@@ -351,7 +362,7 @@ func createPodIfNecessary(cmd *cobra.Command, s *specgen.SpecGenerator, netOpts
}
podName := strings.Replace(s.Pod, "new:", "", 1)
if len(podName) < 1 {
- return errors.Errorf("new pod name must be at least one character")
+ return errors.New("new pod name must be at least one character")
}
var err error
diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go
index e1a8ea729..ce501f890 100644
--- a/cmd/podman/containers/diff.go
+++ b/cmd/podman/containers/diff.go
@@ -1,13 +1,14 @@
package containers
import (
+ "errors"
+
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/diff"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -32,13 +33,9 @@ func init() {
Parent: containerCmd,
})
- diffOpts = &entities.DiffOptions{}
+ diffOpts = new(entities.DiffOptions)
flags := diffCmd.Flags()
- // FIXME: Why does this exists? It is not used anywhere.
- flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive")
- _ = flags.MarkHidden("archive")
-
formatFlagName := "format"
flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format (json)")
_ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil))
diff --git a/cmd/podman/containers/exec.go b/cmd/podman/containers/exec.go
index f5b93a96a..303795489 100644
--- a/cmd/podman/containers/exec.go
+++ b/cmd/podman/containers/exec.go
@@ -2,6 +2,7 @@ package containers
import (
"bufio"
+ "errors"
"fmt"
"os"
@@ -13,7 +14,6 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities"
envLib "github.com/containers/podman/v4/pkg/env"
"github.com/containers/podman/v4/pkg/rootless"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -126,14 +126,14 @@ func exec(_ *cobra.Command, args []string) error {
cliEnv, err := envLib.ParseSlice(envInput)
if err != nil {
- return errors.Wrap(err, "error parsing environment variables")
+ return fmt.Errorf("error parsing environment variables: %w", err)
}
execOpts.Envs = envLib.Join(execOpts.Envs, cliEnv)
for fd := 3; fd < int(3+execOpts.PreserveFDs); fd++ {
if !rootless.IsFdInherited(fd) {
- return errors.Errorf("file descriptor %d is not available - the preserve-fds option requires that file descriptors must be passed", fd)
+ return fmt.Errorf("file descriptor %d is not available - the preserve-fds option requires that file descriptors must be passed", fd)
}
}
diff --git a/cmd/podman/containers/export.go b/cmd/podman/containers/export.go
index 681df93e0..d78bfe960 100644
--- a/cmd/podman/containers/export.go
+++ b/cmd/podman/containers/export.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "errors"
"os"
"github.com/containers/common/pkg/completion"
@@ -9,7 +10,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/parse"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/term"
)
@@ -70,7 +70,7 @@ func export(cmd *cobra.Command, args []string) error {
if len(exportOpts.Output) == 0 {
file := os.Stdout
if term.IsTerminal(int(file.Fd())) {
- return errors.Errorf("refusing to export to terminal. Use -o flag or redirect")
+ return errors.New("refusing to export to terminal. Use -o flag or redirect")
}
exportOpts.Output = "/dev/stdout"
} else if err := parse.ValidateFileName(exportOpts.Output); err != nil {
diff --git a/cmd/podman/containers/init.go b/cmd/podman/containers/init.go
index 7336a2332..649cdf1c9 100644
--- a/cmd/podman/containers/init.go
+++ b/cmd/podman/containers/init.go
@@ -21,7 +21,7 @@ var (
Long: initDescription,
RunE: initContainer,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainersCreated,
Example: `podman init --latest
diff --git a/cmd/podman/containers/kill.go b/cmd/podman/containers/kill.go
index e994fbf2c..5a5379389 100644
--- a/cmd/podman/containers/kill.go
+++ b/cmd/podman/containers/kill.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "errors"
"fmt"
"io/ioutil"
"strings"
@@ -13,7 +14,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/signal"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -25,7 +25,7 @@ var (
Long: killDescription,
RunE: kill,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
ValidArgsFunction: common.AutocompleteContainersRunning,
Example: `podman kill mywebserver
@@ -35,7 +35,7 @@ var (
containerKillCommand = &cobra.Command{
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
Use: killCommand.Use,
Short: killCommand.Short,
@@ -97,7 +97,7 @@ func kill(_ *cobra.Command, args []string) error {
for _, cidFile := range cidFiles {
content, err := ioutil.ReadFile(cidFile)
if err != nil {
- return errors.Wrap(err, "error reading CIDFile")
+ return fmt.Errorf("error reading CIDFile: %w", err)
}
id := strings.Split(string(content), "\n")[0]
args = append(args, id)
diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go
index 374bf6b1c..9a9749210 100644
--- a/cmd/podman/containers/logs.go
+++ b/cmd/podman/containers/logs.go
@@ -1,6 +1,8 @@
package containers
import (
+ "errors"
+ "fmt"
"os"
"github.com/containers/common/pkg/completion"
@@ -9,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -125,7 +126,7 @@ func logs(_ *cobra.Command, args []string) error {
// parse time, error out if something is wrong
since, err := util.ParseInputTime(logsOptions.SinceRaw, true)
if err != nil {
- return errors.Wrapf(err, "error parsing --since %q", logsOptions.SinceRaw)
+ return fmt.Errorf("error parsing --since %q: %w", logsOptions.SinceRaw, err)
}
logsOptions.Since = since
}
@@ -133,7 +134,7 @@ func logs(_ *cobra.Command, args []string) error {
// parse time, error out if something is wrong
until, err := util.ParseInputTime(logsOptions.UntilRaw, false)
if err != nil {
- return errors.Wrapf(err, "error parsing --until %q", logsOptions.UntilRaw)
+ return fmt.Errorf("error parsing --until %q: %w", logsOptions.UntilRaw, err)
}
logsOptions.Until = until
}
diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go
index 18177e3ce..a1f0b0cf3 100644
--- a/cmd/podman/containers/mount.go
+++ b/cmd/podman/containers/mount.go
@@ -1,6 +1,7 @@
package containers
import (
+ "errors"
"fmt"
"os"
@@ -10,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -33,7 +33,7 @@ var (
Long: mountDescription,
RunE: mount,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, true, "")
},
ValidArgsFunction: common.AutocompleteContainers,
}
@@ -83,7 +83,7 @@ func init() {
func mount(cmd *cobra.Command, args []string) error {
if len(args) > 0 && mountOpts.Latest {
- return errors.Errorf("--latest and containers cannot be used together")
+ return errors.New("--latest and containers cannot be used together")
}
reports, err := registry.ContainerEngine().ContainerMount(registry.GetContext(), args, mountOpts)
if err != nil {
@@ -108,7 +108,7 @@ func mount(cmd *cobra.Command, args []string) error {
case mountOpts.Format == "":
break // print defaults
default:
- return errors.Errorf("unknown --format argument: %q", mountOpts.Format)
+ return fmt.Errorf("unknown --format argument: %q", mountOpts.Format)
}
mrs := make([]mountReporter, 0, len(reports))
diff --git a/cmd/podman/containers/pause.go b/cmd/podman/containers/pause.go
index 9dbe97d11..3c26fd5c8 100644
--- a/cmd/podman/containers/pause.go
+++ b/cmd/podman/containers/pause.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "errors"
"fmt"
"github.com/containers/common/pkg/cgroups"
@@ -10,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -73,7 +73,7 @@ func pause(cmd *cobra.Command, args []string) error {
}
if len(args) < 1 && !pauseOpts.All {
- return errors.Errorf("you must provide at least one container name or id")
+ return errors.New("you must provide at least one container name or id")
}
responses, err := registry.ContainerEngine().ContainerPause(context.Background(), args, pauseOpts)
if err != nil {
diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go
index 22d1d16d3..74bfdf5c6 100644
--- a/cmd/podman/containers/port.go
+++ b/cmd/podman/containers/port.go
@@ -1,6 +1,7 @@
package containers
import (
+ "errors"
"fmt"
"strconv"
"strings"
@@ -9,13 +10,12 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var (
- portDescription = `List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT
+ portDescription = `List port mappings for the CONTAINER, or look up the public-facing port that is NAT-ed to the PRIVATE_PORT
`
portCommand = &cobra.Command{
Use: "port [options] CONTAINER [PORT]",
@@ -23,7 +23,7 @@ var (
Long: portDescription,
RunE: port,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, true, "")
},
ValidArgsFunction: common.AutocompleteContainerOneArg,
Example: `podman port --all
@@ -37,7 +37,7 @@ var (
Long: portDescription,
RunE: portCommand.RunE,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, true, "")
},
ValidArgsFunction: portCommand.ValidArgsFunction,
Example: `podman container port --all
@@ -77,14 +77,14 @@ func port(_ *cobra.Command, args []string) error {
)
if len(args) == 0 && !portOpts.Latest && !portOpts.All {
- return errors.Errorf("you must supply a running container name or id")
+ return errors.New("you must supply a running container name or id")
}
if !portOpts.Latest && len(args) >= 1 {
container = args[0]
}
port := ""
if len(args) > 2 {
- return errors.Errorf("`port` accepts at most 2 arguments")
+ return errors.New("`port` accepts at most 2 arguments")
}
if len(args) > 1 && !portOpts.Latest {
port = args[1]
@@ -95,7 +95,7 @@ func port(_ *cobra.Command, args []string) error {
if len(port) > 0 {
fields := strings.Split(port, "/")
if len(fields) > 2 || len(fields) < 1 {
- return errors.Errorf("port formats are port/protocol. '%s' is invalid", port)
+ return fmt.Errorf("port formats are port/protocol. '%s' is invalid", port)
}
if len(fields) == 1 {
fields = append(fields, "tcp")
@@ -149,7 +149,7 @@ func port(_ *cobra.Command, args []string) error {
}
}
if !found && port != "" {
- return errors.Errorf("failed to find published port %q", port)
+ return fmt.Errorf("failed to find published port %q", port)
}
}
return nil
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index a011a8ae6..12b7f5dae 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -1,6 +1,7 @@
package containers
import (
+ "errors"
"fmt"
"os"
"strings"
@@ -16,7 +17,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/docker/go-units"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -108,18 +108,18 @@ func listFlagSet(cmd *cobra.Command) {
func checkFlags(c *cobra.Command) error {
// latest, and last are mutually exclusive.
if listOpts.Last >= 0 && listOpts.Latest {
- return errors.Errorf("last and latest are mutually exclusive")
+ return errors.New("last and latest are mutually exclusive")
}
// Quiet conflicts with size and namespace and is overridden by a Go
// template.
if listOpts.Quiet {
if listOpts.Size || listOpts.Namespace {
- return errors.Errorf("quiet conflicts with size and namespace")
+ return errors.New("quiet conflicts with size and namespace")
}
}
// Size and namespace conflict with each other
if listOpts.Size && listOpts.Namespace {
- return errors.Errorf("size and namespace options conflict")
+ return errors.New("size and namespace options conflict")
}
if listOpts.Watch > 0 && listOpts.Latest {
@@ -191,7 +191,7 @@ func ps(cmd *cobra.Command, _ []string) error {
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) == 1 {
- return errors.Errorf("invalid filter %q", f)
+ return fmt.Errorf("invalid filter %q", f)
}
listOpts.Filters[split[0]] = append(listOpts.Filters[split[0]], split[1])
}
diff --git a/cmd/podman/containers/rename.go b/cmd/podman/containers/rename.go
index 1c0b28ce3..d78492ae4 100644
--- a/cmd/podman/containers/rename.go
+++ b/cmd/podman/containers/rename.go
@@ -1,10 +1,11 @@
package containers
import (
+ "errors"
+
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -44,7 +45,7 @@ func init() {
func rename(cmd *cobra.Command, args []string) error {
if len(args) > 2 {
- return errors.Errorf("must provide at least two arguments to rename")
+ return errors.New("must provide at least two arguments to rename")
}
renameOpts := entities.ContainerRenameOptions{
NewName: args[1],
diff --git a/cmd/podman/containers/restart.go b/cmd/podman/containers/restart.go
index 69d8d71ea..9d704d671 100644
--- a/cmd/podman/containers/restart.go
+++ b/cmd/podman/containers/restart.go
@@ -11,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -26,7 +25,7 @@ var (
Long: restartDescription,
RunE: restart,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman restart ctrID
@@ -85,10 +84,10 @@ func restart(cmd *cobra.Command, args []string) error {
errs utils.OutputErrors
)
if len(args) < 1 && !restartOptions.Latest && !restartOptions.All {
- return errors.Wrapf(define.ErrInvalidArg, "you must provide at least one container name or ID")
+ return fmt.Errorf("you must provide at least one container name or ID: %w", define.ErrInvalidArg)
}
if len(args) > 0 && restartOptions.Latest {
- return errors.Wrapf(define.ErrInvalidArg, "--latest and containers cannot be used together")
+ return fmt.Errorf("--latest and containers cannot be used together: %w", define.ErrInvalidArg)
}
if cmd.Flag("time").Changed {
diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go
index eeda5a05f..1e4745354 100644
--- a/cmd/podman/containers/restore.go
+++ b/cmd/podman/containers/restore.go
@@ -28,7 +28,7 @@ var (
Long: restoreDescription,
RunE: restore,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, true, "")
},
ValidArgsFunction: common.AutocompleteContainersAndImages,
Example: `podman container restore ctrID
diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go
index 420e3c38d..9fa688d23 100644
--- a/cmd/podman/containers/rm.go
+++ b/cmd/podman/containers/rm.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "errors"
"fmt"
"io/ioutil"
"strings"
@@ -13,7 +14,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -28,7 +28,7 @@ var (
Long: rmDescription,
RunE: rm,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman rm imageID
@@ -104,7 +104,7 @@ func rm(cmd *cobra.Command, args []string) error {
for _, cidFile := range cidFiles {
content, err := ioutil.ReadFile(cidFile)
if err != nil {
- return errors.Wrap(err, "error reading CIDFile")
+ return fmt.Errorf("error reading CIDFile: %w", err)
}
id := strings.Split(string(content), "\n")[0]
args = append(args, id)
@@ -123,9 +123,7 @@ func rm(cmd *cobra.Command, args []string) error {
// removeContainers will set the exit code according to the `podman-rm` man
// page.
func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit bool) error {
- var (
- errs utils.OutputErrors
- )
+ var errs utils.OutputErrors
responses, err := registry.ContainerEngine().ContainerRm(context.Background(), namesOrIDs, rmOptions)
if err != nil {
if setExit {
@@ -135,8 +133,7 @@ func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit
}
for _, r := range responses {
if r.Err != nil {
- // TODO this will not work with the remote client
- if errors.Cause(err) == define.ErrWillDeadlock {
+ if errors.Is(r.Err, define.ErrWillDeadlock) {
logrus.Errorf("Potential deadlock detected - please run 'podman system renumber' to resolve")
}
if setExit {
@@ -155,15 +152,9 @@ func setExitCode(err error) {
if registry.GetExitCode() == 1 {
return
}
- cause := errors.Cause(err)
- switch {
- case cause == define.ErrNoSuchCtr:
+ if errors.Is(err, define.ErrNoSuchCtr) || strings.Contains(err.Error(), define.ErrNoSuchCtr.Error()) {
registry.SetExitCode(1)
- case strings.Contains(cause.Error(), define.ErrNoSuchCtr.Error()):
- registry.SetExitCode(1)
- case cause == define.ErrCtrStateInvalid:
- registry.SetExitCode(2)
- case strings.Contains(cause.Error(), define.ErrCtrStateInvalid.Error()):
+ } else if errors.Is(err, define.ErrCtrStateInvalid) || strings.Contains(err.Error(), define.ErrCtrStateInvalid.Error()) {
registry.SetExitCode(2)
}
}
diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go
index 951981293..ef13ea95e 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -15,7 +15,6 @@ import (
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/podman/v4/pkg/specgenutil"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/term"
@@ -61,6 +60,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 +109,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,20 +124,16 @@ 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
}
for fd := 3; fd < int(3+runOpts.PreserveFDs); fd++ {
if !rootless.IsFdInherited(fd) {
- return errors.Errorf("file descriptor %d is not available - the preserve-fds option requires that file descriptors must be passed", fd)
+ return fmt.Errorf("file descriptor %d is not available - the preserve-fds option requires that file descriptors must be passed", fd)
}
}
@@ -143,7 +141,7 @@ func run(cmd *cobra.Command, args []string) error {
rawImageName := ""
if !cliVals.RootFS {
rawImageName = args[0]
- name, err := PullImage(args[0], cliVals)
+ name, err := PullImage(args[0], &cliVals)
if err != nil {
return err
}
@@ -166,7 +164,7 @@ func run(cmd *cobra.Command, args []string) error {
// If attach is set, clear stdin/stdout/stderr and only attach requested
if cmd.Flag("attach").Changed {
if passthrough {
- return errors.Wrapf(define.ErrInvalidArg, "cannot specify --attach with --log-driver=passthrough")
+ return fmt.Errorf("cannot specify --attach with --log-driver=passthrough: %w", define.ErrInvalidArg)
}
runOpts.OutputStream = nil
runOpts.ErrorStream = nil
@@ -183,7 +181,7 @@ func run(cmd *cobra.Command, args []string) error {
case "stdin":
runOpts.InputStream = os.Stdin
default:
- return errors.Wrapf(define.ErrInvalidArg, "invalid stream %q for --attach - must be one of stdin, stdout, or stderr", stream)
+ return fmt.Errorf("invalid stream %q for --attach - must be one of stdin, stdout, or stderr: %w", stream, define.ErrInvalidArg)
}
}
}
@@ -194,6 +192,9 @@ func run(cmd *cobra.Command, args []string) error {
return err
}
s.RawImageName = rawImageName
+ s.ImageOS = cliVals.OS
+ s.ImageArch = cliVals.Arch
+ s.ImageVariant = cliVals.Variant
s.Passwd = &runOpts.Passwd
runOpts.Spec = s
diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go
index b70e975b7..cd4fa17b8 100644
--- a/cmd/podman/containers/start.go
+++ b/cmd/podman/containers/start.go
@@ -1,6 +1,7 @@
package containers
import (
+ "errors"
"fmt"
"os"
"strings"
@@ -11,7 +12,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -88,19 +88,19 @@ func validateStart(cmd *cobra.Command, args []string) error {
return errors.New("start requires at least one argument")
}
if startOptions.All && startOptions.Latest {
- return errors.Errorf("--all and --latest cannot be used together")
+ return errors.New("--all and --latest cannot be used together")
}
if len(args) > 0 && startOptions.Latest {
- return errors.Errorf("--latest and containers cannot be used together")
+ return errors.New("--latest and containers cannot be used together")
}
if len(args) > 1 && startOptions.Attach {
- return errors.Errorf("you cannot start and attach multiple containers at once")
+ return errors.New("you cannot start and attach multiple containers at once")
}
if (len(args) > 0 || startOptions.Latest) && startOptions.All {
- return errors.Errorf("either start all containers or the container(s) provided in the arguments")
+ return errors.New("either start all containers or the container(s) provided in the arguments")
}
if startOptions.Attach && startOptions.All {
- return errors.Errorf("you cannot start and attach all containers at once")
+ return errors.New("you cannot start and attach all containers at once")
}
return nil
}
@@ -114,7 +114,7 @@ func start(cmd *cobra.Command, args []string) error {
startOptions.SigProxy = sigProxy
if sigProxy && !startOptions.Attach {
- return errors.Wrapf(define.ErrInvalidArg, "you cannot use sig-proxy without --attach")
+ return fmt.Errorf("you cannot use sig-proxy without --attach: %w", define.ErrInvalidArg)
}
if startOptions.Attach {
startOptions.Stdin = os.Stdin
@@ -127,7 +127,7 @@ func start(cmd *cobra.Command, args []string) error {
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) == 1 {
- return errors.Errorf("invalid filter %q", f)
+ return fmt.Errorf("invalid filter %q", f)
}
startOptions.Filters[split[0]] = append(startOptions.Filters[split[0]], split[1])
}
diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go
index 500671d31..0dd8ce80a 100644
--- a/cmd/podman/containers/stats.go
+++ b/cmd/podman/containers/stats.go
@@ -1,6 +1,7 @@
package containers
import (
+ "errors"
"fmt"
"os"
@@ -14,7 +15,6 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/utils"
"github.com/docker/go-units"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -105,7 +105,7 @@ func checkStatOptions(cmd *cobra.Command, args []string) error {
opts++
}
if opts > 1 {
- return errors.Errorf("--all, --latest and containers cannot be used together")
+ return errors.New("--all, --latest and containers cannot be used together")
}
return nil
}
@@ -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/containers/stop.go b/cmd/podman/containers/stop.go
index af2250abb..2ddd169a1 100644
--- a/cmd/podman/containers/stop.go
+++ b/cmd/podman/containers/stop.go
@@ -12,7 +12,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -26,7 +25,7 @@ var (
Long: stopDescription,
RunE: stop,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
ValidArgsFunction: common.AutocompleteContainersRunning,
Example: `podman stop ctrID
@@ -40,7 +39,7 @@ var (
Long: stopCommand.Long,
RunE: stopCommand.RunE,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
ValidArgsFunction: stopCommand.ValidArgsFunction,
Example: `podman container stop ctrID
@@ -102,7 +101,7 @@ func stop(cmd *cobra.Command, args []string) error {
for _, cidFile := range cidFiles {
content, err := ioutil.ReadFile(cidFile)
if err != nil {
- return errors.Wrap(err, "error reading CIDFile")
+ return fmt.Errorf("error reading CIDFile: %w", err)
}
id := strings.Split(string(content), "\n")[0]
args = append(args, id)
diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go
index 389034e37..9340acd9a 100644
--- a/cmd/podman/containers/top.go
+++ b/cmd/podman/containers/top.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "errors"
"fmt"
"os"
"strings"
@@ -12,7 +13,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -88,7 +88,7 @@ func top(cmd *cobra.Command, args []string) error {
}
if len(args) < 1 && !topOptions.Latest {
- return errors.Errorf("you must provide the name or id of a running container")
+ return errors.New("you must provide the name or id of a running container")
}
if topOptions.Latest {
diff --git a/cmd/podman/containers/unmount.go b/cmd/podman/containers/unmount.go
index 26b8cfcc5..6869de2e2 100644
--- a/cmd/podman/containers/unmount.go
+++ b/cmd/podman/containers/unmount.go
@@ -27,7 +27,7 @@ var (
Long: description,
RunE: unmount,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman unmount ctrID
@@ -43,7 +43,7 @@ var (
Long: unmountCommand.Long,
RunE: unmountCommand.RunE,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman container unmount ctrID
diff --git a/cmd/podman/containers/unpause.go b/cmd/podman/containers/unpause.go
index eaf50f2c7..a5375e737 100644
--- a/cmd/podman/containers/unpause.go
+++ b/cmd/podman/containers/unpause.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "errors"
"fmt"
"github.com/containers/common/pkg/cgroups"
@@ -10,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -70,7 +70,7 @@ func unpause(cmd *cobra.Command, args []string) error {
}
}
if len(args) < 1 && !unPauseOptions.All {
- return errors.Errorf("you must provide at least one container name or id")
+ return errors.New("you must provide at least one container name or id")
}
responses, err := registry.ContainerEngine().ContainerUnpause(context.Background(), args, unPauseOptions)
if err != nil {
diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go
index 720a696ce..5b8480781 100644
--- a/cmd/podman/containers/wait.go
+++ b/cmd/podman/containers/wait.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "errors"
"fmt"
"time"
@@ -12,7 +13,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -86,7 +86,7 @@ func wait(cmd *cobra.Command, args []string) error {
}
if !waitOptions.Latest && len(args) == 0 {
- return errors.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath())
+ return fmt.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath())
}
if waitOptions.Latest && len(args) > 0 {
return errors.New("--latest and containers cannot be used together")
diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go
index 7b78c8312..ec98fb5b5 100644
--- a/cmd/podman/diff.go
+++ b/cmd/podman/diff.go
@@ -35,9 +35,6 @@ func init() {
Command: diffCmd,
})
flags := diffCmd.Flags()
- // FIXME: Why does this exists? It is not used anywhere.
- flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive")
- _ = flags.MarkHidden("archive")
formatFlagName := "format"
flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format (json)")
diff --git a/cmd/podman/diff/diff.go b/cmd/podman/diff/diff.go
index a26502de9..06df767d0 100644
--- a/cmd/podman/diff/diff.go
+++ b/cmd/podman/diff/diff.go
@@ -2,6 +2,7 @@ package diff
import (
"encoding/json"
+ "errors"
"fmt"
"os"
@@ -9,11 +10,10 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/storage/pkg/archive"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
-func Diff(cmd *cobra.Command, args []string, options entities.DiffOptions) error {
+func Diff(_ *cobra.Command, args []string, options entities.DiffOptions) error {
results, err := registry.ContainerEngine().Diff(registry.GetContext(), args, options)
if err != nil {
return err
@@ -46,7 +46,7 @@ func changesToJSON(diffs *entities.DiffReport) error {
case archive.ChangeModify:
body.Changed = append(body.Changed, row.Path)
default:
- return errors.Errorf("output kind %q not recognized", row.Kind)
+ return fmt.Errorf("output kind %q not recognized", row.Kind)
}
}
@@ -63,7 +63,7 @@ func changesToTable(diffs *entities.DiffReport) error {
return nil
}
-// IDOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag
+// ValidateContainerDiffArgs used to validate a nameOrId was provided or the "--latest" flag
func ValidateContainerDiffArgs(cmd *cobra.Command, args []string) error {
given, _ := cmd.Flags().GetBool("latest")
if len(args) > 0 && !given {
@@ -73,7 +73,7 @@ func ValidateContainerDiffArgs(cmd *cobra.Command, args []string) error {
return errors.New("--latest and containers cannot be used together")
}
if len(args) == 0 && !given {
- return errors.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath())
+ return fmt.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath())
}
return nil
}
diff --git a/cmd/podman/early_init_linux.go b/cmd/podman/early_init_linux.go
index dfd9d1e30..e26c4264e 100644
--- a/cmd/podman/early_init_linux.go
+++ b/cmd/podman/early_init_linux.go
@@ -6,7 +6,6 @@ import (
"syscall"
"github.com/containers/podman/v4/libpod/define"
- "github.com/pkg/errors"
)
func setRLimits() error {
@@ -15,11 +14,11 @@ func setRLimits() error {
rlimits.Max = define.RLimitDefaultValue
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
- return errors.Wrapf(err, "error getting rlimits")
+ return fmt.Errorf("error getting rlimits: %w", err)
}
rlimits.Cur = rlimits.Max
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
- return errors.Wrapf(err, "error setting new rlimits")
+ return fmt.Errorf("error setting new rlimits: %w", err)
}
}
return nil
diff --git a/cmd/podman/generate/kube.go b/cmd/podman/generate/kube.go
index c4c92799c..7bfc3dcf7 100644
--- a/cmd/podman/generate/kube.go
+++ b/cmd/podman/generate/kube.go
@@ -11,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -68,10 +67,10 @@ func kube(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("filename") {
if _, err := os.Stat(kubeFile); err == nil {
- return errors.Errorf("cannot write to %q; file exists", kubeFile)
+ return fmt.Errorf("cannot write to %q; file exists", kubeFile)
}
if err := ioutil.WriteFile(kubeFile, content, 0644); err != nil {
- return errors.Wrapf(err, "cannot write to %q", kubeFile)
+ return fmt.Errorf("cannot write to %q: %w", kubeFile, err)
}
return nil
}
diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go
index 0dab6299d..1ece64a30 100644
--- a/cmd/podman/generate/systemd.go
+++ b/cmd/podman/generate/systemd.go
@@ -2,6 +2,7 @@ package pods
import (
"encoding/json"
+ "errors"
"fmt"
"os"
"path/filepath"
@@ -13,7 +14,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
systemDefine "github.com/containers/podman/v4/pkg/systemd/define"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -156,7 +156,7 @@ func systemd(cmd *cobra.Command, args []string) error {
if files {
cwd, err := os.Getwd()
if err != nil {
- return errors.Wrap(err, "error getting current working directory")
+ return fmt.Errorf("error getting current working directory: %w", err)
}
for name, content := range reports.Units {
path := filepath.Join(cwd, fmt.Sprintf("%s.service", name))
@@ -189,7 +189,7 @@ func systemd(cmd *cobra.Command, args []string) error {
case format == "":
return printDefault(reports.Units)
default:
- return errors.Errorf("unknown --format argument: %s", format)
+ return fmt.Errorf("unknown --format argument: %s", format)
}
}
diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go
index 3ea60e18a..9f1b86eb4 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -1,6 +1,7 @@
package images
import (
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -23,7 +24,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -191,15 +191,15 @@ func buildFlags(cmd *cobra.Command) {
_ = flags.MarkHidden("compress")
_ = flags.MarkHidden("volume")
_ = flags.MarkHidden("output")
+ _ = flags.MarkHidden("logsplit")
}
}
// build executes the build command.
func build(cmd *cobra.Command, args []string) error {
if (cmd.Flags().Changed("squash") && cmd.Flags().Changed("layers")) ||
- (cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("layers")) ||
(cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("squash")) {
- return errors.New("cannot specify --squash, --squash-all and --layers options together")
+ return errors.New("cannot specify --squash with --layers and --squash-all with --squash")
}
if cmd.Flag("output").Changed && registry.IsRemote() {
@@ -222,7 +222,7 @@ func build(cmd *cobra.Command, args []string) error {
// The context directory could be a URL. Try to handle that.
tempDir, subDir, err := buildahDefine.TempDirForURL("", "buildah", args[0])
if err != nil {
- return errors.Wrapf(err, "error prepping temporary context directory")
+ return fmt.Errorf("error prepping temporary context directory: %w", err)
}
if tempDir != "" {
// We had to download it to a temporary directory.
@@ -237,7 +237,7 @@ func build(cmd *cobra.Command, args []string) error {
// Nope, it was local. Use it as is.
absDir, err := filepath.Abs(args[0])
if err != nil {
- return errors.Wrapf(err, "error determining path to directory %q", args[0])
+ return fmt.Errorf("error determining path to directory %q: %w", args[0], err)
}
contextDir = absDir
}
@@ -253,7 +253,7 @@ func build(cmd *cobra.Command, args []string) error {
}
absFile, err := filepath.Abs(containerFiles[i])
if err != nil {
- return errors.Wrapf(err, "error determining path to file %q", containerFiles[i])
+ return fmt.Errorf("error determining path to file %q: %w", containerFiles[i], err)
}
contextDir = filepath.Dir(absFile)
containerFiles[i] = absFile
@@ -262,10 +262,10 @@ func build(cmd *cobra.Command, args []string) error {
}
if contextDir == "" {
- return errors.Errorf("no context directory and no Containerfile specified")
+ return errors.New("no context directory and no Containerfile specified")
}
if !utils.IsDir(contextDir) {
- return errors.Errorf("context must be a directory: %q", contextDir)
+ return fmt.Errorf("context must be a directory: %q", contextDir)
}
if len(containerFiles) == 0 {
if utils.FileExists(filepath.Join(contextDir, "Containerfile")) {
@@ -296,14 +296,15 @@ func build(cmd *cobra.Command, args []string) error {
if registry.IsRemote() {
// errors from server does not contain ExitCode
// so parse exit code from error message
- remoteExitCode, parseErr := utils.ExitCodeFromBuildError(fmt.Sprint(errors.Cause(err)))
+ remoteExitCode, parseErr := utils.ExitCodeFromBuildError(err.Error())
if parseErr == nil {
exitCode = remoteExitCode
}
}
- if ee, ok := (errors.Cause(err)).(*exec.ExitError); ok {
- exitCode = ee.ExitCode()
+ exitError := &exec.ExitError{}
+ if errors.As(err, &exitError) {
+ exitCode = exitError.ExitCode()
}
registry.SetExitCode(exitCode)
@@ -356,7 +357,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
}
if pullFlagsCount > 1 {
- return nil, errors.Errorf("can only set one of 'pull' or 'pull-always' or 'pull-never'")
+ return nil, errors.New("can only set one of 'pull' or 'pull-always' or 'pull-never'")
}
// Allow for --pull, --pull=true, --pull=false, --pull=never, --pull=always
@@ -418,7 +419,13 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
// Squash-all invoked, squash both new and old layers into one.
if c.Flags().Changed("squash-all") {
flags.Squash = true
- flags.Layers = false
+ if !c.Flags().Changed("layers") {
+ // Buildah supports using layers and --squash together
+ // after https://github.com/containers/buildah/pull/3674
+ // so podman must honor if user wants to still use layers
+ // with --squash-all.
+ flags.Layers = false
+ }
}
var stdin io.Reader
@@ -442,22 +449,6 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
return nil, err
}
- // `buildah bud --layers=false` acts like `docker build --squash` does.
- // That is all of the new layers created during the build process are
- // condensed into one, any layers present prior to this build are retained
- // without condensing. `buildah bud --squash` squashes both new and old
- // layers down into one. Translate Podman commands into Buildah.
- // Squash invoked, retain old layers, squash new layers into one.
- if c.Flags().Changed("squash") && flags.Squash {
- flags.Squash = false
- flags.Layers = false
- }
- // Squash-all invoked, squash both new and old layers into one.
- if c.Flags().Changed("squash-all") {
- flags.Squash = true
- flags.Layers = false
- }
-
compression := buildahDefine.Gzip
if flags.DisableCompression {
compression = buildahDefine.Uncompressed
@@ -487,7 +478,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
case strings.HasPrefix(flags.Format, buildahDefine.DOCKER):
format = buildahDefine.Dockerv2ImageManifest
default:
- return nil, errors.Errorf("unrecognized image type %q", flags.Format)
+ return nil, fmt.Errorf("unrecognized image type %q", flags.Format)
}
runtimeFlags := []string{}
@@ -510,12 +501,29 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
decConfig, err := getDecryptConfig(flags.DecryptionKeys)
if err != nil {
- return nil, errors.Wrapf(err, "unable to obtain decrypt config")
+ return nil, fmt.Errorf("unable to obtain decrypt config: %w", err)
+ }
+
+ additionalBuildContext := make(map[string]*buildahDefine.AdditionalBuildContext)
+ if c.Flag("build-context").Changed {
+ for _, contextString := range flags.BuildContext {
+ av := strings.SplitN(contextString, "=", 2)
+ if len(av) > 1 {
+ parseAdditionalBuildContext, err := parse.GetAdditionalBuildContext(av[1])
+ if err != nil {
+ return nil, fmt.Errorf("while parsing additional build context: %w", err)
+ }
+ additionalBuildContext[av[0]] = &parseAdditionalBuildContext
+ } else {
+ return nil, fmt.Errorf("while parsing additional build context: %q, accepts value in the form of key=value", av)
+ }
+ }
}
opts := buildahDefine.BuildOptions{
AddCapabilities: flags.CapAdd,
AdditionalTags: tags,
+ AdditionalBuildContexts: additionalBuildContext,
AllPlatforms: flags.AllPlatforms,
Annotations: flags.Annotation,
Args: args,
@@ -525,6 +533,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
Compression: compression,
ConfigureNetwork: networkPolicy,
ContextDirectory: contextDir,
+ CPPFlags: flags.CPPFlags,
DefaultMountsFilePath: containerConfig.Containers.DefaultMountsFile,
Devices: flags.Devices,
DropCapabilities: flags.CapDrop,
@@ -539,6 +548,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,
@@ -570,7 +581,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
if flags.IgnoreFile != "" {
excludes, err := parseDockerignore(flags.IgnoreFile)
if err != nil {
- return nil, errors.Wrapf(err, "unable to obtain decrypt config")
+ return nil, fmt.Errorf("unable to obtain decrypt config: %w", err)
}
opts.Excludes = excludes
}
@@ -589,7 +600,7 @@ func getDecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error)
// decryption
dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys)
if err != nil {
- return nil, errors.Wrapf(err, "invalid decryption keys")
+ return nil, fmt.Errorf("invalid decryption keys: %w", err)
}
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc})
decConfig = cc.DecryptConfig
diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go
index 13a8f1d9d..a017d569d 100644
--- a/cmd/podman/images/diff.go
+++ b/cmd/podman/images/diff.go
@@ -34,9 +34,7 @@ func init() {
}
func diffFlags(flags *pflag.FlagSet) {
- diffOpts = &entities.DiffOptions{}
- flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive")
- _ = flags.MarkDeprecated("archive", "Provided for backwards compatibility, has no impact on output.")
+ diffOpts = new(entities.DiffOptions)
formatFlagName := "format"
flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format (json)")
diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go
index e190941e7..8f910f82d 100644
--- a/cmd/podman/images/history.go
+++ b/cmd/podman/images/history.go
@@ -13,7 +13,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/docker/go-units"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -132,7 +131,7 @@ func history(cmd *cobra.Command, args []string) error {
})
if err := rpt.Execute(hdrs); err != nil {
- return errors.Wrapf(err, "failed to write report column headers")
+ return fmt.Errorf("failed to write report column headers: %w", err)
}
}
return rpt.Execute(hr)
diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go
index 1910fef6d..8343a0bda 100644
--- a/cmd/podman/images/import.go
+++ b/cmd/podman/images/import.go
@@ -2,6 +2,7 @@ package images
import (
"context"
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -14,7 +15,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/hashicorp/go-multierror"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -102,7 +102,7 @@ func importCon(cmd *cobra.Command, args []string) error {
)
switch len(args) {
case 0:
- return errors.Errorf("need to give the path to the tarball, or must specify a tarball of '-' for stdin")
+ return errors.New("need to give the path to the tarball, or must specify a tarball of '-' for stdin")
case 1:
source = args[0]
case 2:
@@ -112,20 +112,20 @@ func importCon(cmd *cobra.Command, args []string) error {
// instead of the localhost ones
reference = args[1]
default:
- return errors.Errorf("too many arguments. Usage TARBALL [REFERENCE]")
+ return errors.New("too many arguments. Usage TARBALL [REFERENCE]")
}
if source == "-" {
outFile, err := ioutil.TempFile("", "podman")
if err != nil {
- return errors.Errorf("creating file %v", err)
+ return fmt.Errorf("creating file %v", err)
}
defer os.Remove(outFile.Name())
defer outFile.Close()
_, err = io.Copy(outFile, os.Stdin)
if err != nil {
- return errors.Errorf("copying file %v", err)
+ return fmt.Errorf("copying file %v", err)
}
source = outFile.Name()
}
diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go
index 81011f9b1..94d8412e5 100644
--- a/cmd/podman/images/list.go
+++ b/cmd/podman/images/list.go
@@ -1,6 +1,7 @@
package images
import (
+ "errors"
"fmt"
"os"
"sort"
@@ -15,7 +16,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/docker/go-units"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -225,7 +225,7 @@ func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) {
h.ImageSummary = *e
h.Repository, h.Tag, err = tokenRepoTag(tag)
if err != nil {
- return nil, errors.Wrapf(err, "error parsing repository tag: %q", tag)
+ return nil, fmt.Errorf("error parsing repository tag: %q: %w", tag, err)
}
if h.Tag == "<none>" {
untagged = append(untagged, h)
diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go
index dbb7c32fa..367b628c7 100644
--- a/cmd/podman/images/load.go
+++ b/cmd/podman/images/load.go
@@ -2,6 +2,7 @@ package images
import (
"context"
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -14,7 +15,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/term"
)
@@ -91,18 +91,18 @@ func load(cmd *cobra.Command, args []string) error {
}
} else {
if term.IsTerminal(int(os.Stdin.Fd())) {
- return errors.Errorf("cannot read from terminal, use command-line redirection or the --input flag")
+ return errors.New("cannot read from terminal, use command-line redirection or the --input flag")
}
outFile, err := ioutil.TempFile(util.Tmpdir(), "podman")
if err != nil {
- return errors.Errorf("creating file %v", err)
+ return fmt.Errorf("creating file %v", err)
}
defer os.Remove(outFile.Name())
defer outFile.Close()
_, err = io.Copy(outFile, os.Stdin)
if err != nil {
- return errors.Errorf("copying file %v", err)
+ return fmt.Errorf("copying file %v", err)
}
loadOpts.Input = outFile.Name()
}
@@ -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/mount.go b/cmd/podman/images/mount.go
index d5ab3d274..cd54e24ae 100644
--- a/cmd/podman/images/mount.go
+++ b/cmd/podman/images/mount.go
@@ -1,15 +1,14 @@
package images
import (
+ "errors"
"fmt"
"os"
"github.com/containers/common/pkg/report"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
- "github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -71,16 +70,12 @@ func mount(cmd *cobra.Command, args []string) error {
return err
}
- if len(args) > 0 || mountOpts.All {
- var errs utils.OutputErrors
- for _, r := range reports {
- if r.Err == nil {
- fmt.Println(r.Path)
- continue
- }
- errs = append(errs, r.Err)
+ if len(args) == 1 && mountOpts.Format == "" && !mountOpts.All {
+ if len(reports) != 1 {
+ return fmt.Errorf("internal error: expected 1 report but got %d", len(reports))
}
- return errs.PrintErrors()
+ fmt.Println(reports[0].Path)
+ return nil
}
switch {
@@ -89,7 +84,7 @@ func mount(cmd *cobra.Command, args []string) error {
case mountOpts.Format == "":
break // see default format below
default:
- return errors.Errorf("unknown --format argument: %q", mountOpts.Format)
+ return fmt.Errorf("unknown --format argument: %q", mountOpts.Format)
}
mrs := make([]mountReporter, 0, len(reports))
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index a7da5518a..6e3ec1517 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -1,6 +1,7 @@
package images
import (
+ "errors"
"fmt"
"os"
"strings"
@@ -13,7 +14,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -138,7 +138,7 @@ func imagePull(cmd *cobra.Command, args []string) error {
}
if platform != "" {
if pullOptions.Arch != "" || pullOptions.OS != "" {
- return errors.Errorf("--platform option can not be specified with --arch or --os")
+ return errors.New("--platform option can not be specified with --arch or --os")
}
split := strings.SplitN(platform, "/", 2)
pullOptions.OS = split[0]
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/images/rm.go b/cmd/podman/images/rm.go
index 13dab62d4..18b22e51d 100644
--- a/cmd/podman/images/rm.go
+++ b/cmd/podman/images/rm.go
@@ -1,13 +1,13 @@
package images
import (
+ "errors"
"fmt"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/errorhandling"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -62,10 +62,10 @@ func imageRemoveFlagSet(flags *pflag.FlagSet) {
func rm(cmd *cobra.Command, args []string) error {
if len(args) < 1 && !imageOpts.All {
- return errors.Errorf("image name or ID must be specified")
+ return errors.New("image name or ID must be specified")
}
if len(args) > 0 && imageOpts.All {
- return errors.Errorf("when using the --all switch, you may not pass any images names or IDs")
+ return errors.New("when using the --all switch, you may not pass any images names or IDs")
}
// Note: certain image-removal errors are non-fatal. Hence, the report
diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go
index 3394c2e99..43366e1b3 100644
--- a/cmd/podman/images/save.go
+++ b/cmd/podman/images/save.go
@@ -2,17 +2,18 @@ package images
import (
"context"
+ "errors"
+ "fmt"
"os"
"strings"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/parse"
"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/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/term"
)
@@ -31,14 +32,14 @@ var (
RunE: save,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
- return errors.Errorf("need at least 1 argument")
+ return errors.New("need at least 1 argument")
}
format, err := cmd.Flags().GetString("format")
if err != nil {
return err
}
if !util.StringInSlice(format, common.ValidSaveFormats) {
- return errors.Errorf("format value must be one of %s", strings.Join(common.ValidSaveFormats, " "))
+ return fmt.Errorf("format value must be one of %s", strings.Join(common.ValidSaveFormats, " "))
}
return nil
},
@@ -103,13 +104,13 @@ func save(cmd *cobra.Command, args []string) (finalErr error) {
succeeded = false
)
if cmd.Flag("compress").Changed && (saveOpts.Format != define.OCIManifestDir && saveOpts.Format != define.V2s2ManifestDir) {
- return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
+ return errors.New("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
}
if len(saveOpts.Output) == 0 {
saveOpts.Quiet = true
fi := os.Stdout
if term.IsTerminal(int(fi.Fd())) {
- return errors.Errorf("refusing to save to terminal. Use -o flag or redirect")
+ return errors.New("refusing to save to terminal. Use -o flag or redirect")
}
pipePath, cleanup, err := setupPipe()
if err != nil {
diff --git a/cmd/podman/images/scp.go b/cmd/podman/images/scp.go
index 3dbc9c331..a7aa43e61 100644
--- a/cmd/podman/images/scp.go
+++ b/cmd/podman/images/scp.go
@@ -1,28 +1,12 @@
package images
import (
- "context"
- "fmt"
- "io/ioutil"
- urlP "net/url"
"os"
- "os/exec"
- "os/user"
- "strconv"
"strings"
- "github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
- "github.com/containers/podman/v4/cmd/podman/system/connection"
- "github.com/containers/podman/v4/libpod/define"
- "github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/containers/podman/v4/utils"
- scpD "github.com/dtylman/scp"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
- "golang.org/x/crypto/ssh"
)
var (
@@ -32,7 +16,6 @@ var (
Annotations: map[string]string{
registry.UnshareNSRequired: "",
registry.ParentNSRequired: "",
- registry.EngineMode: registry.ABIMode,
},
Long: saveScpDescription,
Short: "securely copy images",
@@ -46,9 +29,6 @@ var (
var (
parentFlags []string
quiet bool
- source entities.ImageScpOptions
- dest entities.ImageScpOptions
- sshInfo entities.ImageScpConnections
)
func init() {
@@ -66,7 +46,6 @@ func scpFlags(cmd *cobra.Command) {
func scp(cmd *cobra.Command, args []string) (finalErr error) {
var (
- // TODO add tag support for images
err error
)
for i, val := range os.Args {
@@ -81,288 +60,17 @@ func scp(cmd *cobra.Command, args []string) (finalErr error) {
}
parentFlags = append(parentFlags, val)
}
- podman, err := os.Executable()
- if err != nil {
- return err
- }
- f, err := ioutil.TempFile("", "podman") // open temp file for load/save output
- if err != nil {
- return err
- }
- confR, err := config.NewConfig("") // create a hand made config for the remote engine since we might use remote and native at once
- if err != nil {
- return errors.Wrapf(err, "could not make config")
- }
-
- abiEng, err := registry.NewImageEngine(cmd, args) // abi native engine
- if err != nil {
- return err
- }
-
- cfg, err := config.ReadCustomConfig() // get ready to set ssh destination if necessary
- if err != nil {
- return err
- }
- locations := []*entities.ImageScpOptions{}
- cliConnections := []string{}
- var flipConnections bool
- for _, arg := range args {
- loc, connect, err := parseImageSCPArg(arg)
- if err != nil {
- return err
- }
- locations = append(locations, loc)
- cliConnections = append(cliConnections, connect...)
- }
- source = *locations[0]
- switch {
- case len(locations) > 1:
- if flipConnections, err = validateSCPArgs(locations); err != nil {
- return err
- }
- if flipConnections { // the order of cliConnections matters, we need to flip both arrays since the args are parsed separately sometimes.
- cliConnections[0], cliConnections[1] = cliConnections[1], cliConnections[0]
- locations[0], locations[1] = locations[1], locations[0]
- }
- dest = *locations[1]
- case len(locations) == 1:
- switch {
- case len(locations[0].Image) == 0:
- return errors.Wrapf(define.ErrInvalidArg, "no source image specified")
- case len(locations[0].Image) > 0 && !locations[0].Remote && len(locations[0].User) == 0: // if we have podman image scp $IMAGE
- return errors.Wrapf(define.ErrInvalidArg, "must specify a destination")
- }
- }
-
- source.Quiet = quiet
- source.File = f.Name() // after parsing the arguments, set the file for the save/load
- dest.File = source.File
- if err = os.Remove(source.File); err != nil { // remove the file and simply use its name so podman creates the file upon save. avoids umask errors
- return err
- }
-
- allLocal := true // if we are all localhost, do not validate connections but if we are using one localhost and one non we need to use sshd
- for _, val := range cliConnections {
- if !strings.Contains(val, "@localhost::") {
- allLocal = false
- break
- }
- }
- if allLocal {
- cliConnections = []string{}
- }
-
- var serv map[string]config.Destination
- serv, err = GetServiceInformation(cliConnections, cfg)
- if err != nil {
- return err
- }
-
- // TODO: Add podman remote support
- confR.Engine = config.EngineConfig{Remote: true, CgroupManager: "cgroupfs", ServiceDestinations: serv} // pass the service dest (either remote or something else) to engine
- saveCmd, loadCmd := createCommands(podman)
- switch {
- case source.Remote: // if we want to load FROM the remote, dest can either be local or remote in this case
- err = saveToRemote(source.Image, source.File, "", sshInfo.URI[0], sshInfo.Identities[0])
- if err != nil {
- return err
- }
- if dest.Remote { // we want to load remote -> remote, both source and dest are remote
- rep, err := loadToRemote(dest.File, "", sshInfo.URI[1], sshInfo.Identities[1])
- if err != nil {
- return err
- }
- fmt.Println(rep)
- break
- }
- err = execPodman(podman, loadCmd)
- if err != nil {
- return err
- }
- case dest.Remote: // remote host load, implies source is local
- err = execPodman(podman, saveCmd)
- if err != nil {
- return err
- }
- rep, err := loadToRemote(source.File, "", sshInfo.URI[0], sshInfo.Identities[0])
- if err != nil {
- return err
- }
- fmt.Println(rep)
- if err = os.Remove(source.File); err != nil {
- return err
- }
- // TODO: Add podman remote support
- default: // else native load, both source and dest are local and transferring between users
- if source.User == "" { // source user has to be set, destination does not
- source.User = os.Getenv("USER")
- if source.User == "" {
- u, err := user.Current()
- if err != nil {
- return errors.Wrapf(err, "could not obtain user, make sure the environmental variable $USER is set")
- }
- source.User = u.Username
- }
- }
- err := abiEng.Transfer(context.Background(), source, dest, parentFlags)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-// loadToRemote takes image and remote connection information. it connects to the specified client
-// and copies the saved image dir over to the remote host and then loads it onto the machine
-// returns a string containing output or an error
-func loadToRemote(localFile string, tag string, url *urlP.URL, iden string) (string, error) {
- dial, remoteFile, err := createConnection(url, iden)
- if err != nil {
- return "", err
- }
- defer dial.Close()
-
- n, err := scpD.CopyTo(dial, localFile, remoteFile)
- if err != nil {
- errOut := strconv.Itoa(int(n)) + " Bytes copied before error"
- return " ", errors.Wrapf(err, errOut)
- }
- var run string
- if tag != "" {
- return "", errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported")
- }
- podman := os.Args[0]
- run = podman + " image load --input=" + remoteFile + ";rm " + remoteFile // run ssh image load of the file copied via scp
- out, err := connection.ExecRemoteCommand(dial, run)
- if err != nil {
- return "", err
+ src := args[0]
+ dst := ""
+ if len(args) > 1 {
+ dst = args[1]
}
- return strings.TrimSuffix(string(out), "\n"), nil
-}
-
-// saveToRemote takes image information and remote connection information. it connects to the specified client
-// and saves the specified image on the remote machine and then copies it to the specified local location
-// returns an error if one occurs.
-func saveToRemote(image, localFile string, tag string, uri *urlP.URL, iden string) error {
- dial, remoteFile, err := createConnection(uri, iden)
+ err = registry.ImageEngine().Scp(registry.Context(), src, dst, parentFlags, quiet)
if err != nil {
return err
}
- defer dial.Close()
- if tag != "" {
- return errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported")
- }
- podman := os.Args[0]
- run := podman + " image save " + image + " --format=oci-archive --output=" + remoteFile // run ssh image load of the file copied via scp. Files are reverse in this case...
- _, err = connection.ExecRemoteCommand(dial, run)
- if err != nil {
- return err
- }
- n, err := scpD.CopyFrom(dial, remoteFile, localFile)
- if _, conErr := connection.ExecRemoteCommand(dial, "rm "+remoteFile); conErr != nil {
- logrus.Errorf("Removing file on endpoint: %v", conErr)
- }
- if err != nil {
- errOut := strconv.Itoa(int(n)) + " Bytes copied before error"
- return errors.Wrapf(err, errOut)
- }
return nil
}
-
-// makeRemoteFile creates the necessary remote file on the host to
-// save or load the image to. returns a string with the file name or an error
-func makeRemoteFile(dial *ssh.Client) (string, error) {
- run := "mktemp"
- remoteFile, err := connection.ExecRemoteCommand(dial, run)
- if err != nil {
- return "", err
- }
- return strings.TrimSuffix(string(remoteFile), "\n"), nil
-}
-
-// createConnections takes a boolean determining which ssh client to dial
-// and returns the dials client, its newly opened remote file, and an error if applicable.
-func createConnection(url *urlP.URL, iden string) (*ssh.Client, string, error) {
- cfg, err := connection.ValidateAndConfigure(url, iden)
- if err != nil {
- return nil, "", err
- }
- dialAdd, err := ssh.Dial("tcp", url.Host, cfg) // dial the client
- if err != nil {
- return nil, "", errors.Wrapf(err, "failed to connect")
- }
- file, err := makeRemoteFile(dialAdd)
- if err != nil {
- return nil, "", err
- }
-
- return dialAdd, file, nil
-}
-
-// GetSerivceInformation takes the parsed list of hosts to connect to and validates the information
-func GetServiceInformation(cliConnections []string, cfg *config.Config) (map[string]config.Destination, error) {
- var serv map[string]config.Destination
- var url string
- var iden string
- for i, val := range cliConnections {
- splitEnv := strings.SplitN(val, "::", 2)
- sshInfo.Connections = append(sshInfo.Connections, splitEnv[0])
- if len(splitEnv[1]) != 0 {
- err := validateImageName(splitEnv[1])
- if err != nil {
- return nil, err
- }
- source.Image = splitEnv[1]
- //TODO: actually use the new name given by the user
- }
- conn, found := cfg.Engine.ServiceDestinations[sshInfo.Connections[i]]
- if found {
- url = conn.URI
- iden = conn.Identity
- } else { // no match, warn user and do a manual connection.
- url = "ssh://" + sshInfo.Connections[i]
- iden = ""
- logrus.Warnf("Unknown connection name given. Please use system connection add to specify the default remote socket location")
- }
- urlT, err := urlP.Parse(url) // create an actual url to pass to exec command
- if err != nil {
- return nil, err
- }
- if urlT.User.Username() == "" {
- if urlT.User, err = connection.GetUserInfo(urlT); err != nil {
- return nil, err
- }
- }
- sshInfo.URI = append(sshInfo.URI, urlT)
- sshInfo.Identities = append(sshInfo.Identities, iden)
- }
- return serv, nil
-}
-
-// execPodman executes the podman save/load command given the podman binary
-func execPodman(podman string, command []string) error {
- cmd := exec.Command(podman)
- utils.CreateSCPCommand(cmd, command[1:])
- logrus.Debugf("Executing podman command: %q", cmd)
- return cmd.Run()
-}
-
-// createCommands forms the podman save and load commands used by SCP
-func createCommands(podman string) ([]string, []string) {
- var parentString string
- quiet := ""
- if source.Quiet {
- quiet = "-q "
- }
- if len(parentFlags) > 0 {
- parentString = strings.Join(parentFlags, " ") + " " // if there are parent args, an extra space needs to be added
- } else {
- parentString = strings.Join(parentFlags, " ")
- }
- loadCmd := strings.Split(fmt.Sprintf("%s %sload %s--input %s", podman, parentString, quiet, dest.File), " ")
- saveCmd := strings.Split(fmt.Sprintf("%s %vsave %s--output %s %s", podman, parentString, quiet, source.File, source.Image), " ")
- return saveCmd, loadCmd
-}
diff --git a/cmd/podman/images/scp_test.go b/cmd/podman/images/scp_test.go
deleted file mode 100644
index 315fda2ab..000000000
--- a/cmd/podman/images/scp_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package images
-
-import (
- "testing"
-
- "github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/stretchr/testify/assert"
-)
-
-func TestParseSCPArgs(t *testing.T) {
- args := []string{"alpine", "root@localhost::"}
- var source *entities.ImageScpOptions
- var dest *entities.ImageScpOptions
- var err error
- source, _, err = parseImageSCPArg(args[0])
- assert.Nil(t, err)
- assert.Equal(t, source.Image, "alpine")
-
- dest, _, err = parseImageSCPArg(args[1])
- assert.Nil(t, err)
- assert.Equal(t, dest.Image, "")
- assert.Equal(t, dest.User, "root")
-
- args = []string{"root@localhost::alpine"}
- source, _, err = parseImageSCPArg(args[0])
- assert.Nil(t, err)
- assert.Equal(t, source.User, "root")
- assert.Equal(t, source.Image, "alpine")
-
- args = []string{"charliedoern@192.168.68.126::alpine", "foobar@192.168.68.126::"}
- source, _, err = parseImageSCPArg(args[0])
- assert.Nil(t, err)
- assert.True(t, source.Remote)
- assert.Equal(t, source.Image, "alpine")
-
- dest, _, err = parseImageSCPArg(args[1])
- assert.Nil(t, err)
- assert.True(t, dest.Remote)
- assert.Equal(t, dest.Image, "")
-
- args = []string{"charliedoern@192.168.68.126::alpine"}
- source, _, err = parseImageSCPArg(args[0])
- assert.Nil(t, err)
- assert.True(t, source.Remote)
- assert.Equal(t, source.Image, "alpine")
-}
diff --git a/cmd/podman/images/scp_utils.go b/cmd/podman/images/scp_utils.go
deleted file mode 100644
index a85687a42..000000000
--- a/cmd/podman/images/scp_utils.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package images
-
-import (
- "strings"
-
- "github.com/containers/image/v5/docker/reference"
- "github.com/containers/podman/v4/libpod/define"
- "github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
-)
-
-// parseImageSCPArg returns the valid connection, and source/destination data based off of the information provided by the user
-// arg is a string containing one of the cli arguments returned is a filled out source/destination options structs as well as a connections array and an error if applicable
-func parseImageSCPArg(arg string) (*entities.ImageScpOptions, []string, error) {
- location := entities.ImageScpOptions{}
- var err error
- cliConnections := []string{}
-
- switch {
- case strings.Contains(arg, "@localhost::"): // image transfer between users
- location.User = strings.Split(arg, "@")[0]
- location, err = validateImagePortion(location, arg)
- if err != nil {
- return nil, nil, err
- }
- cliConnections = append(cliConnections, arg)
- case strings.Contains(arg, "::"):
- location, err = validateImagePortion(location, arg)
- if err != nil {
- return nil, nil, err
- }
- location.Remote = true
- cliConnections = append(cliConnections, arg)
- default:
- location.Image = arg
- }
- return &location, cliConnections, nil
-}
-
-// validateImagePortion is a helper function to validate the image name in an SCP argument
-func validateImagePortion(location entities.ImageScpOptions, arg string) (entities.ImageScpOptions, error) {
- if remoteArgLength(arg, 1) > 0 {
- err := validateImageName(strings.Split(arg, "::")[1])
- if err != nil {
- return location, err
- }
- location.Image = strings.Split(arg, "::")[1] // this will get checked/set again once we validate connections
- }
- return location, nil
-}
-
-// validateSCPArgs takes the array of source and destination options and checks for common errors
-func validateSCPArgs(locations []*entities.ImageScpOptions) (bool, error) {
- if len(locations) > 2 {
- return false, errors.Wrapf(define.ErrInvalidArg, "cannot specify more than two arguments")
- }
- switch {
- case len(locations[0].Image) > 0 && len(locations[1].Image) > 0:
- return false, errors.Wrapf(define.ErrInvalidArg, "cannot specify an image rename")
- case len(locations[0].Image) == 0 && len(locations[1].Image) == 0:
- return false, errors.Wrapf(define.ErrInvalidArg, "a source image must be specified")
- case len(locations[0].Image) == 0 && len(locations[1].Image) != 0:
- if locations[0].Remote && locations[1].Remote {
- return true, nil // we need to flip the cliConnections array so the save/load connections are in the right place
- }
- }
- return false, nil
-}
-
-// validateImageName makes sure that the image given is valid and no injections are occurring
-// we simply use this for error checking, bot setting the image
-func validateImageName(input string) error {
- // ParseNormalizedNamed transforms a shortname image into its
- // full name reference so busybox => docker.io/library/busybox
- // we want to keep our shortnames, so only return an error if
- // we cannot parse what the user has given us
- _, err := reference.ParseNormalizedNamed(input)
- return err
-}
-
-// remoteArgLength is a helper function to simplify the extracting of host argument data
-// returns an int which contains the length of a specified index in a host::image string
-func remoteArgLength(input string, side int) int {
- if strings.Contains(input, "::") {
- return len((strings.Split(input, "::"))[side])
- }
- return -1
-}
diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go
index 335ea2b5a..eb876d3d4 100644
--- a/cmd/podman/images/search.go
+++ b/cmd/podman/images/search.go
@@ -1,6 +1,7 @@
package images
import (
+ "errors"
"fmt"
"os"
"strings"
@@ -12,7 +13,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -83,8 +83,7 @@ func searchFlags(cmd *cobra.Command) {
filterFlagName := "filter"
flags.StringSliceVarP(&searchOptions.Filters, filterFlagName, "f", []string{}, "Filter output based on conditions provided (default [])")
- // TODO add custom filter function
- _ = cmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone)
+ _ = cmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteImageSearchFilters)
formatFlagName := "format"
flags.StringVar(&searchOptions.Format, formatFlagName, "", "Change the output format to JSON or a Go template")
@@ -112,11 +111,11 @@ func imageSearch(cmd *cobra.Command, args []string) error {
case 1:
searchTerm = args[0]
default:
- return errors.Errorf("search requires exactly one argument")
+ return errors.New("search requires exactly one argument")
}
if searchOptions.ListTags && len(searchOptions.Filters) != 0 {
- return errors.Errorf("filters are not applicable to list tags result")
+ return errors.New("filters are not applicable to list tags result")
}
// TLS verification in c/image is controlled via a `types.OptionalBool`
@@ -156,7 +155,7 @@ func imageSearch(cmd *cobra.Command, args []string) error {
switch {
case searchOptions.ListTags:
if len(searchOptions.Filters) != 0 {
- return errors.Errorf("filters are not applicable to list tags result")
+ return errors.New("filters are not applicable to list tags result")
}
if isJSON {
listTagsEntries := buildListTagsJSON(searchReport)
@@ -182,7 +181,7 @@ func imageSearch(cmd *cobra.Command, args []string) error {
if rpt.RenderHeaders {
hdrs := report.Headers(entities.ImageSearchReport{}, nil)
if err := rpt.Execute(hdrs); err != nil {
- return errors.Wrapf(err, "failed to write report column headers")
+ return fmt.Errorf("failed to write report column headers: %w", err)
}
}
return rpt.Execute(searchReport)
diff --git a/cmd/podman/images/sign.go b/cmd/podman/images/sign.go
index e4e201894..beea6d2b8 100644
--- a/cmd/podman/images/sign.go
+++ b/cmd/podman/images/sign.go
@@ -1,6 +1,7 @@
package images
import (
+ "errors"
"os"
"github.com/containers/common/pkg/auth"
@@ -8,7 +9,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -57,7 +57,7 @@ func init() {
func sign(cmd *cobra.Command, args []string) error {
if signOptions.SignBy == "" {
- return errors.Errorf("please provide an identity")
+ return errors.New("please provide an identity")
}
var sigStoreDir string
diff --git a/cmd/podman/images/trust_set.go b/cmd/podman/images/trust_set.go
index fff035d12..832e9f724 100644
--- a/cmd/podman/images/trust_set.go
+++ b/cmd/podman/images/trust_set.go
@@ -1,15 +1,15 @@
package images
import (
+ "fmt"
"net/url"
"regexp"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -61,7 +61,7 @@ func setTrust(cmd *cobra.Command, args []string) error {
}
if !util.StringInSlice(setOptions.Type, validTrustTypes) {
- return errors.Errorf("invalid choice: %s (choose from 'accept', 'reject', 'signedBy')", setOptions.Type)
+ return fmt.Errorf("invalid choice: %s (choose from 'accept', 'reject', 'signedBy')", setOptions.Type)
}
return registry.ImageEngine().SetTrust(registry.Context(), args, setOptions)
}
@@ -71,17 +71,17 @@ func isValidImageURI(imguri string) (bool, error) {
uri := "http://" + imguri
u, err := url.Parse(uri)
if err != nil {
- return false, errors.Wrapf(err, "invalid image uri: %s", imguri)
+ return false, fmt.Errorf("invalid image uri: %s: %w", imguri, err)
}
reg := regexp.MustCompile(`^[a-zA-Z0-9-_\.]+\/?:?[0-9]*[a-z0-9-\/:]*$`)
ret := reg.FindAllString(u.Host, -1)
if len(ret) == 0 {
- return false, errors.Wrapf(err, "invalid image uri: %s", imguri)
+ return false, fmt.Errorf("invalid image uri: %s: %w", imguri, err)
}
reg = regexp.MustCompile(`^[a-z0-9-:\./]*$`)
ret = reg.FindAllString(u.Fragment, -1)
if len(ret) == 0 {
- return false, errors.Wrapf(err, "invalid image uri: %s", imguri)
+ return false, fmt.Errorf("invalid image uri: %s: %w", imguri, err)
}
return true, nil
}
diff --git a/cmd/podman/images/unmount.go b/cmd/podman/images/unmount.go
index 3ada09937..2a3df7cbd 100644
--- a/cmd/podman/images/unmount.go
+++ b/cmd/podman/images/unmount.go
@@ -1,13 +1,13 @@
package images
import (
+ "errors"
"fmt"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
diff --git a/cmd/podman/images/utils_linux.go b/cmd/podman/images/utils_linux.go
index f7c159415..5923716ec 100644
--- a/cmd/podman/images/utils_linux.go
+++ b/cmd/podman/images/utils_linux.go
@@ -1,12 +1,12 @@
package images
import (
+ "fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -26,7 +26,7 @@ func setupPipe() (string, func() <-chan error, error) {
if e := os.RemoveAll(pipeDir); e != nil {
logrus.Errorf("Removing named pipe: %q", e)
}
- return "", nil, errors.Wrapf(err, "error creating named pipe")
+ return "", nil, fmt.Errorf("error creating named pipe: %w", err)
}
go func() {
fpipe, err := os.Open(pipePath)
diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go
index f6e3fca06..edddf026e 100644
--- a/cmd/podman/inspect/inspect.go
+++ b/cmd/podman/inspect/inspect.go
@@ -3,6 +3,7 @@ package inspect
import (
"context"
"encoding/json" // due to a bug in json-iterator it cannot be used here
+ "errors"
"fmt"
"os"
"regexp"
@@ -16,7 +17,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -41,7 +41,7 @@ func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions {
return &opts
}
-// Inspect inspects the specified container/image names or IDs.
+// Inspect inspects the specified container/image/pod/volume names or IDs.
func Inspect(namesOrIDs []string, options entities.InspectOptions) error {
inspector, err := newInspector(options)
if err != nil {
@@ -64,19 +64,19 @@ func newInspector(options entities.InspectOptions) (*inspector, error) {
case common.ImageType, common.ContainerType, common.AllType, common.PodType, common.NetworkType, common.VolumeType:
// Valid types.
default:
- return nil, errors.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", options.Type,
+ return nil, fmt.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", options.Type,
common.ImageType, common.ContainerType, common.PodType, common.NetworkType, common.VolumeType, common.AllType)
}
if options.Type == common.ImageType {
if options.Latest {
- return nil, errors.Errorf("latest is not supported for type %q", common.ImageType)
+ return nil, fmt.Errorf("latest is not supported for type %q", common.ImageType)
}
if options.Size {
- return nil, errors.Errorf("size is not supported for type %q", common.ImageType)
+ return nil, fmt.Errorf("size is not supported for type %q", common.ImageType)
}
}
if options.Type == common.PodType && options.Size {
- return nil, errors.Errorf("size is not supported for type %q", common.PodType)
+ return nil, fmt.Errorf("size is not supported for type %q", common.PodType)
}
podOpts := entities.PodInspectOptions{
Latest: options.Latest,
@@ -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()
@@ -145,8 +145,7 @@ func (i *inspector) inspect(namesOrIDs []string) error {
i.podOptions.NameOrID = pod
podData, err := i.containerEngine.PodInspect(ctx, i.podOptions)
if err != nil {
- cause := errors.Cause(err)
- if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) {
+ if !strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) {
errs = []error{err}
} else {
return err
@@ -159,8 +158,7 @@ func (i *inspector) inspect(namesOrIDs []string) error {
if i.podOptions.Latest { // latest means there are no names in the namesOrID array
podData, err := i.containerEngine.PodInspect(ctx, i.podOptions)
if err != nil {
- cause := errors.Cause(err)
- if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) {
+ if !strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) {
errs = []error{err}
} else {
return err
@@ -189,7 +187,7 @@ func (i *inspector) inspect(namesOrIDs []string) error {
data = append(data, volumeData[i])
}
default:
- return errors.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", i.options.Type,
+ return fmt.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", i.options.Type,
common.ImageType, common.ContainerType, common.PodType, common.NetworkType, common.VolumeType, common.AllType)
}
// Always print an empty array
@@ -218,7 +216,7 @@ func (i *inspector) inspect(namesOrIDs []string) error {
fmt.Fprintf(os.Stderr, "error inspecting object: %v\n", err)
}
}
- return errors.Errorf("inspecting object: %v", errs[0])
+ return fmt.Errorf("inspecting object: %w", errs[0])
}
return nil
}
@@ -249,7 +247,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)
@@ -287,8 +285,7 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]inte
i.podOptions.NameOrID = name
podData, err := i.containerEngine.PodInspect(ctx, i.podOptions)
if err != nil {
- cause := errors.Cause(err)
- if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) {
+ if !strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) {
return nil, nil, err
}
} else {
@@ -296,7 +293,7 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]inte
continue
}
if len(errs) > 0 {
- allErrs = append(allErrs, errors.Errorf("no such object: %q", name))
+ allErrs = append(allErrs, fmt.Errorf("no such object: %q", name))
continue
}
}
diff --git a/cmd/podman/machine/info.go b/cmd/podman/machine/info.go
new file mode 100644
index 000000000..9932027d8
--- /dev/null
+++ b/cmd/podman/machine/info.go
@@ -0,0 +1,182 @@
+//go:build amd64 || arm64
+// +build amd64 arm64
+
+package machine
+
+import (
+ "fmt"
+ "html/template"
+ "os"
+ "runtime"
+
+ "github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/config"
+ "github.com/containers/common/pkg/report"
+ "github.com/containers/podman/v4/cmd/podman/common"
+ "github.com/containers/podman/v4/cmd/podman/registry"
+ "github.com/containers/podman/v4/cmd/podman/validate"
+ "github.com/containers/podman/v4/libpod/define"
+ "github.com/containers/podman/v4/pkg/machine"
+ "github.com/ghodss/yaml"
+ "github.com/spf13/cobra"
+)
+
+var infoDescription = `Display information pertaining to the machine host.`
+
+var (
+ infoCmd = &cobra.Command{
+ Use: "info [options]",
+ Short: "Display machine host info",
+ Long: infoDescription,
+ PersistentPreRunE: rootlessOnly,
+ RunE: info,
+ Args: validate.NoArgs,
+ ValidArgsFunction: completion.AutocompleteNone,
+ Example: `podman machine info`,
+ }
+)
+
+var (
+ inFormat string
+)
+
+// Info contains info on the machine host and version info
+type Info struct {
+ Host *HostInfo `json:"Host"`
+ Version define.Version `json:"Version"`
+}
+
+// HostInfo contains info on the machine host
+type HostInfo struct {
+ Arch string `json:"Arch"`
+ CurrentMachine string `json:"CurrentMachine"`
+ DefaultMachine string `json:"DefaultMachine"`
+ EventsDir string `json:"EventsDir"`
+ MachineConfigDir string `json:"MachineConfigDir"`
+ MachineImageDir string `json:"MachineImageDir"`
+ MachineState string `json:"MachineState"`
+ NumberOfMachines int `json:"NumberOfMachines"`
+ OS string `json:"OS"`
+ VMType string `json:"VMType"`
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Command: infoCmd,
+ Parent: machineCmd,
+ })
+
+ flags := infoCmd.Flags()
+ formatFlagName := "format"
+ flags.StringVarP(&inFormat, formatFlagName, "f", "", "Change the output format to JSON or a Go template")
+ _ = infoCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&define.Info{}))
+}
+
+func info(cmd *cobra.Command, args []string) error {
+ info := Info{}
+ version, err := define.GetVersion()
+ if err != nil {
+ return fmt.Errorf("error getting version info %w", err)
+ }
+ info.Version = version
+
+ host, err := hostInfo()
+ if err != nil {
+ return err
+ }
+ info.Host = host
+
+ switch {
+ case report.IsJSON(inFormat):
+ b, err := json.MarshalIndent(info, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ case cmd.Flags().Changed("format"):
+ tmpl := template.New(cmd.Name()).Funcs(template.FuncMap(report.DefaultFuncs))
+ inFormat = report.NormalizeFormat(inFormat)
+ tmpl, err := tmpl.Parse(inFormat)
+ if err != nil {
+ return err
+ }
+ return tmpl.Execute(os.Stdout, info)
+ default:
+ b, err := yaml.Marshal(info)
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ }
+
+ return nil
+}
+
+func hostInfo() (*HostInfo, error) {
+ host := HostInfo{}
+
+ host.Arch = runtime.GOARCH
+ host.OS = runtime.GOOS
+
+ provider := GetSystemDefaultProvider()
+ var listOpts machine.ListOptions
+ listResponse, err := provider.List(listOpts)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get machines %w", err)
+ }
+
+ host.NumberOfMachines = len(listResponse)
+
+ cfg, err := config.ReadCustomConfig()
+ if err != nil {
+ return nil, err
+ }
+
+ // Default state of machine is stopped
+ host.MachineState = "Stopped"
+ for _, vm := range listResponse {
+ // Set default machine if found
+ if vm.Name == cfg.Engine.ActiveService {
+ host.DefaultMachine = vm.Name
+ }
+ // If machine is running or starting, it is automatically the current machine
+ if vm.Running {
+ host.CurrentMachine = vm.Name
+ host.MachineState = "Running"
+ } else if vm.Starting {
+ host.CurrentMachine = vm.Name
+ host.MachineState = "Starting"
+ }
+ }
+ // If no machines are starting or running, set current machine to default machine
+ // If no default machines are found, do not report a default machine or a state
+ if host.CurrentMachine == "" {
+ if host.DefaultMachine == "" {
+ host.MachineState = ""
+ } else {
+ host.CurrentMachine = host.DefaultMachine
+ }
+ }
+
+ host.VMType = provider.VMType()
+
+ dataDir, err := machine.GetDataDir(host.VMType)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get machine image dir")
+ }
+ host.MachineImageDir = dataDir
+
+ confDir, err := machine.GetConfDir(host.VMType)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get machine config dir %w", err)
+ }
+ host.MachineConfigDir = confDir
+
+ eventsDir, err := eventSockDir()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get events dir: %w", err)
+ }
+ host.EventsDir = eventsDir
+
+ return &host, nil
+}
diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go
index 6c31f3531..def3334e8 100644
--- a/cmd/podman/machine/init.go
+++ b/cmd/podman/machine/init.go
@@ -11,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/machine"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -20,21 +19,20 @@ var (
Use: "init [options] [NAME]",
Short: "Initialize a virtual machine",
Long: "initialize a virtual machine ",
+ PersistentPreRunE: rootlessOnly,
RunE: initMachine,
Args: cobra.MaximumNArgs(1),
Example: `podman machine init myvm`,
ValidArgsFunction: completion.AutocompleteNone,
}
-)
-var (
initOpts = machine.InitOptions{}
defaultMachineName = machine.DefaultMachineName
now bool
)
// maxMachineNameSize is set to thirty to limit huge machine names primarily
-// because macos has a much smaller file size limit.
+// because macOS has a much smaller file size limit.
const maxMachineNameSize = 30
func init() {
@@ -111,7 +109,6 @@ func init() {
flags.BoolVar(&initOpts.Rootful, rootfulFlagName, false, "Whether this machine should prefer rootful container execution")
}
-// TODO should we allow for a users to append to the qemu cmdline?
func initMachine(cmd *cobra.Command, args []string) error {
var (
err error
@@ -122,12 +119,12 @@ func initMachine(cmd *cobra.Command, args []string) error {
initOpts.Name = defaultMachineName
if len(args) > 0 {
if len(args[0]) > maxMachineNameSize {
- return errors.New("machine name must be 30 characters or less")
+ return fmt.Errorf("machine name %q must be %d characters or less", args[0], maxMachineNameSize)
}
initOpts.Name = args[0]
}
if _, err := provider.LoadVMByName(initOpts.Name); err == nil {
- return errors.Wrap(machine.ErrVMAlreadyExists, initOpts.Name)
+ return fmt.Errorf("%s: %w", initOpts.Name, machine.ErrVMAlreadyExists)
}
for idx, vol := range initOpts.Volumes {
initOpts.Volumes[idx] = os.ExpandEnv(vol)
@@ -150,17 +147,12 @@ func initMachine(cmd *cobra.Command, args []string) error {
fmt.Println("Machine init complete")
if now {
- err = vm.Start(initOpts.Name, machine.StartOptions{})
- if err == nil {
- fmt.Printf("Machine %q started successfully\n", initOpts.Name)
- newMachineEvent(events.Start, events.Event{Name: initOpts.Name})
- }
- } else {
- extra := ""
- if initOpts.Name != defaultMachineName {
- extra = " " + initOpts.Name
- }
- fmt.Printf("To start your machine run:\n\n\tpodman machine start%s\n\n", extra)
+ return start(cmd, args)
+ }
+ extra := ""
+ if initOpts.Name != defaultMachineName {
+ extra = " " + initOpts.Name
}
+ fmt.Printf("To start your machine run:\n\n\tpodman machine start%s\n\n", extra)
return err
}
diff --git a/cmd/podman/machine/inspect.go b/cmd/podman/machine/inspect.go
index 4600a2b6d..d69c382f2 100644
--- a/cmd/podman/machine/inspect.go
+++ b/cmd/podman/machine/inspect.go
@@ -20,6 +20,7 @@ var (
Use: "inspect [options] [MACHINE...]",
Short: "Inspect an existing machine",
Long: "Provide details on a managed virtual machine",
+ PersistentPreRunE: rootlessOnly,
RunE: inspect,
Example: `podman machine inspect myvm`,
ValidArgsFunction: autocompleteMachine,
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go
index 5254d50cf..dd4a86697 100644
--- a/cmd/podman/machine/list.go
+++ b/cmd/podman/machine/list.go
@@ -4,6 +4,7 @@
package machine
import (
+ "fmt"
"os"
"sort"
"strconv"
@@ -15,9 +16,9 @@ import (
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
+ "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/machine"
"github.com/docker/go-units"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -27,6 +28,7 @@ var (
Aliases: []string{"ls"},
Short: "List machines",
Long: "List managed virtual machines.",
+ PersistentPreRunE: rootlessOnly,
RunE: list,
Args: validate.NoArgs,
ValidArgsFunction: completion.AutocompleteNone,
@@ -43,22 +45,6 @@ type listFlagType struct {
quiet bool
}
-type ListReporter struct {
- Name string
- Default bool
- Created string
- Running bool
- LastUp string
- Stream string
- VMType string
- CPUs uint64
- Memory string
- DiskSize string
- Port int
- RemoteUsername string
- IdentityPath string
-}
-
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: lsCmd,
@@ -68,7 +54,7 @@ func init() {
flags := lsCmd.Flags()
formatFlagName := "format"
flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\t{{.CPUs}}\t{{.Memory}}\t{{.DiskSize}}\n", "Format volume output using JSON or a Go template")
- _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&ListReporter{}))
+ _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.ListReporter{}))
flags.BoolVar(&listFlag.noHeading, "noheading", false, "Do not print headers")
flags.BoolVarP(&listFlag.quiet, "quiet", "q", false, "Show only machine names")
}
@@ -87,7 +73,7 @@ func list(cmd *cobra.Command, args []string) error {
provider := GetSystemDefaultProvider()
listResponse, err = provider.List(opts)
if err != nil {
- return errors.Wrap(err, "error listing vms")
+ return fmt.Errorf("listing vms: %w", err)
}
// Sort by last run
@@ -121,8 +107,8 @@ func list(cmd *cobra.Command, args []string) error {
return outputTemplate(cmd, machineReporter)
}
-func outputTemplate(cmd *cobra.Command, responses []*ListReporter) error {
- headers := report.Headers(ListReporter{}, map[string]string{
+func outputTemplate(cmd *cobra.Command, responses []*entities.ListReporter) error {
+ headers := report.Headers(entities.ListReporter{}, map[string]string{
"LastUp": "LAST UP",
"VmType": "VM TYPE",
"CPUs": "CPUS",
@@ -137,7 +123,7 @@ func outputTemplate(cmd *cobra.Command, responses []*ListReporter) error {
switch {
case cmd.Flags().Changed("format"):
row = cmd.Flag("format").Value.String()
- listFlag.noHeading = !report.HasTable(row)
+ printHeader = report.HasTable(row)
row = report.NormalizeFormat(row)
default:
row = cmd.Flag("format").Value.String()
@@ -156,7 +142,7 @@ func outputTemplate(cmd *cobra.Command, responses []*ListReporter) error {
defer w.Flush()
if printHeader {
if err := tmpl.Execute(w, headers); err != nil {
- return errors.Wrapf(err, "failed to write report column headers")
+ return fmt.Errorf("failed to write report column headers: %w", err)
}
}
return tmpl.Execute(w, responses)
@@ -181,15 +167,15 @@ func streamName(imageStream string) string {
return imageStream
}
-func toMachineFormat(vms []*machine.ListResponse) ([]*ListReporter, error) {
+func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
- machineResponses := make([]*ListReporter, 0, len(vms))
+ machineResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
- response := new(ListReporter)
+ response := new(entities.ListReporter)
response.Default = vm.Name == cfg.Engine.ActiveService
response.Name = vm.Name
response.Running = vm.Running
@@ -209,25 +195,29 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*ListReporter, error) {
return machineResponses, nil
}
-func toHumanFormat(vms []*machine.ListResponse) ([]*ListReporter, error) {
+func toHumanFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
- humanResponses := make([]*ListReporter, 0, len(vms))
+ humanResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
- response := new(ListReporter)
+ response := new(entities.ListReporter)
if vm.Name == cfg.Engine.ActiveService {
response.Name = vm.Name + "*"
response.Default = true
} 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/machine.go b/cmd/podman/machine/machine.go
index 5a8a06b9d..0618337cc 100644
--- a/cmd/podman/machine/machine.go
+++ b/cmd/podman/machine/machine.go
@@ -101,12 +101,6 @@ func resolveEventSock() ([]string, error) {
return []string{sock}, nil
}
- xdg, err := util.GetRuntimeDir()
- if err != nil {
- logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err)
- return nil, nil
- }
-
re := regexp.MustCompile(`machine_events.*\.sock`)
sockPaths := make([]string, 0)
fn := func(path string, info os.DirEntry, err error) error {
@@ -125,8 +119,12 @@ func resolveEventSock() ([]string, error) {
sockPaths = append(sockPaths, path)
return nil
}
+ sockDir, err := eventSockDir()
+ if err != nil {
+ logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err)
+ }
- if err := filepath.WalkDir(filepath.Join(xdg, "podman"), fn); err != nil {
+ if err := filepath.WalkDir(sockDir, fn); err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, nil
}
@@ -135,6 +133,14 @@ func resolveEventSock() ([]string, error) {
return sockPaths, nil
}
+func eventSockDir() (string, error) {
+ xdg, err := util.GetRuntimeDir()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Join(xdg, "podman"), nil
+}
+
func newMachineEvent(status events.Status, event events.Event) {
openEventSock.Do(initMachineEvents)
diff --git a/cmd/podman/machine/machine_unix.go b/cmd/podman/machine/machine_unix.go
index b56d081ec..a2d9b9d8e 100644
--- a/cmd/podman/machine/machine_unix.go
+++ b/cmd/podman/machine/machine_unix.go
@@ -4,9 +4,20 @@
package machine
import (
+ "fmt"
"os"
+
+ "github.com/containers/podman/v4/pkg/rootless"
+ "github.com/spf13/cobra"
)
func isUnixSocket(file os.DirEntry) bool {
return file.Type()&os.ModeSocket != 0
}
+
+func rootlessOnly(cmd *cobra.Command, args []string) error {
+ if !rootless.IsRootless() {
+ return fmt.Errorf("cannot run command %q as root", cmd.CommandPath())
+ }
+ return nil
+}
diff --git a/cmd/podman/machine/machine_windows.go b/cmd/podman/machine/machine_windows.go
index ffd5d8827..bf1240868 100644
--- a/cmd/podman/machine/machine_windows.go
+++ b/cmd/podman/machine/machine_windows.go
@@ -3,9 +3,18 @@ package machine
import (
"os"
"strings"
+
+ "github.com/spf13/cobra"
)
func isUnixSocket(file os.DirEntry) bool {
// Assume a socket on Windows, since sock mode is not supported yet https://github.com/golang/go/issues/33357
return !file.Type().IsDir() && strings.HasSuffix(file.Name(), ".sock")
}
+
+func rootlessOnly(cmd *cobra.Command, args []string) error {
+ // Rootless is not relevant on Windows. In the future rootless.IsRootless
+ // could be switched to return true on Windows, and other codepaths migrated
+
+ return nil
+}
diff --git a/cmd/podman/machine/rm.go b/cmd/podman/machine/rm.go
index a6e66265c..362c9a7d3 100644
--- a/cmd/podman/machine/rm.go
+++ b/cmd/podman/machine/rm.go
@@ -20,6 +20,7 @@ var (
Use: "rm [options] [MACHINE]",
Short: "Remove an existing machine",
Long: "Remove a managed virtual machine ",
+ PersistentPreRunE: rootlessOnly,
RunE: rm,
Args: cobra.MaximumNArgs(1),
Example: `podman machine rm myvm`,
diff --git a/cmd/podman/machine/set.go b/cmd/podman/machine/set.go
index 5777882da..1b9e1b2bd 100644
--- a/cmd/podman/machine/set.go
+++ b/cmd/podman/machine/set.go
@@ -18,6 +18,7 @@ var (
Use: "set [options] [NAME]",
Short: "Sets a virtual machine setting",
Long: "Sets an updatable virtual machine setting",
+ PersistentPreRunE: rootlessOnly,
RunE: setMachine,
Args: cobra.MaximumNArgs(1),
Example: `podman machine set --rootful=false`,
diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go
index 4a86da67a..cb2f62f51 100644
--- a/cmd/podman/machine/ssh.go
+++ b/cmd/podman/machine/ssh.go
@@ -4,22 +4,24 @@
package machine
import (
+ "fmt"
"net/url"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/registry"
+ "github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/machine"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
var (
sshCmd = &cobra.Command{
- Use: "ssh [options] [NAME] [COMMAND [ARG ...]]",
- Short: "SSH into an existing machine",
- Long: "SSH into a managed virtual machine ",
- RunE: ssh,
+ Use: "ssh [options] [NAME] [COMMAND [ARG ...]]",
+ Short: "SSH into an existing machine",
+ Long: "SSH into a managed virtual machine ",
+ PersistentPreRunE: rootlessOnly,
+ RunE: ssh,
Example: `podman machine ssh myvm
podman machine ssh myvm echo hello`,
ValidArgsFunction: autocompleteMachineSSH,
@@ -87,9 +89,10 @@ func ssh(cmd *cobra.Command, args []string) error {
vm, err = provider.LoadVMByName(vmName)
if err != nil {
- return errors.Wrapf(err, "vm %s not found", vmName)
+ return fmt.Errorf("vm %s not found: %w", vmName, err)
}
- return vm.SSH(vmName, sshOpts)
+ err = vm.SSH(vmName, sshOpts)
+ return utils.HandleOSExecError(err)
}
func remoteConnectionUsername() (string, error) {
diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go
index c9b99e63b..15a75522a 100644
--- a/cmd/podman/machine/start.go
+++ b/cmd/podman/machine/start.go
@@ -9,7 +9,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/machine"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -18,6 +17,7 @@ var (
Use: "start [MACHINE]",
Short: "Start an existing machine",
Long: "Start a managed virtual machine ",
+ PersistentPreRunE: rootlessOnly,
RunE: start,
Args: cobra.MaximumNArgs(1),
Example: `podman machine start myvm`,
@@ -54,9 +54,9 @@ func start(_ *cobra.Command, args []string) error {
}
if active {
if vmName == activeName {
- return errors.Wrapf(machine.ErrVMAlreadyRunning, "cannot start VM %s", vmName)
+ return fmt.Errorf("cannot start VM %s: %w", vmName, machine.ErrVMAlreadyRunning)
}
- return errors.Wrapf(machine.ErrMultipleActiveVM, "cannot start VM %s. VM %s is currently running", vmName, activeName)
+ return fmt.Errorf("cannot start VM %s. VM %s is currently running or starting: %w", vmName, activeName, machine.ErrMultipleActiveVM)
}
fmt.Printf("Starting machine %q\n", vmName)
if err := vm.Start(vmName, machine.StartOptions{}); err != nil {
diff --git a/cmd/podman/machine/stop.go b/cmd/podman/machine/stop.go
index 993662792..ce87a44c4 100644
--- a/cmd/podman/machine/stop.go
+++ b/cmd/podman/machine/stop.go
@@ -17,6 +17,7 @@ var (
Use: "stop [MACHINE]",
Short: "Stop an existing machine",
Long: "Stop a managed virtual machine ",
+ PersistentPreRunE: rootlessOnly,
RunE: stop,
Args: cobra.MaximumNArgs(1),
Example: `podman machine stop myvm`,
diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go
index b96a65c4a..9479e79a3 100644
--- a/cmd/podman/manifest/push.go
+++ b/cmd/podman/manifest/push.go
@@ -1,6 +1,7 @@
package manifest
import (
+ "fmt"
"io/ioutil"
"github.com/containers/common/pkg/auth"
@@ -11,7 +12,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -88,10 +88,10 @@ func push(cmd *cobra.Command, args []string) error {
listImageSpec := args[0]
destSpec := args[1]
if listImageSpec == "" {
- return errors.Errorf(`invalid image name "%s"`, listImageSpec)
+ return fmt.Errorf(`invalid image name "%s"`, listImageSpec)
}
if destSpec == "" {
- return errors.Errorf(`invalid destination "%s"`, destSpec)
+ return fmt.Errorf(`invalid destination "%s"`, destSpec)
}
if manifestPushOpts.CredentialsCLI != "" {
diff --git a/cmd/podman/manifest/remove.go b/cmd/podman/manifest/remove.go
index c32ffad78..4aa3b66b7 100644
--- a/cmd/podman/manifest/remove.go
+++ b/cmd/podman/manifest/remove.go
@@ -5,7 +5,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -31,7 +30,7 @@ func init() {
func remove(cmd *cobra.Command, args []string) error {
updatedListID, err := registry.ImageEngine().ManifestRemoveDigest(registry.Context(), args[0], args[1])
if err != nil {
- return errors.Wrapf(err, "error removing from manifest list %s", args[0])
+ return fmt.Errorf("removing from manifest list %s: %w", args[0], err)
}
fmt.Println(updatedListID)
return nil
diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go
index 84c58d4dc..2cf7023f3 100644
--- a/cmd/podman/networks/create.go
+++ b/cmd/podman/networks/create.go
@@ -1,6 +1,7 @@
package network
import (
+ "errors"
"fmt"
"net"
@@ -11,7 +12,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/parse"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -97,11 +97,11 @@ func networkCreate(cmd *cobra.Command, args []string) error {
var err error
networkCreateOptions.Labels, err = parse.GetAllLabels([]string{}, labels)
if err != nil {
- return errors.Wrap(err, "failed to parse labels")
+ return fmt.Errorf("failed to parse labels: %w", err)
}
networkCreateOptions.Options, err = parse.GetAllLabels([]string{}, opts)
if err != nil {
- return errors.Wrapf(err, "unable to parse options")
+ return fmt.Errorf("unable to parse options: %w", err)
}
network := types.Network{
@@ -181,11 +181,11 @@ func parseRange(iprange string) (*types.LeaseRange, error) {
startIP, err := util.FirstIPInSubnet(subnet)
if err != nil {
- return nil, errors.Wrap(err, "failed to get first ip in range")
+ return nil, fmt.Errorf("failed to get first ip in range: %w", err)
}
lastIP, err := util.LastIPInSubnet(subnet)
if err != nil {
- return nil, errors.Wrap(err, "failed to get last ip in range")
+ return nil, fmt.Errorf("failed to get last ip in range: %w", err)
}
return &types.LeaseRange{
StartIP: startIP,
diff --git a/cmd/podman/networks/reload.go b/cmd/podman/networks/reload.go
index 7b6323187..66248e9fb 100644
--- a/cmd/podman/networks/reload.go
+++ b/cmd/podman/networks/reload.go
@@ -21,7 +21,7 @@ var (
Long: networkReloadDescription,
RunE: networkReload,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman network reload --latest
diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go
index f71f59eea..c2d3f655f 100644
--- a/cmd/podman/networks/rm.go
+++ b/cmd/podman/networks/rm.go
@@ -1,6 +1,7 @@
package network
import (
+ "errors"
"fmt"
"strings"
@@ -10,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -78,15 +78,9 @@ func networkRm(cmd *cobra.Command, args []string) error {
}
func setExitCode(err error) {
- cause := errors.Cause(err)
- switch {
- case cause == define.ErrNoSuchNetwork:
+ if errors.Is(err, define.ErrNoSuchNetwork) || strings.Contains(err.Error(), define.ErrNoSuchNetwork.Error()) {
registry.SetExitCode(1)
- case strings.Contains(cause.Error(), define.ErrNoSuchNetwork.Error()):
- registry.SetExitCode(1)
- case cause == define.ErrNetworkInUse:
- registry.SetExitCode(2)
- case strings.Contains(cause.Error(), define.ErrNetworkInUse.Error()):
+ } else if errors.Is(err, define.ErrNetworkInUse) || strings.Contains(err.Error(), define.ErrNetworkInUse.Error()) {
registry.SetExitCode(2)
}
}
diff --git a/cmd/podman/parse/filters.go b/cmd/podman/parse/filters.go
index 8a10f2a97..e4ab942af 100644
--- a/cmd/podman/parse/filters.go
+++ b/cmd/podman/parse/filters.go
@@ -1,10 +1,9 @@
package parse
import (
+ "fmt"
"net/url"
"strings"
-
- "github.com/pkg/errors"
)
func FilterArgumentsIntoFilters(filters []string) (url.Values, error) {
@@ -12,7 +11,7 @@ func FilterArgumentsIntoFilters(filters []string) (url.Values, error) {
for _, f := range filters {
t := strings.SplitN(f, "=", 2)
if len(t) < 2 {
- return parsedFilters, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ return parsedFilters, fmt.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
parsedFilters.Add(t[0], t[1])
}
diff --git a/cmd/podman/parse/net.go b/cmd/podman/parse/net.go
index 870690db3..9228c7127 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
@@ -11,29 +10,13 @@ import (
"os"
"regexp"
"strings"
-
- "github.com/pkg/errors"
)
const (
- Protocol_TCP Protocol = 0
- Protocol_UDP Protocol = 1
+ 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
@@ -46,7 +29,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 {
@@ -89,16 +72,14 @@ func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) {
// There's an argument that we SHOULD be doing that parsing for
// all environment variables, even those sourced from files, but
// that would require a substantial rework.
- if err := parseEnvFile(labels, file); err != nil {
- // FIXME: parseEnvFile is using parseEnv, so we need to add extra
- // logic for labels.
+ if err := parseEnvOrLabelFile(labels, file, LabelType); err != nil {
return nil, err
}
}
for _, label := range inputLabels {
split := strings.SplitN(label, "=", 2)
if split[0] == "" {
- return nil, errors.Errorf("invalid label format: %q", label)
+ return nil, fmt.Errorf("invalid label format: %q", label)
}
value := ""
if len(split) > 1 {
@@ -109,18 +90,18 @@ func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) {
return labels, nil
}
-func parseEnv(env map[string]string, line string) error {
+func parseEnvOrLabel(env map[string]string, line, configType string) error {
data := strings.SplitN(line, "=", 2)
// catch invalid variables such as "=" or "=A"
if data[0] == "" {
- return errors.Errorf("invalid environment variable: %q", line)
+ return fmt.Errorf("invalid environment variable: %q", line)
}
// trim the front of a variable, but nothing else
name := strings.TrimLeft(data[0], whiteSpaces)
if strings.ContainsAny(name, whiteSpaces) {
- return errors.Errorf("name %q has white spaces, poorly formatted name", name)
+ return fmt.Errorf("name %q has white spaces, poorly formatted name", name)
}
if len(data) > 1 {
@@ -137,7 +118,7 @@ func parseEnv(env map[string]string, line string) error {
env[part[0]] = part[1]
}
}
- } else {
+ } else if configType == ENVType {
// if only a pass-through variable is given, clean it up.
if val, ok := os.LookupEnv(name); ok {
env[name] = val
@@ -147,8 +128,9 @@ func parseEnv(env map[string]string, line string) error {
return nil
}
-// parseEnvFile reads a file with environment variables enumerated by lines
-func parseEnvFile(env map[string]string, filename string) error {
+// parseEnvOrLabelFile reads a file with environment variables enumerated by lines
+// configType should be set to either "label" or "env" based on what type is being parsed
+func parseEnvOrLabelFile(envOrLabel map[string]string, filename, configType string) error {
fh, err := os.Open(filename)
if err != nil {
return err
@@ -161,7 +143,7 @@ func parseEnvFile(env map[string]string, filename string) error {
line := strings.TrimLeft(scanner.Text(), whiteSpaces)
// line is not empty, and not starting with '#'
if len(line) > 0 && !strings.HasPrefix(line, "#") {
- if err := parseEnv(env, line); err != nil {
+ if err := parseEnvOrLabel(envOrLabel, line, configType); err != nil {
return err
}
}
@@ -173,7 +155,7 @@ func parseEnvFile(env map[string]string, filename string) error {
// as it is currently not supported
func ValidateFileName(filename string) error {
if strings.Contains(filename, ":") {
- return errors.Errorf("invalid filename (should not contain ':') %q", filename)
+ return fmt.Errorf("invalid filename (should not contain ':') %q", filename)
}
return nil
}
@@ -182,10 +164,10 @@ func ValidateFileName(filename string) error {
func ValidURL(urlStr string) error {
url, err := url.ParseRequestURI(urlStr)
if err != nil {
- return errors.Wrapf(err, "invalid url %q", urlStr)
+ return fmt.Errorf("invalid url %q: %w", urlStr, err)
}
if url.Scheme == "" {
- return errors.Errorf("invalid url %q: missing scheme", urlStr)
+ return fmt.Errorf("invalid url %q: missing scheme", urlStr)
}
return nil
}
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/play/kube.go b/cmd/podman/play/kube.go
index f5b121009..8fd12baaf 100644
--- a/cmd/podman/play/kube.go
+++ b/cmd/podman/play/kube.go
@@ -1,6 +1,7 @@
package pods
import (
+ "errors"
"fmt"
"net"
"os"
@@ -16,7 +17,6 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -188,14 +188,14 @@ func kube(cmd *cobra.Command, args []string) error {
for _, annotation := range annotations {
splitN := strings.SplitN(annotation, "=", 2)
if len(splitN) > 2 {
- return errors.Errorf("annotation %q must include an '=' sign", annotation)
+ return fmt.Errorf("annotation %q must include an '=' sign", annotation)
}
if kubeOptions.Annotations == nil {
kubeOptions.Annotations = make(map[string]string)
}
annotation := splitN[1]
if len(annotation) > define.MaxKubeAnnotation {
- return errors.Errorf("annotation exceeds maximum size, %d, of kubernetes annotation: %s", define.MaxKubeAnnotation, annotation)
+ return fmt.Errorf("annotation exceeds maximum size, %d, of kubernetes annotation: %s", define.MaxKubeAnnotation, annotation)
}
kubeOptions.Annotations[splitN[0]] = annotation
}
@@ -235,7 +235,7 @@ func teardown(yamlfile string) error {
defer f.Close()
reports, err := registry.ContainerEngine().PlayKubeDown(registry.GetContext(), f, *options)
if err != nil {
- return errors.Wrap(err, yamlfile)
+ return fmt.Errorf("%v: %w", yamlfile, err)
}
// Output stopped pods
@@ -273,7 +273,7 @@ func playkube(yamlfile string) error {
defer f.Close()
report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), f, kubeOptions.PlayKubeOptions)
if err != nil {
- return errors.Wrap(err, yamlfile)
+ return fmt.Errorf("%s: %w", yamlfile, err)
}
// Print volumes report
for i, volume := range report.Volumes {
@@ -320,7 +320,7 @@ func playkube(yamlfile string) error {
}
if ctrsFailed > 0 {
- return errors.Errorf("failed to start %d containers", ctrsFailed)
+ return fmt.Errorf("failed to start %d containers", ctrsFailed)
}
return nil
diff --git a/cmd/podman/pods/clone.go b/cmd/podman/pods/clone.go
new file mode 100644
index 000000000..9558c6aed
--- /dev/null
+++ b/cmd/podman/pods/clone.go
@@ -0,0 +1,92 @@
+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/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 fmt.Errorf("must specify at least 1 argument: %w", define.ErrInvalidArg)
+ 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 62f820790..aea8a7229 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -2,6 +2,7 @@ package pods
import (
"context"
+ "errors"
"fmt"
"io/ioutil"
"os"
@@ -16,7 +17,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/containers"
"github.com/containers/podman/v4/cmd/podman/parse"
"github.com/containers/podman/v4/cmd/podman/registry"
- "github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/errorhandling"
@@ -24,7 +24,6 @@ import (
"github.com/containers/podman/v4/pkg/specgenutil"
"github.com/containers/podman/v4/pkg/util"
"github.com/docker/docker/pkg/parsers"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -36,12 +35,14 @@ var (
You can then start it at any time with the podman pod start <pod_id> command. The pod will be created with the initial state 'created'.`
createCommand = &cobra.Command{
- Use: "create [options]",
- Args: validate.NoArgs,
+ Use: "create [options] [NAME]",
+ Args: cobra.MaximumNArgs(1),
Short: "Create a new empty pod",
Long: podCreateDescription,
RunE: create,
ValidArgsFunction: completion.AutocompleteNone,
+ Example: `podman pod create
+ podman pod create --label foo=bar mypod`,
}
)
@@ -63,6 +64,7 @@ func init() {
})
flags := createCommand.Flags()
flags.SetInterspersed(false)
+ common.DefineCreateDefaults(&infraOptions)
common.DefineCreateFlags(createCommand, &infraOptions, true, false)
common.DefineNetFlags(createCommand)
@@ -115,11 +117,17 @@ func create(cmd *cobra.Command, args []string) error {
rawImageName string
podName string
)
+ if len(args) > 0 {
+ if len(createOptions.Name) > 0 {
+ return fmt.Errorf("cannot specify --name and NAME at the same time")
+ }
+ createOptions.Name = args[0]
+ }
labelFile = infraOptions.LabelFile
labels = infraOptions.Label
createOptions.Labels, err = parse.GetAllLabels(labelFile, labels)
if err != nil {
- return errors.Wrapf(err, "unable to process labels")
+ return fmt.Errorf("unable to process labels: %w", err)
}
if cmd.Flag("infra-image").Changed {
@@ -128,7 +136,7 @@ func create(cmd *cobra.Command, args []string) error {
img := imageName
if !createOptions.Infra {
if cmd.Flag("no-hosts").Changed {
- return fmt.Errorf("cannot specify no-hosts without an infra container")
+ return fmt.Errorf("cannot specify --no-hosts without an infra container")
}
flags := cmd.Flags()
createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags)
@@ -157,9 +165,14 @@ func create(cmd *cobra.Command, args []string) error {
return err
}
if strings.Contains(share, "cgroup") && shareParent {
- return errors.Wrapf(define.ErrInvalidArg, "cannot define the pod as the cgroup parent at the same time as joining the infra container's cgroupNS")
+ return fmt.Errorf("cannot define the pod as the cgroup parent at the same time as joining the infra container's cgroupNS: %w", define.ErrInvalidArg)
}
- createOptions.Share = strings.Split(share, ",")
+
+ if strings.HasPrefix(share, "+") {
+ createOptions.Share = append(createOptions.Share, strings.Split(specgen.DefaultKernelNamespaces, ",")...)
+ share = share[1:]
+ }
+ createOptions.Share = append(createOptions.Share, strings.Split(share, ",")...)
createOptions.ShareParent = &shareParent
if cmd.Flag("infra-command").Changed {
// Only send content to server side if user changed defaults
@@ -180,10 +193,10 @@ func create(cmd *cobra.Command, args []string) error {
if cmd.Flag("pod-id-file").Changed {
podIDFD, err = util.OpenExclusiveFile(podIDFile)
if err != nil && os.IsExist(err) {
- return errors.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", podIDFile)
+ return fmt.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", podIDFile)
}
if err != nil {
- return errors.Errorf("opening pod-id-file %s", podIDFile)
+ return fmt.Errorf("opening pod-id-file %s", podIDFile)
}
defer errorhandling.CloseQuiet(podIDFD)
defer errorhandling.SyncQuiet(podIDFD)
@@ -191,7 +204,7 @@ func create(cmd *cobra.Command, args []string) error {
if len(createOptions.Net.PublishPorts) > 0 {
if !createOptions.Infra {
- return errors.Errorf("you must have an infra container to publish port bindings to the host")
+ return fmt.Errorf("you must have an infra container to publish port bindings to the host")
}
}
@@ -218,7 +231,7 @@ func create(cmd *cobra.Command, args []string) error {
ret, err := parsers.ParseUintList(copy)
copy = ""
if err != nil {
- return errors.Wrapf(err, "could not parse list")
+ return fmt.Errorf("could not parse list: %w", err)
}
var vals []int
for ind, val := range ret {
@@ -264,6 +277,7 @@ func create(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
+
podSpec.Volumes = podSpec.InfraContainerSpec.Volumes
podSpec.ImageVolumes = podSpec.InfraContainerSpec.ImageVolumes
podSpec.OverlayVolumes = podSpec.InfraContainerSpec.OverlayVolumes
@@ -288,7 +302,7 @@ func create(cmd *cobra.Command, args []string) error {
if len(podIDFile) > 0 {
if err = ioutil.WriteFile(podIDFile, []byte(response.Id), 0644); err != nil {
- return errors.Wrapf(err, "failed to write pod ID to file")
+ return fmt.Errorf("failed to write pod ID to file: %w", err)
}
}
fmt.Println(response.Id)
diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go
index bb30fe6e6..082e8d9a1 100644
--- a/cmd/podman/pods/inspect.go
+++ b/cmd/podman/pods/inspect.go
@@ -2,6 +2,7 @@ package pods
import (
"context"
+ "errors"
"os"
"text/template"
@@ -10,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -49,10 +49,10 @@ func init() {
func inspect(cmd *cobra.Command, args []string) error {
if len(args) < 1 && !inspectOptions.Latest {
- return errors.Errorf("you must provide the name or id of a running pod")
+ return errors.New("you must provide the name or id of a running pod")
}
if len(args) > 0 && inspectOptions.Latest {
- return errors.Errorf("--latest and containers cannot be used together")
+ return errors.New("--latest and containers cannot be used together")
}
if !inspectOptions.Latest {
diff --git a/cmd/podman/pods/kill.go b/cmd/podman/pods/kill.go
index 7216e08bb..5d3b15dc3 100644
--- a/cmd/podman/pods/kill.go
+++ b/cmd/podman/pods/kill.go
@@ -22,7 +22,7 @@ var (
Long: podKillDescription,
RunE: kill,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompletePodsRunning,
Example: `podman pod kill podID
diff --git a/cmd/podman/pods/logs.go b/cmd/podman/pods/logs.go
index 28e7b7a43..0102d4b71 100644
--- a/cmd/podman/pods/logs.go
+++ b/cmd/podman/pods/logs.go
@@ -1,6 +1,8 @@
package pods
import (
+ "errors"
+ "fmt"
"os"
"github.com/containers/common/pkg/completion"
@@ -10,7 +12,6 @@ import (
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -100,7 +101,7 @@ func logs(_ *cobra.Command, args []string) error {
// parse time, error out if something is wrong
since, err := util.ParseInputTime(logsPodOptions.SinceRaw, true)
if err != nil {
- return errors.Wrapf(err, "error parsing --since %q", logsPodOptions.SinceRaw)
+ return fmt.Errorf("error parsing --since %q: %w", logsPodOptions.SinceRaw, err)
}
logsPodOptions.Since = since
}
@@ -108,14 +109,14 @@ func logs(_ *cobra.Command, args []string) error {
// parse time, error out if something is wrong
until, err := util.ParseInputTime(logsPodOptions.UntilRaw, false)
if err != nil {
- return errors.Wrapf(err, "error parsing --until %q", logsPodOptions.UntilRaw)
+ return fmt.Errorf("error parsing --until %q: %w", logsPodOptions.UntilRaw, err)
}
logsPodOptions.Until = until
}
// Remote can only process one container at a time
if registry.IsRemote() && logsPodOptions.ContainerName == "" {
- return errors.Wrapf(define.ErrInvalidArg, "-c or --container cannot be empty")
+ return fmt.Errorf("-c or --container cannot be empty: %w", define.ErrInvalidArg)
}
logsPodOptions.StdoutWriter = os.Stdout
diff --git a/cmd/podman/pods/pause.go b/cmd/podman/pods/pause.go
index adc54d171..389fb8415 100644
--- a/cmd/podman/pods/pause.go
+++ b/cmd/podman/pods/pause.go
@@ -22,7 +22,7 @@ var (
Long: podPauseDescription,
RunE: pause,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompletePodsRunning,
Example: `podman pod pause podID1 podID2
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index 1275e65dc..681c9c42e 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -2,6 +2,7 @@ package pods
import (
"context"
+ "errors"
"fmt"
"os"
"sort"
@@ -15,7 +16,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/docker/go-units"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -24,7 +24,7 @@ var (
// Command: podman pod _ps_
psCmd = &cobra.Command{
- Use: "ps [options]",
+ Use: "ps [options]",
Aliases: []string{"ls", "list"},
Short: "List pods",
Long: psDescription,
@@ -49,7 +49,6 @@ func init() {
flags.BoolVar(&psInput.CtrNames, "ctr-names", false, "Display the container names")
flags.BoolVar(&psInput.CtrIds, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated")
flags.BoolVar(&psInput.CtrStatus, "ctr-status", false, "Display the container status")
- // TODO should we make this a [] ?
filterFlagName := "filter"
flags.StringSliceVarP(&inputFilters, filterFlagName, "f", []string{}, "Filter output based on conditions given")
@@ -81,7 +80,7 @@ func pods(cmd *cobra.Command, _ []string) error {
for _, f := range inputFilters {
split := strings.SplitN(f, "=", 2)
if len(split) < 2 {
- return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ return fmt.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
psInput.Filters[split[0]] = append(psInput.Filters[split[0]], split[1])
}
@@ -212,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
}
@@ -226,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 ""
}
@@ -277,7 +276,7 @@ func sortPodPsOutput(sortBy string, lprs []*entities.ListPodsReport) error {
case "status":
sort.Sort(podPsSortedStatus{lprs})
default:
- return errors.Errorf("invalid option for --sort, options are: id, names, or number")
+ return errors.New("invalid option for --sort, options are: id, names, or number")
}
return nil
}
diff --git a/cmd/podman/pods/restart.go b/cmd/podman/pods/restart.go
index 6d624806a..a8e31ce07 100644
--- a/cmd/podman/pods/restart.go
+++ b/cmd/podman/pods/restart.go
@@ -22,7 +22,7 @@ var (
Long: podRestartDescription,
RunE: restart,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompletePods,
Example: `podman pod restart podID1 podID2
diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go
index 52a815534..2ffd968f9 100644
--- a/cmd/podman/pods/rm.go
+++ b/cmd/podman/pods/rm.go
@@ -2,6 +2,7 @@ package pods
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -13,7 +14,6 @@ import (
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/specgenutil"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -35,7 +35,7 @@ var (
Long: podRmDescription,
RunE: rm,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "pod-id-file")
},
ValidArgsFunction: common.AutocompletePods,
Example: `podman pod rm mywebserverpod
@@ -112,11 +112,7 @@ func removePods(namesOrIDs []string, rmOptions entities.PodRmOptions, printIDs b
}
func setExitCode(err error) {
- cause := errors.Cause(err)
- switch {
- case cause == define.ErrNoSuchPod:
- registry.SetExitCode(1)
- case strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()):
+ if errors.Is(err, define.ErrNoSuchPod) || strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) {
registry.SetExitCode(1)
}
}
diff --git a/cmd/podman/pods/start.go b/cmd/podman/pods/start.go
index b668cdd61..9436d34a5 100644
--- a/cmd/podman/pods/start.go
+++ b/cmd/podman/pods/start.go
@@ -31,7 +31,7 @@ var (
Long: podStartDescription,
RunE: start,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "pod-id-file")
},
ValidArgsFunction: common.AutocompletePods,
Example: `podman pod start podID
diff --git a/cmd/podman/pods/stop.go b/cmd/podman/pods/stop.go
index c8c3d2732..e8f82bee9 100644
--- a/cmd/podman/pods/stop.go
+++ b/cmd/podman/pods/stop.go
@@ -36,7 +36,7 @@ var (
Long: podStopDescription,
RunE: stop,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "pod-id-file")
},
ValidArgsFunction: common.AutocompletePodsRunning,
Example: `podman pod stop mywebserverpod
diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go
index 4e9c7a3ee..34f3d1c33 100644
--- a/cmd/podman/pods/top.go
+++ b/cmd/podman/pods/top.go
@@ -2,6 +2,7 @@ package pods
import (
"context"
+ "errors"
"fmt"
"os"
"strings"
@@ -12,7 +13,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -67,7 +67,7 @@ func top(_ *cobra.Command, args []string) error {
}
if len(args) < 1 && !topOptions.Latest {
- return errors.Errorf("you must provide the name or id of a running pod")
+ return errors.New("you must provide the name or id of a running pod")
}
if topOptions.Latest {
diff --git a/cmd/podman/pods/unpause.go b/cmd/podman/pods/unpause.go
index a308a82c3..47b29458b 100644
--- a/cmd/podman/pods/unpause.go
+++ b/cmd/podman/pods/unpause.go
@@ -22,11 +22,9 @@ var (
Long: podUnpauseDescription,
RunE: unpause,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
- // TODO have a function which shows only pods which could be unpaused
- // for now show all
- ValidArgsFunction: common.AutocompletePods,
+ ValidArgsFunction: common.AutoCompletePodsPause,
Example: `podman pod unpause podID1 podID2
podman pod unpause --all
podman pod unpause --latest`,
@@ -43,7 +41,7 @@ func init() {
Parent: podCmd,
})
flags := unpauseCommand.Flags()
- flags.BoolVarP(&unpauseOptions.All, "all", "a", false, "Pause all running pods")
+ flags.BoolVarP(&unpauseOptions.All, "all", "a", false, "Unpause all running pods")
validate.AddLatestFlag(unpauseCommand, &unpauseOptions.Latest)
}
diff --git a/cmd/podman/registry/config.go b/cmd/podman/registry/config.go
index b5c9b359c..cae618b44 100644
--- a/cmd/podman/registry/config.go
+++ b/cmd/podman/registry/config.go
@@ -11,7 +11,6 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
)
const (
@@ -92,14 +91,14 @@ func setXdgDirs() error {
return nil
}
- // Setup XDG_RUNTIME_DIR
+ // Set up XDG_RUNTIME_DIR
if _, found := os.LookupEnv("XDG_RUNTIME_DIR"); !found {
dir, err := util.GetRuntimeDir()
if err != nil {
return err
}
if err := os.Setenv("XDG_RUNTIME_DIR", dir); err != nil {
- return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR="+dir)
+ return fmt.Errorf("cannot set XDG_RUNTIME_DIR=%s: %w", dir, err)
}
}
@@ -110,14 +109,14 @@ func setXdgDirs() error {
}
}
- // Setup XDG_CONFIG_HOME
+ // Set up XDG_CONFIG_HOME
if _, found := os.LookupEnv("XDG_CONFIG_HOME"); !found {
cfgHomeDir, err := util.GetRootlessConfigHomeDir()
if err != nil {
return err
}
if err := os.Setenv("XDG_CONFIG_HOME", cfgHomeDir); err != nil {
- return errors.Wrapf(err, "cannot set XDG_CONFIG_HOME="+cfgHomeDir)
+ return fmt.Errorf("cannot set XDG_CONFIG_HOME=%s: %w", cfgHomeDir, err)
}
}
return nil
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 2bd4fa723..f28d92e2f 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -1,6 +1,7 @@
package main
import (
+ "errors"
"fmt"
"os"
"path/filepath"
@@ -20,7 +21,6 @@ import (
"github.com/containers/podman/v4/pkg/parallel"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/version"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -137,22 +137,20 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
if cmd.Flag("import").Changed {
runtime, err := crutils.CRGetRuntimeFromArchive(cmd.Flag("import").Value.String())
if err != nil {
- return errors.Wrapf(
- err,
- "failed extracting runtime information from %s",
- cmd.Flag("import").Value.String(),
+ return fmt.Errorf(
+ "failed extracting runtime information from %s: %w",
+ cmd.Flag("import").Value.String(), err,
)
}
- if cfg.RuntimePath == "" {
+
+ runtimeFlag := cmd.Root().Flag("runtime")
+ if runtimeFlag == nil {
+ return errors.New("failed to load --runtime flag")
+ }
+
+ if !runtimeFlag.Changed {
// If the user did not select a runtime, this takes the one from
// the checkpoint archives and tells Podman to use it for the restore.
- runtimeFlag := cmd.Root().Flags().Lookup("runtime")
- if runtimeFlag == nil {
- return errors.Errorf(
- "setting runtime to '%s' for restore",
- *runtime,
- )
- }
if err := runtimeFlag.Value.Set(*runtime); err != nil {
return err
}
@@ -161,7 +159,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
} else if cfg.RuntimePath != *runtime {
// If the user selected a runtime on the command-line this checks if
// it is the same then during checkpointing and errors out if not.
- return errors.Errorf(
+ return fmt.Errorf(
"checkpoint archive %s was created with runtime '%s' and cannot be restored with runtime '%s'",
cmd.Flag("import").Value.String(),
*runtime,
@@ -179,15 +177,15 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
var err error
cfg.URI, cfg.Identity, err = cfg.ActiveDestination()
if err != nil {
- return errors.Wrap(err, "failed to resolve active destination")
+ return fmt.Errorf("failed to resolve active destination: %w", err)
}
if err := cmd.Root().LocalFlags().Set("url", cfg.URI); err != nil {
- return errors.Wrap(err, "failed to override --url flag")
+ return fmt.Errorf("failed to override --url flag: %w", err)
}
if err := cmd.Root().LocalFlags().Set("identity", cfg.Identity); err != nil {
- return errors.Wrap(err, "failed to override --identity flag")
+ return fmt.Errorf("failed to override --identity flag: %w", err)
}
}
@@ -256,7 +254,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
}
if cfg.MaxWorks <= 0 {
- return errors.Errorf("maximum workers must be set to a positive number (got %d)", cfg.MaxWorks)
+ return fmt.Errorf("maximum workers must be set to a positive number (got %d)", cfg.MaxWorks)
}
if err := parallel.SetMaxThreads(uint(cfg.MaxWorks)); err != nil {
return err
@@ -298,12 +296,12 @@ func persistentPostRunE(cmd *cobra.Command, args []string) error {
if cmd.Flag("memory-profile").Changed {
f, err := os.Create(registry.PodmanConfig().MemoryProfile)
if err != nil {
- return errors.Wrap(err, "creating memory profile")
+ return fmt.Errorf("creating memory profile: %w", err)
}
defer f.Close()
runtime.GC() // get up-to-date GC statistics
if err := pprof.WriteHeapProfile(f); err != nil {
- return errors.Wrap(err, "writing memory profile")
+ return fmt.Errorf("writing memory profile: %w", err)
}
}
@@ -423,7 +421,7 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
// -s is deprecated due to conflict with -s on subcommands
storageDriverFlagName := "storage-driver"
pFlags.StringVar(&opts.StorageDriver, storageDriverFlagName, "", "Select which storage driver is used to manage storage of images and containers")
- _ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone) //TODO: what can we recommend here?
+ _ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, 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")
@@ -482,7 +480,7 @@ func resolveDestination() (string, string, string) {
cfg, err := config.ReadCustomConfig()
if err != nil {
- logrus.Warning(errors.Wrap(err, "unable to read local containers.conf"))
+ logrus.Warning(fmt.Errorf("unable to read local containers.conf: %w", err))
return "", registry.DefaultAPIAddress(), ""
}
@@ -495,7 +493,7 @@ func resolveDestination() (string, string, string) {
func formatError(err error) string {
var message string
- if errors.Cause(err) == define.ErrOCIRuntime {
+ if errors.Is(err, define.ErrOCIRuntime) {
// OCIRuntimeErrors include the reason for the failure in the
// second to last message in the error chain.
message = fmt.Sprintf(
diff --git a/cmd/podman/root_test.go b/cmd/podman/root_test.go
index 0a73afdc4..98a3de79d 100644
--- a/cmd/podman/root_test.go
+++ b/cmd/podman/root_test.go
@@ -1,12 +1,12 @@
package main
import (
+ "errors"
"fmt"
"strings"
"testing"
"github.com/containers/podman/v4/libpod/define"
- "github.com/pkg/errors"
)
func TestFormatError(t *testing.T) {
@@ -22,7 +22,7 @@ func TestFormatError(t *testing.T) {
func TestFormatOCIError(t *testing.T) {
expectedPrefix := "Error: "
expectedSuffix := "OCI runtime output"
- err := errors.Wrap(define.ErrOCIRuntime, expectedSuffix)
+ err := fmt.Errorf("%s: %w", expectedSuffix, define.ErrOCIRuntime)
output := formatError(err)
if !strings.HasPrefix(output, expectedPrefix) {
diff --git a/cmd/podman/secrets/create.go b/cmd/podman/secrets/create.go
index 01ee3d256..8ecfecf69 100644
--- a/cmd/podman/secrets/create.go
+++ b/cmd/podman/secrets/create.go
@@ -2,6 +2,7 @@ package secrets
import (
"context"
+ "errors"
"fmt"
"io"
"os"
@@ -11,7 +12,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -66,7 +66,7 @@ func create(cmd *cobra.Command, args []string) error {
case env:
envValue := os.Getenv(path)
if envValue == "" {
- return errors.Errorf("cannot create store secret data: environment variable %s is not set", path)
+ return fmt.Errorf("cannot create store secret data: environment variable %s is not set", path)
}
reader = strings.NewReader(envValue)
case path == "-" || path == "/dev/stdin":
diff --git a/cmd/podman/secrets/inspect.go b/cmd/podman/secrets/inspect.go
index 473d5620c..1fcc676b4 100644
--- a/cmd/podman/secrets/inspect.go
+++ b/cmd/podman/secrets/inspect.go
@@ -10,7 +10,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -78,7 +77,7 @@ func inspect(cmd *cobra.Command, args []string) error {
fmt.Fprintf(os.Stderr, "error inspecting secret: %v\n", err)
}
}
- return errors.Errorf("inspecting secret: %v", errs[0])
+ return fmt.Errorf("inspecting secret: %w", errs[0])
}
return nil
}
diff --git a/cmd/podman/secrets/list.go b/cmd/podman/secrets/list.go
index 558a16ccf..8b1956eab 100644
--- a/cmd/podman/secrets/list.go
+++ b/cmd/podman/secrets/list.go
@@ -2,6 +2,7 @@ package secrets
import (
"context"
+ "fmt"
"os"
"time"
@@ -13,7 +14,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/docker/go-units"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -108,7 +108,7 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.SecretListReport)
if !listFlag.noHeading {
if err := tmpl.Execute(w, headers); err != nil {
- return errors.Wrapf(err, "failed to write report column headers")
+ return fmt.Errorf("failed to write report column headers: %w", err)
}
}
return tmpl.Execute(w, responses)
diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go
index 387de3c58..191603718 100644
--- a/cmd/podman/system/connection/add.go
+++ b/cmd/podman/system/connection/add.go
@@ -2,25 +2,22 @@ package connection
import (
"encoding/json"
+ "errors"
"fmt"
"net"
"net/url"
"os"
- "os/user"
"regexp"
- "time"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/system"
"github.com/containers/podman/v4/libpod/define"
- "github.com/containers/podman/v4/pkg/terminal"
- "github.com/pkg/errors"
+ "github.com/containers/podman/v4/pkg/domain/utils"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh"
- "golang.org/x/crypto/ssh/agent"
)
var (
@@ -79,7 +76,7 @@ func add(cmd *cobra.Command, args []string) error {
// Default to ssh schema if none given
dest := args[1]
if match, err := regexp.Match("^[A-Za-z][A-Za-z0-9+.-]*://", []byte(dest)); err != nil {
- return errors.Wrapf(err, "invalid destination")
+ return fmt.Errorf("invalid destination: %w", err)
} else if !match {
dest = "ssh://" + dest
}
@@ -95,7 +92,7 @@ func add(cmd *cobra.Command, args []string) error {
switch uri.Scheme {
case "ssh":
if uri.User.Username() == "" {
- if uri.User, err = GetUserInfo(uri); err != nil {
+ if uri.User, err = utils.GetUserInfo(uri); err != nil {
return err
}
}
@@ -180,44 +177,20 @@ func add(cmd *cobra.Command, args []string) error {
return cfg.Write()
}
-func GetUserInfo(uri *url.URL) (*url.Userinfo, error) {
- var (
- usr *user.User
- err error
- )
- if u, found := os.LookupEnv("_CONTAINERS_ROOTLESS_UID"); found {
- usr, err = user.LookupId(u)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to lookup rootless user")
- }
- } else {
- usr, err = user.Current()
- if err != nil {
- return nil, errors.Wrapf(err, "failed to obtain current user")
- }
- }
-
- pw, set := uri.User.Password()
- if set {
- return url.UserPassword(usr.Username, pw), nil
- }
- return url.User(usr.Username), nil
-}
-
func getUDS(uri *url.URL, iden string) (string, error) {
- cfg, err := ValidateAndConfigure(uri, iden)
+ cfg, err := utils.ValidateAndConfigure(uri, iden)
if err != nil {
- return "", errors.Wrapf(err, "failed to validate")
+ return "", fmt.Errorf("failed to validate: %w", err)
}
dial, err := ssh.Dial("tcp", uri.Host, cfg)
if err != nil {
- return "", errors.Wrapf(err, "failed to connect")
+ return "", fmt.Errorf("failed to connect: %w", err)
}
defer dial.Close()
session, err := dial.NewSession()
if err != nil {
- return "", errors.Wrapf(err, "failed to create new ssh session on %q", uri.Host)
+ return "", fmt.Errorf("failed to create new ssh session on %q: %w", uri.Host, err)
}
defer session.Close()
@@ -226,94 +199,18 @@ func getUDS(uri *url.URL, iden string) (string, error) {
if v, found := os.LookupEnv("PODMAN_BINARY"); found {
podman = v
}
- infoJSON, err := ExecRemoteCommand(dial, podman+" info --format=json")
+ infoJSON, err := utils.ExecRemoteCommand(dial, podman+" info --format=json")
if err != nil {
return "", err
}
var info define.Info
if err := json.Unmarshal(infoJSON, &info); err != nil {
- return "", errors.Wrapf(err, "failed to parse 'podman info' results")
+ return "", fmt.Errorf("failed to parse 'podman info' results: %w", err)
}
if info.Host.RemoteSocket == nil || len(info.Host.RemoteSocket.Path) == 0 {
- return "", errors.Errorf("remote podman %q failed to report its UDS socket", uri.Host)
+ return "", fmt.Errorf("remote podman %q failed to report its UDS socket", uri.Host)
}
return info.Host.RemoteSocket.Path, nil
}
-
-// ValidateAndConfigure will take a ssh url and an identity key (rsa and the like) and ensure the information given is valid
-// iden iden can be blank to mean no identity key
-// once the function validates the information it creates and returns an ssh.ClientConfig.
-func ValidateAndConfigure(uri *url.URL, iden string) (*ssh.ClientConfig, error) {
- var signers []ssh.Signer
- passwd, passwdSet := uri.User.Password()
- if iden != "" { // iden might be blank if coming from image scp or if no validation is needed
- value := iden
- s, err := terminal.PublicKey(value, []byte(passwd))
- if err != nil {
- return nil, errors.Wrapf(err, "failed to read identity %q", value)
- }
- signers = append(signers, s)
- logrus.Debugf("SSH Ident Key %q %s %s", value, ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
- }
- if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found { // validate ssh information, specifically the unix file socket used by the ssh agent.
- logrus.Debugf("Found SSH_AUTH_SOCK %q, ssh-agent signer enabled", sock)
-
- c, err := net.Dial("unix", sock)
- if err != nil {
- return nil, err
- }
- agentSigners, err := agent.NewClient(c).Signers()
- if err != nil {
- return nil, err
- }
-
- signers = append(signers, agentSigners...)
-
- if logrus.IsLevelEnabled(logrus.DebugLevel) {
- for _, s := range agentSigners {
- logrus.Debugf("SSH Agent Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
- }
- }
- }
- var authMethods []ssh.AuthMethod // now we validate and check for the authorization methods, most notaibly public key authorization
- if len(signers) > 0 {
- var dedup = make(map[string]ssh.Signer)
- for _, s := range signers {
- fp := ssh.FingerprintSHA256(s.PublicKey())
- if _, found := dedup[fp]; found {
- logrus.Debugf("Dedup SSH Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
- }
- dedup[fp] = s
- }
-
- var uniq []ssh.Signer
- for _, s := range dedup {
- uniq = append(uniq, s)
- }
- authMethods = append(authMethods, ssh.PublicKeysCallback(func() ([]ssh.Signer, error) {
- return uniq, nil
- }))
- }
- if passwdSet { // if password authentication is given and valid, add to the list
- authMethods = append(authMethods, ssh.Password(passwd))
- }
- if len(authMethods) == 0 {
- authMethods = append(authMethods, ssh.PasswordCallback(func() (string, error) {
- pass, err := terminal.ReadPassword(fmt.Sprintf("%s's login password:", uri.User.Username()))
- return string(pass), err
- }))
- }
- tick, err := time.ParseDuration("40s")
- if err != nil {
- return nil, err
- }
- cfg := &ssh.ClientConfig{
- User: uri.User.Username(),
- Auth: authMethods,
- HostKeyCallback: ssh.InsecureIgnoreHostKey(),
- Timeout: tick,
- }
- return cfg, nil
-}
diff --git a/cmd/podman/system/connection/remove.go b/cmd/podman/system/connection/remove.go
index 463eae9fa..29bf98c43 100644
--- a/cmd/podman/system/connection/remove.go
+++ b/cmd/podman/system/connection/remove.go
@@ -1,11 +1,12 @@
package connection
import (
+ "errors"
+
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/system"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
diff --git a/cmd/podman/system/connection/shared.go b/cmd/podman/system/connection/shared.go
deleted file mode 100644
index 714ae827d..000000000
--- a/cmd/podman/system/connection/shared.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package connection
-
-import (
- "bytes"
-
- "github.com/pkg/errors"
- "golang.org/x/crypto/ssh"
-)
-
-// ExecRemoteCommand takes a ssh client connection and a command to run and executes the
-// command on the specified client. The function returns the Stdout from the client or the Stderr
-func ExecRemoteCommand(dial *ssh.Client, run string) ([]byte, error) {
- sess, err := dial.NewSession() // new ssh client session
- if err != nil {
- return nil, err
- }
- defer sess.Close()
-
- var buffer bytes.Buffer
- var bufferErr bytes.Buffer
- sess.Stdout = &buffer // output from client funneled into buffer
- sess.Stderr = &bufferErr // err form client funneled into buffer
- if err := sess.Run(run); err != nil { // run the command on the ssh client
- return nil, errors.Wrapf(err, bufferErr.String())
- }
- return buffer.Bytes(), nil
-}
diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go
index dad14df6b..5b8126be6 100644
--- a/cmd/podman/system/df.go
+++ b/cmd/podman/system/df.go
@@ -78,11 +78,11 @@ func printSummary(cmd *cobra.Command, reports *entities.SystemDfReport) error {
}
}
imageSummary := dfSummary{
- Type: "Images",
- Total: len(reports.Images),
- Active: active,
- size: size,
- reclaimable: reclaimable,
+ Type: "Images",
+ Total: len(reports.Images),
+ Active: active,
+ RawSize: size,
+ RawReclaimable: reclaimable,
}
dfSummaries = append(dfSummaries, &imageSummary)
@@ -100,11 +100,11 @@ func printSummary(cmd *cobra.Command, reports *entities.SystemDfReport) error {
conSize += c.RWSize
}
containerSummary := dfSummary{
- Type: "Containers",
- Total: len(reports.Containers),
- Active: conActive,
- size: conSize,
- reclaimable: conReclaimable,
+ Type: "Containers",
+ Total: len(reports.Containers),
+ Active: conActive,
+ RawSize: conSize,
+ RawReclaimable: conReclaimable,
}
dfSummaries = append(dfSummaries, &containerSummary)
@@ -120,11 +120,11 @@ func printSummary(cmd *cobra.Command, reports *entities.SystemDfReport) error {
volumesReclaimable += v.ReclaimableSize
}
volumeSummary := dfSummary{
- Type: "Local Volumes",
- Total: len(reports.Volumes),
- Active: activeVolumes,
- size: volumesSize,
- reclaimable: volumesReclaimable,
+ Type: "Local Volumes",
+ Total: len(reports.Volumes),
+ Active: activeVolumes,
+ RawSize: volumesSize,
+ RawReclaimable: volumesReclaimable,
}
dfSummaries = append(dfSummaries, &volumeSummary)
@@ -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()
@@ -277,22 +277,22 @@ func (d *dfVolume) Size() string {
}
type dfSummary struct {
- Type string
- Total int
- Active int
- size int64
- reclaimable int64
+ Type string
+ Total int
+ Active int
+ RawSize int64 `json:"Size"`
+ RawReclaimable int64 `json:"Reclaimable"`
}
func (d *dfSummary) Size() string {
- return units.HumanSize(float64(d.size))
+ return units.HumanSize(float64(d.RawSize))
}
func (d *dfSummary) Reclaimable() string {
percent := 0
// make sure to check this to prevent div by zero problems
- if d.size > 0 {
- percent = int(math.Round(float64(d.reclaimable) / float64(d.size) * float64(100)))
+ if d.RawSize > 0 {
+ percent = int(math.Round(float64(d.RawReclaimable) / float64(d.RawSize) * float64(100)))
}
- return fmt.Sprintf("%s (%d%%)", units.HumanSize(float64(d.reclaimable)), percent)
+ return fmt.Sprintf("%s (%d%%)", units.HumanSize(float64(d.RawReclaimable)), percent)
}
diff --git a/cmd/podman/system/dial_stdio.go b/cmd/podman/system/dial_stdio.go
index 8b665bedc..42ce65746 100644
--- a/cmd/podman/system/dial_stdio.go
+++ b/cmd/podman/system/dial_stdio.go
@@ -2,13 +2,15 @@ package system
import (
"context"
+ "fmt"
"io"
"os"
+ "errors"
+
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/bindings"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -40,15 +42,15 @@ func runDialStdio() error {
defer cancel()
bindCtx, err := bindings.NewConnection(ctx, cfg.URI)
if err != nil {
- return errors.Wrap(err, "failed to open connection to podman")
+ return fmt.Errorf("failed to open connection to podman: %w", err)
}
conn, err := bindings.GetClient(bindCtx)
if err != nil {
- return errors.Wrap(err, "failed to get connection after initialization")
+ return fmt.Errorf("failed to get connection after initialization: %w", err)
}
netConn, err := conn.GetDialer(bindCtx)
if err != nil {
- return errors.Wrap(err, "failed to open the raw stream connection")
+ return fmt.Errorf("failed to open the raw stream connection: %w", err)
}
defer netConn.Close()
@@ -95,7 +97,7 @@ func copier(to halfWriteCloser, from halfReadCloser, debugDescription string) er
}
}()
if _, err := io.Copy(to, from); err != nil {
- return errors.Wrapf(err, "error while Copy (%s)", debugDescription)
+ return fmt.Errorf("error while Copy (%s): %w", debugDescription, err)
}
return nil
}
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..6823d77ba 100644
--- a/cmd/podman/system/service_abi.go
+++ b/cmd/podman/system/service_abi.go
@@ -4,24 +4,46 @@
package system
import (
+ "errors"
"fmt"
"net"
"net/url"
"os"
"path/filepath"
+ "github.com/containers/common/pkg/cgroups"
"github.com/containers/podman/v4/cmd/podman/registry"
api "github.com/containers/podman/v4/pkg/api/server"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/domain/infra"
"github.com/containers/podman/v4/pkg/servicereaper"
+ "github.com/containers/podman/v4/utils"
"github.com/coreos/go-systemd/v22/activation"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/pflag"
"golang.org/x/sys/unix"
)
+// maybeMoveToSubCgroup moves the current process in a sub cgroup when
+// it is running in the root cgroup on a system that uses cgroupv2.
+func maybeMoveToSubCgroup() error {
+ unifiedMode, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return err
+ }
+ if !unifiedMode {
+ return nil
+ }
+ cgroup, err := utils.GetOwnCgroup()
+ if err != nil {
+ return err
+ }
+ if cgroup == "/" {
+ return utils.MoveUnderCgroupSubtree("init")
+ }
+ return nil
+}
+
func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities.ServiceOptions) error {
var (
listener net.Listener
@@ -46,11 +68,15 @@ 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 errors.New("unexpected fd received from systemd: cannot listen on it")
+ }
libpodRuntime.SetRemoteURI(listeners[0].Addr().String())
} else {
uri, err := url.Parse(opts.URI)
if err != nil {
- return errors.Errorf("%s is an invalid socket destination", opts.URI)
+ return fmt.Errorf("%s is an invalid socket destination", opts.URI)
}
switch uri.Scheme {
@@ -70,7 +96,7 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
} else {
listener, err = net.Listen(uri.Scheme, path)
if err != nil {
- return errors.Wrapf(err, "unable to create socket")
+ return fmt.Errorf("unable to create socket: %w", err)
}
}
case "tcp":
@@ -81,7 +107,7 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
}
listener, err = net.Listen(uri.Scheme, host)
if err != nil {
- return errors.Wrapf(err, "unable to create socket %v", host)
+ return fmt.Errorf("unable to create socket %v: %w", host, err)
}
default:
logrus.Debugf("Attempting API Service endpoint scheme %q", uri.Scheme)
@@ -99,6 +125,10 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
return err
}
+ if err := maybeMoveToSubCgroup(); err != nil {
+ return err
+ }
+
servicereaper.Start()
infra.StartWatcher(libpodRuntime)
server, err := api.NewServerWithSettings(libpodRuntime, listener, opts)
diff --git a/cmd/podman/system/unshare.go b/cmd/podman/system/unshare.go
index 0ae5b81ad..6d9c33b64 100644
--- a/cmd/podman/system/unshare.go
+++ b/cmd/podman/system/unshare.go
@@ -1,14 +1,14 @@
package system
import (
+ "errors"
"os"
- "os/exec"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v4/cmd/podman/registry"
+ "github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -47,35 +47,18 @@ func init() {
func unshare(cmd *cobra.Command, args []string) error {
if isRootless := rootless.IsRootless(); !isRootless {
- return errors.Errorf("please use unshare with rootless")
+ return errors.New("please use unshare with rootless")
}
// exec the specified command, if there is one
if len(args) < 1 {
// try to exec the shell, if one's set
shell, shellSet := os.LookupEnv("SHELL")
if !shellSet {
- return errors.Errorf("no command specified and no $SHELL specified")
+ return errors.New("no command specified and no $SHELL specified")
}
args = []string{shell}
}
err := registry.ContainerEngine().Unshare(registry.Context(), args, unshareOptions)
- if err != nil {
- if exitError, ok := err.(*exec.ExitError); ok {
- // the user command inside the unshare env has failed
- // we set the exit code, do not return the error to the user
- // otherwise "exit status X" will be printed
- registry.SetExitCode(exitError.ExitCode())
- return nil
- }
- // cmd.Run() can return fs.ErrNotExist, fs.ErrPermission or exec.ErrNotFound
- // follow podman run/exec standard with the exit codes
- if errors.Is(err, os.ErrNotExist) || errors.Is(err, exec.ErrNotFound) {
- registry.SetExitCode(127)
- } else if errors.Is(err, os.ErrPermission) {
- registry.SetExitCode(126)
- }
- return err
- }
- return nil
+ return utils.HandleOSExecError(err)
}
diff --git a/cmd/podman/utils/error.go b/cmd/podman/utils/error.go
index 2aaa71373..3efff0301 100644
--- a/cmd/podman/utils/error.go
+++ b/cmd/podman/utils/error.go
@@ -4,10 +4,12 @@ import (
"errors"
"fmt"
"os"
+ "os/exec"
"strconv"
"strings"
buildahCLI "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/podman/v4/cmd/podman/registry"
)
type OutputErrors []error
@@ -43,3 +45,33 @@ func ExitCodeFromBuildError(errorMsg string) (int, error) {
}
return buildahCLI.ExecErrorCodeGeneric, errors.New("message does not contains a valid exit code")
}
+
+// HandleOSExecError checks the given error for an exec.ExitError error and
+// sets the same podman exit code as the error.
+// No error will be returned in this case to make sure things like podman
+// unshare false work correctly without extra output.
+// When the exec file does not exists we set the exit code to 127, for
+// permission errors 126 is used as exit code. In this case we still return
+// the error so the user gets an error message.
+// If the error is nil it returns nil.
+func HandleOSExecError(err error) error {
+ if err == nil {
+ return nil
+ }
+ var exitError *exec.ExitError
+ if errors.As(err, &exitError) {
+ // the user command inside the unshare/ssh env has failed
+ // we set the exit code, do not return the error to the user
+ // otherwise "exit status X" will be printed
+ registry.SetExitCode(exitError.ExitCode())
+ return nil
+ }
+ // cmd.Run() can return fs.ErrNotExist, fs.ErrPermission or exec.ErrNotFound
+ // follow podman run/exec standard with the exit codes
+ if errors.Is(err, os.ErrNotExist) || errors.Is(err, exec.ErrNotFound) {
+ registry.SetExitCode(127)
+ } else if errors.Is(err, os.ErrPermission) {
+ registry.SetExitCode(126)
+ }
+ return err
+}
diff --git a/cmd/podman/utils/signals_linux.go b/cmd/podman/utils/signals_linux.go
deleted file mode 100644
index dd0507c0e..000000000
--- a/cmd/podman/utils/signals_linux.go
+++ /dev/null
@@ -1,15 +0,0 @@
-//go:build !windows
-// +build !windows
-
-package utils
-
-import (
- "os"
-
- "golang.org/x/sys/unix"
-)
-
-// Platform specific signal synonyms
-var (
- SIGHUP os.Signal = unix.SIGHUP
-)
diff --git a/cmd/podman/utils/signals_windows.go b/cmd/podman/utils/signals_windows.go
deleted file mode 100644
index e6fcc1b32..000000000
--- a/cmd/podman/utils/signals_windows.go
+++ /dev/null
@@ -1,15 +0,0 @@
-//go:build windows
-// +build windows
-
-package utils
-
-import (
- "os"
-
- "golang.org/x/sys/windows"
-)
-
-// Platform specific signal synonyms
-var (
- SIGHUP os.Signal = windows.SIGHUP
-)
diff --git a/cmd/podman/utils/utils.go b/cmd/podman/utils/utils.go
index 6fd6647d0..2ae123388 100644
--- a/cmd/podman/utils/utils.go
+++ b/cmd/podman/utils/utils.go
@@ -44,7 +44,7 @@ func PrintPodPruneResults(podPruneReports []*entities.PodPruneReport, heading bo
func PrintContainerPruneResults(containerPruneReports []*reports.PruneReport, heading bool) error {
var errs OutputErrors
- if heading && (len(containerPruneReports) > 0) {
+ if heading && len(containerPruneReports) > 0 {
fmt.Println("Deleted Containers")
}
for _, v := range containerPruneReports {
@@ -72,7 +72,7 @@ func PrintVolumePruneResults(volumePruneReport []*reports.PruneReport, heading b
}
func PrintImagePruneResults(imagePruneReports []*reports.PruneReport, heading bool) error {
- if heading {
+ if heading && len(imagePruneReports) > 0 {
fmt.Println("Deleted Images")
}
for _, r := range imagePruneReports {
@@ -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 669456bd3..39eedca64 100644
--- a/cmd/podman/validate/args.go
+++ b/cmd/podman/validate/args.go
@@ -1,12 +1,12 @@
package validate
import (
+ "errors"
"fmt"
"strconv"
"strings"
"github.com/containers/podman/v4/cmd/podman/registry"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -23,12 +23,12 @@ func SubCommandExists(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
suggestions := cmd.SuggestionsFor(args[0])
if len(suggestions) == 0 {
- return errors.Errorf("unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information", cmd.CommandPath(), args[0])
+ return fmt.Errorf("unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information", cmd.CommandPath(), args[0])
}
- 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"))
+ return fmt.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
- return errors.Errorf("missing command '%[1]s COMMAND'", cmd.CommandPath())
+ cmd.Help() //nolint: errcheck
+ return fmt.Errorf("missing command '%[1]s COMMAND'", cmd.CommandPath())
}
// IDOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag
@@ -44,101 +44,56 @@ func IDOrLatestArgs(cmd *cobra.Command, args []string) error {
return fmt.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath())
}
if len(args) > 0 && given {
- return fmt.Errorf("--latest and containers cannot be used together")
+ return errors.New("--latest and containers cannot be used together")
}
}
return nil
}
-// TODO: the two functions CheckAllLatestAndCIDFile and CheckAllLatestAndPodIDFile are almost identical.
-// It may be worth looking into generalizing the two a bit more and share code but time is scarce and
-// we only live once.
-
-// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly.
-// If cidfile is set, also check for the --cidfile flag.
+// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly for containers and pods
+// If idFileFlag is set is set, also checks for the --cidfile or --pod-id-file flag.
+// Note: this has been deprecated, use CheckAllLatestAndIDFile instead
func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error {
- var specifiedLatest bool
- argLen := len(args)
- if !registry.IsRemote() {
- specifiedLatest, _ = c.Flags().GetBool("latest")
- if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
- if !cidfile {
- return errors.New("unable to lookup values for 'latest' or 'all'")
- } else if c.Flags().Lookup("cidfile") == nil {
- return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'")
- }
- }
- }
-
- specifiedAll, _ := c.Flags().GetBool("all")
- specifiedCIDFile := false
- if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 {
- specifiedCIDFile = true
- }
-
- if specifiedCIDFile && (specifiedAll || specifiedLatest) {
- return errors.Errorf("--all, --latest and --cidfile cannot be used together")
- } else if specifiedAll && specifiedLatest {
- return errors.Errorf("--all and --latest cannot be used together")
- }
-
- if (argLen > 0) && specifiedAll {
- return errors.Errorf("no arguments are needed with --all")
- }
-
- if ignoreArgLen {
- return nil
- }
-
- if argLen > 0 {
- if specifiedLatest {
- return errors.Errorf("--latest and containers cannot be used together")
- } else if cidfile && (specifiedLatest || specifiedCIDFile) {
- return errors.Errorf("no arguments are needed with --latest or --cidfile")
- }
- }
-
- if specifiedCIDFile {
- return nil
- }
-
- if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile {
- return errors.Errorf("you must provide at least one name or id")
- }
- return nil
+ return CheckAllLatestAndIDFile(c, args, ignoreArgLen, "cidfile")
}
// CheckAllLatestAndPodIDFile checks that --all and --latest are used correctly.
// If withIDFile is set, also check for the --pod-id-file flag.
+// Note: this has been deprecated, use CheckAllLatestAndIDFile instead
func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bool, withIDFile bool) error {
+ return CheckAllLatestAndIDFile(c, args, ignoreArgLen, "pod-id-file")
+}
+
+// CheckAllLatestAndIDFile checks that --all and --latest are used correctly for containers and pods
+// If idFileFlag is set is set, also checks for the --cidfile or --pod-id-file flag.
+func CheckAllLatestAndIDFile(c *cobra.Command, args []string, ignoreArgLen bool, idFileFlag string) error {
var specifiedLatest bool
argLen := len(args)
if !registry.IsRemote() {
- // remote clients have no latest flag
specifiedLatest, _ = c.Flags().GetBool("latest")
if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
- if !withIDFile {
- return errors.New("unable to lookup values for 'latest' or 'all'")
- } else if c.Flags().Lookup("pod-id-file") == nil {
- return errors.New("unable to lookup values for 'latest', 'all' or 'pod-id-file'")
+ if idFileFlag == "" {
+ return errors.New("unable to look up values for 'latest' or 'all'")
+ } else if c.Flags().Lookup(idFileFlag) == nil {
+ return fmt.Errorf("unable to look up values for 'latest', 'all', or '%s'", idFileFlag)
}
}
}
specifiedAll, _ := c.Flags().GetBool("all")
- specifiedPodIDFile := false
- if pid, _ := c.Flags().GetStringArray("pod-id-file"); len(pid) > 0 {
- specifiedPodIDFile = true
+ specifiedIDFile := false
+ if cid, _ := c.Flags().GetStringArray(idFileFlag); len(cid) > 0 {
+ specifiedIDFile = true
}
- if specifiedPodIDFile && (specifiedAll || specifiedLatest) {
- return errors.Errorf("--all, --latest and --pod-id-file cannot be used together")
+ if specifiedIDFile && (specifiedAll || specifiedLatest) {
+ return fmt.Errorf("--all, --latest, and --%s cannot be used together", idFileFlag)
} else if specifiedAll && specifiedLatest {
- return errors.Errorf("--all and --latest cannot be used together")
+ return errors.New("--all and --latest cannot be used together")
}
if (argLen > 0) && specifiedAll {
- return errors.Errorf("no arguments are needed with --all")
+ return errors.New("no arguments are needed with --all")
}
if ignoreArgLen {
@@ -147,18 +102,18 @@ func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bo
if argLen > 0 {
if specifiedLatest {
- return errors.Errorf("--latest and pods cannot be used together")
- } else if withIDFile && (specifiedLatest || specifiedPodIDFile) {
- return errors.Errorf("no arguments are needed with --latest or --pod-id-file")
+ return errors.New("--latest and containers cannot be used together")
+ } else if idFileFlag != "" && (specifiedLatest || specifiedIDFile) {
+ return fmt.Errorf("no arguments are needed with --latest or --%s", idFileFlag)
}
}
- if specifiedPodIDFile {
+ if specifiedIDFile {
return nil
}
- if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedPodIDFile {
- return errors.Errorf("you must provide at least one name or id")
+ if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedIDFile {
+ return errors.New("you must provide at least one name or id")
}
return nil
}
diff --git a/cmd/podman/volumes/create.go b/cmd/podman/volumes/create.go
index 1668c72de..0d19fab47 100644
--- a/cmd/podman/volumes/create.go
+++ b/cmd/podman/volumes/create.go
@@ -8,7 +8,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/parse"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -17,6 +16,7 @@ var (
createCommand = &cobra.Command{
Use: "create [options] [NAME]",
+ Args: cobra.MaximumNArgs(1),
Short: "Create a new volume",
Long: createDescription,
RunE: create,
@@ -59,19 +59,16 @@ func create(cmd *cobra.Command, args []string) error {
var (
err error
)
- if len(args) > 1 {
- return errors.Errorf("too many arguments, create takes at most 1 argument")
- }
if len(args) > 0 {
createOpts.Name = args[0]
}
createOpts.Label, err = parse.GetAllLabels([]string{}, opts.Label)
if err != nil {
- return errors.Wrapf(err, "unable to process labels")
+ return fmt.Errorf("unable to process labels: %w", err)
}
createOpts.Options, err = parse.GetAllLabels([]string{}, opts.Opts)
if err != nil {
- return errors.Wrapf(err, "unable to process options")
+ return fmt.Errorf("unable to process options: %w", err)
}
response, err := registry.ContainerEngine().VolumeCreate(context.Background(), createOpts)
if err != nil {
diff --git a/cmd/podman/volumes/export.go b/cmd/podman/volumes/export.go
index 5086323f9..f9e08be87 100644
--- a/cmd/podman/volumes/export.go
+++ b/cmd/podman/volumes/export.go
@@ -2,14 +2,15 @@ package volumes
import (
"context"
+ "errors"
"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/pkg/domain/entities"
+ "github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/utils"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -58,10 +59,13 @@ func export(cmd *cobra.Command, args []string) error {
return errors.New("expects output path, use --output=[path]")
}
inspectOpts.Type = common.VolumeType
- volumeData, _, err := containerEngine.VolumeInspect(ctx, args, inspectOpts)
+ volumeData, errs, err := containerEngine.VolumeInspect(ctx, args, inspectOpts)
if err != nil {
return err
}
+ if len(errs) > 0 {
+ return errorhandling.JoinErrors(errs)
+ }
if len(volumeData) < 1 {
return errors.New("no volume data found")
}
diff --git a/cmd/podman/volumes/import.go b/cmd/podman/volumes/import.go
index 988c5536d..8f3c7f27e 100644
--- a/cmd/podman/volumes/import.go
+++ b/cmd/podman/volumes/import.go
@@ -1,6 +1,7 @@
package volumes
import (
+ "errors"
"fmt"
"os"
@@ -8,8 +9,8 @@ import (
"github.com/containers/podman/v4/cmd/podman/parse"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
+ "github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/utils"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -60,10 +61,14 @@ func importVol(cmd *cobra.Command, args []string) error {
}
inspectOpts.Type = common.VolumeType
- volumeData, _, err := containerEngine.VolumeInspect(ctx, volumes, inspectOpts)
+ inspectOpts.Type = common.VolumeType
+ volumeData, errs, err := containerEngine.VolumeInspect(ctx, volumes, inspectOpts)
if err != nil {
return err
}
+ if len(errs) > 0 {
+ return errorhandling.JoinErrors(errs)
+ }
if len(volumeData) < 1 {
return errors.New("no volume data found")
}
diff --git a/cmd/podman/volumes/inspect.go b/cmd/podman/volumes/inspect.go
index 7cf363f36..68ba2976a 100644
--- a/cmd/podman/volumes/inspect.go
+++ b/cmd/podman/volumes/inspect.go
@@ -1,12 +1,13 @@
package volumes
import (
+ "errors"
+
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/inspect"
"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"
)
diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go
index c14cf08bd..06118513d 100644
--- a/cmd/podman/volumes/list.go
+++ b/cmd/podman/volumes/list.go
@@ -2,6 +2,7 @@ package volumes
import (
"context"
+ "errors"
"fmt"
"os"
@@ -12,7 +13,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -119,7 +119,7 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.VolumeListReport)
if !(noHeading || cliOpts.Quiet || cmd.Flag("format").Changed) {
if err := tmpl.Execute(w, headers); err != nil {
- return errors.Wrapf(err, "failed to write report column headers")
+ return fmt.Errorf("failed to write report column headers: %w", err)
}
}
return tmpl.Execute(w, responses)
diff --git a/cmd/podman/volumes/reload.go b/cmd/podman/volumes/reload.go
new file mode 100644
index 000000000..d0d76fb88
--- /dev/null
+++ b/cmd/podman/volumes/reload.go
@@ -0,0 +1,52 @@
+package volumes
+
+import (
+ "fmt"
+
+ "github.com/containers/common/pkg/completion"
+ "github.com/containers/podman/v4/cmd/podman/registry"
+ "github.com/containers/podman/v4/cmd/podman/utils"
+ "github.com/containers/podman/v4/cmd/podman/validate"
+ "github.com/spf13/cobra"
+)
+
+var (
+ reloadDescription = `Check all configured volume plugins and update the libpod database with all available volumes.
+
+ Existing volumes are also removed from the database when they are no longer present in the plugin.`
+ reloadCommand = &cobra.Command{
+ Use: "reload",
+ Args: validate.NoArgs,
+ Short: "reload all volumes from volume plugins",
+ Long: reloadDescription,
+ RunE: reload,
+ ValidArgsFunction: completion.AutocompleteNone,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Command: reloadCommand,
+ Parent: volumeCmd,
+ })
+}
+
+func reload(cmd *cobra.Command, args []string) error {
+ report, err := registry.ContainerEngine().VolumeReload(registry.Context())
+ if err != nil {
+ return err
+ }
+ printReload("Added", report.Added)
+ printReload("Removed", report.Removed)
+ errs := (utils.OutputErrors)(report.Errors)
+ return errs.PrintErrors()
+}
+
+func printReload(typ string, values []string) {
+ if len(values) > 0 {
+ fmt.Println(typ + ":")
+ for _, name := range values {
+ fmt.Println(name)
+ }
+ }
+}
diff --git a/cmd/podman/volumes/rm.go b/cmd/podman/volumes/rm.go
index 2012b7d3a..c160b8623 100644
--- a/cmd/podman/volumes/rm.go
+++ b/cmd/podman/volumes/rm.go
@@ -2,6 +2,7 @@ package volumes
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -11,7 +12,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -80,15 +80,9 @@ func rm(cmd *cobra.Command, args []string) error {
}
func setExitCode(err error) {
- cause := errors.Cause(err)
- switch {
- case cause == define.ErrNoSuchVolume:
+ if errors.Is(err, define.ErrNoSuchVolume) || strings.Contains(err.Error(), define.ErrNoSuchVolume.Error()) {
registry.SetExitCode(1)
- case strings.Contains(cause.Error(), define.ErrNoSuchVolume.Error()):
- registry.SetExitCode(1)
- case cause == define.ErrVolumeBeingUsed:
- registry.SetExitCode(2)
- case strings.Contains(cause.Error(), define.ErrVolumeBeingUsed.Error()):
+ } else if errors.Is(err, define.ErrVolumeBeingUsed) || strings.Contains(err.Error(), define.ErrVolumeBeingUsed.Error()) {
registry.SetExitCode(2)
}
}
diff --git a/cmd/rootlessport/main.go b/cmd/rootlessport/main.go
index 5bd35a985..5410cd14a 100644
--- a/cmd/rootlessport/main.go
+++ b/cmd/rootlessport/main.go
@@ -6,6 +6,7 @@ package main
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -18,7 +19,6 @@ import (
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/common/libnetwork/types"
"github.com/containers/podman/v4/pkg/rootlessport"
- "github.com/pkg/errors"
rkport "github.com/rootless-containers/rootlesskit/pkg/port"
rkbuiltin "github.com/rootless-containers/rootlesskit/pkg/port/builtin"
rkportutil "github.com/rootless-containers/rootlesskit/pkg/port/portutil"
@@ -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)
@@ -269,16 +269,16 @@ func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error {
dec := json.NewDecoder(conn)
err := dec.Decode(&childIP)
if err != nil {
- return errors.Wrap(err, "rootless port failed to decode ports")
+ return fmt.Errorf("rootless port failed to decode ports: %w", err)
}
portStatus, err := pm.ListPorts(ctx)
if err != nil {
- return errors.Wrap(err, "rootless port failed to list ports")
+ return fmt.Errorf("rootless port failed to list ports: %w", err)
}
for _, status := range portStatus {
err = pm.RemovePort(ctx, status.ID)
if err != nil {
- return errors.Wrap(err, "rootless port failed to remove port")
+ return fmt.Errorf("rootless port failed to remove port: %w", err)
}
}
// add the ports with the new child IP
@@ -287,7 +287,7 @@ func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error {
status.Spec.ChildIP = childIP
_, err = pm.AddPort(ctx, status.Spec)
if err != nil {
- return errors.Wrap(err, "rootless port failed to add port")
+ return fmt.Errorf("rootless port failed to add port: %w", err)
}
}
return nil
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"
diff --git a/cmd/winpath/main.go b/cmd/winpath/main.go
index 6fbe72837..bb57e39de 100644
--- a/cmd/winpath/main.go
+++ b/cmd/winpath/main.go
@@ -12,6 +12,7 @@ import (
"syscall"
"unsafe"
+ "golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
@@ -26,6 +27,7 @@ const (
Environment = "Environment"
Add operation = iota
Remove
+ Open
NotSpecified
)
@@ -37,6 +39,8 @@ func main() {
op = Add
case "remove":
op = Remove
+ case "open":
+ op = Open
}
}
@@ -46,6 +50,14 @@ func main() {
os.Exit(ERR_BAD_ARGS)
}
+ // Hidden operation as a workaround for the installer
+ if op == Open && len(os.Args) > 2 {
+ if err := winOpenFile(os.Args[2]); err != nil {
+ os.Exit(OPERATION_FAILED)
+ }
+ os.Exit(0)
+ }
+
if err := modify(op); err != nil {
os.Exit(OPERATION_FAILED)
}
@@ -119,7 +131,7 @@ func removePathFromRegistry(path string) error {
k, err := registry.OpenKey(registry.CURRENT_USER, Environment, registry.READ|registry.WRITE)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
- // Nothing to cleanup, the Environment registry key does not exist.
+ // Nothing to clean up, the Environment registry key does not exist.
return nil
}
return err
@@ -182,3 +194,9 @@ func alert(caption string) int {
return int(ret)
}
+
+func winOpenFile(file string) error {
+ verb, _ := syscall.UTF16PtrFromString("open")
+ fileW, _ := syscall.UTF16PtrFromString(file)
+ return windows.ShellExecute(0, verb, fileW, nil, nil, windows.SW_NORMAL)
+}