Merge pull request #6230 from tiborvass/bump_v0.12.0

Bump version to v0.12.0
This commit is contained in:
Michael Crosby 2014-06-06 11:41:06 -07:00
commit e267ebfc6b
4 changed files with 165 additions and 11 deletions

View file

@ -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)

View file

@ -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
@ -155,7 +156,13 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
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
) )

View file

@ -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,
} }
@ -735,9 +810,11 @@ func NewRegistry(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, inde
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

View file

@ -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'")
}
}
}