...

Source file src/github.com/docker/distribution/registry/proxy/proxymanifeststore_test.go

Documentation: github.com/docker/distribution/registry/proxy

     1  package proxy
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/distribution/reference"
    10  	"github.com/docker/distribution"
    11  	"github.com/docker/distribution/manifest"
    12  	"github.com/docker/distribution/manifest/schema1"
    13  	"github.com/docker/distribution/registry/client/auth"
    14  	"github.com/docker/distribution/registry/client/auth/challenge"
    15  	"github.com/docker/distribution/registry/proxy/scheduler"
    16  	"github.com/docker/distribution/registry/storage"
    17  	"github.com/docker/distribution/registry/storage/cache/memory"
    18  	"github.com/docker/distribution/registry/storage/driver/inmemory"
    19  	"github.com/docker/distribution/testutil"
    20  	"github.com/docker/libtrust"
    21  	"github.com/opencontainers/go-digest"
    22  )
    23  
    24  type statsManifest struct {
    25  	manifests distribution.ManifestService
    26  	stats     map[string]int
    27  }
    28  
    29  type manifestStoreTestEnv struct {
    30  	manifestDigest digest.Digest // digest of the signed manifest in the local storage
    31  	manifests      proxyManifestStore
    32  }
    33  
    34  func (te manifestStoreTestEnv) LocalStats() *map[string]int {
    35  	ls := te.manifests.localManifests.(statsManifest).stats
    36  	return &ls
    37  }
    38  
    39  func (te manifestStoreTestEnv) RemoteStats() *map[string]int {
    40  	rs := te.manifests.remoteManifests.(statsManifest).stats
    41  	return &rs
    42  }
    43  
    44  func (sm statsManifest) Delete(ctx context.Context, dgst digest.Digest) error {
    45  	sm.stats["delete"]++
    46  	return sm.manifests.Delete(ctx, dgst)
    47  }
    48  
    49  func (sm statsManifest) Exists(ctx context.Context, dgst digest.Digest) (bool, error) {
    50  	sm.stats["exists"]++
    51  	return sm.manifests.Exists(ctx, dgst)
    52  }
    53  
    54  func (sm statsManifest) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
    55  	sm.stats["get"]++
    56  	return sm.manifests.Get(ctx, dgst)
    57  }
    58  
    59  func (sm statsManifest) Put(ctx context.Context, manifest distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) {
    60  	sm.stats["put"]++
    61  	return sm.manifests.Put(ctx, manifest)
    62  }
    63  
    64  type mockChallenger struct {
    65  	sync.Mutex
    66  	count int
    67  }
    68  
    69  // Called for remote operations only
    70  func (m *mockChallenger) tryEstablishChallenges(context.Context) error {
    71  	m.Lock()
    72  	defer m.Unlock()
    73  	m.count++
    74  	return nil
    75  }
    76  
    77  func (m *mockChallenger) credentialStore() auth.CredentialStore {
    78  	return nil
    79  }
    80  
    81  func (m *mockChallenger) challengeManager() challenge.Manager {
    82  	return nil
    83  }
    84  
    85  func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv {
    86  	nameRef, err := reference.WithName(name)
    87  	if err != nil {
    88  		t.Fatalf("unable to parse reference: %s", err)
    89  	}
    90  	k, err := libtrust.GenerateECP256PrivateKey()
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	ctx := context.Background()
    96  	truthRegistry, err := storage.NewRegistry(ctx, inmemory.New(),
    97  		storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()),
    98  		storage.Schema1SigningKey(k),
    99  		storage.EnableSchema1)
   100  	if err != nil {
   101  		t.Fatalf("error creating registry: %v", err)
   102  	}
   103  	truthRepo, err := truthRegistry.Repository(ctx, nameRef)
   104  	if err != nil {
   105  		t.Fatalf("unexpected error getting repo: %v", err)
   106  	}
   107  	tr, err := truthRepo.Manifests(ctx)
   108  	if err != nil {
   109  		t.Fatal(err.Error())
   110  	}
   111  	truthManifests := statsManifest{
   112  		manifests: tr,
   113  		stats:     make(map[string]int),
   114  	}
   115  
   116  	manifestDigest, err := populateRepo(ctx, t, truthRepo, name, tag)
   117  	if err != nil {
   118  		t.Fatalf(err.Error())
   119  	}
   120  
   121  	localRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableRedirect, storage.DisableDigestResumption, storage.Schema1SigningKey(k), storage.EnableSchema1)
   122  	if err != nil {
   123  		t.Fatalf("error creating registry: %v", err)
   124  	}
   125  	localRepo, err := localRegistry.Repository(ctx, nameRef)
   126  	if err != nil {
   127  		t.Fatalf("unexpected error getting repo: %v", err)
   128  	}
   129  	lr, err := localRepo.Manifests(ctx)
   130  	if err != nil {
   131  		t.Fatal(err.Error())
   132  	}
   133  
   134  	localManifests := statsManifest{
   135  		manifests: lr,
   136  		stats:     make(map[string]int),
   137  	}
   138  
   139  	s := scheduler.New(ctx, inmemory.New(), "/scheduler-state.json")
   140  	return &manifestStoreTestEnv{
   141  		manifestDigest: manifestDigest,
   142  		manifests: proxyManifestStore{
   143  			ctx:             ctx,
   144  			localManifests:  localManifests,
   145  			remoteManifests: truthManifests,
   146  			scheduler:       s,
   147  			repositoryName:  nameRef,
   148  			authChallenger:  &mockChallenger{},
   149  		},
   150  	}
   151  }
   152  
   153  func populateRepo(ctx context.Context, t *testing.T, repository distribution.Repository, name, tag string) (digest.Digest, error) {
   154  	m := schema1.Manifest{
   155  		Versioned: manifest.Versioned{
   156  			SchemaVersion: 1,
   157  		},
   158  		Name: name,
   159  		Tag:  tag,
   160  	}
   161  
   162  	for i := 0; i < 2; i++ {
   163  		wr, err := repository.Blobs(ctx).Create(ctx)
   164  		if err != nil {
   165  			t.Fatalf("unexpected error creating test upload: %v", err)
   166  		}
   167  
   168  		rs, dgst, err := testutil.CreateRandomTarFile()
   169  		if err != nil {
   170  			t.Fatalf("unexpected error generating test layer file")
   171  		}
   172  		if _, err := io.Copy(wr, rs); err != nil {
   173  			t.Fatalf("unexpected error copying to upload: %v", err)
   174  		}
   175  
   176  		if _, err := wr.Commit(ctx, distribution.Descriptor{Digest: dgst}); err != nil {
   177  			t.Fatalf("unexpected error finishing upload: %v", err)
   178  		}
   179  	}
   180  
   181  	pk, err := libtrust.GenerateECP256PrivateKey()
   182  	if err != nil {
   183  		t.Fatalf("unexpected error generating private key: %v", err)
   184  	}
   185  
   186  	sm, err := schema1.Sign(&m, pk)
   187  	if err != nil {
   188  		t.Fatalf("error signing manifest: %v", err)
   189  	}
   190  
   191  	ms, err := repository.Manifests(ctx)
   192  	if err != nil {
   193  		t.Fatalf(err.Error())
   194  	}
   195  	dgst, err := ms.Put(ctx, sm)
   196  	if err != nil {
   197  		t.Fatalf("unexpected errors putting manifest: %v", err)
   198  	}
   199  
   200  	return dgst, nil
   201  }
   202  
   203  // TestProxyManifests contains basic acceptance tests
   204  // for the pull-through behavior
   205  func TestProxyManifests(t *testing.T) {
   206  	name := "foo/bar"
   207  	env := newManifestStoreTestEnv(t, name, "latest")
   208  
   209  	localStats := env.LocalStats()
   210  	remoteStats := env.RemoteStats()
   211  
   212  	ctx := context.Background()
   213  	// Stat - must check local and remote
   214  	exists, err := env.manifests.Exists(ctx, env.manifestDigest)
   215  	if err != nil {
   216  		t.Fatalf("Error checking existence")
   217  	}
   218  	if !exists {
   219  		t.Errorf("Unexpected non-existent manifest")
   220  	}
   221  
   222  	if (*localStats)["exists"] != 1 && (*remoteStats)["exists"] != 1 {
   223  		t.Errorf("Unexpected exists count : \n%v \n%v", localStats, remoteStats)
   224  	}
   225  
   226  	if env.manifests.authChallenger.(*mockChallenger).count != 1 {
   227  		t.Fatalf("Expected 1 auth challenge, got %#v", env.manifests.authChallenger)
   228  	}
   229  
   230  	// Get - should succeed and pull manifest into local
   231  	_, err = env.manifests.Get(ctx, env.manifestDigest)
   232  	if err != nil {
   233  		t.Fatal(err)
   234  	}
   235  
   236  	if (*localStats)["get"] != 1 && (*remoteStats)["get"] != 1 {
   237  		t.Errorf("Unexpected get count")
   238  	}
   239  
   240  	if (*localStats)["put"] != 1 {
   241  		t.Errorf("Expected local put")
   242  	}
   243  
   244  	if env.manifests.authChallenger.(*mockChallenger).count != 2 {
   245  		t.Fatalf("Expected 2 auth challenges, got %#v", env.manifests.authChallenger)
   246  	}
   247  
   248  	// Stat - should only go to local
   249  	exists, err = env.manifests.Exists(ctx, env.manifestDigest)
   250  	if err != nil {
   251  		t.Fatal(err)
   252  	}
   253  	if !exists {
   254  		t.Errorf("Unexpected non-existent manifest")
   255  	}
   256  
   257  	if (*localStats)["exists"] != 2 && (*remoteStats)["exists"] != 1 {
   258  		t.Errorf("Unexpected exists count")
   259  	}
   260  
   261  	if env.manifests.authChallenger.(*mockChallenger).count != 2 {
   262  		t.Fatalf("Expected 2 auth challenges, got %#v", env.manifests.authChallenger)
   263  	}
   264  
   265  	// Get proxied - won't require another authchallenge
   266  	_, err = env.manifests.Get(ctx, env.manifestDigest)
   267  	if err != nil {
   268  		t.Fatal(err)
   269  	}
   270  
   271  	if env.manifests.authChallenger.(*mockChallenger).count != 2 {
   272  		t.Fatalf("Expected 2 auth challenges, got %#v", env.manifests.authChallenger)
   273  	}
   274  
   275  }
   276  

View as plain text