You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
helm/cmd/manager/router/router.go

204 lines
5.4 KiB

/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* Package router is an HTTP router.
This router provides appropriate dependency injection/encapsulation for the
HTTP routing layer. This removes the requirement to set global variables for
resources like database handles.
This library does not replace the default HTTP mux because there is no need.
Instead, it implements an HTTP handler.
It then defines a handler function that is given a context as well as a
request and response.
*/
package router
import (
"fmt"
"net/http"
"os"
"github.com/Masterminds/httputil"
"github.com/kubernetes/deployment-manager/cmd/manager/manager"
"github.com/kubernetes/deployment-manager/pkg/common"
)
const LogAccess = "Access: %s %s"
// 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 Encoder
CredentialProvider common.CredentialProvider
}
func (c *Context) Log(msg string, v ...interface{}) {
// FIXME: This should be configurable via the context.
fmt.Fprintf(os.Stdout, msg+"\n", v...)
}
func (c *Context) Err(msg string, v ...interface{}) {
// FIXME: This should be configurable via the context.
fmt.Fprintf(os.Stderr, msg+"\n", v...)
}
// NotFound writes a 404 error to the client and logs an error.
func NotFound(w http.ResponseWriter, r *http.Request) {
// TODO: Log this.
w.WriteHeader(http.StatusNotFound)
fmt.Fprintln(w, "File Not Found")
}
// Fatal writes a 500 response to the client and logs the message.
//
// Additional arguments are past into the the formatter as params to msg.
func Fatal(w http.ResponseWriter, r *http.Request, msg string, v ...interface{}) {
// TODO: Log this.
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintln(w, "Internal Server Error")
}
// HandlerFunc responds to an individual HTTP request.
//
// Returned errors will be captured, logged, and returned as HTTP 500 errors.
type HandlerFunc func(w http.ResponseWriter, r *http.Request, c *Context) error
// Handler implements an http.Handler.
//
// This is the top level route handler.
type Handler struct {
c *Context
resolver *httputil.Resolver
routes Routes
}
// Create a new Handler.
//
// Routes cannot be modified after construction. The order that the route
// names are returned by Routes.Paths() determines the lookup order.
func NewHandler(c *Context, r Routes) *Handler {
paths := make([]string, r.Len())
i := 0
for _, k := range r.Paths() {
paths[i] = k
i++
}
return &Handler{
c: c,
resolver: httputil.NewResolver(paths),
routes: r,
}
}
// ServeHTTP serves an HTTP request.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.c.Log(LogAccess, r.Method, r.URL)
route, err := h.resolver.Resolve(r)
if err != nil {
NotFound(w, r)
return
}
fn, ok := h.routes.Get(route)
if !ok {
Fatal(w, r, "route %s missing", route)
}
if err := fn(w, r, h.c); err != nil {
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
}