forked from TrueCloudLab/distribution
237 lines
6.4 KiB
Go
237 lines
6.4 KiB
Go
|
package registry
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"net/http/httptest"
|
||
|
"net/http/httputil"
|
||
|
"net/url"
|
||
|
"os"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/Sirupsen/logrus"
|
||
|
_ "github.com/docker/docker-registry/storagedriver/inmemory"
|
||
|
|
||
|
"github.com/gorilla/handlers"
|
||
|
|
||
|
"github.com/docker/docker-registry/common/testutil"
|
||
|
"github.com/docker/docker-registry/configuration"
|
||
|
"github.com/docker/docker-registry/digest"
|
||
|
)
|
||
|
|
||
|
// TestLayerAPI conducts a full of the of the layer api.
|
||
|
func TestLayerAPI(t *testing.T) {
|
||
|
// TODO(stevvooe): This test code is complete junk but it should cover the
|
||
|
// complete flow. This must be broken down and checked against the
|
||
|
// specification *before* we submit the final to docker core.
|
||
|
|
||
|
config := configuration.Configuration{
|
||
|
Storage: configuration.Storage{
|
||
|
"inmemory": configuration.Parameters{},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
app := NewApp(config)
|
||
|
server := httptest.NewServer(handlers.CombinedLoggingHandler(os.Stderr, app))
|
||
|
router := v2APIRouter()
|
||
|
|
||
|
u, err := url.Parse(server.URL)
|
||
|
if err != nil {
|
||
|
t.Fatalf("error parsing server url: %v", err)
|
||
|
}
|
||
|
|
||
|
imageName := "foo/bar"
|
||
|
// "build" our layer file
|
||
|
layerFile, tarSumStr, err := testutil.CreateRandomTarFile()
|
||
|
if err != nil {
|
||
|
t.Fatalf("error creating random layer file: %v", err)
|
||
|
}
|
||
|
|
||
|
layerDigest := digest.Digest(tarSumStr)
|
||
|
|
||
|
// -----------------------------------
|
||
|
// Test fetch for non-existent content
|
||
|
r, err := router.GetRoute(routeNameBlob).Host(u.Host).
|
||
|
URL("name", imageName,
|
||
|
"digest", tarSumStr)
|
||
|
|
||
|
resp, err := http.Get(r.String())
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected error fetching non-existent layer: %v", err)
|
||
|
}
|
||
|
|
||
|
switch resp.StatusCode {
|
||
|
case http.StatusNotFound:
|
||
|
break // expected
|
||
|
default:
|
||
|
d, err := httputil.DumpResponse(resp, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected status fetching non-existent layer: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
t.Logf("response:\n%s", string(d))
|
||
|
t.Fatalf("unexpected status fetching non-existent layer: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
// ------------------------------------------
|
||
|
// Test head request for non-existent content
|
||
|
resp, err = http.Head(r.String())
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected error checking head on non-existent layer: %v", err)
|
||
|
}
|
||
|
|
||
|
switch resp.StatusCode {
|
||
|
case http.StatusNotFound:
|
||
|
break // expected
|
||
|
default:
|
||
|
d, err := httputil.DumpResponse(resp, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected status checking head on non-existent layer: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
t.Logf("response:\n%s", string(d))
|
||
|
t.Fatalf("unexpected status checking head on non-existent layer: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
// ------------------------------------------
|
||
|
// Upload a layer
|
||
|
r, err = router.GetRoute(routeNameBlobUpload).Host(u.Host).
|
||
|
URL("name", imageName)
|
||
|
if err != nil {
|
||
|
t.Fatalf("error starting layer upload: %v", err)
|
||
|
}
|
||
|
|
||
|
resp, err = http.Post(r.String(), "", nil)
|
||
|
if err != nil {
|
||
|
t.Fatalf("error starting layer upload: %v", err)
|
||
|
}
|
||
|
|
||
|
if resp.StatusCode != http.StatusAccepted {
|
||
|
d, err := httputil.DumpResponse(resp, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected status starting layer upload: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
t.Logf("response:\n%s", string(d))
|
||
|
t.Fatalf("unexpected status starting layer upload: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
if resp.Header.Get("Location") == "" { // TODO(stevvooe): Need better check here.
|
||
|
t.Fatalf("unexpected Location: %q != %q", resp.Header.Get("Location"), "foo")
|
||
|
}
|
||
|
|
||
|
if resp.Header.Get("Content-Length") != "0" {
|
||
|
t.Fatalf("unexpected content-length: %q != %q", resp.Header.Get("Content-Length"), "0")
|
||
|
}
|
||
|
|
||
|
layerLength, _ := layerFile.Seek(0, os.SEEK_END)
|
||
|
layerFile.Seek(0, os.SEEK_SET)
|
||
|
|
||
|
uploadURLStr := resp.Header.Get("Location")
|
||
|
|
||
|
// TODO(sday): Cancel the layer upload here and restart.
|
||
|
|
||
|
query := url.Values{
|
||
|
"digest": []string{layerDigest.String()},
|
||
|
"length": []string{fmt.Sprint(layerLength)},
|
||
|
}
|
||
|
|
||
|
uploadURL, err := url.Parse(uploadURLStr)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected error parsing url: %v", err)
|
||
|
}
|
||
|
|
||
|
uploadURL.RawQuery = query.Encode()
|
||
|
|
||
|
// Just do a monolithic upload
|
||
|
req, err := http.NewRequest("PUT", uploadURL.String(), layerFile)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected error creating new request: %v", err)
|
||
|
}
|
||
|
|
||
|
resp, err = http.DefaultClient.Do(req)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected error doing put: %v", err)
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
switch resp.StatusCode {
|
||
|
case http.StatusCreated:
|
||
|
break // expected
|
||
|
default:
|
||
|
d, err := httputil.DumpResponse(resp, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected status putting chunk: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
t.Logf("response:\n%s", string(d))
|
||
|
t.Fatalf("unexpected status putting chunk: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
if resp.Header.Get("Location") == "" {
|
||
|
t.Fatalf("unexpected Location: %q", resp.Header.Get("Location"))
|
||
|
}
|
||
|
|
||
|
if resp.Header.Get("Content-Length") != "0" {
|
||
|
t.Fatalf("unexpected content-length: %q != %q", resp.Header.Get("Content-Length"), "0")
|
||
|
}
|
||
|
|
||
|
layerURL := resp.Header.Get("Location")
|
||
|
|
||
|
// ------------------------
|
||
|
// Use a head request to see if the layer exists.
|
||
|
resp, err = http.Head(layerURL)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected error checking head on non-existent layer: %v", err)
|
||
|
}
|
||
|
|
||
|
switch resp.StatusCode {
|
||
|
case http.StatusOK:
|
||
|
break // expected
|
||
|
default:
|
||
|
d, err := httputil.DumpResponse(resp, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected status checking head on layer: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
t.Logf("response:\n%s", string(d))
|
||
|
t.Fatalf("unexpected status checking head on layer: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
logrus.Infof("fetch the layer")
|
||
|
// ----------------
|
||
|
// Fetch the layer!
|
||
|
resp, err = http.Get(layerURL)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected error fetching layer: %v", err)
|
||
|
}
|
||
|
|
||
|
switch resp.StatusCode {
|
||
|
case http.StatusOK:
|
||
|
break // expected
|
||
|
default:
|
||
|
d, err := httputil.DumpResponse(resp, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected status fetching layer: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
t.Logf("response:\n%s", string(d))
|
||
|
t.Fatalf("unexpected status fetching layer: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
// Verify the body
|
||
|
verifier := digest.NewDigestVerifier(layerDigest)
|
||
|
io.Copy(verifier, resp.Body)
|
||
|
|
||
|
if !verifier.Verified() {
|
||
|
d, err := httputil.DumpResponse(resp, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected status checking head on layer ayo!: %v, %v", resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
t.Logf("response:\n%s", string(d))
|
||
|
t.Fatalf("response body did not pass verification")
|
||
|
}
|
||
|
}
|