Merge pull request #835 from smarterclayton/apiserver_cleanup

Centralize path magic in apiserver into the New method
This commit is contained in:
Daniel Smith 2014-08-08 13:53:15 -07:00
commit d52492111f
3 changed files with 59 additions and 63 deletions

View File

@ -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) {
parts := splitPath(req.URL.Path)
if len(parts) < 1 {
notFound(w, req)
return
}
requestParts := strings.Split(req.URL.Path[len(s.prefix):], "/")[1:]
if len(requestParts) < 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, "/")
}

View File

@ -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)
}
}

View File

@ -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)
}