package dockerregistry import ( "fmt" "net/http" "net/http/httptest" "net/url" "os" "strings" "testing" "time" ) // tests of running registries are done in the integration client test func TestHTTPFallback(t *testing.T) { called := make(chan struct{}, 2) var uri *url.URL server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called <- struct{}{} if strings.HasSuffix(r.URL.Path, "/tags") { w.WriteHeader(http.StatusNotFound) return } w.Header().Set("X-Docker-Endpoints", uri.Host) w.WriteHeader(http.StatusOK) })) uri, _ = url.Parse(server.URL) conn, err := NewClient(10*time.Second).Connect(uri.Host, true) if err != nil { t.Fatal(err) } v2 := false conn.(*connection).isV2 = &v2 if _, err := conn.ImageTags("foo", "bar"); !IsRepositoryNotFound(err) { t.Error(err) } <-called <-called } func TestV2Check(t *testing.T) { called := make(chan struct{}, 2) var uri *url.URL server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called <- struct{}{} if strings.HasSuffix(r.URL.Path, "/v2/") { w.Header().Set("Docker-Distribution-API-Version", "registry/2.0") w.WriteHeader(http.StatusOK) return } if strings.HasSuffix(r.URL.Path, "/tags/list") { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, `{"tags":["tag1","image1"]}`) return } t.Fatalf("unexpected request: %s %s", r.Method, r.URL.RequestURI()) })) uri, _ = url.Parse(server.URL) conn, err := NewClient(10*time.Second).Connect(uri.Host, true) if err != nil { t.Fatal(err) } tags, err := conn.ImageTags("foo", "bar") if err != nil { t.Fatal(err) } if tags["tag1"] != "tag1" { t.Errorf("unexpected tags: %#v", tags) } if tags["image1"] != "image1" { t.Errorf("unexpected tags: %#v", tags) } <-called <-called } func TestV2CheckNoDistributionHeader(t *testing.T) { called := make(chan struct{}, 3) var uri *url.URL server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called <- struct{}{} if strings.HasSuffix(r.URL.Path, "/v2/") { w.Header().Set("Docker-Distribution-API-Version", "") w.WriteHeader(http.StatusOK) return } w.Header().Set("X-Docker-Endpoints", uri.Host) // Images if strings.HasSuffix(r.URL.Path, "/images") { return } // ImageTags if strings.HasSuffix(r.URL.Path, "/tags") { fmt.Fprintln(w, `{"tag1":"image1"}`) return } // get tag->image id if strings.HasSuffix(r.URL.Path, "latest") { fmt.Fprintln(w, `"image1"`) return } // get image json if strings.HasSuffix(r.URL.Path, "json") { fmt.Fprintln(w, `{"id":"image1"}`) return } t.Fatalf("unexpected request: %s %s", r.Method, r.URL.RequestURI()) })) uri, _ = url.Parse(server.URL) conn, err := NewClient(10*time.Second).Connect(uri.Host, true) if err != nil { t.Fatal(err) } tags, err := conn.ImageTags("foo", "bar") if err != nil { t.Fatal(err) } if tags["tag1"] != "image1" { t.Errorf("unexpected tags: %#v", tags) } <-called <-called <-called } func TestInsecureHTTPS(t *testing.T) { called := make(chan struct{}, 2) var uri *url.URL server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called <- struct{}{} if strings.HasSuffix(r.URL.Path, "/tags") { w.WriteHeader(http.StatusNotFound) return } w.Header().Set("X-Docker-Endpoints", uri.Host) w.WriteHeader(http.StatusOK) })) uri, _ = url.Parse(server.URL) conn, err := NewClient(10*time.Second).Connect(uri.Host, true) if err != nil { t.Fatal(err) } v2 := false conn.(*connection).isV2 = &v2 if _, err := conn.ImageTags("foo", "bar"); !IsRepositoryNotFound(err) { t.Error(err) } <-called <-called } func TestProxy(t *testing.T) { called := make(chan struct{}, 2) var uri *url.URL server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called <- struct{}{} if strings.HasSuffix(r.URL.Path, "/tags") { w.WriteHeader(http.StatusNotFound) return } w.Header().Set("X-Docker-Endpoints", uri.Host) w.WriteHeader(http.StatusOK) })) os.Setenv("HTTP_PROXY", "http.proxy.tld") os.Setenv("HTTPS_PROXY", "secure.proxy.tld") os.Setenv("NO_PROXY", "") uri, _ = url.Parse(server.URL) conn, err := NewClient(10*time.Second).Connect(uri.Host, true) if err != nil { t.Fatal(err) } v2 := false conn.(*connection).isV2 = &v2 if _, err := conn.ImageTags("foo", "bar"); !IsRepositoryNotFound(err) { t.Error(err) } <-called <-called } func TestTokenExpiration(t *testing.T) { var uri *url.URL lastToken := "" tokenIndex := 0 validToken := "" server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-Docker-Token") == "true" { tokenIndex++ lastToken = fmt.Sprintf("token%d", tokenIndex) validToken = lastToken w.Header().Set("X-Docker-Token", lastToken) w.Header().Set("X-Docker-Endpoints", uri.Host) return } auth := r.Header.Get("Authorization") parts := strings.Split(auth, " ") token := parts[1] if token != validToken { w.WriteHeader(http.StatusUnauthorized) return } w.WriteHeader(http.StatusOK) // ImageTags if strings.HasSuffix(r.URL.Path, "/tags") { fmt.Fprintln(w, `{"tag1":"image1"}`) } // get tag->image id if strings.HasSuffix(r.URL.Path, "latest") { fmt.Fprintln(w, `"image1"`) } // get image json if strings.HasSuffix(r.URL.Path, "json") { fmt.Fprintln(w, `{"id":"image1"}`) } })) uri, _ = url.Parse(server.URL) conn, err := NewClient(10*time.Second).Connect(uri.Host, true) if err != nil { t.Fatal(err) } v2 := false conn.(*connection).isV2 = &v2 if _, err := conn.ImageTags("foo", "bar"); err != nil { t.Fatal(err) } // expire token, should get an error validToken = "" if _, err := conn.ImageTags("foo", "bar"); err == nil { t.Fatal("expected error") } // retry, should get a new token if _, err := conn.ImageTags("foo", "bar"); err != nil { t.Fatal(err) } // expire token, should get an error validToken = "" if _, err := conn.ImageByTag("foo", "bar", "latest"); err == nil { t.Fatal("expected error") } // retry, should get a new token if _, err := conn.ImageByTag("foo", "bar", "latest"); err != nil { t.Fatal(err) } // expire token, should get an error validToken = "" if _, err := conn.ImageByID("foo", "bar", "image1"); err == nil { t.Fatal("expected error") } // retry, should get a new token if _, err := conn.ImageByID("foo", "bar", "image1"); err != nil { t.Fatal(err) } } func TestGetTagFallback(t *testing.T) { var uri *url.URL server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Docker-Endpoints", uri.Host) // get all tags if strings.HasSuffix(r.URL.Path, "/tags") { fmt.Fprintln(w, `{"tag1":"image1", "test":"image2"}`) w.WriteHeader(http.StatusOK) return } if strings.HasSuffix(r.URL.Path, "/json") { fmt.Fprintln(w, `{"ID":"image2"}`) w.WriteHeader(http.StatusOK) return } w.WriteHeader(http.StatusNotFound) })) uri, _ = url.Parse(server.URL) conn, err := NewClient(10*time.Second).Connect(uri.Host, true) c := conn.(*connection) if err != nil { t.Fatal(err) } repo := &v1repository{ name: "testrepo", endpoint: *uri, } // Case when tag is found img, err := repo.getTaggedImage(c, "test", "") if err != nil { t.Errorf("unexpected error getting tag: %v", err) return } if img.ID != "image2" { t.Errorf("unexpected image for tag: %v", img) } // Case when tag is not found img, err = repo.getTaggedImage(c, "test2", "") if err == nil { t.Errorf("expected error") } }