Merge pull request #6230 from tiborvass/bump_v0.12.0
Bump version to v0.12.0
This commit is contained in:
commit
e267ebfc6b
4 changed files with 165 additions and 11 deletions
|
@ -1,3 +1,4 @@
|
||||||
Sam Alba <sam@dotcloud.com> (@samalba)
|
Sam Alba <sam@dotcloud.com> (@samalba)
|
||||||
Joffrey Fuhrer <joffrey@dotcloud.com> (@shin-)
|
Joffrey Fuhrer <joffrey@dotcloud.com> (@shin-)
|
||||||
Ken Cochrane <ken@dotcloud.com> (@kencochrane)
|
Ken Cochrane <ken@dotcloud.com> (@kencochrane)
|
||||||
|
Vincent Batts <vbatts@redhat.com> (@vbatts)
|
||||||
|
|
17
docs/auth.go
17
docs/auth.go
|
@ -5,12 +5,13 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/utils"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Where we store the config file
|
// Where we store the config file
|
||||||
|
@ -152,10 +153,16 @@ func SaveConfig(configFile *ConfigFile) error {
|
||||||
// try to register/login to the registry server
|
// try to register/login to the registry server
|
||||||
func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, error) {
|
func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, error) {
|
||||||
var (
|
var (
|
||||||
status string
|
status string
|
||||||
reqBody []byte
|
reqBody []byte
|
||||||
err error
|
err error
|
||||||
client = &http.Client{}
|
client = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DisableKeepAlives: true,
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
},
|
||||||
|
CheckRedirect: AddRequiredHeadersToRedirectedRequests,
|
||||||
|
}
|
||||||
reqStatusCode = 0
|
reqStatusCode = 0
|
||||||
serverAddress = authConfig.ServerAddress
|
serverAddress = authConfig.ServerAddress
|
||||||
)
|
)
|
||||||
|
|
|
@ -256,12 +256,43 @@ func (r *Registry) GetRemoteImageJSON(imgID, registry string, token []string) ([
|
||||||
return jsonString, imageSize, nil
|
return jsonString, imageSize, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) GetRemoteImageLayer(imgID, registry string, token []string) (io.ReadCloser, error) {
|
func (r *Registry) GetRemoteImageLayer(imgID, registry string, token []string, imgSize int64) (io.ReadCloser, error) {
|
||||||
req, err := r.reqFactory.NewRequest("GET", registry+"images/"+imgID+"/layer", nil)
|
var (
|
||||||
|
retries = 5
|
||||||
|
headRes *http.Response
|
||||||
|
hasResume bool = false
|
||||||
|
imageURL = fmt.Sprintf("%simages/%s/layer", registry, imgID)
|
||||||
|
)
|
||||||
|
headReq, err := r.reqFactory.NewRequest("HEAD", imageURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error while getting from the server: %s\n", err)
|
||||||
|
}
|
||||||
|
setTokenAuth(headReq, token)
|
||||||
|
for i := 1; i <= retries; i++ {
|
||||||
|
headRes, err = r.client.Do(headReq)
|
||||||
|
if err != nil && i == retries {
|
||||||
|
return nil, fmt.Errorf("Eror while making head request: %s\n", err)
|
||||||
|
} else if err != nil {
|
||||||
|
time.Sleep(time.Duration(i) * 5 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if headRes.Header.Get("Accept-Ranges") == "bytes" && imgSize > 0 {
|
||||||
|
hasResume = true
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := r.reqFactory.NewRequest("GET", imageURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error while getting from the server: %s\n", err)
|
return nil, fmt.Errorf("Error while getting from the server: %s\n", err)
|
||||||
}
|
}
|
||||||
setTokenAuth(req, token)
|
setTokenAuth(req, token)
|
||||||
|
if hasResume {
|
||||||
|
utils.Debugf("server supports resume")
|
||||||
|
return utils.ResumableRequestReader(r.client, req, 5, imgSize), nil
|
||||||
|
}
|
||||||
|
utils.Debugf("server doesn't support resume")
|
||||||
res, err := r.client.Do(req)
|
res, err := r.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -725,8 +756,52 @@ type Registry struct {
|
||||||
indexEndpoint string
|
indexEndpoint string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func trustedLocation(req *http.Request) bool {
|
||||||
|
var (
|
||||||
|
trusteds = []string{"docker.com", "docker.io"}
|
||||||
|
hostname = strings.SplitN(req.Host, ":", 2)[0]
|
||||||
|
)
|
||||||
|
if req.URL.Scheme != "https" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, trusted := range trusteds {
|
||||||
|
if strings.HasSuffix(hostname, trusted) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error {
|
||||||
|
if via != nil && via[0] != nil {
|
||||||
|
if trustedLocation(req) && trustedLocation(via[0]) {
|
||||||
|
req.Header = via[0].Header
|
||||||
|
} else {
|
||||||
|
for k, v := range via[0].Header {
|
||||||
|
if k != "Authorization" {
|
||||||
|
for _, vv := range v {
|
||||||
|
req.Header.Add(k, vv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewRegistry(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, indexEndpoint string) (r *Registry, err error) {
|
func NewRegistry(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, indexEndpoint string) (r *Registry, err error) {
|
||||||
|
httpDial := func(proto string, addr string) (net.Conn, error) {
|
||||||
|
conn, err := net.Dial(proto, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn = utils.NewTimeoutConn(conn, time.Duration(1)*time.Minute)
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
httpTransport := &http.Transport{
|
httpTransport := &http.Transport{
|
||||||
|
Dial: httpDial,
|
||||||
DisableKeepAlives: true,
|
DisableKeepAlives: true,
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
}
|
}
|
||||||
|
@ -734,10 +809,12 @@ func NewRegistry(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, inde
|
||||||
r = &Registry{
|
r = &Registry{
|
||||||
authConfig: authConfig,
|
authConfig: authConfig,
|
||||||
client: &http.Client{
|
client: &http.Client{
|
||||||
Transport: httpTransport,
|
Transport: httpTransport,
|
||||||
|
CheckRedirect: AddRequiredHeadersToRedirectedRequests,
|
||||||
},
|
},
|
||||||
indexEndpoint: indexEndpoint,
|
indexEndpoint: indexEndpoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
r.client.Jar, err = cookiejar.New(nil)
|
r.client.Jar, err = cookiejar.New(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -2,10 +2,12 @@ package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/utils"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -70,7 +72,7 @@ func TestGetRemoteImageJSON(t *testing.T) {
|
||||||
|
|
||||||
func TestGetRemoteImageLayer(t *testing.T) {
|
func TestGetRemoteImageLayer(t *testing.T) {
|
||||||
r := spawnTestRegistry(t)
|
r := spawnTestRegistry(t)
|
||||||
data, err := r.GetRemoteImageLayer(IMAGE_ID, makeURL("/v1/"), TOKEN)
|
data, err := r.GetRemoteImageLayer(IMAGE_ID, makeURL("/v1/"), TOKEN, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -78,7 +80,7 @@ func TestGetRemoteImageLayer(t *testing.T) {
|
||||||
t.Fatal("Expected non-nil data result")
|
t.Fatal("Expected non-nil data result")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), TOKEN)
|
_, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), TOKEN, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected image not found error")
|
t.Fatal("Expected image not found error")
|
||||||
}
|
}
|
||||||
|
@ -231,3 +233,70 @@ func TestValidRepositoryName(t *testing.T) {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrustedLocation(t *testing.T) {
|
||||||
|
for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.io"} {
|
||||||
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
|
if trustedLocation(req) == true {
|
||||||
|
t.Fatalf("'%s' shouldn't be detected as a trusted location", url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, url := range []string{"https://docker.io", "https://test.docker.io:80"} {
|
||||||
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
|
if trustedLocation(req) == false {
|
||||||
|
t.Fatalf("'%s' should be detected as a trusted location", url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) {
|
||||||
|
for _, urls := range [][]string{
|
||||||
|
{"http://docker.io", "https://docker.com"},
|
||||||
|
{"https://foo.docker.io:7777", "http://bar.docker.com"},
|
||||||
|
{"https://foo.docker.io", "https://example.com"},
|
||||||
|
} {
|
||||||
|
reqFrom, _ := http.NewRequest("GET", urls[0], nil)
|
||||||
|
reqFrom.Header.Add("Content-Type", "application/json")
|
||||||
|
reqFrom.Header.Add("Authorization", "super_secret")
|
||||||
|
reqTo, _ := http.NewRequest("GET", urls[1], nil)
|
||||||
|
|
||||||
|
AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
|
||||||
|
|
||||||
|
if len(reqTo.Header) != 1 {
|
||||||
|
t.Fatal("Expected 1 headers, got %d", len(reqTo.Header))
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqTo.Header.Get("Content-Type") != "application/json" {
|
||||||
|
t.Fatal("'Content-Type' should be 'application/json'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqTo.Header.Get("Authorization") != "" {
|
||||||
|
t.Fatal("'Authorization' should be empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, urls := range [][]string{
|
||||||
|
{"https://docker.io", "https://docker.com"},
|
||||||
|
{"https://foo.docker.io:7777", "https://bar.docker.com"},
|
||||||
|
} {
|
||||||
|
reqFrom, _ := http.NewRequest("GET", urls[0], nil)
|
||||||
|
reqFrom.Header.Add("Content-Type", "application/json")
|
||||||
|
reqFrom.Header.Add("Authorization", "super_secret")
|
||||||
|
reqTo, _ := http.NewRequest("GET", urls[1], nil)
|
||||||
|
|
||||||
|
AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
|
||||||
|
|
||||||
|
if len(reqTo.Header) != 2 {
|
||||||
|
t.Fatal("Expected 2 headers, got %d", len(reqTo.Header))
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqTo.Header.Get("Content-Type") != "application/json" {
|
||||||
|
t.Fatal("'Content-Type' should be 'application/json'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqTo.Header.Get("Authorization") != "super_secret" {
|
||||||
|
t.Fatal("'Authorization' should be 'super_secret'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue