2014-06-16 06:29:07 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2014-06-16 06:29:07 +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 master
import (
2015-10-09 05:18:16 +00:00
"crypto/tls"
2014-11-02 20:52:31 +00:00
"fmt"
2014-09-18 23:03:34 +00:00
"net"
2014-10-23 23:55:14 +00:00
"net/http"
2015-03-13 15:44:11 +00:00
"net/http/pprof"
2014-11-02 20:52:31 +00:00
"net/url"
2014-10-28 00:56:33 +00:00
"strconv"
2014-10-23 23:55:14 +00:00
"strings"
2015-06-04 18:58:38 +00:00
"sync"
2014-06-16 06:29:07 +00:00
"time"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/latest"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/rest"
2015-10-09 01:33:33 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-09-29 21:36:47 +00:00
apiutil "k8s.io/kubernetes/pkg/api/util"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api/v1"
2015-10-09 22:04:41 +00:00
expapi "k8s.io/kubernetes/pkg/apis/extensions"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/apiserver"
"k8s.io/kubernetes/pkg/auth/authenticator"
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/handlers"
2015-08-13 19:01:50 +00:00
client "k8s.io/kubernetes/pkg/client/unversioned"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/healthz"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/master/ports"
"k8s.io/kubernetes/pkg/registry/componentstatus"
controlleretcd "k8s.io/kubernetes/pkg/registry/controller/etcd"
2015-08-29 05:19:32 +00:00
deploymentetcd "k8s.io/kubernetes/pkg/registry/deployment/etcd"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/registry/endpoint"
endpointsetcd "k8s.io/kubernetes/pkg/registry/endpoint/etcd"
2015-08-11 09:21:00 +00:00
eventetcd "k8s.io/kubernetes/pkg/registry/event/etcd"
2015-08-06 16:53:34 +00:00
expcontrolleretcd "k8s.io/kubernetes/pkg/registry/experimental/controller/etcd"
2015-09-22 18:26:36 +00:00
ingressetcd "k8s.io/kubernetes/pkg/registry/ingress/etcd"
2015-08-18 14:39:49 +00:00
jobetcd "k8s.io/kubernetes/pkg/registry/job/etcd"
2015-08-11 07:05:40 +00:00
limitrangeetcd "k8s.io/kubernetes/pkg/registry/limitrange/etcd"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/registry/namespace"
namespaceetcd "k8s.io/kubernetes/pkg/registry/namespace/etcd"
2015-09-09 14:18:17 +00:00
"k8s.io/kubernetes/pkg/registry/node"
nodeetcd "k8s.io/kubernetes/pkg/registry/node/etcd"
2015-08-05 22:03:47 +00:00
pvetcd "k8s.io/kubernetes/pkg/registry/persistentvolume/etcd"
pvcetcd "k8s.io/kubernetes/pkg/registry/persistentvolumeclaim/etcd"
podetcd "k8s.io/kubernetes/pkg/registry/pod/etcd"
podtemplateetcd "k8s.io/kubernetes/pkg/registry/podtemplate/etcd"
resourcequotaetcd "k8s.io/kubernetes/pkg/registry/resourcequota/etcd"
secretetcd "k8s.io/kubernetes/pkg/registry/secret/etcd"
"k8s.io/kubernetes/pkg/registry/service"
etcdallocator "k8s.io/kubernetes/pkg/registry/service/allocator/etcd"
2015-08-06 10:02:01 +00:00
serviceetcd "k8s.io/kubernetes/pkg/registry/service/etcd"
2015-08-05 22:03:47 +00:00
ipallocator "k8s.io/kubernetes/pkg/registry/service/ipallocator"
serviceaccountetcd "k8s.io/kubernetes/pkg/registry/serviceaccount/etcd"
2015-08-15 05:10:15 +00:00
thirdpartyresourceetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresource/etcd"
2015-08-20 05:08:26 +00:00
"k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
2015-08-19 18:02:01 +00:00
thirdpartyresourcedataetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata/etcd"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/ui"
"k8s.io/kubernetes/pkg/util"
2015-09-09 17:45:01 +00:00
"k8s.io/kubernetes/pkg/util/sets"
2015-10-19 21:08:35 +00:00
utilsets "k8s.io/kubernetes/pkg/util/sets"
2014-10-23 23:55:14 +00:00
2015-09-08 23:58:25 +00:00
daemonetcd "k8s.io/kubernetes/pkg/registry/daemonset/etcd"
2015-08-06 14:14:28 +00:00
horizontalpodautoscaleretcd "k8s.io/kubernetes/pkg/registry/horizontalpodautoscaler/etcd"
2014-11-11 07:11:45 +00:00
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful/swagger"
2014-10-23 23:55:14 +00:00
"github.com/golang/glog"
2015-07-14 19:30:43 +00:00
"github.com/prometheus/client_golang/prometheus"
2015-10-09 14:49:01 +00:00
"golang.org/x/net/context"
2015-08-05 22:05:17 +00:00
"k8s.io/kubernetes/pkg/registry/service/allocator"
"k8s.io/kubernetes/pkg/registry/service/portallocator"
2014-06-16 06:29:07 +00:00
)
2015-03-11 17:10:09 +00:00
const (
DefaultEtcdPathPrefix = "/registry"
)
2015-09-30 07:56:51 +00:00
// StorageDestinations is a mapping from API group & resource to
// the underlying storage interfaces.
type StorageDestinations struct {
APIGroups map [ string ] * StorageDestinationsForAPIGroup
}
type StorageDestinationsForAPIGroup struct {
Default storage . Interface
Overrides map [ string ] storage . Interface
}
func NewStorageDestinations ( ) StorageDestinations {
return StorageDestinations {
APIGroups : map [ string ] * StorageDestinationsForAPIGroup { } ,
}
}
func ( s * StorageDestinations ) AddAPIGroup ( group string , defaultStorage storage . Interface ) {
s . APIGroups [ group ] = & StorageDestinationsForAPIGroup {
Default : defaultStorage ,
Overrides : map [ string ] storage . Interface { } ,
}
}
func ( s * StorageDestinations ) AddStorageOverride ( group , resource string , override storage . Interface ) {
if _ , ok := s . APIGroups [ group ] ; ! ok {
s . AddAPIGroup ( group , nil )
}
if s . APIGroups [ group ] . Overrides == nil {
s . APIGroups [ group ] . Overrides = map [ string ] storage . Interface { }
}
s . APIGroups [ group ] . Overrides [ resource ] = override
}
func ( s * StorageDestinations ) get ( group , resource string ) storage . Interface {
apigroup , ok := s . APIGroups [ group ]
if ! ok {
glog . Errorf ( "No storage defined for API group: '%s'" , apigroup )
return nil
}
if apigroup . Overrides != nil {
if client , exists := apigroup . Overrides [ resource ] ; exists {
return client
}
}
return apigroup . Default
}
// Get all backends for all registered storage destinations.
// Used for getting all instances for health validations.
func ( s * StorageDestinations ) backends ( ) [ ] string {
backends := sets . String { }
for _ , group := range s . APIGroups {
if group . Default != nil {
2015-10-09 14:49:01 +00:00
for _ , backend := range group . Default . Backends ( context . TODO ( ) ) {
2015-09-30 07:56:51 +00:00
backends . Insert ( backend )
}
}
if group . Overrides != nil {
for _ , storage := range group . Overrides {
2015-10-09 14:49:01 +00:00
for _ , backend := range storage . Backends ( context . TODO ( ) ) {
2015-09-30 07:56:51 +00:00
backends . Insert ( backend )
}
}
}
}
return backends . List ( )
}
2015-10-13 00:40:37 +00:00
// Specifies the overrides for various API group versions.
// This can be used to enable/disable entire group versions or specific resources.
type APIGroupVersionOverride struct {
// Whether to enable or disable this group version.
Disable bool
// List of overrides for individual resources in this group version.
ResourceOverrides map [ string ] bool
}
2014-07-27 02:16:39 +00:00
// Config is a structure used to configure a Master.
type Config struct {
2015-09-30 07:56:51 +00:00
StorageDestinations StorageDestinations
2015-09-15 03:55:18 +00:00
// StorageVersions is a map between groups and their storage versions
StorageVersions map [ string ] string
EventTTL time . Duration
NodeRegexp string
KubeletClient client . KubeletClient
2015-05-03 22:44:05 +00:00
// allow downstream consumers to disable the core controller loops
EnableCoreControllers bool
EnableLogsSupport bool
EnableUISupport bool
2015-01-30 23:53:04 +00:00
// allow downstream consumers to disable swagger
EnableSwaggerSupport bool
2015-10-13 00:40:37 +00:00
// Allows api group versions or specific resources to be conditionally enabled/disabled.
APIGroupVersionOverrides map [ string ] APIGroupVersionOverride
2015-01-30 23:53:04 +00:00
// allow downstream consumers to disable the index route
2015-04-28 05:24:26 +00:00
EnableIndex bool
EnableProfiling bool
2015-08-28 15:10:05 +00:00
EnableWatchCache bool
2015-04-28 05:24:26 +00:00
APIPrefix string
2015-09-14 22:30:32 +00:00
APIGroupPrefix string
2015-08-05 14:21:47 +00:00
CorsAllowedOriginList [ ] string
2015-04-28 05:24:26 +00:00
Authenticator authenticator . Request
// TODO(roberthbailey): Remove once the server no longer supports http basic auth.
SupportsBasicAuth bool
2015-01-08 15:25:14 +00:00
Authorizer authorizer . Authorizer
AdmissionControl admission . Interface
MasterServiceNamespace string
2014-10-28 00:56:33 +00:00
2015-02-11 22:09:25 +00:00
// Map requests to contexts. Exported so downstream consumers can provider their own mappers
RequestContextMapper api . RequestContextMapper
2014-12-15 20:29:55 +00:00
// If specified, all web services will be registered into this container
RestfulContainer * restful . Container
2015-05-27 01:39:42 +00:00
// If specified, requests will be allocated a random timeout between this value, and twice this value.
2015-06-16 02:39:31 +00:00
// Note that it is up to the request handlers to ignore or honor this timeout. In seconds.
2015-05-27 01:39:42 +00:00
MinRequestTimeout int
2014-10-28 23:49:52 +00:00
// Number of masters running; all masters must be started with the
// same value for this field. (Numbers > 1 currently untested.)
MasterCount int
2014-10-28 00:56:33 +00:00
// The port on PublicAddress where a read-write server will be installed.
2015-02-07 00:33:28 +00:00
// Defaults to 6443 if not set.
2014-10-28 00:56:33 +00:00
ReadWritePort int
2015-03-25 04:39:19 +00:00
// ExternalHost is the host name to use for external (public internet) facing URLs (e.g. Swagger)
ExternalHost string
2015-05-20 21:05:23 +00:00
// PublicAddress is the IP address where members of the cluster (kubelet,
// kube-proxy, services, etc.) can reach the master.
2015-05-22 06:12:57 +00:00
// If nil or 0.0.0.0, the host's default interface will be used.
2015-01-20 03:25:06 +00:00
PublicAddress net . IP
2015-01-28 22:39:57 +00:00
// Control the interval that pod, node IP, and node heath status caches
// expire.
CacheTimeout time . Duration
2015-02-13 22:58:42 +00:00
// The name of the cluster.
ClusterName string
2015-05-22 22:28:48 +00:00
2015-05-23 20:41:11 +00:00
// The range of IPs to be assigned to services with type=ClusterIP or greater
ServiceClusterIPRange * net . IPNet
2015-07-01 20:57:48 +00:00
// The IP address for the master service (must be inside ServiceClusterIPRange
ServiceReadWriteIP net . IP
2015-05-22 22:58:39 +00:00
// The range of ports to be assigned to services with type=NodePort or greater
2015-05-23 20:41:11 +00:00
ServiceNodePortRange util . PortRange
2015-05-28 04:38:21 +00:00
2015-10-09 05:18:16 +00:00
// Used to customize default proxy dial/tls options
ProxyDialer apiserver . ProxyDialerFunc
ProxyTLSClientConfig * tls . Config
// Used to start and monitor tunneling
Tunneler Tunneler
2015-10-02 02:59:00 +00:00
2015-10-07 15:06:05 +00:00
// Additional ports to be exposed on the master service
// extraServicePorts is injectable in the event that more ports
// (other than the default 443/tcp) are exposed on the master
// and those ports need to be load balanced by the master
// service because this pkg is linked by out-of-tree projects
// like openshift which want to use the master but also do
// more stuff.
ExtraServicePorts [ ] api . ServicePort
// Additional ports to be exposed on the master endpoints
// Port names should align with ports defined in ExtraServicePorts
ExtraEndpointPorts [ ] api . EndpointPort
2015-10-02 02:59:00 +00:00
KubernetesServiceNodePort int
2014-07-27 02:16:39 +00:00
}
2015-05-28 18:45:08 +00:00
type InstallSSHKey func ( user string , data [ ] byte ) error
2014-06-16 06:29:07 +00:00
// Master contains state for a Kubernetes cluster master/api server.
type Master struct {
2014-10-23 23:55:14 +00:00
// "Inputs", Copied from Config
2015-05-23 20:41:11 +00:00
serviceClusterIPRange * net . IPNet
serviceNodePortRange util . PortRange
cacheTimeout time . Duration
2015-06-16 02:39:31 +00:00
minRequestTimeout time . Duration
2014-12-15 20:29:55 +00:00
2015-10-13 00:40:37 +00:00
mux apiserver . Mux
muxHelper * apiserver . MuxHelper
handlerContainer * restful . Container
rootWebService * restful . WebService
enableCoreControllers bool
enableLogsSupport bool
enableUISupport bool
enableSwaggerSupport bool
enableProfiling bool
enableWatchCache bool
apiPrefix string
apiGroupPrefix string
corsAllowedOriginList [ ] string
authenticator authenticator . Request
authorizer authorizer . Authorizer
admissionControl admission . Interface
masterCount int
apiGroupVersionOverrides map [ string ] APIGroupVersionOverride
requestContextMapper api . RequestContextMapper
2014-10-28 00:56:33 +00:00
2015-03-25 04:39:19 +00:00
// External host is the name that should be used in external (public internet) URLs for this master
externalHost string
// clusterIP is the IP address of the master within the cluster.
clusterIP net . IP
2015-01-20 03:25:06 +00:00
publicReadWritePort int
serviceReadWriteIP net . IP
serviceReadWritePort int
masterServices * util . Runner
2015-10-07 15:06:05 +00:00
extraServicePorts [ ] api . ServicePort
extraEndpointPorts [ ] api . EndpointPort
2014-11-06 17:11:31 +00:00
2015-02-12 00:07:54 +00:00
// storage contains the RESTful endpoints exposed by this master
2015-03-21 16:24:16 +00:00
storage map [ string ] rest . Storage
2015-02-12 00:07:54 +00:00
// registries are internal client APIs for accessing the storage layer
// TODO: define the internal typed interface in a way that clients can
// also be replaced
2015-09-09 14:18:17 +00:00
nodeRegistry node . Registry
2015-05-23 20:41:11 +00:00
namespaceRegistry namespace . Registry
serviceRegistry service . Registry
endpointRegistry endpoint . Registry
serviceClusterIPAllocator service . RangeRegistry
serviceNodePortAllocator service . RangeRegistry
2015-02-12 00:07:54 +00:00
2014-11-06 17:11:31 +00:00
// "Outputs"
Handler http . Handler
InsecureHandler http . Handler
2015-05-28 04:38:21 +00:00
2015-10-09 05:18:16 +00:00
// Used for custom proxy dialing, and proxy TLS options
proxyTransport http . RoundTripper
// Used to start and monitor tunneling
tunneler Tunneler
2015-08-19 18:02:01 +00:00
// storage for third party objects
thirdPartyStorage storage . Interface
2015-09-09 21:36:19 +00:00
// map from api path to storage for those objects
thirdPartyResources map [ string ] * thirdpartyresourcedataetcd . REST
// protects the map
2015-10-02 02:59:00 +00:00
thirdPartyResourcesLock sync . RWMutex
KubernetesServiceNodePort int
2014-06-16 06:29:07 +00:00
}
2015-07-30 07:45:06 +00:00
// NewEtcdStorage returns a storage.Interface for the provided arguments or an error if the version
2014-09-11 23:01:29 +00:00
// is incorrect.
2015-07-29 23:15:24 +00:00
func NewEtcdStorage ( client tools . EtcdClient , interfacesFunc meta . VersionInterfacesFunc , version , prefix string ) ( etcdStorage storage . Interface , err error ) {
versionInterfaces , err := interfacesFunc ( version )
2014-09-11 23:01:29 +00:00
if err != nil {
2015-07-24 11:09:49 +00:00
return etcdStorage , err
2014-09-11 23:01:29 +00:00
}
2015-07-30 11:27:18 +00:00
return etcdstorage . NewEtcdStorage ( client , versionInterfaces . Codec , prefix ) , nil
2014-09-11 23:01:29 +00:00
}
2014-10-28 00:56:33 +00:00
// setDefaults fills in any fields not set that are required to have valid data.
func setDefaults ( c * Config ) {
2015-05-23 20:41:11 +00:00
if c . ServiceClusterIPRange == nil {
2014-10-29 19:27:35 +00:00
defaultNet := "10.0.0.0/24"
2015-05-23 20:41:11 +00:00
glog . Warningf ( "Network range for service cluster IPs is unspecified. Defaulting to %v." , defaultNet )
_ , serviceClusterIPRange , err := net . ParseCIDR ( defaultNet )
2014-10-29 19:27:35 +00:00
if err != nil {
glog . Fatalf ( "Unable to parse CIDR: %v" , err )
}
2015-05-23 20:41:11 +00:00
if size := ipallocator . RangeSize ( serviceClusterIPRange ) ; size < 8 {
glog . Fatalf ( "The service cluster IP range must be at least %d IP addresses" , 8 )
2015-05-03 22:44:05 +00:00
}
2015-05-23 20:41:11 +00:00
c . ServiceClusterIPRange = serviceClusterIPRange
2014-10-29 19:27:35 +00:00
}
2015-07-01 20:57:48 +00:00
if c . ServiceReadWriteIP == nil {
// Select the first valid IP from ServiceClusterIPRange to use as the master service IP.
serviceReadWriteIP , err := ipallocator . GetIndexedIP ( c . ServiceClusterIPRange , 1 )
if err != nil {
glog . Fatalf ( "Failed to generate service read-write IP for master service: %v" , err )
}
glog . V ( 4 ) . Infof ( "Setting master service IP to %q (read-write)." , serviceReadWriteIP )
c . ServiceReadWriteIP = serviceReadWriteIP
}
2015-05-23 20:41:11 +00:00
if c . ServiceNodePortRange . Size == 0 {
2015-05-22 22:28:48 +00:00
// 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)
2015-05-22 22:58:39 +00:00
// but then that breaks the strict nestedness of ServiceType.
2015-05-22 22:28:48 +00:00
// Review post-v1
2015-06-06 18:40:13 +00:00
defaultServiceNodePortRange := util . PortRange { Base : 30000 , Size : 2768 }
2015-05-23 20:41:11 +00:00
c . ServiceNodePortRange = defaultServiceNodePortRange
glog . Infof ( "Node port range unspecified. Defaulting to %v." , c . ServiceNodePortRange )
2015-05-22 22:28:48 +00:00
}
2014-10-28 23:49:52 +00:00
if c . MasterCount == 0 {
// Clearly, there will be at least one master.
c . MasterCount = 1
}
2015-02-07 00:33:28 +00:00
if c . ReadWritePort == 0 {
c . ReadWritePort = 6443
}
2015-01-28 22:39:57 +00:00
if c . CacheTimeout == 0 {
c . CacheTimeout = 5 * time . Second
}
2015-07-04 04:05:15 +00:00
for c . PublicAddress == nil || c . PublicAddress . IsUnspecified ( ) || c . PublicAddress . IsLoopback ( ) {
2015-04-07 18:49:08 +00:00
// TODO: This should be done in the caller and just require a
// valid value to be passed in.
2015-03-02 10:41:45 +00:00
hostIP , err := util . ChooseHostInterface ( )
2014-10-28 00:56:33 +00:00
if err != nil {
2015-03-02 10:41:45 +00:00
glog . Fatalf ( "Unable to find suitable network address.error='%v' . " +
"Will try again in 5 seconds. Set the public address directly to avoid this wait." , err )
2014-11-05 20:07:33 +00:00
time . Sleep ( 5 * time . Second )
2014-10-28 00:56:33 +00:00
}
2015-03-02 10:41:45 +00:00
c . PublicAddress = hostIP
glog . Infof ( "Will report %v as public IP address." , c . PublicAddress )
2014-10-28 00:56:33 +00:00
}
2015-02-11 22:09:25 +00:00
if c . RequestContextMapper == nil {
c . RequestContextMapper = api . NewRequestContextMapper ( )
}
2014-10-28 00:56:33 +00:00
}
2014-10-28 20:02:19 +00:00
// New returns a new instance of Master from the given config.
// Certain config fields will be set to a default value if unset,
// including:
2015-05-23 20:41:11 +00:00
// ServiceClusterIPRange
// ServiceNodePortRange
2014-10-28 20:02:19 +00:00
// MasterCount
// ReadWritePort
// PublicAddress
// Certain config fields must be specified, including:
// KubeletClient
// Public fields:
// Handler -- The returned master has a field TopHandler which is an
// http.Handler which handles all the endpoints provided by the master,
2015-08-08 21:29:57 +00:00
// including the API, the UI, and miscellaneous debugging endpoints. All
2014-10-28 20:02:19 +00:00
// these are subject to authorization and authentication.
2014-11-06 17:11:31 +00:00
// InsecureHandler -- an http.Handler which handles all the same
// endpoints as Handler, but no authorization and authentication is done.
2014-10-28 20:02:19 +00:00
// 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 master's built-in endpoints.
// If the caller wants to add additional endpoints not using the master's
// auth, then the caller should create a handler for those endpoints, which delegates the
// any unhandled paths to "Handler".
2014-07-27 02:16:39 +00:00
func New ( c * Config ) * Master {
2014-10-28 00:56:33 +00:00
setDefaults ( c )
2014-11-03 22:50:41 +00:00
if c . KubeletClient == nil {
glog . Fatalf ( "master.New() called with config.KubeletClient == nil" )
}
2014-12-15 20:29:55 +00:00
2014-06-16 06:29:07 +00:00
m := & Master {
2015-10-13 00:40:37 +00:00
serviceClusterIPRange : c . ServiceClusterIPRange ,
serviceNodePortRange : c . ServiceNodePortRange ,
rootWebService : new ( restful . WebService ) ,
enableCoreControllers : c . EnableCoreControllers ,
enableLogsSupport : c . EnableLogsSupport ,
enableUISupport : c . EnableUISupport ,
enableSwaggerSupport : c . EnableSwaggerSupport ,
enableProfiling : c . EnableProfiling ,
enableWatchCache : c . EnableWatchCache ,
apiPrefix : c . APIPrefix ,
apiGroupPrefix : c . APIGroupPrefix ,
corsAllowedOriginList : c . CorsAllowedOriginList ,
authenticator : c . Authenticator ,
authorizer : c . Authorizer ,
admissionControl : c . AdmissionControl ,
apiGroupVersionOverrides : c . APIGroupVersionOverrides ,
requestContextMapper : c . RequestContextMapper ,
2015-01-28 22:39:57 +00:00
2015-06-16 02:39:31 +00:00
cacheTimeout : c . CacheTimeout ,
minRequestTimeout : time . Duration ( c . MinRequestTimeout ) * time . Second ,
2014-10-16 21:18:16 +00:00
2015-01-20 03:25:06 +00:00
masterCount : c . MasterCount ,
2015-03-25 04:39:19 +00:00
externalHost : c . ExternalHost ,
clusterIP : c . PublicAddress ,
2015-01-20 03:25:06 +00:00
publicReadWritePort : c . ReadWritePort ,
2015-07-01 20:57:48 +00:00
serviceReadWriteIP : c . ServiceReadWriteIP ,
2015-01-20 03:25:06 +00:00
// TODO: serviceReadWritePort should be passed in as an argument, it may not always be 443
serviceReadWritePort : 443 ,
2015-10-07 15:06:05 +00:00
extraServicePorts : c . ExtraServicePorts ,
extraEndpointPorts : c . ExtraEndpointPorts ,
2015-06-04 18:58:38 +00:00
2015-10-09 05:18:16 +00:00
tunneler : c . Tunneler ,
2015-10-02 02:59:00 +00:00
KubernetesServiceNodePort : c . KubernetesServiceNodePort ,
2014-06-16 06:29:07 +00:00
}
2014-12-15 20:29:55 +00:00
2015-05-27 01:39:42 +00:00
var handlerContainer * restful . Container
2014-12-15 20:29:55 +00:00
if c . RestfulContainer != nil {
m . mux = c . RestfulContainer . ServeMux
2015-05-27 01:39:42 +00:00
handlerContainer = c . RestfulContainer
2014-12-15 20:29:55 +00:00
} else {
mux := http . NewServeMux ( )
m . mux = mux
2015-05-27 01:39:42 +00:00
handlerContainer = NewHandlerContainer ( mux )
2014-12-15 20:29:55 +00:00
}
2015-06-16 02:39:31 +00:00
m . handlerContainer = handlerContainer
2015-01-31 00:08:59 +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}/{*})
m . handlerContainer . Router ( restful . CurlyRouter { } )
2015-08-08 01:52:23 +00:00
m . muxHelper = & apiserver . MuxHelper { Mux : m . mux , RegisteredPaths : [ ] string { } }
2014-12-15 20:29:55 +00:00
2014-10-08 23:14:37 +00:00
m . init ( c )
2015-05-06 21:54:54 +00:00
2014-06-16 06:29:07 +00:00
return m
}
2014-10-28 20:02:19 +00:00
// HandleWithAuth adds an http.Handler for pattern to an http.ServeMux
// Applies the same authentication and authorization (if any is configured)
// to the request is used for the master's built-in endpoints.
func ( m * Master ) HandleWithAuth ( pattern string , handler http . Handler ) {
// TODO: Add a way for plugged-in endpoints to translate their
// URLs into attributes that an Authorizer can understand, and have
// sensible policy defaults for plugged-in endpoints. This will be different
// for generic endpoints versus REST object endpoints.
2014-11-11 07:11:45 +00:00
// TODO: convert to go-restful
2015-01-16 00:18:47 +00:00
m . muxHelper . Handle ( pattern , handler )
2014-10-28 20:02:19 +00:00
}
// HandleFuncWithAuth adds an http.Handler for pattern to an http.ServeMux
// Applies the same authentication and authorization (if any is configured)
// to the request is used for the master's built-in endpoints.
func ( m * Master ) HandleFuncWithAuth ( pattern string , handler func ( http . ResponseWriter , * http . Request ) ) {
2014-11-11 07:11:45 +00:00
// TODO: convert to go-restful
2015-01-16 00:18:47 +00:00
m . muxHelper . HandleFunc ( pattern , handler )
2014-10-28 20:02:19 +00:00
}
2014-11-11 07:11:45 +00:00
func NewHandlerContainer ( mux * http . ServeMux ) * restful . Container {
container := restful . NewContainer ( )
container . ServeMux = mux
2015-08-28 18:01:31 +00:00
apiserver . InstallRecoverHandler ( container )
2014-11-11 07:11:45 +00:00
return container
}
2014-10-08 23:14:37 +00:00
// init initializes master.
func ( m * Master ) init ( c * Config ) {
2015-10-09 05:18:16 +00:00
if c . ProxyDialer != nil || c . ProxyTLSClientConfig != nil {
m . proxyTransport = util . SetTransportDefaults ( & http . Transport {
Dial : c . ProxyDialer ,
TLSClientConfig : c . ProxyTLSClientConfig ,
} )
}
2015-07-14 19:30:43 +00:00
healthzChecks := [ ] healthz . HealthzChecker { }
2015-10-09 05:18:16 +00:00
2015-09-30 07:56:51 +00:00
dbClient := func ( resource string ) storage . Interface { return c . StorageDestinations . get ( "" , resource ) }
2015-10-09 05:18:16 +00:00
podStorage := podetcd . NewStorage ( dbClient ( "pods" ) , c . EnableWatchCache , c . KubeletClient , m . proxyTransport )
2015-02-11 23:37:12 +00:00
2015-09-30 07:56:51 +00:00
podTemplateStorage := podtemplateetcd . NewREST ( dbClient ( "podTemplates" ) )
2015-03-04 00:54:17 +00:00
2015-09-30 07:56:51 +00:00
eventStorage := eventetcd . NewREST ( dbClient ( "events" ) , uint64 ( c . EventTTL . Seconds ( ) ) )
limitRangeStorage := limitrangeetcd . NewREST ( dbClient ( "limitRanges" ) )
2015-03-13 19:15:04 +00:00
2015-09-30 07:56:51 +00:00
resourceQuotaStorage , resourceQuotaStatusStorage := resourcequotaetcd . NewREST ( dbClient ( "resourceQuotas" ) )
secretStorage := secretetcd . NewREST ( dbClient ( "secrets" ) )
serviceAccountStorage := serviceaccountetcd . NewREST ( dbClient ( "serviceAccounts" ) )
persistentVolumeStorage , persistentVolumeStatusStorage := pvetcd . NewREST ( dbClient ( "persistentVolumes" ) )
persistentVolumeClaimStorage , persistentVolumeClaimStatusStorage := pvcetcd . NewREST ( dbClient ( "persistentVolumeClaims" ) )
2015-03-12 15:08:06 +00:00
2015-09-30 07:56:51 +00:00
namespaceStorage , namespaceStatusStorage , namespaceFinalizeStorage := namespaceetcd . NewREST ( dbClient ( "namespaces" ) )
2015-03-12 15:08:06 +00:00
m . namespaceRegistry = namespace . NewRegistry ( namespaceStorage )
2015-02-12 00:07:54 +00:00
2015-09-30 07:56:51 +00:00
endpointsStorage := endpointsetcd . NewREST ( dbClient ( "endpoints" ) , c . EnableWatchCache )
2015-03-15 06:03:46 +00:00
m . endpointRegistry = endpoint . NewRegistry ( endpointsStorage )
2015-10-09 05:18:16 +00:00
nodeStorage , nodeStatusStorage := nodeetcd . NewREST ( dbClient ( "nodes" ) , c . EnableWatchCache , c . KubeletClient , m . proxyTransport )
2015-09-09 14:18:17 +00:00
m . nodeRegistry = node . NewRegistry ( nodeStorage )
2015-03-13 14:49:38 +00:00
2015-09-30 07:56:51 +00:00
serviceStorage := serviceetcd . NewREST ( dbClient ( "services" ) )
2015-08-06 10:02:01 +00:00
m . serviceRegistry = service . NewRegistry ( serviceStorage )
2015-02-11 23:37:12 +00:00
2015-05-23 20:41:11 +00:00
var serviceClusterIPRegistry service . RangeRegistry
serviceClusterIPAllocator := ipallocator . NewAllocatorCIDRRange ( m . serviceClusterIPRange , func ( max int , rangeSpec string ) allocator . Interface {
2015-05-22 22:28:48 +00:00
mem := allocator . NewAllocationMap ( max , rangeSpec )
2015-09-30 07:56:51 +00:00
etcd := etcdallocator . NewEtcd ( mem , "/ranges/serviceips" , "serviceipallocation" , dbClient ( "services" ) )
2015-05-23 20:41:11 +00:00
serviceClusterIPRegistry = etcd
2015-05-22 22:28:48 +00:00
return etcd
} )
2015-05-23 20:41:11 +00:00
m . serviceClusterIPAllocator = serviceClusterIPRegistry
2015-05-22 22:28:48 +00:00
var serviceNodePortRegistry service . RangeRegistry
2015-05-23 20:41:11 +00:00
serviceNodePortAllocator := portallocator . NewPortAllocatorCustom ( m . serviceNodePortRange , func ( max int , rangeSpec string ) allocator . Interface {
2015-05-22 22:28:48 +00:00
mem := allocator . NewAllocationMap ( max , rangeSpec )
2015-09-30 07:56:51 +00:00
etcd := etcdallocator . NewEtcd ( mem , "/ranges/servicenodeports" , "servicenodeportallocation" , dbClient ( "services" ) )
2015-05-22 22:28:48 +00:00
serviceNodePortRegistry = etcd
return etcd
} )
m . serviceNodePortAllocator = serviceNodePortRegistry
2015-05-03 22:44:05 +00:00
2015-09-28 19:39:57 +00:00
controllerStorage , controllerStatusStorage := controlleretcd . NewREST ( dbClient ( "replicationControllers" ) )
2015-03-16 04:36:26 +00:00
2014-11-11 07:11:45 +00:00
// TODO: Factor out the core API registration
2015-03-21 16:24:16 +00:00
m . storage = map [ string ] rest . Storage {
2015-04-14 15:12:27 +00:00
"pods" : podStorage . Pod ,
2015-07-28 22:56:27 +00:00
"pods/attach" : podStorage . Attach ,
2015-04-14 15:12:27 +00:00
"pods/status" : podStorage . Status ,
"pods/log" : podStorage . Log ,
"pods/exec" : podStorage . Exec ,
"pods/portforward" : podStorage . PortForward ,
"pods/proxy" : podStorage . Proxy ,
"pods/binding" : podStorage . Binding ,
"bindings" : podStorage . Binding ,
2015-02-11 23:37:12 +00:00
2015-03-04 00:54:17 +00:00
"podTemplates" : podTemplateStorage ,
2015-09-28 19:39:57 +00:00
"replicationControllers" : controllerStorage ,
"replicationControllers/status" : controllerStatusStorage ,
2015-10-09 05:18:16 +00:00
"services" : service . NewStorage ( m . serviceRegistry , m . endpointRegistry , serviceClusterIPAllocator , serviceNodePortAllocator , m . proxyTransport ) ,
2015-09-28 19:39:57 +00:00
"endpoints" : endpointsStorage ,
"nodes" : nodeStorage ,
"nodes/status" : nodeStatusStorage ,
"events" : eventStorage ,
2014-08-15 23:01:33 +00:00
2015-08-11 07:05:40 +00:00
"limitRanges" : limitRangeStorage ,
2015-04-06 18:40:45 +00:00
"resourceQuotas" : resourceQuotaStorage ,
"resourceQuotas/status" : resourceQuotaStatusStorage ,
"namespaces" : namespaceStorage ,
"namespaces/status" : namespaceStatusStorage ,
"namespaces/finalize" : namespaceFinalizeStorage ,
2015-04-28 03:50:56 +00:00
"secrets" : secretStorage ,
2015-04-27 22:53:28 +00:00
"serviceAccounts" : serviceAccountStorage ,
2015-04-06 18:40:45 +00:00
"persistentVolumes" : persistentVolumeStorage ,
"persistentVolumes/status" : persistentVolumeStatusStorage ,
"persistentVolumeClaims" : persistentVolumeClaimStorage ,
"persistentVolumeClaims/status" : persistentVolumeClaimStatusStorage ,
2015-04-15 19:23:02 +00:00
2015-05-14 00:29:25 +00:00
"componentStatuses" : componentstatus . NewStorage ( func ( ) map [ string ] apiserver . Server { return m . getServersToValidate ( c ) } ) ,
2014-06-16 06:29:07 +00:00
}
2014-10-28 00:56:33 +00:00
2015-10-09 05:18:16 +00:00
if m . tunneler != nil {
m . tunneler . Run ( m . getNodeAddresses )
2015-07-14 19:30:43 +00:00
healthzChecks = append ( healthzChecks , healthz . NamedCheck ( "SSH Tunnel Check" , m . IsTunnelSyncHealthy ) )
2015-10-09 05:18:16 +00:00
prometheus . NewGaugeFunc ( prometheus . GaugeOpts {
2015-07-14 19:30:43 +00:00
Name : "apiserver_proxy_tunnel_sync_latency_secs" ,
Help : "The time since the last successful synchronization of the SSH tunnels for proxy requests." ,
2015-10-09 05:18:16 +00:00
} , func ( ) float64 { return float64 ( m . tunneler . SecondsSinceSync ( ) ) } )
2015-05-28 04:38:21 +00:00
}
2015-05-11 23:09:25 +00:00
apiVersions := [ ] string { }
2015-10-13 00:40:37 +00:00
// Install v1 unless disabled.
if ! m . apiGroupVersionOverrides [ "api/v1" ] . Disable {
2015-06-16 02:39:31 +00:00
if err := m . api_v1 ( ) . InstallREST ( m . handlerContainer ) ; err != nil {
2015-04-28 19:23:13 +00:00
glog . Fatalf ( "Unable to setup API v1: %v" , err )
}
apiVersions = append ( apiVersions , "v1" )
2015-01-08 17:42:20 +00:00
}
2014-11-11 07:11:45 +00:00
2015-07-14 19:30:43 +00:00
apiserver . InstallSupport ( m . muxHelper , m . rootWebService , c . EnableProfiling , healthzChecks ... )
2015-06-16 02:39:31 +00:00
apiserver . AddApiWebService ( m . handlerContainer , c . APIPrefix , apiVersions )
2015-09-25 18:57:10 +00:00
apiserver . InstallServiceErrorHandler ( m . handlerContainer , m . newAPIRequestInfoResolver ( ) , apiVersions )
2015-01-07 23:43:38 +00:00
2015-09-15 03:55:18 +00:00
// allGroups records all supported groups at /apis
2015-10-09 01:33:22 +00:00
allGroups := [ ] unversioned . APIGroup { }
2015-10-13 00:40:37 +00:00
// Install extensions unless disabled.
if ! m . apiGroupVersionOverrides [ "extensions/v1beta1" ] . Disable {
2015-10-09 22:14:03 +00:00
m . thirdPartyStorage = c . StorageDestinations . APIGroups [ "extensions" ] . Default
2015-09-09 21:36:19 +00:00
m . thirdPartyResources = map [ string ] * thirdpartyresourcedataetcd . REST { }
2015-09-09 22:46:06 +00:00
expVersion := m . experimental ( c )
2015-09-09 21:36:19 +00:00
2015-07-29 23:15:24 +00:00
if err := expVersion . InstallREST ( m . handlerContainer ) ; err != nil {
glog . Fatalf ( "Unable to setup experimental api: %v" , err )
}
2015-10-09 22:14:03 +00:00
g , err := latest . Group ( "extensions" )
2015-09-15 03:55:18 +00:00
if err != nil {
glog . Fatalf ( "Unable to setup experimental api: %v" , err )
}
2015-10-09 03:46:12 +00:00
expAPIVersions := [ ] unversioned . GroupVersion {
2015-09-15 03:55:18 +00:00
{
2015-09-28 18:08:47 +00:00
GroupVersion : expVersion . Version ,
Version : apiutil . GetVersion ( expVersion . Version ) ,
2015-09-15 03:55:18 +00:00
} ,
}
storageVersion , found := c . StorageVersions [ g . Group ]
if ! found {
glog . Fatalf ( "Couldn't find storage version of group %v" , g . Group )
}
2015-10-09 01:33:22 +00:00
group := unversioned . APIGroup {
2015-09-15 03:55:18 +00:00
Name : g . Group ,
Versions : expAPIVersions ,
2015-10-09 03:46:12 +00:00
PreferredVersion : unversioned . GroupVersion { GroupVersion : storageVersion , Version : apiutil . GetVersion ( storageVersion ) } ,
2015-09-15 03:55:18 +00:00
}
2015-10-09 22:14:03 +00:00
apiserver . AddGroupWebService ( m . handlerContainer , c . APIGroupPrefix + "/" + latest . GroupOrDie ( "extensions" ) . Group + "/" , group )
2015-09-15 03:55:18 +00:00
allGroups = append ( allGroups , group )
2015-09-25 18:57:10 +00:00
apiserver . InstallServiceErrorHandler ( m . handlerContainer , m . newAPIRequestInfoResolver ( ) , [ ] string { expVersion . Version } )
2015-07-29 23:15:24 +00:00
}
2015-09-15 03:55:18 +00:00
// This should be done after all groups are registered
// TODO: replace the hardcoded "apis".
apiserver . AddApisWebService ( m . handlerContainer , "/apis" , allGroups )
2015-01-07 23:43:38 +00:00
// Register root handler.
// We do not register this using restful Webservice since we do not want to surface this in api docs.
2015-01-30 23:53:04 +00:00
// Allow master to be embedded in contexts which already have something registered at the root
if c . EnableIndex {
2015-06-16 02:39:31 +00:00
m . mux . HandleFunc ( "/" , apiserver . IndexHandler ( m . handlerContainer , m . muxHelper ) )
2015-01-30 23:53:04 +00:00
}
2014-11-11 07:11:45 +00:00
2014-10-23 20:56:18 +00:00
if c . EnableLogsSupport {
2015-01-16 00:18:47 +00:00
apiserver . InstallLogsSupport ( m . muxHelper )
2014-10-23 20:56:18 +00:00
}
if c . EnableUISupport {
2015-01-16 00:18:47 +00:00
ui . InstallSupport ( m . muxHelper , m . enableSwaggerSupport )
2014-10-23 20:56:18 +00:00
}
2014-10-23 23:55:14 +00:00
2015-03-13 15:44:11 +00:00
if c . EnableProfiling {
m . mux . HandleFunc ( "/debug/pprof/" , pprof . Index )
m . mux . HandleFunc ( "/debug/pprof/profile" , pprof . Profile )
m . mux . HandleFunc ( "/debug/pprof/symbol" , pprof . Symbol )
}
2014-11-11 07:11:45 +00:00
2014-10-23 23:55:14 +00:00
handler := http . Handler ( m . mux . ( * http . ServeMux ) )
2014-11-11 07:11:45 +00:00
// TODO: handle CORS and auth using go-restful
// See github.com/emicklei/go-restful/blob/master/examples/restful-CORS-filter.go, and
// github.com/emicklei/go-restful/blob/master/examples/restful-basic-authentication.go
2014-10-23 23:55:14 +00:00
if len ( c . CorsAllowedOriginList ) > 0 {
allowedOriginRegexps , err := util . CompileRegexps ( c . CorsAllowedOriginList )
if err != nil {
2015-07-29 20:09:04 +00:00
glog . Fatalf ( "Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v" , strings . Join ( c . CorsAllowedOriginList , "," ) , err )
2014-10-23 23:55:14 +00:00
}
handler = apiserver . CORS ( handler , allowedOriginRegexps , nil , nil , "true" )
}
2014-11-06 17:11:31 +00:00
m . InsecureHandler = handler
2015-09-25 18:57:10 +00:00
attributeGetter := apiserver . NewRequestAttributeGetter ( m . requestContextMapper , m . newAPIRequestInfoResolver ( ) )
2014-11-02 06:50:00 +00:00
handler = apiserver . WithAuthorizationCheck ( handler , attributeGetter , m . authorizer )
2014-10-16 21:18:16 +00:00
// Install Authenticator
2015-02-11 22:09:25 +00:00
if c . Authenticator != nil {
2015-04-28 05:24:26 +00:00
authenticatedHandler , err := handlers . NewRequestAuthenticator ( m . requestContextMapper , c . Authenticator , handlers . Unauthorized ( c . SupportsBasicAuth ) , handler )
2015-02-11 22:09:25 +00:00
if err != nil {
glog . Fatalf ( "Could not initialize authenticator: %v" , err )
}
handler = authenticatedHandler
2014-10-23 23:55:14 +00:00
}
2014-11-11 07:11:45 +00:00
// Install root web services
m . handlerContainer . Add ( m . rootWebService )
2014-12-15 20:29:55 +00:00
// TODO: Make this optional? Consumers of master depend on this currently.
m . Handler = handler
if m . enableSwaggerSupport {
m . InstallSwaggerAPI ( )
}
2015-02-11 22:09:25 +00:00
// After all wrapping is done, put a context filter around both handlers
if handler , err := api . NewRequestContextFilter ( m . requestContextMapper , m . Handler ) ; err != nil {
glog . Fatalf ( "Could not initialize request context filter: %v" , err )
} else {
m . Handler = handler
}
if handler , err := api . NewRequestContextFilter ( m . requestContextMapper , m . InsecureHandler ) ; err != nil {
glog . Fatalf ( "Could not initialize request context filter: %v" , err )
} else {
m . InsecureHandler = handler
}
2014-12-15 20:29:55 +00:00
// TODO: Attempt clean shutdown?
2015-05-03 22:44:05 +00:00
if m . enableCoreControllers {
2015-05-04 19:11:19 +00:00
m . NewBootstrapController ( ) . Start ( )
}
}
// NewBootstrapController returns a controller for watching the core capabilities of the master.
func ( m * Master ) NewBootstrapController ( ) * Controller {
return & Controller {
NamespaceRegistry : m . namespaceRegistry ,
ServiceRegistry : m . serviceRegistry ,
MasterCount : m . masterCount ,
2015-05-23 20:41:11 +00:00
EndpointRegistry : m . endpointRegistry ,
EndpointInterval : 10 * time . Second ,
2015-05-22 22:28:48 +00:00
2015-05-23 20:41:11 +00:00
ServiceClusterIPRegistry : m . serviceClusterIPAllocator ,
ServiceClusterIPRange : m . serviceClusterIPRange ,
ServiceClusterIPInterval : 3 * time . Minute ,
ServiceNodePortRegistry : m . serviceNodePortAllocator ,
ServiceNodePortRange : m . serviceNodePortRange ,
2015-05-22 22:28:48 +00:00
ServiceNodePortInterval : 3 * time . Minute ,
2015-05-04 19:11:19 +00:00
PublicIP : m . clusterIP ,
2015-10-02 02:59:00 +00:00
ServiceIP : m . serviceReadWriteIP ,
ServicePort : m . serviceReadWritePort ,
2015-10-07 15:06:05 +00:00
ExtraServicePorts : m . extraServicePorts ,
ExtraEndpointPorts : m . extraEndpointPorts ,
2015-10-02 02:59:00 +00:00
PublicServicePort : m . publicReadWritePort ,
KubernetesServiceNodePort : m . KubernetesServiceNodePort ,
2015-05-03 22:44:05 +00:00
}
2014-12-15 20:29:55 +00:00
}
// InstallSwaggerAPI installs the /swaggerapi/ endpoint to allow schema discovery
// and traversal. It is optional to allow consumers of the Kubernetes master to
// register their own web services into the Kubernetes mux prior to initialization
// of swagger, so that other resource types show up in the documentation.
func ( m * Master ) InstallSwaggerAPI ( ) {
2015-03-25 04:39:19 +00:00
hostAndPort := m . externalHost
protocol := "https://"
// TODO: this is kind of messed up, we should just pipe in the full URL from the outside, rather
// than guessing at it.
if len ( m . externalHost ) == 0 && m . clusterIP != nil {
host := m . clusterIP . String ( )
if m . publicReadWritePort != 0 {
hostAndPort = net . JoinHostPort ( host , strconv . Itoa ( m . publicReadWritePort ) )
}
2015-03-11 23:14:14 +00:00
}
2015-03-25 04:39:19 +00:00
webServicesUrl := protocol + hostAndPort
2014-11-11 07:11:45 +00:00
// Enable swagger UI and discovery API
swaggerConfig := swagger . Config {
2015-03-11 23:14:14 +00:00
WebServicesUrl : webServicesUrl ,
WebServices : m . handlerContainer . RegisteredWebServices ( ) ,
2014-12-20 00:16:54 +00:00
ApiPath : "/swaggerapi/" ,
SwaggerPath : "/swaggerui/" ,
2015-01-05 23:50:19 +00:00
SwaggerFilePath : "/swagger-ui/" ,
2014-11-11 07:11:45 +00:00
}
2015-06-16 02:39:31 +00:00
swagger . RegisterSwaggerService ( swaggerConfig , m . handlerContainer )
2014-06-16 06:29:07 +00:00
}
2015-05-14 00:29:25 +00:00
func ( m * Master ) getServersToValidate ( c * Config ) map [ string ] apiserver . Server {
2014-11-02 20:52:31 +00:00
serversToValidate := map [ string ] apiserver . Server {
2014-12-16 03:45:27 +00:00
"controller-manager" : { Addr : "127.0.0.1" , Port : ports . ControllerManagerPort , Path : "/healthz" } ,
"scheduler" : { Addr : "127.0.0.1" , Port : ports . SchedulerPort , Path : "/healthz" } ,
2014-11-02 20:52:31 +00:00
}
2015-09-30 07:56:51 +00:00
for ix , machine := range c . StorageDestinations . backends ( ) {
2014-11-02 20:52:31 +00:00
etcdUrl , err := url . Parse ( machine )
if err != nil {
glog . Errorf ( "Failed to parse etcd url for validation: %v" , err )
continue
}
var port int
var addr string
if strings . Contains ( etcdUrl . Host , ":" ) {
var portString string
addr , portString , err = net . SplitHostPort ( etcdUrl . Host )
if err != nil {
glog . Errorf ( "Failed to split host/port: %s (%v)" , etcdUrl . Host , err )
continue
}
port , _ = strconv . Atoi ( portString )
} else {
addr = etcdUrl . Host
port = 4001
}
2015-07-30 11:27:18 +00:00
serversToValidate [ fmt . Sprintf ( "etcd-%d" , ix ) ] = apiserver . Server { Addr : addr , Port : port , Path : "/health" , Validate : etcdstorage . EtcdHealthCheck }
2014-11-02 20:52:31 +00:00
}
return serversToValidate
}
2015-09-25 18:57:10 +00:00
func ( m * Master ) newAPIRequestInfoResolver ( ) * apiserver . APIRequestInfoResolver {
return & apiserver . APIRequestInfoResolver {
sets . NewString ( strings . Trim ( m . apiPrefix , "/" ) , strings . Trim ( thirdpartyprefix , "/" ) ) , // all possible API prefixes
sets . NewString ( strings . Trim ( m . apiPrefix , "/" ) ) , // APIPrefixes that won't have groups (legacy)
}
}
2015-03-04 20:57:05 +00:00
func ( m * Master ) defaultAPIGroupVersion ( ) * apiserver . APIGroupVersion {
return & apiserver . APIGroupVersion {
Root : m . apiPrefix ,
2015-09-25 18:57:10 +00:00
APIRequestInfoResolver : m . newAPIRequestInfoResolver ( ) ,
2015-03-04 20:57:05 +00:00
2015-09-10 19:30:47 +00:00
Mapper : latest . GroupOrDie ( "" ) . RESTMapper ,
2015-03-04 20:57:05 +00:00
2015-03-22 21:43:00 +00:00
Creater : api . Scheme ,
Convertor : api . Scheme ,
Typer : api . Scheme ,
2015-09-10 19:30:47 +00:00
Linker : latest . GroupOrDie ( "" ) . SelfLinker ,
2015-03-04 20:57:05 +00:00
Admit : m . admissionControl ,
Context : m . requestContextMapper ,
2015-06-16 02:39:31 +00:00
MinRequestTimeout : m . minRequestTimeout ,
2015-03-04 20:57:05 +00:00
}
}
2015-04-28 19:23:13 +00:00
// api_v1 returns the resources and codec for API version v1.
func ( m * Master ) api_v1 ( ) * apiserver . APIGroupVersion {
storage := make ( map [ string ] rest . Storage )
for k , v := range m . storage {
storage [ strings . ToLower ( k ) ] = v
}
version := m . defaultAPIGroupVersion ( )
version . Storage = storage
version . Version = "v1"
version . Codec = v1 . Codec
return version
}
2015-05-28 04:38:21 +00:00
2015-09-09 21:36:19 +00:00
// HasThirdPartyResource returns true if a particular third party resource currently installed.
func ( m * Master ) HasThirdPartyResource ( rsrc * expapi . ThirdPartyResource ) ( bool , error ) {
_ , group , err := thirdpartyresourcedata . ExtractApiGroupAndKind ( rsrc )
if err != nil {
return false , err
}
path := makeThirdPartyPath ( group )
services := m . handlerContainer . RegisteredWebServices ( )
for ix := range services {
if services [ ix ] . RootPath ( ) == path {
return true , nil
}
}
return false , nil
}
func ( m * Master ) removeThirdPartyStorage ( path string ) error {
m . thirdPartyResourcesLock . Lock ( )
defer m . thirdPartyResourcesLock . Unlock ( )
storage , found := m . thirdPartyResources [ path ]
if found {
if err := m . removeAllThirdPartyResources ( storage ) ; err != nil {
return err
}
delete ( m . thirdPartyResources , path )
}
return nil
}
// RemoveThirdPartyResource removes all resources matching `path`. Also deletes any stored data
func ( m * Master ) RemoveThirdPartyResource ( path string ) error {
if err := m . removeThirdPartyStorage ( path ) ; err != nil {
return err
}
services := m . handlerContainer . RegisteredWebServices ( )
for ix := range services {
root := services [ ix ] . RootPath ( )
if root == path || strings . HasPrefix ( root , path + "/" ) {
m . handlerContainer . Remove ( services [ ix ] )
}
}
return nil
}
func ( m * Master ) removeAllThirdPartyResources ( registry * thirdpartyresourcedataetcd . REST ) error {
ctx := api . NewDefaultContext ( )
existingData , err := registry . List ( ctx , labels . Everything ( ) , fields . Everything ( ) )
if err != nil {
return err
}
list , ok := existingData . ( * expapi . ThirdPartyResourceDataList )
if ! ok {
return fmt . Errorf ( "expected a *ThirdPartyResourceDataList, got %#v" , list )
}
for ix := range list . Items {
item := & list . Items [ ix ]
if _ , err := registry . Delete ( ctx , item . Name , nil ) ; err != nil {
return err
}
}
return nil
}
// ListThirdPartyResources lists all currently installed third party resources
func ( m * Master ) ListThirdPartyResources ( ) [ ] string {
m . thirdPartyResourcesLock . RLock ( )
defer m . thirdPartyResourcesLock . RUnlock ( )
result := [ ] string { }
for key := range m . thirdPartyResources {
result = append ( result , key )
}
return result
}
func ( m * Master ) addThirdPartyResourceStorage ( path string , storage * thirdpartyresourcedataetcd . REST ) {
m . thirdPartyResourcesLock . Lock ( )
defer m . thirdPartyResourcesLock . Unlock ( )
m . thirdPartyResources [ path ] = storage
}
// InstallThirdPartyResource installs a third party resource specified by 'rsrc'. When a resource is
// installed a corresponding RESTful resource is added as a valid path in the web service provided by
// the master.
//
// For example, if you install a resource ThirdPartyResource{ Name: "foo.company.com", Versions: {"v1"} }
// then the following RESTful resource is created on the server:
// http://<host>/apis/company.com/v1/foos/...
func ( m * Master ) InstallThirdPartyResource ( rsrc * expapi . ThirdPartyResource ) error {
2015-08-20 05:08:26 +00:00
kind , group , err := thirdpartyresourcedata . ExtractApiGroupAndKind ( rsrc )
if err != nil {
return err
}
2015-08-21 21:24:16 +00:00
thirdparty := m . thirdpartyapi ( group , kind , rsrc . Versions [ 0 ] . Name )
2015-08-19 18:02:01 +00:00
if err := thirdparty . InstallREST ( m . handlerContainer ) ; err != nil {
glog . Fatalf ( "Unable to setup thirdparty api: %v" , err )
}
2015-09-09 21:36:19 +00:00
path := makeThirdPartyPath ( group )
2015-10-09 03:46:12 +00:00
groupVersion := unversioned . GroupVersion {
2015-09-15 03:55:18 +00:00
GroupVersion : group + "/" + rsrc . Versions [ 0 ] . Name ,
Version : rsrc . Versions [ 0 ] . Name ,
}
2015-10-09 01:33:22 +00:00
apiGroup := unversioned . APIGroup {
2015-09-15 03:55:18 +00:00
Name : group ,
2015-10-09 03:46:12 +00:00
Versions : [ ] unversioned . GroupVersion { groupVersion } ,
2015-09-15 03:55:18 +00:00
}
2015-09-09 21:36:19 +00:00
apiserver . AddGroupWebService ( m . handlerContainer , path , apiGroup )
m . addThirdPartyResourceStorage ( path , thirdparty . Storage [ strings . ToLower ( kind ) + "s" ] . ( * thirdpartyresourcedataetcd . REST ) )
2015-09-25 18:57:10 +00:00
apiserver . InstallServiceErrorHandler ( m . handlerContainer , m . newAPIRequestInfoResolver ( ) , [ ] string { thirdparty . Version } )
2015-08-19 18:02:01 +00:00
return nil
}
2015-08-20 05:08:26 +00:00
func ( m * Master ) thirdpartyapi ( group , kind , version string ) * apiserver . APIGroupVersion {
resourceStorage := thirdpartyresourcedataetcd . NewREST ( m . thirdPartyStorage , group , kind )
2015-08-19 18:02:01 +00:00
2015-09-29 21:36:47 +00:00
apiRoot := makeThirdPartyPath ( "" )
2015-08-19 18:02:01 +00:00
storage := map [ string ] rest . Storage {
2015-08-21 21:24:16 +00:00
strings . ToLower ( kind ) + "s" : resourceStorage ,
2015-08-19 18:02:01 +00:00
}
return & apiserver . APIGroupVersion {
2015-09-25 18:57:10 +00:00
Root : apiRoot ,
2015-09-29 21:36:47 +00:00
Version : apiutil . GetGroupVersion ( group , version ) ,
2015-09-25 18:57:10 +00:00
APIRequestInfoResolver : m . newAPIRequestInfoResolver ( ) ,
2015-08-19 18:02:01 +00:00
2015-09-29 21:36:47 +00:00
Creater : thirdpartyresourcedata . NewObjectCreator ( group , version , api . Scheme ) ,
2015-08-19 18:02:01 +00:00
Convertor : api . Scheme ,
Typer : api . Scheme ,
2015-10-09 22:14:03 +00:00
Mapper : thirdpartyresourcedata . NewMapper ( latest . GroupOrDie ( "extensions" ) . RESTMapper , kind , version , group ) ,
Codec : thirdpartyresourcedata . NewCodec ( latest . GroupOrDie ( "extensions" ) . Codec , kind ) ,
Linker : latest . GroupOrDie ( "extensions" ) . SelfLinker ,
2015-09-18 05:54:15 +00:00
Storage : storage ,
ServerVersion : latest . GroupOrDie ( "" ) . GroupVersion ,
2015-08-19 18:02:01 +00:00
Context : m . requestContextMapper ,
MinRequestTimeout : m . minRequestTimeout ,
}
}
2015-09-09 22:46:06 +00:00
// experimental returns the resources and codec for the experimental api
func ( m * Master ) experimental ( c * Config ) * apiserver . APIGroupVersion {
2015-10-13 00:40:37 +00:00
// All resources except these are disabled by default.
2015-10-19 21:08:35 +00:00
enabledResources := utilsets . NewString ( "jobs" , "horizontalpodautoscalers" , "ingress" )
2015-10-13 00:40:37 +00:00
resourceOverrides := m . apiGroupVersionOverrides [ "extensions/v1beta1" ] . ResourceOverrides
isEnabled := func ( resource string ) bool {
// Check if the resource has been overriden.
enabled , ok := resourceOverrides [ resource ]
if ! ok {
return enabledResources . Has ( resource )
}
return enabled
}
2015-09-30 07:56:51 +00:00
dbClient := func ( resource string ) storage . Interface {
2015-10-09 22:14:03 +00:00
return c . StorageDestinations . get ( "extensions" , resource )
2015-09-30 07:56:51 +00:00
}
2015-10-13 00:40:37 +00:00
storage := map [ string ] rest . Storage { }
if isEnabled ( "horizontalpodautoscalers" ) {
autoscalerStorage , autoscalerStatusStorage := horizontalpodautoscaleretcd . NewREST ( dbClient ( "horizonalpodautoscalers" ) )
storage [ "horizontalpodautoscalers" ] = autoscalerStorage
storage [ "horizontalpodautoscalers/status" ] = autoscalerStatusStorage
2015-10-16 08:50:36 +00:00
controllerStorage := expcontrolleretcd . NewStorage ( c . StorageDestinations . get ( "" , "replicationControllers" ) )
storage [ "replicationcontrollers" ] = controllerStorage . ReplicationController
storage [ "replicationcontrollers/scale" ] = controllerStorage . Scale
2015-10-13 00:40:37 +00:00
}
if isEnabled ( "thirdpartyresources" ) {
thirdPartyResourceStorage := thirdpartyresourceetcd . NewREST ( dbClient ( "thirdpartyresources" ) )
thirdPartyControl := ThirdPartyController {
master : m ,
thirdPartyResourceRegistry : thirdPartyResourceStorage ,
}
go func ( ) {
util . Forever ( func ( ) {
if err := thirdPartyControl . SyncResources ( ) ; err != nil {
glog . Warningf ( "third party resource sync failed: %v" , err )
}
} , 10 * time . Second )
} ( )
storage [ "thirdpartyresources" ] = thirdPartyResourceStorage
}
if isEnabled ( "daemonsets" ) {
daemonSetStorage , daemonSetStatusStorage := daemonetcd . NewREST ( dbClient ( "daemonsets" ) )
storage [ "daemonsets" ] = daemonSetStorage
storage [ "daemonsets/status" ] = daemonSetStatusStorage
}
if isEnabled ( "deployments" ) {
deploymentStorage := deploymentetcd . NewStorage ( dbClient ( "deployments" ) )
storage [ "deployments" ] = deploymentStorage . Deployment
storage [ "deployments/scale" ] = deploymentStorage . Scale
}
if isEnabled ( "jobs" ) {
jobStorage , jobStatusStorage := jobetcd . NewREST ( dbClient ( "jobs" ) )
storage [ "jobs" ] = jobStorage
storage [ "jobs/status" ] = jobStatusStorage
}
if isEnabled ( "ingress" ) {
ingressStorage , ingressStatusStorage := ingressetcd . NewREST ( dbClient ( "ingress" ) )
storage [ "ingress" ] = ingressStorage
storage [ "ingress/status" ] = ingressStatusStorage
}
extensionsGroup := latest . GroupOrDie ( "extensions" )
2015-09-17 05:15:05 +00:00
2015-07-29 23:15:24 +00:00
return & apiserver . APIGroupVersion {
2015-09-17 05:15:05 +00:00
Root : m . apiGroupPrefix ,
2015-09-25 18:57:10 +00:00
APIRequestInfoResolver : m . newAPIRequestInfoResolver ( ) ,
2015-07-29 23:15:24 +00:00
Creater : api . Scheme ,
Convertor : api . Scheme ,
Typer : api . Scheme ,
2015-10-13 00:40:37 +00:00
Mapper : extensionsGroup . RESTMapper ,
Codec : extensionsGroup . Codec ,
Linker : extensionsGroup . SelfLinker ,
2015-09-17 05:15:05 +00:00
Storage : storage ,
2015-10-13 00:40:37 +00:00
Version : extensionsGroup . GroupVersion ,
2015-09-17 05:15:05 +00:00
ServerVersion : latest . GroupOrDie ( "" ) . GroupVersion ,
2015-07-29 23:15:24 +00:00
Admit : m . admissionControl ,
Context : m . requestContextMapper ,
MinRequestTimeout : m . minRequestTimeout ,
}
}
2015-07-28 08:10:48 +00:00
// findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP.
2015-05-28 04:38:21 +00:00
func findExternalAddress ( node * api . Node ) ( string , error ) {
2015-07-28 08:10:48 +00:00
var fallback string
2015-05-28 04:38:21 +00:00
for ix := range node . Status . Addresses {
addr := & node . Status . Addresses [ ix ]
if addr . Type == api . NodeExternalIP {
return addr . Address , nil
}
2015-07-28 08:10:48 +00:00
if fallback == "" && addr . Type == api . NodeLegacyHostIP {
fallback = addr . Address
}
}
if fallback != "" {
return fallback , nil
2015-05-28 04:38:21 +00:00
}
return "" , fmt . Errorf ( "Couldn't find external address: %v" , node )
}
2015-06-02 16:52:35 +00:00
func ( m * Master ) getNodeAddresses ( ) ( [ ] string , error ) {
2015-09-09 14:18:17 +00:00
nodes , err := m . nodeRegistry . ListNodes ( api . NewDefaultContext ( ) , labels . Everything ( ) , fields . Everything ( ) )
2015-05-28 04:38:21 +00:00
if err != nil {
2015-06-02 16:52:35 +00:00
return nil , err
2015-05-28 04:38:21 +00:00
}
2015-06-02 16:52:35 +00:00
addrs := [ ] string { }
2015-05-28 04:38:21 +00:00
for ix := range nodes . Items {
node := & nodes . Items [ ix ]
addr , err := findExternalAddress ( node )
if err != nil {
2015-06-02 16:52:35 +00:00
return nil , err
2015-05-28 04:38:21 +00:00
}
2015-06-02 16:52:35 +00:00
addrs = append ( addrs , addr )
2015-05-28 04:38:21 +00:00
}
2015-06-02 16:52:35 +00:00
return addrs , nil
}
2015-05-28 04:38:21 +00:00
2015-07-14 19:30:43 +00:00
func ( m * Master ) IsTunnelSyncHealthy ( req * http . Request ) error {
2015-10-09 05:18:16 +00:00
if m . tunneler == nil {
return nil
}
lag := m . tunneler . SecondsSinceSync ( )
2015-07-14 19:30:43 +00:00
if lag > 600 {
return fmt . Errorf ( "Tunnel sync is taking to long: %d" , lag )
}
return nil
}