Signed-off-by: Vincent Batts <vbatts@redhat.com>
| ... | ... |
@@ -52,7 +52,7 @@ func (s *TagStore) CmdPull(job *engine.Job) engine.Status {
|
| 52 | 52 |
return job.Error(err) |
| 53 | 53 |
} |
| 54 | 54 |
|
| 55 |
- endpoint, err := registry.ExpandAndVerifyRegistryUrl(hostname) |
|
| 55 |
+ endpoint, err := registry.NewEndpoint(hostname) |
|
| 56 | 56 |
if err != nil {
|
| 57 | 57 |
return job.Error(err) |
| 58 | 58 |
} |
| ... | ... |
@@ -62,7 +62,7 @@ func (s *TagStore) CmdPull(job *engine.Job) engine.Status {
|
| 62 | 62 |
return job.Error(err) |
| 63 | 63 |
} |
| 64 | 64 |
|
| 65 |
- if endpoint == registry.IndexServerAddress() {
|
|
| 65 |
+ if endpoint.String() == registry.IndexServerAddress() {
|
|
| 66 | 66 |
// If pull "index.docker.io/foo/bar", it's stored locally under "foo/bar" |
| 67 | 67 |
localName = remoteName |
| 68 | 68 |
|
| ... | ... |
@@ -214,7 +214,7 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
|
| 214 | 214 |
return job.Error(err) |
| 215 | 215 |
} |
| 216 | 216 |
|
| 217 |
- endpoint, err := registry.ExpandAndVerifyRegistryUrl(hostname) |
|
| 217 |
+ endpoint, err := registry.NewEndpoint(hostname) |
|
| 218 | 218 |
if err != nil {
|
| 219 | 219 |
return job.Error(err) |
| 220 | 220 |
} |
| ... | ... |
@@ -243,7 +243,7 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
|
| 243 | 243 |
|
| 244 | 244 |
var token []string |
| 245 | 245 |
job.Stdout.Write(sf.FormatStatus("", "The push refers to an image: [%s]", localName))
|
| 246 |
- if _, err := s.pushImage(r, job.Stdout, remoteName, img.ID, endpoint, token, sf); err != nil {
|
|
| 246 |
+ if _, err := s.pushImage(r, job.Stdout, remoteName, img.ID, endpoint.String(), token, sf); err != nil {
|
|
| 247 | 247 |
return job.Error(err) |
| 248 | 248 |
} |
| 249 | 249 |
return engine.StatusOK |
| 250 | 250 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,129 @@ |
| 0 |
+package registry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "io/ioutil" |
|
| 7 |
+ "net/http" |
|
| 8 |
+ "net/url" |
|
| 9 |
+ "strings" |
|
| 10 |
+ |
|
| 11 |
+ "github.com/docker/docker/pkg/log" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+// scans string for api version in the URL path. returns the trimmed hostname, if version found, string and API version. |
|
| 15 |
+func scanForApiVersion(hostname string) (string, APIVersion) {
|
|
| 16 |
+ var ( |
|
| 17 |
+ chunks []string |
|
| 18 |
+ apiVersionStr string |
|
| 19 |
+ ) |
|
| 20 |
+ if strings.HasSuffix(hostname, "/") {
|
|
| 21 |
+ chunks = strings.Split(hostname[:len(hostname)-1], "/") |
|
| 22 |
+ apiVersionStr = chunks[len(chunks)-1] |
|
| 23 |
+ } else {
|
|
| 24 |
+ chunks = strings.Split(hostname, "/") |
|
| 25 |
+ apiVersionStr = chunks[len(chunks)-1] |
|
| 26 |
+ } |
|
| 27 |
+ for k, v := range apiVersions {
|
|
| 28 |
+ if apiVersionStr == v {
|
|
| 29 |
+ hostname = strings.Join(chunks[:len(chunks)-1], "/") |
|
| 30 |
+ return hostname, k |
|
| 31 |
+ } |
|
| 32 |
+ } |
|
| 33 |
+ return hostname, DefaultAPIVersion |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func NewEndpoint(hostname string) (*Endpoint, error) {
|
|
| 37 |
+ var ( |
|
| 38 |
+ endpoint Endpoint |
|
| 39 |
+ trimmedHostname string |
|
| 40 |
+ err error |
|
| 41 |
+ ) |
|
| 42 |
+ if !strings.HasPrefix(hostname, "http") {
|
|
| 43 |
+ hostname = "https://" + hostname |
|
| 44 |
+ } |
|
| 45 |
+ trimmedHostname, endpoint.Version = scanForApiVersion(hostname) |
|
| 46 |
+ endpoint.URL, err = url.Parse(trimmedHostname) |
|
| 47 |
+ if err != nil {
|
|
| 48 |
+ return nil, err |
|
| 49 |
+ } |
|
| 50 |
+ |
|
| 51 |
+ endpoint.URL.Scheme = "https" |
|
| 52 |
+ if _, err := endpoint.Ping(); err != nil {
|
|
| 53 |
+ log.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err)
|
|
| 54 |
+ // TODO: Check if http fallback is enabled |
|
| 55 |
+ endpoint.URL.Scheme = "http" |
|
| 56 |
+ if _, err = endpoint.Ping(); err != nil {
|
|
| 57 |
+ return nil, errors.New("Invalid Registry endpoint: " + err.Error())
|
|
| 58 |
+ } |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ return &endpoint, nil |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+type Endpoint struct {
|
|
| 65 |
+ URL *url.URL |
|
| 66 |
+ Version APIVersion |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+// Get the formated URL for the root of this registry Endpoint |
|
| 70 |
+func (e Endpoint) String() string {
|
|
| 71 |
+ return fmt.Sprintf("%s/v%d/", e.URL.String(), e.Version)
|
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+func (e Endpoint) VersionString(version APIVersion) string {
|
|
| 75 |
+ return fmt.Sprintf("%s/v%d/", e.URL.String(), version)
|
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+func (e Endpoint) Ping() (RegistryInfo, error) {
|
|
| 79 |
+ if e.String() == IndexServerAddress() {
|
|
| 80 |
+ // Skip the check, we now this one is valid |
|
| 81 |
+ // (and we never want to fallback to http in case of error) |
|
| 82 |
+ return RegistryInfo{Standalone: false}, nil
|
|
| 83 |
+ } |
|
| 84 |
+ |
|
| 85 |
+ req, err := http.NewRequest("GET", e.String()+"_ping", nil)
|
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return RegistryInfo{Standalone: false}, err
|
|
| 88 |
+ } |
|
| 89 |
+ |
|
| 90 |
+ resp, _, err := doRequest(req, nil, ConnectTimeout) |
|
| 91 |
+ if err != nil {
|
|
| 92 |
+ return RegistryInfo{Standalone: false}, err
|
|
| 93 |
+ } |
|
| 94 |
+ |
|
| 95 |
+ defer resp.Body.Close() |
|
| 96 |
+ |
|
| 97 |
+ jsonString, err := ioutil.ReadAll(resp.Body) |
|
| 98 |
+ if err != nil {
|
|
| 99 |
+ return RegistryInfo{Standalone: false}, fmt.Errorf("Error while reading the http response: %s", err)
|
|
| 100 |
+ } |
|
| 101 |
+ |
|
| 102 |
+ // If the header is absent, we assume true for compatibility with earlier |
|
| 103 |
+ // versions of the registry. default to true |
|
| 104 |
+ info := RegistryInfo{
|
|
| 105 |
+ Standalone: true, |
|
| 106 |
+ } |
|
| 107 |
+ if err := json.Unmarshal(jsonString, &info); err != nil {
|
|
| 108 |
+ log.Debugf("Error unmarshalling the _ping RegistryInfo: %s", err)
|
|
| 109 |
+ // don't stop here. Just assume sane defaults |
|
| 110 |
+ } |
|
| 111 |
+ if hdr := resp.Header.Get("X-Docker-Registry-Version"); hdr != "" {
|
|
| 112 |
+ log.Debugf("Registry version header: '%s'", hdr)
|
|
| 113 |
+ info.Version = hdr |
|
| 114 |
+ } |
|
| 115 |
+ log.Debugf("RegistryInfo.Version: %q", info.Version)
|
|
| 116 |
+ |
|
| 117 |
+ standalone := resp.Header.Get("X-Docker-Registry-Standalone")
|
|
| 118 |
+ log.Debugf("Registry standalone header: '%s'", standalone)
|
|
| 119 |
+ // Accepted values are "true" (case-insensitive) and "1". |
|
| 120 |
+ if strings.EqualFold(standalone, "true") || standalone == "1" {
|
|
| 121 |
+ info.Standalone = true |
|
| 122 |
+ } else if len(standalone) > 0 {
|
|
| 123 |
+ // there is a header set, and it is not "true" or "1", so assume fails |
|
| 124 |
+ info.Standalone = false |
|
| 125 |
+ } |
|
| 126 |
+ log.Debugf("RegistryInfo.Standalone: %q", info.Standalone)
|
|
| 127 |
+ return info, nil |
|
| 128 |
+} |
| ... | ... |
@@ -3,7 +3,6 @@ package registry |
| 3 | 3 |
import ( |
| 4 | 4 |
"crypto/tls" |
| 5 | 5 |
"crypto/x509" |
| 6 |
- "encoding/json" |
|
| 7 | 6 |
"errors" |
| 8 | 7 |
"fmt" |
| 9 | 8 |
"io/ioutil" |
| ... | ... |
@@ -15,7 +14,6 @@ import ( |
| 15 | 15 |
"strings" |
| 16 | 16 |
"time" |
| 17 | 17 |
|
| 18 |
- "github.com/docker/docker/pkg/log" |
|
| 19 | 18 |
"github.com/docker/docker/utils" |
| 20 | 19 |
) |
| 21 | 20 |
|
| ... | ... |
@@ -152,55 +150,6 @@ func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType) (*htt |
| 152 | 152 |
return nil, nil, nil |
| 153 | 153 |
} |
| 154 | 154 |
|
| 155 |
-func pingRegistryEndpoint(endpoint string) (RegistryInfo, error) {
|
|
| 156 |
- if endpoint == IndexServerAddress() {
|
|
| 157 |
- // Skip the check, we now this one is valid |
|
| 158 |
- // (and we never want to fallback to http in case of error) |
|
| 159 |
- return RegistryInfo{Standalone: false}, nil
|
|
| 160 |
- } |
|
| 161 |
- |
|
| 162 |
- req, err := http.NewRequest("GET", endpoint+"_ping", nil)
|
|
| 163 |
- if err != nil {
|
|
| 164 |
- return RegistryInfo{Standalone: false}, err
|
|
| 165 |
- } |
|
| 166 |
- |
|
| 167 |
- resp, _, err := doRequest(req, nil, ConnectTimeout) |
|
| 168 |
- if err != nil {
|
|
| 169 |
- return RegistryInfo{Standalone: false}, err
|
|
| 170 |
- } |
|
| 171 |
- |
|
| 172 |
- defer resp.Body.Close() |
|
| 173 |
- |
|
| 174 |
- jsonString, err := ioutil.ReadAll(resp.Body) |
|
| 175 |
- if err != nil {
|
|
| 176 |
- return RegistryInfo{Standalone: false}, fmt.Errorf("Error while reading the http response: %s", err)
|
|
| 177 |
- } |
|
| 178 |
- |
|
| 179 |
- // If the header is absent, we assume true for compatibility with earlier |
|
| 180 |
- // versions of the registry. default to true |
|
| 181 |
- info := RegistryInfo{
|
|
| 182 |
- Standalone: true, |
|
| 183 |
- } |
|
| 184 |
- if err := json.Unmarshal(jsonString, &info); err != nil {
|
|
| 185 |
- log.Debugf("Error unmarshalling the _ping RegistryInfo: %s", err)
|
|
| 186 |
- // don't stop here. Just assume sane defaults |
|
| 187 |
- } |
|
| 188 |
- if hdr := resp.Header.Get("X-Docker-Registry-Version"); hdr != "" {
|
|
| 189 |
- log.Debugf("Registry version header: '%s'", hdr)
|
|
| 190 |
- info.Version = hdr |
|
| 191 |
- } |
|
| 192 |
- log.Debugf("RegistryInfo.Version: %q", info.Version)
|
|
| 193 |
- |
|
| 194 |
- standalone := resp.Header.Get("X-Docker-Registry-Standalone")
|
|
| 195 |
- log.Debugf("Registry standalone header: '%s'", standalone)
|
|
| 196 |
- if !strings.EqualFold(standalone, "true") && standalone != "1" && len(standalone) > 0 {
|
|
| 197 |
- // there is a header set, and it is not "true" or "1", so assume fails |
|
| 198 |
- info.Standalone = false |
|
| 199 |
- } |
|
| 200 |
- log.Debugf("RegistryInfo.Standalone: %q", info.Standalone)
|
|
| 201 |
- return info, nil |
|
| 202 |
-} |
|
| 203 |
- |
|
| 204 | 155 |
func validateRepositoryName(repositoryName string) error {
|
| 205 | 156 |
var ( |
| 206 | 157 |
namespace string |
| ... | ... |
@@ -252,33 +201,6 @@ func ResolveRepositoryName(reposName string) (string, string, error) {
|
| 252 | 252 |
return hostname, reposName, nil |
| 253 | 253 |
} |
| 254 | 254 |
|
| 255 |
-// this method expands the registry name as used in the prefix of a repo |
|
| 256 |
-// to a full url. if it already is a url, there will be no change. |
|
| 257 |
-// The registry is pinged to test if it http or https |
|
| 258 |
-func ExpandAndVerifyRegistryUrl(hostname string) (string, error) {
|
|
| 259 |
- if strings.HasPrefix(hostname, "http:") || strings.HasPrefix(hostname, "https:") {
|
|
| 260 |
- // if there is no slash after https:// (8 characters) then we have no path in the url |
|
| 261 |
- if strings.LastIndex(hostname, "/") < 9 {
|
|
| 262 |
- // there is no path given. Expand with default path |
|
| 263 |
- hostname = hostname + "/v1/" |
|
| 264 |
- } |
|
| 265 |
- if _, err := pingRegistryEndpoint(hostname); err != nil {
|
|
| 266 |
- return "", errors.New("Invalid Registry endpoint: " + err.Error())
|
|
| 267 |
- } |
|
| 268 |
- return hostname, nil |
|
| 269 |
- } |
|
| 270 |
- endpoint := fmt.Sprintf("https://%s/v1/", hostname)
|
|
| 271 |
- if _, err := pingRegistryEndpoint(endpoint); err != nil {
|
|
| 272 |
- log.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err)
|
|
| 273 |
- endpoint = fmt.Sprintf("http://%s/v1/", hostname)
|
|
| 274 |
- if _, err = pingRegistryEndpoint(endpoint); err != nil {
|
|
| 275 |
- //TODO: triggering highland build can be done there without "failing" |
|
| 276 |
- return "", errors.New("Invalid Registry endpoint: " + err.Error())
|
|
| 277 |
- } |
|
| 278 |
- } |
|
| 279 |
- return endpoint, nil |
|
| 280 |
-} |
|
| 281 |
- |
|
| 282 | 255 |
func trustedLocation(req *http.Request) bool {
|
| 283 | 256 |
var ( |
| 284 | 257 |
trusteds = []string{"docker.com", "docker.io"}
|
| ... | ... |
@@ -18,7 +18,11 @@ var ( |
| 18 | 18 |
|
| 19 | 19 |
func spawnTestRegistrySession(t *testing.T) *Session {
|
| 20 | 20 |
authConfig := &AuthConfig{}
|
| 21 |
- r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), makeURL("/v1/"), true)
|
|
| 21 |
+ endpoint, err := NewEndpoint(makeURL("/v1/"))
|
|
| 22 |
+ if err != nil {
|
|
| 23 |
+ t.Fatal(err) |
|
| 24 |
+ } |
|
| 25 |
+ r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), endpoint, true) |
|
| 22 | 26 |
if err != nil {
|
| 23 | 27 |
t.Fatal(err) |
| 24 | 28 |
} |
| ... | ... |
@@ -26,7 +30,11 @@ func spawnTestRegistrySession(t *testing.T) *Session {
|
| 26 | 26 |
} |
| 27 | 27 |
|
| 28 | 28 |
func TestPingRegistryEndpoint(t *testing.T) {
|
| 29 |
- regInfo, err := pingRegistryEndpoint(makeURL("/v1/"))
|
|
| 29 |
+ ep, err := NewEndpoint(makeURL("/v1/"))
|
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ t.Fatal(err) |
|
| 32 |
+ } |
|
| 33 |
+ regInfo, err := ep.Ping() |
|
| 30 | 34 |
if err != nil {
|
| 31 | 35 |
t.Fatal(err) |
| 32 | 36 |
} |
| ... | ... |
@@ -197,7 +205,7 @@ func TestPushImageJSONIndex(t *testing.T) {
|
| 197 | 197 |
if repoData == nil {
|
| 198 | 198 |
t.Fatal("Expected RepositoryData object")
|
| 199 | 199 |
} |
| 200 |
- repoData, err = r.PushImageJSONIndex("foo42/bar", imgData, true, []string{r.indexEndpoint})
|
|
| 200 |
+ repoData, err = r.PushImageJSONIndex("foo42/bar", imgData, true, []string{r.indexEndpoint.String()})
|
|
| 201 | 201 |
if err != nil {
|
| 202 | 202 |
t.Fatal(err) |
| 203 | 203 |
} |
| ... | ... |
@@ -40,11 +40,14 @@ func (s *Service) Auth(job *engine.Job) engine.Status {
|
| 40 | 40 |
job.GetenvJson("authConfig", authConfig)
|
| 41 | 41 |
// TODO: this is only done here because auth and registry need to be merged into one pkg |
| 42 | 42 |
if addr := authConfig.ServerAddress; addr != "" && addr != IndexServerAddress() {
|
| 43 |
- addr, err = ExpandAndVerifyRegistryUrl(addr) |
|
| 43 |
+ endpoint, err := NewEndpoint(addr) |
|
| 44 | 44 |
if err != nil {
|
| 45 | 45 |
return job.Error(err) |
| 46 | 46 |
} |
| 47 |
- authConfig.ServerAddress = addr |
|
| 47 |
+ if _, err := endpoint.Ping(); err != nil {
|
|
| 48 |
+ return job.Error(err) |
|
| 49 |
+ } |
|
| 50 |
+ authConfig.ServerAddress = endpoint.String() |
|
| 48 | 51 |
} |
| 49 | 52 |
status, err := Login(authConfig, HTTPRequestFactory(nil)) |
| 50 | 53 |
if err != nil {
|
| ... | ... |
@@ -86,11 +89,11 @@ func (s *Service) Search(job *engine.Job) engine.Status {
|
| 86 | 86 |
if err != nil {
|
| 87 | 87 |
return job.Error(err) |
| 88 | 88 |
} |
| 89 |
- hostname, err = ExpandAndVerifyRegistryUrl(hostname) |
|
| 89 |
+ endpoint, err := NewEndpoint(hostname) |
|
| 90 | 90 |
if err != nil {
|
| 91 | 91 |
return job.Error(err) |
| 92 | 92 |
} |
| 93 |
- r, err := NewSession(authConfig, HTTPRequestFactory(metaHeaders), hostname, true) |
|
| 93 |
+ r, err := NewSession(authConfig, HTTPRequestFactory(metaHeaders), endpoint, true) |
|
| 94 | 94 |
if err != nil {
|
| 95 | 95 |
return job.Error(err) |
| 96 | 96 |
} |
| ... | ... |
@@ -25,15 +25,15 @@ import ( |
| 25 | 25 |
type Session struct {
|
| 26 | 26 |
authConfig *AuthConfig |
| 27 | 27 |
reqFactory *utils.HTTPRequestFactory |
| 28 |
- indexEndpoint string |
|
| 28 |
+ indexEndpoint *Endpoint |
|
| 29 | 29 |
jar *cookiejar.Jar |
| 30 | 30 |
timeout TimeoutType |
| 31 | 31 |
} |
| 32 | 32 |
|
| 33 |
-func NewSession(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, indexEndpoint string, timeout bool) (r *Session, err error) {
|
|
| 33 |
+func NewSession(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
|
|
| 34 | 34 |
r = &Session{
|
| 35 | 35 |
authConfig: authConfig, |
| 36 |
- indexEndpoint: indexEndpoint, |
|
| 36 |
+ indexEndpoint: endpoint, |
|
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 | 39 |
if timeout {
|
| ... | ... |
@@ -47,13 +47,13 @@ func NewSession(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, index |
| 47 | 47 |
|
| 48 | 48 |
// If we're working with a standalone private registry over HTTPS, send Basic Auth headers |
| 49 | 49 |
// alongside our requests. |
| 50 |
- if indexEndpoint != IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") {
|
|
| 51 |
- info, err := pingRegistryEndpoint(indexEndpoint) |
|
| 50 |
+ if r.indexEndpoint.String() != IndexServerAddress() && r.indexEndpoint.URL.Scheme == "https" {
|
|
| 51 |
+ info, err := r.indexEndpoint.Ping() |
|
| 52 | 52 |
if err != nil {
|
| 53 | 53 |
return nil, err |
| 54 | 54 |
} |
| 55 | 55 |
if info.Standalone {
|
| 56 |
- log.Debugf("Endpoint %s is eligible for private registry registry. Enabling decorator.", indexEndpoint)
|
|
| 56 |
+ log.Debugf("Endpoint %s is eligible for private registry registry. Enabling decorator.", r.indexEndpoint.String())
|
|
| 57 | 57 |
dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password) |
| 58 | 58 |
factory.AddDecorator(dec) |
| 59 | 59 |
} |
| ... | ... |
@@ -261,8 +261,7 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
|
| 261 | 261 |
} |
| 262 | 262 |
|
| 263 | 263 |
func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) {
|
| 264 |
- indexEp := r.indexEndpoint |
|
| 265 |
- repositoryTarget := fmt.Sprintf("%srepositories/%s/images", indexEp, remote)
|
|
| 264 |
+ repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), remote)
|
|
| 266 | 265 |
|
| 267 | 266 |
log.Debugf("[registry] Calling GET %s", repositoryTarget)
|
| 268 | 267 |
|
| ... | ... |
@@ -296,17 +295,13 @@ func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) {
|
| 296 | 296 |
|
| 297 | 297 |
var endpoints []string |
| 298 | 298 |
if res.Header.Get("X-Docker-Endpoints") != "" {
|
| 299 |
- endpoints, err = buildEndpointsList(res.Header["X-Docker-Endpoints"], indexEp) |
|
| 299 |
+ endpoints, err = buildEndpointsList(res.Header["X-Docker-Endpoints"], r.indexEndpoint.String()) |
|
| 300 | 300 |
if err != nil {
|
| 301 | 301 |
return nil, err |
| 302 | 302 |
} |
| 303 | 303 |
} else {
|
| 304 | 304 |
// Assume the endpoint is on the same host |
| 305 |
- u, err := url.Parse(indexEp) |
|
| 306 |
- if err != nil {
|
|
| 307 |
- return nil, err |
|
| 308 |
- } |
|
| 309 |
- endpoints = append(endpoints, fmt.Sprintf("%s://%s/v1/", u.Scheme, req.URL.Host))
|
|
| 305 |
+ endpoints = append(endpoints, fmt.Sprintf("%s://%s/v1/", r.indexEndpoint.URL.Scheme, req.URL.Host))
|
|
| 310 | 306 |
} |
| 311 | 307 |
|
| 312 | 308 |
checksumsJSON, err := ioutil.ReadAll(res.Body) |
| ... | ... |
@@ -474,7 +469,6 @@ func (r *Session) PushRegistryTag(remote, revision, tag, registry string, token |
| 474 | 474 |
|
| 475 | 475 |
func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool, regs []string) (*RepositoryData, error) {
|
| 476 | 476 |
cleanImgList := []*ImgData{}
|
| 477 |
- indexEp := r.indexEndpoint |
|
| 478 | 477 |
|
| 479 | 478 |
if validate {
|
| 480 | 479 |
for _, elem := range imgList {
|
| ... | ... |
@@ -494,7 +488,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate |
| 494 | 494 |
if validate {
|
| 495 | 495 |
suffix = "images" |
| 496 | 496 |
} |
| 497 |
- u := fmt.Sprintf("%srepositories/%s/%s", indexEp, remote, suffix)
|
|
| 497 |
+ u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), remote, suffix)
|
|
| 498 | 498 |
log.Debugf("[registry] PUT %s", u)
|
| 499 | 499 |
log.Debugf("Image list pushed to index:\n%s", imgListJSON)
|
| 500 | 500 |
req, err := r.reqFactory.NewRequest("PUT", u, bytes.NewReader(imgListJSON))
|
| ... | ... |
@@ -552,7 +546,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate |
| 552 | 552 |
} |
| 553 | 553 |
|
| 554 | 554 |
if res.Header.Get("X-Docker-Endpoints") != "" {
|
| 555 |
- endpoints, err = buildEndpointsList(res.Header["X-Docker-Endpoints"], indexEp) |
|
| 555 |
+ endpoints, err = buildEndpointsList(res.Header["X-Docker-Endpoints"], r.indexEndpoint.String()) |
|
| 556 | 556 |
if err != nil {
|
| 557 | 557 |
return nil, err |
| 558 | 558 |
} |
| ... | ... |
@@ -578,7 +572,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate |
| 578 | 578 |
|
| 579 | 579 |
func (r *Session) SearchRepositories(term string) (*SearchResults, error) {
|
| 580 | 580 |
log.Debugf("Index server: %s", r.indexEndpoint)
|
| 581 |
- u := r.indexEndpoint + "search?q=" + url.QueryEscape(term) |
|
| 581 |
+ u := r.indexEndpoint.String() + "search?q=" + url.QueryEscape(term) |
|
| 582 | 582 |
req, err := r.reqFactory.NewRequest("GET", u, nil)
|
| 583 | 583 |
if err != nil {
|
| 584 | 584 |
return nil, err |
| ... | ... |
@@ -31,3 +31,21 @@ type RegistryInfo struct {
|
| 31 | 31 |
Version string `json:"version"` |
| 32 | 32 |
Standalone bool `json:"standalone"` |
| 33 | 33 |
} |
| 34 |
+ |
|
| 35 |
+type APIVersion int |
|
| 36 |
+ |
|
| 37 |
+func (av APIVersion) String() string {
|
|
| 38 |
+ return apiVersions[av] |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+var DefaultAPIVersion APIVersion = APIVersion1 |
|
| 42 |
+var apiVersions = map[APIVersion]string{
|
|
| 43 |
+ 1: "v1", |
|
| 44 |
+ 2: "v2", |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+const ( |
|
| 48 |
+ _ = iota |
|
| 49 |
+ APIVersion1 = iota |
|
| 50 |
+ APIVersion2 |
|
| 51 |
+) |