Repository: regclient/regclient
Pull request #387 (2023-03-11 16:30:27)

Adding docs and testing for regctl blob copy
<!--

Commits must be signed indicating your agreement to the [DCO](https://developercertificate.org/).
See [DCO missing](https://github.com/src-d/guide/blob/master/developer-community/fix-DCO.md) for steps to fix a missing signoff.

-->

### Fixes issue

<!-- If this is a bug fix, include "fixes #xxxx", or "closes #xxxx" -->

### Describe the change

Adds some testing and docs to the `regctl blob copy` command.
<!-- Include the type of change: bug fix, new feature, breaking change, documentation update -->
<!-- Describe what was changed, why the change was made, and how it was implemented -->

### How to verify it

No user visible changes.
<!-- Include steps that can be taken to verify the change -->

### Changelog text

<!-- If the release changelog should have an entry for this, include it here -->

### Please verify and check that the pull request fulfills the following requirements

<!-- Mark the following with an [X] to verify they are included -->

- [X] Tests have been added or not applicable
- [X] Documentation has been added, updated, or not applicable
- [X] Changes have been rebased to main
- [X] Multiple commits to the same code have been squashed



Patch:
diff --git a/cmd/regctl/blob.go b/cmd/regctl/blob.go
index 8a99ddd..ef8c4f7 100644
--- a/cmd/regctl/blob.go
+++ b/cmd/regctl/blob.go
@@ -437,7 +437,7 @@ func runBlobPut(cmd *cobra.Command, args []string) error {
 		"repository": r.Repository,
 		"digest":     blobOpts.digest,
 	}).Debug("Pushing blob")
-	dOut, err := rc.BlobPut(ctx, r, types.Descriptor{Digest: digest.Digest(blobOpts.digest)}, os.Stdin)
+	dOut, err := rc.BlobPut(ctx, r, types.Descriptor{Digest: digest.Digest(blobOpts.digest)}, cmd.InOrStdin())
 	if err != nil {
 		return err
 	}
diff --git a/docs/regctl.md b/docs/regctl.md
index 21ff852..ca7bf4c 100644
--- a/docs/regctl.md
+++ b/docs/regctl.md
@@ -212,6 +212,7 @@ Usage:
   regctl blob [command]
 
 Available Commands:
+  copy        copy blob
   diff-config diff two image configs
   diff-layer  diff two tar layers
   get         download a blob/layer
@@ -219,6 +220,9 @@ Available Commands:
   put         upload a blob/layer
 ```
 
+The `copy` command copies a blob between registries and repositories.
+Note that many registries will clean unreferenced blobs, so this should be used in combination with a `manifest put`.
+
 The `diff-config` command compares two config blobs, showing the differences between the configs.
 
 The `diff-layer` command compares two layer blobs, showing exactly what changed in the filesystem between the two layers.


Test patch:
diff --git a/cmd/regctl/blob_test.go b/cmd/regctl/blob_test.go
new file mode 100644
index 0000000..423ea4c
--- /dev/null
+++ b/cmd/regctl/blob_test.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+	"bytes"
+	"testing"
+)
+
+// TODO: implement tests
+
+func TestBlob(t *testing.T) {
+	repo := "ocidir://../../testdata/testrepo"
+	digBaseA, err := cobraTest(t, "manifest", "get", repo+":b1", "--platform", "linux/amd64", "--format", "{{(index .Layers 0).Digest}}")
+	if err != nil {
+		t.Errorf("failed getting layer digest: %v", err)
+	}
+	digBaseB, err := cobraTest(t, "manifest", "get", repo+":b3", "--platform", "linux/amd64", "--format", "{{(index .Layers 0).Digest}}")
+	if err != nil {
+		t.Errorf("failed getting layer digest: %v", err)
+	}
+	digConf1, err := cobraTest(t, "manifest", "get", repo+":b1", "--platform", "linux/amd64", "--format", "{{.Config.Digest}}")
+	if err != nil {
+		t.Errorf("failed getting layer digest: %v", err)
+	}
+	digConf3, err := cobraTest(t, "manifest", "get", repo+":b3", "--platform", "linux/amd64", "--format", "{{.Config.Digest}}")
+	if err != nil {
+		t.Errorf("failed getting layer digest: %v", err)
+	}
+
+	t.Run("Get", func(t *testing.T) {
+		// run a get request
+		out, err := cobraTest(t, "blob", "get", "--format", "{{printPretty .}}", repo, digBaseA)
+		if err != nil {
+			t.Errorf("failed to blob get: %v", err)
+		}
+		if out == "" {
+			t.Errorf("no blob output received")
+		}
+		// run a head request
+		out, err = cobraTest(t, "blob", "head", "--format", "{{printPretty .}}", repo, digBaseA)
+		if err != nil {
+			t.Errorf("failed to blob head: %v", err)
+		}
+		if out == "" {
+			t.Errorf("no blob output received")
+		}
+		// get a file from the blob
+		out, err = cobraTest(t, "blob", "get-file", repo, digBaseA, "base.txt")
+		if err != nil {
+			t.Errorf("failed to blob get-file: %v", err)
+		}
+		if out != "A" {
+			t.Errorf("unexpected blob get-file output, expected A, received %s", out)
+		}
+	})
+
+	t.Run("Put", func(t *testing.T) {
+		dir := t.TempDir()
+		bufStr := "hello world"
+		buf := bytes.NewBufferString(bufStr)
+		origIn := rootCmd.InOrStdin()
+		rootCmd.SetIn(buf)
+		defer rootCmd.SetIn(origIn)
+		// put a blob
+		dig, err := cobraTest(t, "blob", "put", "--format", "{{println .Digest}}", "ocidir://"+dir)
+		if err != nil {
+			t.Errorf("failed to blob copy: %v", err)
+		}
+		// get the blob from the tempdir
+		out, err := cobraTest(t, "blob", "get", "--format", "{{printPretty .}}", "ocidir://"+dir, dig)
+		if err != nil {
+			t.Errorf("failed to blob get: %v", err)
+		}
+		if out != bufStr {
+			t.Errorf("unexpected blob output, expected %s, received %s", bufStr, out)
+		}
+	})
+
+	t.Run("Copy", func(t *testing.T) {
+		dir := t.TempDir()
+		// copy the blob to the tempdir
+		_, err := cobraTest(t, "blob", "copy", repo, "ocidir://"+dir, digBaseA)
+		if err != nil {
+			t.Errorf("failed to blob copy: %v", err)
+		}
+		// get the blob from the tempdir
+		out, err := cobraTest(t, "blob", "get", "--format", "{{printPretty .}}", "ocidir://"+dir, digBaseA)
+		if err != nil {
+			t.Errorf("failed to blob get: %v", err)
+		}
+		if out == "" {
+			t.Errorf("no blob output received")
+		}
+	})
+
+	t.Run("Diff", func(t *testing.T) {
+		// diff the layers between two images
+		out, err := cobraTest(t, "blob", "diff-layer", repo, digBaseA, repo, digBaseB)
+		if err != nil {
+			t.Errorf("failed to diff layers: %v", err)
+		}
+		if out == "" {
+			t.Errorf("no output received from diff-layer")
+		}
+		// diff the config between two images
+		out, err = cobraTest(t, "blob", "diff-config", repo, digConf1, repo, digConf3)
+		if err != nil {
+			t.Errorf("failed to diff config: %v", err)
+		}
+		if out == "" {
+			t.Errorf("no output received from diff-config")
+		}
+	})
+
+}