feat(go): Return error responses in registry format

The registry specifies a format for how errors should be returned and
this commit implements it:

https://docs.docker.com/registry/spec/api/#errors
This commit is contained in:
Vincent Ambo 2019-08-02 00:45:22 +01:00 committed by Vincent Ambo
parent 119af77b43
commit 3d0596596a

View file

@ -138,7 +138,7 @@ type image struct {
// //
// The later field is simply treated as opaque JSON and passed through. // The later field is simply treated as opaque JSON and passed through.
type BuildResult struct { type BuildResult struct {
Error string `json:"error` Error string `json:"error"`
Pkgs []string `json:"pkgs"` Pkgs []string `json:"pkgs"`
Manifest json.RawMessage `json:"manifest"` Manifest json.RawMessage `json:"manifest"`
@ -338,13 +338,29 @@ var (
layerRegex = regexp.MustCompile(`^/v2/([\w|\-|\.|\_|\/]+)/blobs/sha256:(\w+)$`) layerRegex = regexp.MustCompile(`^/v2/([\w|\-|\.|\_|\/]+)/blobs/sha256:(\w+)$`)
) )
func getConfig(key, desc string) string { // Error format corresponding to the registry protocol V2 specification. This
value := os.Getenv(key) // allows feeding back errors to clients in a way that can be presented to
if value == "" { // users.
log.Fatalln(desc + " must be specified") type registryError struct {
} Code string `json:"code"`
Message string `json:"message"`
}
return value type registryErrors struct {
Errors []registryError `json:"errors"`
}
func writeError(w http.ResponseWriter, status int, code, message string) {
err := registryErrors{
Errors: []registryError{
{code, message},
},
}
json, _ := json.Marshal(err)
w.WriteHeader(status)
w.Header().Add("Content-Type", "application/json")
w.Write(json)
} }
type registryHandler struct { type registryHandler struct {
@ -364,16 +380,17 @@ func (h *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
buildResult, err := buildImage(h.ctx, h.cfg, &image, h.bucket) buildResult, err := buildImage(h.ctx, h.cfg, &image, h.bucket)
if err != nil { if err != nil {
writeError(w, 500, "UNKNOWN", "image build failure")
log.Println("Failed to build image manifest", err) log.Println("Failed to build image manifest", err)
w.WriteHeader(500)
return return
} }
// Some error types have special handling, which is applied // Some error types have special handling, which is applied
// here. // here.
if buildResult.Error == "not_found" { if buildResult.Error == "not_found" {
log.Printf("Could not find packages: %v\n", buildResult.Pkgs) s := fmt.Sprintf("Could not find Nix packages: %v", buildResult.Pkgs)
w.WriteHeader(404) writeError(w, 404, "MANIFEST_UNKNOWN", s)
log.Println(s)
return return
} }
@ -399,6 +416,15 @@ func (h *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404) w.WriteHeader(404)
} }
func getConfig(key, desc string) string {
value := os.Getenv(key)
if value == "" {
log.Fatalln(desc + " must be specified")
}
return value
}
func main() { func main() {
cfg := &config{ cfg := &config{
bucket: getConfig("BUCKET", "GCS bucket for layer storage"), bucket: getConfig("BUCKET", "GCS bucket for layer storage"),