Commit a74fac7d authored by Clayton Coleman's avatar Clayton Coleman

Centralize path magic in apiserver into the New method

Make OperationHandler and WatchHandler properly encapsulate their concerns and make them not depend on the global path
parent 2297bf8c
......@@ -21,6 +21,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"path"
"runtime/debug"
"strings"
"time"
......@@ -48,12 +49,11 @@ type Codec interface {
//
// TODO: consider migrating this to go-restful which is a more full-featured version of the same thing.
type APIServer struct {
prefix string
storage map[string]RESTStorage
codec Codec
ops *Operations
mux *http.ServeMux
asyncOpWait time.Duration
handler http.Handler
}
// New creates a new APIServer object. 'storage' contains a map of handlers. 'codec'
......@@ -65,31 +65,43 @@ type APIServer struct {
// TODO: add multitype codec serialization
func New(storage map[string]RESTStorage, codec Codec, prefix string) *APIServer {
s := &APIServer{
prefix: strings.TrimRight(prefix, "/"),
storage: storage,
codec: codec,
ops: NewOperations(),
mux: http.NewServeMux(),
// Delay just long enough to handle most simple write operations
asyncOpWait: time.Millisecond * 25,
}
// Primary API methods
s.mux.HandleFunc(s.prefix+"/", s.handleREST)
s.mux.HandleFunc(s.watchPrefix()+"/", s.handleWatch)
mux := http.NewServeMux()
prefix = strings.TrimRight(prefix, "/")
// Primary API handlers
restPrefix := prefix + "/"
mux.Handle(restPrefix, http.StripPrefix(restPrefix, http.HandlerFunc(s.handleREST)))
// Watch API handlers
watchPrefix := path.Join(prefix, "watch") + "/"
mux.Handle(watchPrefix, http.StripPrefix(watchPrefix, &WatchHandler{storage}))
// Support services for the apiserver
s.mux.Handle("/logs/", http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/"))))
healthz.InstallHandler(s.mux)
s.mux.HandleFunc("/version", handleVersion)
s.mux.HandleFunc("/", handleIndex)
logsPrefix := "/logs/"
mux.Handle(logsPrefix, http.StripPrefix(logsPrefix, http.FileServer(http.Dir("/var/log/"))))
healthz.InstallHandler(mux)
mux.HandleFunc("/version", handleVersion)
mux.HandleFunc("/", handleIndex)
// Handle both operations and operations/* with the same handler
s.mux.HandleFunc(s.operationPrefix(), s.handleOperation)
s.mux.HandleFunc(s.operationPrefix()+"/", s.handleOperation)
handler := &OperationHandler{s.ops, s.codec}
operationPrefix := path.Join(prefix, "operations")
mux.Handle(operationPrefix, http.StripPrefix(operationPrefix, handler))
operationsPrefix := operationPrefix + "/"
mux.Handle(operationsPrefix, http.StripPrefix(operationsPrefix, handler))
// Proxy minion requests
s.mux.Handle("/proxy/minion/", http.StripPrefix("/proxy/minion", http.HandlerFunc(handleProxyMinion)))
mux.Handle("/proxy/minion/", http.StripPrefix("/proxy/minion", http.HandlerFunc(handleProxyMinion)))
s.handler = mux
return s
}
......@@ -112,29 +124,25 @@ func (s *APIServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
),
).Log()
// Dispatch via our mux.
s.mux.ServeHTTP(w, req)
// Dispatch to the internal handler
s.handler.ServeHTTP(w, req)
}
// handleREST handles requests to all our RESTStorage objects.
func (s *APIServer) handleREST(w http.ResponseWriter, req *http.Request) {
if !strings.HasPrefix(req.URL.Path, s.prefix) {
notFound(w, req)
return
}
requestParts := strings.Split(req.URL.Path[len(s.prefix):], "/")[1:]
if len(requestParts) < 1 {
parts := splitPath(req.URL.Path)
if len(parts) < 1 {
notFound(w, req)
return
}
storage := s.storage[requestParts[0]]
storage := s.storage[parts[0]]
if storage == nil {
httplog.LogOf(w).Addf("'%v' has no storage object", requestParts[0])
httplog.LogOf(w).Addf("'%v' has no storage object", parts[0])
notFound(w, req)
return
}
s.handleRESTStorage(requestParts, req, w, storage)
s.handleRESTStorage(parts, req, w, storage)
}
// handleRESTStorage is the main dispatcher for a storage object. It switches on the HTTP method, and then
......@@ -347,3 +355,12 @@ func readBody(req *http.Request) ([]byte, error) {
defer req.Body.Close()
return ioutil.ReadAll(req.Body)
}
// splitPath returns the segments for a URL path
func splitPath(path string) []string {
path = strings.Trim(path, "/")
if path == "" {
return []string{}
}
return strings.Split(path, "/")
}
......@@ -18,10 +18,8 @@ package apiserver
import (
"net/http"
"path"
"sort"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
......@@ -30,37 +28,25 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
func (s *APIServer) operationPrefix() string {
return path.Join(s.prefix, "operations")
type OperationHandler struct {
ops *Operations
codec Codec
}
func (s *APIServer) handleOperation(w http.ResponseWriter, req *http.Request) {
opPrefix := s.operationPrefix()
if !strings.HasPrefix(req.URL.Path, opPrefix) {
notFound(w, req)
return
}
trimmed := strings.TrimLeft(req.URL.Path[len(opPrefix):], "/")
parts := strings.Split(trimmed, "/")
if trimmed == "" {
parts = []string{}
}
if len(parts) > 1 {
notFound(w, req)
return
}
if req.Method != "GET" {
func (h *OperationHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
parts := splitPath(req.URL.Path)
if len(parts) > 1 || req.Method != "GET" {
notFound(w, req)
return
}
if len(parts) == 0 {
// List outstanding operations.
list := s.ops.List()
writeJSON(http.StatusOK, s.codec, list, w)
list := h.ops.List()
writeJSON(http.StatusOK, h.codec, list, w)
return
}
op := s.ops.Get(parts[0])
op := h.ops.Get(parts[0])
if op == nil {
notFound(w, req)
return
......@@ -68,9 +54,9 @@ func (s *APIServer) handleOperation(w http.ResponseWriter, req *http.Request) {
obj, complete := op.StatusOrResult()
if complete {
writeJSON(http.StatusOK, s.codec, obj, w)
writeJSON(http.StatusOK, h.codec, obj, w)
} else {
writeJSON(http.StatusAccepted, s.codec, obj, w)
writeJSON(http.StatusAccepted, h.codec, obj, w)
}
}
......
......@@ -19,8 +19,6 @@ package apiserver
import (
"encoding/json"
"net/http"
"path"
"strings"
"code.google.com/p/go.net/websocket"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
......@@ -28,22 +26,17 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
func (s *APIServer) watchPrefix() string {
return path.Join(s.prefix, "watch")
type WatchHandler struct {
storage map[string]RESTStorage
}
// handleWatch processes a watch request
func (s *APIServer) handleWatch(w http.ResponseWriter, req *http.Request) {
prefix := s.watchPrefix()
if !strings.HasPrefix(req.URL.Path, prefix) {
notFound(w, req)
return
}
parts := strings.Split(req.URL.Path[len(prefix):], "/")[1:]
if req.Method != "GET" || len(parts) < 1 {
func (h *WatchHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
parts := splitPath(req.URL.Path)
if len(parts) < 1 || req.Method != "GET" {
notFound(w, req)
}
storage := s.storage[parts[0]]
storage := h.storage[parts[0]]
if storage == nil {
notFound(w, req)
}
......
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