diff options
Diffstat (limited to 'pkg/domain/infra/abi/images.go')
-rw-r--r-- | pkg/domain/infra/abi/images.go | 197 |
1 files changed, 109 insertions, 88 deletions
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index c3ec7dd8a..38008c7b9 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -2,7 +2,9 @@ package abi import ( "context" + "errors" "fmt" + "io/fs" "io/ioutil" "net/url" "os" @@ -29,12 +31,10 @@ 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" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -128,14 +128,14 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entiti func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entities.ImageMountOptions) ([]*entities.ImageMountReport, error) { if opts.All && len(nameOrIDs) > 0 { - return nil, errors.Errorf("cannot mix --all with images") + return nil, errors.New("cannot mix --all with images") } if os.Geteuid() != 0 { if driver := ir.Libpod.StorageConfig().GraphDriverName; driver != "vfs" { // Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part // of the mount command. - return nil, errors.Errorf("cannot mount using driver %s in rootless mode", driver) + return nil, fmt.Errorf("cannot mount using driver %s in rootless mode", driver) } became, ret, err := rootless.BecomeRootInUserNS("") @@ -159,10 +159,6 @@ func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entit mountReports := []*entities.ImageMountReport{} listMountsOnly := !opts.All && len(nameOrIDs) == 0 for _, i := range images { - // TODO: the .Err fields are not used. This pre-dates the - // libimage migration but should be addressed at some point. - // A quick glimpse at cmd/podman/image/mount.go suggests that - // the errors needed to be handled there as well. var mountPoint string var err error if listMountsOnly { @@ -198,7 +194,7 @@ func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entit func (ir *ImageEngine) Unmount(ctx context.Context, nameOrIDs []string, options entities.ImageUnmountOptions) ([]*entities.ImageUnmountReport, error) { if options.All && len(nameOrIDs) > 0 { - return nil, errors.Errorf("cannot mix --all with images") + return nil, errors.New("cannot mix --all with images") } listImagesOptions := &libimage.ListImagesOptions{} @@ -296,7 +292,7 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri case "v2s2", "docker": manifestType = manifest.DockerV2Schema2MediaType default: - return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format) + return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format) } pushOptions := &libimage.PushOptions{} @@ -354,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} @@ -543,12 +523,12 @@ func removeErrorsToExitCode(rmErrors []error) int { } for _, e := range rmErrors { - switch errors.Cause(e) { - case storage.ErrImageUnknown, storage.ErrLayerUnknown: + //nolint:gocritic + if errors.Is(e, storage.ErrImageUnknown) || errors.Is(e, storage.ErrLayerUnknown) { noSuchImageErrors = true - case storage.ErrImageUsedByContainer: + } else if errors.Is(e, storage.ErrImageUsedByContainer) { inUseErrors = true - default: + } else { otherErrors = true } } @@ -597,7 +577,7 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie rmErrors = libimageErrors - return //nolint + return } // Shutdown Libpod engine @@ -610,11 +590,11 @@ func (ir *ImageEngine) Shutdown(_ context.Context) { func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) { mech, err := signature.NewGPGSigningMechanism() if err != nil { - return nil, errors.Wrap(err, "error initializing GPG") + return nil, fmt.Errorf("error initializing GPG: %w", err) } defer mech.Close() if err := mech.SupportsSigning(); err != nil { - return nil, errors.Wrap(err, "signing is not supported") + return nil, fmt.Errorf("signing is not supported: %w", err) } sc := ir.Libpod.SystemContext() sc.DockerCertPath = options.CertDir @@ -624,11 +604,11 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie err = func() error { srcRef, err := alltransports.ParseImageName(signimage) if err != nil { - return errors.Wrapf(err, "error parsing image name") + return fmt.Errorf("error parsing image name: %w", err) } rawSource, err := srcRef.NewImageSource(ctx, sc) if err != nil { - return errors.Wrapf(err, "error getting image source") + return fmt.Errorf("error getting image source: %w", err) } defer func() { if err = rawSource.Close(); err != nil { @@ -637,17 +617,17 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie }() topManifestBlob, manifestType, err := rawSource.GetManifest(ctx, nil) if err != nil { - return errors.Wrapf(err, "error getting manifest blob") + return fmt.Errorf("error getting manifest blob: %w", err) } dockerReference := rawSource.Reference().DockerReference() if dockerReference == nil { - return errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference())) + return fmt.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference())) } var sigStoreDir string if options.Directory != "" { repo := reference.Path(dockerReference) if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references - return errors.Errorf("Unexpected path elements in Docker reference %s for signature storage", dockerReference.String()) + return fmt.Errorf("unexpected path elements in Docker reference %s for signature storage", dockerReference.String()) } sigStoreDir = filepath.Join(options.Directory, repo) } else { @@ -667,11 +647,11 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie if options.All { if !manifest.MIMETypeIsMultiImage(manifestType) { - return errors.Errorf("%s is not a multi-architecture image (manifest type %s)", signimage, manifestType) + return fmt.Errorf("%s is not a multi-architecture image (manifest type %s)", signimage, manifestType) } list, err := manifest.ListFromBlob(topManifestBlob, manifestType) if err != nil { - return errors.Wrapf(err, "Error parsing manifest list %q", string(topManifestBlob)) + return fmt.Errorf("error parsing manifest list %q: %w", string(topManifestBlob), err) } instanceDigests := list.Instances() for _, instanceDigest := range instanceDigests { @@ -681,13 +661,13 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie return err } if err = putSignature(man, mech, sigStoreDir, instanceDigest, dockerReference, options); err != nil { - return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), instanceDigest) + return fmt.Errorf("error storing signature for %s, %v: %w", dockerReference.String(), instanceDigest, err) } } return nil } if err = putSignature(topManifestBlob, mech, sigStoreDir, manifestDigest, dockerReference, options); err != nil { - return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), manifestDigest) + return fmt.Errorf("error storing signature for %s, %v: %w", dockerReference.String(), manifestDigest, err) } return nil }() @@ -698,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++ } + return nil } -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 !os.IsExist(err) { - return err - } +func Transfer(ctx context.Context, source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string) error { + if source.User == "" { + return fmt.Errorf("you must define a user when transferring from root to rootless storage: %w", define.ErrInvalidArg) } - 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 @@ -767,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 { @@ -780,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() } @@ -837,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 } - return execPodman(uLoad, loadCommand) + out, err := execTransferPodman(uLoad, loadCommand, (len(dest.Tag) > 0)) + if err != nil { + return err + } + 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) { @@ -851,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() { @@ -868,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{ @@ -882,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 "", fmt.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) } |