diff --git a/cmd/skopeo/fixtures/v2s1-invalid-signatures.manifest.json b/cmd/skopeo/fixtures/v2s1-invalid-signatures.manifest.json new file mode 100644 index 00000000..8dfefd4e --- /dev/null +++ b/cmd/skopeo/fixtures/v2s1-invalid-signatures.manifest.json @@ -0,0 +1,11 @@ +{ + "schemaVersion": 1, + "name": "mitr/buxybox", + "tag": "latest", + "architecture": "amd64", + "fsLayers": [ + ], + "history": [ + ], + "signatures": 1 +} diff --git a/cmd/skopeo/main.go b/cmd/skopeo/main.go index f660eec6..b56eee0a 100644 --- a/cmd/skopeo/main.go +++ b/cmd/skopeo/main.go @@ -61,6 +61,7 @@ func createApp() *cli.App { inspectCmd, layersCmd, deleteCmd, + manifestDigestCmd, standaloneSignCmd, standaloneVerifyCmd, } diff --git a/cmd/skopeo/manifest.go b/cmd/skopeo/manifest.go new file mode 100644 index 00000000..a04f5e21 --- /dev/null +++ b/cmd/skopeo/manifest.go @@ -0,0 +1,35 @@ +package main + +import ( + "errors" + "fmt" + "io/ioutil" + + "github.com/containers/image/manifest" + "github.com/urfave/cli" +) + +func manifestDigest(context *cli.Context) error { + if len(context.Args()) != 1 { + return errors.New("Usage: skopeo manifest-digest manifest") + } + manifestPath := context.Args()[0] + + man, err := ioutil.ReadFile(manifestPath) + if err != nil { + return fmt.Errorf("Error reading manifest from %s: %v", manifestPath, err) + } + digest, err := manifest.Digest(man) + if err != nil { + return fmt.Errorf("Error computing digest: %v", err) + } + fmt.Fprintf(context.App.Writer, "%s\n", digest) + return nil +} + +var manifestDigestCmd = cli.Command{ + Name: "manifest-digest", + Usage: "Compute a manifest digest of a file", + ArgsUsage: "MANIFEST", + Action: manifestDigest, +} diff --git a/cmd/skopeo/manifest_test.go b/cmd/skopeo/manifest_test.go new file mode 100644 index 00000000..eab9f386 --- /dev/null +++ b/cmd/skopeo/manifest_test.go @@ -0,0 +1,31 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestManifestDigest(t *testing.T) { + // Invalid command-line arguments + for _, args := range [][]string{ + {}, + {"a1", "a2"}, + } { + out, err := runSkopeo(append([]string{"manifest-digest"}, args...)...) + assertTestFailed(t, out, err, "Usage") + } + + // Error reading manifest + out, err := runSkopeo("manifest-digest", "/this/doesnt/exist") + assertTestFailed(t, out, err, "/this/doesnt/exist") + + // Error computing manifest + out, err = runSkopeo("manifest-digest", "fixtures/v2s1-invalid-signatures.manifest.json") + assertTestFailed(t, out, err, "computing digest") + + // Success + out, err = runSkopeo("manifest-digest", "fixtures/image.manifest.json") + assert.NoError(t, err) + assert.Equal(t, fixturesTestImageManifestDigest+"\n", out) +} diff --git a/docs/skopeo.1.md b/docs/skopeo.1.md index aff3b8ca..90852d6f 100644 --- a/docs/skopeo.1.md +++ b/docs/skopeo.1.md @@ -91,6 +91,11 @@ Get image layers of _image-name_ _image-name_ name of the image to retrieve layers +## skopeo manifest-digest +**skopeo manifest-digest** _manifest-file_ + +Compute a manifest digest of _manifest-file_ and write it to standard output. + ## skopeo standalone-sign **skopeo standalone-sign** _manifest docker-reference key-fingerprint_ **--output**|**-o** _signature_ @@ -184,6 +189,11 @@ $ ls layers-500650331/ manifest.json a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.tar ``` +## skopeo manifest-digest +```sh +$ skopeo manifest-digest manifest.json +sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6 +``` ## skopeo standalone-sign ```sh $ skopeo standalone-sign busybox-manifest.json registry.example.com/example/busybox 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8 --output busybox.signature