refactor(main): Split HTTP handlers into separate functions
There is a new handler coming up to fix #102 and I want to avoid falling into the classic Go trap of creating thousand-line functions.
This commit is contained in:
parent
4ce32adfe8
commit
5ce745d104
2 changed files with 65 additions and 60 deletions
|
@ -140,7 +140,7 @@ func pkgSourceFromEnv() (PkgSource, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if git := os.Getenv("NIXERY_PKGS_REPO"); git != "" {
|
if git := os.Getenv("NIXERY_PKGS_REPO"); git != "" {
|
||||||
log.WithField("repo", git).Info("using NIx package set from git repository")
|
log.WithField("repo", git).Info("using Nix package set from git repository")
|
||||||
|
|
||||||
return &GitSource{
|
return &GitSource{
|
||||||
repository: git,
|
repository: git,
|
||||||
|
|
|
@ -53,8 +53,8 @@ var version string = "devel"
|
||||||
// routes required for serving images, since pushing and other such
|
// routes required for serving images, since pushing and other such
|
||||||
// functionality is not available.
|
// functionality is not available.
|
||||||
var (
|
var (
|
||||||
manifestRegex = regexp.MustCompile(`^/v2/([\w|\-|\.|\_|\/]+)/manifests/([\w|\-|\.|\_]+)$`)
|
manifestTagRegex = regexp.MustCompile(`^/v2/([\w|\-|\.|\_|\/]+)/manifests/([\w|\-|\.|\_]+)$`)
|
||||||
layerRegex = regexp.MustCompile(`^/v2/([\w|\-|\.|\_|\/]+)/blobs/sha256:(\w+)$`)
|
layerRegex = regexp.MustCompile(`^/v2/([\w|\-|\.|\_|\/]+)/blobs/sha256:(\w+)$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Downloads the popularity information for the package set from the
|
// Downloads the popularity information for the package set from the
|
||||||
|
@ -112,75 +112,80 @@ type registryHandler struct {
|
||||||
state *builder.State
|
state *builder.State
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serve a manifest by tag, building it via Nix and populating caches
|
||||||
|
// if necessary.
|
||||||
|
func (h *registryHandler) serveManifestTag(w http.ResponseWriter, r *http.Request, name string, tag string) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"image": name,
|
||||||
|
"tag": tag,
|
||||||
|
}).Info("requesting image manifest")
|
||||||
|
|
||||||
|
image := builder.ImageFromName(name, tag)
|
||||||
|
buildResult, err := builder.BuildImage(r.Context(), h.state, &image)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
writeError(w, 500, "UNKNOWN", "image build failure")
|
||||||
|
|
||||||
|
log.WithError(err).WithFields(log.Fields{
|
||||||
|
"image": name,
|
||||||
|
"tag": tag,
|
||||||
|
}).Error("failed to build image manifest")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some error types have special handling, which is applied
|
||||||
|
// here.
|
||||||
|
if buildResult.Error == "not_found" {
|
||||||
|
s := fmt.Sprintf("Could not find Nix packages: %v", buildResult.Pkgs)
|
||||||
|
writeError(w, 404, "MANIFEST_UNKNOWN", s)
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"image": name,
|
||||||
|
"tag": tag,
|
||||||
|
"packages": buildResult.Pkgs,
|
||||||
|
}).Warn("could not find Nix packages")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// This marshaling error is ignored because we know that this
|
||||||
|
// field represents valid JSON data.
|
||||||
|
manifest, _ := json.Marshal(buildResult.Manifest)
|
||||||
|
w.Header().Add("Content-Type", manifestMediaType)
|
||||||
|
w.Write(manifest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveLayer serves an image layer from storage (if it exists).
|
||||||
|
func (h *registryHandler) serveLayer(w http.ResponseWriter, r *http.Request, digest string) {
|
||||||
|
storage := h.state.Storage
|
||||||
|
err := storage.ServeLayer(digest, r, w)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).WithFields(log.Fields{
|
||||||
|
"layer": digest,
|
||||||
|
"backend": storage.Name(),
|
||||||
|
}).Error("failed to serve layer from storage backend")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP dispatches HTTP requests to the matching handlers.
|
||||||
func (h *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// Acknowledge that we speak V2 with an empty response
|
// Acknowledge that we speak V2 with an empty response
|
||||||
if r.RequestURI == "/v2/" {
|
if r.RequestURI == "/v2/" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve the manifest (straight from Nix)
|
// Build & serve a manifest by tag
|
||||||
manifestMatches := manifestRegex.FindStringSubmatch(r.RequestURI)
|
manifestMatches := manifestTagRegex.FindStringSubmatch(r.RequestURI)
|
||||||
if len(manifestMatches) == 3 {
|
if len(manifestMatches) == 3 {
|
||||||
imageName := manifestMatches[1]
|
h.serveManifestTag(w, r, manifestMatches[1], manifestMatches[2])
|
||||||
imageTag := manifestMatches[2]
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"image": imageName,
|
|
||||||
"tag": imageTag,
|
|
||||||
}).Info("requesting image manifest")
|
|
||||||
|
|
||||||
image := builder.ImageFromName(imageName, imageTag)
|
|
||||||
buildResult, err := builder.BuildImage(r.Context(), h.state, &image)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, 500, "UNKNOWN", "image build failure")
|
|
||||||
|
|
||||||
log.WithError(err).WithFields(log.Fields{
|
|
||||||
"image": imageName,
|
|
||||||
"tag": imageTag,
|
|
||||||
}).Error("failed to build image manifest")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some error types have special handling, which is applied
|
|
||||||
// here.
|
|
||||||
if buildResult.Error == "not_found" {
|
|
||||||
s := fmt.Sprintf("Could not find Nix packages: %v", buildResult.Pkgs)
|
|
||||||
writeError(w, 404, "MANIFEST_UNKNOWN", s)
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"image": imageName,
|
|
||||||
"tag": imageTag,
|
|
||||||
"packages": buildResult.Pkgs,
|
|
||||||
}).Warn("could not find Nix packages")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// This marshaling error is ignored because we know that this
|
|
||||||
// field represents valid JSON data.
|
|
||||||
manifest, _ := json.Marshal(buildResult.Manifest)
|
|
||||||
w.Header().Add("Content-Type", manifestMediaType)
|
|
||||||
w.Write(manifest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve an image layer. For this we need to first ask Nix for
|
// Serve an image layer
|
||||||
// the manifest, then proceed to extract the correct layer from
|
|
||||||
// it.
|
|
||||||
layerMatches := layerRegex.FindStringSubmatch(r.RequestURI)
|
layerMatches := layerRegex.FindStringSubmatch(r.RequestURI)
|
||||||
if len(layerMatches) == 3 {
|
if len(layerMatches) == 3 {
|
||||||
digest := layerMatches[2]
|
h.serveLayer(w, r, layerMatches[2])
|
||||||
storage := h.state.Storage
|
|
||||||
err := storage.ServeLayer(digest, r, w)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).WithFields(log.Fields{
|
|
||||||
"layer": digest,
|
|
||||||
"backend": storage.Name(),
|
|
||||||
}).Error("failed to serve layer from storage backend")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue