forked from TrueCloudLab/distribution
feat(storage)!: remove schema1 except manifeststore_test
Signed-off-by: David van der Spek <vanderspek.david@gmail.com>
This commit is contained in:
parent
db460d2636
commit
f9bc9220eb
10 changed files with 116 additions and 459 deletions
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/distribution/distribution/v3/registry/storage"
|
"github.com/distribution/distribution/v3/registry/storage"
|
||||||
"github.com/distribution/distribution/v3/registry/storage/driver/factory"
|
"github.com/distribution/distribution/v3/registry/storage/driver/factory"
|
||||||
"github.com/distribution/distribution/v3/version"
|
"github.com/distribution/distribution/v3/version"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,13 +66,7 @@ var GCCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
k, err := libtrust.GenerateECP256PrivateKey()
|
registry, err := storage.NewRegistry(ctx, driver)
|
||||||
if err != nil {
|
|
||||||
fmt.Fprint(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
registry, err := storage.NewRegistry(ctx, driver, storage.Schema1SigningKey(k))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "failed to construct registry: %v", err)
|
fmt.Fprintf(os.Stderr, "failed to construct registry: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -26,7 +26,7 @@ type setupEnv struct {
|
||||||
func setupFS(t *testing.T) *setupEnv {
|
func setupFS(t *testing.T) *setupEnv {
|
||||||
d := inmemory.New()
|
d := inmemory.New()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableRedirect, EnableSchema1)
|
registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableRedirect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating registry: %v", err)
|
t.Fatalf("error creating registry: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -81,19 +81,18 @@ func makeRepo(ctx context.Context, t *testing.T, name string, reg distribution.N
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// upload the layers
|
||||||
err = testutil.UploadBlobs(repo, layers)
|
err = testutil.UploadBlobs(repo, layers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to upload layers: %v", err)
|
t.Fatalf("failed to upload layers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
getKeys := func(digests map[digest.Digest]io.ReadSeeker) (ds []digest.Digest) {
|
digests := []digest.Digest{}
|
||||||
for d := range digests {
|
for digest := range layers {
|
||||||
ds = append(ds, d)
|
digests = append(digests, digest)
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest, err := testutil.MakeSchema1Manifest(getKeys(layers)) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
manifest, err := testutil.MakeSchema2Manifest(repo, digests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -206,7 +205,7 @@ func testEq(a, b []string, size int) bool {
|
||||||
func setupBadWalkEnv(t *testing.T) *setupEnv {
|
func setupBadWalkEnv(t *testing.T) *setupEnv {
|
||||||
d := newBadListDriver()
|
d := newBadListDriver()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableRedirect, EnableSchema1)
|
registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableRedirect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating registry: %v", err)
|
t.Fatalf("error creating registry: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/distribution/distribution/v3/registry/storage/driver"
|
"github.com/distribution/distribution/v3/registry/storage/driver"
|
||||||
"github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
|
"github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
|
||||||
"github.com/distribution/distribution/v3/testutil"
|
"github.com/distribution/distribution/v3/testutil"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,11 +22,7 @@ type image struct {
|
||||||
|
|
||||||
func createRegistry(t *testing.T, driver driver.StorageDriver, options ...RegistryOption) distribution.Namespace {
|
func createRegistry(t *testing.T, driver driver.StorageDriver, options ...RegistryOption) distribution.Namespace {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
k, err := libtrust.GenerateECP256PrivateKey()
|
options = append(options, EnableDelete)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
options = append([]RegistryOption{EnableDelete, Schema1SigningKey(k), EnableSchema1}, options...)
|
|
||||||
registry, err := NewRegistry(ctx, driver, options...)
|
registry, err := NewRegistry(ctx, driver, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to construct namespace")
|
t.Fatalf("Failed to construct namespace")
|
||||||
|
@ -110,30 +105,6 @@ func uploadImage(t *testing.T, repository distribution.Repository, im image) dig
|
||||||
return manifestDigest
|
return manifestDigest
|
||||||
}
|
}
|
||||||
|
|
||||||
func uploadRandomSchema1Image(t *testing.T, repository distribution.Repository) image {
|
|
||||||
randomLayers, err := testutil.CreateRandomLayers(2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
digests := []digest.Digest{}
|
|
||||||
for digest := range randomLayers {
|
|
||||||
digests = append(digests, digest)
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest, err := testutil.MakeSchema1Manifest(digests) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
manifestDigest := uploadImage(t, repository, image{manifest: manifest, layers: randomLayers})
|
|
||||||
return image{
|
|
||||||
manifest: manifest,
|
|
||||||
manifestDigest: manifestDigest,
|
|
||||||
layers: randomLayers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func uploadRandomSchema2Image(t *testing.T, repository distribution.Repository) image {
|
func uploadRandomSchema2Image(t *testing.T, repository distribution.Repository) image {
|
||||||
randomLayers, err := testutil.CreateRandomLayers(2)
|
randomLayers, err := testutil.CreateRandomLayers(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -166,8 +137,8 @@ func TestNoDeletionNoEffect(t *testing.T) {
|
||||||
repo := makeRepository(t, registry, "palailogos")
|
repo := makeRepository(t, registry, "palailogos")
|
||||||
manifestService, _ := repo.Manifests(ctx)
|
manifestService, _ := repo.Manifests(ctx)
|
||||||
|
|
||||||
image1 := uploadRandomSchema1Image(t, repo)
|
image1 := uploadRandomSchema2Image(t, repo)
|
||||||
image2 := uploadRandomSchema1Image(t, repo)
|
image2 := uploadRandomSchema2Image(t, repo)
|
||||||
uploadRandomSchema2Image(t, repo)
|
uploadRandomSchema2Image(t, repo)
|
||||||
|
|
||||||
// construct manifestlist for fun.
|
// construct manifestlist for fun.
|
||||||
|
@ -215,11 +186,21 @@ func TestDeleteManifestIfTagNotFound(t *testing.T) {
|
||||||
t.Fatalf("failed to make layers: %v", err)
|
t.Fatalf("failed to make layers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
digests1 := []digest.Digest{}
|
||||||
|
for digest := range randomLayers1 {
|
||||||
|
digests1 = append(digests1, digest)
|
||||||
|
}
|
||||||
|
|
||||||
randomLayers2, err := testutil.CreateRandomLayers(3)
|
randomLayers2, err := testutil.CreateRandomLayers(3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to make layers: %v", err)
|
t.Fatalf("failed to make layers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
digests2 := []digest.Digest{}
|
||||||
|
for digest := range randomLayers2 {
|
||||||
|
digests2 = append(digests2, digest)
|
||||||
|
}
|
||||||
|
|
||||||
// Upload all layers
|
// Upload all layers
|
||||||
err = testutil.UploadBlobs(repo, randomLayers1)
|
err = testutil.UploadBlobs(repo, randomLayers1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -232,12 +213,12 @@ func TestDeleteManifestIfTagNotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct manifests
|
// Construct manifests
|
||||||
manifest1, err := testutil.MakeSchema1Manifest(getKeys(randomLayers1)) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
manifest1, err := testutil.MakeSchema2Manifest(repo, digests1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to make manifest: %v", err)
|
t.Fatalf("failed to make manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest2, err := testutil.MakeSchema1Manifest(getKeys(randomLayers2)) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
manifest2, err := testutil.MakeSchema2Manifest(repo, digests2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to make manifest: %v", err)
|
t.Fatalf("failed to make manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -303,7 +284,7 @@ func TestGCWithMissingManifests(t *testing.T) {
|
||||||
|
|
||||||
registry := createRegistry(t, d)
|
registry := createRegistry(t, d)
|
||||||
repo := makeRepository(t, registry, "testrepo")
|
repo := makeRepository(t, registry, "testrepo")
|
||||||
uploadRandomSchema1Image(t, repo)
|
uploadRandomSchema2Image(t, repo)
|
||||||
|
|
||||||
// Simulate a missing _manifests directory
|
// Simulate a missing _manifests directory
|
||||||
revPath, err := pathFor(manifestRevisionsPathSpec{"testrepo"})
|
revPath, err := pathFor(manifestRevisionsPathSpec{"testrepo"})
|
||||||
|
@ -339,8 +320,8 @@ func TestDeletionHasEffect(t *testing.T) {
|
||||||
repo := makeRepository(t, registry, "komnenos")
|
repo := makeRepository(t, registry, "komnenos")
|
||||||
manifests, _ := repo.Manifests(ctx)
|
manifests, _ := repo.Manifests(ctx)
|
||||||
|
|
||||||
image1 := uploadRandomSchema1Image(t, repo)
|
image1 := uploadRandomSchema2Image(t, repo)
|
||||||
image2 := uploadRandomSchema1Image(t, repo)
|
image2 := uploadRandomSchema2Image(t, repo)
|
||||||
image3 := uploadRandomSchema2Image(t, repo)
|
image3 := uploadRandomSchema2Image(t, repo)
|
||||||
|
|
||||||
manifests.Delete(ctx, image2.manifestDigest)
|
manifests.Delete(ctx, image2.manifestDigest)
|
||||||
|
@ -389,13 +370,6 @@ func getAnyKey(digests map[digest.Digest]io.ReadSeeker) (d digest.Digest) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKeys(digests map[digest.Digest]io.ReadSeeker) (ds []digest.Digest) {
|
|
||||||
for d := range digests {
|
|
||||||
ds = append(ds, d)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeletionWithSharedLayer(t *testing.T) {
|
func TestDeletionWithSharedLayer(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
inmemoryDriver := inmemory.New()
|
inmemoryDriver := inmemory.New()
|
||||||
|
@ -409,11 +383,21 @@ func TestDeletionWithSharedLayer(t *testing.T) {
|
||||||
t.Fatalf("failed to make layers: %v", err)
|
t.Fatalf("failed to make layers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
digests1 := []digest.Digest{}
|
||||||
|
for digest := range randomLayers1 {
|
||||||
|
digests1 = append(digests1, digest)
|
||||||
|
}
|
||||||
|
|
||||||
randomLayers2, err := testutil.CreateRandomLayers(3)
|
randomLayers2, err := testutil.CreateRandomLayers(3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to make layers: %v", err)
|
t.Fatalf("failed to make layers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
digests2 := []digest.Digest{}
|
||||||
|
for digest := range randomLayers2 {
|
||||||
|
digests2 = append(digests2, digest)
|
||||||
|
}
|
||||||
|
|
||||||
// Upload all layers
|
// Upload all layers
|
||||||
err = testutil.UploadBlobs(repo, randomLayers1)
|
err = testutil.UploadBlobs(repo, randomLayers1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -426,13 +410,13 @@ func TestDeletionWithSharedLayer(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct manifests
|
// Construct manifests
|
||||||
manifest1, err := testutil.MakeSchema1Manifest(getKeys(randomLayers1)) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
manifest1, err := testutil.MakeSchema2Manifest(repo, digests1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to make manifest: %v", err)
|
t.Fatalf("failed to make manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedKey := getAnyKey(randomLayers1)
|
sharedKey := getAnyKey(randomLayers1)
|
||||||
manifest2, err := testutil.MakeSchema2Manifest(repo, append(getKeys(randomLayers2), sharedKey))
|
manifest2, err := testutil.MakeSchema2Manifest(repo, append(digests2, sharedKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to make manifest: %v", err)
|
t.Fatalf("failed to make manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/distribution/distribution/v3/manifest"
|
"github.com/distribution/distribution/v3/manifest"
|
||||||
"github.com/distribution/distribution/v3/manifest/manifestlist"
|
"github.com/distribution/distribution/v3/manifest/manifestlist"
|
||||||
"github.com/distribution/distribution/v3/manifest/ocischema"
|
"github.com/distribution/distribution/v3/manifest/ocischema"
|
||||||
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
"github.com/distribution/distribution/v3/manifest/schema2"
|
"github.com/distribution/distribution/v3/manifest/schema2"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
@ -48,7 +47,6 @@ type manifestStore struct {
|
||||||
|
|
||||||
skipDependencyVerification bool
|
skipDependencyVerification bool
|
||||||
|
|
||||||
schema1Handler ManifestHandler
|
|
||||||
schema2Handler ManifestHandler
|
schema2Handler ManifestHandler
|
||||||
manifestListHandler ManifestHandler
|
manifestListHandler ManifestHandler
|
||||||
ocischemaHandler ManifestHandler
|
ocischemaHandler ManifestHandler
|
||||||
|
@ -96,8 +94,6 @@ func (ms *manifestStore) Get(ctx context.Context, dgst digest.Digest, options ..
|
||||||
}
|
}
|
||||||
|
|
||||||
switch versioned.SchemaVersion {
|
switch versioned.SchemaVersion {
|
||||||
case 1:
|
|
||||||
return ms.schema1Handler.Unmarshal(ctx, dgst, content)
|
|
||||||
case 2:
|
case 2:
|
||||||
// This can be an image manifest or a manifest list
|
// This can be an image manifest or a manifest list
|
||||||
switch versioned.MediaType {
|
switch versioned.MediaType {
|
||||||
|
@ -133,8 +129,6 @@ func (ms *manifestStore) Put(ctx context.Context, manifest distribution.Manifest
|
||||||
dcontext.GetLogger(ms.ctx).Debug("(*manifestStore).Put")
|
dcontext.GetLogger(ms.ctx).Debug("(*manifestStore).Put")
|
||||||
|
|
||||||
switch manifest.(type) {
|
switch manifest.(type) {
|
||||||
case *schema1.SignedManifest: //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
return ms.schema1Handler.Put(ctx, manifest, ms.skipDependencyVerification)
|
|
||||||
case *schema2.DeserializedManifest:
|
case *schema2.DeserializedManifest:
|
||||||
return ms.schema2Handler.Put(ctx, manifest, ms.skipDependencyVerification)
|
return ms.schema2Handler.Put(ctx, manifest, ms.skipDependencyVerification)
|
||||||
case *ocischema.DeserializedManifest:
|
case *ocischema.DeserializedManifest:
|
||||||
|
|
|
@ -11,13 +11,12 @@ import (
|
||||||
"github.com/distribution/distribution/v3"
|
"github.com/distribution/distribution/v3"
|
||||||
"github.com/distribution/distribution/v3/manifest"
|
"github.com/distribution/distribution/v3/manifest"
|
||||||
"github.com/distribution/distribution/v3/manifest/ocischema"
|
"github.com/distribution/distribution/v3/manifest/ocischema"
|
||||||
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
"github.com/distribution/distribution/v3/manifest/schema2"
|
||||||
"github.com/distribution/distribution/v3/reference"
|
"github.com/distribution/distribution/v3/reference"
|
||||||
"github.com/distribution/distribution/v3/registry/storage/cache/memory"
|
"github.com/distribution/distribution/v3/registry/storage/cache/memory"
|
||||||
"github.com/distribution/distribution/v3/registry/storage/driver"
|
"github.com/distribution/distribution/v3/registry/storage/driver"
|
||||||
"github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
|
"github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
|
||||||
"github.com/distribution/distribution/v3/testutil"
|
"github.com/distribution/distribution/v3/testutil"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
|
@ -55,22 +54,10 @@ func newManifestStoreTestEnv(t *testing.T, name reference.Named, tag string, opt
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestManifestStorage(t *testing.T) {
|
func TestManifestStorage(t *testing.T) {
|
||||||
k, err := libtrust.GenerateECP256PrivateKey()
|
testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableDelete, EnableRedirect)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
testManifestStorage(t, true, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableDelete, EnableRedirect, Schema1SigningKey(k), EnableSchema1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestManifestStorageV1Unsupported(t *testing.T) {
|
func testManifestStorage(t *testing.T, options ...RegistryOption) {
|
||||||
k, err := libtrust.GenerateECP256PrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
testManifestStorage(t, false, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableDelete, EnableRedirect, Schema1SigningKey(k))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testManifestStorage(t *testing.T, schema1Enabled bool, options ...RegistryOption) {
|
|
||||||
repoName, _ := reference.WithName("foo/bar")
|
repoName, _ := reference.WithName("foo/bar")
|
||||||
env := newManifestStoreTestEnv(t, repoName, "thetag", options...)
|
env := newManifestStoreTestEnv(t, repoName, "thetag", options...)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -79,13 +66,59 @@ func testManifestStorage(t *testing.T, schema1Enabled bool, options ...RegistryO
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m := schema1.Manifest{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
// Push a config, and reference it in the manifest
|
||||||
Versioned: manifest.Versioned{
|
sampleConfig := []byte(`{
|
||||||
SchemaVersion: 1,
|
"architecture": "amd64",
|
||||||
},
|
"history": [
|
||||||
Name: env.name.Name(),
|
{
|
||||||
Tag: env.tag,
|
"created": "2015-10-31T22:22:54.690851953Z",
|
||||||
|
"created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"rootfs": {
|
||||||
|
"diff_ids": [
|
||||||
|
"sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
|
||||||
|
],
|
||||||
|
"type": "layers"
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
// Build a manifest and store it and its layers in the registry
|
||||||
|
|
||||||
|
blobStore := env.repository.Blobs(ctx)
|
||||||
|
d, err := blobStore.Put(ctx, schema2.MediaTypeImageConfig, sampleConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
builder := schema2.NewManifestBuilder(d, sampleConfig)
|
||||||
|
|
||||||
|
m := &schema2.Manifest{
|
||||||
|
Versioned: manifest.Versioned{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
MediaType: schema2.MediaTypeManifest,
|
||||||
|
},
|
||||||
|
Config: distribution.Descriptor{
|
||||||
|
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
|
Size: 3253,
|
||||||
|
MediaType: schema2.MediaTypeImageConfig,
|
||||||
|
},
|
||||||
|
Layers: []distribution.Descriptor{
|
||||||
|
{
|
||||||
|
Digest: "sha256:463434349086340864309863409683460843608348608934092322395278926a",
|
||||||
|
Size: 6323,
|
||||||
|
MediaType: schema2.MediaTypeLayer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Digest: "sha256:630923423623623423352523525237238023652897356239852383652aaaaaaa",
|
||||||
|
Size: 6863,
|
||||||
|
MediaType: schema2.MediaTypeLayer,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
sampleConfigDigest := digest.FromBytes(sampleConfig)
|
||||||
|
sampleConfigSize := int64(len(sampleConfig))
|
||||||
|
m.Config.Digest = sampleConfigDigest
|
||||||
|
m.Config.Size = sampleConfigSize
|
||||||
|
|
||||||
// Build up some test layers and add them to the manifest, saving the
|
// Build up some test layers and add them to the manifest, saving the
|
||||||
// readseekers for upload later.
|
// readseekers for upload later.
|
||||||
|
@ -97,52 +130,7 @@ func testManifestStorage(t *testing.T, schema1Enabled bool, options ...RegistryO
|
||||||
}
|
}
|
||||||
|
|
||||||
testLayers[dgst] = rs
|
testLayers[dgst] = rs
|
||||||
m.FSLayers = append(m.FSLayers, schema1.FSLayer{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
m.Layers[i].Digest = dgst
|
||||||
BlobSum: dgst,
|
|
||||||
})
|
|
||||||
m.History = append(m.History, schema1.History{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
V1Compatibility: "",
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pk, err := libtrust.GenerateECP256PrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error generating private key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sm, merr := schema1.Sign(&m, pk) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
if merr != nil {
|
|
||||||
t.Fatalf("error signing manifest: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ms.Put(ctx, sm)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected errors putting manifest with full verification")
|
|
||||||
}
|
|
||||||
|
|
||||||
// If schema1 is not enabled, do a short version of this test, just checking
|
|
||||||
// if we get the right error when we Put
|
|
||||||
if !schema1Enabled {
|
|
||||||
if err != distribution.ErrSchemaV1Unsupported {
|
|
||||||
t.Fatalf("got the wrong error when schema1 is disabled: %s", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch err := err.(type) {
|
|
||||||
case distribution.ErrManifestVerification:
|
|
||||||
if len(err) != 2 {
|
|
||||||
t.Fatalf("expected 2 verification errors: %#v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, err := range err {
|
|
||||||
if _, ok := err.(distribution.ErrManifestBlobUnknown); !ok {
|
|
||||||
t.Fatalf("unexpected error type: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
t.Fatalf("unexpected error verifying manifest: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, upload the layers that were missing!
|
// Now, upload the layers that were missing!
|
||||||
|
@ -159,6 +147,12 @@ func testManifestStorage(t *testing.T, schema1Enabled bool, options ...RegistryO
|
||||||
if _, err := wr.Commit(env.ctx, distribution.Descriptor{Digest: dgst}); err != nil {
|
if _, err := wr.Commit(env.ctx, distribution.Descriptor{Digest: dgst}); err != nil {
|
||||||
t.Fatalf("unexpected error finishing upload: %v", err)
|
t.Fatalf("unexpected error finishing upload: %v", err)
|
||||||
}
|
}
|
||||||
|
builder.AppendReference(distribution.Descriptor{Digest: dgst})
|
||||||
|
}
|
||||||
|
|
||||||
|
sm, err := builder.Build(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: unexpected error generating manifest: %v", repoName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var manifestDigest digest.Digest
|
var manifestDigest digest.Digest
|
||||||
|
@ -180,34 +174,19 @@ func testManifestStorage(t *testing.T, schema1Enabled bool, options ...RegistryO
|
||||||
t.Fatalf("unexpected error fetching manifest: %v", err)
|
t.Fatalf("unexpected error fetching manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchedManifest, ok := fromStore.(*schema1.SignedManifest) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
fetchedManifest, ok := fromStore.(*schema2.DeserializedManifest)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("unexpected manifest type from signedstore")
|
t.Fatalf("unexpected manifest type from signedstore")
|
||||||
}
|
}
|
||||||
|
_, pl, err := fetchedManifest.Payload()
|
||||||
if !bytes.Equal(fetchedManifest.Canonical, sm.Canonical) {
|
|
||||||
t.Fatalf("fetched payload does not match original payload: %q != %q", fetchedManifest.Canonical, sm.Canonical)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, pl, err := fetchedManifest.Payload() //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error getting payload %#v", err)
|
t.Fatalf("could not get manifest payload: %v", err)
|
||||||
}
|
|
||||||
|
|
||||||
fetchedJWS, err := libtrust.ParsePrettySignature(pl, "signatures")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error parsing jws: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, err := fetchedJWS.Payload()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error extracting payload: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that we have a payload, take a moment to check that the manifest is
|
// Now that we have a payload, take a moment to check that the manifest is
|
||||||
// return by the payload digest.
|
// return by the payload digest.
|
||||||
|
|
||||||
dgst := digest.FromBytes(payload)
|
dgst := digest.FromBytes(pl)
|
||||||
exists, err = ms.Exists(ctx, dgst)
|
exists, err = ms.Exists(ctx, dgst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error checking manifest existence by digest: %v", err)
|
t.Fatalf("error checking manifest existence by digest: %v", err)
|
||||||
|
@ -222,55 +201,18 @@ func testManifestStorage(t *testing.T, schema1Enabled bool, options ...RegistryO
|
||||||
t.Fatalf("unexpected error fetching manifest by digest: %v", err)
|
t.Fatalf("unexpected error fetching manifest by digest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
byDigestManifest, ok := fetchedByDigest.(*schema1.SignedManifest) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
byDigestManifest, ok := fetchedByDigest.(*schema2.DeserializedManifest)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("unexpected manifest type from signedstore")
|
t.Fatalf("unexpected manifest type from signedstore")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(byDigestManifest.Canonical, fetchedManifest.Canonical) {
|
_, byDigestCanonical, err := byDigestManifest.Payload()
|
||||||
t.Fatalf("fetched manifest not equal: %q != %q", byDigestManifest.Canonical, fetchedManifest.Canonical)
|
|
||||||
}
|
|
||||||
|
|
||||||
sigs, err := fetchedJWS.Signatures()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to extract signatures: %v", err)
|
t.Fatalf("could not get manifest payload: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sigs) != 1 {
|
if !bytes.Equal(byDigestCanonical, pl) {
|
||||||
t.Fatalf("unexpected number of signatures: %d != %d", len(sigs), 1)
|
t.Fatalf("fetched manifest not equal: %q != %q", byDigestCanonical, pl)
|
||||||
}
|
|
||||||
|
|
||||||
// Now, push the same manifest with a different key
|
|
||||||
pk2, err := libtrust.GenerateECP256PrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error generating private key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sm2, err := schema1.Sign(&m, pk2) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error signing manifest: %v", err)
|
|
||||||
}
|
|
||||||
_, pl, err = sm2.Payload() //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error getting payload %#v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
jws2, err := libtrust.ParsePrettySignature(pl, "signatures")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error parsing signature: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sigs2, err := jws2.Signatures()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to extract signatures: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(sigs2) != 1 {
|
|
||||||
t.Fatalf("unexpected number of signatures: %d != %d", len(sigs2), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if manifestDigest, err = ms.Put(ctx, sm2); err != nil {
|
|
||||||
t.Fatalf("unexpected error putting manifest: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fromStore, err = ms.Get(ctx, manifestDigest)
|
fromStore, err = ms.Get(ctx, manifestDigest)
|
||||||
|
@ -278,31 +220,17 @@ func testManifestStorage(t *testing.T, schema1Enabled bool, options ...RegistryO
|
||||||
t.Fatalf("unexpected error fetching manifest: %v", err)
|
t.Fatalf("unexpected error fetching manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetched, ok := fromStore.(*schema1.SignedManifest) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
fetched, ok := fromStore.(*schema2.DeserializedManifest)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("unexpected type from signed manifeststore : %T", fetched)
|
t.Fatalf("unexpected type from signed manifeststore : %T", fetched)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := schema1.Verify(fetched); err != nil { //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
_, receivedPL, err := fetched.Payload()
|
||||||
t.Fatalf("unexpected error verifying manifest: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, pl, err = fetched.Payload() //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error getting payload %#v", err)
|
t.Fatalf("error getting payload %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
receivedJWS, err := libtrust.ParsePrettySignature(pl, "signatures")
|
if !bytes.Equal(receivedPL, pl) {
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error parsing jws: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
receivedPayload, err := receivedJWS.Payload()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error extracting received payload: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(receivedPayload, payload) {
|
|
||||||
t.Fatalf("payloads are not equal")
|
t.Fatalf("payloads are not equal")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/distribution/distribution/v3/reference"
|
"github.com/distribution/distribution/v3/reference"
|
||||||
"github.com/distribution/distribution/v3/registry/storage/cache"
|
"github.com/distribution/distribution/v3/registry/storage/cache"
|
||||||
storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
|
storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// registry is the top-level implementation of Registry for use in the storage
|
// registry is the top-level implementation of Registry for use in the storage
|
||||||
|
@ -19,9 +18,7 @@ type registry struct {
|
||||||
statter *blobStatter // global statter service.
|
statter *blobStatter // global statter service.
|
||||||
blobDescriptorCacheProvider cache.BlobDescriptorCacheProvider
|
blobDescriptorCacheProvider cache.BlobDescriptorCacheProvider
|
||||||
deleteEnabled bool
|
deleteEnabled bool
|
||||||
schema1Enabled bool
|
|
||||||
resumableDigestEnabled bool
|
resumableDigestEnabled bool
|
||||||
schema1SigningKey libtrust.PrivateKey
|
|
||||||
blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory
|
blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory
|
||||||
manifestURLs manifestURLs
|
manifestURLs manifestURLs
|
||||||
driver storagedriver.StorageDriver
|
driver storagedriver.StorageDriver
|
||||||
|
@ -50,13 +47,6 @@ func EnableDelete(registry *registry) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableSchema1 is a functional option for NewRegistry. It enables pushing of
|
|
||||||
// schema1 manifests.
|
|
||||||
func EnableSchema1(registry *registry) error {
|
|
||||||
registry.schema1Enabled = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableDigestResumption is a functional option for NewRegistry. It should be
|
// DisableDigestResumption is a functional option for NewRegistry. It should be
|
||||||
// used if the registry is acting as a caching proxy.
|
// used if the registry is acting as a caching proxy.
|
||||||
func DisableDigestResumption(registry *registry) error {
|
func DisableDigestResumption(registry *registry) error {
|
||||||
|
@ -80,15 +70,6 @@ func ManifestURLsDenyRegexp(r *regexp.Regexp) RegistryOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schema1SigningKey returns a functional option for NewRegistry. It sets the
|
|
||||||
// key for signing all schema1 manifests.
|
|
||||||
func Schema1SigningKey(key libtrust.PrivateKey) RegistryOption {
|
|
||||||
return func(registry *registry) error {
|
|
||||||
registry.schema1SigningKey = key
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlobDescriptorServiceFactory returns a functional option for NewRegistry. It sets the
|
// BlobDescriptorServiceFactory returns a functional option for NewRegistry. It sets the
|
||||||
// factory to create BlobDescriptorServiceFactory middleware.
|
// factory to create BlobDescriptorServiceFactory middleware.
|
||||||
func BlobDescriptorServiceFactory(factory distribution.BlobDescriptorServiceFactory) RegistryOption {
|
func BlobDescriptorServiceFactory(factory distribution.BlobDescriptorServiceFactory) RegistryOption {
|
||||||
|
@ -240,25 +221,6 @@ func (repo *repository) Manifests(ctx context.Context, options ...distribution.M
|
||||||
linkDirectoryPathSpec: manifestDirectoryPathSpec,
|
linkDirectoryPathSpec: manifestDirectoryPathSpec,
|
||||||
}
|
}
|
||||||
|
|
||||||
var v1Handler ManifestHandler
|
|
||||||
if repo.schema1Enabled {
|
|
||||||
v1Handler = &signedManifestHandler{
|
|
||||||
ctx: ctx,
|
|
||||||
schema1SigningKey: repo.schema1SigningKey,
|
|
||||||
repository: repo,
|
|
||||||
blobStore: blobStore,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
v1Handler = &v1UnsupportedHandler{
|
|
||||||
innerHandler: &signedManifestHandler{
|
|
||||||
ctx: ctx,
|
|
||||||
schema1SigningKey: repo.schema1SigningKey,
|
|
||||||
repository: repo,
|
|
||||||
blobStore: blobStore,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
manifestListHandler := &manifestListHandler{
|
manifestListHandler := &manifestListHandler{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
repository: repo,
|
repository: repo,
|
||||||
|
@ -266,10 +228,9 @@ func (repo *repository) Manifests(ctx context.Context, options ...distribution.M
|
||||||
}
|
}
|
||||||
|
|
||||||
ms := &manifestStore{
|
ms := &manifestStore{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
repository: repo,
|
repository: repo,
|
||||||
blobStore: blobStore,
|
blobStore: blobStore,
|
||||||
schema1Handler: v1Handler,
|
|
||||||
schema2Handler: &schema2ManifestHandler{
|
schema2Handler: &schema2ManifestHandler{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
repository: repo,
|
repository: repo,
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"github.com/distribution/distribution/v3"
|
"github.com/distribution/distribution/v3"
|
||||||
dcontext "github.com/distribution/distribution/v3/context"
|
dcontext "github.com/distribution/distribution/v3/context"
|
||||||
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
"github.com/distribution/distribution/v3/manifest/schema2"
|
"github.com/distribution/distribution/v3/manifest/schema2"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
@ -110,7 +109,7 @@ func (ms *schema2ManifestHandler) verifyManifest(ctx context.Context, mnfst sche
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case schema2.MediaTypeManifest, schema1.MediaTypeManifest: //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
case schema2.MediaTypeManifest:
|
||||||
var exists bool
|
var exists bool
|
||||||
exists, err = manifestService.Exists(ctx, descriptor.Digest)
|
exists, err = manifestService.Exists(ctx, descriptor.Digest)
|
||||||
if err != nil || !exists {
|
if err != nil || !exists {
|
||||||
|
|
|
@ -1,142 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/distribution/distribution/v3"
|
|
||||||
dcontext "github.com/distribution/distribution/v3/context"
|
|
||||||
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
"github.com/distribution/distribution/v3/reference"
|
|
||||||
"github.com/docker/libtrust"
|
|
||||||
"github.com/opencontainers/go-digest"
|
|
||||||
)
|
|
||||||
|
|
||||||
// signedManifestHandler is a ManifestHandler that covers schema1 manifests. It
|
|
||||||
// can unmarshal and put schema1 manifests that have been signed by libtrust.
|
|
||||||
type signedManifestHandler struct {
|
|
||||||
repository distribution.Repository
|
|
||||||
schema1SigningKey libtrust.PrivateKey
|
|
||||||
blobStore distribution.BlobStore
|
|
||||||
ctx context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ ManifestHandler = &signedManifestHandler{}
|
|
||||||
|
|
||||||
func (ms *signedManifestHandler) Unmarshal(ctx context.Context, dgst digest.Digest, content []byte) (distribution.Manifest, error) {
|
|
||||||
dcontext.GetLogger(ms.ctx).Debug("(*signedManifestHandler).Unmarshal")
|
|
||||||
|
|
||||||
var (
|
|
||||||
signatures [][]byte
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
jsig, err := libtrust.NewJSONSignature(content, signatures...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ms.schema1SigningKey != nil {
|
|
||||||
if err := jsig.Sign(ms.schema1SigningKey); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the pretty JWS
|
|
||||||
raw, err := jsig.PrettySignature("signatures")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var sm schema1.SignedManifest //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
if err := json.Unmarshal(raw, &sm); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &sm, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *signedManifestHandler) Put(ctx context.Context, manifest distribution.Manifest, skipDependencyVerification bool) (digest.Digest, error) {
|
|
||||||
dcontext.GetLogger(ms.ctx).Debug("(*signedManifestHandler).Put")
|
|
||||||
|
|
||||||
sm, ok := manifest.(*schema1.SignedManifest) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("non-schema1 manifest put to signedManifestHandler: %T", manifest)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ms.verifyManifest(ms.ctx, *sm, skipDependencyVerification); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
mt := schema1.MediaTypeManifest //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
payload := sm.Canonical
|
|
||||||
|
|
||||||
revision, err := ms.blobStore.Put(ctx, mt, payload)
|
|
||||||
if err != nil {
|
|
||||||
dcontext.GetLogger(ctx).Errorf("error putting payload into blobstore: %v", err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return revision.Digest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyManifest ensures that the manifest content is valid from the
|
|
||||||
// perspective of the registry. It ensures that the signature is valid for the
|
|
||||||
// enclosed payload. As a policy, the registry only tries to store valid
|
|
||||||
// content, leaving trust policies of that content up to consumers.
|
|
||||||
func (ms *signedManifestHandler) verifyManifest(ctx context.Context, mnfst schema1.SignedManifest, skipDependencyVerification bool) error { //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
var errs distribution.ErrManifestVerification
|
|
||||||
|
|
||||||
if len(mnfst.Name) > reference.NameTotalLengthMax {
|
|
||||||
errs = append(errs,
|
|
||||||
distribution.ErrManifestNameInvalid{
|
|
||||||
Name: mnfst.Name,
|
|
||||||
Reason: fmt.Errorf("manifest name must not be more than %v characters", reference.NameTotalLengthMax),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reference.NameRegexp.MatchString(mnfst.Name) {
|
|
||||||
errs = append(errs,
|
|
||||||
distribution.ErrManifestNameInvalid{
|
|
||||||
Name: mnfst.Name,
|
|
||||||
Reason: fmt.Errorf("invalid manifest name format"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(mnfst.History) != len(mnfst.FSLayers) {
|
|
||||||
errs = append(errs, fmt.Errorf("mismatched history and fslayer cardinality %d != %d",
|
|
||||||
len(mnfst.History), len(mnfst.FSLayers)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := schema1.Verify(&mnfst); err != nil { //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
switch err {
|
|
||||||
case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey:
|
|
||||||
errs = append(errs, distribution.ErrManifestUnverified{})
|
|
||||||
default:
|
|
||||||
if err.Error() == "invalid signature" { // TODO(stevvooe): This should be exported by libtrust
|
|
||||||
errs = append(errs, distribution.ErrManifestUnverified{})
|
|
||||||
} else {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !skipDependencyVerification {
|
|
||||||
for _, fsLayer := range mnfst.References() { //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
_, err := ms.repository.Blobs(ctx).Stat(ctx, fsLayer.Digest)
|
|
||||||
if err != nil {
|
|
||||||
if err != distribution.ErrBlobUnknown {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// On error here, we always append unknown blob errors.
|
|
||||||
errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: fsLayer.Digest})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(errs) != 0 {
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/distribution/distribution/v3"
|
|
||||||
digest "github.com/opencontainers/go-digest"
|
|
||||||
)
|
|
||||||
|
|
||||||
// signedManifestHandler is a ManifestHandler that unmarshals v1 manifests but
|
|
||||||
// refuses to Put v1 manifests
|
|
||||||
type v1UnsupportedHandler struct {
|
|
||||||
innerHandler ManifestHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ ManifestHandler = &v1UnsupportedHandler{}
|
|
||||||
|
|
||||||
func (v *v1UnsupportedHandler) Unmarshal(ctx context.Context, dgst digest.Digest, content []byte) (distribution.Manifest, error) {
|
|
||||||
return v.innerHandler.Unmarshal(ctx, dgst, content)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *v1UnsupportedHandler) Put(ctx context.Context, manifest distribution.Manifest, skipDependencyVerification bool) (digest.Digest, error) {
|
|
||||||
return digest.Digest(""), distribution.ErrSchemaV1Unsupported
|
|
||||||
}
|
|
|
@ -5,11 +5,8 @@ import (
|
||||||
|
|
||||||
"github.com/distribution/distribution/v3"
|
"github.com/distribution/distribution/v3"
|
||||||
"github.com/distribution/distribution/v3/context"
|
"github.com/distribution/distribution/v3/context"
|
||||||
"github.com/distribution/distribution/v3/manifest"
|
|
||||||
"github.com/distribution/distribution/v3/manifest/manifestlist"
|
"github.com/distribution/distribution/v3/manifest/manifestlist"
|
||||||
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
|
|
||||||
"github.com/distribution/distribution/v3/manifest/schema2"
|
"github.com/distribution/distribution/v3/manifest/schema2"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,38 +36,6 @@ func MakeManifestList(blobstatter distribution.BlobStatter, manifestDigests []di
|
||||||
return manifestlist.FromDescriptors(manifestDescriptors)
|
return manifestlist.FromDescriptors(manifestDescriptors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeSchema1Manifest constructs a schema 1 manifest from a given list of digests and returns
|
|
||||||
// the digest of the manifest.
|
|
||||||
//
|
|
||||||
// Deprecated: Docker Image Manifest v2, Schema 1 is deprecated since 2015.
|
|
||||||
// Use Docker Image Manifest v2, Schema 2, or the OCI Image Specification.
|
|
||||||
func MakeSchema1Manifest(digests []digest.Digest) (*schema1.SignedManifest, error) {
|
|
||||||
mfst := schema1.Manifest{
|
|
||||||
Versioned: manifest.Versioned{
|
|
||||||
SchemaVersion: 1,
|
|
||||||
},
|
|
||||||
Name: "who",
|
|
||||||
Tag: "cares",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, d := range digests {
|
|
||||||
mfst.FSLayers = append(mfst.FSLayers, schema1.FSLayer{BlobSum: d})
|
|
||||||
mfst.History = append(mfst.History, schema1.History{V1Compatibility: ""})
|
|
||||||
}
|
|
||||||
|
|
||||||
pk, err := libtrust.GenerateECP256PrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unexpected error generating private key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
signedManifest, err := schema1.Sign(&mfst, pk)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error signing manifest: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return signedManifest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeSchema2Manifest constructs a schema 2 manifest from a given list of digests and returns
|
// MakeSchema2Manifest constructs a schema 2 manifest from a given list of digests and returns
|
||||||
// the digest of the manifest
|
// the digest of the manifest
|
||||||
func MakeSchema2Manifest(repository distribution.Repository, digests []digest.Digest) (distribution.Manifest, error) {
|
func MakeSchema2Manifest(repository distribution.Repository, digests []digest.Digest) (distribution.Manifest, error) {
|
||||||
|
|
Loading…
Reference in a new issue