Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the control flow for manifests and blobs. #1157

Merged
merged 1 commit into from
Oct 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 80 additions & 56 deletions pkg/registry/blobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
digest := req.URL.Query().Get("digest")
contentRange := req.Header.Get("Content-Range")

if req.Method == "HEAD" {
switch req.Method {
case http.MethodHead:
b.lock.Lock()
defer b.lock.Unlock()
b, ok := b.contents[target]
Expand All @@ -88,9 +89,8 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
resp.Header().Set("Docker-Content-Digest", target)
resp.WriteHeader(http.StatusOK)
return nil
}

if req.Method == "GET" {
case http.MethodGet:
b.lock.Lock()
defer b.lock.Unlock()
b, ok := b.contents[target]
Expand All @@ -107,65 +107,81 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
resp.WriteHeader(http.StatusOK)
io.Copy(resp, bytes.NewReader(b))
return nil
}

if req.Method == "POST" && target == "uploads" && digest != "" {
l := &bytes.Buffer{}
io.Copy(l, req.Body)
rd := sha256.Sum256(l.Bytes())
d := "sha256:" + hex.EncodeToString(rd[:])
if d != digest {
case http.MethodPost:
// It is weird that this is "target" instead of "service", but
// that's how the index math works out above.
if target != "uploads" {
return &regError{
Status: http.StatusBadRequest,
Code: "DIGEST_INVALID",
Message: "digest does not match contents",
Code: "METHOD_UNKNOWN",
Message: fmt.Sprintf("POST to /blobs must be followed by /uploads, got %s", target),
}
}

b.lock.Lock()
defer b.lock.Unlock()
b.contents[d] = l.Bytes()
resp.Header().Set("Docker-Content-Digest", d)
resp.WriteHeader(http.StatusCreated)
return nil
}
if digest != "" {
l := &bytes.Buffer{}
io.Copy(l, req.Body)
rd := sha256.Sum256(l.Bytes())
d := "sha256:" + hex.EncodeToString(rd[:])
if d != digest {
return &regError{
Status: http.StatusBadRequest,
Code: "DIGEST_INVALID",
Message: "digest does not match contents",
}
}

b.lock.Lock()
defer b.lock.Unlock()
b.contents[d] = l.Bytes()
resp.Header().Set("Docker-Content-Digest", d)
resp.WriteHeader(http.StatusCreated)
return nil
}

if req.Method == "POST" && target == "uploads" && digest == "" {
id := fmt.Sprint(rand.Int63())
resp.Header().Set("Location", "/"+path.Join("v2", path.Join(elem[1:len(elem)-2]...), "blobs/uploads", id))
resp.Header().Set("Range", "0-0")
resp.WriteHeader(http.StatusAccepted)
return nil
}

if req.Method == "PATCH" && service == "uploads" && contentRange != "" {
start, end := 0, 0
if _, err := fmt.Sscanf(contentRange, "%d-%d", &start, &end); err != nil {
case http.MethodPatch:
if service != "uploads" {
return &regError{
Status: http.StatusRequestedRangeNotSatisfiable,
Code: "BLOB_UPLOAD_UNKNOWN",
Message: "We don't understand your Content-Range",
Status: http.StatusBadRequest,
Code: "METHOD_UNKNOWN",
Message: fmt.Sprintf("PATCH to /blobs must be followed by /uploads, got %s", service),
}
}
b.lock.Lock()
defer b.lock.Unlock()
if start != len(b.uploads[target]) {
return &regError{
Status: http.StatusRequestedRangeNotSatisfiable,
Code: "BLOB_UPLOAD_UNKNOWN",
Message: "Your content range doesn't match what we have",

if contentRange != "" {
start, end := 0, 0
if _, err := fmt.Sscanf(contentRange, "%d-%d", &start, &end); err != nil {
return &regError{
Status: http.StatusRequestedRangeNotSatisfiable,
Code: "BLOB_UPLOAD_UNKNOWN",
Message: "We don't understand your Content-Range",
}
}
b.lock.Lock()
defer b.lock.Unlock()
if start != len(b.uploads[target]) {
return &regError{
Status: http.StatusRequestedRangeNotSatisfiable,
Code: "BLOB_UPLOAD_UNKNOWN",
Message: "Your content range doesn't match what we have",
}
}
l := bytes.NewBuffer(b.uploads[target])
io.Copy(l, req.Body)
b.uploads[target] = l.Bytes()
resp.Header().Set("Location", "/"+path.Join("v2", path.Join(elem[1:len(elem)-3]...), "blobs/uploads", target))
resp.Header().Set("Range", fmt.Sprintf("0-%d", len(l.Bytes())-1))
resp.WriteHeader(http.StatusNoContent)
return nil
}
l := bytes.NewBuffer(b.uploads[target])
io.Copy(l, req.Body)
b.uploads[target] = l.Bytes()
resp.Header().Set("Location", "/"+path.Join("v2", path.Join(elem[1:len(elem)-3]...), "blobs/uploads", target))
resp.Header().Set("Range", fmt.Sprintf("0-%d", len(l.Bytes())-1))
resp.WriteHeader(http.StatusNoContent)
return nil
}

if req.Method == "PATCH" && service == "uploads" && contentRange == "" {
b.lock.Lock()
defer b.lock.Unlock()
if _, ok := b.uploads[target]; ok {
Expand All @@ -184,17 +200,24 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
resp.Header().Set("Range", fmt.Sprintf("0-%d", len(l.Bytes())-1))
resp.WriteHeader(http.StatusNoContent)
return nil
}

if req.Method == "PUT" && service == "uploads" && digest == "" {
return &regError{
Status: http.StatusBadRequest,
Code: "DIGEST_INVALID",
Message: "digest not specified",
case http.MethodPut:
if service != "uploads" {
return &regError{
Status: http.StatusBadRequest,
Code: "METHOD_UNKNOWN",
Message: fmt.Sprintf("PUT to /blobs must be followed by /uploads, got %s", service),
}
}

if digest == "" {
return &regError{
Status: http.StatusBadRequest,
Code: "DIGEST_INVALID",
Message: "digest not specified",
}
}
}

if req.Method == "PUT" && service == "uploads" && digest != "" {
b.lock.Lock()
defer b.lock.Unlock()
l := bytes.NewBuffer(b.uploads[target])
Expand All @@ -214,11 +237,12 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
resp.Header().Set("Docker-Content-Digest", d)
resp.WriteHeader(http.StatusCreated)
return nil
}

return &regError{
Status: http.StatusBadRequest,
Code: "METHOD_UNKNOWN",
Message: "We don't understand your method + url",
default:
return &regError{
Status: http.StatusBadRequest,
Code: "METHOD_UNKNOWN",
Message: "We don't understand your method + url",
}
}
}
23 changes: 11 additions & 12 deletions pkg/registry/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regErro
target := elem[len(elem)-1]
repo := strings.Join(elem[1:len(elem)-2], "/")

if req.Method == "GET" {
switch req.Method {
case http.MethodGet:
m.lock.Lock()
defer m.lock.Unlock()

Expand Down Expand Up @@ -117,9 +118,8 @@ func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regErro
resp.WriteHeader(http.StatusOK)
io.Copy(resp, bytes.NewReader(m.blob))
return nil
}

if req.Method == "HEAD" {
case http.MethodHead:
m.lock.Lock()
defer m.lock.Unlock()
if _, ok := m.manifests[repo]; !ok {
Expand All @@ -144,9 +144,8 @@ func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regErro
resp.Header().Set("Content-Length", fmt.Sprint(len(m.blob)))
resp.WriteHeader(http.StatusOK)
return nil
}

if req.Method == "PUT" {
case http.MethodPut:
m.lock.Lock()
defer m.lock.Unlock()
if _, ok := m.manifests[repo]; !ok {
Expand Down Expand Up @@ -200,9 +199,8 @@ func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regErro
resp.Header().Set("Docker-Content-Digest", digest)
resp.WriteHeader(http.StatusCreated)
return nil
}

if req.Method == "DELETE" {
case http.MethodDelete:
m.lock.Lock()
defer m.lock.Unlock()
if _, ok := m.manifests[repo]; !ok {
Expand All @@ -225,12 +223,13 @@ func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regErro
delete(m.manifests[repo], target)
resp.WriteHeader(http.StatusAccepted)
return nil
}

return &regError{
Status: http.StatusBadRequest,
Code: "METHOD_UNKNOWN",
Message: "We don't understand your method + url",
default:
return &regError{
Status: http.StatusBadRequest,
Code: "METHOD_UNKNOWN",
Message: "We don't understand your method + url",
}
}
}

Expand Down