Merge pull request #1687 from RichardScothern/signature-store
Remove signature store from registry.master
						commit
						b370cfc940
					
				| 
						 | 
				
			
			@ -1067,13 +1067,13 @@ func testManifestAPISchema1(t *testing.T, env *testEnv, imageName reference.Name
 | 
			
		|||
		t.Fatalf("error decoding fetched manifest: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check two signatures were roundtripped
 | 
			
		||||
	// check only 1 signature is returned
 | 
			
		||||
	signatures, err = fetchedManifestByDigest.Signatures()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(signatures) != 2 {
 | 
			
		||||
	if len(signatures) != 1 {
 | 
			
		||||
		t.Fatalf("expected 2 signature from manifest, got: %d", len(signatures))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -155,6 +155,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
 | 
			
		|||
	app.configureRedis(config)
 | 
			
		||||
	app.configureLogHook(config)
 | 
			
		||||
 | 
			
		||||
	options := registrymiddleware.GetRegistryOptions()
 | 
			
		||||
	if config.Compatibility.Schema1.TrustKey != "" {
 | 
			
		||||
		app.trustKey, err = libtrust.LoadKeyFile(config.Compatibility.Schema1.TrustKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -169,6 +170,8 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	options = append(options, storage.Schema1SigningKey(app.trustKey))
 | 
			
		||||
 | 
			
		||||
	if config.HTTP.Host != "" {
 | 
			
		||||
		u, err := url.Parse(config.HTTP.Host)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -177,17 +180,10 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
 | 
			
		|||
		app.httpHost = *u
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	options := registrymiddleware.GetRegistryOptions()
 | 
			
		||||
 | 
			
		||||
	if app.isCache {
 | 
			
		||||
		options = append(options, storage.DisableDigestResumption)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.Compatibility.Schema1.DisableSignatureStore {
 | 
			
		||||
		options = append(options, storage.DisableSchema1Signatures)
 | 
			
		||||
		options = append(options, storage.Schema1SigningKey(app.trustKey))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// configure deletion
 | 
			
		||||
	if d, ok := config.Storage["delete"]; ok {
 | 
			
		||||
		e, ok := d["enabled"]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,12 +60,6 @@ func (sm statsManifest) Put(ctx context.Context, manifest distribution.Manifest,
 | 
			
		|||
	return sm.manifests.Put(ctx, manifest)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*func (sm statsManifest) Enumerate(ctx context.Context, manifests []distribution.Manifest, last distribution.Manifest) (n int, err error) {
 | 
			
		||||
	sm.stats["enumerate"]++
 | 
			
		||||
	return sm.manifests.Enumerate(ctx, manifests, last)
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
type mockChallenger struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	count int
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +69,6 @@ type mockChallenger struct {
 | 
			
		|||
func (m *mockChallenger) tryEstablishChallenges(context.Context) error {
 | 
			
		||||
	m.Lock()
 | 
			
		||||
	defer m.Unlock()
 | 
			
		||||
 | 
			
		||||
	m.count++
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -93,9 +86,15 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unable to parse reference: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	k, err := libtrust.GenerateECP256PrivateKey()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	truthRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()))
 | 
			
		||||
	truthRegistry, err := storage.NewRegistry(ctx, inmemory.New(),
 | 
			
		||||
		storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()),
 | 
			
		||||
		storage.Schema1SigningKey(k))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error creating registry: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +116,7 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE
 | 
			
		|||
		t.Fatalf(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableRedirect, storage.DisableDigestResumption)
 | 
			
		||||
	localRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableRedirect, storage.DisableDigestResumption, storage.Schema1SigningKey(k))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error creating registry: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,7 +69,7 @@ var GCCmd = &cobra.Command{
 | 
			
		|||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		registry, err := storage.NewRegistry(ctx, driver, storage.DisableSchema1Signatures, storage.Schema1SigningKey(k))
 | 
			
		||||
		registry, err := storage.NewRegistry(ctx, driver, storage.Schema1SigningKey(k))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "failed to construct registry: %v", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,7 +75,6 @@ func (bs *blobStore) Put(ctx context.Context, mediaType string, p []byte) (distr
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO(stevvooe): Write out mediatype here, as well.
 | 
			
		||||
 | 
			
		||||
	return distribution.Descriptor{
 | 
			
		||||
		Size: int64(len(p)),
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,6 @@ import (
 | 
			
		|||
	"github.com/docker/distribution"
 | 
			
		||||
	"github.com/docker/distribution/context"
 | 
			
		||||
	"github.com/docker/distribution/digest"
 | 
			
		||||
	"github.com/docker/distribution/manifest/schema1"
 | 
			
		||||
	"github.com/docker/distribution/manifest/schema2"
 | 
			
		||||
	"github.com/docker/distribution/reference"
 | 
			
		||||
	"github.com/docker/distribution/registry/storage/driver"
 | 
			
		||||
| 
						 | 
				
			
			@ -71,22 +70,6 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
			switch manifest.(type) {
 | 
			
		||||
			case *schema1.SignedManifest:
 | 
			
		||||
				signaturesGetter, ok := manifestService.(distribution.SignaturesGetter)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					return fmt.Errorf("unable to convert ManifestService into SignaturesGetter")
 | 
			
		||||
				}
 | 
			
		||||
				signatures, err := signaturesGetter.GetSignatures(ctx, dgst)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return fmt.Errorf("failed to get signatures for signed manifest: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
				for _, signatureDigest := range signatures {
 | 
			
		||||
					if dryRun {
 | 
			
		||||
						emit("%s: marking signature %s", repoName, signatureDigest)
 | 
			
		||||
					}
 | 
			
		||||
					markSet[signatureDigest] = struct{}{}
 | 
			
		||||
				}
 | 
			
		||||
				break
 | 
			
		||||
			case *schema2.DeserializedManifest:
 | 
			
		||||
				config := manifest.(*schema2.DeserializedManifest).Config
 | 
			
		||||
				if dryRun {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ import (
 | 
			
		|||
	"github.com/docker/distribution/registry/storage/driver"
 | 
			
		||||
	"github.com/docker/distribution/registry/storage/driver/inmemory"
 | 
			
		||||
	"github.com/docker/distribution/testutil"
 | 
			
		||||
	"github.com/docker/libtrust"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type image struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +23,11 @@ type image struct {
 | 
			
		|||
 | 
			
		||||
func createRegistry(t *testing.T, driver driver.StorageDriver) distribution.Namespace {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	registry, err := NewRegistry(ctx, driver, EnableDelete)
 | 
			
		||||
	k, err := libtrust.GenerateECP256PrivateKey()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	registry, err := NewRegistry(ctx, driver, EnableDelete, Schema1SigningKey(k))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Failed to construct namespace")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -139,13 +144,13 @@ func TestNoDeletionNoEffect(t *testing.T) {
 | 
			
		|||
	ctx := context.Background()
 | 
			
		||||
	inmemoryDriver := inmemory.New()
 | 
			
		||||
 | 
			
		||||
	registry := createRegistry(t, inmemoryDriver)
 | 
			
		||||
	registry := createRegistry(t, inmemory.New())
 | 
			
		||||
	repo := makeRepository(t, registry, "palailogos")
 | 
			
		||||
	manifestService, err := repo.Manifests(ctx)
 | 
			
		||||
 | 
			
		||||
	image1 := uploadRandomSchema1Image(t, repo)
 | 
			
		||||
	image2 := uploadRandomSchema1Image(t, repo)
 | 
			
		||||
	image3 := uploadRandomSchema2Image(t, repo)
 | 
			
		||||
	uploadRandomSchema2Image(t, repo)
 | 
			
		||||
 | 
			
		||||
	// construct manifestlist for fun.
 | 
			
		||||
	blobstatter := registry.BlobStatter()
 | 
			
		||||
| 
						 | 
				
			
			@ -160,20 +165,17 @@ func TestNoDeletionNoEffect(t *testing.T) {
 | 
			
		|||
		t.Fatalf("Failed to add manifest list: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	before := allBlobs(t, registry)
 | 
			
		||||
 | 
			
		||||
	// Run GC
 | 
			
		||||
	err = MarkAndSweep(context.Background(), inmemoryDriver, registry, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Failed mark and sweep: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	blobs := allBlobs(t, registry)
 | 
			
		||||
 | 
			
		||||
	// the +1 at the end is for the manifestList
 | 
			
		||||
	// the first +3 at the end for each manifest's blob
 | 
			
		||||
	// the second +3 at the end for each manifest's signature/config layer
 | 
			
		||||
	totalBlobCount := len(image1.layers) + len(image2.layers) + len(image3.layers) + 1 + 3 + 3
 | 
			
		||||
	if len(blobs) != totalBlobCount {
 | 
			
		||||
		t.Fatalf("Garbage collection affected storage")
 | 
			
		||||
	after := allBlobs(t, registry)
 | 
			
		||||
	if len(before) != len(after) {
 | 
			
		||||
		t.Fatalf("Garbage collection affected storage: %d != %d", len(before), len(after))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,6 @@ package storage
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path"
 | 
			
		||||
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/docker/distribution"
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +11,6 @@ import (
 | 
			
		|||
	"github.com/docker/distribution/manifest/manifestlist"
 | 
			
		||||
	"github.com/docker/distribution/manifest/schema1"
 | 
			
		||||
	"github.com/docker/distribution/manifest/schema2"
 | 
			
		||||
	"github.com/docker/distribution/registry/storage/driver"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A ManifestHandler gets and puts manifests of a particular type.
 | 
			
		||||
| 
						 | 
				
			
			@ -141,48 +139,3 @@ func (ms *manifestStore) Enumerate(ctx context.Context, ingester func(digest.Dig
 | 
			
		|||
	})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Only valid for schema1 signed manifests
 | 
			
		||||
func (ms *manifestStore) GetSignatures(ctx context.Context, manifestDigest digest.Digest) ([]digest.Digest, error) {
 | 
			
		||||
	// sanity check that digest refers to a schema1 digest
 | 
			
		||||
	manifest, err := ms.Get(ctx, manifestDigest)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := manifest.(*schema1.SignedManifest); !ok {
 | 
			
		||||
		return nil, fmt.Errorf("digest %v is not for schema1 manifest", manifestDigest)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	signaturesPath, err := pathFor(manifestSignaturesPathSpec{
 | 
			
		||||
		name:     ms.repository.Named().Name(),
 | 
			
		||||
		revision: manifestDigest,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var digests []digest.Digest
 | 
			
		||||
	alg := string(digest.SHA256)
 | 
			
		||||
	signaturePaths, err := ms.blobStore.driver.List(ctx, path.Join(signaturesPath, alg))
 | 
			
		||||
 | 
			
		||||
	switch err.(type) {
 | 
			
		||||
	case nil:
 | 
			
		||||
		break
 | 
			
		||||
	case driver.PathNotFoundError:
 | 
			
		||||
		// Manifest may have been pushed with signature store disabled
 | 
			
		||||
		return digests, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, sigPath := range signaturePaths {
 | 
			
		||||
		sigdigest, err := digest.ParseDigest(alg + ":" + path.Base(sigPath))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// merely found not a digest
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		digests = append(digests, sigdigest)
 | 
			
		||||
	}
 | 
			
		||||
	return digests, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,15 +52,11 @@ func newManifestStoreTestEnv(t *testing.T, name reference.Named, tag string, opt
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestManifestStorage(t *testing.T) {
 | 
			
		||||
	testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestManifestStorageDisabledSignatures(t *testing.T) {
 | 
			
		||||
	k, err := libtrust.GenerateECP256PrivateKey()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect, DisableSchema1Signatures, Schema1SigningKey(k))
 | 
			
		||||
	testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect, Schema1SigningKey(k))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testManifestStorage(t *testing.T, options ...RegistryOption) {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +67,6 @@ func testManifestStorage(t *testing.T, options ...RegistryOption) {
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	equalSignatures := env.registry.(*registry).schema1SignaturesEnabled
 | 
			
		||||
 | 
			
		||||
	m := schema1.Manifest{
 | 
			
		||||
		Versioned: manifest.Versioned{
 | 
			
		||||
| 
						 | 
				
			
			@ -175,12 +170,6 @@ func testManifestStorage(t *testing.T, options ...RegistryOption) {
 | 
			
		|||
		t.Fatalf("fetched payload does not match original payload: %q != %q", fetchedManifest.Canonical, sm.Canonical)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if equalSignatures {
 | 
			
		||||
		if !reflect.DeepEqual(fetchedManifest, sm) {
 | 
			
		||||
			t.Fatalf("fetched manifest not equal: %#v != %#v", fetchedManifest.Manifest, sm.Manifest)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, pl, err := fetchedManifest.Payload()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error getting payload %#v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -223,12 +212,6 @@ func testManifestStorage(t *testing.T, options ...RegistryOption) {
 | 
			
		|||
		t.Fatalf("fetched manifest not equal: %q != %q", byDigestManifest.Canonical, fetchedManifest.Canonical)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if equalSignatures {
 | 
			
		||||
		if !reflect.DeepEqual(fetchedByDigest, fetchedManifest) {
 | 
			
		||||
			t.Fatalf("fetched manifest not equal: %#v != %#v", fetchedByDigest, fetchedManifest)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sigs, err := fetchedJWS.Signatures()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unable to extract signatures: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -285,17 +268,6 @@ func testManifestStorage(t *testing.T, options ...RegistryOption) {
 | 
			
		|||
		t.Fatalf("unexpected error verifying manifest: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Assemble our payload and two signatures to get what we expect!
 | 
			
		||||
	expectedJWS, err := libtrust.NewJSONSignature(payload, sigs[0], sigs2[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error merging jws: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expectedSigs, err := expectedJWS.Signatures()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error getting expected signatures: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, pl, err = fetched.Payload()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error getting payload %#v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -315,19 +287,6 @@ func testManifestStorage(t *testing.T, options ...RegistryOption) {
 | 
			
		|||
		t.Fatalf("payloads are not equal")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if equalSignatures {
 | 
			
		||||
		receivedSigs, err := receivedJWS.Signatures()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("error getting signatures: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for i, sig := range receivedSigs {
 | 
			
		||||
			if !bytes.Equal(sig, expectedSigs[i]) {
 | 
			
		||||
				t.Fatalf("mismatched signatures from remote: %v != %v", string(sig), string(expectedSigs[i]))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test deleting manifests
 | 
			
		||||
	err = ms.Delete(ctx, dgst)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,8 +30,6 @@ const (
 | 
			
		|||
// 						revisions
 | 
			
		||||
//							-> <manifest digest path>
 | 
			
		||||
//								-> link
 | 
			
		||||
//								-> signatures
 | 
			
		||||
// 									<algorithm>/<digest>/link
 | 
			
		||||
// 						tags/<tag>
 | 
			
		||||
//							-> current/link
 | 
			
		||||
// 							-> index
 | 
			
		||||
| 
						 | 
				
			
			@ -62,8 +60,7 @@ const (
 | 
			
		|||
//
 | 
			
		||||
// The third component of the repository directory is the manifests store,
 | 
			
		||||
// which is made up of a revision store and tag store. Manifests are stored in
 | 
			
		||||
// the blob store and linked into the revision store. Signatures are separated
 | 
			
		||||
// from the manifest payload data and linked into the blob store, as well.
 | 
			
		||||
// the blob store and linked into the revision store.
 | 
			
		||||
// While the registry can save all revisions of a manifest, no relationship is
 | 
			
		||||
// implied as to the ordering of changes to a manifest. The tag store provides
 | 
			
		||||
// support for name, tag lookups of manifests, using "current/link" under a
 | 
			
		||||
| 
						 | 
				
			
			@ -77,8 +74,6 @@ const (
 | 
			
		|||
// 	manifestRevisionsPathSpec:      <root>/v2/repositories/<name>/_manifests/revisions/
 | 
			
		||||
// 	manifestRevisionPathSpec:      <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/
 | 
			
		||||
// 	manifestRevisionLinkPathSpec:  <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/link
 | 
			
		||||
// 	manifestSignaturesPathSpec:    <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/signatures/
 | 
			
		||||
// 	manifestSignatureLinkPathSpec: <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/signatures/<algorithm>/<hex digest>/link
 | 
			
		||||
//
 | 
			
		||||
//	Tags:
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -148,33 +143,6 @@ func pathFor(spec pathSpec) (string, error) {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		return path.Join(root, "link"), nil
 | 
			
		||||
	case manifestSignaturesPathSpec:
 | 
			
		||||
		root, err := pathFor(manifestRevisionPathSpec{
 | 
			
		||||
			name:     v.name,
 | 
			
		||||
			revision: v.revision,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return path.Join(root, "signatures"), nil
 | 
			
		||||
	case manifestSignatureLinkPathSpec:
 | 
			
		||||
		root, err := pathFor(manifestSignaturesPathSpec{
 | 
			
		||||
			name:     v.name,
 | 
			
		||||
			revision: v.revision,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		signatureComponents, err := digestPathComponents(v.signature, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return path.Join(root, path.Join(append(signatureComponents, "link")...)), nil
 | 
			
		||||
	case manifestTagsPathSpec:
 | 
			
		||||
		return path.Join(append(repoPrefix, v.name, "_manifests", "tags")...), nil
 | 
			
		||||
	case manifestTagPathSpec:
 | 
			
		||||
| 
						 | 
				
			
			@ -325,26 +293,6 @@ type manifestRevisionLinkPathSpec struct {
 | 
			
		|||
 | 
			
		||||
func (manifestRevisionLinkPathSpec) pathSpec() {}
 | 
			
		||||
 | 
			
		||||
// manifestSignaturesPathSpec describes the path components for the directory
 | 
			
		||||
// containing all the signatures for the target blob. Entries are named with
 | 
			
		||||
// the underlying key id.
 | 
			
		||||
type manifestSignaturesPathSpec struct {
 | 
			
		||||
	name     string
 | 
			
		||||
	revision digest.Digest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (manifestSignaturesPathSpec) pathSpec() {}
 | 
			
		||||
 | 
			
		||||
// manifestSignatureLinkPathSpec describes the path components used to look up
 | 
			
		||||
// a signature file by the hash of its blob.
 | 
			
		||||
type manifestSignatureLinkPathSpec struct {
 | 
			
		||||
	name      string
 | 
			
		||||
	revision  digest.Digest
 | 
			
		||||
	signature digest.Digest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (manifestSignatureLinkPathSpec) pathSpec() {}
 | 
			
		||||
 | 
			
		||||
// manifestTagsPathSpec describes the path elements required to point to the
 | 
			
		||||
// manifest tags directory.
 | 
			
		||||
type manifestTagsPathSpec struct {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,21 +26,6 @@ func TestPathMapper(t *testing.T) {
 | 
			
		|||
			},
 | 
			
		||||
			expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/link",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			spec: manifestSignatureLinkPathSpec{
 | 
			
		||||
				name:      "foo/bar",
 | 
			
		||||
				revision:  "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
 | 
			
		||||
				signature: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
 | 
			
		||||
			},
 | 
			
		||||
			expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/signatures/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/link",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			spec: manifestSignaturesPathSpec{
 | 
			
		||||
				name:     "foo/bar",
 | 
			
		||||
				revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
 | 
			
		||||
			},
 | 
			
		||||
			expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/signatures",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			spec: manifestTagsPathSpec{
 | 
			
		||||
				name: "foo/bar",
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +98,7 @@ func TestPathMapper(t *testing.T) {
 | 
			
		|||
	// Add a few test cases to ensure we cover some errors
 | 
			
		||||
 | 
			
		||||
	// Specify a path that requires a revision and get a digest validation error.
 | 
			
		||||
	badpath, err := pathFor(manifestSignaturesPathSpec{
 | 
			
		||||
	badpath, err := pathFor(manifestRevisionPathSpec{
 | 
			
		||||
		name: "foo/bar",
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,6 @@ type registry struct {
 | 
			
		|||
	blobDescriptorCacheProvider  cache.BlobDescriptorCacheProvider
 | 
			
		||||
	deleteEnabled                bool
 | 
			
		||||
	resumableDigestEnabled       bool
 | 
			
		||||
	schema1SignaturesEnabled     bool
 | 
			
		||||
	schema1SigningKey            libtrust.PrivateKey
 | 
			
		||||
	blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -47,17 +46,8 @@ func DisableDigestResumption(registry *registry) error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DisableSchema1Signatures is a functional option for NewRegistry. It disables
 | 
			
		||||
// signature storage and ensures all schema1 manifests will only be returned
 | 
			
		||||
// with a signature from a provided signing key.
 | 
			
		||||
func DisableSchema1Signatures(registry *registry) error {
 | 
			
		||||
	registry.schema1SignaturesEnabled = false
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Schema1SigningKey returns a functional option for NewRegistry. It sets the
 | 
			
		||||
// signing key for adding a signature to all schema1 manifests. This should be
 | 
			
		||||
// used in conjunction with disabling signature store.
 | 
			
		||||
// key for signing  all schema1 manifests.
 | 
			
		||||
func Schema1SigningKey(key libtrust.PrivateKey) RegistryOption {
 | 
			
		||||
	return func(registry *registry) error {
 | 
			
		||||
		registry.schema1SigningKey = key
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +108,6 @@ func NewRegistry(ctx context.Context, driver storagedriver.StorageDriver, option
 | 
			
		|||
		},
 | 
			
		||||
		statter:                statter,
 | 
			
		||||
		resumableDigestEnabled: true,
 | 
			
		||||
		schema1SignaturesEnabled: true,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, option := range options {
 | 
			
		||||
| 
						 | 
				
			
			@ -231,11 +220,6 @@ func (repo *repository) Manifests(ctx context.Context, options ...distribution.M
 | 
			
		|||
			ctx:        ctx,
 | 
			
		||||
			repository: repo,
 | 
			
		||||
			blobStore:  blobStore,
 | 
			
		||||
			signatures: &signatureStore{
 | 
			
		||||
				ctx:        ctx,
 | 
			
		||||
				repository: repo,
 | 
			
		||||
				blobStore:  repo.blobStore,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		schema2Handler: &schema2ManifestHandler{
 | 
			
		||||
			ctx:        ctx,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,131 +0,0 @@
 | 
			
		|||
package storage
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/distribution/context"
 | 
			
		||||
	"github.com/docker/distribution/digest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type signatureStore struct {
 | 
			
		||||
	repository *repository
 | 
			
		||||
	blobStore  *blobStore
 | 
			
		||||
	ctx        context.Context
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *signatureStore) Get(dgst digest.Digest) ([][]byte, error) {
 | 
			
		||||
	signaturesPath, err := pathFor(manifestSignaturesPathSpec{
 | 
			
		||||
		name:     s.repository.Named().Name(),
 | 
			
		||||
		revision: dgst,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Need to append signature digest algorithm to path to get all items.
 | 
			
		||||
	// Perhaps, this should be in the pathMapper but it feels awkward. This
 | 
			
		||||
	// can be eliminated by implementing listAll on drivers.
 | 
			
		||||
	signaturesPath = path.Join(signaturesPath, "sha256")
 | 
			
		||||
 | 
			
		||||
	signaturePaths, err := s.blobStore.driver.List(s.ctx, signaturesPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	type result struct {
 | 
			
		||||
		index     int
 | 
			
		||||
		signature []byte
 | 
			
		||||
		err       error
 | 
			
		||||
	}
 | 
			
		||||
	ch := make(chan result)
 | 
			
		||||
 | 
			
		||||
	bs := s.linkedBlobStore(s.ctx, dgst)
 | 
			
		||||
	for i, sigPath := range signaturePaths {
 | 
			
		||||
		sigdgst, err := digest.ParseDigest("sha256:" + path.Base(sigPath))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			context.GetLogger(s.ctx).Errorf("could not get digest from path: %q, skipping", sigPath)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		wg.Add(1)
 | 
			
		||||
		go func(idx int, sigdgst digest.Digest) {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			context.GetLogger(s.ctx).
 | 
			
		||||
				Debugf("fetching signature %q", sigdgst)
 | 
			
		||||
 | 
			
		||||
			r := result{index: idx}
 | 
			
		||||
 | 
			
		||||
			if p, err := bs.Get(s.ctx, sigdgst); err != nil {
 | 
			
		||||
				context.GetLogger(s.ctx).
 | 
			
		||||
					Errorf("error fetching signature %q: %v", sigdgst, err)
 | 
			
		||||
				r.err = err
 | 
			
		||||
			} else {
 | 
			
		||||
				r.signature = p
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ch <- r
 | 
			
		||||
		}(i, sigdgst)
 | 
			
		||||
	}
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	go func() {
 | 
			
		||||
		wg.Wait()
 | 
			
		||||
		close(done)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// aggregrate the results
 | 
			
		||||
	signatures := make([][]byte, len(signaturePaths))
 | 
			
		||||
loop:
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case result := <-ch:
 | 
			
		||||
			signatures[result.index] = result.signature
 | 
			
		||||
			if result.err != nil && err == nil {
 | 
			
		||||
				// only set the first one.
 | 
			
		||||
				err = result.err
 | 
			
		||||
			}
 | 
			
		||||
		case <-done:
 | 
			
		||||
			break loop
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return signatures, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *signatureStore) Put(dgst digest.Digest, signatures ...[]byte) error {
 | 
			
		||||
	bs := s.linkedBlobStore(s.ctx, dgst)
 | 
			
		||||
	for _, signature := range signatures {
 | 
			
		||||
		if _, err := bs.Put(s.ctx, "application/json", signature); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// linkedBlobStore returns the namedBlobStore of the signatures for the
 | 
			
		||||
// manifest with the given digest. Effectively, each signature link path
 | 
			
		||||
// layout is a unique linked blob store.
 | 
			
		||||
func (s *signatureStore) linkedBlobStore(ctx context.Context, revision digest.Digest) *linkedBlobStore {
 | 
			
		||||
	linkpath := func(name string, dgst digest.Digest) (string, error) {
 | 
			
		||||
		return pathFor(manifestSignatureLinkPathSpec{
 | 
			
		||||
			name:      name,
 | 
			
		||||
			revision:  revision,
 | 
			
		||||
			signature: dgst,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &linkedBlobStore{
 | 
			
		||||
		ctx:        ctx,
 | 
			
		||||
		repository: s.repository,
 | 
			
		||||
		blobStore:  s.blobStore,
 | 
			
		||||
		blobAccessController: &linkedBlobStatter{
 | 
			
		||||
			blobStore:   s.blobStore,
 | 
			
		||||
			repository:  s.repository,
 | 
			
		||||
			linkPathFns: []linkPathFunc{linkpath},
 | 
			
		||||
		},
 | 
			
		||||
		linkPathFns: []linkPathFunc{linkpath},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +18,6 @@ type signedManifestHandler struct {
 | 
			
		|||
	repository *repository
 | 
			
		||||
	blobStore  *linkedBlobStore
 | 
			
		||||
	ctx        context.Context
 | 
			
		||||
	signatures *signatureStore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ ManifestHandler = &signedManifestHandler{}
 | 
			
		||||
| 
						 | 
				
			
			@ -30,13 +29,6 @@ func (ms *signedManifestHandler) Unmarshal(ctx context.Context, dgst digest.Dige
 | 
			
		|||
		signatures [][]byte
 | 
			
		||||
		err        error
 | 
			
		||||
	)
 | 
			
		||||
	if ms.repository.schema1SignaturesEnabled {
 | 
			
		||||
		// Fetch the signatures for the manifest
 | 
			
		||||
		signatures, err = ms.signatures.Get(dgst)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	jsig, err := libtrust.NewJSONSignature(content, signatures...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -47,8 +39,6 @@ func (ms *signedManifestHandler) Unmarshal(ctx context.Context, dgst digest.Dige
 | 
			
		|||
		if err := jsig.Sign(ms.repository.schema1SigningKey); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	} else if !ms.repository.schema1SignaturesEnabled {
 | 
			
		||||
		return nil, fmt.Errorf("missing signing key with signature store disabled")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Extract the pretty JWS
 | 
			
		||||
| 
						 | 
				
			
			@ -90,18 +80,6 @@ func (ms *signedManifestHandler) Put(ctx context.Context, manifest distribution.
 | 
			
		|||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ms.repository.schema1SignaturesEnabled {
 | 
			
		||||
		// Grab each json signature and store them.
 | 
			
		||||
		signatures, err := sm.Signatures()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := ms.signatures.Put(revision.Digest, signatures...); err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return revision.Digest, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue