summaryrefslogtreecommitdiff
path: root/cmd/podman/images
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman/images')
-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
19 files changed, 99 insertions, 523 deletions
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)