-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Josh Hawn
committed
Aug 28, 2014
1 parent
7c9996c
commit c003172
Showing
9 changed files
with
614 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"net/http" | ||
"time" | ||
|
||
registryServer "github.com/docker/docker/registry/v2/server" | ||
) | ||
|
||
func main() { | ||
server := &http.Server{ | ||
Addr: ":8080", | ||
Handler: registryServer.NewRegistryHandler(), | ||
ReadTimeout: 10 * time.Second, | ||
WriteTimeout: 10 * time.Second, | ||
MaxHeaderBytes: 1 << 20, | ||
} | ||
|
||
log.Fatal(server.ListenAndServe()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package routes | ||
|
||
import ( | ||
"github.com/gorilla/mux" | ||
) | ||
|
||
const ( | ||
ManifestsRouteName = "manifests" | ||
TagsRouteName = "tags" | ||
DownloadBlobRouteName = "downloadBlob" | ||
UploadBlobRouteName = "uploadBlob" | ||
MountBlobRouteName = "mountBlob" | ||
) | ||
|
||
func NewRegistryRouter() *mux.Router { | ||
router := mux.NewRouter() | ||
|
||
v2Route := router.PathPrefix("/v2/").Subrouter() | ||
|
||
// Image Manifests | ||
v2Route.Path("/manifest/{imagename:[a-z0-9-._/]+}/{tagname:[a-zA-Z0-9-._]+}").Name(ManifestsRouteName) | ||
|
||
// List Image Tags | ||
v2Route.Path("/tags/{imagename:[a-z0-9-._/]+}").Name(TagsRouteName) | ||
|
||
// Download a blob | ||
v2Route.Path("/blob/{imagename:[a-z0-9-._/]+}/{sumtype:[a-z0-9_+-]+}/{sum:[a-fA-F0-9]{4,}}").Name(DownloadBlobRouteName) | ||
|
||
// Upload a blob | ||
v2Route.Path("/blob/{imagename:[a-z0-9-._/]+}/{sumtype:[a-z0-9_+-]+}").Name(UploadBlobRouteName) | ||
|
||
// Mounting a blob in an image | ||
v2Route.Path("/mountblob/{imagename:[a-z0-9-._/]+}/{sumtype:[a-z0-9_+-]+}/{sum:[a-fA-F0-9]{4,}}").Name(MountBlobRouteName) | ||
|
||
return router | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package server | ||
|
||
import ( | ||
"encoding/json" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"os" | ||
"path" | ||
"strings" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
func getBlob(w http.ResponseWriter, r *http.Request) { | ||
vars := mux.Vars(r) | ||
log.Printf("Get Blob: %#v\n", vars) | ||
|
||
_, ok := vars["imagename"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
sumType, ok := vars["sumtype"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
sum, ok := vars["sum"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
prefix1, prefix2 := sum[:2], sum[2:4] | ||
|
||
blobPath := path.Join(blobsDirectory, sumType, prefix1, prefix2, sum) | ||
blobFile, err := os.Open(blobPath) | ||
if err != nil { | ||
errStatus := 500 | ||
if os.IsNotExist(err) { | ||
errStatus = 404 | ||
} | ||
log.Printf("unable to open blob file %q: %s\n", blobPath, err) | ||
w.WriteHeader(errStatus) | ||
return | ||
} | ||
|
||
bytesCopied, err := io.Copy(w, blobFile) | ||
if err != nil { | ||
log.Printf("unable to copy blob file %q: %s\n", blobPath, err) | ||
w.WriteHeader(500) | ||
} else { | ||
log.Printf("copied %d bytes from blob file %q\n", bytesCopied, blobPath) | ||
} | ||
} | ||
|
||
func putBlob(w http.ResponseWriter, r *http.Request) { | ||
vars := mux.Vars(r) | ||
log.Printf("Put Blob: %#v\n", vars) | ||
|
||
_, ok := vars["imagename"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
sumType, ok := vars["sumtype"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
blobDir := path.Join(blobsDirectory, sumType) | ||
err := os.MkdirAll(blobDir, os.FileMode(0755)) | ||
if err != nil { | ||
log.Printf("unable to create blob directory %q: %s\n", blobDir, err) | ||
w.WriteHeader(500) | ||
return | ||
} | ||
|
||
tempBlobFile, err := ioutil.TempFile(blobDir, "temp") | ||
if err != nil { | ||
log.Printf("unable to open temporary blob file %q: %s\n", tempBlobFile.Name(), err) | ||
w.WriteHeader(500) | ||
return | ||
} | ||
|
||
sumReader, err := NewSumReader(sumType, io.TeeReader(r.Body, tempBlobFile)) | ||
if err != nil { | ||
log.Printf("unable to create %q sum reader: %s\n", sumType, err) | ||
tempBlobFile.Close() | ||
os.Remove(tempBlobFile.Name()) | ||
if err == ErrSumTypeNotSupported { | ||
// sumType is not Supported | ||
w.WriteHeader(501) | ||
} else { | ||
// content type must not be what the sumReader expects. | ||
w.WriteHeader(400) | ||
} | ||
return | ||
} | ||
|
||
bytesCopied, err := io.Copy(ioutil.Discard, sumReader) | ||
tempBlobFile.Close() | ||
sumReader.Close() | ||
if err != nil { | ||
log.Printf("unable to copy request body to temp blob file %q: %s\n", tempBlobFile.Name(), err) | ||
// Delete temp file. | ||
os.Remove(tempBlobFile.Name()) | ||
w.WriteHeader(500) | ||
return | ||
} | ||
|
||
log.Printf("copied %d bytes from request body to temp blob file %q\n", bytesCopied, tempBlobFile.Name()) | ||
|
||
type sumReturn struct { | ||
Checksum string `json:"checksum"` | ||
} | ||
|
||
sumInfo := sumReturn{ | ||
Checksum: strings.ToLower(sumReader.Sum(nil)), | ||
} | ||
|
||
// Split on the sumType delimiter to get the sum value. | ||
sum := strings.SplitN(sumInfo.Checksum, ":", 2)[1] | ||
|
||
prefix1, prefix2 := sum[:2], sum[2:4] | ||
|
||
blobDir = path.Join(blobsDirectory, sumType, prefix1, prefix2) | ||
err = os.MkdirAll(blobDir, os.FileMode(0755)) | ||
if err != nil { | ||
log.Printf("unable to create blob directory %q: %s\n", blobDir, err) | ||
os.Remove(tempBlobFile.Name()) | ||
w.WriteHeader(500) | ||
return | ||
} | ||
|
||
// Rename temp file. | ||
blobPath := path.Join(blobDir, sum) | ||
os.Rename(tempBlobFile.Name(), blobPath) | ||
// Set 201 Header. | ||
w.WriteHeader(201) | ||
|
||
// Write JSON body. | ||
encoder := json.NewEncoder(w) | ||
encoder.Encode(sumInfo) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package server | ||
|
||
import ( | ||
"io" | ||
"log" | ||
"net/http" | ||
"os" | ||
"path" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
func getManifest(w http.ResponseWriter, r *http.Request) { | ||
vars := mux.Vars(r) | ||
log.Printf("Get Manifest: %#v\n", vars) | ||
|
||
imageName, ok := vars["imagename"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
tagName, ok := vars["tagname"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
manifestPath := path.Join(imagesDirectory, imageName, tagName) | ||
manifestFile, err := os.Open(manifestPath) | ||
if err != nil { | ||
errStatus := 500 | ||
if os.IsNotExist(err) { | ||
errStatus = 404 | ||
} | ||
log.Printf("unable to open manifest file %q: %s\n", manifestPath, err) | ||
w.WriteHeader(errStatus) | ||
return | ||
} | ||
|
||
bytesCopied, err := io.Copy(w, manifestFile) | ||
if err != nil { | ||
log.Printf("unable to copy manifest file %q: %s\n", manifestPath, err) | ||
w.WriteHeader(500) | ||
} else { | ||
log.Printf("copied %d bytes from manifest file %q\n", bytesCopied, manifestPath) | ||
} | ||
} | ||
|
||
func putManifest(w http.ResponseWriter, r *http.Request) { | ||
vars := mux.Vars(r) | ||
log.Printf("Put Manifest: %#v\n", vars) | ||
|
||
imageName, ok := vars["imagename"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
tagName, ok := vars["tagname"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
manifestDir := path.Join(imagesDirectory, imageName) | ||
err := os.MkdirAll(manifestDir, os.FileMode(0755)) | ||
if err != nil { | ||
log.Printf("unable to create manifest directory %q: %s\n", manifestDir, err) | ||
w.WriteHeader(500) | ||
return | ||
} | ||
|
||
manifestPath := path.Join(manifestDir, tagName) | ||
manifestFile, err := os.OpenFile(manifestPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(0644)) | ||
if err != nil { | ||
log.Printf("unable to open manifest file %q: %s\n", manifestPath, err) | ||
w.WriteHeader(500) | ||
return | ||
} | ||
|
||
bytesCopied, err := io.Copy(manifestFile, r.Body) | ||
if err != nil { | ||
log.Printf("unable to copy request body to manifest file %q: %s\n", manifestPath, err) | ||
w.WriteHeader(500) | ||
} else { | ||
log.Printf("copied %d bytes from request body to manifest file %q\n", bytesCopied, manifestPath) | ||
} | ||
|
||
w.WriteHeader(201) | ||
} | ||
|
||
func deleteManifest(w http.ResponseWriter, r *http.Request) { | ||
vars := mux.Vars(r) | ||
log.Printf("Delete Manifest: %#v\n", vars) | ||
|
||
imageName, ok := vars["imagename"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
tagName, ok := vars["tagname"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
manifestPath := path.Join(imagesDirectory, imageName, tagName) | ||
err := os.Remove(manifestPath) | ||
if err != nil { | ||
errStatus := 500 | ||
if os.IsNotExist(err) { | ||
errStatus = 404 | ||
} | ||
log.Printf("unable to remove manifest file %q: %s\n", manifestPath, err) | ||
w.WriteHeader(errStatus) | ||
return | ||
} | ||
|
||
w.WriteHeader(204) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package server | ||
|
||
import ( | ||
"log" | ||
"net/http" | ||
"os" | ||
"path" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
func mountBlob(w http.ResponseWriter, r *http.Request) { | ||
vars := mux.Vars(r) | ||
log.Printf("Mount Blob: %#v\n", vars) | ||
|
||
_, ok := vars["imagename"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
sumType, ok := vars["sumtype"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
sum, ok := vars["sum"] | ||
if !ok { | ||
w.WriteHeader(404) | ||
return | ||
} | ||
|
||
prefix1, prefix2 := sum[:2], sum[2:4] | ||
|
||
blobPath := path.Join(blobsDirectory, sumType, prefix1, prefix2, sum) | ||
fileInfo, err := os.Lstat(blobPath) | ||
if err != nil { | ||
errStatus := 500 | ||
if os.IsNotExist(err) { | ||
// The blob does not exist. Indicate to the client that they should upload it. | ||
errStatus = 300 | ||
} | ||
log.Printf("unable to open blob file %q: %s\n", blobPath, err) | ||
w.WriteHeader(errStatus) | ||
return | ||
} | ||
|
||
if !fileInfo.Mode().IsRegular() { | ||
log.Printf("unable to associate blob file %q: not a regular file", blobPath) | ||
w.WriteHeader(500) | ||
return | ||
} | ||
|
||
// The blob exists and is a regular file! On this naive server, that's OK. | ||
// We don't really have access control lists to worry about, everything is public. | ||
// TODO: return some content. | ||
} |
Oops, something went wrong.