Merge pull request #12001 from uluyol/os-exp-hack

Add (stopgap) support for an experimental API prefix.
This commit is contained in:
Mike Danese 2015-07-31 10:15:06 -07:00
commit e58ea24d0b
31 changed files with 529 additions and 110 deletions

View File

@ -42,6 +42,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/nodecontroller"
replicationControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/controller/replication"
explatest "github.com/GoogleCloudPlatform/kubernetes/pkg/expapi/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/cadvisor"
@ -132,10 +133,14 @@ func startComponents(firstManifestURL, secondManifestURL, apiVersion string) (st
cl := client.NewOrDie(&client.Config{Host: apiServer.URL, Version: apiVersion})
etcdStorage, err := master.NewEtcdStorage(etcdClient, "", etcdtest.PathPrefix())
etcdStorage, err := master.NewEtcdStorage(etcdClient, latest.InterfacesFor, latest.Version, etcdtest.PathPrefix())
if err != nil {
glog.Fatalf("Unable to get etcd storage: %v", err)
}
expEtcdStorage, err := master.NewEtcdStorage(etcdClient, explatest.InterfacesFor, explatest.Version, etcdtest.PathPrefix())
if err != nil {
glog.Fatalf("Unable to get etcd storage for experimental: %v", err)
}
// Master
host, port, err := net.SplitHostPort(strings.TrimLeft(apiServer.URL, "http://"))
@ -155,11 +160,13 @@ func startComponents(firstManifestURL, secondManifestURL, apiVersion string) (st
// Create a master and install handlers into mux.
m := master.New(&master.Config{
DatabaseStorage: etcdStorage,
ExpDatabaseStorage: expEtcdStorage,
KubeletClient: fakeKubeletClient{},
EnableCoreControllers: true,
EnableLogsSupport: false,
EnableProfiling: true,
APIPrefix: "/api",
ExpAPIPrefix: "/experimental",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
ReadWritePort: portNumber,

View File

@ -32,10 +32,13 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
explatest "github.com/GoogleCloudPlatform/kubernetes/pkg/expapi/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
"github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports"
"github.com/GoogleCloudPlatform/kubernetes/pkg/storage"
@ -71,7 +74,9 @@ type APIServer struct {
TLSPrivateKeyFile string
CertDirectory string
APIPrefix string
ExpAPIPrefix string
StorageVersion string
ExpStorageVersion string
CloudProvider string
CloudConfigFile string
EventTTL time.Duration
@ -115,6 +120,7 @@ func NewAPIServer() *APIServer {
APIRate: 10.0,
APIBurst: 200,
APIPrefix: "/api",
ExpAPIPrefix: "/experimental",
EventTTL: 1 * time.Hour,
AuthorizationMode: "AlwaysAllow",
AdmissionControl: "AlwaysAdmit",
@ -172,6 +178,7 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.CertDirectory, "cert-dir", s.CertDirectory, "The directory where the TLS certs are located (by default /var/run/kubernetes). "+
"If --tls-cert-file and --tls-private-key-file are provided, this flag will be ignored.")
fs.StringVar(&s.APIPrefix, "api-prefix", s.APIPrefix, "The prefix for API requests on the server. Default '/api'.")
fs.StringVar(&s.ExpAPIPrefix, "experimental-prefix", s.ExpAPIPrefix, "The prefix for experimental API requests on the server. Default '/experimental'.")
fs.StringVar(&s.StorageVersion, "storage-version", s.StorageVersion, "The version to store resources with. Defaults to server preferred")
fs.StringVar(&s.CloudProvider, "cloud-provider", s.CloudProvider, "The provider for cloud services. Empty string for no provider.")
fs.StringVar(&s.CloudConfigFile, "cloud-config", s.CloudConfigFile, "The path to the cloud provider configuration file. Empty string for no configuration file.")
@ -217,7 +224,7 @@ func (s *APIServer) verifyClusterIPFlags() {
}
}
func newEtcd(etcdConfigFile string, etcdServerList util.StringList, storageVersion string, pathPrefix string) (etcdStorage storage.Interface, err error) {
func newEtcd(etcdConfigFile string, etcdServerList util.StringList, interfacesFunc meta.VersionInterfacesFunc, defaultVersion, storageVersion, pathPrefix string) (etcdStorage storage.Interface, err error) {
var client tools.EtcdClient
if etcdConfigFile != "" {
client, err = etcd.NewClientFromFile(etcdConfigFile)
@ -237,7 +244,10 @@ func newEtcd(etcdConfigFile string, etcdServerList util.StringList, storageVersi
client = etcdClient
}
return master.NewEtcdStorage(client, storageVersion, pathPrefix)
if storageVersion == "" {
storageVersion = defaultVersion
}
return master.NewEtcdStorage(client, interfacesFunc, storageVersion, pathPrefix)
}
// Run runs the specified APIServer. This should never exit.
@ -292,6 +302,10 @@ func (s *APIServer) Run(_ []string) error {
disableV1 := disableAllAPIs
disableV1 = !s.getRuntimeConfigValue("api/v1", !disableV1)
// "experimental/v1={true|false} allows users to enable/disable the experimental API.
// This takes preference over api/all, if specified.
enableExp := s.getRuntimeConfigValue("experimental/v1", false)
// TODO: expose same flags as client.BindClientConfigFlags but for a server
clientConfig := &client.Config{
Host: net.JoinHostPort(s.InsecureBindAddress.String(), strconv.Itoa(s.InsecurePort)),
@ -302,10 +316,14 @@ func (s *APIServer) Run(_ []string) error {
glog.Fatalf("Invalid server address: %v", err)
}
etcdStorage, err := newEtcd(s.EtcdConfigFile, s.EtcdServerList, s.StorageVersion, s.EtcdPathPrefix)
etcdStorage, err := newEtcd(s.EtcdConfigFile, s.EtcdServerList, latest.InterfacesFor, latest.Version, s.StorageVersion, s.EtcdPathPrefix)
if err != nil {
glog.Fatalf("Invalid storage version or misconfigured etcd: %v", err)
}
expEtcdStorage, err := newEtcd(s.EtcdConfigFile, s.EtcdServerList, explatest.InterfacesFor, explatest.Version, s.ExpStorageVersion, s.EtcdPathPrefix)
if err != nil {
glog.Fatalf("Invalid experimental storage version or misconfigured etcd: %v", err)
}
n := net.IPNet(s.ServiceClusterIPRange)
@ -360,7 +378,9 @@ func (s *APIServer) Run(_ []string) error {
}
}
config := &master.Config{
DatabaseStorage: etcdStorage,
DatabaseStorage: etcdStorage,
ExpDatabaseStorage: expEtcdStorage,
EventTTL: s.EventTTL,
KubeletClient: kubeletClient,
ServiceClusterIPRange: &n,
@ -371,6 +391,7 @@ func (s *APIServer) Run(_ []string) error {
EnableProfiling: s.EnableProfiling,
EnableIndex: true,
APIPrefix: s.APIPrefix,
ExpAPIPrefix: s.ExpAPIPrefix,
CorsAllowedOriginList: s.CorsAllowedOriginList,
ReadWritePort: s.SecurePort,
PublicAddress: net.IP(s.AdvertiseAddress),
@ -379,6 +400,7 @@ func (s *APIServer) Run(_ []string) error {
Authorizer: authorizer,
AdmissionControl: admissionController,
DisableV1: disableV1,
EnableExp: enableExp,
MasterServiceNamespace: s.MasterServiceNamespace,
ClusterName: s.ClusterName,
ExternalHost: s.ExternalHost,

View File

@ -30,12 +30,14 @@ import (
kubeletapp "github.com/GoogleCloudPlatform/kubernetes/cmd/kubelet/app"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/nodecontroller"
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/servicecontroller"
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller/replication"
explatest "github.com/GoogleCloudPlatform/kubernetes/pkg/expapi/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/cadvisor"
kubecontainer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/container"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
@ -79,14 +81,19 @@ func (h *delegateHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func runApiServer(etcdClient tools.EtcdClient, addr net.IP, port int, masterServiceNamespace string) {
handler := delegateHandler{}
etcdStorage, err := master.NewEtcdStorage(etcdClient, "", master.DefaultEtcdPathPrefix)
etcdStorage, err := master.NewEtcdStorage(etcdClient, latest.InterfacesFor, latest.Version, master.DefaultEtcdPathPrefix)
if err != nil {
glog.Fatalf("Unable to get etcd storage: %v", err)
}
expEtcdStorage, err := master.NewEtcdStorage(etcdClient, explatest.InterfacesFor, explatest.Version, master.DefaultEtcdPathPrefix)
if err != nil {
glog.Fatalf("Unable to get etcd storage for experimental: %v", err)
}
// Create a master and install handlers into mux.
m := master.New(&master.Config{
DatabaseStorage: etcdStorage,
DatabaseStorage: etcdStorage,
ExpDatabaseStorage: expEtcdStorage,
KubeletClient: &client.HTTPKubeletClient{
Client: http.DefaultClient,
Config: &client.KubeletConfig{Port: 10250},
@ -96,6 +103,7 @@ func runApiServer(etcdClient tools.EtcdClient, addr net.IP, port int, masterServ
EnableSwaggerSupport: true,
EnableProfiling: *enableProfiling,
APIPrefix: "/api",
ExpAPIPrefix: "/experimental",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
ReadWritePort: port,

View File

@ -63,6 +63,8 @@ var RESTMapper meta.RESTMapper
// userResources is a group of resources mostly used by a kubectl user
var userResources = []string{"rc", "svc", "pods", "pvc"}
const importPrefix = "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
func init() {
// Use the first API version in the list of registered versions as the latest.
Version = registered.RegisteredVersions[0]
@ -75,28 +77,14 @@ func init() {
Versions = append(Versions, versions[i])
}
mapper := meta.NewDefaultRESTMapper(
versions,
func(version string) (*meta.VersionInterfaces, bool) {
interfaces, err := InterfacesFor(version)
if err != nil {
return nil, false
}
return interfaces, true
},
)
// the list of kinds that are scoped at the root of the api hierarchy
// if a kind is not enumerated here, it is assumed to have a namespace scope
kindToRootScope := map[string]bool{
"Node": true,
"Minion": true,
"Namespace": true,
"PersistentVolume": true,
}
// setup aliases for groups of resources
mapper.AddResourceAlias("all", userResources...)
rootScoped := util.NewStringSet(
"Node",
"Minion",
"Namespace",
"PersistentVolume",
)
// these kinds should be excluded from the list of resources
ignoredKinds := util.NewStringSet(
@ -107,20 +95,11 @@ func init() {
"PodExecOptions",
"PodProxyOptions")
// enumerate all supported versions, get the kinds, and register with the mapper how to address our resources.
for _, version := range versions {
for kind := range api.Scheme.KnownTypes(version) {
if ignoredKinds.Has(kind) {
continue
}
scope := meta.RESTScopeNamespace
if kindToRootScope[kind] {
scope = meta.RESTScopeRoot
}
mapper.Add(scope, kind, version, false)
}
}
mapper := api.NewDefaultRESTMapper(versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
// setup aliases for groups of resources
mapper.AddResourceAlias("all", userResources...)
RESTMapper = mapper
api.RegisterRESTMapper(RESTMapper)
}
// InterfacesFor returns the default Codec and ResourceVersioner for a given version

57
pkg/api/mapper.go Normal file
View File

@ -0,0 +1,57 @@
/*
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 api
import (
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
var RESTMapper meta.RESTMapper
func init() {
RESTMapper = meta.MultiRESTMapper{}
}
func RegisterRESTMapper(m meta.RESTMapper) {
RESTMapper = append(RESTMapper.(meta.MultiRESTMapper), m)
}
func NewDefaultRESTMapper(versions []string, interfacesFunc meta.VersionInterfacesFunc, importPathPrefix string,
ignoredKinds, rootScoped util.StringSet) *meta.DefaultRESTMapper {
mapper := meta.NewDefaultRESTMapper(versions, interfacesFunc)
// enumerate all supported versions, get the kinds, and register with the mapper how to address our resources.
for _, version := range versions {
for kind, oType := range Scheme.KnownTypes(version) {
// TODO: Remove import path prefix check.
// We check the import path prefix because we currently stuff both "api" and "experimental" objects
// into the same group within Scheme since Scheme has no notion of groups yet.
if !strings.HasPrefix(oType.PkgPath(), importPathPrefix) || ignoredKinds.Has(kind) {
continue
}
scope := meta.RESTScopeNamespace
if rootScoped.Has(kind) {
scope = meta.RESTScopeRoot
}
mapper.Add(scope, kind, version, false)
}
}
return mapper
}

View File

@ -83,8 +83,8 @@ type DefaultRESTMapper struct {
}
// VersionInterfacesFunc returns the appropriate codec, typer, and metadata accessor for a
// given api version, or false if no such api version exists.
type VersionInterfacesFunc func(apiVersion string) (*VersionInterfaces, bool)
// given api version, or an error if no such api version exists.
type VersionInterfacesFunc func(apiVersion string) (*VersionInterfaces, error)
// NewDefaultRESTMapper initializes a mapping between Kind and APIVersion
// to a resource name and back based on the objects in a runtime.Scheme
@ -226,8 +226,8 @@ func (m *DefaultRESTMapper) RESTMapping(kind string, versions ...string) (*RESTM
return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", version, kind)
}
interfaces, ok := m.interfacesFunc(version)
if !ok {
interfaces, err := m.interfacesFunc(version)
if err != nil {
return nil, fmt.Errorf("the provided version %q has no relevant versions", version)
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package meta
import (
"errors"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -54,12 +55,14 @@ var validCodec = fakeCodec{}
var validAccessor = resourceAccessor{}
var validConvertor = fakeConvertor{}
func fakeInterfaces(version string) (*VersionInterfaces, bool) {
return &VersionInterfaces{Codec: validCodec, ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, true
func fakeInterfaces(version string) (*VersionInterfaces, error) {
return &VersionInterfaces{Codec: validCodec, ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, nil
}
func unmatchedVersionInterfaces(version string) (*VersionInterfaces, bool) {
return nil, false
var unmatchedErr = errors.New("no version")
func unmatchedVersionInterfaces(version string) (*VersionInterfaces, error) {
return nil, unmatchedErr
}
func TestRESTMapperVersionAndKindForResource(t *testing.T) {

View File

@ -88,16 +88,7 @@ func interfacesFor(version string) (*meta.VersionInterfaces, error) {
}
func newMapper() *meta.DefaultRESTMapper {
return meta.NewDefaultRESTMapper(
versions,
func(version string) (*meta.VersionInterfaces, bool) {
interfaces, err := interfacesFor(version)
if err != nil {
return nil, false
}
return interfaces, true
},
)
return meta.NewDefaultRESTMapper(versions, interfacesFor)
}
func addTestTypes() {

19
pkg/expapi/deep_copy.go Normal file
View File

@ -0,0 +1,19 @@
/*
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 expapi
func addDeepCopyFuncs() {}

View File

@ -0,0 +1,75 @@
/*
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 latest
import (
"fmt"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/registered"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/expapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/expapi/v1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
var (
Version string
Versions []string
accessor = meta.NewAccessor()
Codec runtime.Codec
SelfLinker = runtime.SelfLinker(accessor)
RESTMapper meta.RESTMapper
)
const importPrefix = "github.com/GoogleCloudPlatform/kubernetes/pkg/expapi"
func init() {
Version = registered.RegisteredVersions[0]
Codec = runtime.CodecFor(api.Scheme, Version)
// Put the registered versions in Versions in reverse order.
for i := len(registered.RegisteredVersions) - 1; i >= 0; i-- {
Versions = append(Versions, registered.RegisteredVersions[i])
}
// the list of kinds that are scoped at the root of the api hierarchy
// if a kind is not enumerated here, it is assumed to have a namespace scope
rootScoped := util.NewStringSet()
ignoredKinds := util.NewStringSet()
RESTMapper = api.NewDefaultRESTMapper(Versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
api.RegisterRESTMapper(RESTMapper)
}
// InterfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
func InterfacesFor(version string) (*meta.VersionInterfaces, error) {
switch version {
case "v1":
return &meta.VersionInterfaces{
Codec: v1.Codec,
ObjectConvertor: api.Scheme,
MetadataAccessor: accessor,
}, nil
default:
return nil, fmt.Errorf("unsupported storage version: %s (valid: %s)", version, strings.Join(Versions, ", "))
}
}

19
pkg/expapi/register.go Normal file
View File

@ -0,0 +1,19 @@
/*
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 expapi
func init() {}

29
pkg/expapi/types.go Normal file
View File

@ -0,0 +1,29 @@
/*
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.
*/
/*
This file (together with pkg/expapi/v1/types.go) contain the experimental
types in kubernetes. These API objects are experimental, meaning that the
APIs may be broken at any time by the kubernetes team.
DISCLAIMER: The implementation of the experimental API group itself is
a temporary one meant as a stopgap solution until kubernetes has proper
support for multiple API groups. The transition may require changes
beyond registration differences. In other words, experimental API group
support is experimental.
*/
package expapi

View File

@ -0,0 +1,19 @@
/*
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 v1
func addConversionFuncs() {}

View File

@ -0,0 +1,19 @@
/*
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 v1
func addDeepCopyFuncs() {}

19
pkg/expapi/v1/defaults.go Normal file
View File

@ -0,0 +1,19 @@
/*
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 v1
func addDefaultingFuncs() {}

30
pkg/expapi/v1/register.go Normal file
View File

@ -0,0 +1,30 @@
/*
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 v1
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
var Codec = runtime.CodecFor(api.Scheme, "v1")
func init() {
addDeepCopyFuncs()
addConversionFuncs()
addDefaultingFuncs()
}

17
pkg/expapi/v1/types.go Normal file
View File

@ -0,0 +1,17 @@
/*
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 v1

View File

@ -18,6 +18,7 @@ package cmd
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
@ -64,6 +65,15 @@ func (*internalType) IsAnAPIObject() {}
func (*externalType) IsAnAPIObject() {}
func (*ExternalType2) IsAnAPIObject() {}
var versionErr = errors.New("not a version")
func versionErrIfFalse(b bool) error {
if b {
return nil
}
return versionErr
}
func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName("", "Type", &internalType{})
@ -73,12 +83,12 @@ func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
codec := runtime.CodecFor(scheme, "unlikelyversion")
validVersion := testapi.Version()
mapper := meta.NewDefaultRESTMapper([]string{"unlikelyversion", validVersion}, func(version string) (*meta.VersionInterfaces, bool) {
mapper := meta.NewDefaultRESTMapper([]string{"unlikelyversion", validVersion}, func(version string) (*meta.VersionInterfaces, error) {
return &meta.VersionInterfaces{
Codec: runtime.CodecFor(scheme, version),
ObjectConvertor: scheme,
MetadataAccessor: meta.NewAccessor(),
}, (version == validVersion || version == "unlikelyversion")
}, versionErrIfFalse(version == validVersion || version == "unlikelyversion")
})
for _, version := range []string{"unlikelyversion", validVersion} {
for kind := range scheme.KnownTypes(version) {

View File

@ -36,6 +36,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
@ -43,6 +44,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/handlers"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
explatest "github.com/GoogleCloudPlatform/kubernetes/pkg/expapi/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
@ -89,22 +91,25 @@ const (
// Config is a structure used to configure a Master.
type Config struct {
DatabaseStorage storage.Interface
EventTTL time.Duration
MinionRegexp string
KubeletClient client.KubeletClient
DatabaseStorage storage.Interface
ExpDatabaseStorage storage.Interface
EventTTL time.Duration
MinionRegexp string
KubeletClient client.KubeletClient
// allow downstream consumers to disable the core controller loops
EnableCoreControllers bool
EnableLogsSupport bool
EnableUISupport bool
// allow downstream consumers to disable swagger
EnableSwaggerSupport bool
// allow v1 to be conditionally disabled
// allow api versions to be conditionally disabled
DisableV1 bool
EnableExp bool
// allow downstream consumers to disable the index route
EnableIndex bool
EnableProfiling bool
APIPrefix string
ExpAPIPrefix string
CorsAllowedOriginList util.StringList
Authenticator authenticator.Request
// TODO(roberthbailey): Remove once the server no longer supports http basic auth.
@ -181,12 +186,14 @@ type Master struct {
enableSwaggerSupport bool
enableProfiling bool
apiPrefix string
expAPIPrefix string
corsAllowedOriginList util.StringList
authenticator authenticator.Request
authorizer authorizer.Authorizer
admissionControl admission.Interface
masterCount int
v1 bool
exp bool
requestContextMapper api.RequestContextMapper
// External host is the name that should be used in external (public internet) URLs for this master
@ -227,11 +234,8 @@ type Master struct {
// NewEtcdStorage returns a storage.Interface for the provided arguments or an error if the version
// is incorrect.
func NewEtcdStorage(client tools.EtcdClient, version string, prefix string) (etcdStorage storage.Interface, err error) {
if version == "" {
version = latest.Version
}
versionInterfaces, err := latest.InterfacesFor(version)
func NewEtcdStorage(client tools.EtcdClient, interfacesFunc meta.VersionInterfacesFunc, version, prefix string) (etcdStorage storage.Interface, err error) {
versionInterfaces, err := interfacesFunc(version)
if err != nil {
return etcdStorage, err
}
@ -337,11 +341,13 @@ func New(c *Config) *Master {
enableSwaggerSupport: c.EnableSwaggerSupport,
enableProfiling: c.EnableProfiling,
apiPrefix: c.APIPrefix,
expAPIPrefix: c.ExpAPIPrefix,
corsAllowedOriginList: c.CorsAllowedOriginList,
authenticator: c.Authenticator,
authorizer: c.Authorizer,
admissionControl: c.AdmissionControl,
v1: !c.DisableV1,
exp: c.EnableExp,
requestContextMapper: c.RequestContextMapper,
cacheTimeout: c.CacheTimeout,
@ -566,6 +572,16 @@ func (m *Master) init(c *Config) {
requestInfoResolver := &apiserver.APIRequestInfoResolver{util.NewStringSet(strings.TrimPrefix(defaultVersion.Root, "/")), defaultVersion.Mapper}
apiserver.InstallServiceErrorHandler(m.handlerContainer, requestInfoResolver, apiVersions)
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})
expRequestInfoResolver := &apiserver.APIRequestInfoResolver{util.NewStringSet(strings.TrimPrefix(expVersion.Root, "/")), expVersion.Mapper}
apiserver.InstallServiceErrorHandler(m.handlerContainer, expRequestInfoResolver, []string{expVersion.Version})
}
// Register root handler.
// We do not register this using restful Webservice since we do not want to surface this in api docs.
// Allow master to be embedded in contexts which already have something registered at the root
@ -760,6 +776,30 @@ func (m *Master) api_v1() *apiserver.APIGroupVersion {
return version
}
// expapi returns the resources and codec for the experimental api
func (m *Master) expapi(c *Config) *apiserver.APIGroupVersion {
storage := map[string]rest.Storage{}
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,
}
}
// findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP.
func findExternalAddress(node *api.Node) (string, error) {
var fallback string

View File

@ -21,6 +21,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
explatest "github.com/GoogleCloudPlatform/kubernetes/pkg/expapi/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
etcdstorage "github.com/GoogleCloudPlatform/kubernetes/pkg/storage/etcd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
@ -33,6 +34,7 @@ func TestGetServersToValidate(t *testing.T) {
fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.Machines = []string{"http://machine1:4001", "http://machine2", "http://machine3:4003"}
config.DatabaseStorage = etcdstorage.NewEtcdStorage(fakeClient, latest.Codec, etcdtest.PathPrefix())
config.ExpDatabaseStorage = etcdstorage.NewEtcdStorage(fakeClient, explatest.Codec, etcdtest.PathPrefix())
master.nodeRegistry = registrytest.NewMinionRegistry([]string{"node1", "node2"}, api.NodeResources{})

View File

@ -21,7 +21,6 @@ import (
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
@ -138,7 +137,7 @@ func TestSyncNamespaceThatIsActive(t *testing.T) {
func TestRunStop(t *testing.T) {
o := testclient.NewObjects(api.Scheme, api.Scheme)
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)}
nsMgr := NewNamespaceManager(client, 1*time.Second)
if nsMgr.StopEverything != nil {

View File

@ -23,7 +23,6 @@ import (
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
@ -238,7 +237,7 @@ func TestNewBuilder(t *testing.T) {
o := testclient.NewObjects(api.Scheme, api.Scheme)
o.Add(item.pv)
o.Add(item.claim)
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)}
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(testProbeVolumePlugins(), newTestHost(t, client))
@ -295,7 +294,7 @@ func TestNewBuilderClaimNotBound(t *testing.T) {
o := testclient.NewObjects(api.Scheme, api.Scheme)
o.Add(pv)
o.Add(claim)
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)}
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(testProbeVolumePlugins(), newTestHost(t, client))

View File

@ -23,7 +23,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
@ -32,7 +31,7 @@ import (
func TestRunStop(t *testing.T) {
o := testclient.NewObjects(api.Scheme, api.Scheme)
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)}
binder := NewPersistentVolumeClaimBinder(client, 1*time.Second)
if len(binder.stopChannels) != 0 {
@ -119,7 +118,7 @@ func TestExampleObjects(t *testing.T) {
t.Fatal(err)
}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)}
if reflect.TypeOf(scenario.expected) == reflect.TypeOf(&api.PersistentVolumeClaim{}) {
pvc, err := client.PersistentVolumeClaims("ns").Get("doesntmatter")
@ -179,7 +178,7 @@ func TestBindingWithExamples(t *testing.T) {
t.Fatal(err)
}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)}
pv, err := client.PersistentVolumes().Get("any")
pv.Spec.PersistentVolumeReclaimPolicy = api.PersistentVolumeReclaimRecycle
@ -282,7 +281,7 @@ func TestMissingFromIndex(t *testing.T) {
t.Fatal(err)
}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)}
pv, err := client.PersistentVolumes().Get("any")
if err != nil {

View File

@ -22,7 +22,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
@ -48,11 +47,11 @@ type provision struct {
}
func (p *provision) Admit(a admission.Attributes) (err error) {
defaultVersion, kind, err := latest.RESTMapper.VersionAndKindForResource(a.GetResource())
defaultVersion, kind, err := api.RESTMapper.VersionAndKindForResource(a.GetResource())
if err != nil {
return admission.NewForbidden(a, err)
}
mapping, err := latest.RESTMapper.RESTMapping(kind, defaultVersion)
mapping, err := api.RESTMapper.RESTMapping(kind, defaultVersion)
if err != nil {
return admission.NewForbidden(a, err)
}

View File

@ -23,7 +23,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
@ -49,11 +48,11 @@ type exists struct {
}
func (e *exists) Admit(a admission.Attributes) (err error) {
defaultVersion, kind, err := latest.RESTMapper.VersionAndKindForResource(a.GetResource())
defaultVersion, kind, err := api.RESTMapper.VersionAndKindForResource(a.GetResource())
if err != nil {
return admission.NewForbidden(a, err)
}
mapping, err := latest.RESTMapper.RESTMapping(kind, defaultVersion)
mapping, err := api.RESTMapper.RESTMapping(kind, defaultVersion)
if err != nil {
return admission.NewForbidden(a, err)
}

View File

@ -23,7 +23,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
@ -59,11 +58,11 @@ func (l *lifecycle) Admit(a admission.Attributes) (err error) {
return nil
}
defaultVersion, kind, err := latest.RESTMapper.VersionAndKindForResource(a.GetResource())
defaultVersion, kind, err := api.RESTMapper.VersionAndKindForResource(a.GetResource())
if err != nil {
return admission.NewForbidden(a, err)
}
mapping, err := latest.RESTMapper.RESTMapping(kind, defaultVersion)
mapping, err := api.RESTMapper.RESTMapping(kind, defaultVersion)
if err != nil {
return admission.NewForbidden(a, err)
}

View File

@ -22,6 +22,7 @@ import (
"fmt"
"math/rand"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
"github.com/GoogleCloudPlatform/kubernetes/pkg/storage"
@ -42,7 +43,7 @@ func NewEtcdClient() *etcd.Client {
}
func NewEtcdStorage() (storage.Interface, error) {
return master.NewEtcdStorage(NewEtcdClient(), testapi.Version(), etcdtest.PathPrefix())
return master.NewEtcdStorage(NewEtcdClient(), latest.InterfacesFor, testapi.Version(), etcdtest.PathPrefix())
}
func RequireEtcd() {

View File

@ -26,11 +26,13 @@ import (
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller/replication"
explatest "github.com/GoogleCloudPlatform/kubernetes/pkg/expapi/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
@ -128,19 +130,27 @@ func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Se
var etcdStorage storage.Interface
var err error
if masterConfig == nil {
etcdStorage, err = master.NewEtcdStorage(NewEtcdClient(), "", etcdtest.PathPrefix())
etcdClient := NewEtcdClient()
etcdStorage, err = master.NewEtcdStorage(etcdClient, latest.InterfacesFor, latest.Version, etcdtest.PathPrefix())
if err != nil {
glog.Fatalf("Failed to create etcd storage for master %v", err)
}
expEtcdStorage, err := master.NewEtcdStorage(etcdClient, explatest.InterfacesFor, explatest.Version, etcdtest.PathPrefix())
if err != nil {
glog.Fatalf("Failed to create etcd storage for master %v", err)
}
masterConfig = &master.Config{
DatabaseStorage: etcdStorage,
KubeletClient: client.FakeKubeletClient{},
EnableLogsSupport: false,
EnableProfiling: true,
EnableUISupport: false,
APIPrefix: "/api",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
DatabaseStorage: etcdStorage,
ExpDatabaseStorage: expEtcdStorage,
KubeletClient: client.FakeKubeletClient{},
EnableLogsSupport: false,
EnableProfiling: true,
EnableUISupport: false,
APIPrefix: "/api",
ExpAPIPrefix: "/experimental",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
}
} else {
etcdStorage = masterConfig.DatabaseStorage
@ -258,20 +268,28 @@ func StartPods(numPods int, host string, restClient *client.Client) error {
// TODO: Merge this into startMasterOrDie.
func RunAMaster(t *testing.T) (*master.Master, *httptest.Server) {
etcdStorage, err := master.NewEtcdStorage(NewEtcdClient(), testapi.Version(), etcdtest.PathPrefix())
etcdClient := NewEtcdClient()
etcdStorage, err := master.NewEtcdStorage(etcdClient, latest.InterfacesFor, testapi.Version(), etcdtest.PathPrefix())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expEtcdStorage, err := master.NewEtcdStorage(etcdClient, explatest.InterfacesFor, explatest.Version, etcdtest.PathPrefix())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
m := master.New(&master.Config{
DatabaseStorage: etcdStorage,
KubeletClient: client.FakeKubeletClient{},
EnableLogsSupport: false,
EnableProfiling: true,
EnableUISupport: false,
APIPrefix: "/api",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
DatabaseStorage: etcdStorage,
ExpDatabaseStorage: expEtcdStorage,
KubeletClient: client.FakeKubeletClient{},
EnableLogsSupport: false,
EnableProfiling: true,
EnableUISupport: false,
APIPrefix: "/api",
ExpAPIPrefix: "/experimental",
EnableExp: true,
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
})
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

View File

@ -0,0 +1,39 @@
// +build integration,!no-etcd
/*
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 integration
import (
"net/http"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/test/integration/framework"
)
func TestExperimentalPrefix(t *testing.T) {
_, s := framework.RunAMaster(t)
defer s.Close()
resp, err := http.Get(s.URL + "/experimental/")
if err != nil {
t.Fatalf("unexpected error getting experimental prefix: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("got status %v instead of 200 OK", resp.StatusCode)
}
}

View File

@ -33,6 +33,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator/bearertoken"
@ -340,7 +341,7 @@ func startServiceAccountTestServer(t *testing.T) (*client.Client, client.Config,
deleteAllEtcdKeys()
// Etcd
etcdStorage, err := master.NewEtcdStorage(newEtcdClient(), testapi.Version(), etcdtest.PathPrefix())
etcdStorage, err := master.NewEtcdStorage(newEtcdClient(), latest.InterfacesFor, testapi.Version(), etcdtest.PathPrefix())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@ -25,6 +25,7 @@ import (
"net/http/httptest"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
@ -67,7 +68,7 @@ func deleteAllEtcdKeys() {
}
func runAMaster(t *testing.T) (*master.Master, *httptest.Server) {
etcdStorage, err := master.NewEtcdStorage(newEtcdClient(), testapi.Version(), etcdtest.PathPrefix())
etcdStorage, err := master.NewEtcdStorage(newEtcdClient(), latest.InterfacesFor, testapi.Version(), etcdtest.PathPrefix())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}