Remove signature store from registry. Return a generated signature for manifest
pull. Signed-off-by: Richard Scothern <richard.scothern@docker.com>master
							parent
							
								
									596ca8b86a
								
							
						
					
					
						commit
						0c15ab6952
					
				|  | @ -157,11 +157,6 @@ type Configuration struct { | ||||||
| 			// TrustKey is the signing key to use for adding the signature to
 | 			// TrustKey is the signing key to use for adding the signature to
 | ||||||
| 			// schema1 manifests.
 | 			// schema1 manifests.
 | ||||||
| 			TrustKey string `yaml:"signingkeyfile,omitempty"` | 			TrustKey string `yaml:"signingkeyfile,omitempty"` | ||||||
| 
 |  | ||||||
| 			// DisableSignatureStore will cause all signatures attached to schema1 manifests
 |  | ||||||
| 			// to be ignored. Signatures will be generated on all schema1 manifest requests
 |  | ||||||
| 			// rather than only requests which converted schema2 to schema1.
 |  | ||||||
| 			DisableSignatureStore bool `yaml:"disablesignaturestore,omitempty"` |  | ||||||
| 		} `yaml:"schema1,omitempty"` | 		} `yaml:"schema1,omitempty"` | ||||||
| 	} `yaml:"compatibility,omitempty"` | 	} `yaml:"compatibility,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -243,7 +243,6 @@ information about each option that appears later in this page. | ||||||
|     compatibility: |     compatibility: | ||||||
|       schema1: |       schema1: | ||||||
|         signingkeyfile: /etc/registry/key.json |         signingkeyfile: /etc/registry/key.json | ||||||
|         disablesignaturestore: true |  | ||||||
| 
 | 
 | ||||||
| In some instances a configuration option is **optional** but it contains child | In some instances a configuration option is **optional** but it contains child | ||||||
| options marked as **required**. This indicates that you can omit the parent with | options marked as **required**. This indicates that you can omit the parent with | ||||||
|  | @ -1730,7 +1729,6 @@ To enable pulling private repositories (e.g. `batman/robin`) a username and pass | ||||||
|     compatibility: |     compatibility: | ||||||
|       schema1: |       schema1: | ||||||
|         signingkeyfile: /etc/registry/key.json |         signingkeyfile: /etc/registry/key.json | ||||||
|         disablesignaturestore: true |  | ||||||
| 
 | 
 | ||||||
| Configure handling of older and deprecated features. Each subsection | Configure handling of older and deprecated features. Each subsection | ||||||
| defines a such a feature with configurable behavior. | defines a such a feature with configurable behavior. | ||||||
|  | @ -1756,23 +1754,6 @@ defines a such a feature with configurable behavior. | ||||||
|      startup. |      startup. | ||||||
|     </td> |     </td> | ||||||
|   </tr> |   </tr> | ||||||
|   <tr> |  | ||||||
|     <td> |  | ||||||
|       <code>disablesignaturestore</code> |  | ||||||
|     </td> |  | ||||||
|     <td> |  | ||||||
|       no |  | ||||||
|     </td> |  | ||||||
|     <td> |  | ||||||
|      Disables storage of signatures attached to schema1 manifests. By default |  | ||||||
|      signatures are detached from schema1 manifests, stored, and reattached |  | ||||||
|      when the manifest is requested. When this is true, the storage is disabled |  | ||||||
|      and a new signature is always generated for schema1 manifests using the |  | ||||||
|      schema1 signing key. Disabling signature storage will cause all newly |  | ||||||
|      uploaded signatures to be discarded. Existing stored signatures will not |  | ||||||
|      be removed but they will not be re-attached to the corresponding manifest. |  | ||||||
|     </td> |  | ||||||
|   </tr> |  | ||||||
| </table> | </table> | ||||||
| 
 | 
 | ||||||
| ## Example: Development configuration | ## Example: Development configuration | ||||||
|  |  | ||||||
|  | @ -61,12 +61,6 @@ type ManifestEnumerator interface { | ||||||
| 	Enumerate(ctx context.Context, ingester func(digest.Digest) error) error | 	Enumerate(ctx context.Context, ingester func(digest.Digest) error) error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SignaturesGetter provides an interface for getting the signatures of a schema1 manifest. If the digest
 |  | ||||||
| // referred to is not a schema1 manifest, an error should be returned.
 |  | ||||||
| type SignaturesGetter interface { |  | ||||||
| 	GetSignatures(ctx context.Context, manifestDigest digest.Digest) ([]digest.Digest, error) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Describable is an interface for descriptors
 | // Describable is an interface for descriptors
 | ||||||
| type Describable interface { | type Describable interface { | ||||||
| 	Descriptor() Descriptor | 	Descriptor() Descriptor | ||||||
|  |  | ||||||
|  | @ -20,7 +20,12 @@ import ( | ||||||
| 
 | 
 | ||||||
| func TestListener(t *testing.T) { | func TestListener(t *testing.T) { | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
| 	registry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableDelete, storage.EnableRedirect) | 	k, err := libtrust.GenerateECP256PrivateKey() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	registry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableDelete, storage.EnableRedirect, storage.Schema1SigningKey(k)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("error creating registry: %v", err) | 		t.Fatalf("error creating registry: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -1067,13 +1067,13 @@ func testManifestAPISchema1(t *testing.T, env *testEnv, imageName reference.Name | ||||||
| 		t.Fatalf("error decoding fetched manifest: %v", err) | 		t.Fatalf("error decoding fetched manifest: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// check two signatures were roundtripped
 | 	// check only 1 signature is returned
 | ||||||
| 	signatures, err = fetchedManifestByDigest.Signatures() | 	signatures, err = fetchedManifestByDigest.Signatures() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if len(signatures) != 2 { | 	if len(signatures) != 1 { | ||||||
| 		t.Fatalf("expected 2 signature from manifest, got: %d", len(signatures)) | 		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.configureRedis(config) | ||||||
| 	app.configureLogHook(config) | 	app.configureLogHook(config) | ||||||
| 
 | 
 | ||||||
|  | 	options := registrymiddleware.GetRegistryOptions() | ||||||
| 	if config.Compatibility.Schema1.TrustKey != "" { | 	if config.Compatibility.Schema1.TrustKey != "" { | ||||||
| 		app.trustKey, err = libtrust.LoadKeyFile(config.Compatibility.Schema1.TrustKey) | 		app.trustKey, err = libtrust.LoadKeyFile(config.Compatibility.Schema1.TrustKey) | ||||||
| 		if err != nil { | 		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 != "" { | 	if config.HTTP.Host != "" { | ||||||
| 		u, err := url.Parse(config.HTTP.Host) | 		u, err := url.Parse(config.HTTP.Host) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | @ -177,17 +180,10 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App { | ||||||
| 		app.httpHost = *u | 		app.httpHost = *u | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	options := registrymiddleware.GetRegistryOptions() |  | ||||||
| 
 |  | ||||||
| 	if app.isCache { | 	if app.isCache { | ||||||
| 		options = append(options, storage.DisableDigestResumption) | 		options = append(options, storage.DisableDigestResumption) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if config.Compatibility.Schema1.DisableSignatureStore { |  | ||||||
| 		options = append(options, storage.DisableSchema1Signatures) |  | ||||||
| 		options = append(options, storage.Schema1SigningKey(app.trustKey)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// configure deletion
 | 	// configure deletion
 | ||||||
| 	if d, ok := config.Storage["delete"]; ok { | 	if d, ok := config.Storage["delete"]; ok { | ||||||
| 		e, ok := d["enabled"] | 		e, ok := d["enabled"] | ||||||
|  |  | ||||||
|  | @ -60,12 +60,6 @@ func (sm statsManifest) Put(ctx context.Context, manifest distribution.Manifest, | ||||||
| 	return sm.manifests.Put(ctx, 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 { | type mockChallenger struct { | ||||||
| 	sync.Mutex | 	sync.Mutex | ||||||
| 	count int | 	count int | ||||||
|  | @ -75,7 +69,6 @@ type mockChallenger struct { | ||||||
| func (m *mockChallenger) tryEstablishChallenges(context.Context) error { | func (m *mockChallenger) tryEstablishChallenges(context.Context) error { | ||||||
| 	m.Lock() | 	m.Lock() | ||||||
| 	defer m.Unlock() | 	defer m.Unlock() | ||||||
| 
 |  | ||||||
| 	m.count++ | 	m.count++ | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | @ -93,9 +86,15 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("unable to parse reference: %s", err) | 		t.Fatalf("unable to parse reference: %s", err) | ||||||
| 	} | 	} | ||||||
|  | 	k, err := libtrust.GenerateECP256PrivateKey() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	ctx := context.Background() | 	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 { | 	if err != nil { | ||||||
| 		t.Fatalf("error creating registry: %v", err) | 		t.Fatalf("error creating registry: %v", err) | ||||||
| 	} | 	} | ||||||
|  | @ -117,7 +116,7 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE | ||||||
| 		t.Fatalf(err.Error()) | 		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 { | 	if err != nil { | ||||||
| 		t.Fatalf("error creating registry: %v", err) | 		t.Fatalf("error creating registry: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -69,7 +69,7 @@ var GCCmd = &cobra.Command{ | ||||||
| 			os.Exit(1) | 			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 { | 		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) | ||||||
|  |  | ||||||
|  | @ -75,7 +75,6 @@ func (bs *blobStore) Put(ctx context.Context, mediaType string, p []byte) (distr | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// TODO(stevvooe): Write out mediatype here, as well.
 | 	// TODO(stevvooe): Write out mediatype here, as well.
 | ||||||
| 
 |  | ||||||
| 	return distribution.Descriptor{ | 	return distribution.Descriptor{ | ||||||
| 		Size: int64(len(p)), | 		Size: int64(len(p)), | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ import ( | ||||||
| 	"github.com/docker/distribution" | 	"github.com/docker/distribution" | ||||||
| 	"github.com/docker/distribution/context" | 	"github.com/docker/distribution/context" | ||||||
| 	"github.com/docker/distribution/digest" | 	"github.com/docker/distribution/digest" | ||||||
| 	"github.com/docker/distribution/manifest/schema1" |  | ||||||
| 	"github.com/docker/distribution/manifest/schema2" | 	"github.com/docker/distribution/manifest/schema2" | ||||||
| 	"github.com/docker/distribution/reference" | 	"github.com/docker/distribution/reference" | ||||||
| 	"github.com/docker/distribution/registry/storage/driver" | 	"github.com/docker/distribution/registry/storage/driver" | ||||||
|  | @ -71,22 +70,6 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			switch manifest.(type) { | 			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: | 			case *schema2.DeserializedManifest: | ||||||
| 				config := manifest.(*schema2.DeserializedManifest).Config | 				config := manifest.(*schema2.DeserializedManifest).Config | ||||||
| 				if dryRun { | 				if dryRun { | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ import ( | ||||||
| 	"github.com/docker/distribution/registry/storage/driver" | 	"github.com/docker/distribution/registry/storage/driver" | ||||||
| 	"github.com/docker/distribution/registry/storage/driver/inmemory" | 	"github.com/docker/distribution/registry/storage/driver/inmemory" | ||||||
| 	"github.com/docker/distribution/testutil" | 	"github.com/docker/distribution/testutil" | ||||||
|  | 	"github.com/docker/libtrust" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type image struct { | type image struct { | ||||||
|  | @ -22,7 +23,11 @@ type image struct { | ||||||
| 
 | 
 | ||||||
| func createRegistry(t *testing.T, driver driver.StorageDriver) distribution.Namespace { | func createRegistry(t *testing.T, driver driver.StorageDriver) distribution.Namespace { | ||||||
| 	ctx := context.Background() | 	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 { | 	if err != nil { | ||||||
| 		t.Fatalf("Failed to construct namespace") | 		t.Fatalf("Failed to construct namespace") | ||||||
| 	} | 	} | ||||||
|  | @ -139,13 +144,13 @@ func TestNoDeletionNoEffect(t *testing.T) { | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
| 	inmemoryDriver := inmemory.New() | 	inmemoryDriver := inmemory.New() | ||||||
| 
 | 
 | ||||||
| 	registry := createRegistry(t, inmemoryDriver) | 	registry := createRegistry(t, inmemory.New()) | ||||||
| 	repo := makeRepository(t, registry, "palailogos") | 	repo := makeRepository(t, registry, "palailogos") | ||||||
| 	manifestService, err := repo.Manifests(ctx) | 	manifestService, err := repo.Manifests(ctx) | ||||||
| 
 | 
 | ||||||
| 	image1 := uploadRandomSchema1Image(t, repo) | 	image1 := uploadRandomSchema1Image(t, repo) | ||||||
| 	image2 := uploadRandomSchema1Image(t, repo) | 	image2 := uploadRandomSchema1Image(t, repo) | ||||||
| 	image3 := uploadRandomSchema2Image(t, repo) | 	uploadRandomSchema2Image(t, repo) | ||||||
| 
 | 
 | ||||||
| 	// construct manifestlist for fun.
 | 	// construct manifestlist for fun.
 | ||||||
| 	blobstatter := registry.BlobStatter() | 	blobstatter := registry.BlobStatter() | ||||||
|  | @ -160,20 +165,17 @@ func TestNoDeletionNoEffect(t *testing.T) { | ||||||
| 		t.Fatalf("Failed to add manifest list: %v", err) | 		t.Fatalf("Failed to add manifest list: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	before := allBlobs(t, registry) | ||||||
|  | 
 | ||||||
| 	// Run GC
 | 	// Run GC
 | ||||||
| 	err = MarkAndSweep(context.Background(), inmemoryDriver, registry, false) | 	err = MarkAndSweep(context.Background(), inmemoryDriver, registry, false) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Failed mark and sweep: %v", err) | 		t.Fatalf("Failed mark and sweep: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	blobs := allBlobs(t, registry) | 	after := allBlobs(t, registry) | ||||||
| 
 | 	if len(before) != len(after) { | ||||||
| 	// the +1 at the end is for the manifestList
 | 		t.Fatalf("Garbage collection affected storage: %d != %d", len(before), len(after)) | ||||||
| 	// 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") |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ package storage | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"path" |  | ||||||
| 
 | 
 | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"github.com/docker/distribution" | 	"github.com/docker/distribution" | ||||||
|  | @ -12,7 +11,6 @@ import ( | ||||||
| 	"github.com/docker/distribution/manifest/manifestlist" | 	"github.com/docker/distribution/manifest/manifestlist" | ||||||
| 	"github.com/docker/distribution/manifest/schema1" | 	"github.com/docker/distribution/manifest/schema1" | ||||||
| 	"github.com/docker/distribution/manifest/schema2" | 	"github.com/docker/distribution/manifest/schema2" | ||||||
| 	"github.com/docker/distribution/registry/storage/driver" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // A ManifestHandler gets and puts manifests of a particular type.
 | // 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 | 	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) { | func TestManifestStorage(t *testing.T) { | ||||||
| 	testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestManifestStorageDisabledSignatures(t *testing.T) { |  | ||||||
| 	k, err := libtrust.GenerateECP256PrivateKey() | 	k, err := libtrust.GenerateECP256PrivateKey() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		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) { | func testManifestStorage(t *testing.T, options ...RegistryOption) { | ||||||
|  | @ -71,7 +67,6 @@ func testManifestStorage(t *testing.T, options ...RegistryOption) { | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 	equalSignatures := env.registry.(*registry).schema1SignaturesEnabled |  | ||||||
| 
 | 
 | ||||||
| 	m := schema1.Manifest{ | 	m := schema1.Manifest{ | ||||||
| 		Versioned: manifest.Versioned{ | 		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) | 		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() | 	_, pl, err := fetchedManifest.Payload() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("error getting payload %#v", err) | 		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) | 		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() | 	sigs, err := fetchedJWS.Signatures() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("unable to extract signatures: %v", err) | 		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) | 		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() | 	_, pl, err = fetched.Payload() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("error getting payload %#v", err) | 		t.Fatalf("error getting payload %#v", err) | ||||||
|  | @ -315,19 +287,6 @@ func testManifestStorage(t *testing.T, options ...RegistryOption) { | ||||||
| 		t.Fatalf("payloads are not equal") | 		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
 | 	// Test deleting manifests
 | ||||||
| 	err = ms.Delete(ctx, dgst) | 	err = ms.Delete(ctx, dgst) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  |  | ||||||
|  | @ -30,8 +30,6 @@ const ( | ||||||
| // 						revisions
 | // 						revisions
 | ||||||
| //							-> <manifest digest path>
 | //							-> <manifest digest path>
 | ||||||
| //								-> link
 | //								-> link
 | ||||||
| //								-> signatures
 |  | ||||||
| // 									<algorithm>/<digest>/link
 |  | ||||||
| // 						tags/<tag>
 | // 						tags/<tag>
 | ||||||
| //							-> current/link
 | //							-> current/link
 | ||||||
| // 							-> index
 | // 							-> index
 | ||||||
|  | @ -62,8 +60,7 @@ const ( | ||||||
| //
 | //
 | ||||||
| // The third component of the repository directory is the manifests store,
 | // 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
 | // 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
 | // the blob store and linked into the revision store.
 | ||||||
| // from the manifest payload data and linked into the blob store, as well.
 |  | ||||||
| // While the registry can save all revisions of a manifest, no relationship is
 | // 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
 | // 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
 | // support for name, tag lookups of manifests, using "current/link" under a
 | ||||||
|  | @ -77,8 +74,6 @@ const ( | ||||||
| // 	manifestRevisionsPathSpec:      <root>/v2/repositories/<name>/_manifests/revisions/
 | // 	manifestRevisionsPathSpec:      <root>/v2/repositories/<name>/_manifests/revisions/
 | ||||||
| // 	manifestRevisionPathSpec:      <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/
 | // 	manifestRevisionPathSpec:      <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/
 | ||||||
| // 	manifestRevisionLinkPathSpec:  <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/link
 | // 	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:
 | //	Tags:
 | ||||||
| //
 | //
 | ||||||
|  | @ -148,33 +143,6 @@ func pathFor(spec pathSpec) (string, error) { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return path.Join(root, "link"), nil | 		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: | 	case manifestTagsPathSpec: | ||||||
| 		return path.Join(append(repoPrefix, v.name, "_manifests", "tags")...), nil | 		return path.Join(append(repoPrefix, v.name, "_manifests", "tags")...), nil | ||||||
| 	case manifestTagPathSpec: | 	case manifestTagPathSpec: | ||||||
|  | @ -325,26 +293,6 @@ type manifestRevisionLinkPathSpec struct { | ||||||
| 
 | 
 | ||||||
| func (manifestRevisionLinkPathSpec) pathSpec() {} | 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
 | // manifestTagsPathSpec describes the path elements required to point to the
 | ||||||
| // manifest tags directory.
 | // manifest tags directory.
 | ||||||
| type manifestTagsPathSpec struct { | type manifestTagsPathSpec struct { | ||||||
|  |  | ||||||
|  | @ -26,21 +26,6 @@ func TestPathMapper(t *testing.T) { | ||||||
| 			}, | 			}, | ||||||
| 			expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/link", | 			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{ | 			spec: manifestTagsPathSpec{ | ||||||
| 				name: "foo/bar", | 				name: "foo/bar", | ||||||
|  | @ -113,7 +98,7 @@ func TestPathMapper(t *testing.T) { | ||||||
| 	// Add a few test cases to ensure we cover some errors
 | 	// Add a few test cases to ensure we cover some errors
 | ||||||
| 
 | 
 | ||||||
| 	// Specify a path that requires a revision and get a digest validation error.
 | 	// Specify a path that requires a revision and get a digest validation error.
 | ||||||
| 	badpath, err := pathFor(manifestSignaturesPathSpec{ | 	badpath, err := pathFor(manifestRevisionPathSpec{ | ||||||
| 		name: "foo/bar", | 		name: "foo/bar", | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,7 +18,6 @@ type registry struct { | ||||||
| 	blobDescriptorCacheProvider  cache.BlobDescriptorCacheProvider | 	blobDescriptorCacheProvider  cache.BlobDescriptorCacheProvider | ||||||
| 	deleteEnabled                bool | 	deleteEnabled                bool | ||||||
| 	resumableDigestEnabled       bool | 	resumableDigestEnabled       bool | ||||||
| 	schema1SignaturesEnabled     bool |  | ||||||
| 	schema1SigningKey            libtrust.PrivateKey | 	schema1SigningKey            libtrust.PrivateKey | ||||||
| 	blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory | 	blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory | ||||||
| } | } | ||||||
|  | @ -47,17 +46,8 @@ func DisableDigestResumption(registry *registry) error { | ||||||
| 	return nil | 	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
 | // Schema1SigningKey returns a functional option for NewRegistry. It sets the
 | ||||||
| // signing key for adding a signature to all schema1 manifests. This should be
 | // key for signing  all schema1 manifests.
 | ||||||
| // used in conjunction with disabling signature store.
 |  | ||||||
| func Schema1SigningKey(key libtrust.PrivateKey) RegistryOption { | func Schema1SigningKey(key libtrust.PrivateKey) RegistryOption { | ||||||
| 	return func(registry *registry) error { | 	return func(registry *registry) error { | ||||||
| 		registry.schema1SigningKey = key | 		registry.schema1SigningKey = key | ||||||
|  | @ -118,7 +108,6 @@ func NewRegistry(ctx context.Context, driver storagedriver.StorageDriver, option | ||||||
| 		}, | 		}, | ||||||
| 		statter:                statter, | 		statter:                statter, | ||||||
| 		resumableDigestEnabled: true, | 		resumableDigestEnabled: true, | ||||||
| 		schema1SignaturesEnabled: true, |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, option := range options { | 	for _, option := range options { | ||||||
|  | @ -231,11 +220,6 @@ func (repo *repository) Manifests(ctx context.Context, options ...distribution.M | ||||||
| 			ctx:        ctx, | 			ctx:        ctx, | ||||||
| 			repository: repo, | 			repository: repo, | ||||||
| 			blobStore:  blobStore, | 			blobStore:  blobStore, | ||||||
| 			signatures: &signatureStore{ |  | ||||||
| 				ctx:        ctx, |  | ||||||
| 				repository: repo, |  | ||||||
| 				blobStore:  repo.blobStore, |  | ||||||
| 			}, |  | ||||||
| 		}, | 		}, | ||||||
| 		schema2Handler: &schema2ManifestHandler{ | 		schema2Handler: &schema2ManifestHandler{ | ||||||
| 			ctx:        ctx, | 			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 | 	repository *repository | ||||||
| 	blobStore  *linkedBlobStore | 	blobStore  *linkedBlobStore | ||||||
| 	ctx        context.Context | 	ctx        context.Context | ||||||
| 	signatures *signatureStore |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var _ ManifestHandler = &signedManifestHandler{} | var _ ManifestHandler = &signedManifestHandler{} | ||||||
|  | @ -30,13 +29,6 @@ func (ms *signedManifestHandler) Unmarshal(ctx context.Context, dgst digest.Dige | ||||||
| 		signatures [][]byte | 		signatures [][]byte | ||||||
| 		err        error | 		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...) | 	jsig, err := libtrust.NewJSONSignature(content, signatures...) | ||||||
| 	if err != nil { | 	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 { | 		if err := jsig.Sign(ms.repository.schema1SigningKey); err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 	} else if !ms.repository.schema1SignaturesEnabled { |  | ||||||
| 		return nil, fmt.Errorf("missing signing key with signature store disabled") |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Extract the pretty JWS
 | 	// Extract the pretty JWS
 | ||||||
|  | @ -90,18 +80,6 @@ func (ms *signedManifestHandler) Put(ctx context.Context, manifest distribution. | ||||||
| 		return "", err | 		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 | 	return revision.Digest, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue