func TestRoundTrip(t *testing.T) { server := test.NewServer( &test.RequestHandlerMapping{ Method: "GET", Pattern: "/", Handler: test.Handler(nil), }) transport := NewTransport(&http.Transport{}, &simpleModifier{}) client := &http.Client{ Transport: transport, } req, err := http.NewRequest("GET", fmt.Sprintf("%s/", server.URL), nil) if err != nil { t.Fatalf("failed to create request: %v", err) } if _, err := client.Do(req); err != nil { t.Fatalf("failed to send request: %s", err) } header := req.Header.Get("Authorization") if header != "token" { t.Errorf("unexpected header: %s != %s", header, "token") } }
func TestListTag(t *testing.T) { handler := test.Handler(&test.Response{ Headers: map[string]string{ "Content-Type": "application/json", }, Body: []byte(fmt.Sprintf("{\"name\": \"%s\",\"tags\": [\"%s\"]}", repository, tag)), }) server := test.NewServer( &test.RequestHandlerMapping{ Method: "GET", Pattern: fmt.Sprintf("/v2/%s/tags/list", repository), Handler: handler, }) defer server.Close() client, err := newRepository(server.URL) if err != nil { t.Fatalf("failed to create client for repository: %v", err) } tags, err := client.ListTag() if err != nil { t.Fatalf("failed to list tags: %v", err) } if len(tags) != 1 { t.Fatalf("unexpected length of tags: %d != %d", len(tags), 1) } if tags[0] != tag { t.Errorf("unexpected tag: %s != %s", tags[0], tag) } }
func TestPushManifest(t *testing.T) { handler := test.Handler(&test.Response{ StatusCode: http.StatusCreated, Headers: map[string]string{ "Content-Length": "0", "Docker-Content-Digest": digest, "Location": "", }, }) server := test.NewServer( &test.RequestHandlerMapping{ Method: "PUT", Pattern: fmt.Sprintf("/v2/%s/manifests/%s", repository, tag), Handler: handler, }) defer server.Close() client, err := newRepository(server.URL) if err != nil { t.Fatalf("failed to create client for repository: %v", err) } d, err := client.PushManifest(tag, mediaType, manifest) if err != nil { t.Fatalf("failed to pull manifest: %v", err) } if d != digest { t.Errorf("unexpected digest of manifest: %s != %s", d, digest) } }
func TestDeleteTag(t *testing.T) { manifestExistHandler := test.Handler(&test.Response{ Headers: map[string]string{ "Docker-Content-Digest": digest, "Content-Type": mediaType, }, }) deleteManifestandler := test.Handler(&test.Response{ StatusCode: http.StatusAccepted, }) server := test.NewServer( &test.RequestHandlerMapping{ Method: "HEAD", Pattern: fmt.Sprintf("/v2/%s/manifests/", repository), Handler: manifestExistHandler, }, &test.RequestHandlerMapping{ Method: "DELETE", Pattern: fmt.Sprintf("/v2/%s/manifests/%s", repository, digest), Handler: deleteManifestandler, }) defer server.Close() client, err := newRepository(server.URL) if err != nil { t.Fatalf("failed to create client for repository: %v", err) } if err = client.DeleteTag(tag); err != nil { t.Fatalf("failed to delete tag: %v", err) } }
func TestBlobExist(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path dgt := path[strings.LastIndex(path, "/")+1 : len(path)] if dgt == digest { w.Header().Add(http.CanonicalHeaderKey("Content-Length"), strconv.Itoa(len(blob))) w.Header().Add(http.CanonicalHeaderKey("Docker-Content-Digest"), digest) w.Header().Add(http.CanonicalHeaderKey("Content-Type"), "application/octet-stream") return } w.WriteHeader(http.StatusNotFound) } server := test.NewServer( &test.RequestHandlerMapping{ Method: "HEAD", Pattern: fmt.Sprintf("/v2/%s/blobs/", repository), Handler: handler, }) defer server.Close() client, err := newRepository(server.URL) if err != nil { err = parseError(err) t.Fatalf("failed to create client for repository: %v", err) } exist, err := client.BlobExist(digest) if err != nil { t.Fatalf("failed to check the existence of blob: %v", err) } if !exist { t.Errorf("blob should exist on registry, but it does not exist") } exist, err = client.BlobExist("invalid_digest") if err != nil { t.Fatalf("failed to check the existence of blob: %v", err) } if exist { t.Errorf("blob should not exist on registry, but it exists") } }
func TestPing(t *testing.T) { server := test.NewServer( &test.RequestHandlerMapping{ Method: "GET", Pattern: "/v2/", Handler: test.Handler(nil), }) defer server.Close() client, err := newRegistryClient(server.URL) if err != nil { t.Fatalf("failed to create client for registry: %v", err) } if err = client.Ping(); err != nil { t.Errorf("failed to ping registry: %v", err) } }
func TestManifestExist(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path tg := path[strings.LastIndex(path, "/")+1 : len(path)] if tg == tag { w.Header().Add(http.CanonicalHeaderKey("Docker-Content-Digest"), digest) w.Header().Add(http.CanonicalHeaderKey("Content-Type"), mediaType) return } w.WriteHeader(http.StatusNotFound) } server := test.NewServer( &test.RequestHandlerMapping{ Method: "HEAD", Pattern: fmt.Sprintf("/v2/%s/manifests/%s", repository, tag), Handler: handler, }) defer server.Close() client, err := newRepository(server.URL) if err != nil { t.Fatalf("failed to create client for repository: %v", err) } d, exist, err := client.ManifestExist(tag) if err != nil { t.Fatalf("failed to check the existence of manifest: %v", err) } if !exist || d != digest { t.Errorf("manifest should exist on registry, but it does not exist") } _, exist, err = client.ManifestExist("invalid_tag") if err != nil { t.Fatalf("failed to check the existence of manifest: %v", err) } if exist { t.Errorf("manifest should not exist on registry, but it exists") } }
func TestPushBlob(t *testing.T) { location := "" initUploadHandler := func(w http.ResponseWriter, r *http.Request) { w.Header().Add(http.CanonicalHeaderKey("Content-Length"), "0") w.Header().Add(http.CanonicalHeaderKey("Location"), location) w.Header().Add(http.CanonicalHeaderKey("Range"), "0-0") w.Header().Add(http.CanonicalHeaderKey("Docker-Upload-UUID"), uuid) w.WriteHeader(http.StatusAccepted) } monolithicUploadHandler := test.Handler(&test.Response{ StatusCode: http.StatusCreated, Headers: map[string]string{ "Content-Length": "0", "Location": fmt.Sprintf("/v2/%s/blobs/%s", repository, digest), "Docker-Content-Digest": digest, }, }) server := test.NewServer( &test.RequestHandlerMapping{ Method: "POST", Pattern: fmt.Sprintf("/v2/%s/blobs/uploads/", repository), Handler: initUploadHandler, }, &test.RequestHandlerMapping{ Method: "PUT", Pattern: fmt.Sprintf("/v2/%s/blobs/uploads/%s", repository, uuid), Handler: monolithicUploadHandler, }) defer server.Close() location = fmt.Sprintf("%s/v2/%s/blobs/uploads/%s", server.URL, repository, uuid) client, err := newRepository(server.URL) if err != nil { t.Fatalf("failed to create client for repository: %v", err) } if err = client.PushBlob(digest, int64(len(blob)), bytes.NewReader(blob)); err != nil { t.Fatalf("failed to push blob: %v", err) } }
func TestNewAuthorizerStore(t *testing.T) { handler := test.Handler(&test.Response{ StatusCode: http.StatusUnauthorized, Headers: map[string]string{ "Www-Authenticate": "Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\"", }, }) server := test.NewServer(&test.RequestHandlerMapping{ Method: "GET", Pattern: "/v2/", Handler: handler, }) defer server.Close() _, err := NewAuthorizerStore(server.URL, false, nil) if err != nil { t.Fatalf("failed to create authorizer store: %v", err) } }
func TestPullBlob(t *testing.T) { handler := test.Handler(&test.Response{ Headers: map[string]string{ "Content-Length": strconv.Itoa(len(blob)), "Docker-Content-Digest": digest, "Content-Type": "application/octet-stream", }, Body: blob, }) server := test.NewServer( &test.RequestHandlerMapping{ Method: "GET", Pattern: fmt.Sprintf("/v2/%s/blobs/%s", repository, digest), Handler: handler, }) defer server.Close() client, err := newRepository(server.URL) if err != nil { t.Fatalf("failed to create client for repository: %v", err) } size, reader, err := client.PullBlob(digest) if err != nil { t.Fatalf("failed to pull blob: %v", err) } if size != int64(len(blob)) { t.Errorf("unexpected size of blob: %d != %d", size, len(blob)) } b, err := ioutil.ReadAll(reader) if err != nil { t.Fatalf("failed to read from reader: %v", err) } if bytes.Compare(b, blob) != 0 { t.Errorf("unexpected blob: %s != %s", string(b), string(blob)) } }
func TestPullManifest(t *testing.T) { handler := test.Handler(&test.Response{ Headers: map[string]string{ "Docker-Content-Digest": digest, "Content-Type": mediaType, }, Body: manifest, }) server := test.NewServer( &test.RequestHandlerMapping{ Method: "GET", Pattern: fmt.Sprintf("/v2/%s/manifests/%s", repository, tag), Handler: handler, }) defer server.Close() client, err := newRepository(server.URL) if err != nil { t.Fatalf("failed to create client for repository: %v", err) } d, md, payload, err := client.PullManifest(tag, []string{mediaType}) if err != nil { t.Fatalf("failed to pull manifest: %v", err) } if d != digest { t.Errorf("unexpected digest of manifest: %s != %s", d, digest) } if md != mediaType { t.Errorf("unexpected media type of manifest: %s != %s", md, mediaType) } if bytes.Compare(payload, manifest) != 0 { t.Errorf("unexpected manifest: %s != %s", string(payload), string(manifest)) } }
func TestDeleteBlob(t *testing.T) { handler := test.Handler(&test.Response{ StatusCode: http.StatusAccepted, }) server := test.NewServer( &test.RequestHandlerMapping{ Method: "DELETE", Pattern: fmt.Sprintf("/v2/%s/blobs/%s", repository, digest), Handler: handler, }) defer server.Close() client, err := newRepository(server.URL) if err != nil { t.Fatalf("failed to create client for repository: %v", err) } if err = client.DeleteBlob(digest); err != nil { t.Fatalf("failed to delete blob: %v", err) } }
func TestAuthorizeOfStandardTokenAuthorizer(t *testing.T) { handler := test.Handler(&test.Response{ Body: []byte(` { "token":"token", "expires_in":300, "issued_at":"2016-08-17T23:17:58+08:00" } `), }) server := test.NewServer(&test.RequestHandlerMapping{ Method: "GET", Pattern: "/token", Handler: handler, }) defer server.Close() authorizer := NewStandardTokenAuthorizer(nil, false, "repository", "library/ubuntu", "pull") req, err := http.NewRequest("GET", "http://registry", nil) if err != nil { t.Fatalf("failed to create request: %v", err) } params := map[string]string{ "realm": server.URL + "/token", } if err := authorizer.Authorize(req, params); err != nil { t.Fatalf("failed to authorize request: %v", err) } tk := req.Header.Get("Authorization") if tk != "Bearer token" { t.Errorf("unexpected token: %s != %s", tk, "Bearer token") } }
func TestCatalog(t *testing.T) { repositories := make([]string, 0, 1001) for i := 0; i < 1001; i++ { repositories = append(repositories, strconv.Itoa(i)) } handler := func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() last := q.Get("last") n, err := strconv.Atoi(q.Get("n")) if err != nil || n <= 0 { n = 1000 } length := len(repositories) begin := length if len(last) == 0 { begin = 0 } else { for i, repository := range repositories { if repository == last { begin = i + 1 break } } } end := begin + n if end > length { end = length } w.Header().Set(http.CanonicalHeaderKey("Content-Type"), "application/json") if end < length { u, err := url.Parse("/v2/_catalog") if err != nil { w.WriteHeader(http.StatusInternalServerError) return } values := u.Query() values.Add("last", repositories[end-1]) values.Add("n", strconv.Itoa(n)) u.RawQuery = values.Encode() link := fmt.Sprintf("<%s>; rel=\"next\"", u.String()) w.Header().Set(http.CanonicalHeaderKey("link"), link) } repos := struct { Repositories []string `json:"repositories"` }{ Repositories: []string{}, } if begin < length { repos.Repositories = repositories[begin:end] } b, err := json.Marshal(repos) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } w.Write(b) } server := test.NewServer( &test.RequestHandlerMapping{ Method: "GET", Pattern: "/v2/_catalog", Handler: handler, }) defer server.Close() client, err := newRegistryClient(server.URL) if err != nil { t.Fatalf("failed to create client for registry: %v", err) } repos, err := client.Catalog() if err != nil { t.Fatalf("failed to catalog repositories: %v", err) } if len(repos) != len(repositories) { t.Errorf("unexpected length of repositories: %d != %d", len(repos), len(repositories)) } }