mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-15 22:20:51 +00:00
Merge pull request #18113 from nikhiljindal/serverLibrary
Extracting api server code into a library: part 1
This commit is contained in:
@@ -5,6 +5,10 @@
|
|||||||
"path": "/api/v1",
|
"path": "/api/v1",
|
||||||
"description": "API at /api/v1"
|
"description": "API at /api/v1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/version",
|
||||||
|
"description": "git code version from which this is built"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/api",
|
"path": "/api",
|
||||||
"description": "get available API versions"
|
"description": "get available API versions"
|
||||||
@@ -20,10 +24,6 @@
|
|||||||
{
|
{
|
||||||
"path": "/apis",
|
"path": "/apis",
|
||||||
"description": "get available API versions"
|
"description": "get available API versions"
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/version",
|
|
||||||
"description": "git code version from which this is built"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"apiVersion": "",
|
"apiVersion": "",
|
||||||
|
@@ -44,6 +44,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/capabilities"
|
"k8s.io/kubernetes/pkg/capabilities"
|
||||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||||
|
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
"k8s.io/kubernetes/pkg/master"
|
"k8s.io/kubernetes/pkg/master"
|
||||||
"k8s.io/kubernetes/pkg/master/ports"
|
"k8s.io/kubernetes/pkg/master/ports"
|
||||||
@@ -130,7 +131,7 @@ func NewAPIServer() *APIServer {
|
|||||||
EventTTL: 1 * time.Hour,
|
EventTTL: 1 * time.Hour,
|
||||||
AuthorizationMode: "AlwaysAllow",
|
AuthorizationMode: "AlwaysAllow",
|
||||||
AdmissionControl: "AlwaysAdmit",
|
AdmissionControl: "AlwaysAdmit",
|
||||||
EtcdPathPrefix: master.DefaultEtcdPathPrefix,
|
EtcdPathPrefix: genericapiserver.DefaultEtcdPathPrefix,
|
||||||
EnableLogsSupport: true,
|
EnableLogsSupport: true,
|
||||||
MasterServiceNamespace: api.NamespaceDefault,
|
MasterServiceNamespace: api.NamespaceDefault,
|
||||||
CertDirectory: "/var/run/kubernetes",
|
CertDirectory: "/var/run/kubernetes",
|
||||||
@@ -314,7 +315,7 @@ func generateStorageVersionMap(legacyVersion string, storageVersions string) map
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse the value of --etcd-servers-overrides and update given storageDestinations.
|
// parse the value of --etcd-servers-overrides and update given storageDestinations.
|
||||||
func updateEtcdOverrides(overrides []string, storageVersions map[string]string, prefix string, storageDestinations *master.StorageDestinations, newEtcdFn newEtcdFunc) {
|
func updateEtcdOverrides(overrides []string, storageVersions map[string]string, prefix string, storageDestinations *genericapiserver.StorageDestinations, newEtcdFn newEtcdFunc) {
|
||||||
if len(overrides) == 0 {
|
if len(overrides) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -448,7 +449,7 @@ func (s *APIServer) Run(_ []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
storageDestinations := master.NewStorageDestinations()
|
storageDestinations := genericapiserver.NewStorageDestinations()
|
||||||
|
|
||||||
storageVersions := generateStorageVersionMap(s.DeprecatedStorageVersion, s.StorageVersions)
|
storageVersions := generateStorageVersionMap(s.DeprecatedStorageVersion, s.StorageVersions)
|
||||||
if _, found := storageVersions[legacyV1Group.GroupVersion.Group]; !found {
|
if _, found := storageVersions[legacyV1Group.GroupVersion.Group]; !found {
|
||||||
@@ -539,36 +540,39 @@ func (s *APIServer) Run(_ []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config := &master.Config{
|
config := &master.Config{
|
||||||
StorageDestinations: storageDestinations,
|
Config: &genericapiserver.Config{
|
||||||
StorageVersions: storageVersions,
|
StorageDestinations: storageDestinations,
|
||||||
EventTTL: s.EventTTL,
|
StorageVersions: storageVersions,
|
||||||
KubeletClient: kubeletClient,
|
ServiceClusterIPRange: &n,
|
||||||
ServiceClusterIPRange: &n,
|
EnableLogsSupport: s.EnableLogsSupport,
|
||||||
EnableCoreControllers: true,
|
EnableUISupport: true,
|
||||||
EnableLogsSupport: s.EnableLogsSupport,
|
EnableSwaggerSupport: true,
|
||||||
EnableUISupport: true,
|
EnableProfiling: s.EnableProfiling,
|
||||||
EnableSwaggerSupport: true,
|
EnableWatchCache: s.EnableWatchCache,
|
||||||
EnableProfiling: s.EnableProfiling,
|
EnableIndex: true,
|
||||||
EnableWatchCache: s.EnableWatchCache,
|
APIPrefix: s.APIPrefix,
|
||||||
EnableIndex: true,
|
APIGroupPrefix: s.APIGroupPrefix,
|
||||||
APIPrefix: s.APIPrefix,
|
CorsAllowedOriginList: s.CorsAllowedOriginList,
|
||||||
APIGroupPrefix: s.APIGroupPrefix,
|
ReadWritePort: s.SecurePort,
|
||||||
CorsAllowedOriginList: s.CorsAllowedOriginList,
|
PublicAddress: s.AdvertiseAddress,
|
||||||
ReadWritePort: s.SecurePort,
|
Authenticator: authenticator,
|
||||||
PublicAddress: s.AdvertiseAddress,
|
SupportsBasicAuth: len(s.BasicAuthFile) > 0,
|
||||||
Authenticator: authenticator,
|
Authorizer: authorizer,
|
||||||
SupportsBasicAuth: len(s.BasicAuthFile) > 0,
|
AdmissionControl: admissionController,
|
||||||
Authorizer: authorizer,
|
APIGroupVersionOverrides: apiGroupVersionOverrides,
|
||||||
AdmissionControl: admissionController,
|
MasterServiceNamespace: s.MasterServiceNamespace,
|
||||||
APIGroupVersionOverrides: apiGroupVersionOverrides,
|
ExternalHost: s.ExternalHost,
|
||||||
MasterServiceNamespace: s.MasterServiceNamespace,
|
MinRequestTimeout: s.MinRequestTimeout,
|
||||||
ExternalHost: s.ExternalHost,
|
ProxyDialer: proxyDialerFn,
|
||||||
MinRequestTimeout: s.MinRequestTimeout,
|
ProxyTLSClientConfig: proxyTLSClientConfig,
|
||||||
ProxyDialer: proxyDialerFn,
|
ServiceNodePortRange: s.ServiceNodePortRange,
|
||||||
ProxyTLSClientConfig: proxyTLSClientConfig,
|
KubernetesServiceNodePort: s.KubernetesServiceNodePort,
|
||||||
Tunneler: tunneler,
|
},
|
||||||
ServiceNodePortRange: s.ServiceNodePortRange,
|
EnableCoreControllers: true,
|
||||||
KubernetesServiceNodePort: s.KubernetesServiceNodePort,
|
EventTTL: s.EventTTL,
|
||||||
|
KubeletClient: kubeletClient,
|
||||||
|
|
||||||
|
Tunneler: tunneler,
|
||||||
}
|
}
|
||||||
m := master.New(config)
|
m := master.New(config)
|
||||||
|
|
||||||
@@ -682,7 +686,7 @@ func (s *APIServer) getRuntimeConfigValue(apiKey string, defaultValue bool) bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parses the given runtime-config and formats it into map[string]ApiGroupVersionOverride
|
// Parses the given runtime-config and formats it into map[string]ApiGroupVersionOverride
|
||||||
func (s *APIServer) parseRuntimeConfig() (map[string]master.APIGroupVersionOverride, error) {
|
func (s *APIServer) parseRuntimeConfig() (map[string]genericapiserver.APIGroupVersionOverride, error) {
|
||||||
// "api/all=false" allows users to selectively enable specific api versions.
|
// "api/all=false" allows users to selectively enable specific api versions.
|
||||||
disableAllAPIs := false
|
disableAllAPIs := false
|
||||||
allAPIFlagValue, ok := s.RuntimeConfig["api/all"]
|
allAPIFlagValue, ok := s.RuntimeConfig["api/all"]
|
||||||
@@ -703,9 +707,9 @@ func (s *APIServer) parseRuntimeConfig() (map[string]master.APIGroupVersionOverr
|
|||||||
disableV1 := disableAllAPIs
|
disableV1 := disableAllAPIs
|
||||||
v1GroupVersion := "api/v1"
|
v1GroupVersion := "api/v1"
|
||||||
disableV1 = !s.getRuntimeConfigValue(v1GroupVersion, !disableV1)
|
disableV1 = !s.getRuntimeConfigValue(v1GroupVersion, !disableV1)
|
||||||
apiGroupVersionOverrides := map[string]master.APIGroupVersionOverride{}
|
apiGroupVersionOverrides := map[string]genericapiserver.APIGroupVersionOverride{}
|
||||||
if disableV1 {
|
if disableV1 {
|
||||||
apiGroupVersionOverrides[v1GroupVersion] = master.APIGroupVersionOverride{
|
apiGroupVersionOverrides[v1GroupVersion] = genericapiserver.APIGroupVersionOverride{
|
||||||
Disable: true,
|
Disable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -717,7 +721,7 @@ func (s *APIServer) parseRuntimeConfig() (map[string]master.APIGroupVersionOverr
|
|||||||
// TODO: Make this a loop over all group/versions when there are more of them.
|
// TODO: Make this a loop over all group/versions when there are more of them.
|
||||||
disableExtensions = !s.getRuntimeConfigValue(extensionsGroupVersion, !disableExtensions)
|
disableExtensions = !s.getRuntimeConfigValue(extensionsGroupVersion, !disableExtensions)
|
||||||
if disableExtensions {
|
if disableExtensions {
|
||||||
apiGroupVersionOverrides[extensionsGroupVersion] = master.APIGroupVersionOverride{
|
apiGroupVersionOverrides[extensionsGroupVersion] = genericapiserver.APIGroupVersionOverride{
|
||||||
Disable: true,
|
Disable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/meta"
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/master"
|
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||||
"k8s.io/kubernetes/pkg/storage"
|
"k8s.io/kubernetes/pkg/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ func TestUpdateEtcdOverrides(t *testing.T) {
|
|||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
storageDestinations := master.NewStorageDestinations()
|
storageDestinations := genericapiserver.NewStorageDestinations()
|
||||||
override := test.apigroup + "/" + test.resource + "#" + strings.Join(test.servers, ";")
|
override := test.apigroup + "/" + test.resource + "#" + strings.Join(test.servers, ";")
|
||||||
updateEtcdOverrides([]string{override}, storageVersions, "", &storageDestinations, newEtcd)
|
updateEtcdOverrides([]string{override}, storageVersions, "", &storageDestinations, newEtcd)
|
||||||
apigroup, ok := storageDestinations.APIGroups[test.apigroup]
|
apigroup, ok := storageDestinations.APIGroups[test.apigroup]
|
||||||
@@ -160,12 +160,12 @@ func TestUpdateEtcdOverrides(t *testing.T) {
|
|||||||
func TestParseRuntimeConfig(t *testing.T) {
|
func TestParseRuntimeConfig(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
runtimeConfig map[string]string
|
runtimeConfig map[string]string
|
||||||
apiGroupVersionOverrides map[string]master.APIGroupVersionOverride
|
apiGroupVersionOverrides map[string]genericapiserver.APIGroupVersionOverride
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
runtimeConfig: map[string]string{},
|
runtimeConfig: map[string]string{},
|
||||||
apiGroupVersionOverrides: map[string]master.APIGroupVersionOverride{},
|
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{},
|
||||||
err: false,
|
err: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -173,7 +173,7 @@ func TestParseRuntimeConfig(t *testing.T) {
|
|||||||
runtimeConfig: map[string]string{
|
runtimeConfig: map[string]string{
|
||||||
"api/v1/pods": "false",
|
"api/v1/pods": "false",
|
||||||
},
|
},
|
||||||
apiGroupVersionOverrides: map[string]master.APIGroupVersionOverride{},
|
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -181,7 +181,7 @@ func TestParseRuntimeConfig(t *testing.T) {
|
|||||||
runtimeConfig: map[string]string{
|
runtimeConfig: map[string]string{
|
||||||
"api/v1": "false",
|
"api/v1": "false",
|
||||||
},
|
},
|
||||||
apiGroupVersionOverrides: map[string]master.APIGroupVersionOverride{
|
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{
|
||||||
"api/v1": {
|
"api/v1": {
|
||||||
Disable: true,
|
Disable: true,
|
||||||
},
|
},
|
||||||
@@ -193,7 +193,7 @@ func TestParseRuntimeConfig(t *testing.T) {
|
|||||||
runtimeConfig: map[string]string{
|
runtimeConfig: map[string]string{
|
||||||
"extensions/v1beta1": "false",
|
"extensions/v1beta1": "false",
|
||||||
},
|
},
|
||||||
apiGroupVersionOverrides: map[string]master.APIGroupVersionOverride{
|
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{
|
||||||
"extensions/v1beta1": {
|
"extensions/v1beta1": {
|
||||||
Disable: true,
|
Disable: true,
|
||||||
},
|
},
|
||||||
@@ -205,7 +205,7 @@ func TestParseRuntimeConfig(t *testing.T) {
|
|||||||
runtimeConfig: map[string]string{
|
runtimeConfig: map[string]string{
|
||||||
"extensions/v1beta1/deployments": "false",
|
"extensions/v1beta1/deployments": "false",
|
||||||
},
|
},
|
||||||
apiGroupVersionOverrides: map[string]master.APIGroupVersionOverride{
|
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{
|
||||||
"extensions/v1beta1": {
|
"extensions/v1beta1": {
|
||||||
ResourceOverrides: map[string]bool{
|
ResourceOverrides: map[string]bool{
|
||||||
"deployments": false,
|
"deployments": false,
|
||||||
@@ -220,7 +220,7 @@ func TestParseRuntimeConfig(t *testing.T) {
|
|||||||
"extensions/v1beta1/deployments": "true",
|
"extensions/v1beta1/deployments": "true",
|
||||||
"extensions/v1beta1/jobs": "false",
|
"extensions/v1beta1/jobs": "false",
|
||||||
},
|
},
|
||||||
apiGroupVersionOverrides: map[string]master.APIGroupVersionOverride{
|
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{
|
||||||
"extensions/v1beta1": {
|
"extensions/v1beta1": {
|
||||||
ResourceOverrides: map[string]bool{
|
ResourceOverrides: map[string]bool{
|
||||||
"deployments": true,
|
"deployments": true,
|
||||||
|
@@ -105,7 +105,7 @@ kube-apiserver
|
|||||||
--watch-cache[=true]: Enable watch caching in the apiserver
|
--watch-cache[=true]: Enable watch caching in the apiserver
|
||||||
```
|
```
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 9-Dec-2015
|
###### Auto generated by spf13/cobra on 15-Dec-2015
|
||||||
|
|
||||||
|
|
||||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||||
|
23
pkg/genericapiserver/doc.go
Normal file
23
pkg/genericapiserver/doc.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package genericapiserver contains code to setup a generic kubernetes-like API server.
|
||||||
|
// This does not contain any kubernetes API specific code.
|
||||||
|
// Note that this is a work in progress. We are pulling out generic code (specifically from
|
||||||
|
// pkg/master and pkg/apiserver) here.
|
||||||
|
// We plan to move this package into a separate repo on github once it is done.
|
||||||
|
// For more details: https://github.com/kubernetes/kubernetes/issues/2742
|
||||||
|
package genericapiserver
|
534
pkg/genericapiserver/genericapiserver.go
Normal file
534
pkg/genericapiserver/genericapiserver.go
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package genericapiserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/admission"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/rest"
|
||||||
|
"k8s.io/kubernetes/pkg/apiserver"
|
||||||
|
"k8s.io/kubernetes/pkg/auth/authenticator"
|
||||||
|
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||||
|
"k8s.io/kubernetes/pkg/auth/handlers"
|
||||||
|
"k8s.io/kubernetes/pkg/registry/generic"
|
||||||
|
genericetcd "k8s.io/kubernetes/pkg/registry/generic/etcd"
|
||||||
|
ipallocator "k8s.io/kubernetes/pkg/registry/service/ipallocator"
|
||||||
|
"k8s.io/kubernetes/pkg/storage"
|
||||||
|
"k8s.io/kubernetes/pkg/ui"
|
||||||
|
"k8s.io/kubernetes/pkg/util"
|
||||||
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
|
|
||||||
|
"github.com/emicklei/go-restful"
|
||||||
|
"github.com/emicklei/go-restful/swagger"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultEtcdPathPrefix = "/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
for _, backend := range group.Default.Backends(context.TODO()) {
|
||||||
|
backends.Insert(backend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if group.Overrides != nil {
|
||||||
|
for _, storage := range group.Overrides {
|
||||||
|
for _, backend := range storage.Backends(context.TODO()) {
|
||||||
|
backends.Insert(backend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return backends.List()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config is a structure used to configure a GenericAPIServer.
|
||||||
|
type Config struct {
|
||||||
|
StorageDestinations StorageDestinations
|
||||||
|
// StorageVersions is a map between groups and their storage versions
|
||||||
|
StorageVersions map[string]string
|
||||||
|
// allow downstream consumers to disable the core controller loops
|
||||||
|
EnableLogsSupport bool
|
||||||
|
EnableUISupport bool
|
||||||
|
// allow downstream consumers to disable swagger
|
||||||
|
EnableSwaggerSupport bool
|
||||||
|
// Allows api group versions or specific resources to be conditionally enabled/disabled.
|
||||||
|
APIGroupVersionOverrides map[string]APIGroupVersionOverride
|
||||||
|
// allow downstream consumers to disable the index route
|
||||||
|
EnableIndex bool
|
||||||
|
EnableProfiling bool
|
||||||
|
EnableWatchCache bool
|
||||||
|
APIPrefix string
|
||||||
|
APIGroupPrefix string
|
||||||
|
CorsAllowedOriginList []string
|
||||||
|
Authenticator authenticator.Request
|
||||||
|
// TODO(roberthbailey): Remove once the server no longer supports http basic auth.
|
||||||
|
SupportsBasicAuth bool
|
||||||
|
Authorizer authorizer.Authorizer
|
||||||
|
AdmissionControl admission.Interface
|
||||||
|
MasterServiceNamespace string
|
||||||
|
|
||||||
|
// Map requests to contexts. Exported so downstream consumers can provider their own mappers
|
||||||
|
RequestContextMapper api.RequestContextMapper
|
||||||
|
|
||||||
|
// If specified, all web services will be registered into this container
|
||||||
|
RestfulContainer *restful.Container
|
||||||
|
|
||||||
|
// If specified, requests will be allocated a random timeout between this value, and twice this value.
|
||||||
|
// Note that it is up to the request handlers to ignore or honor this timeout. In seconds.
|
||||||
|
MinRequestTimeout int
|
||||||
|
|
||||||
|
// Number of masters running; all masters must be started with the
|
||||||
|
// same value for this field. (Numbers > 1 currently untested.)
|
||||||
|
MasterCount int
|
||||||
|
|
||||||
|
// The port on PublicAddress where a read-write server will be installed.
|
||||||
|
// Defaults to 6443 if not set.
|
||||||
|
ReadWritePort int
|
||||||
|
|
||||||
|
// ExternalHost is the host name to use for external (public internet) facing URLs (e.g. Swagger)
|
||||||
|
ExternalHost string
|
||||||
|
|
||||||
|
// PublicAddress is the IP address where members of the cluster (kubelet,
|
||||||
|
// kube-proxy, services, etc.) can reach the GenericAPIServer.
|
||||||
|
// If nil or 0.0.0.0, the host's default interface will be used.
|
||||||
|
PublicAddress net.IP
|
||||||
|
|
||||||
|
// Control the interval that pod, node IP, and node heath status caches
|
||||||
|
// expire.
|
||||||
|
CacheTimeout time.Duration
|
||||||
|
|
||||||
|
// The range of IPs to be assigned to services with type=ClusterIP or greater
|
||||||
|
ServiceClusterIPRange *net.IPNet
|
||||||
|
|
||||||
|
// The IP address for the GenericAPIServer service (must be inside ServiceClusterIPRange
|
||||||
|
ServiceReadWriteIP net.IP
|
||||||
|
|
||||||
|
// The range of ports to be assigned to services with type=NodePort or greater
|
||||||
|
ServiceNodePortRange util.PortRange
|
||||||
|
|
||||||
|
// Used to customize default proxy dial/tls options
|
||||||
|
ProxyDialer apiserver.ProxyDialerFunc
|
||||||
|
ProxyTLSClientConfig *tls.Config
|
||||||
|
|
||||||
|
// Additional ports to be exposed on the GenericAPIServer service
|
||||||
|
// extraServicePorts is injectable in the event that more ports
|
||||||
|
// (other than the default 443/tcp) are exposed on the GenericAPIServer
|
||||||
|
// and those ports need to be load balanced by the GenericAPIServer
|
||||||
|
// service because this pkg is linked by out-of-tree projects
|
||||||
|
// like openshift which want to use the GenericAPIServer but also do
|
||||||
|
// more stuff.
|
||||||
|
ExtraServicePorts []api.ServicePort
|
||||||
|
// Additional ports to be exposed on the GenericAPIServer endpoints
|
||||||
|
// Port names should align with ports defined in ExtraServicePorts
|
||||||
|
ExtraEndpointPorts []api.EndpointPort
|
||||||
|
|
||||||
|
KubernetesServiceNodePort int
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenericAPIServer contains state for a Kubernetes cluster api server.
|
||||||
|
type GenericAPIServer struct {
|
||||||
|
// "Inputs", Copied from Config
|
||||||
|
ServiceClusterIPRange *net.IPNet
|
||||||
|
ServiceNodePortRange util.PortRange
|
||||||
|
cacheTimeout time.Duration
|
||||||
|
MinRequestTimeout time.Duration
|
||||||
|
|
||||||
|
mux apiserver.Mux
|
||||||
|
MuxHelper *apiserver.MuxHelper
|
||||||
|
HandlerContainer *restful.Container
|
||||||
|
RootWebService *restful.WebService
|
||||||
|
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
|
||||||
|
|
||||||
|
// External host is the name that should be used in external (public internet) URLs for this GenericAPIServer
|
||||||
|
externalHost string
|
||||||
|
// ClusterIP is the IP address of the GenericAPIServer within the cluster.
|
||||||
|
ClusterIP net.IP
|
||||||
|
PublicReadWritePort int
|
||||||
|
ServiceReadWriteIP net.IP
|
||||||
|
ServiceReadWritePort int
|
||||||
|
masterServices *util.Runner
|
||||||
|
ExtraServicePorts []api.ServicePort
|
||||||
|
ExtraEndpointPorts []api.EndpointPort
|
||||||
|
|
||||||
|
// storage contains the RESTful endpoints exposed by this GenericAPIServer
|
||||||
|
storage map[string]rest.Storage
|
||||||
|
|
||||||
|
// "Outputs"
|
||||||
|
Handler http.Handler
|
||||||
|
InsecureHandler http.Handler
|
||||||
|
|
||||||
|
// Used for custom proxy dialing, and proxy TLS options
|
||||||
|
ProxyTransport http.RoundTripper
|
||||||
|
|
||||||
|
KubernetesServiceNodePort int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GenericAPIServer) StorageDecorator() generic.StorageDecorator {
|
||||||
|
if s.enableWatchCache {
|
||||||
|
return genericetcd.StorageWithCacher
|
||||||
|
}
|
||||||
|
return generic.UndecoratedStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
// setDefaults fills in any fields not set that are required to have valid data.
|
||||||
|
func setDefaults(c *Config) {
|
||||||
|
if c.ServiceClusterIPRange == nil {
|
||||||
|
defaultNet := "10.0.0.0/24"
|
||||||
|
glog.Warningf("Network range for service cluster IPs is unspecified. Defaulting to %v.", defaultNet)
|
||||||
|
_, serviceClusterIPRange, err := net.ParseCIDR(defaultNet)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("Unable to parse CIDR: %v", err)
|
||||||
|
}
|
||||||
|
if size := ipallocator.RangeSize(serviceClusterIPRange); size < 8 {
|
||||||
|
glog.Fatalf("The service cluster IP range must be at least %d IP addresses", 8)
|
||||||
|
}
|
||||||
|
c.ServiceClusterIPRange = serviceClusterIPRange
|
||||||
|
}
|
||||||
|
if c.ServiceReadWriteIP == nil {
|
||||||
|
// Select the first valid IP from ServiceClusterIPRange to use as the GenericAPIServer service IP.
|
||||||
|
serviceReadWriteIP, err := ipallocator.GetIndexedIP(c.ServiceClusterIPRange, 1)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("Failed to generate service read-write IP for GenericAPIServer service: %v", err)
|
||||||
|
}
|
||||||
|
glog.V(4).Infof("Setting GenericAPIServer service IP to %q (read-write).", serviceReadWriteIP)
|
||||||
|
c.ServiceReadWriteIP = serviceReadWriteIP
|
||||||
|
}
|
||||||
|
if c.ServiceNodePortRange.Size == 0 {
|
||||||
|
// TODO: Currently no way to specify an empty range (do we need to allow this?)
|
||||||
|
// We should probably allow this for clouds that don't require NodePort to do load-balancing (GCE)
|
||||||
|
// but then that breaks the strict nestedness of ServiceType.
|
||||||
|
// Review post-v1
|
||||||
|
defaultServiceNodePortRange := util.PortRange{Base: 30000, Size: 2768}
|
||||||
|
c.ServiceNodePortRange = defaultServiceNodePortRange
|
||||||
|
glog.Infof("Node port range unspecified. Defaulting to %v.", c.ServiceNodePortRange)
|
||||||
|
}
|
||||||
|
if c.MasterCount == 0 {
|
||||||
|
// Clearly, there will be at least one GenericAPIServer.
|
||||||
|
c.MasterCount = 1
|
||||||
|
}
|
||||||
|
if c.ReadWritePort == 0 {
|
||||||
|
c.ReadWritePort = 6443
|
||||||
|
}
|
||||||
|
if c.CacheTimeout == 0 {
|
||||||
|
c.CacheTimeout = 5 * time.Second
|
||||||
|
}
|
||||||
|
if c.RequestContextMapper == nil {
|
||||||
|
c.RequestContextMapper = api.NewRequestContextMapper()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new instance of GenericAPIServer from the given config.
|
||||||
|
// Certain config fields will be set to a default value if unset,
|
||||||
|
// including:
|
||||||
|
// ServiceClusterIPRange
|
||||||
|
// ServiceNodePortRange
|
||||||
|
// MasterCount
|
||||||
|
// ReadWritePort
|
||||||
|
// PublicAddress
|
||||||
|
// Public fields:
|
||||||
|
// Handler -- The returned GenericAPIServer has a field TopHandler which is an
|
||||||
|
// http.Handler which handles all the endpoints provided by the GenericAPIServer,
|
||||||
|
// including the API, the UI, and miscellaneous debugging endpoints. All
|
||||||
|
// these are subject to authorization and authentication.
|
||||||
|
// InsecureHandler -- an http.Handler which handles all the same
|
||||||
|
// endpoints as Handler, but no authorization and authentication is done.
|
||||||
|
// Public methods:
|
||||||
|
// HandleWithAuth -- Allows caller to add an http.Handler for an endpoint
|
||||||
|
// that uses the same authentication and authorization (if any is configured)
|
||||||
|
// as the GenericAPIServer's built-in endpoints.
|
||||||
|
// If the caller wants to add additional endpoints not using the GenericAPIServer's
|
||||||
|
// auth, then the caller should create a handler for those endpoints, which delegates the
|
||||||
|
// any unhandled paths to "Handler".
|
||||||
|
func New(c *Config) *GenericAPIServer {
|
||||||
|
setDefaults(c)
|
||||||
|
|
||||||
|
s := &GenericAPIServer{
|
||||||
|
ServiceClusterIPRange: c.ServiceClusterIPRange,
|
||||||
|
ServiceNodePortRange: c.ServiceNodePortRange,
|
||||||
|
RootWebService: new(restful.WebService),
|
||||||
|
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,
|
||||||
|
|
||||||
|
cacheTimeout: c.CacheTimeout,
|
||||||
|
MinRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second,
|
||||||
|
|
||||||
|
MasterCount: c.MasterCount,
|
||||||
|
externalHost: c.ExternalHost,
|
||||||
|
ClusterIP: c.PublicAddress,
|
||||||
|
PublicReadWritePort: c.ReadWritePort,
|
||||||
|
ServiceReadWriteIP: c.ServiceReadWriteIP,
|
||||||
|
// TODO: ServiceReadWritePort should be passed in as an argument, it may not always be 443
|
||||||
|
ServiceReadWritePort: 443,
|
||||||
|
ExtraServicePorts: c.ExtraServicePorts,
|
||||||
|
ExtraEndpointPorts: c.ExtraEndpointPorts,
|
||||||
|
|
||||||
|
KubernetesServiceNodePort: c.KubernetesServiceNodePort,
|
||||||
|
}
|
||||||
|
|
||||||
|
var handlerContainer *restful.Container
|
||||||
|
if c.RestfulContainer != nil {
|
||||||
|
s.mux = c.RestfulContainer.ServeMux
|
||||||
|
handlerContainer = c.RestfulContainer
|
||||||
|
} else {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
s.mux = mux
|
||||||
|
handlerContainer = NewHandlerContainer(mux)
|
||||||
|
}
|
||||||
|
s.HandlerContainer = handlerContainer
|
||||||
|
// Use CurlyRouter to be able to use regular expressions in paths. Regular expressions are required in paths for example for proxy (where the path is proxy/{kind}/{name}/{*})
|
||||||
|
s.HandlerContainer.Router(restful.CurlyRouter{})
|
||||||
|
s.MuxHelper = &apiserver.MuxHelper{Mux: s.mux, RegisteredPaths: []string{}}
|
||||||
|
|
||||||
|
s.init(c)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GenericAPIServer) NewRequestInfoResolver() *apiserver.RequestInfoResolver {
|
||||||
|
return &apiserver.RequestInfoResolver{
|
||||||
|
sets.NewString(strings.Trim(s.ApiPrefix, "/"), strings.Trim(s.ApiGroupPrefix, "/")), // all possible API prefixes
|
||||||
|
sets.NewString(strings.Trim(s.ApiPrefix, "/")), // APIPrefixes that won't have groups (legacy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 GenericAPIServer's built-in endpoints.
|
||||||
|
func (s *GenericAPIServer) 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.
|
||||||
|
// TODO: convert to go-restful
|
||||||
|
s.MuxHelper.Handle(pattern, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 GenericAPIServer's built-in endpoints.
|
||||||
|
func (s *GenericAPIServer) HandleFuncWithAuth(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
||||||
|
// TODO: convert to go-restful
|
||||||
|
s.MuxHelper.HandleFunc(pattern, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandlerContainer(mux *http.ServeMux) *restful.Container {
|
||||||
|
container := restful.NewContainer()
|
||||||
|
container.ServeMux = mux
|
||||||
|
apiserver.InstallRecoverHandler(container)
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes GenericAPIServer.
|
||||||
|
func (s *GenericAPIServer) init(c *Config) {
|
||||||
|
|
||||||
|
if c.ProxyDialer != nil || c.ProxyTLSClientConfig != nil {
|
||||||
|
s.ProxyTransport = util.SetTransportDefaults(&http.Transport{
|
||||||
|
Dial: c.ProxyDialer,
|
||||||
|
TLSClientConfig: c.ProxyTLSClientConfig,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register root handler.
|
||||||
|
// We do not register this using restful Webservice since we do not want to surface this in api docs.
|
||||||
|
// Allow GenericAPIServer to be embedded in contexts which already have something registered at the root
|
||||||
|
if c.EnableIndex {
|
||||||
|
s.mux.HandleFunc("/", apiserver.IndexHandler(s.HandlerContainer, s.MuxHelper))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.EnableLogsSupport {
|
||||||
|
apiserver.InstallLogsSupport(s.MuxHelper)
|
||||||
|
}
|
||||||
|
if c.EnableUISupport {
|
||||||
|
ui.InstallSupport(s.MuxHelper, s.enableSwaggerSupport)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.EnableProfiling {
|
||||||
|
s.mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||||
|
s.mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||||
|
s.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := http.Handler(s.mux.(*http.ServeMux))
|
||||||
|
|
||||||
|
// TODO: handle CORS and auth using go-restful
|
||||||
|
// See github.com/emicklei/go-restful/blob/GenericAPIServer/examples/restful-CORS-filter.go, and
|
||||||
|
// github.com/emicklei/go-restful/blob/GenericAPIServer/examples/restful-basic-authentication.go
|
||||||
|
|
||||||
|
if len(c.CorsAllowedOriginList) > 0 {
|
||||||
|
allowedOriginRegexps, err := util.CompileRegexps(c.CorsAllowedOriginList)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v", strings.Join(c.CorsAllowedOriginList, ","), err)
|
||||||
|
}
|
||||||
|
handler = apiserver.CORS(handler, allowedOriginRegexps, nil, nil, "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.InsecureHandler = handler
|
||||||
|
|
||||||
|
attributeGetter := apiserver.NewRequestAttributeGetter(s.RequestContextMapper, s.NewRequestInfoResolver())
|
||||||
|
handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, s.authorizer)
|
||||||
|
|
||||||
|
// Install Authenticator
|
||||||
|
if c.Authenticator != nil {
|
||||||
|
authenticatedHandler, err := handlers.NewRequestAuthenticator(s.RequestContextMapper, c.Authenticator, handlers.Unauthorized(c.SupportsBasicAuth), handler)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("Could not initialize authenticator: %v", err)
|
||||||
|
}
|
||||||
|
handler = authenticatedHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make this optional? Consumers of GenericAPIServer depend on this currently.
|
||||||
|
s.Handler = handler
|
||||||
|
|
||||||
|
// After all wrapping is done, put a context filter around both handlers
|
||||||
|
if handler, err := api.NewRequestContextFilter(s.RequestContextMapper, s.Handler); err != nil {
|
||||||
|
glog.Fatalf("Could not initialize request context filter: %v", err)
|
||||||
|
} else {
|
||||||
|
s.Handler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
if handler, err := api.NewRequestContextFilter(s.RequestContextMapper, s.InsecureHandler); err != nil {
|
||||||
|
glog.Fatalf("Could not initialize request context filter: %v", err)
|
||||||
|
} else {
|
||||||
|
s.InsecureHandler = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallSwaggerAPI installs the /swaggerapi/ endpoint to allow schema discovery
|
||||||
|
// and traversal. It is optional to allow consumers of the Kubernetes GenericAPIServer 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 (s *GenericAPIServer) InstallSwaggerAPI() {
|
||||||
|
hostAndPort := s.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(s.externalHost) == 0 && s.ClusterIP != nil {
|
||||||
|
host := s.ClusterIP.String()
|
||||||
|
if s.PublicReadWritePort != 0 {
|
||||||
|
hostAndPort = net.JoinHostPort(host, strconv.Itoa(s.PublicReadWritePort))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
webServicesUrl := protocol + hostAndPort
|
||||||
|
|
||||||
|
// Enable swagger UI and discovery API
|
||||||
|
swaggerConfig := swagger.Config{
|
||||||
|
WebServicesUrl: webServicesUrl,
|
||||||
|
WebServices: s.HandlerContainer.RegisteredWebServices(),
|
||||||
|
ApiPath: "/swaggerapi/",
|
||||||
|
SwaggerPath: "/swaggerui/",
|
||||||
|
SwaggerFilePath: "/swagger-ui/",
|
||||||
|
}
|
||||||
|
swagger.RegisterSwaggerService(swaggerConfig, s.HandlerContainer)
|
||||||
|
}
|
154
pkg/genericapiserver/genericapiserver_test.go
Normal file
154
pkg/genericapiserver/genericapiserver_test.go
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package genericapiserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/apiserver"
|
||||||
|
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
|
||||||
|
"k8s.io/kubernetes/pkg/util"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setUp is a convience function for setting up for (most) tests.
|
||||||
|
func setUp(t *testing.T) (GenericAPIServer, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
|
||||||
|
etcdServer := etcdtesting.NewEtcdTestClientServer(t)
|
||||||
|
|
||||||
|
genericapiserver := GenericAPIServer{}
|
||||||
|
config := Config{}
|
||||||
|
config.PublicAddress = net.ParseIP("192.168.10.4")
|
||||||
|
|
||||||
|
return genericapiserver, etcdServer, config, assert.New(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNew verifies that the New function returns a GenericAPIServer
|
||||||
|
// using the configuration properly.
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
_, etcdserver, config, assert := setUp(t)
|
||||||
|
defer etcdserver.Terminate(t)
|
||||||
|
|
||||||
|
config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
|
||||||
|
config.ProxyTLSClientConfig = &tls.Config{}
|
||||||
|
|
||||||
|
s := New(&config)
|
||||||
|
|
||||||
|
// Verify many of the variables match their config counterparts
|
||||||
|
assert.Equal(s.enableLogsSupport, config.EnableLogsSupport)
|
||||||
|
assert.Equal(s.enableUISupport, config.EnableUISupport)
|
||||||
|
assert.Equal(s.enableSwaggerSupport, config.EnableSwaggerSupport)
|
||||||
|
assert.Equal(s.enableProfiling, config.EnableProfiling)
|
||||||
|
assert.Equal(s.ApiPrefix, config.APIPrefix)
|
||||||
|
assert.Equal(s.ApiGroupPrefix, config.APIGroupPrefix)
|
||||||
|
assert.Equal(s.corsAllowedOriginList, config.CorsAllowedOriginList)
|
||||||
|
assert.Equal(s.authenticator, config.Authenticator)
|
||||||
|
assert.Equal(s.authorizer, config.Authorizer)
|
||||||
|
assert.Equal(s.AdmissionControl, config.AdmissionControl)
|
||||||
|
assert.Equal(s.ApiGroupVersionOverrides, config.APIGroupVersionOverrides)
|
||||||
|
assert.Equal(s.RequestContextMapper, config.RequestContextMapper)
|
||||||
|
assert.Equal(s.cacheTimeout, config.CacheTimeout)
|
||||||
|
assert.Equal(s.externalHost, config.ExternalHost)
|
||||||
|
assert.Equal(s.ClusterIP, config.PublicAddress)
|
||||||
|
assert.Equal(s.PublicReadWritePort, config.ReadWritePort)
|
||||||
|
assert.Equal(s.ServiceReadWriteIP, config.ServiceReadWriteIP)
|
||||||
|
|
||||||
|
// These functions should point to the same memory location
|
||||||
|
serverDialer, _ := util.Dialer(s.ProxyTransport)
|
||||||
|
serverDialerFunc := fmt.Sprintf("%p", serverDialer)
|
||||||
|
configDialerFunc := fmt.Sprintf("%p", config.ProxyDialer)
|
||||||
|
assert.Equal(serverDialerFunc, configDialerFunc)
|
||||||
|
|
||||||
|
assert.Equal(s.ProxyTransport.(*http.Transport).TLSClientConfig, config.ProxyTLSClientConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNewHandlerContainer verifies that NewHandlerContainer uses the
|
||||||
|
// mux provided
|
||||||
|
func TestNewHandlerContainer(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
container := NewHandlerContainer(mux)
|
||||||
|
assert.Equal(mux, container.ServeMux, "ServerMux's do not match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHandleWithAuth verifies HandleWithAuth adds the path
|
||||||
|
// to the MuxHelper.RegisteredPaths.
|
||||||
|
func TestHandleWithAuth(t *testing.T) {
|
||||||
|
server, etcdserver, _, assert := setUp(t)
|
||||||
|
defer etcdserver.Terminate(t)
|
||||||
|
|
||||||
|
mh := apiserver.MuxHelper{Mux: http.NewServeMux()}
|
||||||
|
server.MuxHelper = &mh
|
||||||
|
handler := func(r http.ResponseWriter, w *http.Request) { w.Write(nil) }
|
||||||
|
server.HandleWithAuth("/test", http.HandlerFunc(handler))
|
||||||
|
|
||||||
|
assert.Contains(server.MuxHelper.RegisteredPaths, "/test", "Path not found in MuxHelper")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHandleFuncWithAuth verifies HandleFuncWithAuth adds the path
|
||||||
|
// to the MuxHelper.RegisteredPaths.
|
||||||
|
func TestHandleFuncWithAuth(t *testing.T) {
|
||||||
|
server, etcdserver, _, assert := setUp(t)
|
||||||
|
defer etcdserver.Terminate(t)
|
||||||
|
|
||||||
|
mh := apiserver.MuxHelper{Mux: http.NewServeMux()}
|
||||||
|
server.MuxHelper = &mh
|
||||||
|
handler := func(r http.ResponseWriter, w *http.Request) { w.Write(nil) }
|
||||||
|
server.HandleFuncWithAuth("/test", handler)
|
||||||
|
|
||||||
|
assert.Contains(server.MuxHelper.RegisteredPaths, "/test", "Path not found in MuxHelper")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestInstallSwaggerAPI verifies that the swagger api is added
|
||||||
|
// at the proper endpoint.
|
||||||
|
func TestInstallSwaggerAPI(t *testing.T) {
|
||||||
|
server, etcdserver, _, assert := setUp(t)
|
||||||
|
defer etcdserver.Terminate(t)
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
server.HandlerContainer = NewHandlerContainer(mux)
|
||||||
|
|
||||||
|
// Ensure swagger isn't installed without the call
|
||||||
|
ws := server.HandlerContainer.RegisteredWebServices()
|
||||||
|
if !assert.Equal(len(ws), 0) {
|
||||||
|
for x := range ws {
|
||||||
|
assert.NotEqual("/swaggerapi", ws[x].RootPath(), "SwaggerAPI was installed without a call to InstallSwaggerAPI()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install swagger and test
|
||||||
|
server.InstallSwaggerAPI()
|
||||||
|
ws = server.HandlerContainer.RegisteredWebServices()
|
||||||
|
if assert.NotEqual(0, len(ws), "SwaggerAPI not installed.") {
|
||||||
|
assert.Equal("/swaggerapi/", ws[0].RootPath(), "SwaggerAPI did not install to the proper path. %s != /swaggerapi", ws[0].RootPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty externalHost verification
|
||||||
|
mux = http.NewServeMux()
|
||||||
|
server.HandlerContainer = NewHandlerContainer(mux)
|
||||||
|
server.externalHost = ""
|
||||||
|
server.ClusterIP = net.IPv4(10, 10, 10, 10)
|
||||||
|
server.PublicReadWritePort = 1010
|
||||||
|
server.InstallSwaggerAPI()
|
||||||
|
if assert.NotEqual(0, len(ws), "SwaggerAPI not installed.") {
|
||||||
|
assert.Equal("/swaggerapi/", ws[0].RootPath(), "SwaggerAPI did not install to the proper path. %s != /swaggerapi", ws[0].RootPath())
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@ import (
|
|||||||
apiutil "k8s.io/kubernetes/pkg/api/util"
|
apiutil "k8s.io/kubernetes/pkg/api/util"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/apiserver"
|
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
"k8s.io/kubernetes/pkg/registry/endpoint"
|
"k8s.io/kubernetes/pkg/registry/endpoint"
|
||||||
"k8s.io/kubernetes/pkg/registry/namespace"
|
"k8s.io/kubernetes/pkg/registry/namespace"
|
||||||
@@ -59,14 +59,19 @@ import (
|
|||||||
func setUp(t *testing.T) (Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
|
func setUp(t *testing.T) (Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
|
||||||
server := etcdtesting.NewEtcdTestClientServer(t)
|
server := etcdtesting.NewEtcdTestClientServer(t)
|
||||||
|
|
||||||
master := Master{}
|
master := Master{
|
||||||
config := Config{}
|
GenericAPIServer: &genericapiserver.GenericAPIServer{},
|
||||||
|
}
|
||||||
|
config := Config{
|
||||||
|
Config: &genericapiserver.Config{},
|
||||||
|
}
|
||||||
storageVersions := make(map[string]string)
|
storageVersions := make(map[string]string)
|
||||||
storageDestinations := NewStorageDestinations()
|
storageDestinations := genericapiserver.NewStorageDestinations()
|
||||||
storageDestinations.AddAPIGroup(
|
storageDestinations.AddAPIGroup(
|
||||||
api.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()))
|
api.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()))
|
||||||
storageDestinations.AddAPIGroup(
|
storageDestinations.AddAPIGroup(
|
||||||
extensions.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix()))
|
extensions.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix()))
|
||||||
|
|
||||||
config.StorageDestinations = storageDestinations
|
config.StorageDestinations = storageDestinations
|
||||||
storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
|
storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
|
||||||
storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
|
storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
|
||||||
@@ -77,11 +82,8 @@ func setUp(t *testing.T) (Master, *etcdtesting.EtcdTestServer, Config, *assert.A
|
|||||||
return master, server, config, assert.New(t)
|
return master, server, config, assert.New(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNew verifies that the New function returns a Master
|
func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
|
||||||
// using the configuration properly.
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
_, etcdserver, config, assert := setUp(t)
|
_, etcdserver, config, assert := setUp(t)
|
||||||
defer etcdserver.Terminate(t)
|
|
||||||
|
|
||||||
config.KubeletClient = client.FakeKubeletClient{}
|
config.KubeletClient = client.FakeKubeletClient{}
|
||||||
|
|
||||||
@@ -89,37 +91,34 @@ func TestNew(t *testing.T) {
|
|||||||
config.ProxyTLSClientConfig = &tls.Config{}
|
config.ProxyTLSClientConfig = &tls.Config{}
|
||||||
|
|
||||||
master := New(&config)
|
master := New(&config)
|
||||||
|
return master, etcdserver, config, assert
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNew verifies that the New function returns a Master
|
||||||
|
// using the configuration properly.
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
master, etcdserver, config, assert := newMaster(t)
|
||||||
|
defer etcdserver.Terminate(t)
|
||||||
|
|
||||||
// Verify many of the variables match their config counterparts
|
// Verify many of the variables match their config counterparts
|
||||||
assert.Equal(master.enableCoreControllers, config.EnableCoreControllers)
|
assert.Equal(master.enableCoreControllers, config.EnableCoreControllers)
|
||||||
assert.Equal(master.enableLogsSupport, config.EnableLogsSupport)
|
|
||||||
assert.Equal(master.enableUISupport, config.EnableUISupport)
|
|
||||||
assert.Equal(master.enableSwaggerSupport, config.EnableSwaggerSupport)
|
|
||||||
assert.Equal(master.enableSwaggerSupport, config.EnableSwaggerSupport)
|
|
||||||
assert.Equal(master.enableProfiling, config.EnableProfiling)
|
|
||||||
assert.Equal(master.apiPrefix, config.APIPrefix)
|
|
||||||
assert.Equal(master.apiGroupPrefix, config.APIGroupPrefix)
|
|
||||||
assert.Equal(master.corsAllowedOriginList, config.CorsAllowedOriginList)
|
|
||||||
assert.Equal(master.authenticator, config.Authenticator)
|
|
||||||
assert.Equal(master.authorizer, config.Authorizer)
|
|
||||||
assert.Equal(master.admissionControl, config.AdmissionControl)
|
|
||||||
assert.Equal(master.apiGroupVersionOverrides, config.APIGroupVersionOverrides)
|
|
||||||
assert.Equal(master.requestContextMapper, config.RequestContextMapper)
|
|
||||||
assert.Equal(master.cacheTimeout, config.CacheTimeout)
|
|
||||||
assert.Equal(master.masterCount, config.MasterCount)
|
|
||||||
assert.Equal(master.externalHost, config.ExternalHost)
|
|
||||||
assert.Equal(master.clusterIP, config.PublicAddress)
|
|
||||||
assert.Equal(master.publicReadWritePort, config.ReadWritePort)
|
|
||||||
assert.Equal(master.serviceReadWriteIP, config.ServiceReadWriteIP)
|
|
||||||
assert.Equal(master.tunneler, config.Tunneler)
|
assert.Equal(master.tunneler, config.Tunneler)
|
||||||
|
assert.Equal(master.ApiPrefix, config.APIPrefix)
|
||||||
|
assert.Equal(master.ApiGroupPrefix, config.APIGroupPrefix)
|
||||||
|
assert.Equal(master.ApiGroupVersionOverrides, config.APIGroupVersionOverrides)
|
||||||
|
assert.Equal(master.RequestContextMapper, config.RequestContextMapper)
|
||||||
|
assert.Equal(master.MasterCount, config.MasterCount)
|
||||||
|
assert.Equal(master.ClusterIP, config.PublicAddress)
|
||||||
|
assert.Equal(master.PublicReadWritePort, config.ReadWritePort)
|
||||||
|
assert.Equal(master.ServiceReadWriteIP, config.ServiceReadWriteIP)
|
||||||
|
|
||||||
// These functions should point to the same memory location
|
// These functions should point to the same memory location
|
||||||
masterDialer, _ := util.Dialer(master.proxyTransport)
|
masterDialer, _ := util.Dialer(master.ProxyTransport)
|
||||||
masterDialerFunc := fmt.Sprintf("%p", masterDialer)
|
masterDialerFunc := fmt.Sprintf("%p", masterDialer)
|
||||||
configDialerFunc := fmt.Sprintf("%p", config.ProxyDialer)
|
configDialerFunc := fmt.Sprintf("%p", config.ProxyDialer)
|
||||||
assert.Equal(masterDialerFunc, configDialerFunc)
|
assert.Equal(masterDialerFunc, configDialerFunc)
|
||||||
|
|
||||||
assert.Equal(master.proxyTransport.(*http.Transport).TLSClientConfig, config.ProxyTLSClientConfig)
|
assert.Equal(master.ProxyTransport.(*http.Transport).TLSClientConfig, config.ProxyTLSClientConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGetServersToValidate verifies the unexported getServersToValidate function
|
// TestGetServersToValidate verifies the unexported getServersToValidate function
|
||||||
@@ -165,14 +164,29 @@ func TestFindExternalAddress(t *testing.T) {
|
|||||||
// TestApi_v1 verifies that the unexported api_v1 function does indeed
|
// TestApi_v1 verifies that the unexported api_v1 function does indeed
|
||||||
// utilize the correct Version and Codec.
|
// utilize the correct Version and Codec.
|
||||||
func TestApi_v1(t *testing.T) {
|
func TestApi_v1(t *testing.T) {
|
||||||
master, etcdserver, _, assert := setUp(t)
|
_, etcdserver, config, assert := setUp(t)
|
||||||
defer etcdserver.Terminate(t)
|
defer etcdserver.Terminate(t)
|
||||||
|
|
||||||
version := master.api_v1()
|
// config.KubeletClient = client.FakeKubeletClient{}
|
||||||
|
|
||||||
|
config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
|
||||||
|
config.ProxyTLSClientConfig = &tls.Config{}
|
||||||
|
|
||||||
|
s := genericapiserver.New(config.Config)
|
||||||
|
master := &Master{
|
||||||
|
GenericAPIServer: s,
|
||||||
|
tunneler: config.Tunneler,
|
||||||
|
}
|
||||||
|
|
||||||
|
version := master.api_v1(&config)
|
||||||
assert.Equal(unversioned.GroupVersion{Version: "v1"}, version.GroupVersion, "Version was not v1: %s", version.GroupVersion)
|
assert.Equal(unversioned.GroupVersion{Version: "v1"}, version.GroupVersion, "Version was not v1: %s", version.GroupVersion)
|
||||||
assert.Equal(v1.Codec, version.Codec, "version.Codec was not for v1: %s", version.Codec)
|
assert.Equal(v1.Codec, version.Codec, "version.Codec was not for v1: %s", version.Codec)
|
||||||
for k, v := range master.storage {
|
// Verify that version storage has all the resources.
|
||||||
assert.Contains(version.Storage, v, "Value %s not found (key: %s)", k, v)
|
for k, v := range master.v1ResourcesStorage {
|
||||||
|
k = strings.ToLower(k)
|
||||||
|
val, ok := version.Storage[k]
|
||||||
|
assert.True(ok, "ok: %s", ok)
|
||||||
|
assert.Equal(val, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,10 +202,10 @@ func TestNewBootstrapController(t *testing.T) {
|
|||||||
master.serviceRegistry = registrytest.NewServiceRegistry()
|
master.serviceRegistry = registrytest.NewServiceRegistry()
|
||||||
master.endpointRegistry = endpoint.NewRegistry(nil)
|
master.endpointRegistry = endpoint.NewRegistry(nil)
|
||||||
|
|
||||||
master.serviceNodePortRange = portRange
|
master.ServiceNodePortRange = portRange
|
||||||
master.masterCount = 1
|
master.MasterCount = 1
|
||||||
master.serviceReadWritePort = 1000
|
master.ServiceReadWritePort = 1000
|
||||||
master.publicReadWritePort = 1010
|
master.PublicReadWritePort = 1010
|
||||||
|
|
||||||
controller := master.NewBootstrapController()
|
controller := master.NewBootstrapController()
|
||||||
|
|
||||||
@@ -199,9 +213,9 @@ func TestNewBootstrapController(t *testing.T) {
|
|||||||
assert.Equal(controller.EndpointRegistry, master.endpointRegistry)
|
assert.Equal(controller.EndpointRegistry, master.endpointRegistry)
|
||||||
assert.Equal(controller.ServiceRegistry, master.serviceRegistry)
|
assert.Equal(controller.ServiceRegistry, master.serviceRegistry)
|
||||||
assert.Equal(controller.ServiceNodePortRange, portRange)
|
assert.Equal(controller.ServiceNodePortRange, portRange)
|
||||||
assert.Equal(controller.MasterCount, master.masterCount)
|
assert.Equal(controller.MasterCount, master.MasterCount)
|
||||||
assert.Equal(controller.ServicePort, master.serviceReadWritePort)
|
assert.Equal(controller.ServicePort, master.ServiceReadWritePort)
|
||||||
assert.Equal(controller.PublicServicePort, master.publicReadWritePort)
|
assert.Equal(controller.PublicServicePort, master.PublicReadWritePort)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestControllerServicePorts verifies master extraServicePorts are
|
// TestControllerServicePorts verifies master extraServicePorts are
|
||||||
@@ -214,7 +228,7 @@ func TestControllerServicePorts(t *testing.T) {
|
|||||||
master.serviceRegistry = registrytest.NewServiceRegistry()
|
master.serviceRegistry = registrytest.NewServiceRegistry()
|
||||||
master.endpointRegistry = endpoint.NewRegistry(nil)
|
master.endpointRegistry = endpoint.NewRegistry(nil)
|
||||||
|
|
||||||
master.extraServicePorts = []api.ServicePort{
|
master.ExtraServicePorts = []api.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "additional-port-1",
|
Name: "additional-port-1",
|
||||||
Port: 1000,
|
Port: 1000,
|
||||||
@@ -235,79 +249,6 @@ func TestControllerServicePorts(t *testing.T) {
|
|||||||
assert.Equal(1010, controller.ExtraServicePorts[1].Port)
|
assert.Equal(1010, controller.ExtraServicePorts[1].Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNewHandlerContainer verifies that NewHandlerContainer uses the
|
|
||||||
// mux provided
|
|
||||||
func TestNewHandlerContainer(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
container := NewHandlerContainer(mux)
|
|
||||||
assert.Equal(mux, container.ServeMux, "ServerMux's do not match")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHandleWithAuth verifies HandleWithAuth adds the path
|
|
||||||
// to the muxHelper.RegisteredPaths.
|
|
||||||
func TestHandleWithAuth(t *testing.T) {
|
|
||||||
master, etcdserver, _, assert := setUp(t)
|
|
||||||
defer etcdserver.Terminate(t)
|
|
||||||
|
|
||||||
mh := apiserver.MuxHelper{Mux: http.NewServeMux()}
|
|
||||||
master.muxHelper = &mh
|
|
||||||
handler := func(r http.ResponseWriter, w *http.Request) { w.Write(nil) }
|
|
||||||
master.HandleWithAuth("/test", http.HandlerFunc(handler))
|
|
||||||
|
|
||||||
assert.Contains(master.muxHelper.RegisteredPaths, "/test", "Path not found in muxHelper")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHandleFuncWithAuth verifies HandleFuncWithAuth adds the path
|
|
||||||
// to the muxHelper.RegisteredPaths.
|
|
||||||
func TestHandleFuncWithAuth(t *testing.T) {
|
|
||||||
master, etcdserver, _, assert := setUp(t)
|
|
||||||
defer etcdserver.Terminate(t)
|
|
||||||
|
|
||||||
mh := apiserver.MuxHelper{Mux: http.NewServeMux()}
|
|
||||||
master.muxHelper = &mh
|
|
||||||
handler := func(r http.ResponseWriter, w *http.Request) { w.Write(nil) }
|
|
||||||
master.HandleFuncWithAuth("/test", handler)
|
|
||||||
|
|
||||||
assert.Contains(master.muxHelper.RegisteredPaths, "/test", "Path not found in muxHelper")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestInstallSwaggerAPI verifies that the swagger api is added
|
|
||||||
// at the proper endpoint.
|
|
||||||
func TestInstallSwaggerAPI(t *testing.T) {
|
|
||||||
master, etcdserver, _, assert := setUp(t)
|
|
||||||
defer etcdserver.Terminate(t)
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
master.handlerContainer = NewHandlerContainer(mux)
|
|
||||||
|
|
||||||
// Ensure swagger isn't installed without the call
|
|
||||||
ws := master.handlerContainer.RegisteredWebServices()
|
|
||||||
if !assert.Equal(len(ws), 0) {
|
|
||||||
for x := range ws {
|
|
||||||
assert.NotEqual("/swaggerapi", ws[x].RootPath(), "SwaggerAPI was installed without a call to InstallSwaggerAPI()")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install swagger and test
|
|
||||||
master.InstallSwaggerAPI()
|
|
||||||
ws = master.handlerContainer.RegisteredWebServices()
|
|
||||||
if assert.NotEqual(0, len(ws), "SwaggerAPI not installed.") {
|
|
||||||
assert.Equal("/swaggerapi/", ws[0].RootPath(), "SwaggerAPI did not install to the proper path. %s != /swaggerapi", ws[0].RootPath())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty externalHost verification
|
|
||||||
mux = http.NewServeMux()
|
|
||||||
master.handlerContainer = NewHandlerContainer(mux)
|
|
||||||
master.externalHost = ""
|
|
||||||
master.clusterIP = net.IPv4(10, 10, 10, 10)
|
|
||||||
master.publicReadWritePort = 1010
|
|
||||||
master.InstallSwaggerAPI()
|
|
||||||
if assert.NotEqual(0, len(ws), "SwaggerAPI not installed.") {
|
|
||||||
assert.Equal("/swaggerapi/", ws[0].RootPath(), "SwaggerAPI did not install to the proper path. %s != /swaggerapi", ws[0].RootPath())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDefaultAPIGroupVersion verifies that the unexported defaultAPIGroupVersion
|
// TestDefaultAPIGroupVersion verifies that the unexported defaultAPIGroupVersion
|
||||||
// creates the expected APIGroupVersion based off of master.
|
// creates the expected APIGroupVersion based off of master.
|
||||||
func TestDefaultAPIGroupVersion(t *testing.T) {
|
func TestDefaultAPIGroupVersion(t *testing.T) {
|
||||||
@@ -316,10 +257,10 @@ func TestDefaultAPIGroupVersion(t *testing.T) {
|
|||||||
|
|
||||||
apiGroup := master.defaultAPIGroupVersion()
|
apiGroup := master.defaultAPIGroupVersion()
|
||||||
|
|
||||||
assert.Equal(apiGroup.Root, master.apiPrefix)
|
assert.Equal(apiGroup.Root, master.ApiPrefix)
|
||||||
assert.Equal(apiGroup.Admit, master.admissionControl)
|
assert.Equal(apiGroup.Admit, master.AdmissionControl)
|
||||||
assert.Equal(apiGroup.Context, master.requestContextMapper)
|
assert.Equal(apiGroup.Context, master.RequestContextMapper)
|
||||||
assert.Equal(apiGroup.MinRequestTimeout, master.minRequestTimeout)
|
assert.Equal(apiGroup.MinRequestTimeout, master.MinRequestTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestExpapi verifies that the unexported exapi creates
|
// TestExpapi verifies that the unexported exapi creates
|
||||||
@@ -331,7 +272,7 @@ func TestExpapi(t *testing.T) {
|
|||||||
extensionsGroupMeta := latest.GroupOrDie(extensions.GroupName)
|
extensionsGroupMeta := latest.GroupOrDie(extensions.GroupName)
|
||||||
|
|
||||||
expAPIGroup := master.experimental(&config)
|
expAPIGroup := master.experimental(&config)
|
||||||
assert.Equal(expAPIGroup.Root, master.apiGroupPrefix)
|
assert.Equal(expAPIGroup.Root, master.ApiGroupPrefix)
|
||||||
assert.Equal(expAPIGroup.Mapper, extensionsGroupMeta.RESTMapper)
|
assert.Equal(expAPIGroup.Mapper, extensionsGroupMeta.RESTMapper)
|
||||||
assert.Equal(expAPIGroup.Codec, extensionsGroupMeta.Codec)
|
assert.Equal(expAPIGroup.Codec, extensionsGroupMeta.Codec)
|
||||||
assert.Equal(expAPIGroup.Linker, extensionsGroupMeta.SelfLinker)
|
assert.Equal(expAPIGroup.Linker, extensionsGroupMeta.SelfLinker)
|
||||||
@@ -371,31 +312,10 @@ func TestGetNodeAddresses(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDiscoveryAtAPIS(t *testing.T) {
|
func TestDiscoveryAtAPIS(t *testing.T) {
|
||||||
master, etcdserver, config, assert := setUp(t)
|
master, etcdserver, config, assert := newMaster(t)
|
||||||
defer etcdserver.Terminate(t)
|
defer etcdserver.Terminate(t)
|
||||||
|
|
||||||
// ================= preparation for master.init() ======================
|
server := httptest.NewServer(master.HandlerContainer.ServeMux)
|
||||||
portRange := util.PortRange{Base: 10, Size: 10}
|
|
||||||
master.serviceNodePortRange = portRange
|
|
||||||
|
|
||||||
_, ipnet, err := net.ParseCIDR("192.168.1.1/24")
|
|
||||||
if !assert.NoError(err) {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
master.serviceClusterIPRange = ipnet
|
|
||||||
|
|
||||||
mh := apiserver.MuxHelper{Mux: http.NewServeMux()}
|
|
||||||
master.muxHelper = &mh
|
|
||||||
master.rootWebService = new(restful.WebService)
|
|
||||||
|
|
||||||
master.handlerContainer = restful.NewContainer()
|
|
||||||
|
|
||||||
master.mux = http.NewServeMux()
|
|
||||||
master.requestContextMapper = api.NewRequestContextMapper()
|
|
||||||
// ======================= end of preparation ===========================
|
|
||||||
|
|
||||||
master.init(&config)
|
|
||||||
server := httptest.NewServer(master.handlerContainer.ServeMux)
|
|
||||||
resp, err := http.Get(server.URL + "/apis")
|
resp, err := http.Get(server.URL + "/apis")
|
||||||
if !assert.NoError(err) {
|
if !assert.NoError(err) {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
@@ -457,14 +377,14 @@ func initThirdParty(t *testing.T, version string) (*Master, *etcdtesting.EtcdTes
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
master.handlerContainer = restful.NewContainer()
|
master.HandlerContainer = restful.NewContainer()
|
||||||
master.thirdPartyStorage = etcdstorage.NewEtcdStorage(etcdserver.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix())
|
master.thirdPartyStorage = etcdstorage.NewEtcdStorage(etcdserver.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix())
|
||||||
|
|
||||||
if !assert.NoError(master.InstallThirdPartyResource(api)) {
|
if !assert.NoError(master.InstallThirdPartyResource(api)) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
server := httptest.NewServer(master.handlerContainer.ServeMux)
|
server := httptest.NewServer(master.HandlerContainer.ServeMux)
|
||||||
return &master, etcdserver, server, assert
|
return &master, etcdserver, server, assert
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,7 +803,7 @@ func testInstallThirdPartyResourceRemove(t *testing.T, version string) {
|
|||||||
if len(installed) != 0 {
|
if len(installed) != 0 {
|
||||||
t.Errorf("Resource(s) still installed: %v", installed)
|
t.Errorf("Resource(s) still installed: %v", installed)
|
||||||
}
|
}
|
||||||
services := master.handlerContainer.RegisteredWebServices()
|
services := master.HandlerContainer.RegisteredWebServices()
|
||||||
for ix := range services {
|
for ix := range services {
|
||||||
if strings.HasPrefix(services[ix].RootPath(), "/apis/company.com") {
|
if strings.HasPrefix(services[ix].RootPath(), "/apis/company.com") {
|
||||||
t.Errorf("Web service still installed at %s: %#v", services[ix].RootPath(), services[ix])
|
t.Errorf("Web service still installed at %s: %#v", services[ix].RootPath(), services[ix])
|
||||||
|
@@ -31,6 +31,8 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type InstallSSHKey func(user string, data []byte) error
|
||||||
|
|
||||||
type AddressFunc func() (addresses []string, err error)
|
type AddressFunc func() (addresses []string, err error)
|
||||||
|
|
||||||
type Tunneler interface {
|
type Tunneler interface {
|
||||||
|
@@ -36,6 +36,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
replicationcontroller "k8s.io/kubernetes/pkg/controller/replication"
|
replicationcontroller "k8s.io/kubernetes/pkg/controller/replication"
|
||||||
"k8s.io/kubernetes/pkg/fields"
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||||
"k8s.io/kubernetes/pkg/kubectl"
|
"k8s.io/kubernetes/pkg/kubectl"
|
||||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
"k8s.io/kubernetes/pkg/master"
|
"k8s.io/kubernetes/pkg/master"
|
||||||
@@ -138,22 +139,26 @@ func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Se
|
|||||||
func NewMasterConfig() *master.Config {
|
func NewMasterConfig() *master.Config {
|
||||||
etcdClient := NewEtcdClient()
|
etcdClient := NewEtcdClient()
|
||||||
storageVersions := make(map[string]string)
|
storageVersions := make(map[string]string)
|
||||||
|
|
||||||
etcdStorage := etcdstorage.NewEtcdStorage(etcdClient, testapi.Default.Codec(), etcdtest.PathPrefix())
|
etcdStorage := etcdstorage.NewEtcdStorage(etcdClient, testapi.Default.Codec(), etcdtest.PathPrefix())
|
||||||
storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
|
storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
|
||||||
expEtcdStorage := NewExtensionsEtcdStorage(etcdClient)
|
expEtcdStorage := NewExtensionsEtcdStorage(etcdClient)
|
||||||
storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
|
storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
|
||||||
storageDestinations := master.NewStorageDestinations()
|
|
||||||
|
storageDestinations := genericapiserver.NewStorageDestinations()
|
||||||
storageDestinations.AddAPIGroup(api.GroupName, etcdStorage)
|
storageDestinations.AddAPIGroup(api.GroupName, etcdStorage)
|
||||||
storageDestinations.AddAPIGroup(extensions.GroupName, expEtcdStorage)
|
storageDestinations.AddAPIGroup(extensions.GroupName, expEtcdStorage)
|
||||||
|
|
||||||
return &master.Config{
|
return &master.Config{
|
||||||
StorageDestinations: storageDestinations,
|
Config: &genericapiserver.Config{
|
||||||
StorageVersions: storageVersions,
|
StorageDestinations: storageDestinations,
|
||||||
KubeletClient: kubeletclient.FakeKubeletClient{},
|
StorageVersions: storageVersions,
|
||||||
APIPrefix: "/api",
|
APIPrefix: "/api",
|
||||||
APIGroupPrefix: "/apis",
|
APIGroupPrefix: "/apis",
|
||||||
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
|
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
|
||||||
AdmissionControl: admit.NewAlwaysAdmit(),
|
AdmissionControl: admit.NewAlwaysAdmit(),
|
||||||
|
},
|
||||||
|
KubeletClient: kubeletclient.FakeKubeletClient{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@ package integration
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||||
"k8s.io/kubernetes/pkg/master"
|
"k8s.io/kubernetes/pkg/master"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,10 +27,15 @@ import (
|
|||||||
// are not referenced directly by a master.
|
// are not referenced directly by a master.
|
||||||
func TestMasterExportsSymbols(t *testing.T) {
|
func TestMasterExportsSymbols(t *testing.T) {
|
||||||
_ = &master.Config{
|
_ = &master.Config{
|
||||||
|
Config: &genericapiserver.Config{
|
||||||
|
EnableUISupport: false,
|
||||||
|
EnableSwaggerSupport: false,
|
||||||
|
RestfulContainer: nil,
|
||||||
|
},
|
||||||
EnableCoreControllers: false,
|
EnableCoreControllers: false,
|
||||||
EnableUISupport: false,
|
|
||||||
EnableSwaggerSupport: false,
|
|
||||||
RestfulContainer: nil,
|
|
||||||
}
|
}
|
||||||
_ = (&master.Master{}).NewBootstrapController()
|
m := &master.Master{
|
||||||
|
GenericAPIServer: &genericapiserver.GenericAPIServer{},
|
||||||
|
}
|
||||||
|
_ = (m).NewBootstrapController()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user