k3s/pkg/master/master.go

368 lines
14 KiB
Go
Raw Normal View History

/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package master
import (
"fmt"
"net/http"
"reflect"
"time"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
2015-10-09 01:33:33 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
2016-04-15 22:30:15 +00:00
appsapi "k8s.io/kubernetes/pkg/apis/apps/v1alpha1"
authenticationv1beta1 "k8s.io/kubernetes/pkg/apis/authentication/v1beta1"
2016-02-03 18:08:10 +00:00
authorizationapiv1beta1 "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"
autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
2016-04-14 01:45:43 +00:00
certificatesapiv1alpha1 "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1"
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
policyapiv1alpha1 "k8s.io/kubernetes/pkg/apis/policy/v1alpha1"
rbacapi "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
2016-09-01 15:29:26 +00:00
storageapiv1beta1 "k8s.io/kubernetes/pkg/apis/storage/v1beta1"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/genericapiserver"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/healthz"
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
"k8s.io/kubernetes/pkg/master/thirdparty"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/routes"
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus"
// RESTStorage installers
appsrest "k8s.io/kubernetes/pkg/registry/apps/rest"
authenticationrest "k8s.io/kubernetes/pkg/registry/authentication/rest"
authorizationrest "k8s.io/kubernetes/pkg/registry/authorization/rest"
autoscalingrest "k8s.io/kubernetes/pkg/registry/autoscaling/rest"
batchrest "k8s.io/kubernetes/pkg/registry/batch/rest"
certificatesrest "k8s.io/kubernetes/pkg/registry/certificates/rest"
2016-09-26 11:51:04 +00:00
corerest "k8s.io/kubernetes/pkg/registry/core/rest"
extensionsrest "k8s.io/kubernetes/pkg/registry/extensions/rest"
policyrest "k8s.io/kubernetes/pkg/registry/policy/rest"
rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest"
storagerest "k8s.io/kubernetes/pkg/registry/storage/rest"
)
const (
// DefaultEndpointReconcilerInterval is the default amount of time for how often the endpoints for
// the kubernetes Service are reconciled.
DefaultEndpointReconcilerInterval = 10 * time.Second
)
type Config struct {
GenericConfig *genericapiserver.Config
StorageFactory genericapiserver.StorageFactory
EnableWatchCache bool
EnableCoreControllers bool
EndpointReconcilerConfig EndpointReconcilerConfig
DeleteCollectionWorkers int
EventTTL time.Duration
KubeletClientConfig kubeletclient.KubeletClientConfig
// Used to start and monitor tunneling
Tunneler genericapiserver.Tunneler
EnableUISupport bool
EnableLogsSupport bool
ProxyTransport http.RoundTripper
2015-10-29 09:51:32 +00:00
}
// EndpointReconcilerConfig holds the endpoint reconciler and endpoint reconciliation interval to be
// used by the master.
type EndpointReconcilerConfig struct {
Reconciler EndpointReconciler
Interval time.Duration
}
// Master contains state for a Kubernetes cluster master/api server.
type Master struct {
2016-09-30 16:16:32 +00:00
GenericAPIServer *genericapiserver.GenericAPIServer
}
type completedConfig struct {
*Config
}
// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (c *Config) Complete() completedConfig {
c.GenericConfig.Complete()
// enable swagger UI only if general UI support is on
c.GenericConfig.EnableSwaggerUI = c.GenericConfig.EnableSwaggerUI && c.EnableUISupport
if c.EndpointReconcilerConfig.Interval == 0 {
c.EndpointReconcilerConfig.Interval = DefaultEndpointReconcilerInterval
}
if c.EndpointReconcilerConfig.Reconciler == nil {
// use a default endpoint reconciler if nothing is set
endpointClient := coreclient.NewForConfigOrDie(c.GenericConfig.LoopbackClientConfig)
c.EndpointReconcilerConfig.Reconciler = NewMasterCountEndpointReconciler(c.GenericConfig.MasterCount, endpointClient)
}
return completedConfig{c}
}
// SkipComplete provides a way to construct a server instance without config completion.
func (c *Config) SkipComplete() completedConfig {
return completedConfig{c}
}
// New returns a new instance of Master from the given config.
// Certain config fields will be set to a default value if unset.
// Certain config fields must be specified, including:
// KubeletClientConfig
func (c completedConfig) New() (*Master, error) {
if reflect.DeepEqual(c.KubeletClientConfig, kubeletclient.KubeletClientConfig{}) {
return nil, fmt.Errorf("Master.New() called with empty config.KubeletClientConfig")
}
s, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time
if err != nil {
return nil, err
}
if c.EnableUISupport {
routes.UIRedirect{}.Install(s.HandlerContainer)
}
if c.EnableLogsSupport {
routes.Logs{}.Install(s.HandlerContainer)
}
m := &Master{
GenericAPIServer: s,
2016-10-10 18:52:39 +00:00
}
2016-10-10 18:52:39 +00:00
restOptionsFactory := restOptionsFactory{
deleteCollectionWorkers: c.DeleteCollectionWorkers,
enableGarbageCollection: c.GenericConfig.EnableGarbageCollection,
storageFactory: c.StorageFactory,
}
if c.EnableWatchCache {
2016-10-10 18:52:39 +00:00
restOptionsFactory.storageDecorator = registry.StorageWithCacher
} else {
2016-10-10 18:52:39 +00:00
restOptionsFactory.storageDecorator = generic.UndecoratedStorage
}
2016-09-30 16:16:32 +00:00
// install legacy rest storage
2016-10-10 18:52:39 +00:00
if c.GenericConfig.APIResourceConfigSource.AnyResourcesForVersionEnabled(apiv1.SchemeGroupVersion) {
legacyRESTStorageProvider := corerest.LegacyRESTStorageProvider{
StorageFactory: c.StorageFactory,
ProxyTransport: c.ProxyTransport,
KubeletClientConfig: c.KubeletClientConfig,
EventTTL: c.EventTTL,
ServiceClusterIPRange: c.GenericConfig.ServiceClusterIPRange,
ServiceNodePortRange: c.GenericConfig.ServiceNodePortRange,
LoopbackClientConfig: c.GenericConfig.LoopbackClientConfig,
2016-10-10 18:52:39 +00:00
}
m.InstallLegacyAPI(c.Config, restOptionsFactory.NewFor, legacyRESTStorageProvider)
2016-09-30 16:16:32 +00:00
}
restStorageProviders := []genericapiserver.RESTStorageProvider{
appsrest.RESTStorageProvider{},
authenticationrest.RESTStorageProvider{Authenticator: c.GenericConfig.Authenticator},
authorizationrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorizer},
autoscalingrest.RESTStorageProvider{},
batchrest.RESTStorageProvider{},
certificatesrest.RESTStorageProvider{},
extensionsrest.RESTStorageProvider{ResourceInterface: thirdparty.NewThirdPartyResourceServer(s, c.StorageFactory)},
policyrest.RESTStorageProvider{},
rbacrest.RESTStorageProvider{AuthorizerRBACSuperUser: c.GenericConfig.AuthorizerRBACSuperUser},
storagerest.RESTStorageProvider{},
}
m.InstallAPIs(c.Config.GenericConfig.APIResourceConfigSource, restOptionsFactory.NewFor, restStorageProviders...)
2016-09-30 16:16:32 +00:00
2016-10-31 12:48:59 +00:00
if c.Tunneler != nil {
m.installTunneler(c.Tunneler, coreclient.NewForConfigOrDie(c.GenericConfig.LoopbackClientConfig).Nodes())
}
2016-09-30 16:16:32 +00:00
m.InstallGeneralEndpoints(c.Config)
return m, nil
}
2016-10-10 18:52:39 +00:00
func (m *Master) InstallLegacyAPI(c *Config, restOptionsGetter genericapiserver.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) {
legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter)
if err != nil {
glog.Fatalf("Error building core storage: %v", err)
}
if c.EnableCoreControllers {
bootstrapController := c.NewBootstrapController(legacyRESTStorage)
if err := m.GenericAPIServer.AddPostStartHook("bootstrap-controller", bootstrapController.PostStartHook); err != nil {
glog.Fatalf("Error registering PostStartHook %q: %v", "bootstrap-controller", err)
}
}
2016-10-18 12:12:24 +00:00
if err := m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo); err != nil {
2016-10-10 18:52:39 +00:00
glog.Fatalf("Error in registering group versions: %v", err)
}
}
2016-10-31 12:48:59 +00:00
func (m *Master) installTunneler(tunneler genericapiserver.Tunneler, nodeClient coreclient.NodeInterface) {
tunneler.Run(nodeAddressProvider{nodeClient}.externalAddresses)
m.GenericAPIServer.AddHealthzChecks(healthz.NamedCheck("SSH Tunnel Check", genericapiserver.TunnelSyncHealthChecker(tunneler)))
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(tunneler.SecondsSinceSync()) })
}
2016-09-30 16:16:32 +00:00
// TODO this needs to be refactored so we have a way to add general health checks to genericapiserver
// TODO profiling should be generic
func (m *Master) InstallGeneralEndpoints(c *Config) {
if c.GenericConfig.EnableProfiling {
routes.MetricsWithReset{}.Install(m.GenericAPIServer.HandlerContainer)
} else {
routes.DefaultMetrics{}.Install(m.GenericAPIServer.HandlerContainer)
}
}
// InstallAPIs will install the APIs for the restStorageProviders if they are enabled.
func (m *Master) InstallAPIs(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter genericapiserver.RESTOptionsGetter, restStorageProviders ...genericapiserver.RESTStorageProvider) {
apiGroupsInfo := []genericapiserver.APIGroupInfo{}
for _, restStorageBuilder := range restStorageProviders {
groupName := restStorageBuilder.GroupName()
if !apiResourceConfigSource.AnyResourcesForGroupEnabled(groupName) {
glog.V(1).Infof("Skipping disabled API group %q.", groupName)
continue
}
apiGroupInfo, enabled := restStorageBuilder.NewRESTStorage(apiResourceConfigSource, restOptionsGetter)
if !enabled {
glog.Warningf("Problem initializing API group %q, skipping.", groupName)
continue
2016-04-15 22:30:15 +00:00
}
glog.V(1).Infof("Enabling API group %q.", groupName)
2016-04-14 01:45:43 +00:00
2016-09-15 17:41:48 +00:00
if postHookProvider, ok := restStorageBuilder.(genericapiserver.PostStartHookProvider); ok {
name, hook, err := postHookProvider.PostStartHook()
if err != nil {
glog.Fatalf("Error building PostStartHook: %v", err)
}
if err := m.GenericAPIServer.AddPostStartHook(name, hook); err != nil {
glog.Fatalf("Error registering PostStartHook %q: %v", name, err)
}
}
apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo)
}
2016-09-22 11:02:52 +00:00
for i := range apiGroupsInfo {
2016-09-30 16:16:32 +00:00
if err := m.GenericAPIServer.InstallAPIGroup(&apiGroupsInfo[i]); err != nil {
2016-09-22 11:02:52 +00:00
glog.Fatalf("Error in registering group versions: %v", err)
}
}
}
type restOptionsFactory struct {
deleteCollectionWorkers int
enableGarbageCollection bool
storageFactory genericapiserver.StorageFactory
storageDecorator generic.StorageDecorator
}
func (f restOptionsFactory) NewFor(resource unversioned.GroupResource) generic.RESTOptions {
storageConfig, err := f.storageFactory.NewConfig(resource)
if err != nil {
glog.Fatalf("Unable to find storage destination for %v, due to %v", resource, err.Error())
}
return generic.RESTOptions{
2016-08-08 22:12:54 +00:00
StorageConfig: storageConfig,
Decorator: f.storageDecorator,
DeleteCollectionWorkers: f.deleteCollectionWorkers,
EnableGarbageCollection: f.enableGarbageCollection,
ResourcePrefix: f.storageFactory.ResourcePrefix(resource),
}
}
type nodeAddressProvider struct {
nodeClient coreclient.NodeInterface
}
func (n nodeAddressProvider) externalAddresses() (addresses []string, err error) {
nodes, err := n.nodeClient.List(api.ListOptions{})
if err != nil {
return nil, err
}
addrs := []string{}
for ix := range nodes.Items {
node := &nodes.Items[ix]
addr, err := findExternalAddress(node)
if err != nil {
return nil, err
}
addrs = append(addrs, addr)
}
return addrs, nil
}
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)
}
func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig {
ret := genericapiserver.NewResourceConfig()
ret.EnableVersions(
apiv1.SchemeGroupVersion,
extensionsapiv1beta1.SchemeGroupVersion,
batchapiv1.SchemeGroupVersion,
authenticationv1beta1.SchemeGroupVersion,
autoscalingapiv1.SchemeGroupVersion,
appsapi.SchemeGroupVersion,
policyapiv1alpha1.SchemeGroupVersion,
rbacapi.SchemeGroupVersion,
2016-09-01 15:29:26 +00:00
storageapiv1beta1.SchemeGroupVersion,
certificatesapiv1alpha1.SchemeGroupVersion,
2016-02-03 18:08:10 +00:00
authorizationapiv1beta1.SchemeGroupVersion,
)
// all extensions resources except these are disabled by default
ret.EnableResources(
extensionsapiv1beta1.SchemeGroupVersion.WithResource("daemonsets"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("deployments"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("horizontalpodautoscalers"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("ingresses"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("jobs"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("networkpolicies"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("replicasets"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("thirdpartyresources"),
)
return ret
}