Merge pull request #653 from coreos/improve-ctx

matchbox: Use Go 1.7 request Context, remove ContextHandler
This commit is contained in:
Dalton Hubble
2017-09-25 17:07:45 -07:00
committed by GitHub
18 changed files with 74 additions and 141 deletions

View File

@@ -7,6 +7,7 @@ Notable changes between releases.
* Add gRPC API endpoints for managing generic (experimental) templates
* Remove Profile `Cmdline` map (deprecated in v0.5.0), use `Args` slice instead
* Remove pixiecore support (deprecated in v0.5.0)
* Remove `ContextHandler`, `ContextHandlerFunc`, and `NewHandler` from the `matchbox/http` package.
### Examples / Modules

View File

@@ -6,7 +6,6 @@ import (
"strings"
"time"
"context"
"github.com/Sirupsen/logrus"
cloudinit "github.com/coreos/coreos-cloudinit/config"
@@ -23,8 +22,9 @@ type CloudConfig struct {
// the request.
// DEPRECATED: Please migrate to using Container Linux configs.
// https://github.com/coreos/matchbox/blob/master/Documentation/cloud-config.md
func (s *Server) cloudHandler(core server.Server) ContextHandler {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func (s *Server) cloudHandler(core server.Server) http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
group, err := groupFromContext(ctx)
if err != nil {
s.logger.WithFields(logrus.Fields{
@@ -99,5 +99,5 @@ func (s *Server) cloudHandler(core server.Server) ContextHandler {
}
http.ServeContent(w, req, "", time.Time{}, strings.NewReader(config))
}
return ContextHandlerFunc(fn)
return http.HandlerFunc(fn)
}

View File

@@ -42,7 +42,7 @@ coreos:
ctx := withGroup(context.Background(), fake.Group)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/?foo=some-param", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - Cloud config is rendered with Group selectors, metadata, and query variables
assert.Equal(t, http.StatusOK, w.Code)
@@ -56,7 +56,7 @@ func TestCloudHandler_MissingCtxProfile(t *testing.T) {
h := srv.cloudHandler(c)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(context.Background(), w, req)
h.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
@@ -68,7 +68,7 @@ func TestCloudHandler_MissingCloudConfig(t *testing.T) {
ctx := withProfile(context.Background(), fake.Profile)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
assert.Equal(t, http.StatusNotFound, w.Code)
}
@@ -89,7 +89,7 @@ coreos:
ctx := withGroup(context.Background(), fake.Group)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - Cloud-config template rendering errors because "missing_key" is not
// present in the template variables

View File

@@ -1,47 +0,0 @@
package http
import (
"net/http"
"context"
)
// ContextHandler defines a handler which receives a passed context.Context
// with the standard ResponseWriter and Request.
type ContextHandler interface {
ServeHTTP(context.Context, http.ResponseWriter, *http.Request)
}
// ContextHandlerFunc type is an adapter to allow the use of an ordinary
// function as a ContextHandler. If f is a function with the correct
// signature, ContextHandlerFunc(f) is a ContextHandler that calls f.
type ContextHandlerFunc func(context.Context, http.ResponseWriter, *http.Request)
// ServeHTTP calls the function f(ctx, w, req).
func (f ContextHandlerFunc) ServeHTTP(ctx context.Context, w http.ResponseWriter, req *http.Request) {
f(ctx, w, req)
}
// handler wraps a ContextHandler to implement the http.Handler interface for
// compatability with ServeMux and middlewares.
//
// Middleswares which do not pass a ctx break the chain so place them before
// or after chains of ContextHandlers.
type handler struct {
ctx context.Context
handler ContextHandler
}
// NewHandler returns an http.Handler which wraps the given ContextHandler
// and creates a background context.Context.
func NewHandler(h ContextHandler) http.Handler {
return &handler{
ctx: context.Background(),
handler: h,
}
}
// ServeHTTP lets handler implement the http.Handler interface.
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h.handler.ServeHTTP(h.ctx, w, req)
}

View File

@@ -1,22 +0,0 @@
package http
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"context"
"github.com/stretchr/testify/assert"
)
func TestNewHandler(t *testing.T) {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "ContextHandler called")
}
h := NewHandler(ContextHandlerFunc(fn))
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(w, req)
assert.Equal(t, "ContextHandler called", w.Body.String())
}

View File

@@ -6,7 +6,6 @@ import (
"strings"
"time"
"context"
"github.com/Sirupsen/logrus"
"github.com/coreos/matchbox/matchbox/server"
@@ -15,8 +14,9 @@ import (
// genericHandler returns a handler that responds with the generic config
// matching the request.
func (s *Server) genericHandler(core server.Server) ContextHandler {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func (s *Server) genericHandler(core server.Server) http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
group, err := groupFromContext(ctx)
if err != nil {
s.logger.WithFields(logrus.Fields{
@@ -73,5 +73,5 @@ func (s *Server) genericHandler(core server.Server) ContextHandler {
config := buf.String()
http.ServeContent(w, req, "", time.Time{}, strings.NewReader(config))
}
return ContextHandlerFunc(fn)
return http.HandlerFunc(fn)
}

View File

@@ -36,7 +36,7 @@ FOO=some-param
ctx := withGroup(context.Background(), fake.Group)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/?foo=some-param", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - Generic config is rendered with Group selectors, metadata, and query variables
assert.Equal(t, http.StatusOK, w.Code)
@@ -50,7 +50,7 @@ func TestGenericHandler_MissingCtxProfile(t *testing.T) {
h := srv.genericHandler(c)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(context.Background(), w, req)
h.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
@@ -62,7 +62,7 @@ func TestGenericHandler_MissingCloudConfig(t *testing.T) {
ctx := withProfile(context.Background(), fake.Profile)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
assert.Equal(t, http.StatusNotFound, w.Code)
}
@@ -81,7 +81,7 @@ KEY={{.missing_key}}
ctx := withGroup(context.Background(), fake.Group)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - Generic template rendering errors because "missing_key" is not
// present in the template variables

View File

@@ -5,7 +5,6 @@ import (
"net/http"
"text/template"
"context"
"github.com/Sirupsen/logrus"
)
@@ -28,8 +27,9 @@ initrd {{ range $element := .Initrd }} "{{$element}}"{{end}}
// grubHandler returns a handler which renders a GRUB2 config for the
// requester.
func (s *Server) grubHandler() ContextHandler {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func (s *Server) grubHandler() http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
profile, err := profileFromContext(ctx)
if err != nil {
s.logger.WithFields(logrus.Fields{
@@ -57,5 +57,5 @@ func (s *Server) grubHandler() ContextHandler {
w.WriteHeader(http.StatusInternalServerError)
}
}
return ContextHandlerFunc(fn)
return http.HandlerFunc(fn)
}

View File

@@ -19,7 +19,7 @@ func TestGrubHandler(t *testing.T) {
ctx := withProfile(context.Background(), fake.Profile)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - the Profile's NetBoot config is rendered as a GRUB2 config
expectedScript := `default=0

View File

@@ -4,8 +4,6 @@ import (
"fmt"
"net/http"
"context"
"github.com/coreos/matchbox/matchbox/server"
pb "github.com/coreos/matchbox/matchbox/server/serverpb"
)
@@ -35,8 +33,9 @@ func (s *Server) logRequest(next http.Handler) http.Handler {
// selectGroup selects the Group whose selectors match the query parameters,
// adds the Group to the ctx, and calls the next handler. The next handler
// should handle a missing Group.
func (s *Server) selectGroup(core server.Server, next ContextHandler) ContextHandler {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func (s *Server) selectGroup(core server.Server, next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
attrs := labelsFromRequest(s.logger, req)
// match machine request
group, err := core.SelectGroup(ctx, &pb.SelectGroupRequest{Labels: attrs})
@@ -44,16 +43,17 @@ func (s *Server) selectGroup(core server.Server, next ContextHandler) ContextHan
// add the Group to the ctx for next handler
ctx = withGroup(ctx, group)
}
next.ServeHTTP(ctx, w, req)
next.ServeHTTP(w, req.WithContext(ctx))
}
return ContextHandlerFunc(fn)
return http.HandlerFunc(fn)
}
// selectProfile selects the Profile for the given query parameters, adds the
// Profile to the ctx, and calls the next handler. The next handler should
// handle a missing profile.
func (s *Server) selectProfile(core server.Server, next ContextHandler) ContextHandler {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func (s *Server) selectProfile(core server.Server, next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
attrs := labelsFromRequest(s.logger, req)
// match machine request
profile, err := core.SelectProfile(ctx, &pb.SelectProfileRequest{Labels: attrs})
@@ -61,7 +61,7 @@ func (s *Server) selectProfile(core server.Server, next ContextHandler) ContextH
// add the Profile to the ctx for the next handler
ctx = withProfile(ctx, profile)
}
next.ServeHTTP(ctx, w, req)
next.ServeHTTP(w, req.WithContext(ctx))
}
return ContextHandlerFunc(fn)
return http.HandlerFunc(fn)
}

View File

@@ -6,7 +6,6 @@ import (
"net/http/httptest"
"testing"
"context"
logtest "github.com/Sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
@@ -22,7 +21,8 @@ func TestSelectGroup(t *testing.T) {
logger, _ := logtest.NewNullLogger()
srv := NewServer(&Config{Logger: logger})
c := server.NewServer(&server.Config{Store: store})
next := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
next := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
group, err := groupFromContext(ctx)
assert.Nil(t, err)
assert.Equal(t, fake.Group, group)
@@ -32,10 +32,10 @@ func TestSelectGroup(t *testing.T) {
// - query params are used to match uuid=a1b2c3d4 to fake.Group
// - the fake.Group is added to the context
// - next handler is called
h := srv.selectGroup(c, ContextHandlerFunc(next))
h := srv.selectGroup(c, http.HandlerFunc(next))
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "?uuid=a1b2c3d4", nil)
h.ServeHTTP(context.Background(), w, req)
h.ServeHTTP(w, req)
assert.Equal(t, "next handler called", w.Body.String())
}
@@ -47,7 +47,8 @@ func TestSelectProfile(t *testing.T) {
logger, _ := logtest.NewNullLogger()
srv := NewServer(&Config{Logger: logger})
c := server.NewServer(&server.Config{Store: store})
next := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
next := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
profile, err := profileFromContext(ctx)
assert.Nil(t, err)
assert.Equal(t, fake.Profile, profile)
@@ -57,9 +58,9 @@ func TestSelectProfile(t *testing.T) {
// - query params are used to match uuid=a1b2c3d4 to fake.Group's fakeProfile
// - the fake.Profile is added to the context
// - next handler is called
h := srv.selectProfile(c, ContextHandlerFunc(next))
h := srv.selectProfile(c, http.HandlerFunc(next))
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "?uuid=a1b2c3d4", nil)
h.ServeHTTP(context.Background(), w, req)
h.ServeHTTP(w, req)
assert.Equal(t, "next handler called", w.Body.String())
}

View File

@@ -5,7 +5,6 @@ import (
"net/http"
"strings"
"context"
"github.com/Sirupsen/logrus"
ct "github.com/coreos/container-linux-config-transpiler/config"
ignition "github.com/coreos/ignition/config"
@@ -19,8 +18,9 @@ import (
// as raw Ignition (for .ign/.ignition) or rendered from a Container Linux
// Config (YAML) and converted to Ignition. Ignition configs are served as HTTP
// JSON responses.
func (s *Server) ignitionHandler(core server.Server) ContextHandler {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func (s *Server) ignitionHandler(core server.Server) http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
group, err := groupFromContext(ctx)
if err != nil {
s.logger.WithFields(logrus.Fields{
@@ -107,7 +107,7 @@ func (s *Server) ignitionHandler(core server.Server) ContextHandler {
s.renderJSON(w, ign)
return
}
return ContextHandlerFunc(fn)
return http.HandlerFunc(fn)
}
// isIgnition returns true if the file should be treated as plain Ignition.

View File

@@ -31,7 +31,7 @@ func TestIgnitionHandler_V2JSON(t *testing.T) {
ctx := withGroup(context.Background(), fake.Group)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - raw Ignition config served directly
assert.Equal(t, http.StatusOK, w.Code)
@@ -64,7 +64,7 @@ systemd:
ctx := withGroup(context.Background(), fake.Group)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/?foo=some-param&bar=b", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - Container Linux Config template rendered with Group selectors, metadata, and query variables
// - Transformed to an Ignition config (JSON)
@@ -80,7 +80,7 @@ func TestIgnitionHandler_MissingCtxProfile(t *testing.T) {
h := srv.ignitionHandler(c)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(context.Background(), w, req)
h.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
@@ -92,7 +92,7 @@ func TestIgnitionHandler_MissingIgnitionConfig(t *testing.T) {
ctx := withProfile(context.Background(), fake.Profile)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
assert.Equal(t, http.StatusNotFound, w.Code)
}
@@ -115,7 +115,7 @@ systemd:
ctx := withGroup(context.Background(), fake.Group)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - Ignition template rendering errors because "missing_key" is not
// present in the template variables

View File

@@ -6,7 +6,6 @@ import (
"net/http"
"text/template"
"context"
"github.com/Sirupsen/logrus"
)
@@ -22,17 +21,18 @@ boot
// ipxeInspect returns a handler that responds with the iPXE script to gather
// client machine data and chainload to the ipxeHandler.
func ipxeInspect() ContextHandler {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func ipxeInspect() http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, ipxeBootstrap)
}
return ContextHandlerFunc(fn)
return http.HandlerFunc(fn)
}
// ipxeBoot returns a handler which renders the iPXE boot script for the
// requester.
func (s *Server) ipxeHandler() ContextHandler {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func (s *Server) ipxeHandler() http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
profile, err := profileFromContext(ctx)
if err != nil {
s.logger.WithFields(logrus.Fields{
@@ -60,5 +60,5 @@ func (s *Server) ipxeHandler() ContextHandler {
w.WriteHeader(http.StatusInternalServerError)
}
}
return ContextHandlerFunc(fn)
return http.HandlerFunc(fn)
}

View File

@@ -17,7 +17,7 @@ func TestIPXEInspect(t *testing.T) {
h := ipxeInspect()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(context.Background(), w, req)
h.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, ipxeBootstrap, w.Body.String())
}
@@ -29,7 +29,7 @@ func TestIPXEHandler(t *testing.T) {
ctx := withProfile(context.Background(), fake.Profile)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - the Profile's NetBoot config is rendered as an iPXE script
expectedScript := `#!ipxe
@@ -47,7 +47,7 @@ func TestIPXEHandler_MissingCtxProfile(t *testing.T) {
h := srv.ipxeHandler()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(context.Background(), w, req)
h.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
@@ -59,7 +59,7 @@ func TestIPXEHandler_RenderTemplateError(t *testing.T) {
ctx := withProfile(context.Background(), &storagepb.Profile{Boot: nil})
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
assert.Equal(t, http.StatusNotFound, w.Code)
}
@@ -70,7 +70,7 @@ func TestIPXEHandler_WriteError(t *testing.T) {
ctx := withProfile(context.Background(), fake.Profile)
w := NewUnwriteableResponseWriter()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
assert.Equal(t, http.StatusInternalServerError, w.Code)
assert.Empty(t, w.Body.String())
}

View File

@@ -6,7 +6,6 @@ import (
"net/http"
"strings"
"context"
"github.com/Sirupsen/logrus"
)
@@ -14,8 +13,9 @@ const plainContentType = "plain/text"
// genericHandler returns a handler that responds with the metadata env file
// matching the request.
func (s *Server) metadataHandler() ContextHandler {
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func (s *Server) metadataHandler() http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
group, err := groupFromContext(ctx)
if err != nil {
s.logger.WithFields(logrus.Fields{
@@ -42,7 +42,7 @@ func (s *Server) metadataHandler() ContextHandler {
w.Header().Set(contentType, plainContentType)
renderAsEnvFile(w, "", data)
}
return ContextHandlerFunc(fn)
return http.HandlerFunc(fn)
}
// renderAsEnvFile writes map data into a KEY=value\n "env file" format,

View File

@@ -26,7 +26,7 @@ func TestMetadataHandler(t *testing.T) {
ctx := withGroup(context.Background(), group)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/?mac=52-54-00-a1-9c-ae&foo=bar&count=3&gate=true", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - Group selectors, metadata, and query variables are formatted
// - nested metadata are namespaced
@@ -69,7 +69,7 @@ func TestMetadataHandler_MetadataEdgeCases(t *testing.T) {
ctx := withGroup(context.Background(), c.group)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(ctx, w, req)
h.ServeHTTP(w, req.WithContext(ctx))
// assert that:
// - Group metadata key names are upper case
// - key/value pairs are newline separated
@@ -85,7 +85,7 @@ func TestMetadataHandler_MissingCtxGroup(t *testing.T) {
h := srv.metadataHandler()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
h.ServeHTTP(context.Background(), w, req)
h.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}

View File

@@ -44,8 +44,8 @@ func NewServer(config *Config) *Server {
func (s *Server) HTTPHandler() http.Handler {
mux := http.NewServeMux()
chain := func(next ContextHandler) http.Handler {
return s.logRequest(NewHandler(next))
chain := func(next http.Handler) http.Handler {
return s.logRequest(next)
}
// matchbox version
mux.Handle("/", s.logRequest(homeHandler()))
@@ -66,8 +66,8 @@ func (s *Server) HTTPHandler() http.Handler {
// Signatures
if s.signer != nil {
signerChain := func(next ContextHandler) http.Handler {
return s.logRequest(sign.SignatureHandler(s.signer, NewHandler(next)))
signerChain := func(next http.Handler) http.Handler {
return s.logRequest(sign.SignatureHandler(s.signer, next))
}
mux.Handle("/grub.sig", signerChain(s.selectProfile(s.core, s.grubHandler())))
mux.Handle("/boot.ipxe.sig", signerChain(ipxeInspect()))
@@ -79,8 +79,8 @@ func (s *Server) HTTPHandler() http.Handler {
mux.Handle("/metadata.sig", signerChain(s.selectGroup(s.core, s.metadataHandler())))
}
if s.armoredSigner != nil {
signerChain := func(next ContextHandler) http.Handler {
return s.logRequest(sign.SignatureHandler(s.armoredSigner, NewHandler(next)))
signerChain := func(next http.Handler) http.Handler {
return s.logRequest(sign.SignatureHandler(s.armoredSigner, next))
}
mux.Handle("/grub.asc", signerChain(s.selectProfile(s.core, s.grubHandler())))
mux.Handle("/boot.ipxe.asc", signerChain(ipxeInspect()))