Hello community,

here is the log from the commit of package docker-distribution for 
openSUSE:Factory checked in at 2016-03-18 21:42:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/docker-distribution (Old)
 and      /work/SRC/openSUSE:Factory/.docker-distribution.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "docker-distribution"

Changes:
--------
--- /work/SRC/openSUSE:Factory/docker-distribution/docker-distribution.changes  
2016-02-17 12:10:41.000000000 +0100
+++ 
/work/SRC/openSUSE:Factory/.docker-distribution.new/docker-distribution.changes 
    2016-03-18 21:42:39.000000000 +0100
@@ -1,0 +2,12 @@
+Thu Mar 10 11:14:26 UTC 2016 - [email protected]
+
+- Ugraded to 2.3.1. The changelog is as follows:
+
+- Allow uppercase characters in hostnames 
(https://github.com/docker/distribution/commit/34c3acf8a8d800524ac06444290b26ed96d4dac0)
+- Fix schema1 manifest etag and docker content digest header 
(https://github.com/docker/distribution/commit/d7eb5d118a6a14a6f320062296b1808506c35241)
+- Add option to disable signatures 
(https://github.com/docker/distribution/commit/d1c173078fe47f45c7f7b96098410d4f13dd68ab)
+- To avoid any network use unless necessary, delay establishing authorization 
(https://github.com/docker/distribution/commit/740ed699f40e1522faacb2a706e44fa1560af8ea)
+- Extend authChallenger interface to remove type cast.  
(https://github.com/docker/distribution/commit/16445b67679e91eab408a40a34625aa1f4dabfd1)
+- Enable proxying registries to downgrade fetched manifests to Schema 1.  
(https://github.com/docker/distribution/commit/36936218c2e6a4527fc99a5c04126bb1f4c7d55c)
+
+-------------------------------------------------------------------

Old:
----
  v2.3.0.tar.gz

New:
----
  v2.3.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ docker-distribution.spec ++++++
--- /var/tmp/diff_new_pack.YRbZye/_old  2016-03-18 21:42:40.000000000 +0100
+++ /var/tmp/diff_new_pack.YRbZye/_new  2016-03-18 21:42:40.000000000 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           docker-distribution
-Version:        2.3.0
+Version:        2.3.1
 Release:        0
 Summary:        The Docker toolset to pack, ship, store, and deliver content
 License:        Apache-2.0

++++++ v2.3.0.tar.gz -> v2.3.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/configuration/configuration.go 
new/distribution-2.3.1/configuration/configuration.go
--- old/distribution-2.3.0/configuration/configuration.go       2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/configuration/configuration.go       2016-02-23 
23:39:19.000000000 +0100
@@ -145,6 +145,21 @@
        Health Health `yaml:"health,omitempty"`
 
        Proxy Proxy `yaml:"proxy,omitempty"`
+
+       // Compatibility is used for configurations of working with older or 
deprecated features.
+       Compatibility struct {
+               // Schema1 configures how schema1 manifests will be handled
+               Schema1 struct {
+                       // TrustKey is the signing key to use for adding the 
signature to
+                       // schema1 manifests.
+                       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:"compatibility,omitempty"`
 }
 
 // LogHook is composed of hook Level and Type.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/manifest/schema1/manifest.go 
new/distribution-2.3.1/manifest/schema1/manifest.go
--- old/distribution-2.3.0/manifest/schema1/manifest.go 2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/manifest/schema1/manifest.go 2016-02-23 
23:39:19.000000000 +0100
@@ -102,7 +102,7 @@
        Canonical []byte `json:"-"`
 
        // all contains the byte representation of the Manifest including 
signatures
-       // and is retuend by Payload()
+       // and is returned by Payload()
        all []byte
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/reference/reference.go 
new/distribution-2.3.1/reference/reference.go
--- old/distribution-2.3.0/reference/reference.go       2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/reference/reference.go       2016-02-23 
23:39:19.000000000 +0100
@@ -6,7 +6,7 @@
 //     reference                       := repository [ ":" tag ] [ "@" digest ]
 //     name                            := [hostname '/'] component ['/' 
component]*
 //     hostname                        := hostcomponent ['.' hostcomponent]* 
[':' port-number]
-//     hostcomponent                   := 
/([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])/
+//     hostcomponent                   := 
/([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
 //     port-number                     := /[0-9]+/
 //     component                       := alpha-numeric [separator 
alpha-numeric]*
 //     alpha-numeric                   := /[a-z0-9]+/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/reference/regexp.go 
new/distribution-2.3.1/reference/regexp.go
--- old/distribution-2.3.0/reference/regexp.go  2016-02-04 20:11:33.000000000 
+0100
+++ new/distribution-2.3.1/reference/regexp.go  2016-02-23 23:39:19.000000000 
+0100
@@ -22,7 +22,7 @@
        // hostnameComponentRegexp restricts the registry hostname component of 
a
        // repository name to start with a component as defined by 
hostnameRegexp
        // and followed by an optional port.
-       hostnameComponentRegexp = 
match(`(?:[a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])`)
+       hostnameComponentRegexp = 
match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
 
        // hostnameRegexp defines the structure of potential hostname components
        // that may be part of image names. This is purposely a subset of what 
is
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/reference/regexp_test.go 
new/distribution-2.3.1/reference/regexp_test.go
--- old/distribution-2.3.0/reference/regexp_test.go     2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/reference/regexp_test.go     2016-02-23 
23:39:19.000000000 +0100
@@ -111,6 +111,10 @@
                        input: "xn--n3h.com", // ☃.com in punycode
                        match: true,
                },
+               {
+                       input: "Asdf.com", // uppercase character
+                       match: true,
+               },
        }
        r := regexp.MustCompile(`^` + hostnameRegexp.String() + `$`)
        for i := range hostcases {
@@ -399,6 +403,14 @@
                        match: true,
                        subs:  []string{"registry.io", 
"foo/project--id.module--name.ver---sion--name"},
                },
+               {
+                       input: "Asdf.com/foo/bar", // uppercase character in 
hostname
+                       match: true,
+               },
+               {
+                       input: "Foo/FarB", // uppercase characters in remote 
name
+                       match: false,
+               },
        }
        for i := range testcases {
                checkRegexp(t, anchoredNameRegexp, testcases[i])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/registry/client/repository.go 
new/distribution-2.3.1/registry/client/repository.go
--- old/distribution-2.3.0/registry/client/repository.go        2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/client/repository.go        2016-02-23 
23:39:19.000000000 +0100
@@ -257,9 +257,18 @@
        if err != nil {
                return distribution.Descriptor{}, err
        }
-       var attempts int
-       resp, err := t.client.Head(u)
 
+       req, err := http.NewRequest("HEAD", u, nil)
+       if err != nil {
+               return distribution.Descriptor{}, err
+       }
+
+       for _, t := range distribution.ManifestMediaTypes() {
+               req.Header.Add("Accept", t)
+       }
+
+       var attempts int
+       resp, err := t.client.Do(req)
 check:
        if err != nil {
                return distribution.Descriptor{}, err
@@ -269,7 +278,16 @@
        case resp.StatusCode >= 200 && resp.StatusCode < 400:
                return descriptorFromResponse(resp)
        case resp.StatusCode == http.StatusMethodNotAllowed:
-               resp, err = t.client.Get(u)
+               req, err = http.NewRequest("GET", u, nil)
+               if err != nil {
+                       return distribution.Descriptor{}, err
+               }
+
+               for _, t := range distribution.ManifestMediaTypes() {
+                       req.Header.Add("Accept", t)
+               }
+
+               resp, err = t.client.Do(req)
                attempts++
                if attempts > 1 {
                        return distribution.Descriptor{}, err
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/registry/handlers/api_test.go 
new/distribution-2.3.1/registry/handlers/api_test.go
--- old/distribution-2.3.0/registry/handlers/api_test.go        2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/handlers/api_test.go        2016-02-23 
23:39:19.000000000 +0100
@@ -1378,19 +1378,28 @@
        }
        defer resp.Body.Close()
 
+       manifestBytes, err := ioutil.ReadAll(resp.Body)
+       if err != nil {
+               t.Fatalf("error reading response body: %v", err)
+       }
+
        checkResponse(t, "fetching uploaded manifest as schema1", resp, 
http.StatusOK)
-       checkHeaders(t, resp, http.Header{
-               "Docker-Content-Digest": []string{dgst.String()},
-               "ETag":                  []string{fmt.Sprintf(`"%s"`, dgst)},
-       })
 
-       var fetchedSchema1Manifest schema1.SignedManifest
-       dec = json.NewDecoder(resp.Body)
+       m, desc, err := 
distribution.UnmarshalManifest(schema1.MediaTypeManifest, manifestBytes)
+       if err != nil {
+               t.Fatalf("unexpected error unmarshalling manifest: %v", err)
+       }
 
-       if err := dec.Decode(&fetchedSchema1Manifest); err != nil {
-               t.Fatalf("error decoding fetched schema1 manifest: %v", err)
+       fetchedSchema1Manifest, ok := m.(*schema1.SignedManifest)
+       if !ok {
+               t.Fatalf("expecting schema1 manifest")
        }
 
+       checkHeaders(t, resp, http.Header{
+               "Docker-Content-Digest": []string{desc.Digest.String()},
+               "ETag":                  []string{fmt.Sprintf(`"%s"`, 
desc.Digest)},
+       })
+
        if fetchedSchema1Manifest.Manifest.SchemaVersion != 1 {
                t.Fatal("wrong schema version")
        }
@@ -1603,19 +1612,28 @@
        }
        defer resp.Body.Close()
 
+       manifestBytes, err := ioutil.ReadAll(resp.Body)
+       if err != nil {
+               t.Fatalf("error reading response body: %v", err)
+       }
+
        checkResponse(t, "fetching uploaded manifest list as schema1", resp, 
http.StatusOK)
-       checkHeaders(t, resp, http.Header{
-               "Docker-Content-Digest": []string{dgst.String()},
-               "ETag":                  []string{fmt.Sprintf(`"%s"`, dgst)},
-       })
 
-       var fetchedSchema1Manifest schema1.SignedManifest
-       dec = json.NewDecoder(resp.Body)
+       m, desc, err := 
distribution.UnmarshalManifest(schema1.MediaTypeManifest, manifestBytes)
+       if err != nil {
+               t.Fatalf("unexpected error unmarshalling manifest: %v", err)
+       }
 
-       if err := dec.Decode(&fetchedSchema1Manifest); err != nil {
-               t.Fatalf("error decoding fetched schema1 manifest: %v", err)
+       fetchedSchema1Manifest, ok := m.(*schema1.SignedManifest)
+       if !ok {
+               t.Fatalf("expecting schema1 manifest")
        }
 
+       checkHeaders(t, resp, http.Header{
+               "Docker-Content-Digest": []string{desc.Digest.String()},
+               "ETag":                  []string{fmt.Sprintf(`"%s"`, 
desc.Digest)},
+       })
+
        if fetchedSchema1Manifest.Manifest.SchemaVersion != 1 {
                t.Fatal("wrong schema version")
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/registry/handlers/app.go 
new/distribution-2.3.1/registry/handlers/app.go
--- old/distribution-2.3.0/registry/handlers/app.go     2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/handlers/app.go     2016-02-23 
23:39:19.000000000 +0100
@@ -146,11 +146,18 @@
        app.configureRedis(configuration)
        app.configureLogHook(configuration)
 
-       // Generate an ephemeral key to be used for signing converted manifests
-       // for clients that don't support schema2.
-       app.trustKey, err = libtrust.GenerateECP256PrivateKey()
-       if err != nil {
-               panic(err)
+       if configuration.Compatibility.Schema1.TrustKey != "" {
+               app.trustKey, err = 
libtrust.LoadKeyFile(configuration.Compatibility.Schema1.TrustKey)
+               if err != nil {
+                       panic(fmt.Sprintf(`could not load schema1 "signingkey" 
parameter: %v`, err))
+               }
+       } else {
+               // Generate an ephemeral key to be used for signing converted 
manifests
+               // for clients that don't support schema2.
+               app.trustKey, err = libtrust.GenerateECP256PrivateKey()
+               if err != nil {
+                       panic(err)
+               }
        }
 
        if configuration.HTTP.Host != "" {
@@ -167,6 +174,11 @@
                options = append(options, storage.DisableDigestResumption)
        }
 
+       if configuration.Compatibility.Schema1.DisableSignatureStore {
+               options = append(options, storage.DisableSchema1Signatures)
+               options = append(options, 
storage.Schema1SigningKey(app.trustKey))
+       }
+
        // configure deletion
        if d, ok := configuration.Storage["delete"]; ok {
                e, ok := d["enabled"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/registry/handlers/images.go 
new/distribution-2.3.1/registry/handlers/images.go
--- old/distribution-2.3.0/registry/handlers/images.go  2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/handlers/images.go  2016-02-23 
23:39:19.000000000 +0100
@@ -196,6 +196,7 @@
                imh.Errors = append(imh.Errors, 
v2.ErrorCodeManifestInvalid.WithDetail(err))
                return nil, err
        }
+       imh.Digest = 
digest.FromBytes(manifest.(*schema1.SignedManifest).Canonical)
 
        return manifest, nil
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/registry/proxy/proxyauth.go 
new/distribution-2.3.1/registry/proxy/proxyauth.go
--- old/distribution-2.3.0/registry/proxy/proxyauth.go  2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/proxy/proxyauth.go  2016-02-23 
23:39:19.000000000 +0100
@@ -8,6 +8,7 @@
 )
 
 const tokenURL = "https://auth.docker.io/token";
+const challengeHeader = "Docker-Distribution-Api-Version"
 
 type userpass struct {
        username string
@@ -24,12 +25,8 @@
        return up.username, up.password
 }
 
-// ConfigureAuth authorizes with the upstream registry
-func ConfigureAuth(remoteURL, username, password string, cm 
auth.ChallengeManager) (auth.CredentialStore, error) {
-       if err := ping(cm, remoteURL+"/v2/", 
"Docker-Distribution-Api-Version"); err != nil {
-               return nil, err
-       }
-
+// configureAuth stores credentials for challenge responses
+func configureAuth(username, password string) (auth.CredentialStore, error) {
        creds := map[string]userpass{
                tokenURL: {
                        username: username,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/registry/proxy/proxyblobstore.go 
new/distribution-2.3.1/registry/proxy/proxyblobstore.go
--- old/distribution-2.3.0/registry/proxy/proxyblobstore.go     2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/proxy/proxyblobstore.go     2016-02-23 
23:39:19.000000000 +0100
@@ -22,6 +22,7 @@
        remoteStore    distribution.BlobService
        scheduler      *scheduler.TTLExpirationScheduler
        repositoryName reference.Named
+       authChallenger authChallenger
 }
 
 var _ distribution.BlobStore = &proxyBlobStore{}
@@ -121,6 +122,10 @@
                return nil
        }
 
+       if err := pbs.authChallenger.tryEstablishChallenges(ctx); err != nil {
+               return err
+       }
+
        mu.Lock()
        _, ok := inflight[dgst]
        if ok {
@@ -162,9 +167,35 @@
                return distribution.Descriptor{}, err
        }
 
+       if err := pbs.authChallenger.tryEstablishChallenges(ctx); err != nil {
+               return distribution.Descriptor{}, err
+       }
+
        return pbs.remoteStore.Stat(ctx, dgst)
 }
 
+func (pbs *proxyBlobStore) Get(ctx context.Context, dgst digest.Digest) 
([]byte, error) {
+       blob, err := pbs.localStore.Get(ctx, dgst)
+       if err == nil {
+               return blob, nil
+       }
+
+       if err := pbs.authChallenger.tryEstablishChallenges(ctx); err != nil {
+               return []byte{}, err
+       }
+
+       blob, err = pbs.remoteStore.Get(ctx, dgst)
+       if err != nil {
+               return []byte{}, err
+       }
+
+       _, err = pbs.localStore.Put(ctx, "", blob)
+       if err != nil {
+               return []byte{}, err
+       }
+       return blob, nil
+}
+
 // Unsupported functions
 func (pbs *proxyBlobStore) Put(ctx context.Context, mediaType string, p 
[]byte) (distribution.Descriptor, error) {
        return distribution.Descriptor{}, distribution.ErrUnsupported
@@ -186,10 +217,6 @@
        return nil, distribution.ErrUnsupported
 }
 
-func (pbs *proxyBlobStore) Get(ctx context.Context, dgst digest.Digest) 
([]byte, error) {
-       return nil, distribution.ErrUnsupported
-}
-
 func (pbs *proxyBlobStore) Delete(ctx context.Context, dgst digest.Digest) 
error {
        return distribution.ErrUnsupported
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/distribution-2.3.0/registry/proxy/proxyblobstore_test.go 
new/distribution-2.3.1/registry/proxy/proxyblobstore_test.go
--- old/distribution-2.3.0/registry/proxy/proxyblobstore_test.go        
2016-02-04 20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/proxy/proxyblobstore_test.go        
2016-02-23 23:39:19.000000000 +0100
@@ -168,6 +168,7 @@
                remoteStore:    truthBlobs,
                localStore:     localBlobs,
                scheduler:      s,
+               authChallenger: &mockChallenger{},
        }
 
        te := &testEnv{
@@ -217,6 +218,40 @@
        te.inRemote = inRemote
        te.numUnique = numUnique
 }
+func TestProxyStoreGet(t *testing.T) {
+       te := makeTestEnv(t, "foo/bar")
+
+       localStats := te.LocalStats()
+       remoteStats := te.RemoteStats()
+
+       populate(t, te, 1, 10, 1)
+       _, err := te.store.Get(te.ctx, te.inRemote[0].Digest)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if (*localStats)["get"] != 1 && (*localStats)["put"] != 1 {
+               t.Errorf("Unexpected local counts")
+       }
+
+       if (*remoteStats)["get"] != 1 {
+               t.Errorf("Unexpected remote get count")
+       }
+
+       _, err = te.store.Get(te.ctx, te.inRemote[0].Digest)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if (*localStats)["get"] != 2 && (*localStats)["put"] != 1 {
+               t.Errorf("Unexpected local counts")
+       }
+
+       if (*remoteStats)["get"] != 1 {
+               t.Errorf("Unexpected remote get count")
+       }
+
+}
 
 func TestProxyStoreStat(t *testing.T) {
        te := makeTestEnv(t, "foo/bar")
@@ -242,6 +277,11 @@
        if (*remoteStats)["stat"] != remoteBlobCount {
                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) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/distribution-2.3.0/registry/proxy/proxymanifeststore.go 
new/distribution-2.3.1/registry/proxy/proxymanifeststore.go
--- old/distribution-2.3.0/registry/proxy/proxymanifeststore.go 2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/proxy/proxymanifeststore.go 2016-02-23 
23:39:19.000000000 +0100
@@ -19,6 +19,7 @@
        remoteManifests distribution.ManifestService
        repositoryName  reference.Named
        scheduler       *scheduler.TTLExpirationScheduler
+       authChallenger  authChallenger
 }
 
 var _ distribution.ManifestService = &proxyManifestStore{}
@@ -31,7 +32,9 @@
        if exists {
                return true, nil
        }
-
+       if err := pms.authChallenger.tryEstablishChallenges(ctx); err != nil {
+               return false, err
+       }
        return pms.remoteManifests.Exists(ctx, dgst)
 }
 
@@ -41,6 +44,10 @@
        var fromRemote bool
        manifest, err := pms.localManifests.Get(ctx, dgst, options...)
        if err != nil {
+               if err := pms.authChallenger.tryEstablishChallenges(ctx); err 
!= nil {
+                       return nil, err
+               }
+
                manifest, err = pms.remoteManifests.Get(ctx, dgst, options...)
                if err != nil {
                        return nil, err
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/distribution-2.3.0/registry/proxy/proxymanifeststore_test.go 
new/distribution-2.3.1/registry/proxy/proxymanifeststore_test.go
--- old/distribution-2.3.0/registry/proxy/proxymanifeststore_test.go    
2016-02-04 20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/proxy/proxymanifeststore_test.go    
2016-02-23 23:39:19.000000000 +0100
@@ -2,6 +2,7 @@
 
 import (
        "io"
+       "sync"
        "testing"
 
        "github.com/docker/distribution"
@@ -10,6 +11,7 @@
        "github.com/docker/distribution/manifest"
        "github.com/docker/distribution/manifest/schema1"
        "github.com/docker/distribution/reference"
+       "github.com/docker/distribution/registry/client/auth"
        "github.com/docker/distribution/registry/proxy/scheduler"
        "github.com/docker/distribution/registry/storage"
        "github.com/docker/distribution/registry/storage/cache/memory"
@@ -64,6 +66,28 @@
 }
 */
 
+type mockChallenger struct {
+       sync.Mutex
+       count int
+}
+
+// Called for remote operations only
+func (m *mockChallenger) tryEstablishChallenges(context.Context) error {
+       m.Lock()
+       defer m.Unlock()
+
+       m.count++
+       return nil
+}
+
+func (m *mockChallenger) credentialStore() auth.CredentialStore {
+       return nil
+}
+
+func (m *mockChallenger) challengeManager() auth.ChallengeManager {
+       return nil
+}
+
 func newManifestStoreTestEnv(t *testing.T, name, tag string) 
*manifestStoreTestEnv {
        nameRef, err := reference.ParseNamed(name)
        if err != nil {
@@ -120,6 +144,7 @@
                        remoteManifests: truthManifests,
                        scheduler:       s,
                        repositoryName:  nameRef,
+                       authChallenger:  &mockChallenger{},
                },
        }
 }
@@ -198,6 +223,10 @@
                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
        _, err = env.manifests.Get(ctx, env.manifestDigest)
        if err != nil {
@@ -212,6 +241,10 @@
                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
        exists, err = env.manifests.Exists(ctx, env.manifestDigest)
        if err != nil {
@@ -225,17 +258,18 @@
                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)
        if err != nil {
                t.Fatal(err)
        }
 
-       if (*remoteStats)["get"] != 2 && (*remoteStats)["exists"] != 1 && 
(*localStats)["put"] != 1 {
-               t.Errorf("Unexpected get count")
+       if env.manifests.authChallenger.(*mockChallenger).count != 2 {
+               t.Fatalf("Expected 2 auth challenges, got %#v", 
env.manifests.authChallenger)
        }
-}
-
-func TestProxyTagService(t *testing.T) {
 
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/registry/proxy/proxyregistry.go 
new/distribution-2.3.1/registry/proxy/proxyregistry.go
--- old/distribution-2.3.0/registry/proxy/proxyregistry.go      2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/proxy/proxyregistry.go      2016-02-23 
23:39:19.000000000 +0100
@@ -1,10 +1,11 @@
 package proxy
 
 import (
+       "fmt"
        "net/http"
        "net/url"
+       "sync"
 
-       "fmt"
        "github.com/docker/distribution"
        "github.com/docker/distribution/configuration"
        "github.com/docker/distribution/context"
@@ -19,13 +20,10 @@
 
 // proxyingRegistry fetches content from a remote registry and caches it 
locally
 type proxyingRegistry struct {
-       embedded distribution.Namespace // provides local registry functionality
-
-       scheduler *scheduler.TTLExpirationScheduler
-
-       remoteURL        string
-       credentialStore  auth.CredentialStore
-       challengeManager auth.ChallengeManager
+       embedded       distribution.Namespace // provides local registry 
functionality
+       scheduler      *scheduler.TTLExpirationScheduler
+       remoteURL      string
+       authChallenger authChallenger
 }
 
 // NewRegistryPullThroughCache creates a registry acting as a pull through 
cache
@@ -93,18 +91,20 @@
                return nil, err
        }
 
-       challengeManager := auth.NewSimpleChallengeManager()
-       cs, err := ConfigureAuth(config.RemoteURL, config.Username, 
config.Password, challengeManager)
+       cs, err := configureAuth(config.Username, config.Password)
        if err != nil {
                return nil, err
        }
 
        return &proxyingRegistry{
-               embedded:         registry,
-               scheduler:        s,
-               challengeManager: challengeManager,
-               credentialStore:  cs,
-               remoteURL:        config.RemoteURL,
+               embedded:  registry,
+               scheduler: s,
+               remoteURL: config.RemoteURL,
+               authChallenger: &remoteAuthChallenger{
+                       remoteURL: config.RemoteURL,
+                       cm:        auth.NewSimpleChallengeManager(),
+                       cs:        cs,
+               },
        }, nil
 }
 
@@ -117,8 +117,10 @@
 }
 
 func (pr *proxyingRegistry) Repository(ctx context.Context, name 
reference.Named) (distribution.Repository, error) {
+       c := pr.authChallenger
+
        tr := transport.NewTransport(http.DefaultTransport,
-               auth.NewAuthorizer(pr.challengeManager, 
auth.NewTokenHandler(http.DefaultTransport, pr.credentialStore, name.Name(), 
"pull")))
+               auth.NewAuthorizer(c.challengeManager(), 
auth.NewTokenHandler(http.DefaultTransport, c.credentialStore(), name.Name(), 
"pull")))
 
        localRepo, err := pr.embedded.Repository(ctx, name)
        if err != nil {
@@ -145,6 +147,7 @@
                        remoteStore:    remoteRepo.Blobs(ctx),
                        scheduler:      pr.scheduler,
                        repositoryName: name,
+                       authChallenger: pr.authChallenger,
                },
                manifests: &proxyManifestStore{
                        repositoryName:  name,
@@ -152,15 +155,63 @@
                        remoteManifests: remoteManifests,
                        ctx:             ctx,
                        scheduler:       pr.scheduler,
+                       authChallenger:  pr.authChallenger,
                },
                name: name,
                tags: &proxyTagService{
-                       localTags:  localRepo.Tags(ctx),
-                       remoteTags: remoteRepo.Tags(ctx),
+                       localTags:      localRepo.Tags(ctx),
+                       remoteTags:     remoteRepo.Tags(ctx),
+                       authChallenger: pr.authChallenger,
                },
        }, nil
 }
 
+// authChallenger encapsulates a request to the upstream to establish 
credential challenges
+type authChallenger interface {
+       tryEstablishChallenges(context.Context) error
+       challengeManager() auth.ChallengeManager
+       credentialStore() auth.CredentialStore
+}
+
+type remoteAuthChallenger struct {
+       remoteURL string
+       sync.Mutex
+       cm auth.ChallengeManager
+       cs auth.CredentialStore
+}
+
+func (r *remoteAuthChallenger) credentialStore() auth.CredentialStore {
+       return r.cs
+}
+
+func (r *remoteAuthChallenger) challengeManager() auth.ChallengeManager {
+       return r.cm
+}
+
+// tryEstablishChallenges will attempt to get a challenge type for the 
upstream if none currently exist
+func (r *remoteAuthChallenger) tryEstablishChallenges(ctx context.Context) 
error {
+       r.Lock()
+       defer r.Unlock()
+
+       remoteURL := r.remoteURL + "/v2/"
+       challenges, err := r.cm.GetChallenges(remoteURL)
+       if err != nil {
+               return err
+       }
+
+       if len(challenges) > 0 {
+               return nil
+       }
+
+       // establish challenge type with upstream
+       if err := ping(r.cm, remoteURL, challengeHeader); err != nil {
+               return err
+       }
+
+       context.GetLogger(ctx).Infof("Challenge established with upstream : %s 
%s", remoteURL, r.cm)
+       return nil
+}
+
 // 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
 // already exist
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/registry/proxy/proxytagservice.go 
new/distribution-2.3.1/registry/proxy/proxytagservice.go
--- old/distribution-2.3.0/registry/proxy/proxytagservice.go    2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/proxy/proxytagservice.go    2016-02-23 
23:39:19.000000000 +0100
@@ -7,8 +7,9 @@
 
 // proxyTagService supports local and remote lookup of tags.
 type proxyTagService struct {
-       localTags  distribution.TagService
-       remoteTags distribution.TagService
+       localTags      distribution.TagService
+       remoteTags     distribution.TagService
+       authChallenger authChallenger
 }
 
 var _ distribution.TagService = proxyTagService{}
@@ -17,16 +18,19 @@
 // tag service first and then caching it locally.  If the remote is unavailable
 // the local association is returned
 func (pt proxyTagService) Get(ctx context.Context, tag string) 
(distribution.Descriptor, error) {
-       desc, err := pt.remoteTags.Get(ctx, tag)
+       err := pt.authChallenger.tryEstablishChallenges(ctx)
        if err == nil {
-               err := pt.localTags.Tag(ctx, tag, desc)
-               if err != nil {
-                       return distribution.Descriptor{}, err
+               desc, err := pt.remoteTags.Get(ctx, tag)
+               if err == nil {
+                       err := pt.localTags.Tag(ctx, tag, desc)
+                       if err != nil {
+                               return distribution.Descriptor{}, err
+                       }
+                       return desc, nil
                }
-               return desc, nil
        }
 
-       desc, err = pt.localTags.Get(ctx, tag)
+       desc, err := pt.localTags.Get(ctx, tag)
        if err != nil {
                return distribution.Descriptor{}, err
        }
@@ -46,9 +50,12 @@
 }
 
 func (pt proxyTagService) All(ctx context.Context) ([]string, error) {
-       tags, err := pt.remoteTags.All(ctx)
+       err := pt.authChallenger.tryEstablishChallenges(ctx)
        if err == nil {
-               return tags, err
+               tags, err := pt.remoteTags.All(ctx)
+               if err == nil {
+                       return tags, err
+               }
        }
        return pt.localTags.All(ctx)
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/distribution-2.3.0/registry/proxy/proxytagservice_test.go 
new/distribution-2.3.1/registry/proxy/proxytagservice_test.go
--- old/distribution-2.3.0/registry/proxy/proxytagservice_test.go       
2016-02-04 20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/proxy/proxytagservice_test.go       
2016-02-23 23:39:19.000000000 +0100
@@ -69,8 +69,9 @@
                remote = make(map[string]distribution.Descriptor)
        }
        return &proxyTagService{
-               localTags:  &mockTagStore{mapping: local},
-               remoteTags: &mockTagStore{mapping: remote},
+               localTags:      &mockTagStore{mapping: local},
+               remoteTags:     &mockTagStore{mapping: remote},
+               authChallenger: &mockChallenger{},
        }
 }
 
@@ -87,6 +88,10 @@
                t.Fatal(err)
        }
 
+       if proxyTags.authChallenger.(*mockChallenger).count != 1 {
+               t.Fatalf("Expected 1 auth challenge call, got %#v", 
proxyTags.authChallenger)
+       }
+
        if d != remoteDesc {
                t.Fatal("unable to get put tag")
        }
@@ -112,6 +117,10 @@
                t.Fatal(err)
        }
 
+       if proxyTags.authChallenger.(*mockChallenger).count != 2 {
+               t.Fatalf("Expected 2 auth challenge calls, got %#v", 
proxyTags.authChallenger)
+       }
+
        if d != newRemoteDesc {
                t.Fatal("unable to get put tag")
        }
@@ -142,7 +151,11 @@
                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})
        if err != nil {
                t.Fatal(err)
@@ -161,4 +174,8 @@
        if all[0] != "funtag" && all[1] != "remote" {
                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)
+       }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/distribution-2.3.0/registry/storage/manifeststore_test.go 
new/distribution-2.3.1/registry/storage/manifeststore_test.go
--- old/distribution-2.3.0/registry/storage/manifeststore_test.go       
2016-02-04 20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/storage/manifeststore_test.go       
2016-02-23 23:39:19.000000000 +0100
@@ -28,11 +28,10 @@
        tag        string
 }
 
-func newManifestStoreTestEnv(t *testing.T, name reference.Named, tag string) 
*manifestStoreTestEnv {
+func newManifestStoreTestEnv(t *testing.T, name reference.Named, tag string, 
options ...RegistryOption) *manifestStoreTestEnv {
        ctx := context.Background()
        driver := inmemory.New()
-       registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(
-               memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, 
EnableRedirect)
+       registry, err := NewRegistry(ctx, driver, options...)
        if err != nil {
                t.Fatalf("error creating registry: %v", err)
        }
@@ -53,13 +52,26 @@
 }
 
 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))
+}
+
+func testManifestStorage(t *testing.T, options ...RegistryOption) {
        repoName, _ := reference.ParseNamed("foo/bar")
-       env := newManifestStoreTestEnv(t, repoName, "thetag")
+       env := newManifestStoreTestEnv(t, repoName, "thetag", options...)
        ctx := context.Background()
        ms, err := env.repository.Manifests(ctx)
        if err != nil {
                t.Fatal(err)
        }
+       equalSignatures := env.registry.(*registry).schema1SignaturesEnabled
 
        m := schema1.Manifest{
                Versioned: manifest.Versioned{
@@ -159,8 +171,14 @@
                t.Fatalf("unexpected manifest type from signedstore")
        }
 
-       if !reflect.DeepEqual(fetchedManifest, sm) {
-               t.Fatalf("fetched manifest not equal: %#v != %#v", 
fetchedManifest, sm)
+       if !bytes.Equal(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()
@@ -196,8 +214,19 @@
                t.Fatalf("unexpected error fetching manifest by digest: %v", 
err)
        }
 
-       if !reflect.DeepEqual(fetchedByDigest, fetchedManifest) {
-               t.Fatalf("fetched manifest not equal: %#v != %#v", 
fetchedByDigest, fetchedManifest)
+       byDigestManifest, ok := fetchedByDigest.(*schema1.SignedManifest)
+       if !ok {
+               t.Fatalf("unexpected manifest type from signedstore")
+       }
+
+       if !bytes.Equal(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()
@@ -286,14 +315,16 @@
                t.Fatalf("payloads are not equal")
        }
 
-       receivedSigs, err := receivedJWS.Signatures()
-       if err != nil {
-               t.Fatalf("error getting signatures: %v", err)
-       }
+       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]))
+               for i, sig := range receivedSigs {
+                       if !bytes.Equal(sig, expectedSigs[i]) {
+                               t.Fatalf("mismatched signatures from remote: %v 
!= %v", string(sig), string(expectedSigs[i]))
+                       }
                }
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/distribution-2.3.0/registry/storage/registry.go 
new/distribution-2.3.1/registry/storage/registry.go
--- old/distribution-2.3.0/registry/storage/registry.go 2016-02-04 
20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/storage/registry.go 2016-02-23 
23:39:19.000000000 +0100
@@ -6,6 +6,7 @@
        "github.com/docker/distribution/reference"
        "github.com/docker/distribution/registry/storage/cache"
        storagedriver "github.com/docker/distribution/registry/storage/driver"
+       "github.com/docker/libtrust"
 )
 
 // registry is the top-level implementation of Registry for use in the storage
@@ -17,6 +18,8 @@
        blobDescriptorCacheProvider cache.BlobDescriptorCacheProvider
        deleteEnabled               bool
        resumableDigestEnabled      bool
+       schema1SignaturesEnabled    bool
+       schema1SigningKey           libtrust.PrivateKey
 }
 
 // RegistryOption is the type used for functional options for NewRegistry.
@@ -43,6 +46,24 @@
        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.
+func Schema1SigningKey(key libtrust.PrivateKey) RegistryOption {
+       return func(registry *registry) error {
+               registry.schema1SigningKey = key
+               return nil
+       }
+}
+
 // BlobDescriptorCacheProvider returns a functional option for
 // NewRegistry. It creates a cached blob statter for use by the
 // registry.
@@ -85,8 +106,9 @@
                        statter: statter,
                        pathFn:  bs.path,
                },
-               statter:                statter,
-               resumableDigestEnabled: true,
+               statter:                  statter,
+               resumableDigestEnabled:   true,
+               schema1SignaturesEnabled: true,
        }
 
        for _, option := range options {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/distribution-2.3.0/registry/storage/signedmanifesthandler.go 
new/distribution-2.3.1/registry/storage/signedmanifesthandler.go
--- old/distribution-2.3.0/registry/storage/signedmanifesthandler.go    
2016-02-04 20:11:33.000000000 +0100
+++ new/distribution-2.3.1/registry/storage/signedmanifesthandler.go    
2016-02-23 23:39:19.000000000 +0100
@@ -25,10 +25,17 @@
 
 func (ms *signedManifestHandler) Unmarshal(ctx context.Context, dgst 
digest.Digest, content []byte) (distribution.Manifest, error) {
        context.GetLogger(ms.ctx).Debug("(*signedManifestHandler).Unmarshal")
-       // Fetch the signatures for the manifest
-       signatures, err := ms.signatures.Get(dgst)
-       if err != nil {
-               return nil, err
+
+       var (
+               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...)
@@ -36,6 +43,14 @@
                return nil, err
        }
 
+       if ms.repository.schema1SigningKey != nil {
+               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
        raw, err := jsig.PrettySignature("signatures")
        if err != nil {
@@ -75,14 +90,16 @@
                return "", err
        }
 
-       // 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
+       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


Reply via email to