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:
parent
119af77b43
commit
3d0596596a
1 changed files with 36 additions and 10 deletions
|
@ -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"),
|
||||||
|
|
Loading…
Reference in a new issue