forked from TrueCloudLab/distribution
Merge pull request #2921 from dmathieu/repository-serve-blob
Implement Repository ServeBlob
This commit is contained in:
commit
90dfea7952
2 changed files with 123 additions and 1 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue