summaryrefslogtreecommitdiff
path: root/pkg/domain/infra
diff options
context:
space:
mode:
authorcdoern <cdoern@redhat.com>2022-05-23 14:12:48 -0400
committerCharlie Doern <cdoern@redhat.com>2022-06-28 08:54:19 -0400
commit6d3520e8b7d7f57d389da08d1c8104c2cfbdd016 (patch)
treea0c045201f5db57fd43e66380409376f602d24f4 /pkg/domain/infra
parentc66a489b75b7bc68c341f0ff39d7beef95569878 (diff)
downloadpodman-6d3520e8b7d7f57d389da08d1c8104c2cfbdd016.tar.gz
podman-6d3520e8b7d7f57d389da08d1c8104c2cfbdd016.tar.bz2
podman-6d3520e8b7d7f57d389da08d1c8104c2cfbdd016.zip
podman image scp remote support & podman image scp tagging
add support for podman-remote image scp as well as direct access via the API. This entailed a full rework of the layering of image scp functions as well as the usual API plugging and type creation also, implemented podman image scp tagging. which makes the syntax much more readable and allows users t tag the new image they are loading to the local/remote machine: allow users to pass a "new name" for the image they are transferring `podman tag` as implemented creates a new image im `image list` when tagging, so this does the same meaning that when transferring images with tags, podman on the remote machine/user will load two images ex: `podman image scp computer1::alpine computer2::foobar` creates alpine:latest and localhost/foobar on the remote host implementing tags means removal of the flexible syntax. In the currently released podman image scp, the user can either specify `podman image scp source::img dest::` or `podman image scp dest:: source::img`. However, with tags this task becomes really hard to check which is the image (src) and which is the new tag (dst). Removal of that streamlines the arg parsing process Signed-off-by: Charlie Doern <cdoern@redhat.com>
Diffstat (limited to 'pkg/domain/infra')
-rw-r--r--pkg/domain/infra/abi/images.go151
-rw-r--r--pkg/domain/infra/tunnel/images.go26
2 files changed, 109 insertions, 68 deletions
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index d63de2424..02aa5923d 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -3,6 +3,7 @@ package abi
import (
"context"
"fmt"
+ "io/fs"
"io/ioutil"
"net/url"
"os"
@@ -29,7 +30,6 @@ import (
domainUtils "github.com/containers/podman/v4/pkg/domain/utils"
"github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/pkg/rootless"
- "github.com/containers/podman/v4/utils"
"github.com/containers/storage"
dockerRef "github.com/docker/distribution/reference"
"github.com/opencontainers/go-digest"
@@ -350,22 +350,6 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri
}
return pushError
}
-
-// Transfer moves images between root and rootless storage so the user specified in the scp call can access and use the image modified by root
-func (ir *ImageEngine) Transfer(ctx context.Context, source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string) error {
- if source.User == "" {
- return errors.Wrapf(define.ErrInvalidArg, "you must define a user when transferring from root to rootless storage")
- }
- podman, err := os.Executable()
- if err != nil {
- return err
- }
- if rootless.IsRootless() && (len(dest.User) == 0 || dest.User == "root") { // if we are rootless and do not have a destination user we can just use sudo
- return transferRootless(source, dest, podman, parentFlags)
- }
- return transferRootful(source, dest, podman, parentFlags)
-}
-
func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error {
// Allow tagging manifest list instead of resolving instances from manifest
lookupOptions := &libimage.LookupImageOptions{ManifestList: true}
@@ -694,53 +678,32 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie
return nil, nil
}
-func getSigFilename(sigStoreDirPath string) (string, error) {
- sigFileSuffix := 1
- sigFiles, err := ioutil.ReadDir(sigStoreDirPath)
+func (ir *ImageEngine) Scp(ctx context.Context, src, dst string, parentFlags []string, quiet bool) error {
+ rep, source, dest, flags, err := domainUtils.ExecuteTransfer(src, dst, parentFlags, quiet)
if err != nil {
- return "", err
- }
- sigFilenames := make(map[string]bool)
- for _, file := range sigFiles {
- sigFilenames[file.Name()] = true
+ return err
}
- for {
- sigFilename := "signature-" + strconv.Itoa(sigFileSuffix)
- if _, exists := sigFilenames[sigFilename]; !exists {
- return sigFilename, nil
+ if (rep == nil && err == nil) && (source != nil && dest != nil) { // we need to execute the transfer
+ err := Transfer(ctx, *source, *dest, flags)
+ if err != nil {
+ return err
}
- sigFileSuffix++
- }
-}
-
-func localPathFromURI(url *url.URL) (string, error) {
- if url.Scheme != "file" {
- return "", errors.Errorf("writing to %s is not supported. Use a supported scheme", url.String())
}
- return url.Path, nil
+ return nil
}
-// putSignature creates signature and saves it to the signstore file
-func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStoreDir string, instanceDigest digest.Digest, dockerReference dockerRef.Reference, options entities.SignOptions) error {
- newSig, err := signature.SignDockerManifest(manifestBlob, dockerReference.String(), mech, options.SignBy)
- if err != nil {
- return err
- }
- signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, instanceDigest.Algorithm(), instanceDigest.Hex())
- if err := os.MkdirAll(signatureDir, 0751); err != nil {
- // The directory is allowed to exist
- if !os.IsExist(err) {
- return err
- }
+func Transfer(ctx context.Context, source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string) error {
+ if source.User == "" {
+ return errors.Wrapf(define.ErrInvalidArg, "you must define a user when transferring from root to rootless storage")
}
- sigFilename, err := getSigFilename(signatureDir)
+ podman, err := os.Executable()
if err != nil {
return err
}
- if err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644); err != nil {
- return err
+ if rootless.IsRootless() && (len(dest.User) == 0 || dest.User == "root") { // if we are rootless and do not have a destination user we can just use sudo
+ return transferRootless(source, dest, podman, parentFlags)
}
- return nil
+ return transferRootful(source, dest, podman, parentFlags)
}
// TransferRootless creates new podman processes using exec.Command and sudo, transferring images between the given source and destination users
@@ -763,7 +726,7 @@ func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOpt
} else {
cmdSave = exec.Command(podman)
}
- cmdSave = utils.CreateSCPCommand(cmdSave, saveCommand)
+ cmdSave = domainUtils.CreateSCPCommand(cmdSave, saveCommand)
logrus.Debugf("Executing save command: %q", cmdSave)
err := cmdSave.Run()
if err != nil {
@@ -776,8 +739,11 @@ func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOpt
} else {
cmdLoad = exec.Command(podman)
}
- cmdLoad = utils.CreateSCPCommand(cmdLoad, loadCommand)
+ cmdLoad = domainUtils.CreateSCPCommand(cmdLoad, loadCommand)
logrus.Debugf("Executing load command: %q", cmdLoad)
+ if len(dest.Tag) > 0 {
+ return domainUtils.ScpTag(cmdLoad, podman, dest)
+ }
return cmdLoad.Run()
}
@@ -833,11 +799,20 @@ func transferRootful(source entities.ImageScpOptions, dest entities.ImageScpOpti
return err
}
}
- err = execPodman(uSave, saveCommand)
+ _, err = execTransferPodman(uSave, saveCommand, false)
+ if err != nil {
+ return err
+ }
+ out, err := execTransferPodman(uLoad, loadCommand, (len(dest.Tag) > 0))
if err != nil {
return err
}
- return execPodman(uLoad, loadCommand)
+ if out != nil {
+ image := domainUtils.ExtractImage(out)
+ _, err := execTransferPodman(uLoad, []string{podman, "tag", image, dest.Tag}, false)
+ return err
+ }
+ return nil
}
func lookupUser(u string) (*user.User, error) {
@@ -847,10 +822,10 @@ func lookupUser(u string) (*user.User, error) {
return user.Lookup(u)
}
-func execPodman(execUser *user.User, command []string) error {
- cmdLogin, err := utils.LoginUser(execUser.Username)
+func execTransferPodman(execUser *user.User, command []string, needToTag bool) ([]byte, error) {
+ cmdLogin, err := domainUtils.LoginUser(execUser.Username)
if err != nil {
- return err
+ return nil, err
}
defer func() {
@@ -864,11 +839,11 @@ func execPodman(execUser *user.User, command []string) error {
cmd.Stdout = os.Stdout
uid, err := strconv.ParseInt(execUser.Uid, 10, 32)
if err != nil {
- return err
+ return nil, err
}
gid, err := strconv.ParseInt(execUser.Gid, 10, 32)
if err != nil {
- return err
+ return nil, err
}
cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: &syscall.Credential{
@@ -878,5 +853,55 @@ func execPodman(execUser *user.User, command []string) error {
NoSetGroups: false,
},
}
- return cmd.Run()
+ if needToTag {
+ cmd.Stdout = nil
+ return cmd.Output()
+ }
+ return nil, cmd.Run()
+}
+
+func getSigFilename(sigStoreDirPath string) (string, error) {
+ sigFileSuffix := 1
+ sigFiles, err := ioutil.ReadDir(sigStoreDirPath)
+ if err != nil {
+ return "", err
+ }
+ sigFilenames := make(map[string]bool)
+ for _, file := range sigFiles {
+ sigFilenames[file.Name()] = true
+ }
+ for {
+ sigFilename := "signature-" + strconv.Itoa(sigFileSuffix)
+ if _, exists := sigFilenames[sigFilename]; !exists {
+ return sigFilename, nil
+ }
+ sigFileSuffix++
+ }
+}
+
+func localPathFromURI(url *url.URL) (string, error) {
+ if url.Scheme != "file" {
+ return "", errors.Errorf("writing to %s is not supported. Use a supported scheme", url.String())
+ }
+ return url.Path, nil
+}
+
+// putSignature creates signature and saves it to the signstore file
+func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStoreDir string, instanceDigest digest.Digest, dockerReference dockerRef.Reference, options entities.SignOptions) error {
+ newSig, err := signature.SignDockerManifest(manifestBlob, dockerReference.String(), mech, options.SignBy)
+ if err != nil {
+ return err
+ }
+ signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, instanceDigest.Algorithm(), instanceDigest.Hex())
+ if err := os.MkdirAll(signatureDir, 0751); err != nil {
+ // The directory is allowed to exist
+ if !errors.Is(err, fs.ErrExist) {
+ return err
+ }
+ }
+ sigFilename, err := getSigFilename(signatureDir)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644)
}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 97838d596..b81b64161 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -2,6 +2,7 @@ package tunnel
import (
"context"
+ "fmt"
"io/ioutil"
"os"
"strconv"
@@ -12,7 +13,6 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/types"
- "github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/bindings/images"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/domain/entities/reports"
@@ -123,10 +123,6 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.
return &entities.ImagePullReport{Images: pulledImages}, nil
}
-func (ir *ImageEngine) Transfer(ctx context.Context, source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string) error {
- return errors.Wrapf(define.ErrNotImplemented, "cannot use the remote client to transfer images between root and rootless storage")
-}
-
func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageTagOptions) error {
options := new(images.TagOptions)
for _, newTag := range tags {
@@ -367,3 +363,23 @@ func (ir *ImageEngine) Shutdown(_ context.Context) {
func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) {
return nil, errors.New("not implemented yet")
}
+
+func (ir *ImageEngine) Scp(ctx context.Context, src, dst string, parentFlags []string, quiet bool) error {
+ options := new(images.ScpOptions)
+
+ var destination *string
+ if len(dst) > 1 {
+ destination = &dst
+ }
+ options.Quiet = &quiet
+ options.Destination = destination
+
+ rep, err := images.Scp(ir.ClientCtx, &src, destination, *options)
+ if err != nil {
+ return err
+ }
+
+ fmt.Println("Loaded Image(s):", rep.Id)
+
+ return nil
+}