Commit ce32351b authored by Matt Butcher's avatar Matt Butcher

fix(manager): simplify adding routes

This removes the Routes interface and routesMap struct in favor of just
maintaining the routes inside of the handler.
parent 05a39656
...@@ -79,23 +79,20 @@ type Route struct { ...@@ -79,23 +79,20 @@ type Route struct {
Type string Type string
} }
func registerRoutes(c *router.Context) router.Routes { func registerRoutes(c *router.Context, h *router.Handler) {
re := regexp.MustCompile("{[a-z]+}") re := regexp.MustCompile("{[a-z]+}")
r := router.NewRoutes() h.Add("GET /healthz", healthz)
r.Add("GET /healthz", healthz)
// TODO: Replace these routes with updated ones. // TODO: Replace these routes with updated ones.
for _, d := range deployments { for _, d := range deployments {
path := fmt.Sprintf("%s %s", d.Methods, re.ReplaceAllString(d.Path, "*")) path := fmt.Sprintf("%s %s", d.Methods, re.ReplaceAllString(d.Path, "*"))
fmt.Printf("\t%s\n", path) fmt.Printf("\t%s\n", path)
r.Add(path, func(w http.ResponseWriter, r *http.Request, c *router.Context) error { h.Add(path, func(w http.ResponseWriter, r *http.Request, c *router.Context) error {
d.HandlerFunc(w, r) d.HandlerFunc(w, r)
return nil return nil
}) })
} }
return r
} }
func healthz(w http.ResponseWriter, r *http.Request, c *router.Context) error { func healthz(w http.ResponseWriter, r *http.Request, c *router.Context) error {
......
...@@ -28,9 +28,8 @@ func TestHealthz(t *testing.T) { ...@@ -28,9 +28,8 @@ func TestHealthz(t *testing.T) {
// //
// You must Close() the returned server. // You must Close() the returned server.
func httpHarness(c *router.Context, route string, fn router.HandlerFunc) *httptest.Server { func httpHarness(c *router.Context, route string, fn router.HandlerFunc) *httptest.Server {
r := router.NewRoutes() h := router.NewHandler(c)
r.Add(route, fn) h.Add(route, fn)
h := router.NewHandler(c, r)
return httptest.NewServer(h) return httptest.NewServer(h)
} }
......
...@@ -53,11 +53,12 @@ func main() { ...@@ -53,11 +53,12 @@ func main() {
} }
// Set up routes // Set up routes
routes := registerRoutes(c) handler := router.NewHandler(c)
registerRoutes(c, handler)
// Now create a server. // Now create a server.
log.Printf("Starting Manager %s on %s", version.Version, c.Config.Address) log.Printf("Starting Manager %s on %s", version.Version, c.Config.Address)
if err := http.ListenAndServe(c.Config.Address, router.NewHandler(c, routes)); err != nil { if err := http.ListenAndServe(c.Config.Address, handler); err != nil {
log.Printf("Server exited with error %s", err) log.Printf("Server exited with error %s", err)
os.Exit(1) os.Exit(1)
} }
......
package router
import (
"github.com/kubernetes/deployment-manager/cmd/manager/manager"
"github.com/kubernetes/deployment-manager/pkg/common"
helmhttp "github.com/kubernetes/deployment-manager/pkg/httputil"
)
// Config holds the global configuration parameters passed into the router.
//
// Config is used concurrently. Once a config is created, it should be treated
// as immutable.
type Config struct {
// Address is the host and port (:8080)
Address string
// MaxTemplateLength is the maximum length of a template.
MaxTemplateLength int64
// ExpanderName is the DNS name of the expansion service.
ExpanderName string
// ExpanderURL is the expander service's URL.
ExpanderURL string
// DeployerName is the deployer's DNS name
DeployerName string
// DeployerURL is the deployer's URL
DeployerURL string
// CredentialFile is the file to the credentials.
CredentialFile string
// CredentialSecrets tells the service to use a secrets file instead.
CredentialSecrets bool
// MongoName is the DNS name of the mongo server.
MongoName string
// MongoPort is the port for the MongoDB protocol on the mongo server.
// It is a string for historical reasons.
MongoPort string
// MongoAddress is the name and port.
MongoAddress string
}
// Context contains dependencies that are passed to each handler function.
//
// Context carries typed information, often scoped to interfaces, so that the
// caller's contract with the service is known at compile time.
//
// Members of the context must be concurrency safe.
type Context struct {
Config *Config
// Manager is a deployment-manager/manager/manager.Manager
Manager manager.Manager
Encoder helmhttp.Encoder
CredentialProvider common.CredentialProvider
}
...@@ -33,55 +33,9 @@ import ( ...@@ -33,55 +33,9 @@ import (
"net/http" "net/http"
"github.com/Masterminds/httputil" "github.com/Masterminds/httputil"
"github.com/kubernetes/deployment-manager/cmd/manager/manager"
"github.com/kubernetes/deployment-manager/pkg/common"
helmhttp "github.com/kubernetes/deployment-manager/pkg/httputil" helmhttp "github.com/kubernetes/deployment-manager/pkg/httputil"
) )
// Config holds the global configuration parameters passed into the router.
//
// Config is used concurrently. Once a config is created, it should be treated
// as immutable.
type Config struct {
// Address is the host and port (:8080)
Address string
// MaxTemplateLength is the maximum length of a template.
MaxTemplateLength int64
// ExpanderName is the DNS name of the expansion service.
ExpanderName string
// ExpanderURL is the expander service's URL.
ExpanderURL string
// DeployerName is the deployer's DNS name
DeployerName string
// DeployerURL is the deployer's URL
DeployerURL string
// CredentialFile is the file to the credentials.
CredentialFile string
// CredentialSecrets tells the service to use a secrets file instead.
CredentialSecrets bool
// MongoName is the DNS name of the mongo server.
MongoName string
// MongoPort is the port for the MongoDB protocol on the mongo server.
// It is a string for historical reasons.
MongoPort string
// MongoAddress is the name and port.
MongoAddress string
}
// Context contains dependencies that are passed to each handler function.
//
// Context carries typed information, often scoped to interfaces, so that the
// caller's contract with the service is known at compile time.
//
// Members of the context must be concurrency safe.
type Context struct {
Config *Config
// Manager is a deployment-manager/manager/manager.Manager
Manager manager.Manager
Encoder helmhttp.Encoder
CredentialProvider common.CredentialProvider
}
// HandlerFunc responds to an individual HTTP request. // HandlerFunc responds to an individual HTTP request.
// //
// Returned errors will be captured, logged, and returned as HTTP 500 errors. // Returned errors will be captured, logged, and returned as HTTP 500 errors.
...@@ -93,28 +47,32 @@ type HandlerFunc func(w http.ResponseWriter, r *http.Request, c *Context) error ...@@ -93,28 +47,32 @@ type HandlerFunc func(w http.ResponseWriter, r *http.Request, c *Context) error
type Handler struct { type Handler struct {
c *Context c *Context
resolver *httputil.Resolver resolver *httputil.Resolver
routes Routes routes map[string]HandlerFunc
paths []string
} }
// NewHandler creates a new Handler. // NewHandler creates a new Handler.
// //
// Routes cannot be modified after construction. The order that the route // Routes cannot be modified after construction. The order that the route
// names are returned by Routes.Paths() determines the lookup order. // names are returned by Routes.Paths() determines the lookup order.
func NewHandler(c *Context, r Routes) *Handler { func NewHandler(c *Context) *Handler {
paths := make([]string, r.Len())
i := 0
for _, k := range r.Paths() {
paths[i] = k
i++
}
return &Handler{ return &Handler{
c: c, c: c,
resolver: httputil.NewResolver(paths), resolver: httputil.NewResolver([]string{}),
routes: r, routes: map[string]HandlerFunc{},
paths: []string{},
} }
} }
// Add a route to a handler.
//
// The route name is "VERB /ENPOINT/PATH", e.g. "GET /foo".
func (h *Handler) Add(route string, fn HandlerFunc) {
h.routes[route] = fn
h.paths = append(h.paths, route)
h.resolver = httputil.NewResolver(h.paths)
}
// ServeHTTP serves an HTTP request. // ServeHTTP serves an HTTP request.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf(helmhttp.LogAccess, r.Method, r.URL) log.Printf(helmhttp.LogAccess, r.Method, r.URL)
...@@ -124,7 +82,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -124,7 +82,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
fn, ok := h.routes.Get(route) fn, ok := h.routes[route]
if !ok { if !ok {
helmhttp.Fatal(w, r, "route %s missing", route) helmhttp.Fatal(w, r, "route %s missing", route)
} }
...@@ -133,43 +91,3 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -133,43 +91,3 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
helmhttp.Fatal(w, r, err.Error()) helmhttp.Fatal(w, r, err.Error())
} }
} }
// Routes defines a container for route-to-function mapping.
type Routes interface {
Add(string, HandlerFunc)
Get(string) (HandlerFunc, bool)
Len() int
Paths() []string
}
// NewRoutes creates a default implementation of a Routes.
//
// The ordering of routes is nonderministic.
func NewRoutes() Routes {
return routeMap{}
}
type routeMap map[string]HandlerFunc
func (r routeMap) Add(name string, fn HandlerFunc) {
r[name] = fn
}
func (r routeMap) Get(name string) (HandlerFunc, bool) {
f, ok := r[name]
return f, ok
}
func (r routeMap) Len() int {
return len(r)
}
func (r routeMap) Paths() []string {
b := make([]string, len(r))
i := 0
for k := range r {
b[i] = k
i++
}
return b
}
...@@ -24,24 +24,19 @@ import ( ...@@ -24,24 +24,19 @@ import (
"testing" "testing"
) )
// Canary
var v Routes = routeMap{}
func TestHandler(t *testing.T) { func TestHandler(t *testing.T) {
c := &Context{} c := &Context{}
r := NewRoutes()
r.Add("GET /", func(w http.ResponseWriter, r *http.Request, c *Context) error { h := NewHandler(c)
h.Add("GET /", func(w http.ResponseWriter, r *http.Request, c *Context) error {
fmt.Fprintln(w, "hello") fmt.Fprintln(w, "hello")
return nil return nil
}) })
r.Add("POST /", func(w http.ResponseWriter, r *http.Request, c *Context) error { h.Add("POST /", func(w http.ResponseWriter, r *http.Request, c *Context) error {
fmt.Fprintln(w, "goodbye") fmt.Fprintln(w, "goodbye")
return nil return nil
}) })
h := NewHandler(c, r)
s := httptest.NewServer(h) s := httptest.NewServer(h)
defer s.Close() defer s.Close()
...@@ -59,14 +54,3 @@ func TestHandler(t *testing.T) { ...@@ -59,14 +54,3 @@ func TestHandler(t *testing.T) {
t.Errorf("Expected 'hello', got %q", data) t.Errorf("Expected 'hello', got %q", data)
} }
} }
// httpHarness is a simple test server fixture.
// Simple fixture for standing up a test server with a single route.
//
// You must Close() the returned server.
func httpHarness(c *Context, route string, fn HandlerFunc) *httptest.Server {
r := NewRoutes()
r.Add(route, fn)
h := NewHandler(c, r)
return httptest.NewServer(h)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment