mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-07 11:13:48 +00:00
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
This commit is contained in:
parent
2297bf8cea
commit
a74fac7d70
@ -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 {
|
|||||||
//
|
//
|
||||||
// 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 {
|
|||||||
// 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) {
|
|||||||
),
|
),
|
||||||
).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)
|
||||||
|
if len(parts) < 1 {
|
||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
requestParts := strings.Split(req.URL.Path[len(s.prefix):], "/")[1:]
|
storage := s.storage[parts[0]]
|
||||||
if len(requestParts) < 1 {
|
|
||||||
notFound(w, req)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
storage := s.storage[requestParts[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) {
|
|||||||
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
|
|||||||
|
|
||||||
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 (
|
|||||||
"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) {
|
|||||||
|
|
||||||
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
|
|||||||
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 (
|
|||||||
"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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user