2014-06-16 02:29:07 -04:00
/ *
2015-05-01 12:19:44 -04:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2014-06-16 02:29:07 -04: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 (
2014-11-02 15:52:31 -05:00
"fmt"
2015-05-28 14:45:08 -04:00
"io/ioutil"
2015-06-18 19:31:54 -04:00
"math/rand"
2014-09-18 19:03:34 -04:00
"net"
2014-10-23 19:55:14 -04:00
"net/http"
2015-03-13 11:44:11 -04:00
"net/http/pprof"
2014-11-02 15:52:31 -05:00
"net/url"
2015-06-17 14:49:13 -04:00
"os"
2014-10-27 20:56:33 -04:00
"strconv"
2014-10-23 19:55:14 -04:00
"strings"
2015-06-04 14:58:38 -04:00
"sync"
2015-07-14 15:30:43 -04:00
"sync/atomic"
2014-06-16 02:29:07 -04:00
"time"
2015-08-05 18:03:47 -04: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"
"k8s.io/kubernetes/pkg/api/v1"
"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 15:01:50 -04:00
client "k8s.io/kubernetes/pkg/client/unversioned"
2015-08-19 14:02:01 -04:00
"k8s.io/kubernetes/pkg/expapi"
2015-08-05 18:03:47 -04:00
explatest "k8s.io/kubernetes/pkg/expapi/latest"
"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 01:19:32 -04:00
deploymentetcd "k8s.io/kubernetes/pkg/registry/deployment/etcd"
2015-08-05 18:03:47 -04:00
"k8s.io/kubernetes/pkg/registry/endpoint"
endpointsetcd "k8s.io/kubernetes/pkg/registry/endpoint/etcd"
2015-08-11 05:21:00 -04:00
eventetcd "k8s.io/kubernetes/pkg/registry/event/etcd"
2015-08-06 12:53:34 -04:00
expcontrolleretcd "k8s.io/kubernetes/pkg/registry/experimental/controller/etcd"
2015-08-11 03:05:40 -04:00
limitrangeetcd "k8s.io/kubernetes/pkg/registry/limitrange/etcd"
2015-08-05 18:03:47 -04:00
"k8s.io/kubernetes/pkg/registry/minion"
nodeetcd "k8s.io/kubernetes/pkg/registry/minion/etcd"
"k8s.io/kubernetes/pkg/registry/namespace"
namespaceetcd "k8s.io/kubernetes/pkg/registry/namespace/etcd"
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 06:02:01 -04:00
serviceetcd "k8s.io/kubernetes/pkg/registry/service/etcd"
2015-08-05 18:03:47 -04:00
ipallocator "k8s.io/kubernetes/pkg/registry/service/ipallocator"
serviceaccountetcd "k8s.io/kubernetes/pkg/registry/serviceaccount/etcd"
2015-08-15 01:10:15 -04:00
thirdpartyresourceetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresource/etcd"
2015-08-20 01:08:26 -04:00
"k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
2015-08-19 14:02:01 -04:00
thirdpartyresourcedataetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata/etcd"
2015-08-05 18:03:47 -04: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"
2014-10-23 19:55:14 -04:00
2015-08-27 13:17:51 -04:00
daemonetcd "k8s.io/kubernetes/pkg/registry/daemon/etcd"
2015-08-06 10:14:28 -04:00
horizontalpodautoscaleretcd "k8s.io/kubernetes/pkg/registry/horizontalpodautoscaler/etcd"
2014-11-11 02:11:45 -05:00
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful/swagger"
2014-10-23 19:55:14 -04:00
"github.com/golang/glog"
2015-07-14 15:30:43 -04:00
"github.com/prometheus/client_golang/prometheus"
2015-08-05 18:05:17 -04:00
"k8s.io/kubernetes/pkg/registry/service/allocator"
"k8s.io/kubernetes/pkg/registry/service/portallocator"
2014-06-16 02:29:07 -04:00
)
2015-03-11 13:10:09 -04:00
const (
DefaultEtcdPathPrefix = "/registry"
)
2014-07-26 22:16:39 -04:00
// Config is a structure used to configure a Master.
type Config struct {
2015-07-29 19:15:24 -04:00
DatabaseStorage storage . Interface
ExpDatabaseStorage storage . Interface
EventTTL time . Duration
MinionRegexp string
KubeletClient client . KubeletClient
2015-05-03 18:44:05 -04:00
// allow downstream consumers to disable the core controller loops
EnableCoreControllers bool
EnableLogsSupport bool
EnableUISupport bool
2015-01-30 18:53:04 -05:00
// allow downstream consumers to disable swagger
EnableSwaggerSupport bool
2015-07-29 19:15:24 -04:00
// allow api versions to be conditionally disabled
2015-06-04 14:37:44 -04:00
DisableV1 bool
2015-07-29 19:15:24 -04:00
EnableExp bool
2015-01-30 18:53:04 -05:00
// allow downstream consumers to disable the index route
2015-04-28 01:24:26 -04:00
EnableIndex bool
EnableProfiling bool
2015-08-28 11:10:05 -04:00
EnableWatchCache bool
2015-04-28 01:24:26 -04:00
APIPrefix string
2015-07-29 19:15:24 -04:00
ExpAPIPrefix string
2015-08-05 10:21:47 -04:00
CorsAllowedOriginList [ ] string
2015-04-28 01:24:26 -04:00
Authenticator authenticator . Request
// TODO(roberthbailey): Remove once the server no longer supports http basic auth.
SupportsBasicAuth bool
2015-01-08 10:25:14 -05:00
Authorizer authorizer . Authorizer
AdmissionControl admission . Interface
MasterServiceNamespace string
2014-10-27 20:56:33 -04:00
2015-02-11 17:09:25 -05:00
// Map requests to contexts. Exported so downstream consumers can provider their own mappers
RequestContextMapper api . RequestContextMapper
2014-12-15 15:29:55 -05:00
// If specified, all web services will be registered into this container
RestfulContainer * restful . Container
2015-05-26 21:39:42 -04:00
// If specified, requests will be allocated a random timeout between this value, and twice this value.
2015-06-15 22:39:31 -04:00
// Note that it is up to the request handlers to ignore or honor this timeout. In seconds.
2015-05-26 21:39:42 -04:00
MinRequestTimeout int
2014-10-28 19:49:52 -04: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-27 20:56:33 -04:00
// The port on PublicAddress where a read-write server will be installed.
2015-02-06 19:33:28 -05:00
// Defaults to 6443 if not set.
2014-10-27 20:56:33 -04:00
ReadWritePort int
2015-03-25 00:39:19 -04:00
// ExternalHost is the host name to use for external (public internet) facing URLs (e.g. Swagger)
ExternalHost string
2015-05-20 17:05:23 -04:00
// PublicAddress is the IP address where members of the cluster (kubelet,
// kube-proxy, services, etc.) can reach the master.
2015-05-22 02:12:57 -04:00
// If nil or 0.0.0.0, the host's default interface will be used.
2015-01-19 22:25:06 -05:00
PublicAddress net . IP
2015-01-28 17:39:57 -05:00
// Control the interval that pod, node IP, and node heath status caches
// expire.
CacheTimeout time . Duration
2015-02-13 17:58:42 -05:00
// The name of the cluster.
ClusterName string
2015-05-22 18:28:48 -04:00
2015-05-23 16:41:11 -04:00
// The range of IPs to be assigned to services with type=ClusterIP or greater
ServiceClusterIPRange * net . IPNet
2015-07-01 16:57:48 -04:00
// The IP address for the master service (must be inside ServiceClusterIPRange
ServiceReadWriteIP net . IP
2015-05-22 18:58:39 -04:00
// The range of ports to be assigned to services with type=NodePort or greater
2015-05-23 16:41:11 -04:00
ServiceNodePortRange util . PortRange
2015-05-28 00:38:21 -04:00
// Used for secure proxy. If empty, don't use secure proxy.
2015-05-28 14:45:08 -04:00
SSHUser string
SSHKeyfile string
InstallSSHKey InstallSSHKey
2014-07-26 22:16:39 -04:00
}
2015-05-28 14:45:08 -04:00
type InstallSSHKey func ( user string , data [ ] byte ) error
2014-06-16 02:29:07 -04:00
// Master contains state for a Kubernetes cluster master/api server.
type Master struct {
2014-10-23 19:55:14 -04:00
// "Inputs", Copied from Config
2015-05-23 16:41:11 -04:00
serviceClusterIPRange * net . IPNet
serviceNodePortRange util . PortRange
cacheTimeout time . Duration
2015-06-15 22:39:31 -04:00
minRequestTimeout time . Duration
2014-12-15 15:29:55 -05:00
2014-10-23 19:55:14 -04:00
mux apiserver . Mux
2015-01-15 19:18:47 -05:00
muxHelper * apiserver . MuxHelper
2015-06-15 22:39:31 -04:00
handlerContainer * restful . Container
2014-11-11 02:11:45 -05:00
rootWebService * restful . WebService
2015-05-03 18:44:05 -04:00
enableCoreControllers bool
2014-10-23 19:55:14 -04:00
enableLogsSupport bool
enableUISupport bool
2014-12-15 15:29:55 -05:00
enableSwaggerSupport bool
2015-03-13 11:44:11 -04:00
enableProfiling bool
2015-08-28 11:10:05 -04:00
enableWatchCache bool
2014-10-23 19:55:14 -04:00
apiPrefix string
2015-07-29 19:15:24 -04:00
expAPIPrefix string
2015-08-05 10:21:47 -04:00
corsAllowedOriginList [ ] string
2014-11-19 10:31:43 -05:00
authenticator authenticator . Request
2014-11-02 01:50:00 -05:00
authorizer authorizer . Authorizer
2015-01-07 14:33:21 -05:00
admissionControl admission . Interface
2014-10-28 19:49:52 -04:00
masterCount int
2015-04-28 15:23:13 -04:00
v1 bool
2015-07-29 19:15:24 -04:00
exp bool
2015-02-11 17:09:25 -05:00
requestContextMapper api . RequestContextMapper
2014-10-27 20:56:33 -04:00
2015-03-25 00:39:19 -04: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-19 22:25:06 -05:00
publicReadWritePort int
serviceReadWriteIP net . IP
serviceReadWritePort int
masterServices * util . Runner
2014-11-06 12:11:31 -05:00
2015-02-11 19:07:54 -05:00
// storage contains the RESTful endpoints exposed by this master
2015-03-21 12:24:16 -04:00
storage map [ string ] rest . Storage
2015-02-11 19:07:54 -05: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-05-23 16:41:11 -04:00
nodeRegistry minion . Registry
namespaceRegistry namespace . Registry
serviceRegistry service . Registry
endpointRegistry endpoint . Registry
serviceClusterIPAllocator service . RangeRegistry
serviceNodePortAllocator service . RangeRegistry
2015-02-11 19:07:54 -05:00
2014-11-06 12:11:31 -05:00
// "Outputs"
Handler http . Handler
InsecureHandler http . Handler
2015-05-28 00:38:21 -04:00
// Used for secure proxy
2015-07-14 15:30:43 -04:00
dialer apiserver . ProxyDialerFunc
tunnels * util . SSHTunnelList
tunnelsLock sync . Mutex
installSSHKey InstallSSHKey
lastSync int64 // Seconds since Epoch
lastSyncMetric prometheus . GaugeFunc
clock util . Clock
2015-08-19 14:02:01 -04:00
// storage for third party objects
thirdPartyStorage storage . Interface
2014-06-16 02:29:07 -04:00
}
2015-07-30 03:45:06 -04:00
// NewEtcdStorage returns a storage.Interface for the provided arguments or an error if the version
2014-09-11 19:01:29 -04:00
// is incorrect.
2015-07-29 19:15:24 -04:00
func NewEtcdStorage ( client tools . EtcdClient , interfacesFunc meta . VersionInterfacesFunc , version , prefix string ) ( etcdStorage storage . Interface , err error ) {
versionInterfaces , err := interfacesFunc ( version )
2014-09-11 19:01:29 -04:00
if err != nil {
2015-07-24 07:09:49 -04:00
return etcdStorage , err
2014-09-11 19:01:29 -04:00
}
2015-07-30 07:27:18 -04:00
return etcdstorage . NewEtcdStorage ( client , versionInterfaces . Codec , prefix ) , nil
2014-09-11 19:01:29 -04:00
}
2014-10-27 20:56:33 -04:00
// setDefaults fills in any fields not set that are required to have valid data.
func setDefaults ( c * Config ) {
2015-05-23 16:41:11 -04:00
if c . ServiceClusterIPRange == nil {
2014-10-29 15:27:35 -04:00
defaultNet := "10.0.0.0/24"
2015-05-23 16:41:11 -04:00
glog . Warningf ( "Network range for service cluster IPs is unspecified. Defaulting to %v." , defaultNet )
_ , serviceClusterIPRange , err := net . ParseCIDR ( defaultNet )
2014-10-29 15:27:35 -04:00
if err != nil {
glog . Fatalf ( "Unable to parse CIDR: %v" , err )
}
2015-05-23 16:41:11 -04: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 18:44:05 -04:00
}
2015-05-23 16:41:11 -04:00
c . ServiceClusterIPRange = serviceClusterIPRange
2014-10-29 15:27:35 -04:00
}
2015-07-01 16:57:48 -04: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 16:41:11 -04:00
if c . ServiceNodePortRange . Size == 0 {
2015-05-22 18:28:48 -04: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 18:58:39 -04:00
// but then that breaks the strict nestedness of ServiceType.
2015-05-22 18:28:48 -04:00
// Review post-v1
2015-06-06 14:40:13 -04:00
defaultServiceNodePortRange := util . PortRange { Base : 30000 , Size : 2768 }
2015-05-23 16:41:11 -04:00
c . ServiceNodePortRange = defaultServiceNodePortRange
glog . Infof ( "Node port range unspecified. Defaulting to %v." , c . ServiceNodePortRange )
2015-05-22 18:28:48 -04:00
}
2014-10-28 19:49:52 -04:00
if c . MasterCount == 0 {
// Clearly, there will be at least one master.
c . MasterCount = 1
}
2015-02-06 19:33:28 -05:00
if c . ReadWritePort == 0 {
c . ReadWritePort = 6443
}
2015-01-28 17:39:57 -05:00
if c . CacheTimeout == 0 {
c . CacheTimeout = 5 * time . Second
}
2015-07-04 00:05:15 -04:00
for c . PublicAddress == nil || c . PublicAddress . IsUnspecified ( ) || c . PublicAddress . IsLoopback ( ) {
2015-04-07 14:49:08 -04:00
// TODO: This should be done in the caller and just require a
// valid value to be passed in.
2015-03-02 05:41:45 -05:00
hostIP , err := util . ChooseHostInterface ( )
2014-10-27 20:56:33 -04:00
if err != nil {
2015-03-02 05:41:45 -05: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 15:07:33 -05:00
time . Sleep ( 5 * time . Second )
2014-10-27 20:56:33 -04:00
}
2015-03-02 05:41:45 -05:00
c . PublicAddress = hostIP
glog . Infof ( "Will report %v as public IP address." , c . PublicAddress )
2014-10-27 20:56:33 -04:00
}
2015-02-11 17:09:25 -05:00
if c . RequestContextMapper == nil {
c . RequestContextMapper = api . NewRequestContextMapper ( )
}
2014-10-27 20:56:33 -04:00
}
2014-10-28 16:02:19 -04: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 16:41:11 -04:00
// ServiceClusterIPRange
// ServiceNodePortRange
2014-10-28 16:02:19 -04: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 17:29:57 -04:00
// including the API, the UI, and miscellaneous debugging endpoints. All
2014-10-28 16:02:19 -04:00
// these are subject to authorization and authentication.
2014-11-06 12:11:31 -05:00
// InsecureHandler -- an http.Handler which handles all the same
// endpoints as Handler, but no authorization and authentication is done.
2014-10-28 16:02:19 -04: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-26 22:16:39 -04:00
func New ( c * Config ) * Master {
2014-10-27 20:56:33 -04:00
setDefaults ( c )
2014-11-03 17:50:41 -05:00
if c . KubeletClient == nil {
glog . Fatalf ( "master.New() called with config.KubeletClient == nil" )
}
2014-12-15 15:29:55 -05:00
2014-06-16 02:29:07 -04:00
m := & Master {
2015-05-23 16:41:11 -04:00
serviceClusterIPRange : c . ServiceClusterIPRange ,
serviceNodePortRange : c . ServiceNodePortRange ,
2014-11-11 02:11:45 -05:00
rootWebService : new ( restful . WebService ) ,
2015-05-03 18:44:05 -04:00
enableCoreControllers : c . EnableCoreControllers ,
2014-10-23 19:55:14 -04:00
enableLogsSupport : c . EnableLogsSupport ,
enableUISupport : c . EnableUISupport ,
2014-12-15 15:29:55 -05:00
enableSwaggerSupport : c . EnableSwaggerSupport ,
2015-03-13 11:44:11 -04:00
enableProfiling : c . EnableProfiling ,
2015-08-28 11:10:05 -04:00
enableWatchCache : c . EnableWatchCache ,
2014-10-23 19:55:14 -04:00
apiPrefix : c . APIPrefix ,
2015-07-29 19:15:24 -04:00
expAPIPrefix : c . ExpAPIPrefix ,
2014-10-23 19:55:14 -04:00
corsAllowedOriginList : c . CorsAllowedOriginList ,
2014-11-19 10:31:43 -05:00
authenticator : c . Authenticator ,
2014-11-02 01:50:00 -05:00
authorizer : c . Authorizer ,
2015-01-06 11:44:43 -05:00
admissionControl : c . AdmissionControl ,
2015-06-04 14:37:44 -04:00
v1 : ! c . DisableV1 ,
2015-07-29 19:15:24 -04:00
exp : c . EnableExp ,
2015-02-11 17:09:25 -05:00
requestContextMapper : c . RequestContextMapper ,
2015-01-28 17:39:57 -05:00
2015-06-15 22:39:31 -04:00
cacheTimeout : c . CacheTimeout ,
minRequestTimeout : time . Duration ( c . MinRequestTimeout ) * time . Second ,
2014-10-16 17:18:16 -04:00
2015-01-19 22:25:06 -05:00
masterCount : c . MasterCount ,
2015-03-25 00:39:19 -04:00
externalHost : c . ExternalHost ,
clusterIP : c . PublicAddress ,
2015-01-19 22:25:06 -05:00
publicReadWritePort : c . ReadWritePort ,
2015-07-01 16:57:48 -04:00
serviceReadWriteIP : c . ServiceReadWriteIP ,
2015-01-19 22:25:06 -05:00
// TODO: serviceReadWritePort should be passed in as an argument, it may not always be 443
serviceReadWritePort : 443 ,
2015-06-04 14:58:38 -04:00
2015-05-29 17:29:17 -04:00
installSSHKey : c . InstallSSHKey ,
2014-06-16 02:29:07 -04:00
}
2014-12-15 15:29:55 -05:00
2015-05-26 21:39:42 -04:00
var handlerContainer * restful . Container
2014-12-15 15:29:55 -05:00
if c . RestfulContainer != nil {
m . mux = c . RestfulContainer . ServeMux
2015-05-26 21:39:42 -04:00
handlerContainer = c . RestfulContainer
2014-12-15 15:29:55 -05:00
} else {
mux := http . NewServeMux ( )
m . mux = mux
2015-05-26 21:39:42 -04:00
handlerContainer = NewHandlerContainer ( mux )
2014-12-15 15:29:55 -05:00
}
2015-06-15 22:39:31 -04:00
m . handlerContainer = handlerContainer
2015-01-30 19:08:59 -05: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-07 21:52:23 -04:00
m . muxHelper = & apiserver . MuxHelper { Mux : m . mux , RegisteredPaths : [ ] string { } }
2014-12-15 15:29:55 -05:00
2014-10-08 19:14:37 -04:00
m . init ( c )
2015-05-06 17:54:54 -04:00
2014-06-16 02:29:07 -04:00
return m
}
2014-10-28 16:02:19 -04: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 02:11:45 -05:00
// TODO: convert to go-restful
2015-01-15 19:18:47 -05:00
m . muxHelper . Handle ( pattern , handler )
2014-10-28 16:02:19 -04: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 02:11:45 -05:00
// TODO: convert to go-restful
2015-01-15 19:18:47 -05:00
m . muxHelper . HandleFunc ( pattern , handler )
2014-10-28 16:02:19 -04:00
}
2014-11-11 02:11:45 -05:00
func NewHandlerContainer ( mux * http . ServeMux ) * restful . Container {
container := restful . NewContainer ( )
container . ServeMux = mux
2015-08-28 14:01:31 -04:00
apiserver . InstallRecoverHandler ( container )
2014-11-11 02:11:45 -05:00
return container
}
2014-10-08 19:14:37 -04:00
// init initializes master.
func ( m * Master ) init ( c * Config ) {
2015-07-14 15:30:43 -04:00
healthzChecks := [ ] healthz . HealthzChecker { }
m . clock = util . RealClock { }
2015-08-28 11:10:05 -04:00
podStorage := podetcd . NewStorage ( c . DatabaseStorage , c . EnableWatchCache , c . KubeletClient )
2015-02-11 18:37:12 -05:00
2015-07-24 07:09:49 -04:00
podTemplateStorage := podtemplateetcd . NewREST ( c . DatabaseStorage )
2015-03-03 19:54:17 -05:00
2015-08-20 05:17:04 -04:00
eventStorage := eventetcd . NewREST ( c . DatabaseStorage , uint64 ( c . EventTTL . Seconds ( ) ) )
limitRangeStorage := limitrangeetcd . NewREST ( c . DatabaseStorage )
2015-03-13 15:15:04 -04:00
2015-08-20 05:17:04 -04:00
resourceQuotaStorage , resourceQuotaStatusStorage := resourcequotaetcd . NewREST ( c . DatabaseStorage )
secretStorage := secretetcd . NewREST ( c . DatabaseStorage )
serviceAccountStorage := serviceaccountetcd . NewREST ( c . DatabaseStorage )
persistentVolumeStorage , persistentVolumeStatusStorage := pvetcd . NewREST ( c . DatabaseStorage )
persistentVolumeClaimStorage , persistentVolumeClaimStatusStorage := pvcetcd . NewREST ( c . DatabaseStorage )
2015-03-12 11:08:06 -04:00
2015-08-20 05:17:04 -04:00
namespaceStorage , namespaceStatusStorage , namespaceFinalizeStorage := namespaceetcd . NewREST ( c . DatabaseStorage )
2015-03-12 11:08:06 -04:00
m . namespaceRegistry = namespace . NewRegistry ( namespaceStorage )
2015-02-11 19:07:54 -05:00
2015-08-28 11:10:05 -04:00
endpointsStorage := endpointsetcd . NewREST ( c . DatabaseStorage , c . EnableWatchCache )
2015-03-15 02:03:46 -04:00
m . endpointRegistry = endpoint . NewRegistry ( endpointsStorage )
2015-08-28 11:10:05 -04:00
nodeStorage , nodeStatusStorage := nodeetcd . NewREST ( c . DatabaseStorage , c . EnableWatchCache , c . KubeletClient )
2015-03-13 10:49:38 -04:00
m . nodeRegistry = minion . NewRegistry ( nodeStorage )
2015-08-20 05:17:04 -04:00
serviceStorage := serviceetcd . NewREST ( c . DatabaseStorage )
2015-08-06 06:02:01 -04:00
m . serviceRegistry = service . NewRegistry ( serviceStorage )
2015-02-11 18:37:12 -05:00
2015-05-23 16:41:11 -04:00
var serviceClusterIPRegistry service . RangeRegistry
serviceClusterIPAllocator := ipallocator . NewAllocatorCIDRRange ( m . serviceClusterIPRange , func ( max int , rangeSpec string ) allocator . Interface {
2015-05-22 18:28:48 -04:00
mem := allocator . NewAllocationMap ( max , rangeSpec )
2015-07-24 07:09:49 -04:00
etcd := etcdallocator . NewEtcd ( mem , "/ranges/serviceips" , "serviceipallocation" , c . DatabaseStorage )
2015-05-23 16:41:11 -04:00
serviceClusterIPRegistry = etcd
2015-05-22 18:28:48 -04:00
return etcd
} )
2015-05-23 16:41:11 -04:00
m . serviceClusterIPAllocator = serviceClusterIPRegistry
2015-05-22 18:28:48 -04:00
var serviceNodePortRegistry service . RangeRegistry
2015-05-23 16:41:11 -04:00
serviceNodePortAllocator := portallocator . NewPortAllocatorCustom ( m . serviceNodePortRange , func ( max int , rangeSpec string ) allocator . Interface {
2015-05-22 18:28:48 -04:00
mem := allocator . NewAllocationMap ( max , rangeSpec )
2015-07-24 07:09:49 -04:00
etcd := etcdallocator . NewEtcd ( mem , "/ranges/servicenodeports" , "servicenodeportallocation" , c . DatabaseStorage )
2015-05-22 18:28:48 -04:00
serviceNodePortRegistry = etcd
return etcd
} )
m . serviceNodePortAllocator = serviceNodePortRegistry
2015-05-03 18:44:05 -04:00
2015-07-24 07:09:49 -04:00
controllerStorage := controlleretcd . NewREST ( c . DatabaseStorage )
2015-03-16 00:36:26 -04:00
2014-11-11 02:11:45 -05:00
// TODO: Factor out the core API registration
2015-03-21 12:24:16 -04:00
m . storage = map [ string ] rest . Storage {
2015-04-14 11:12:27 -04:00
"pods" : podStorage . Pod ,
2015-07-28 18:56:27 -04:00
"pods/attach" : podStorage . Attach ,
2015-04-14 11:12:27 -04: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 18:37:12 -05:00
2015-03-03 19:54:17 -05:00
"podTemplates" : podTemplateStorage ,
2015-03-16 00:36:26 -04:00
"replicationControllers" : controllerStorage ,
2015-08-06 02:57:29 -04:00
"services" : service . NewStorage ( m . serviceRegistry , m . endpointRegistry , serviceClusterIPAllocator , serviceNodePortAllocator ) ,
2015-03-15 02:03:46 -04:00
"endpoints" : endpointsStorage ,
2015-02-11 19:07:54 -05:00
"nodes" : nodeStorage ,
2015-04-08 05:32:47 -04:00
"nodes/status" : nodeStatusStorage ,
2015-08-11 05:21:00 -04:00
"events" : eventStorage ,
2014-08-15 19:01:33 -04:00
2015-08-11 03:05:40 -04:00
"limitRanges" : limitRangeStorage ,
2015-04-06 14:40:45 -04:00
"resourceQuotas" : resourceQuotaStorage ,
"resourceQuotas/status" : resourceQuotaStatusStorage ,
"namespaces" : namespaceStorage ,
"namespaces/status" : namespaceStatusStorage ,
"namespaces/finalize" : namespaceFinalizeStorage ,
2015-04-27 23:50:56 -04:00
"secrets" : secretStorage ,
2015-04-27 18:53:28 -04:00
"serviceAccounts" : serviceAccountStorage ,
2015-04-06 14:40:45 -04:00
"persistentVolumes" : persistentVolumeStorage ,
"persistentVolumes/status" : persistentVolumeStatusStorage ,
"persistentVolumeClaims" : persistentVolumeClaimStorage ,
"persistentVolumeClaims/status" : persistentVolumeClaimStatusStorage ,
2015-04-15 15:23:02 -04:00
2015-05-13 20:29:25 -04:00
"componentStatuses" : componentstatus . NewStorage ( func ( ) map [ string ] apiserver . Server { return m . getServersToValidate ( c ) } ) ,
2014-06-16 02:29:07 -04:00
}
2014-10-27 20:56:33 -04:00
2015-06-15 22:39:31 -04:00
// establish the node proxy dialer
2015-05-28 00:38:21 -04:00
if len ( c . SSHUser ) > 0 {
2015-06-18 02:10:32 -04:00
// Usernames are capped @ 32
if len ( c . SSHUser ) > 32 {
glog . Warning ( "SSH User is too long, truncating to 32 chars" )
c . SSHUser = c . SSHUser [ 0 : 32 ]
}
2015-05-28 00:38:21 -04:00
glog . Infof ( "Setting up proxy: %s %s" , c . SSHUser , c . SSHKeyfile )
2015-06-15 22:39:31 -04:00
2015-06-17 14:49:13 -04:00
// public keyfile is written last, so check for that.
publicKeyFile := c . SSHKeyfile + ".pub"
exists , err := util . FileExists ( publicKeyFile )
2015-05-28 14:45:08 -04:00
if err != nil {
glog . Errorf ( "Error detecting if key exists: %v" , err )
} else if ! exists {
glog . Infof ( "Key doesn't exist, attempting to create" )
2015-06-17 14:49:13 -04:00
err := m . generateSSHKey ( c . SSHUser , c . SSHKeyfile , publicKeyFile )
2015-05-28 14:45:08 -04:00
if err != nil {
glog . Errorf ( "Failed to create key pair: %v" , err )
}
}
2015-06-17 14:49:13 -04:00
m . tunnels = & util . SSHTunnelList { }
2015-06-15 22:39:31 -04:00
m . dialer = m . Dial
2015-06-17 14:49:13 -04:00
m . setupSecureProxy ( c . SSHUser , c . SSHKeyfile , publicKeyFile )
2015-07-14 15:30:43 -04:00
m . lastSync = m . clock . Now ( ) . Unix ( )
2015-05-29 18:33:22 -04:00
// This is pretty ugly. A better solution would be to pull this all the way up into the
// server.go file.
httpKubeletClient , ok := c . KubeletClient . ( * client . HTTPKubeletClient )
if ok {
2015-06-15 22:39:31 -04:00
httpKubeletClient . Config . Dial = m . dialer
2015-05-29 18:33:22 -04:00
transport , err := client . MakeTransport ( httpKubeletClient . Config )
if err != nil {
glog . Errorf ( "Error setting up transport over SSH: %v" , err )
} else {
httpKubeletClient . Client . Transport = transport
}
} else {
2015-08-07 21:52:23 -04:00
glog . Errorf ( "Failed to cast %v to HTTPKubeletClient, skipping SSH tunnel." , c . KubeletClient )
2015-05-29 18:33:22 -04:00
}
2015-07-14 15:30:43 -04:00
healthzChecks = append ( healthzChecks , healthz . NamedCheck ( "SSH Tunnel Check" , m . IsTunnelSyncHealthy ) )
m . lastSyncMetric = prometheus . NewGaugeFunc ( prometheus . GaugeOpts {
Name : "apiserver_proxy_tunnel_sync_latency_secs" ,
Help : "The time since the last successful synchronization of the SSH tunnels for proxy requests." ,
} , func ( ) float64 { return float64 ( m . secondsSinceSync ( ) ) } )
2015-05-28 00:38:21 -04:00
}
2015-05-11 19:09:25 -04:00
apiVersions := [ ] string { }
2015-04-28 15:23:13 -04:00
if m . v1 {
2015-06-15 22:39:31 -04:00
if err := m . api_v1 ( ) . InstallREST ( m . handlerContainer ) ; err != nil {
2015-04-28 15:23:13 -04:00
glog . Fatalf ( "Unable to setup API v1: %v" , err )
}
apiVersions = append ( apiVersions , "v1" )
2015-01-08 12:42:20 -05:00
}
2014-11-11 02:11:45 -05:00
2015-07-14 15:30:43 -04:00
apiserver . InstallSupport ( m . muxHelper , m . rootWebService , c . EnableProfiling , healthzChecks ... )
2015-06-15 22:39:31 -04:00
apiserver . AddApiWebService ( m . handlerContainer , c . APIPrefix , apiVersions )
2015-04-23 03:06:59 -04:00
defaultVersion := m . defaultAPIGroupVersion ( )
2015-08-07 21:52:23 -04:00
requestInfoResolver := & apiserver . APIRequestInfoResolver { APIPrefixes : util . NewStringSet ( strings . TrimPrefix ( defaultVersion . Root , "/" ) ) , RestMapper : defaultVersion . Mapper }
2015-06-15 22:39:31 -04:00
apiserver . InstallServiceErrorHandler ( m . handlerContainer , requestInfoResolver , apiVersions )
2015-01-07 18:43:38 -05:00
2015-07-29 19:15:24 -04:00
if m . exp {
expVersion := m . expapi ( c )
if err := expVersion . InstallREST ( m . handlerContainer ) ; err != nil {
glog . Fatalf ( "Unable to setup experimental api: %v" , err )
}
apiserver . AddApiWebService ( m . handlerContainer , c . ExpAPIPrefix , [ ] string { expVersion . Version } )
2015-08-07 21:52:23 -04:00
expRequestInfoResolver := & apiserver . APIRequestInfoResolver { APIPrefixes : util . NewStringSet ( strings . TrimPrefix ( expVersion . Root , "/" ) ) , RestMapper : expVersion . Mapper }
2015-07-29 19:15:24 -04:00
apiserver . InstallServiceErrorHandler ( m . handlerContainer , expRequestInfoResolver , [ ] string { expVersion . Version } )
}
2015-01-07 18:43:38 -05: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 18:53:04 -05:00
// Allow master to be embedded in contexts which already have something registered at the root
if c . EnableIndex {
2015-06-15 22:39:31 -04:00
m . mux . HandleFunc ( "/" , apiserver . IndexHandler ( m . handlerContainer , m . muxHelper ) )
2015-01-30 18:53:04 -05:00
}
2014-11-11 02:11:45 -05:00
2014-10-23 16:56:18 -04:00
if c . EnableLogsSupport {
2015-01-15 19:18:47 -05:00
apiserver . InstallLogsSupport ( m . muxHelper )
2014-10-23 16:56:18 -04:00
}
if c . EnableUISupport {
2015-01-15 19:18:47 -05:00
ui . InstallSupport ( m . muxHelper , m . enableSwaggerSupport )
2014-10-23 16:56:18 -04:00
}
2014-10-23 19:55:14 -04:00
2015-03-13 11:44:11 -04: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 02:11:45 -05:00
2014-10-23 19:55:14 -04:00
handler := http . Handler ( m . mux . ( * http . ServeMux ) )
2014-11-11 02:11:45 -05: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 19:55:14 -04:00
if len ( c . CorsAllowedOriginList ) > 0 {
allowedOriginRegexps , err := util . CompileRegexps ( c . CorsAllowedOriginList )
if err != nil {
2015-07-29 16:09:04 -04:00
glog . Fatalf ( "Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v" , strings . Join ( c . CorsAllowedOriginList , "," ) , err )
2014-10-23 19:55:14 -04:00
}
handler = apiserver . CORS ( handler , allowedOriginRegexps , nil , nil , "true" )
}
2014-11-06 12:11:31 -05:00
m . InsecureHandler = handler
2015-02-11 17:09:25 -05:00
attributeGetter := apiserver . NewRequestAttributeGetter ( m . requestContextMapper , latest . RESTMapper , "api" )
2014-11-02 01:50:00 -05:00
handler = apiserver . WithAuthorizationCheck ( handler , attributeGetter , m . authorizer )
2014-10-16 17:18:16 -04:00
// Install Authenticator
2015-02-11 17:09:25 -05:00
if c . Authenticator != nil {
2015-04-28 01:24:26 -04:00
authenticatedHandler , err := handlers . NewRequestAuthenticator ( m . requestContextMapper , c . Authenticator , handlers . Unauthorized ( c . SupportsBasicAuth ) , handler )
2015-02-11 17:09:25 -05:00
if err != nil {
glog . Fatalf ( "Could not initialize authenticator: %v" , err )
}
handler = authenticatedHandler
2014-10-23 19:55:14 -04:00
}
2014-11-11 02:11:45 -05:00
// Install root web services
m . handlerContainer . Add ( m . rootWebService )
2014-12-15 15:29:55 -05:00
// TODO: Make this optional? Consumers of master depend on this currently.
m . Handler = handler
if m . enableSwaggerSupport {
m . InstallSwaggerAPI ( )
}
2015-02-11 17:09:25 -05: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 15:29:55 -05:00
// TODO: Attempt clean shutdown?
2015-05-03 18:44:05 -04:00
if m . enableCoreControllers {
2015-05-04 15:11:19 -04: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 16:41:11 -04:00
EndpointRegistry : m . endpointRegistry ,
EndpointInterval : 10 * time . Second ,
2015-05-22 18:28:48 -04:00
2015-05-23 16:41:11 -04:00
ServiceClusterIPRegistry : m . serviceClusterIPAllocator ,
ServiceClusterIPRange : m . serviceClusterIPRange ,
ServiceClusterIPInterval : 3 * time . Minute ,
ServiceNodePortRegistry : m . serviceNodePortAllocator ,
ServiceNodePortRange : m . serviceNodePortRange ,
2015-05-22 18:28:48 -04:00
ServiceNodePortInterval : 3 * time . Minute ,
2015-05-04 15:11:19 -04:00
PublicIP : m . clusterIP ,
ServiceIP : m . serviceReadWriteIP ,
ServicePort : m . serviceReadWritePort ,
PublicServicePort : m . publicReadWritePort ,
2015-05-03 18:44:05 -04:00
}
2014-12-15 15:29:55 -05: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 00:39:19 -04: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 19:14:14 -04:00
}
2015-03-25 00:39:19 -04:00
webServicesUrl := protocol + hostAndPort
2014-11-11 02:11:45 -05:00
// Enable swagger UI and discovery API
swaggerConfig := swagger . Config {
2015-03-11 19:14:14 -04:00
WebServicesUrl : webServicesUrl ,
WebServices : m . handlerContainer . RegisteredWebServices ( ) ,
2014-12-19 19:16:54 -05:00
ApiPath : "/swaggerapi/" ,
SwaggerPath : "/swaggerui/" ,
2015-01-05 18:50:19 -05:00
SwaggerFilePath : "/swagger-ui/" ,
2014-11-11 02:11:45 -05:00
}
2015-06-15 22:39:31 -04:00
swagger . RegisterSwaggerService ( swaggerConfig , m . handlerContainer )
2014-06-16 02:29:07 -04:00
}
2015-05-13 20:29:25 -04:00
func ( m * Master ) getServersToValidate ( c * Config ) map [ string ] apiserver . Server {
2014-11-02 15:52:31 -05:00
serversToValidate := map [ string ] apiserver . Server {
2014-12-15 22:45:27 -05: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 15:52:31 -05:00
}
2015-07-24 07:09:49 -04:00
for ix , machine := range c . DatabaseStorage . Backends ( ) {
2014-11-02 15:52:31 -05: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 07:27:18 -04:00
serversToValidate [ fmt . Sprintf ( "etcd-%d" , ix ) ] = apiserver . Server { Addr : addr , Port : port , Path : "/health" , Validate : etcdstorage . EtcdHealthCheck }
2014-11-02 15:52:31 -05:00
}
return serversToValidate
}
2015-03-04 15:57:05 -05:00
func ( m * Master ) defaultAPIGroupVersion ( ) * apiserver . APIGroupVersion {
return & apiserver . APIGroupVersion {
Root : m . apiPrefix ,
Mapper : latest . RESTMapper ,
2015-03-22 17:43:00 -04:00
Creater : api . Scheme ,
Convertor : api . Scheme ,
Typer : api . Scheme ,
Linker : latest . SelfLinker ,
2015-03-04 15:57:05 -05:00
Admit : m . admissionControl ,
Context : m . requestContextMapper ,
2015-06-15 22:39:31 -04:00
ProxyDialerFn : m . dialer ,
MinRequestTimeout : m . minRequestTimeout ,
2015-03-04 15:57:05 -05:00
}
}
2015-04-28 15:23:13 -04: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 00:38:21 -04:00
2015-08-19 14:02:01 -04:00
func ( m * Master ) InstallThirdPartyAPI ( rsrc * expapi . ThirdPartyResource ) error {
2015-08-20 01:08:26 -04:00
kind , group , err := thirdpartyresourcedata . ExtractApiGroupAndKind ( rsrc )
if err != nil {
return err
}
2015-08-21 17:24:16 -04:00
thirdparty := m . thirdpartyapi ( group , kind , rsrc . Versions [ 0 ] . Name )
2015-08-19 14:02:01 -04:00
if err := thirdparty . InstallREST ( m . handlerContainer ) ; err != nil {
glog . Fatalf ( "Unable to setup thirdparty api: %v" , err )
}
2015-08-20 01:08:26 -04:00
thirdPartyPrefix := "/thirdparty/" + group + "/"
2015-08-19 14:02:01 -04:00
apiserver . AddApiWebService ( m . handlerContainer , thirdPartyPrefix , [ ] string { rsrc . Versions [ 0 ] . Name } )
2015-08-20 01:08:26 -04:00
thirdPartyRequestInfoResolver := & apiserver . APIRequestInfoResolver { APIPrefixes : util . NewStringSet ( strings . TrimPrefix ( group , "/" ) ) , RestMapper : thirdparty . Mapper }
2015-08-19 14:02:01 -04:00
apiserver . InstallServiceErrorHandler ( m . handlerContainer , thirdPartyRequestInfoResolver , [ ] string { thirdparty . Version } )
return nil
}
2015-08-20 01:08:26 -04:00
func ( m * Master ) thirdpartyapi ( group , kind , version string ) * apiserver . APIGroupVersion {
resourceStorage := thirdpartyresourcedataetcd . NewREST ( m . thirdPartyStorage , group , kind )
2015-08-19 14:02:01 -04:00
2015-08-20 01:08:26 -04:00
apiRoot := "/thirdparty/" + group + "/"
2015-08-19 14:02:01 -04:00
storage := map [ string ] rest . Storage {
2015-08-21 17:24:16 -04:00
strings . ToLower ( kind ) + "s" : resourceStorage ,
2015-08-19 14:02:01 -04:00
}
return & apiserver . APIGroupVersion {
Root : apiRoot ,
2015-09-01 01:28:08 -04:00
Creater : thirdpartyresourcedata . NewObjectCreator ( version , api . Scheme ) ,
2015-08-19 14:02:01 -04:00
Convertor : api . Scheme ,
Typer : api . Scheme ,
2015-09-01 01:28:08 -04:00
Mapper : thirdpartyresourcedata . NewMapper ( explatest . RESTMapper , kind , version ) ,
2015-08-19 14:02:01 -04:00
Codec : explatest . Codec ,
Linker : explatest . SelfLinker ,
Storage : storage ,
2015-08-20 01:08:26 -04:00
Version : version ,
2015-08-19 14:02:01 -04:00
Admit : m . admissionControl ,
Context : m . requestContextMapper ,
ProxyDialerFn : m . dialer ,
MinRequestTimeout : m . minRequestTimeout ,
}
}
2015-07-29 19:15:24 -04:00
// expapi returns the resources and codec for the experimental api
func ( m * Master ) expapi ( c * Config ) * apiserver . APIGroupVersion {
2015-08-15 01:10:15 -04:00
controllerStorage := expcontrolleretcd . NewStorage ( c . ExpDatabaseStorage )
autoscalerStorage := horizontalpodautoscaleretcd . NewREST ( c . ExpDatabaseStorage )
thirdPartyResourceStorage := thirdpartyresourceetcd . NewREST ( c . ExpDatabaseStorage )
2015-08-27 13:17:51 -04:00
daemonStorage := daemonetcd . NewREST ( c . ExpDatabaseStorage )
2015-08-29 01:19:32 -04:00
deploymentStorage := deploymentetcd . NewREST ( c . ExpDatabaseStorage )
2015-08-06 10:14:28 -04:00
2015-08-06 12:53:34 -04:00
storage := map [ string ] rest . Storage {
2015-08-11 05:42:48 -04:00
strings . ToLower ( "replicationControllers" ) : controllerStorage . ReplicationController ,
strings . ToLower ( "replicationControllers/scale" ) : controllerStorage . Scale ,
2015-08-06 10:14:28 -04:00
strings . ToLower ( "horizontalpodautoscalers" ) : autoscalerStorage ,
2015-08-27 13:17:51 -04:00
strings . ToLower ( "thirdpartyresources" ) : thirdPartyResourceStorage ,
strings . ToLower ( "daemons" ) : daemonStorage ,
2015-08-29 01:19:32 -04:00
strings . ToLower ( "deployments" ) : deploymentStorage ,
2015-08-06 12:53:34 -04:00
}
2015-07-29 19:15:24 -04:00
return & apiserver . APIGroupVersion {
Root : m . expAPIPrefix ,
Creater : api . Scheme ,
Convertor : api . Scheme ,
Typer : api . Scheme ,
Mapper : explatest . RESTMapper ,
Codec : explatest . Codec ,
Linker : explatest . SelfLinker ,
Storage : storage ,
Version : explatest . Version ,
Admit : m . admissionControl ,
Context : m . requestContextMapper ,
ProxyDialerFn : m . dialer ,
MinRequestTimeout : m . minRequestTimeout ,
}
}
2015-07-28 04:10:48 -04:00
// findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP.
2015-05-28 00:38:21 -04:00
func findExternalAddress ( node * api . Node ) ( string , error ) {
2015-07-28 04:10:48 -04:00
var fallback string
2015-05-28 00:38:21 -04:00
for ix := range node . Status . Addresses {
addr := & node . Status . Addresses [ ix ]
if addr . Type == api . NodeExternalIP {
return addr . Address , nil
}
2015-07-28 04:10:48 -04:00
if fallback == "" && addr . Type == api . NodeLegacyHostIP {
fallback = addr . Address
}
}
if fallback != "" {
return fallback , nil
2015-05-28 00:38:21 -04:00
}
return "" , fmt . Errorf ( "Couldn't find external address: %v" , node )
}
func ( m * Master ) Dial ( net , addr string ) ( net . Conn , error ) {
2015-06-18 19:31:54 -04:00
// Only lock while picking a tunnel.
tunnel , err := func ( ) ( util . SSHTunnelEntry , error ) {
m . tunnelsLock . Lock ( )
defer m . tunnelsLock . Unlock ( )
return m . tunnels . PickRandomTunnel ( )
} ( )
if err != nil {
return nil , err
}
start := time . Now ( )
id := rand . Int63 ( ) // So you can match begins/ends in the log.
glog . V ( 3 ) . Infof ( "[%x: %v] Dialing..." , id , tunnel . Address )
defer func ( ) {
glog . V ( 3 ) . Infof ( "[%x: %v] Dialed in %v." , id , tunnel . Address , time . Now ( ) . Sub ( start ) )
} ( )
return tunnel . Tunnel . Dial ( net , addr )
2015-05-28 00:38:21 -04:00
}
2015-06-02 12:52:35 -04:00
func ( m * Master ) needToReplaceTunnels ( addrs [ ] string ) bool {
2015-06-29 19:15:42 -04:00
m . tunnelsLock . Lock ( )
defer m . tunnelsLock . Unlock ( )
2015-06-16 13:36:38 -04:00
if m . tunnels == nil || m . tunnels . Len ( ) != len ( addrs ) {
2015-05-28 00:38:21 -04:00
return true
}
2015-06-04 14:58:38 -04:00
// TODO (cjcullen): This doesn't need to be n^2
2015-05-28 00:38:21 -04:00
for ix := range addrs {
if ! m . tunnels . Has ( addrs [ ix ] ) {
return true
}
}
return false
}
2015-06-02 12:52:35 -04:00
func ( m * Master ) getNodeAddresses ( ) ( [ ] string , error ) {
2015-05-28 00:38:21 -04:00
nodes , err := m . nodeRegistry . ListMinions ( api . NewDefaultContext ( ) , labels . Everything ( ) , fields . Everything ( ) )
if err != nil {
2015-06-02 12:52:35 -04:00
return nil , err
2015-05-28 00:38:21 -04:00
}
2015-06-02 12:52:35 -04:00
addrs := [ ] string { }
2015-05-28 00:38:21 -04:00
for ix := range nodes . Items {
node := & nodes . Items [ ix ]
addr , err := findExternalAddress ( node )
if err != nil {
2015-06-02 12:52:35 -04:00
return nil , err
2015-05-28 00:38:21 -04:00
}
2015-06-02 12:52:35 -04:00
addrs = append ( addrs , addr )
2015-05-28 00:38:21 -04:00
}
2015-06-02 12:52:35 -04:00
return addrs , nil
}
2015-05-28 00:38:21 -04:00
2015-07-14 15:30:43 -04:00
func ( m * Master ) IsTunnelSyncHealthy ( req * http . Request ) error {
lag := m . secondsSinceSync ( )
if lag > 600 {
return fmt . Errorf ( "Tunnel sync is taking to long: %d" , lag )
}
return nil
}
func ( m * Master ) secondsSinceSync ( ) int64 {
now := m . clock . Now ( ) . Unix ( )
then := atomic . LoadInt64 ( & m . lastSync )
return now - then
}
2015-06-02 12:52:35 -04:00
func ( m * Master ) replaceTunnels ( user , keyfile string , newAddrs [ ] string ) error {
2015-06-04 14:58:38 -04:00
glog . Infof ( "replacing tunnels. New addrs: %v" , newAddrs )
2015-06-17 14:49:13 -04:00
tunnels := util . MakeSSHTunnels ( user , keyfile , newAddrs )
2015-06-15 15:38:14 -04:00
if err := tunnels . Open ( ) ; err != nil {
return err
}
2015-06-29 19:15:42 -04:00
m . tunnelsLock . Lock ( )
defer m . tunnelsLock . Unlock ( )
2015-05-28 00:38:21 -04:00
if m . tunnels != nil {
m . tunnels . Close ( )
}
m . tunnels = tunnels
2015-07-14 15:30:43 -04:00
atomic . StoreInt64 ( & m . lastSync , m . clock . Now ( ) . Unix ( ) )
2015-05-28 00:38:21 -04:00
return nil
}
2015-06-02 12:52:35 -04:00
func ( m * Master ) loadTunnels ( user , keyfile string ) error {
addrs , err := m . getNodeAddresses ( )
if err != nil {
return err
}
if ! m . needToReplaceTunnels ( addrs ) {
return nil
}
// TODO: This is going to unnecessarily close connections to unchanged nodes.
// See comment about using Watch above.
2015-06-04 14:58:38 -04:00
glog . Info ( "found different nodes. Need to replace tunnels" )
2015-06-02 12:52:35 -04:00
return m . replaceTunnels ( user , keyfile , addrs )
}
func ( m * Master ) refreshTunnels ( user , keyfile string ) error {
addrs , err := m . getNodeAddresses ( )
if err != nil {
return err
2015-05-28 00:38:21 -04:00
}
2015-06-02 12:52:35 -04:00
return m . replaceTunnels ( user , keyfile , addrs )
}
2015-05-28 00:38:21 -04:00
2015-06-17 14:49:13 -04:00
func ( m * Master ) setupSecureProxy ( user , privateKeyfile , publicKeyfile string ) {
// Sync loop to ensure that the SSH key has been installed.
go util . Until ( func ( ) {
if m . installSSHKey == nil {
glog . Error ( "Won't attempt to install ssh key: installSSHKey function is nil" )
return
}
key , err := util . ParsePublicKeyFromFile ( publicKeyfile )
if err != nil {
glog . Errorf ( "Failed to load public key: %v" , err )
return
}
keyData , err := util . EncodeSSHKey ( key )
if err != nil {
glog . Errorf ( "Failed to encode public key: %v" , err )
return
}
if err := m . installSSHKey ( user , keyData ) ; err != nil {
glog . Errorf ( "Failed to install ssh key: %v" , err )
}
} , 5 * time . Minute , util . NeverStop )
2015-05-28 00:38:21 -04:00
// Sync loop for tunnels
// TODO: switch this to watch.
2015-06-15 15:38:14 -04:00
go util . Until ( func ( ) {
2015-06-17 14:49:13 -04:00
if err := m . loadTunnels ( user , privateKeyfile ) ; err != nil {
2015-06-15 15:38:14 -04:00
glog . Errorf ( "Failed to load SSH Tunnels: %v" , err )
}
2015-06-16 13:36:38 -04:00
if m . tunnels != nil && m . tunnels . Len ( ) != 0 {
2015-06-15 20:13:11 -04:00
// Sleep for 10 seconds if we have some tunnels.
// TODO (cjcullen): tunnels can lag behind actually existing nodes.
2015-06-15 15:38:14 -04:00
time . Sleep ( 9 * time . Second )
2015-05-28 00:38:21 -04:00
}
2015-06-15 20:13:11 -04:00
} , 1 * time . Second , util . NeverStop )
2015-06-02 12:52:35 -04:00
// Refresh loop for tunnels
// TODO: could make this more controller-ish
2015-06-15 15:38:14 -04:00
go util . Until ( func ( ) {
time . Sleep ( 5 * time . Minute )
2015-06-17 14:49:13 -04:00
if err := m . refreshTunnels ( user , privateKeyfile ) ; err != nil {
2015-06-15 15:38:14 -04:00
glog . Errorf ( "Failed to refresh SSH Tunnels: %v" , err )
2015-06-02 12:52:35 -04:00
}
2015-06-15 20:13:11 -04:00
} , 0 * time . Second , util . NeverStop )
2015-05-28 00:38:21 -04:00
}
2015-05-28 14:45:08 -04:00
2015-06-17 14:49:13 -04:00
func ( m * Master ) generateSSHKey ( user , privateKeyfile , publicKeyfile string ) error {
2015-05-28 14:45:08 -04:00
private , public , err := util . GenerateKey ( 2048 )
if err != nil {
return err
}
2015-06-17 14:49:13 -04:00
// If private keyfile already exists, we must have only made it halfway
// through last time, so delete it.
exists , err := util . FileExists ( privateKeyfile )
if err != nil {
glog . Errorf ( "Error detecting if private key exists: %v" , err )
} else if exists {
glog . Infof ( "Private key exists, but public key does not" )
if err := os . Remove ( privateKeyfile ) ; err != nil {
glog . Errorf ( "Failed to remove stale private key: %v" , err )
}
}
if err := ioutil . WriteFile ( privateKeyfile , util . EncodePrivateKey ( private ) , 0600 ) ; err != nil {
2015-05-29 17:29:17 -04:00
return err
}
2015-06-17 14:49:13 -04:00
publicKeyBytes , err := util . EncodePublicKey ( public )
2015-05-28 14:45:08 -04:00
if err != nil {
return err
}
2015-06-17 14:49:13 -04:00
if err := ioutil . WriteFile ( publicKeyfile + ".tmp" , publicKeyBytes , 0600 ) ; err != nil {
return err
}
return os . Rename ( publicKeyfile + ".tmp" , publicKeyfile )
2015-05-28 14:45:08 -04:00
}