2016-08-23 14:09:54 +00:00
/ *
Copyright 2016 The Kubernetes Authors .
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 genericapiserver
import (
"crypto/tls"
"fmt"
2016-09-26 09:20:04 +00:00
"io"
2016-08-23 14:09:54 +00:00
"net"
"net/http"
"os"
2016-09-20 14:26:29 +00:00
"regexp"
2016-08-23 14:09:54 +00:00
"strconv"
2016-09-27 09:35:39 +00:00
"strings"
2016-08-23 14:09:54 +00:00
"time"
"github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
"github.com/golang/glog"
"gopkg.in/natefinch/lumberjack.v2"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apiserver"
2016-09-26 11:07:35 +00:00
apiserverfilters "k8s.io/kubernetes/pkg/apiserver/filters"
2016-09-27 09:35:39 +00:00
"k8s.io/kubernetes/pkg/apiserver/request"
2016-08-23 14:09:54 +00:00
"k8s.io/kubernetes/pkg/auth/authenticator"
"k8s.io/kubernetes/pkg/auth/authorizer"
2016-09-21 09:36:44 +00:00
authhandlers "k8s.io/kubernetes/pkg/auth/handlers"
2016-08-23 14:09:54 +00:00
"k8s.io/kubernetes/pkg/cloudprovider"
2016-09-21 09:36:44 +00:00
genericfilters "k8s.io/kubernetes/pkg/genericapiserver/filters"
2016-09-21 21:42:15 +00:00
"k8s.io/kubernetes/pkg/genericapiserver/openapi/common"
2016-08-23 14:09:54 +00:00
"k8s.io/kubernetes/pkg/genericapiserver/options"
2016-09-06 11:20:36 +00:00
"k8s.io/kubernetes/pkg/genericapiserver/routes"
2016-08-23 14:09:54 +00:00
genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation"
2016-09-21 13:14:26 +00:00
ipallocator "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
2016-08-23 14:09:54 +00:00
"k8s.io/kubernetes/pkg/runtime"
utilnet "k8s.io/kubernetes/pkg/util/net"
2016-09-27 09:35:39 +00:00
"k8s.io/kubernetes/pkg/util/sets"
2016-08-23 14:09:54 +00:00
)
// Config is a structure used to configure a GenericAPIServer.
type Config struct {
2016-09-26 09:20:04 +00:00
// Destination for audit logs
AuditWriter io . Writer
2016-08-23 14:09:54 +00:00
// Allow downstream consumers to disable swagger.
// This includes returning the generated swagger spec at /swaggerapi and swagger ui at /swagger-ui.
EnableSwaggerSupport bool
// Allow downstream consumers to disable swagger ui.
2016-09-06 11:20:36 +00:00
// Note that this is ignored if EnableSwaggerSupport is false
2016-08-23 14:09:54 +00:00
EnableSwaggerUI bool
// Allows api group versions or specific resources to be conditionally enabled/disabled.
APIResourceConfigSource APIResourceConfigSource
// allow downstream consumers to disable the index route
2016-09-12 23:14:04 +00:00
EnableIndex bool
EnableProfiling bool
2016-09-06 11:20:36 +00:00
EnableVersion bool
2016-09-12 23:14:04 +00:00
EnableGarbageCollection bool
APIPrefix string
APIGroupPrefix string
CorsAllowedOriginList [ ] string
Authenticator authenticator . Request
2016-08-23 14:09:54 +00:00
// TODO(roberthbailey): Remove once the server no longer supports http basic auth.
SupportsBasicAuth bool
Authorizer authorizer . Authorizer
AdmissionControl admission . Interface
MasterServiceNamespace string
// TODO(ericchiang): Determine if policy escalation checks should be an admission controller.
AuthorizerRBACSuperUser string
// Map requests to contexts. Exported so downstream consumers can provider their own mappers
RequestContextMapper api . RequestContextMapper
// Required, the interface for serializing and converting objects to and from the wire
Serializer runtime . NegotiatedSerializer
// If specified, all web services will be registered into this container
RestfulContainer * restful . Container
// If specified, requests will be allocated a random timeout between this value, and twice this value.
// Note that it is up to the request handlers to ignore or honor this timeout. In seconds.
MinRequestTimeout int
// Number of masters running; all masters must be started with the
// same value for this field. (Numbers > 1 currently untested.)
MasterCount int
// The port on PublicAddress where a read-write server will be installed.
// Defaults to 6443 if not set.
ReadWritePort int
// ExternalHost is the host name to use for external (public internet) facing URLs (e.g. Swagger)
ExternalHost string
// PublicAddress is the IP address where members of the cluster (kubelet,
// kube-proxy, services, etc.) can reach the GenericAPIServer.
// If nil or 0.0.0.0, the host's default interface will be used.
PublicAddress net . IP
// Control the interval that pod, node IP, and node heath status caches
// expire.
CacheTimeout time . Duration
// The range of IPs to be assigned to services with type=ClusterIP or greater
ServiceClusterIPRange * net . IPNet
// The IP address for the GenericAPIServer service (must be inside ServiceClusterIPRange)
ServiceReadWriteIP net . IP
// Port for the apiserver service.
ServiceReadWritePort int
// The range of ports to be assigned to services with type=NodePort or greater
ServiceNodePortRange utilnet . PortRange
// Used to customize default proxy dial/tls options
ProxyDialer apiserver . ProxyDialerFunc
ProxyTLSClientConfig * tls . Config
// Additional ports to be exposed on the GenericAPIServer service
// extraServicePorts is injectable in the event that more ports
// (other than the default 443/tcp) are exposed on the GenericAPIServer
// and those ports need to be load balanced by the GenericAPIServer
// service because this pkg is linked by out-of-tree projects
// like openshift which want to use the GenericAPIServer but also do
// more stuff.
ExtraServicePorts [ ] api . ServicePort
// Additional ports to be exposed on the GenericAPIServer endpoints
// Port names should align with ports defined in ExtraServicePorts
ExtraEndpointPorts [ ] api . EndpointPort
KubernetesServiceNodePort int
// EnableOpenAPISupport enables OpenAPI support. Allow downstream customers to disable OpenAPI spec.
EnableOpenAPISupport bool
// OpenAPIInfo will be directly available as Info section of Open API spec.
OpenAPIInfo spec . Info
// OpenAPIDefaultResponse will be used if an web service operation does not have any responses listed.
OpenAPIDefaultResponse spec . Response
2016-09-14 00:11:36 +00:00
// OpenAPIDefinitions is a map of type to OpenAPI spec for all types used in this API server. Failure to provide
// this map or any of the models used by the server APIs will result in spec generation failure.
OpenAPIDefinitions * common . OpenAPIDefinitions
2016-09-20 14:26:29 +00:00
// MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further
// request has to wait.
2016-09-26 09:25:02 +00:00
MaxRequestsInFlight int
// Predicate which is true for paths of long-running http requests
LongRunningFunc genericfilters . LongRunningRequestCheck
2016-08-23 14:09:54 +00:00
}
func NewConfig ( options * options . ServerRunOptions ) * Config {
2016-09-26 09:25:02 +00:00
longRunningRE := regexp . MustCompile ( options . LongRunningRequestRE )
2016-09-26 09:20:04 +00:00
var auditWriter io . Writer
if len ( options . AuditLogPath ) != 0 {
auditWriter = & lumberjack . Logger {
Filename : options . AuditLogPath ,
MaxAge : options . AuditLogMaxAge ,
MaxBackups : options . AuditLogMaxBackups ,
MaxSize : options . AuditLogMaxSize ,
}
}
2016-08-23 14:09:54 +00:00
return & Config {
APIGroupPrefix : options . APIGroupPrefix ,
APIPrefix : options . APIPrefix ,
CorsAllowedOriginList : options . CorsAllowedOriginList ,
2016-09-26 09:20:04 +00:00
AuditWriter : auditWriter ,
2016-09-12 23:14:04 +00:00
EnableGarbageCollection : options . EnableGarbageCollection ,
2016-08-23 14:09:54 +00:00
EnableIndex : true ,
EnableProfiling : options . EnableProfiling ,
EnableSwaggerSupport : true ,
EnableSwaggerUI : options . EnableSwaggerUI ,
2016-09-06 11:20:36 +00:00
EnableVersion : true ,
2016-08-23 14:09:54 +00:00
ExternalHost : options . ExternalHost ,
KubernetesServiceNodePort : options . KubernetesServiceNodePort ,
MasterCount : options . MasterCount ,
MinRequestTimeout : options . MinRequestTimeout ,
PublicAddress : options . AdvertiseAddress ,
ReadWritePort : options . SecurePort ,
ServiceClusterIPRange : & options . ServiceClusterIPRange ,
ServiceNodePortRange : options . ServiceNodePortRange ,
OpenAPIDefaultResponse : spec . Response {
ResponseProps : spec . ResponseProps {
Description : "Default Response." } } ,
OpenAPIInfo : spec . Info {
InfoProps : spec . InfoProps {
Title : "Generic API Server" ,
Version : "unversioned" ,
} ,
} ,
2016-09-26 09:25:02 +00:00
MaxRequestsInFlight : options . MaxRequestsInFlight ,
LongRunningFunc : genericfilters . BasicLongRunningRequestCheck ( longRunningRE , map [ string ] string { "watch" : "true" } ) ,
2016-08-23 14:09:54 +00:00
}
}
2016-09-28 16:39:31 +00:00
type completedConfig struct {
* Config
}
2016-09-27 15:52:31 +00:00
// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
2016-09-28 16:39:31 +00:00
func ( c * Config ) Complete ( ) completedConfig {
2016-08-23 14:09:54 +00:00
if c . ServiceClusterIPRange == nil {
defaultNet := "10.0.0.0/24"
glog . Warningf ( "Network range for service cluster IPs is unspecified. Defaulting to %v." , defaultNet )
_ , serviceClusterIPRange , err := net . ParseCIDR ( defaultNet )
if err != nil {
glog . Fatalf ( "Unable to parse CIDR: %v" , err )
}
if size := ipallocator . RangeSize ( serviceClusterIPRange ) ; size < 8 {
glog . Fatalf ( "The service cluster IP range must be at least %d IP addresses" , 8 )
}
c . ServiceClusterIPRange = serviceClusterIPRange
}
if c . ServiceReadWriteIP == nil {
// Select the first valid IP from ServiceClusterIPRange to use as the GenericAPIServer service IP.
serviceReadWriteIP , err := ipallocator . GetIndexedIP ( c . ServiceClusterIPRange , 1 )
if err != nil {
glog . Fatalf ( "Failed to generate service read-write IP for GenericAPIServer service: %v" , err )
}
glog . V ( 4 ) . Infof ( "Setting GenericAPIServer service IP to %q (read-write)." , serviceReadWriteIP )
c . ServiceReadWriteIP = serviceReadWriteIP
}
if c . ServiceReadWritePort == 0 {
c . ServiceReadWritePort = 443
}
if c . ServiceNodePortRange . Size == 0 {
// TODO: Currently no way to specify an empty range (do we need to allow this?)
// We should probably allow this for clouds that don't require NodePort to do load-balancing (GCE)
// but then that breaks the strict nestedness of ServiceType.
// Review post-v1
c . ServiceNodePortRange = options . DefaultServiceNodePortRange
glog . Infof ( "Node port range unspecified. Defaulting to %v." , c . ServiceNodePortRange )
}
if c . MasterCount == 0 {
// Clearly, there will be at least one GenericAPIServer.
c . MasterCount = 1
}
if c . ReadWritePort == 0 {
c . ReadWritePort = 6443
}
if c . CacheTimeout == 0 {
c . CacheTimeout = 5 * time . Second
}
if c . RequestContextMapper == nil {
c . RequestContextMapper = api . NewRequestContextMapper ( )
}
if len ( c . ExternalHost ) == 0 && c . PublicAddress != nil {
hostAndPort := c . PublicAddress . String ( )
if c . ReadWritePort != 0 {
hostAndPort = net . JoinHostPort ( hostAndPort , strconv . Itoa ( c . ReadWritePort ) )
}
c . ExternalHost = hostAndPort
}
2016-09-28 16:39:31 +00:00
return completedConfig { c }
}
// SkipComplete provides a way to construct a server instance without config completion.
func ( c * Config ) SkipComplete ( ) completedConfig {
return completedConfig { c }
2016-08-23 14:09:54 +00:00
}
// New returns a new instance of GenericAPIServer from the given config.
// Certain config fields will be set to a default value if unset,
// including:
// ServiceClusterIPRange
// ServiceNodePortRange
// MasterCount
// ReadWritePort
// PublicAddress
// Public fields:
// Handler -- The returned GenericAPIServer has a field TopHandler which is an
// http.Handler which handles all the endpoints provided by the GenericAPIServer,
// including the API, the UI, and miscellaneous debugging endpoints. All
// these are subject to authorization and authentication.
// InsecureHandler -- an http.Handler which handles all the same
// endpoints as Handler, but no authorization and authentication is done.
// Public methods:
// HandleWithAuth -- Allows caller to add an http.Handler for an endpoint
// that uses the same authentication and authorization (if any is configured)
// as the GenericAPIServer's built-in endpoints.
// If the caller wants to add additional endpoints not using the GenericAPIServer's
// auth, then the caller should create a handler for those endpoints, which delegates the
// any unhandled paths to "Handler".
2016-09-28 16:39:31 +00:00
func ( c completedConfig ) New ( ) ( * GenericAPIServer , error ) {
2016-08-23 14:09:54 +00:00
if c . Serializer == nil {
return nil , fmt . Errorf ( "Genericapiserver.New() called with config.Serializer == nil" )
}
s := & GenericAPIServer {
ServiceClusterIPRange : c . ServiceClusterIPRange ,
ServiceNodePortRange : c . ServiceNodePortRange ,
legacyAPIPrefix : c . APIPrefix ,
apiPrefix : c . APIGroupPrefix ,
admissionControl : c . AdmissionControl ,
requestContextMapper : c . RequestContextMapper ,
Serializer : c . Serializer ,
minRequestTimeout : time . Duration ( c . MinRequestTimeout ) * time . Second ,
enableSwaggerSupport : c . EnableSwaggerSupport ,
MasterCount : c . MasterCount ,
ExternalAddress : c . ExternalHost ,
ClusterIP : c . PublicAddress ,
PublicReadWritePort : c . ReadWritePort ,
ServiceReadWriteIP : c . ServiceReadWriteIP ,
ServiceReadWritePort : c . ServiceReadWritePort ,
ExtraServicePorts : c . ExtraServicePorts ,
ExtraEndpointPorts : c . ExtraEndpointPorts ,
KubernetesServiceNodePort : c . KubernetesServiceNodePort ,
apiGroupsForDiscovery : map [ string ] unversioned . APIGroup { } ,
enableOpenAPISupport : c . EnableOpenAPISupport ,
openAPIInfo : c . OpenAPIInfo ,
openAPIDefaultResponse : c . OpenAPIDefaultResponse ,
2016-09-14 00:11:36 +00:00
openAPIDefinitions : c . OpenAPIDefinitions ,
2016-08-23 14:09:54 +00:00
}
if c . RestfulContainer != nil {
s . HandlerContainer = c . RestfulContainer
} else {
2016-09-06 11:20:36 +00:00
s . HandlerContainer = NewHandlerContainer ( http . NewServeMux ( ) , c . Serializer )
2016-08-23 14:09:54 +00:00
}
// Use CurlyRouter to be able to use regular expressions in paths. Regular expressions are required in paths for example for proxy (where the path is proxy/{kind}/{name}/{*})
s . HandlerContainer . Router ( restful . CurlyRouter { } )
2016-09-06 11:20:36 +00:00
s . Mux = apiserver . NewPathRecorderMux ( s . HandlerContainer . ServeMux )
2016-09-27 09:29:22 +00:00
apiserver . InstallServiceErrorHandler ( s . Serializer , s . HandlerContainer )
2016-08-23 14:09:54 +00:00
if c . ProxyDialer != nil || c . ProxyTLSClientConfig != nil {
s . ProxyTransport = utilnet . SetTransportDefaults ( & http . Transport {
Dial : c . ProxyDialer ,
TLSClientConfig : c . ProxyTLSClientConfig ,
} )
}
2016-09-28 16:39:31 +00:00
s . installAPI ( c . Config )
s . Handler , s . InsecureHandler = s . buildHandlerChains ( c . Config , http . Handler ( s . Mux . BaseMux ( ) . ( * http . ServeMux ) ) )
2016-09-21 09:36:44 +00:00
return s , nil
}
func ( s * GenericAPIServer ) buildHandlerChains ( c * Config , handler http . Handler ) ( secure http . Handler , insecure http . Handler ) {
// filters which insecure and secure have in common
2016-09-28 23:31:29 +00:00
handler = genericfilters . WithCORS ( handler , c . CorsAllowedOriginList , nil , nil , nil , "true" )
2016-09-21 09:36:44 +00:00
// insecure filters
insecure = handler
2016-09-26 15:18:19 +00:00
insecure = genericfilters . WithPanicRecovery ( insecure , c . RequestContextMapper )
2016-09-27 09:35:39 +00:00
insecure = apiserverfilters . WithRequestInfo ( insecure , NewRequestInfoResolver ( c ) , c . RequestContextMapper )
2016-09-26 15:18:19 +00:00
insecure = api . WithRequestContext ( insecure , c . RequestContextMapper )
2016-09-26 09:25:02 +00:00
insecure = genericfilters . WithTimeoutForNonLongRunningRequests ( insecure , c . LongRunningFunc )
2016-09-21 09:36:44 +00:00
// secure filters
2016-09-26 15:18:19 +00:00
attributeGetter := apiserverfilters . NewRequestAttributeGetter ( c . RequestContextMapper )
2016-09-21 09:36:44 +00:00
secure = handler
2016-09-26 11:07:35 +00:00
secure = apiserverfilters . WithAuthorization ( secure , attributeGetter , c . Authorizer )
secure = apiserverfilters . WithImpersonation ( secure , c . RequestContextMapper , c . Authorizer )
2016-09-26 09:20:04 +00:00
secure = apiserverfilters . WithAudit ( secure , attributeGetter , c . AuditWriter ) // before impersonation to read original user
2016-09-21 09:36:44 +00:00
secure = authhandlers . WithAuthentication ( secure , c . RequestContextMapper , c . Authenticator , authhandlers . Unauthorized ( c . SupportsBasicAuth ) )
2016-09-26 15:18:19 +00:00
secure = genericfilters . WithPanicRecovery ( secure , c . RequestContextMapper )
2016-09-27 09:35:39 +00:00
secure = apiserverfilters . WithRequestInfo ( secure , NewRequestInfoResolver ( c ) , c . RequestContextMapper )
2016-09-26 15:18:19 +00:00
secure = api . WithRequestContext ( secure , c . RequestContextMapper )
2016-09-26 09:25:02 +00:00
secure = genericfilters . WithTimeoutForNonLongRunningRequests ( secure , c . LongRunningFunc )
secure = genericfilters . WithMaxInFlightLimit ( secure , c . MaxRequestsInFlight , c . LongRunningFunc )
2016-09-21 09:36:44 +00:00
return
}
func ( s * GenericAPIServer ) installAPI ( c * Config ) {
2016-08-23 14:09:54 +00:00
if c . EnableIndex {
2016-09-06 11:20:36 +00:00
routes . Index { } . Install ( s . Mux , s . HandlerContainer )
2016-08-23 14:09:54 +00:00
}
2016-09-06 11:20:36 +00:00
if c . EnableSwaggerSupport && c . EnableSwaggerUI {
routes . SwaggerUI { } . Install ( s . Mux , s . HandlerContainer )
2016-08-23 14:09:54 +00:00
}
if c . EnableProfiling {
2016-09-06 11:20:36 +00:00
routes . Profiling { } . Install ( s . Mux , s . HandlerContainer )
}
if c . EnableVersion {
routes . Version { } . Install ( s . Mux , s . HandlerContainer )
2016-08-23 14:09:54 +00:00
}
2016-09-21 09:36:44 +00:00
s . HandlerContainer . Add ( s . DynamicApisDiscovery ( ) )
2016-08-23 14:09:54 +00:00
}
func DefaultAndValidateRunOptions ( options * options . ServerRunOptions ) {
genericvalidation . ValidateRunOptions ( options )
// If advertise-address is not specified, use bind-address. If bind-address
// is not usable (unset, 0.0.0.0, or loopback), we will use the host's default
// interface as valid public addr for master (see: util/net#ValidPublicAddrForMaster)
if options . AdvertiseAddress == nil || options . AdvertiseAddress . IsUnspecified ( ) {
hostIP , err := utilnet . ChooseBindAddress ( options . BindAddress )
if err != nil {
glog . Fatalf ( "Unable to find suitable network address.error='%v' . " +
"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this." , err )
}
options . AdvertiseAddress = hostIP
}
glog . Infof ( "Will report %v as public IP address." , options . AdvertiseAddress )
// Set default value for ExternalHost if not specified.
if len ( options . ExternalHost ) == 0 {
// TODO: extend for other providers
if options . CloudProvider == "gce" {
cloud , err := cloudprovider . InitCloudProvider ( options . CloudProvider , options . CloudConfigFile )
if err != nil {
glog . Fatalf ( "Cloud provider could not be initialized: %v" , err )
}
instances , supported := cloud . Instances ( )
if ! supported {
glog . Fatalf ( "GCE cloud provider has no instances. this shouldn't happen. exiting." )
}
2016-07-16 06:10:29 +00:00
hostname , err := os . Hostname ( )
2016-08-23 14:09:54 +00:00
if err != nil {
glog . Fatalf ( "Failed to get hostname: %v" , err )
}
2016-07-16 06:10:29 +00:00
nodeName , err := instances . CurrentNodeName ( hostname )
if err != nil {
glog . Fatalf ( "Failed to get NodeName: %v" , err )
}
addrs , err := instances . NodeAddresses ( nodeName )
2016-08-23 14:09:54 +00:00
if err != nil {
glog . Warningf ( "Unable to obtain external host address from cloud provider: %v" , err )
} else {
for _ , addr := range addrs {
if addr . Type == api . NodeExternalIP {
options . ExternalHost = addr . Address
}
}
}
}
}
}
2016-09-27 09:35:39 +00:00
func NewRequestInfoResolver ( c * Config ) * request . RequestInfoResolver {
return & request . RequestInfoResolver {
APIPrefixes : sets . NewString ( strings . Trim ( c . APIPrefix , "/" ) , strings . Trim ( c . APIGroupPrefix , "/" ) ) , // all possible API prefixes
GrouplessAPIPrefixes : sets . NewString ( strings . Trim ( c . APIPrefix , "/" ) ) , // APIPrefixes that won't have groups (legacy)
}
}