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