Add dry-run mode to copy skopeo-copy

Dry-run functionality was merged into the sync command in #1608.
Here, the same sort of functionality is added to the copy command
as well.

Signed-off-by: Ryan Egesdahl <deriamis@gmail.com>
This commit is contained in:
Ryan Egesdahl
2023-12-08 00:46:44 -08:00
parent a85eaac984
commit a28ea75ebb
4 changed files with 41 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ import (
"github.com/containers/image/v5/transports/alltransports"
encconfig "github.com/containers/ocicrypt/config"
enchelpers "github.com/containers/ocicrypt/helpers"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -39,6 +40,7 @@ type copyOptions struct {
format commonFlag.OptionalString // Force conversion of the image to a specified format
quiet bool // Suppress output information when copying images
all bool // Copy all of the images if the source is a list
dryRun bool // Don't actually copy anything, just output what it would have done
multiArch commonFlag.OptionalString // How to handle multi architecture images
preserveDigests bool // Preserve digests during copy
encryptLayer []int // The list of layers to encrypt
@@ -82,6 +84,7 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
flags.StringSliceVar(&opts.additionalTags, "additional-tag", []string{}, "additional tags (supports docker-archive)")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress output information when copying images")
flags.BoolVarP(&opts.all, "all", "a", false, "Copy all images if SOURCE-IMAGE is a list")
flags.BoolVar(&opts.dryRun, "dry-run", false, "Run without actually copying images")
flags.Var(commonFlag.NewOptionalStringValue(&opts.multiArch), "multi-arch", `How to handle multi-architecture images (system, all, or index-only)`)
flags.BoolVar(&opts.preserveDigests, "preserve-digests", false, "Preserve digests of images and lists")
flags.BoolVar(&opts.removeSignatures, "remove-signatures", false, "Do not copy signatures from SOURCE-IMAGE")
@@ -126,6 +129,10 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) (retErr error) {
opts.deprecatedTLSVerify.warnIfUsed([]string{"--src-tls-verify", "--dest-tls-verify"})
imageNames := args
if opts.dryRun {
logrus.Warn("Running in dry-run mode")
}
if err := reexecIfNecessaryForImages(imageNames...); err != nil {
return err
}
@@ -282,6 +289,11 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) (retErr error) {
opts.destImage.warnAboutIneffectiveOptions(destRef.Transport())
if opts.dryRun {
logrus.Info(fmt.Sprintf("Would have copied from=%s to=%s", imageNames[0], imageNames[1]))
return nil
}
return retry.IfNecessary(ctx, func() error {
manifestBytes, err := copy.Image(ctx, policyContext, destRef, srcRef, &copy.Options{
RemoveSignatures: opts.removeSignatures,

View File

@@ -32,6 +32,10 @@ If _source-image_ refers to a list of images, instead of copying just the image
architecture (subject to the use of the global --override-os, --override-arch and --override-variant options), attempt to copy all of
the images in the list, and the list itself.
**--dry-run**
Run the sync without actually copying data to the destination.
**--authfile** _path_
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.

View File

@@ -6,6 +6,7 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
"io"
"io/fs"
"log"
"net/http"
@@ -127,6 +128,13 @@ func (s *copySuite) TestCopyAllWithManifestListRoundTrip() {
assert.Equal(t, "", out)
}
func (s *copySuite) TestCopyDryRun() {
t := s.T()
dir := t.TempDir()
assertSkopeoSucceeds(t, "", "copy", "--dry-run", knownListImage, "dir:"+dir)
assertDirIsEmpty(t, dir)
}
func (s *copySuite) TestCopyAllWithManifestListConverge() {
t := s.T()
oci1 := t.TempDir()
@@ -662,6 +670,14 @@ func assertSchema1DirImagesAreEqualExceptNames(t *testing.T, dir1, ref1, dir2, r
assert.Equal(t, "", out)
}
func assertDirIsEmpty(t *testing.T, dir string) {
d, err := os.Open(dir)
require.NoError(t, err)
defer d.Close()
_, err = d.Readdirnames(1)
assert.Equal(t, err, io.EOF)
}
// Streaming (skopeo copy)
func (s *copySuite) TestCopyStreaming() {
t := s.T()

View File

@@ -158,6 +158,15 @@ function setup() {
expect_output "amd64"
}
@test "copy: --dry-run" {
local remote_image=docker://quay.io/libpod/busybox:latest
local dir=$TESTDIR/dir
run_skopeo copy --dry-run $remote_image oci:$dir:latest
expect_output --substring "Running in dry-run mode"
expect_output --substring "Would have copied from=${remote_image} to=oci:${dir}:latest"
}
teardown() {
podman rm -f reg