2014-09-02 16:28:24 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2014-09-02 16:28:24 +00:00
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 apiserver
import (
2015-07-01 21:36:36 +00:00
"bufio"
"encoding/json"
2014-09-02 16:28:24 +00:00
"fmt"
2015-07-01 21:36:36 +00:00
"net"
2014-09-02 16:28:24 +00:00
"net/http"
"regexp"
"runtime/debug"
"strings"
2015-07-01 21:36:36 +00:00
"sync"
"time"
2014-09-02 16:28:24 +00:00
2015-08-05 22:05:17 +00:00
"github.com/golang/glog"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/httplog"
"k8s.io/kubernetes/pkg/util"
2015-09-09 17:45:01 +00:00
"k8s.io/kubernetes/pkg/util/sets"
2014-09-02 16:28:24 +00:00
)
2014-11-02 06:50:00 +00:00
// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal
// CRUDdy GET/POST/PUT/DELETE actions on REST objects.
// TODO: find a way to keep this up to date automatically. Maybe dynamically populate list as handlers added to
// master's Mux.
var specialVerbs = map [ string ] bool {
"proxy" : true ,
"redirect" : true ,
"watch" : true ,
}
2015-03-31 04:41:10 +00:00
// Constant for the retry-after interval on rate limiting.
// TODO: maybe make this dynamic? or user-adjustable?
const RetryAfter = "1"
2014-11-02 06:50:00 +00:00
// IsReadOnlyReq() is true for any (or at least many) request which has no observable
// side effects on state of apiserver (though there may be internal side effects like
// caching and logging).
func IsReadOnlyReq ( req http . Request ) bool {
if req . Method == "GET" {
// TODO: add OPTIONS and HEAD if we ever support those.
return true
}
return false
}
2014-10-20 22:23:28 +00:00
// ReadOnly passes all GET requests on to handler, and returns an error on all other requests.
func ReadOnly ( handler http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
2014-11-02 06:50:00 +00:00
if IsReadOnlyReq ( * req ) {
2014-10-20 22:23:28 +00:00
handler . ServeHTTP ( w , req )
return
}
w . WriteHeader ( http . StatusForbidden )
fmt . Fprintf ( w , "This is a read-only endpoint." )
} )
}
2015-03-31 04:41:10 +00:00
// MaxInFlight limits the number of in-flight requests to buffer size of the passed in channel.
func MaxInFlightLimit ( c chan bool , longRunningRequestRE * regexp . Regexp , handler http . Handler ) http . Handler {
if c == nil {
return handler
}
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
if longRunningRequestRE . MatchString ( r . URL . Path ) {
// Skip tracking long running events.
handler . ServeHTTP ( w , r )
return
}
select {
case c <- true :
defer func ( ) { <- c } ( )
handler . ServeHTTP ( w , r )
default :
tooManyRequests ( w )
}
} )
}
2014-10-20 22:23:28 +00:00
// RateLimit uses rl to rate limit accepting requests to 'handler'.
func RateLimit ( rl util . RateLimiter , handler http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
if rl . CanAccept ( ) {
handler . ServeHTTP ( w , req )
return
}
2015-03-31 04:41:10 +00:00
tooManyRequests ( w )
2014-10-20 22:23:28 +00:00
} )
}
2015-03-31 04:41:10 +00:00
func tooManyRequests ( w http . ResponseWriter ) {
// Return a 429 status indicating "Too Many Requests"
w . Header ( ) . Set ( "Retry-After" , RetryAfter )
2015-04-22 06:04:32 +00:00
http . Error ( w , "Too many requests, please try again later." , errors . StatusTooManyRequests )
2015-03-31 04:41:10 +00:00
}
2014-09-02 16:28:24 +00:00
// RecoverPanics wraps an http Handler to recover and log panics.
func RecoverPanics ( handler http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
defer func ( ) {
if x := recover ( ) ; x != nil {
2015-04-22 06:04:32 +00:00
http . Error ( w , "apis panic. Look in log for details." , http . StatusInternalServerError )
2015-04-24 13:09:14 +00:00
glog . Errorf ( "APIServer panic'd on %v %v: %v\n%s\n" , req . Method , req . RequestURI , x , debug . Stack ( ) )
2014-09-02 16:28:24 +00:00
}
} ( )
defer httplog . NewLogged ( req , & w ) . StacktraceWhen (
httplog . StatusIsNot (
http . StatusOK ,
2014-10-24 17:16:02 +00:00
http . StatusCreated ,
2014-09-02 16:28:24 +00:00
http . StatusAccepted ,
http . StatusMovedPermanently ,
http . StatusTemporaryRedirect ,
http . StatusConflict ,
http . StatusNotFound ,
2015-06-30 22:19:41 +00:00
http . StatusUnauthorized ,
http . StatusForbidden ,
2015-01-24 01:17:11 +00:00
errors . StatusUnprocessableEntity ,
2015-01-08 20:41:38 +00:00
http . StatusSwitchingProtocols ,
2014-09-02 16:28:24 +00:00
) ,
) . Log ( )
// Dispatch to the internal handler
handler . ServeHTTP ( w , req )
} )
}
2015-07-01 21:36:36 +00:00
// TimeoutHandler returns an http.Handler that runs h with a timeout
// determined by timeoutFunc. The new http.Handler calls h.ServeHTTP to handle
// each request, but if a call runs for longer than its time limit, the
// handler responds with a 503 Service Unavailable error and the message
// provided. (If msg is empty, a suitable default message with be sent.) After
// the handler times out, writes by h to its http.ResponseWriter will return
// http.ErrHandlerTimeout. If timeoutFunc returns a nil timeout channel, no
// timeout will be enforced.
func TimeoutHandler ( h http . Handler , timeoutFunc func ( * http . Request ) ( timeout <- chan time . Time , msg string ) ) http . Handler {
return & timeoutHandler { h , timeoutFunc }
}
type timeoutHandler struct {
handler http . Handler
timeout func ( * http . Request ) ( <- chan time . Time , string )
}
func ( t * timeoutHandler ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
after , msg := t . timeout ( r )
if after == nil {
t . handler . ServeHTTP ( w , r )
return
}
done := make ( chan struct { } , 1 )
tw := newTimeoutWriter ( w )
go func ( ) {
t . handler . ServeHTTP ( tw , r )
done <- struct { } { }
} ( )
select {
case <- done :
return
case <- after :
tw . timeout ( msg )
}
}
type timeoutWriter interface {
http . ResponseWriter
timeout ( string )
}
func newTimeoutWriter ( w http . ResponseWriter ) timeoutWriter {
base := & baseTimeoutWriter { w : w }
_ , notifiable := w . ( http . CloseNotifier )
_ , hijackable := w . ( http . Hijacker )
switch {
case notifiable && hijackable :
return & closeHijackTimeoutWriter { base }
case notifiable :
return & closeTimeoutWriter { base }
case hijackable :
return & hijackTimeoutWriter { base }
default :
return base
}
}
type baseTimeoutWriter struct {
w http . ResponseWriter
mu sync . Mutex
timedOut bool
wroteHeader bool
hijacked bool
}
func ( tw * baseTimeoutWriter ) Header ( ) http . Header {
return tw . w . Header ( )
}
func ( tw * baseTimeoutWriter ) Write ( p [ ] byte ) ( int , error ) {
tw . mu . Lock ( )
defer tw . mu . Unlock ( )
tw . wroteHeader = true
if tw . hijacked {
return 0 , http . ErrHijacked
}
if tw . timedOut {
return 0 , http . ErrHandlerTimeout
}
return tw . w . Write ( p )
}
func ( tw * baseTimeoutWriter ) WriteHeader ( code int ) {
tw . mu . Lock ( )
defer tw . mu . Unlock ( )
if tw . timedOut || tw . wroteHeader || tw . hijacked {
return
}
tw . wroteHeader = true
tw . w . WriteHeader ( code )
}
func ( tw * baseTimeoutWriter ) timeout ( msg string ) {
tw . mu . Lock ( )
defer tw . mu . Unlock ( )
if ! tw . wroteHeader && ! tw . hijacked {
tw . w . WriteHeader ( http . StatusGatewayTimeout )
if msg != "" {
tw . w . Write ( [ ] byte ( msg ) )
} else {
enc := json . NewEncoder ( tw . w )
enc . Encode ( errors . NewServerTimeout ( "" , "" , 0 ) )
}
}
tw . timedOut = true
}
func ( tw * baseTimeoutWriter ) closeNotify ( ) <- chan bool {
return tw . w . ( http . CloseNotifier ) . CloseNotify ( )
}
func ( tw * baseTimeoutWriter ) hijack ( ) ( net . Conn , * bufio . ReadWriter , error ) {
tw . mu . Lock ( )
defer tw . mu . Unlock ( )
if tw . timedOut {
return nil , nil , http . ErrHandlerTimeout
}
conn , rw , err := tw . w . ( http . Hijacker ) . Hijack ( )
if err == nil {
tw . hijacked = true
}
return conn , rw , err
}
type closeTimeoutWriter struct {
* baseTimeoutWriter
}
func ( tw * closeTimeoutWriter ) CloseNotify ( ) <- chan bool {
return tw . closeNotify ( )
}
type hijackTimeoutWriter struct {
* baseTimeoutWriter
}
func ( tw * hijackTimeoutWriter ) Hijack ( ) ( net . Conn , * bufio . ReadWriter , error ) {
return tw . hijack ( )
}
type closeHijackTimeoutWriter struct {
* baseTimeoutWriter
}
func ( tw * closeHijackTimeoutWriter ) CloseNotify ( ) <- chan bool {
return tw . closeNotify ( )
}
func ( tw * closeHijackTimeoutWriter ) Hijack ( ) ( net . Conn , * bufio . ReadWriter , error ) {
return tw . hijack ( )
}
2014-11-11 07:11:45 +00:00
// TODO: use restful.CrossOriginResourceSharing
2014-09-02 16:28:24 +00:00
// Simple CORS implementation that wraps an http Handler
// For a more detailed implementation use https://github.com/martini-contrib/cors
// or implement CORS at your proxy layer
// Pass nil for allowedMethods and allowedHeaders to use the defaults
2014-09-04 17:55:30 +00:00
func CORS ( handler http . Handler , allowedOriginPatterns [ ] * regexp . Regexp , allowedMethods [ ] string , allowedHeaders [ ] string , allowCredentials string ) http . Handler {
2014-09-02 16:28:24 +00:00
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
origin := req . Header . Get ( "Origin" )
if origin != "" {
allowed := false
2014-09-04 17:55:30 +00:00
for _ , pattern := range allowedOriginPatterns {
2014-09-03 18:33:52 +00:00
if allowed = pattern . MatchString ( origin ) ; allowed {
break
}
2014-09-02 16:28:24 +00:00
}
if allowed {
w . Header ( ) . Set ( "Access-Control-Allow-Origin" , origin )
// Set defaults for methods and headers if nothing was passed
if allowedMethods == nil {
allowedMethods = [ ] string { "POST" , "GET" , "OPTIONS" , "PUT" , "DELETE" }
}
if allowedHeaders == nil {
2014-09-04 17:55:30 +00:00
allowedHeaders = [ ] string { "Content-Type" , "Content-Length" , "Accept-Encoding" , "X-CSRF-Token" , "Authorization" , "X-Requested-With" , "If-Modified-Since" }
2014-09-02 16:28:24 +00:00
}
w . Header ( ) . Set ( "Access-Control-Allow-Methods" , strings . Join ( allowedMethods , ", " ) )
w . Header ( ) . Set ( "Access-Control-Allow-Headers" , strings . Join ( allowedHeaders , ", " ) )
w . Header ( ) . Set ( "Access-Control-Allow-Credentials" , allowCredentials )
// Stop here if its a preflight OPTIONS request
if req . Method == "OPTIONS" {
w . WriteHeader ( http . StatusNoContent )
return
}
}
}
// Dispatch to the next handler
handler . ServeHTTP ( w , req )
} )
}
2014-10-16 21:18:16 +00:00
// RequestAttributeGetter is a function that extracts authorizer.Attributes from an http.Request
2014-11-03 15:57:08 +00:00
type RequestAttributeGetter interface {
GetAttribs ( req * http . Request ) ( attribs authorizer . Attributes )
}
type requestAttributeGetter struct {
2015-02-11 22:09:25 +00:00
requestContextMapper api . RequestContextMapper
2015-01-29 19:14:36 +00:00
apiRequestInfoResolver * APIRequestInfoResolver
2014-11-03 15:57:08 +00:00
}
// NewAttributeGetter returns an object which implements the RequestAttributeGetter interface.
2015-02-11 22:09:25 +00:00
func NewRequestAttributeGetter ( requestContextMapper api . RequestContextMapper , restMapper meta . RESTMapper , apiRoots ... string ) RequestAttributeGetter {
2015-09-09 17:45:01 +00:00
return & requestAttributeGetter { requestContextMapper , & APIRequestInfoResolver { sets . NewString ( apiRoots ... ) , restMapper } }
2014-11-03 15:57:08 +00:00
}
func ( r * requestAttributeGetter ) GetAttribs ( req * http . Request ) authorizer . Attributes {
attribs := authorizer . AttributesRecord { }
2015-02-11 22:09:25 +00:00
ctx , ok := r . requestContextMapper . Get ( req )
2014-11-03 15:57:08 +00:00
if ok {
2015-02-11 22:09:25 +00:00
user , ok := api . UserFrom ( ctx )
if ok {
attribs . User = user
}
2014-11-03 15:57:08 +00:00
}
2014-10-16 21:18:16 +00:00
2014-11-02 06:50:00 +00:00
attribs . ReadOnly = IsReadOnlyReq ( * req )
2015-01-29 19:14:36 +00:00
apiRequestInfo , _ := r . apiRequestInfoResolver . GetAPIRequestInfo ( req )
2014-12-09 19:23:21 +00:00
2014-11-02 06:50:00 +00:00
// If a path follows the conventions of the REST object store, then
2015-01-29 19:14:54 +00:00
// we can extract the resource. Otherwise, not.
attribs . Resource = apiRequestInfo . Resource
2014-11-02 06:50:00 +00:00
// If the request specifies a namespace, then the namespace is filled in.
// Assumes there is no empty string namespace. Unspecified results
// in empty (does not understand defaulting rules.)
2015-01-29 19:14:36 +00:00
attribs . Namespace = apiRequestInfo . Namespace
2014-11-02 06:50:00 +00:00
2014-11-03 15:57:08 +00:00
return & attribs
2014-10-16 21:18:16 +00:00
}
// WithAuthorizationCheck passes all authorized requests on to handler, and returns a forbidden error otherwise.
func WithAuthorizationCheck ( handler http . Handler , getAttribs RequestAttributeGetter , a authorizer . Authorizer ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
2014-11-03 15:57:08 +00:00
err := a . Authorize ( getAttribs . GetAttribs ( req ) )
2014-10-16 21:18:16 +00:00
if err == nil {
handler . ServeHTTP ( w , req )
return
}
forbidden ( w , req )
} )
}
2014-12-09 19:23:21 +00:00
2015-01-29 19:14:36 +00:00
// APIRequestInfo holds information parsed from the http.Request
type APIRequestInfo struct {
// Verb is the kube verb associated with the request, not the http verb. This includes things like list and watch.
Verb string
APIVersion string
Namespace string
// Resource is the name of the resource being requested. This is not the kind. For example: pods
Resource string
2015-04-10 17:37:13 +00:00
// Subresource is the name of the subresource being requested. This is a different resource, scoped to the parent resource, but it may have a different kind.
// For instance, /pods has the resource "pods" and the kind "Pod", while /pods/foo/status has the resource "pods", the sub resource "status", and the kind "Pod"
// (because status operates on pods). The binding resource for a pod though may be /pods/foo/binding, which has resource "pods", subresource "binding", and kind "Binding".
2015-04-10 16:42:52 +00:00
Subresource string
2015-01-29 19:14:36 +00:00
// Kind is the type of object being manipulated. For example: Pod
Kind string
// Name is empty for some verbs, but if the request directly indicates a name (not in body content) then this field is filled in.
Name string
2015-02-24 01:17:39 +00:00
// Parts are the path parts for the request, always starting with /{resource}/{name}
2015-01-29 19:14:36 +00:00
Parts [ ] string
2015-02-24 01:17:39 +00:00
// Raw is the unparsed form of everything other than parts.
// Raw + Parts = complete URL path
Raw [ ] string
}
2015-01-29 19:14:36 +00:00
type APIRequestInfoResolver struct {
2015-09-09 17:45:01 +00:00
APIPrefixes sets . String
2015-02-16 18:53:31 +00:00
RestMapper meta . RESTMapper
2015-01-29 19:14:36 +00:00
}
2015-04-10 17:37:13 +00:00
// TODO write an integration test against the swagger doc to test the APIRequestInfo and match up behavior to responses
2015-01-29 19:14:36 +00:00
// GetAPIRequestInfo returns the information from the http request. If error is not nil, APIRequestInfo holds the information as best it is known before the failure
2014-12-09 19:23:21 +00:00
// Valid Inputs:
// Storage paths
2015-01-19 21:50:00 +00:00
// /namespaces
// /namespaces/{namespace}
// /namespaces/{namespace}/{resource}
// /namespaces/{namespace}/{resource}/{resourceName}
2015-01-29 19:14:36 +00:00
// /{resource}
// /{resource}/{resourceName}
2014-12-09 19:23:21 +00:00
//
// Special verbs:
2015-01-29 19:14:36 +00:00
// /proxy/{resource}/{resourceName}
2015-03-24 12:00:26 +00:00
// /proxy/namespaces/{namespace}/{resource}/{resourceName}
// /redirect/namespaces/{namespace}/{resource}/{resourceName}
2015-01-29 19:14:36 +00:00
// /redirect/{resource}/{resourceName}
// /watch/{resource}
2015-03-24 12:00:26 +00:00
// /watch/namespaces/{namespace}/{resource}
2014-12-09 19:23:21 +00:00
//
// Fully qualified paths for above:
// /api/{version}/*
// /api/{version}/*
2015-01-29 19:14:36 +00:00
func ( r * APIRequestInfoResolver ) GetAPIRequestInfo ( req * http . Request ) ( APIRequestInfo , error ) {
2015-02-24 01:17:39 +00:00
requestInfo := APIRequestInfo {
Raw : splitPath ( req . URL . Path ) ,
}
2015-01-29 19:14:36 +00:00
2015-02-24 01:17:39 +00:00
currentParts := requestInfo . Raw
2015-01-29 19:14:36 +00:00
if len ( currentParts ) < 1 {
return requestInfo , fmt . Errorf ( "Unable to determine kind and namespace from an empty URL path" )
2014-12-09 19:23:21 +00:00
}
2015-02-16 18:53:31 +00:00
for _ , currPrefix := range r . APIPrefixes . List ( ) {
2015-01-29 19:14:36 +00:00
// handle input of form /api/{version}/* by adjusting special paths
if currentParts [ 0 ] == currPrefix {
if len ( currentParts ) > 1 {
requestInfo . APIVersion = currentParts [ 1 ]
}
if len ( currentParts ) > 2 {
currentParts = currentParts [ 2 : ]
} else {
return requestInfo , fmt . Errorf ( "Unable to determine kind and namespace from url, %v" , req . URL )
}
2014-12-09 19:23:21 +00:00
}
}
// handle input of form /{specialVerb}/*
2015-01-29 19:14:36 +00:00
if _ , ok := specialVerbs [ currentParts [ 0 ] ] ; ok {
requestInfo . Verb = currentParts [ 0 ]
if len ( currentParts ) > 1 {
currentParts = currentParts [ 1 : ]
2014-12-09 19:23:21 +00:00
} else {
2015-01-29 19:14:36 +00:00
return requestInfo , fmt . Errorf ( "Unable to determine kind and namespace from url, %v" , req . URL )
}
} else {
switch req . Method {
case "POST" :
requestInfo . Verb = "create"
case "GET" :
requestInfo . Verb = "get"
case "PUT" :
requestInfo . Verb = "update"
case "DELETE" :
requestInfo . Verb = "delete"
2014-12-09 19:23:21 +00:00
}
2015-01-29 19:14:36 +00:00
2014-12-09 19:23:21 +00:00
}
2015-01-19 21:50:00 +00:00
// URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind
if currentParts [ 0 ] == "namespaces" {
2015-04-10 16:42:52 +00:00
if len ( currentParts ) > 1 {
2015-01-19 21:50:00 +00:00
requestInfo . Namespace = currentParts [ 1 ]
2015-04-10 16:42:52 +00:00
// if there is another step after the namespace name and it is not a known namespace subresource
// move currentParts to include it as a resource in its own right
2015-04-10 17:37:13 +00:00
if len ( currentParts ) > 2 {
2015-04-10 16:42:52 +00:00
currentParts = currentParts [ 2 : ]
}
2014-12-09 19:23:21 +00:00
}
2015-01-29 19:14:36 +00:00
} else {
2015-06-10 20:17:04 +00:00
requestInfo . Namespace = api . NamespaceNone
2014-12-09 19:23:21 +00:00
}
2015-01-29 19:14:36 +00:00
// parsing successful, so we now know the proper value for .Parts
requestInfo . Parts = currentParts
2015-02-24 01:17:39 +00:00
// Raw should have everything not in Parts
requestInfo . Raw = requestInfo . Raw [ : len ( requestInfo . Raw ) - len ( currentParts ) ]
2015-01-29 19:14:36 +00:00
2015-04-10 16:42:52 +00:00
// parts look like: resource/resourceName/subresource/other/stuff/we/don't/interpret
switch {
case len ( requestInfo . Parts ) >= 3 :
requestInfo . Subresource = requestInfo . Parts [ 2 ]
fallthrough
case len ( requestInfo . Parts ) == 2 :
2015-01-29 19:14:36 +00:00
requestInfo . Name = requestInfo . Parts [ 1 ]
2015-04-10 16:42:52 +00:00
fallthrough
case len ( requestInfo . Parts ) == 1 :
requestInfo . Resource = requestInfo . Parts [ 0 ]
2015-01-29 19:14:36 +00:00
}
// if there's no name on the request and we thought it was a get before, then the actual verb is a list
if len ( requestInfo . Name ) == 0 && requestInfo . Verb == "get" {
requestInfo . Verb = "list"
}
// if we have a resource, we have a good shot at being able to determine kind
if len ( requestInfo . Resource ) > 0 {
2015-02-16 18:53:31 +00:00
_ , requestInfo . Kind , _ = r . RestMapper . VersionAndKindForResource ( requestInfo . Resource )
2015-01-29 19:14:36 +00:00
}
return requestInfo , nil
2014-12-09 19:23:21 +00:00
}