Merge pull request #2921 from dmathieu/repository-serve-blob

Implement Repository ServeBlob
This commit is contained in:
Ryan Abrams 2019-06-25 19:07:38 -07:00 committed by GitHub
commit 90dfea7952
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 123 additions and 1 deletions

View file

@ -667,7 +667,28 @@ func (bs *blobs) Open(ctx context.Context, dgst digest.Digest) (distribution.Rea
} }
func (bs *blobs) ServeBlob(ctx context.Context, w http.ResponseWriter, r *http.Request, dgst digest.Digest) error { func (bs *blobs) ServeBlob(ctx context.Context, w http.ResponseWriter, r *http.Request, dgst digest.Digest) error {
panic("not implemented") desc, err := bs.statter.Stat(ctx, dgst)
if err != nil {
return err
}
w.Header().Set("Content-Length", strconv.FormatInt(desc.Size, 10))
w.Header().Set("Content-Type", desc.MediaType)
w.Header().Set("Docker-Content-Digest", dgst.String())
w.Header().Set("Etag", dgst.String())
if r.Method == http.MethodHead {
return nil
}
blob, err := bs.Open(ctx, dgst)
if err != nil {
return err
}
defer blob.Close()
_, err = io.CopyN(w, blob, desc.Size)
return err
} }
func (bs *blobs) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) { func (bs *blobs) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {

View file

@ -6,6 +6,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -57,6 +58,7 @@ func addTestFetch(repo string, dgst digest.Digest, content []byte, m *testutil.R
Body: content, Body: content,
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"Content-Length": {fmt.Sprint(len(content))}, "Content-Length": {fmt.Sprint(len(content))},
"Content-Type": {"application/octet-stream"},
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, "Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
}), }),
}, },
@ -71,6 +73,7 @@ func addTestFetch(repo string, dgst digest.Digest, content []byte, m *testutil.R
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"Content-Length": {fmt.Sprint(len(content))}, "Content-Length": {fmt.Sprint(len(content))},
"Content-Type": {"application/octet-stream"},
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, "Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
}), }),
}, },
@ -99,6 +102,104 @@ func addTestCatalog(route string, content []byte, link string, m *testutil.Reque
}) })
} }
func TestBlobServeBlob(t *testing.T) {
dgst, blob := newRandomBlob(1024)
var m testutil.RequestResponseMap
addTestFetch("test.example.com/repo1", dgst, blob, &m)
e, c := testServer(m)
defer c()
ctx := context.Background()
repo, _ := reference.WithName("test.example.com/repo1")
r, err := NewRepository(repo, e, nil)
if err != nil {
t.Fatal(err)
}
l := r.Blobs(ctx)
resp := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil)
err = l.ServeBlob(ctx, resp, req, dgst)
if err != nil {
t.Errorf("Error serving blob: %s", err.Error())
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("Error reading response body: %s", err.Error())
}
if string(body) != string(blob) {
t.Errorf("Unexpected response body. Got %q, expected %q", string(body), string(blob))
}
expectedHeaders := []struct {
Name string
Value string
}{
{Name: "Content-Length", Value: "1024"},
{Name: "Content-Type", Value: "application/octet-stream"},
{Name: "Docker-Content-Digest", Value: dgst.String()},
{Name: "Etag", Value: dgst.String()},
}
for _, h := range expectedHeaders {
if resp.Header().Get(h.Name) != h.Value {
t.Errorf("Unexpected %s. Got %s, expected %s", h.Name, resp.Header().Get(h.Name), h.Value)
}
}
}
func TestBlobServeBlobHEAD(t *testing.T) {
dgst, blob := newRandomBlob(1024)
var m testutil.RequestResponseMap
addTestFetch("test.example.com/repo1", dgst, blob, &m)
e, c := testServer(m)
defer c()
ctx := context.Background()
repo, _ := reference.WithName("test.example.com/repo1")
r, err := NewRepository(repo, e, nil)
if err != nil {
t.Fatal(err)
}
l := r.Blobs(ctx)
resp := httptest.NewRecorder()
req := httptest.NewRequest("HEAD", "/", nil)
err = l.ServeBlob(ctx, resp, req, dgst)
if err != nil {
t.Errorf("Error serving blob: %s", err.Error())
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("Error reading response body: %s", err.Error())
}
if string(body) != "" {
t.Errorf("Unexpected response body. Got %q, expected %q", string(body), "")
}
expectedHeaders := []struct {
Name string
Value string
}{
{Name: "Content-Length", Value: "1024"},
{Name: "Content-Type", Value: "application/octet-stream"},
{Name: "Docker-Content-Digest", Value: dgst.String()},
{Name: "Etag", Value: dgst.String()},
}
for _, h := range expectedHeaders {
if resp.Header().Get(h.Name) != h.Value {
t.Errorf("Unexpected %s. Got %s, expected %s", h.Name, resp.Header().Get(h.Name), h.Value)
}
}
}
func TestBlobDelete(t *testing.T) { func TestBlobDelete(t *testing.T) {
dgst, _ := newRandomBlob(1024) dgst, _ := newRandomBlob(1024)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap