Tizen_4.0 base
[platform/upstream/docker-engine.git] / distribution / registry_unit_test.go
1 package distribution
2
3 import (
4         "fmt"
5         "io/ioutil"
6         "net/http"
7         "net/http/httptest"
8         "net/url"
9         "os"
10         "runtime"
11         "strings"
12         "testing"
13
14         "github.com/Sirupsen/logrus"
15         "github.com/docker/distribution/reference"
16         "github.com/docker/docker/api/types"
17         registrytypes "github.com/docker/docker/api/types/registry"
18         "github.com/docker/docker/pkg/archive"
19         "github.com/docker/docker/pkg/stringid"
20         "github.com/docker/docker/registry"
21         "golang.org/x/net/context"
22 )
23
24 const secretRegistryToken = "mysecrettoken"
25
26 type tokenPassThruHandler struct {
27         reached       bool
28         gotToken      bool
29         shouldSend401 func(url string) bool
30 }
31
32 func (h *tokenPassThruHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
33         h.reached = true
34         if strings.Contains(r.Header.Get("Authorization"), secretRegistryToken) {
35                 logrus.Debug("Detected registry token in auth header")
36                 h.gotToken = true
37         }
38         if h.shouldSend401 == nil || h.shouldSend401(r.RequestURI) {
39                 w.Header().Set("WWW-Authenticate", `Bearer realm="foorealm"`)
40                 w.WriteHeader(401)
41         }
42 }
43
44 func testTokenPassThru(t *testing.T, ts *httptest.Server) {
45         tmp, err := testDirectory("")
46         if err != nil {
47                 t.Fatal(err)
48         }
49         defer os.RemoveAll(tmp)
50
51         uri, err := url.Parse(ts.URL)
52         if err != nil {
53                 t.Fatalf("could not parse url from test server: %v", err)
54         }
55
56         endpoint := registry.APIEndpoint{
57                 Mirror:       false,
58                 URL:          uri,
59                 Version:      2,
60                 Official:     false,
61                 TrimHostname: false,
62                 TLSConfig:    nil,
63         }
64         n, _ := reference.ParseNormalizedNamed("testremotename")
65         repoInfo := &registry.RepositoryInfo{
66                 Name: n,
67                 Index: &registrytypes.IndexInfo{
68                         Name:     "testrepo",
69                         Mirrors:  nil,
70                         Secure:   false,
71                         Official: false,
72                 },
73                 Official: false,
74         }
75         imagePullConfig := &ImagePullConfig{
76                 Config: Config{
77                         MetaHeaders: http.Header{},
78                         AuthConfig: &types.AuthConfig{
79                                 RegistryToken: secretRegistryToken,
80                         },
81                 },
82                 Schema2Types: ImageTypes,
83         }
84         puller, err := newPuller(endpoint, repoInfo, imagePullConfig)
85         if err != nil {
86                 t.Fatal(err)
87         }
88         p := puller.(*v2Puller)
89         ctx := context.Background()
90         p.repo, _, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
91         if err != nil {
92                 t.Fatal(err)
93         }
94
95         logrus.Debug("About to pull")
96         // We expect it to fail, since we haven't mock'd the full registry exchange in our handler above
97         tag, _ := reference.WithTag(n, "tag_goes_here")
98         _ = p.pullV2Repository(ctx, tag)
99 }
100
101 func TestTokenPassThru(t *testing.T) {
102         handler := &tokenPassThruHandler{shouldSend401: func(url string) bool { return url == "/v2/" }}
103         ts := httptest.NewServer(handler)
104         defer ts.Close()
105
106         testTokenPassThru(t, ts)
107
108         if !handler.reached {
109                 t.Fatal("Handler not reached")
110         }
111         if !handler.gotToken {
112                 t.Fatal("Failed to receive registry token")
113         }
114 }
115
116 func TestTokenPassThruDifferentHost(t *testing.T) {
117         handler := new(tokenPassThruHandler)
118         ts := httptest.NewServer(handler)
119         defer ts.Close()
120
121         tsredirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
122                 if r.RequestURI == "/v2/" {
123                         w.Header().Set("WWW-Authenticate", `Bearer realm="foorealm"`)
124                         w.WriteHeader(401)
125                         return
126                 }
127                 http.Redirect(w, r, ts.URL+r.URL.Path, http.StatusMovedPermanently)
128         }))
129         defer tsredirect.Close()
130
131         testTokenPassThru(t, tsredirect)
132
133         if !handler.reached {
134                 t.Fatal("Handler not reached")
135         }
136         if handler.gotToken {
137                 t.Fatal("Redirect should not forward Authorization header to another host")
138         }
139 }
140
141 // testDirectory creates a new temporary directory and returns its path.
142 // The contents of directory at path `templateDir` is copied into the
143 // new directory.
144 func testDirectory(templateDir string) (dir string, err error) {
145         testID := stringid.GenerateNonCryptoID()[:4]
146         prefix := fmt.Sprintf("docker-test%s-%s-", testID, getCallerName(2))
147         if prefix == "" {
148                 prefix = "docker-test-"
149         }
150         dir, err = ioutil.TempDir("", prefix)
151         if err = os.Remove(dir); err != nil {
152                 return
153         }
154         if templateDir != "" {
155                 if err = archive.NewDefaultArchiver().CopyWithTar(templateDir, dir); err != nil {
156                         return
157                 }
158         }
159         return
160 }
161
162 // getCallerName introspects the call stack and returns the name of the
163 // function `depth` levels down in the stack.
164 func getCallerName(depth int) string {
165         // Use the caller function name as a prefix.
166         // This helps trace temp directories back to their test.
167         pc, _, _, _ := runtime.Caller(depth + 1)
168         callerLongName := runtime.FuncForPC(pc).Name()
169         parts := strings.Split(callerLongName, ".")
170         callerShortName := parts[len(parts)-1]
171         return callerShortName
172 }