forked from TrueCloudLab/distribution
To avoid any network use unless necessary, delay establishing authorization
challenges with the upstream until any proxied data is found not to be local. Implement auth challenges behind an interface and add to unit tests. Also, remove a non-sensical unit test. Signed-off-by: Richard Scothern <richard.scothern@docker.com>
This commit is contained in:
parent
881ef1096f
commit
7d16fee7a4
8 changed files with 156 additions and 44 deletions
|
@ -8,6 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const tokenURL = "https://auth.docker.io/token"
|
const tokenURL = "https://auth.docker.io/token"
|
||||||
|
const challengeHeader = "Docker-Distribution-Api-Version"
|
||||||
|
|
||||||
type userpass struct {
|
type userpass struct {
|
||||||
username string
|
username string
|
||||||
|
@ -24,12 +25,8 @@ func (c credentials) Basic(u *url.URL) (string, string) {
|
||||||
return up.username, up.password
|
return up.username, up.password
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureAuth authorizes with the upstream registry
|
// ConfigureAuth stores credentials for challenge responses
|
||||||
func ConfigureAuth(remoteURL, username, password string, cm auth.ChallengeManager) (auth.CredentialStore, error) {
|
func configureAuth(username, password string) (auth.CredentialStore, error) {
|
||||||
if err := ping(cm, remoteURL+"/v2/", "Docker-Distribution-Api-Version"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
creds := map[string]userpass{
|
creds := map[string]userpass{
|
||||||
tokenURL: {
|
tokenURL: {
|
||||||
username: username,
|
username: username,
|
||||||
|
|
|
@ -22,6 +22,7 @@ type proxyBlobStore struct {
|
||||||
remoteStore distribution.BlobService
|
remoteStore distribution.BlobService
|
||||||
scheduler *scheduler.TTLExpirationScheduler
|
scheduler *scheduler.TTLExpirationScheduler
|
||||||
repositoryName reference.Named
|
repositoryName reference.Named
|
||||||
|
authChallenger authChallenger
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ distribution.BlobStore = &proxyBlobStore{}
|
var _ distribution.BlobStore = &proxyBlobStore{}
|
||||||
|
@ -121,6 +122,10 @@ func (pbs *proxyBlobStore) ServeBlob(ctx context.Context, w http.ResponseWriter,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := pbs.authChallenger.tryEstablishChallenges(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
_, ok := inflight[dgst]
|
_, ok := inflight[dgst]
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -162,6 +167,10 @@ func (pbs *proxyBlobStore) Stat(ctx context.Context, dgst digest.Digest) (distri
|
||||||
return distribution.Descriptor{}, err
|
return distribution.Descriptor{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := pbs.authChallenger.tryEstablishChallenges(ctx); err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
return pbs.remoteStore.Stat(ctx, dgst)
|
return pbs.remoteStore.Stat(ctx, dgst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,6 +168,7 @@ func makeTestEnv(t *testing.T, name string) *testEnv {
|
||||||
remoteStore: truthBlobs,
|
remoteStore: truthBlobs,
|
||||||
localStore: localBlobs,
|
localStore: localBlobs,
|
||||||
scheduler: s,
|
scheduler: s,
|
||||||
|
authChallenger: &mockChallenger{},
|
||||||
}
|
}
|
||||||
|
|
||||||
te := &testEnv{
|
te := &testEnv{
|
||||||
|
@ -242,6 +243,11 @@ func TestProxyStoreStat(t *testing.T) {
|
||||||
if (*remoteStats)["stat"] != remoteBlobCount {
|
if (*remoteStats)["stat"] != remoteBlobCount {
|
||||||
t.Errorf("Unexpected remote stat count")
|
t.Errorf("Unexpected remote stat count")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if te.store.authChallenger.(*mockChallenger).count != len(te.inRemote) {
|
||||||
|
t.Fatalf("Unexpected auth challenge count, got %#v", te.store.authChallenger)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyStoreServeHighConcurrency(t *testing.T) {
|
func TestProxyStoreServeHighConcurrency(t *testing.T) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ type proxyManifestStore struct {
|
||||||
remoteManifests distribution.ManifestService
|
remoteManifests distribution.ManifestService
|
||||||
repositoryName reference.Named
|
repositoryName reference.Named
|
||||||
scheduler *scheduler.TTLExpirationScheduler
|
scheduler *scheduler.TTLExpirationScheduler
|
||||||
|
authChallenger authChallenger
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ distribution.ManifestService = &proxyManifestStore{}
|
var _ distribution.ManifestService = &proxyManifestStore{}
|
||||||
|
@ -31,7 +32,9 @@ func (pms proxyManifestStore) Exists(ctx context.Context, dgst digest.Digest) (b
|
||||||
if exists {
|
if exists {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
if err := pms.authChallenger.tryEstablishChallenges(ctx); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
return pms.remoteManifests.Exists(ctx, dgst)
|
return pms.remoteManifests.Exists(ctx, dgst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +44,10 @@ func (pms proxyManifestStore) Get(ctx context.Context, dgst digest.Digest, optio
|
||||||
var fromRemote bool
|
var fromRemote bool
|
||||||
manifest, err := pms.localManifests.Get(ctx, dgst, options...)
|
manifest, err := pms.localManifests.Get(ctx, dgst, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err := pms.authChallenger.tryEstablishChallenges(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
manifest, err = pms.remoteManifests.Get(ctx, dgst, options...)
|
manifest, err = pms.remoteManifests.Get(ctx, dgst, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -2,6 +2,7 @@ package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
|
@ -64,6 +65,20 @@ func (sm statsManifest) Put(ctx context.Context, manifest distribution.Manifest,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
type mockChallenger struct {
|
||||||
|
sync.Mutex
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called for remote operations only
|
||||||
|
func (mc *mockChallenger) tryEstablishChallenges(context.Context) error {
|
||||||
|
mc.Lock()
|
||||||
|
defer mc.Unlock()
|
||||||
|
|
||||||
|
mc.count++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv {
|
func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv {
|
||||||
nameRef, err := reference.ParseNamed(name)
|
nameRef, err := reference.ParseNamed(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -120,6 +135,7 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE
|
||||||
remoteManifests: truthManifests,
|
remoteManifests: truthManifests,
|
||||||
scheduler: s,
|
scheduler: s,
|
||||||
repositoryName: nameRef,
|
repositoryName: nameRef,
|
||||||
|
authChallenger: &mockChallenger{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,6 +214,10 @@ func TestProxyManifests(t *testing.T) {
|
||||||
t.Errorf("Unexpected exists count : \n%v \n%v", localStats, remoteStats)
|
t.Errorf("Unexpected exists count : \n%v \n%v", localStats, remoteStats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if env.manifests.authChallenger.(*mockChallenger).count != 1 {
|
||||||
|
t.Fatalf("Expected 1 auth challenge, got %#v", env.manifests.authChallenger)
|
||||||
|
}
|
||||||
|
|
||||||
// Get - should succeed and pull manifest into local
|
// Get - should succeed and pull manifest into local
|
||||||
_, err = env.manifests.Get(ctx, env.manifestDigest)
|
_, err = env.manifests.Get(ctx, env.manifestDigest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -212,6 +232,10 @@ func TestProxyManifests(t *testing.T) {
|
||||||
t.Errorf("Expected local put")
|
t.Errorf("Expected local put")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if env.manifests.authChallenger.(*mockChallenger).count != 2 {
|
||||||
|
t.Fatalf("Expected 2 auth challenges, got %#v", env.manifests.authChallenger)
|
||||||
|
}
|
||||||
|
|
||||||
// Stat - should only go to local
|
// Stat - should only go to local
|
||||||
exists, err = env.manifests.Exists(ctx, env.manifestDigest)
|
exists, err = env.manifests.Exists(ctx, env.manifestDigest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -225,17 +249,18 @@ func TestProxyManifests(t *testing.T) {
|
||||||
t.Errorf("Unexpected exists count")
|
t.Errorf("Unexpected exists count")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get - should get from remote, to test freshness
|
if env.manifests.authChallenger.(*mockChallenger).count != 2 {
|
||||||
|
t.Fatalf("Expected 2 auth challenges, got %#v", env.manifests.authChallenger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get proxied - won't require another authchallenge
|
||||||
_, err = env.manifests.Get(ctx, env.manifestDigest)
|
_, err = env.manifests.Get(ctx, env.manifestDigest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*remoteStats)["get"] != 2 && (*remoteStats)["exists"] != 1 && (*localStats)["put"] != 1 {
|
if env.manifests.authChallenger.(*mockChallenger).count != 2 {
|
||||||
t.Errorf("Unexpected get count")
|
t.Fatalf("Expected 2 auth challenges, got %#v", env.manifests.authChallenger)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyTagService(t *testing.T) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/configuration"
|
"github.com/docker/distribution/configuration"
|
||||||
"github.com/docker/distribution/context"
|
"github.com/docker/distribution/context"
|
||||||
|
@ -20,12 +21,9 @@ import (
|
||||||
// proxyingRegistry fetches content from a remote registry and caches it locally
|
// proxyingRegistry fetches content from a remote registry and caches it locally
|
||||||
type proxyingRegistry struct {
|
type proxyingRegistry struct {
|
||||||
embedded distribution.Namespace // provides local registry functionality
|
embedded distribution.Namespace // provides local registry functionality
|
||||||
|
|
||||||
scheduler *scheduler.TTLExpirationScheduler
|
scheduler *scheduler.TTLExpirationScheduler
|
||||||
|
|
||||||
remoteURL string
|
remoteURL string
|
||||||
credentialStore auth.CredentialStore
|
authChallenger authChallenger
|
||||||
challengeManager auth.ChallengeManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRegistryPullThroughCache creates a registry acting as a pull through cache
|
// NewRegistryPullThroughCache creates a registry acting as a pull through cache
|
||||||
|
@ -93,8 +91,7 @@ func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Name
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeManager := auth.NewSimpleChallengeManager()
|
cs, err := configureAuth(config.Username, config.Password)
|
||||||
cs, err := ConfigureAuth(config.RemoteURL, config.Username, config.Password, challengeManager)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -102,9 +99,12 @@ func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Name
|
||||||
return &proxyingRegistry{
|
return &proxyingRegistry{
|
||||||
embedded: registry,
|
embedded: registry,
|
||||||
scheduler: s,
|
scheduler: s,
|
||||||
challengeManager: challengeManager,
|
|
||||||
credentialStore: cs,
|
|
||||||
remoteURL: config.RemoteURL,
|
remoteURL: config.RemoteURL,
|
||||||
|
authChallenger: &remoteAuthChallenger{
|
||||||
|
remoteURL: config.RemoteURL,
|
||||||
|
challengeManager: auth.NewSimpleChallengeManager(),
|
||||||
|
credentialStore: cs,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +117,13 @@ func (pr *proxyingRegistry) Repositories(ctx context.Context, repos []string, la
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named) (distribution.Repository, error) {
|
func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named) (distribution.Repository, error) {
|
||||||
|
hcm, ok := pr.authChallenger.(*remoteAuthChallenger)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected challenge manager type %T", pr.authChallenger)
|
||||||
|
}
|
||||||
|
|
||||||
tr := transport.NewTransport(http.DefaultTransport,
|
tr := transport.NewTransport(http.DefaultTransport,
|
||||||
auth.NewAuthorizer(pr.challengeManager, auth.NewTokenHandler(http.DefaultTransport, pr.credentialStore, name.Name(), "pull")))
|
auth.NewAuthorizer(hcm.challengeManager, auth.NewTokenHandler(http.DefaultTransport, hcm.credentialStore, name.Name(), "pull")))
|
||||||
|
|
||||||
localRepo, err := pr.embedded.Repository(ctx, name)
|
localRepo, err := pr.embedded.Repository(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -145,6 +150,7 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named
|
||||||
remoteStore: remoteRepo.Blobs(ctx),
|
remoteStore: remoteRepo.Blobs(ctx),
|
||||||
scheduler: pr.scheduler,
|
scheduler: pr.scheduler,
|
||||||
repositoryName: name,
|
repositoryName: name,
|
||||||
|
authChallenger: pr.authChallenger,
|
||||||
},
|
},
|
||||||
manifests: &proxyManifestStore{
|
manifests: &proxyManifestStore{
|
||||||
repositoryName: name,
|
repositoryName: name,
|
||||||
|
@ -152,15 +158,53 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named
|
||||||
remoteManifests: remoteManifests,
|
remoteManifests: remoteManifests,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
scheduler: pr.scheduler,
|
scheduler: pr.scheduler,
|
||||||
|
authChallenger: pr.authChallenger,
|
||||||
},
|
},
|
||||||
name: name,
|
name: name,
|
||||||
tags: &proxyTagService{
|
tags: &proxyTagService{
|
||||||
localTags: localRepo.Tags(ctx),
|
localTags: localRepo.Tags(ctx),
|
||||||
remoteTags: remoteRepo.Tags(ctx),
|
remoteTags: remoteRepo.Tags(ctx),
|
||||||
|
authChallenger: pr.authChallenger,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// authChallenger encapsulates a request to the upstream to establish credential challenges
|
||||||
|
type authChallenger interface {
|
||||||
|
tryEstablishChallenges(context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type remoteAuthChallenger struct {
|
||||||
|
remoteURL string
|
||||||
|
sync.Mutex
|
||||||
|
challengeManager auth.ChallengeManager
|
||||||
|
credentialStore auth.CredentialStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryEstablishChallenges will attempt to get a challenge types for the upstream if none currently exist
|
||||||
|
func (hcm *remoteAuthChallenger) tryEstablishChallenges(ctx context.Context) error {
|
||||||
|
hcm.Lock()
|
||||||
|
defer hcm.Unlock()
|
||||||
|
|
||||||
|
remoteURL := hcm.remoteURL + "/v2/"
|
||||||
|
challenges, err := hcm.challengeManager.GetChallenges(remoteURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(challenges) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// establish challenge type with upstream
|
||||||
|
if err := ping(hcm.challengeManager, remoteURL, challengeHeader); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
context.GetLogger(ctx).Infof("Challenge established with upstream : %s %s", remoteURL, hcm.challengeManager)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// proxiedRepository uses proxying blob and manifest services to serve content
|
// proxiedRepository uses proxying blob and manifest services to serve content
|
||||||
// locally, or pulling it through from a remote and caching it locally if it doesn't
|
// locally, or pulling it through from a remote and caching it locally if it doesn't
|
||||||
// already exist
|
// already exist
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type proxyTagService struct {
|
type proxyTagService struct {
|
||||||
localTags distribution.TagService
|
localTags distribution.TagService
|
||||||
remoteTags distribution.TagService
|
remoteTags distribution.TagService
|
||||||
|
authChallenger authChallenger
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ distribution.TagService = proxyTagService{}
|
var _ distribution.TagService = proxyTagService{}
|
||||||
|
@ -17,6 +18,8 @@ var _ distribution.TagService = proxyTagService{}
|
||||||
// tag service first and then caching it locally. If the remote is unavailable
|
// tag service first and then caching it locally. If the remote is unavailable
|
||||||
// the local association is returned
|
// the local association is returned
|
||||||
func (pt proxyTagService) Get(ctx context.Context, tag string) (distribution.Descriptor, error) {
|
func (pt proxyTagService) Get(ctx context.Context, tag string) (distribution.Descriptor, error) {
|
||||||
|
err := pt.authChallenger.tryEstablishChallenges(ctx)
|
||||||
|
if err == nil {
|
||||||
desc, err := pt.remoteTags.Get(ctx, tag)
|
desc, err := pt.remoteTags.Get(ctx, tag)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err := pt.localTags.Tag(ctx, tag, desc)
|
err := pt.localTags.Tag(ctx, tag, desc)
|
||||||
|
@ -25,8 +28,9 @@ func (pt proxyTagService) Get(ctx context.Context, tag string) (distribution.Des
|
||||||
}
|
}
|
||||||
return desc, nil
|
return desc, nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
desc, err = pt.localTags.Get(ctx, tag)
|
desc, err := pt.localTags.Get(ctx, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return distribution.Descriptor{}, err
|
return distribution.Descriptor{}, err
|
||||||
}
|
}
|
||||||
|
@ -46,10 +50,13 @@ func (pt proxyTagService) Untag(ctx context.Context, tag string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt proxyTagService) All(ctx context.Context) ([]string, error) {
|
func (pt proxyTagService) All(ctx context.Context) ([]string, error) {
|
||||||
|
err := pt.authChallenger.tryEstablishChallenges(ctx)
|
||||||
|
if err == nil {
|
||||||
tags, err := pt.remoteTags.All(ctx)
|
tags, err := pt.remoteTags.All(ctx)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return tags, err
|
return tags, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return pt.localTags.All(ctx)
|
return pt.localTags.All(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ func testProxyTagService(local, remote map[string]distribution.Descriptor) *prox
|
||||||
return &proxyTagService{
|
return &proxyTagService{
|
||||||
localTags: &mockTagStore{mapping: local},
|
localTags: &mockTagStore{mapping: local},
|
||||||
remoteTags: &mockTagStore{mapping: remote},
|
remoteTags: &mockTagStore{mapping: remote},
|
||||||
|
authChallenger: &mockChallenger{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +88,10 @@ func TestGet(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if proxyTags.authChallenger.(*mockChallenger).count != 1 {
|
||||||
|
t.Fatalf("Expected 1 auth challenge call, got %#v", proxyTags.authChallenger)
|
||||||
|
}
|
||||||
|
|
||||||
if d != remoteDesc {
|
if d != remoteDesc {
|
||||||
t.Fatal("unable to get put tag")
|
t.Fatal("unable to get put tag")
|
||||||
}
|
}
|
||||||
|
@ -112,6 +117,10 @@ func TestGet(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if proxyTags.authChallenger.(*mockChallenger).count != 2 {
|
||||||
|
t.Fatalf("Expected 2 auth challenge calls, got %#v", proxyTags.authChallenger)
|
||||||
|
}
|
||||||
|
|
||||||
if d != newRemoteDesc {
|
if d != newRemoteDesc {
|
||||||
t.Fatal("unable to get put tag")
|
t.Fatal("unable to get put tag")
|
||||||
}
|
}
|
||||||
|
@ -142,7 +151,11 @@ func TestGet(t *testing.T) {
|
||||||
t.Fatal("untagged tag should be pulled through")
|
t.Fatal("untagged tag should be pulled through")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add another tag. Ensure both tags appear in enumerate
|
if proxyTags.authChallenger.(*mockChallenger).count != 3 {
|
||||||
|
t.Fatalf("Expected 3 auth challenge calls, got %#v", proxyTags.authChallenger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add another tag. Ensure both tags appear in 'All'
|
||||||
err = proxyTags.remoteTags.Tag(ctx, "funtag", distribution.Descriptor{Size: 42})
|
err = proxyTags.remoteTags.Tag(ctx, "funtag", distribution.Descriptor{Size: 42})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -161,4 +174,8 @@ func TestGet(t *testing.T) {
|
||||||
if all[0] != "funtag" && all[1] != "remote" {
|
if all[0] != "funtag" && all[1] != "remote" {
|
||||||
t.Fatalf("Unexpected tags returned from All() : %v ", all)
|
t.Fatalf("Unexpected tags returned from All() : %v ", all)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if proxyTags.authChallenger.(*mockChallenger).count != 4 {
|
||||||
|
t.Fatalf("Expected 4 auth challenge calls, got %#v", proxyTags.authChallenger)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue