Merge pull request #13955 from caesarxuchao/API-discovery

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2015-09-21 14:01:36 -07:00
commit 6c30a0e170
16 changed files with 277 additions and 49 deletions

View File

@ -9,6 +9,10 @@
"path": "/api",
"description": "get available API versions"
},
{
"path": "/apis",
"description": "get available API versions"
},
{
"path": "/version",
"description": "git code version from which this is built"

View File

@ -10950,6 +10950,25 @@
]
}
]
},
{
"path": "/api/v1",
"description": "API at /api/v1 version v1",
"operations": [
{
"type": "void",
"method": "GET",
"summary": "get available resources",
"nickname": "getAPIResources",
"parameters": [],
"produces": [
"application/json"
],
"consumes": [
"application/json"
]
}
]
}
],
"models": {

View File

@ -132,11 +132,14 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string
// We will fix this by supporting multiple group versions in Config
cl.ExperimentalClient = client.NewExperimentalOrDie(&client.Config{Host: apiServer.URL, Version: testapi.Experimental.Version()})
storageVersions := make(map[string]string)
etcdStorage, err := master.NewEtcdStorage(etcdClient, latest.GroupOrDie("").InterfacesFor, testapi.Default.Version(), etcdtest.PathPrefix())
storageVersions[""] = testapi.Default.Version()
if err != nil {
glog.Fatalf("Unable to get etcd storage: %v", err)
}
expEtcdStorage, err := master.NewEtcdStorage(etcdClient, latest.GroupOrDie("experimental").InterfacesFor, testapi.Experimental.Version(), etcdtest.PathPrefix())
storageVersions["experimental"] = testapi.Experimental.Version()
if err != nil {
glog.Fatalf("Unable to get etcd storage for experimental: %v", err)
}
@ -171,6 +174,7 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string
ReadWritePort: portNumber,
PublicAddress: publicAddress,
CacheTimeout: 2 * time.Second,
StorageVersions: storageVersions,
})
handler.delegate = m.Handler

View File

@ -21,6 +21,7 @@ package app
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"os"
@ -245,7 +246,10 @@ func (s *APIServer) verifyClusterIPFlags() {
}
}
func newEtcd(etcdConfigFile string, etcdServerList []string, interfacesFunc meta.VersionInterfacesFunc, defaultVersion, storageVersion, pathPrefix string) (etcdStorage storage.Interface, err error) {
func newEtcd(etcdConfigFile string, etcdServerList []string, interfacesFunc meta.VersionInterfacesFunc, storageVersion, pathPrefix string) (etcdStorage storage.Interface, err error) {
if storageVersion == "" {
return etcdStorage, fmt.Errorf("storageVersion is required to create a etcd storage")
}
var client tools.EtcdClient
if etcdConfigFile != "" {
client, err = etcd.NewClientFromFile(etcdConfigFile)
@ -264,11 +268,8 @@ func newEtcd(etcdConfigFile string, etcdServerList []string, interfacesFunc meta
etcdClient.SetTransport(transport)
client = etcdClient
}
if storageVersion == "" {
storageVersion = defaultVersion
}
return master.NewEtcdStorage(client, interfacesFunc, storageVersion, pathPrefix)
etcdStorage, err = master.NewEtcdStorage(client, interfacesFunc, storageVersion, pathPrefix)
return etcdStorage, err
}
// Run runs the specified APIServer. This should never exit.
@ -341,7 +342,16 @@ func (s *APIServer) Run(_ []string) error {
glog.Fatalf("Invalid server address: %v", err)
}
etcdStorage, err := newEtcd(s.EtcdConfigFile, s.EtcdServerList, latest.GroupOrDie("").InterfacesFor, latest.GroupOrDie("").Version, s.StorageVersion, s.EtcdPathPrefix)
g, err := latest.Group("")
if err != nil {
return err
}
storageVersions := make(map[string]string)
if s.StorageVersion == "" {
s.StorageVersion = g.Version
}
etcdStorage, err := newEtcd(s.EtcdConfigFile, s.EtcdServerList, g.InterfacesFor, s.StorageVersion, s.EtcdPathPrefix)
storageVersions[""] = s.StorageVersion
if err != nil {
glog.Fatalf("Invalid storage version or misconfigured etcd: %v", err)
}
@ -352,10 +362,14 @@ func (s *APIServer) Run(_ []string) error {
if err != nil {
glog.Fatalf("experimental API is enabled in runtime config, but not enabled in the environment variable KUBE_API_VERSIONS. Error: %v", err)
}
expEtcdStorage, err = newEtcd(s.EtcdConfigFile, s.EtcdServerList, g.InterfacesFor, g.Version, s.ExpStorageVersion, s.EtcdPathPrefix)
if s.ExpStorageVersion == "" {
s.ExpStorageVersion = g.Version
}
expEtcdStorage, err = newEtcd(s.EtcdConfigFile, s.EtcdServerList, g.InterfacesFor, s.ExpStorageVersion, s.EtcdPathPrefix)
if err != nil {
glog.Fatalf("Invalid experimental storage version or misconfigured etcd: %v", err)
}
storageVersions["experimental"] = s.StorageVersion
}
n := s.ServiceClusterIPRange
@ -427,6 +441,7 @@ func (s *APIServer) Run(_ []string) error {
config := &master.Config{
DatabaseStorage: etcdStorage,
ExpDatabaseStorage: expEtcdStorage,
StorageVersions: storageVersions,
EventTTL: s.EventTTL,
KubeletClient: kubeletClient,

View File

@ -22,16 +22,64 @@ import (
// This file contains API types that are unversioned.
// APIVersions lists the api versions that are available, to allow
// version negotiation. APIVersions isn't just an unnamed array of
// strings in order to allow for future evolution, though unversioned
// APIVersions lists the versions that are available, to allow clients to
// discover the API at /api, which is the root path of the legacy v1 API.
type APIVersions struct {
// versions are the api versions that are available.
Versions []string `json:"versions"`
}
// APIGroupList is a list of APIGroup, to allow clients to discover the API at
// /apis.
type APIGroupList struct {
// groups is a list of APIGroup.
Groups []APIGroup `json:"groups"`
}
// APIGroup contains the name, the supported versions, and the preferred version
// of a group.
type APIGroup struct {
// name is the name of the group.
Name string `json:"name"`
// versions are the versions supported in this group.
Versions []GroupVersion `json:"versions"`
// preferredVersion is the version preferred by the API server, which
// probably is the storage version.
PreferredVersion GroupVersion `json:"preferredVersion,omitempty"`
}
// GroupVersion contains the "group/version" and "version" string of a version.
// It is made a struct to keep extensiblity.
type GroupVersion struct {
// groupVersion specifies the API group and version in the form "group/version"
GroupVersion string `json:"groupVersion"`
// version specifies the version in the form of "version". This is to save
// the clients the trouble of splitting the GroupVersion.
Version string `json:"version"`
}
// APIResource specifies the name of a resource and whether it is namespaced.
type APIResource struct {
// name is the name of the resource.
Name string `json:"name"`
// namespaced indicates if a resource is namespaced or not.
Namespaced bool `json:"namespaced"`
}
// APIResourceList is a list of APIResource, it is used to expose the name of the
// resources supported in a specific group and version, and if the resource
// is namespaced.
type APIResourceList struct {
// groupVersion is the group and version this APIResourceList is for.
GroupVersion string `json:"groupVersion"`
// resources contains the name of the resources and if they are namespaced.
APIResources []APIResource `json:"resources"`
}
// RootPaths lists the paths available at root.
// For example: "/healthz", "/api".
// For example: "/healthz", "/apis".
type RootPaths struct {
// paths are the paths available at root.
Paths []string `json:"paths"`
}

View File

@ -168,6 +168,7 @@ type ThirdPartyResourceList struct {
}
// An APIVersion represents a single concrete version of an object model.
// TODO: we should consider merge this struct with GroupVersion in unversioned.go
type APIVersion struct {
// Name of this version (e.g. 'v1').
Name string `json:"name,omitempty"`

View File

@ -61,8 +61,8 @@ type documentable interface {
var errEmptyName = errors.NewBadRequest("name must be provided")
// Installs handlers for API resources.
func (a *APIInstaller) Install(ws *restful.WebService) []error {
errors := make([]error, 0)
func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []api.APIResource, errors []error) {
errors = make([]error, 0)
proxyHandler := (&ProxyHandler{a.prefix + "/proxy/", a.group.Storage, a.group.Codec, a.group.Context, a.info, a.proxyDialerFn})
@ -75,11 +75,15 @@ func (a *APIInstaller) Install(ws *restful.WebService) []error {
}
sort.Strings(paths)
for _, path := range paths {
if err := a.registerResourceHandlers(path, a.group.Storage[path], ws, proxyHandler); err != nil {
apiResource, err := a.registerResourceHandlers(path, a.group.Storage[path], ws, proxyHandler)
if err != nil {
errors = append(errors, err)
}
if apiResource != nil {
apiResources = append(apiResources, *apiResource)
}
return errors
}
return apiResources, errors
}
// NewWebService creates a new restful webservice with the api installer's prefix and version.
@ -95,7 +99,7 @@ func (a *APIInstaller) NewWebService() *restful.WebService {
return ws
}
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) error {
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) (*api.APIResource, error) {
admit := a.group.Admit
context := a.group.Context
@ -112,40 +116,40 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
resource = parts[0]
default:
// TODO: support deeper paths
return fmt.Errorf("api_installer allows only one or two segment paths (resource or resource/subresource)")
return nil, fmt.Errorf("api_installer allows only one or two segment paths (resource or resource/subresource)")
}
hasSubresource := len(subresource) > 0
object := storage.New()
_, kind, err := a.group.Typer.ObjectVersionAndKind(object)
if err != nil {
return err
return nil, err
}
versionedPtr, err := a.group.Creater.New(a.group.Version, kind)
if err != nil {
return err
return nil, err
}
versionedObject := indirectArbitraryPointer(versionedPtr)
mapping, err := a.group.Mapper.RESTMapping(kind, a.group.Version)
if err != nil {
return err
return nil, err
}
// subresources must have parent resources, and follow the namespacing rules of their parent
if hasSubresource {
parentStorage, ok := a.group.Storage[resource]
if !ok {
return fmt.Errorf("subresources can only be declared when the parent is also registered: %s needs %s", path, resource)
return nil, fmt.Errorf("subresources can only be declared when the parent is also registered: %s needs %s", path, resource)
}
parentObject := parentStorage.New()
_, parentKind, err := a.group.Typer.ObjectVersionAndKind(parentObject)
if err != nil {
return err
return nil, err
}
parentMapping, err := a.group.Mapper.RESTMapping(parentKind, a.group.Version)
if err != nil {
return err
return nil, err
}
mapping.Scope = parentMapping.Scope
}
@ -178,14 +182,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
_, listKind, err := a.group.Typer.ObjectVersionAndKind(list)
versionedListPtr, err := a.group.Creater.New(a.group.Version, listKind)
if err != nil {
return err
return nil, err
}
versionedList = indirectArbitraryPointer(versionedListPtr)
}
versionedListOptions, err := a.group.Creater.New(serverVersion, "ListOptions")
if err != nil {
return err
return nil, err
}
var versionedDeleterObject interface{}
@ -193,7 +197,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
case isGracefulDeleter:
objectPtr, err := a.group.Creater.New(serverVersion, "DeleteOptions")
if err != nil {
return err
return nil, err
}
versionedDeleterObject = indirectArbitraryPointer(objectPtr)
isDeleter = true
@ -203,7 +207,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
versionedStatusPtr, err := a.group.Creater.New(serverVersion, "Status")
if err != nil {
return err
return nil, err
}
versionedStatus := indirectArbitraryPointer(versionedStatusPtr)
var (
@ -217,11 +221,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
getOptions, getSubpath, getSubpathKey = getterWithOptions.NewGetOptions()
_, getOptionsKind, err = a.group.Typer.ObjectVersionAndKind(getOptions)
if err != nil {
return err
return nil, err
}
versionedGetOptions, err = a.group.Creater.New(serverVersion, getOptionsKind)
if err != nil {
return err
return nil, err
}
isGetter = true
}
@ -238,7 +242,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
if connectOptions != nil {
_, connectOptionsKind, err = a.group.Typer.ObjectVersionAndKind(connectOptions)
if err != nil {
return err
return nil, err
}
versionedConnectOptions, err = a.group.Creater.New(serverVersion, connectOptionsKind)
}
@ -262,6 +266,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
params := []*restful.Parameter{}
actions := []action{}
var apiResource api.APIResource
// Get the list of actions for the given scope.
switch scope.Name() {
case meta.RESTScopeNameRoot:
@ -276,6 +281,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
resourcePath = itemPath
resourceParams = nameParams
}
apiResource.Name = path
apiResource.Namespaced = false
namer := rootScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath)}
// Handler for standard REST verbs (GET, PUT, POST and DELETE).
@ -314,6 +321,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
resourcePath = itemPath
resourceParams = nameParams
}
apiResource.Name = path
apiResource.Namespaced = true
namer := scopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath), false}
actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer}, isLister)
@ -344,7 +353,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
}
break
default:
return fmt.Errorf("unsupported restscope: %s", scope.Name())
return nil, fmt.Errorf("unsupported restscope: %s", scope.Name())
}
// Create Routes for the actions.
@ -404,7 +413,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Writes(versionedObject)
if isGetterWithOptions {
if err := addObjectParams(ws, route, versionedGetOptions); err != nil {
return err
return nil, err
}
}
addParams(route, action.Params)
@ -423,7 +432,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Returns(http.StatusOK, "OK", versionedList).
Writes(versionedList)
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
return err
return nil, err
}
switch {
case isLister && isWatcher:
@ -529,7 +538,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Returns(http.StatusOK, "OK", watchjson.WatchEvent{}).
Writes(watchjson.WatchEvent{})
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
return err
return nil, err
}
addParams(route, action.Params)
ws.Route(route)
@ -548,7 +557,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Returns(http.StatusOK, "OK", watchjson.WatchEvent{}).
Writes(watchjson.WatchEvent{})
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
return err
return nil, err
}
addParams(route, action.Params)
ws.Route(route)
@ -576,18 +585,18 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Writes("string")
if versionedConnectOptions != nil {
if err := addObjectParams(ws, route, versionedConnectOptions); err != nil {
return err
return nil, err
}
}
addParams(route, action.Params)
ws.Route(route)
}
default:
return fmt.Errorf("unrecognized action verb: %s", action.Verb)
return nil, fmt.Errorf("unrecognized action verb: %s", action.Verb)
}
// Note: update GetAttribs() when adding a custom handler.
}
return nil
return &apiResource, nil
}
// rootScopeNaming reads only names from a request and ignores namespaces. It implements ScopeNamer

View File

@ -118,7 +118,9 @@ const (
func (g *APIGroupVersion) InstallREST(container *restful.Container) error {
installer := g.newInstaller()
ws := installer.NewWebService()
registrationErrors := installer.Install(ws)
apiResources, registrationErrors := installer.Install(ws)
// TODO: g.Version only contains "version" now, it will contain "group/version" in the near future.
AddSupportedResourcesWebService(ws, g.Version, apiResources)
container.Add(ws)
return errors.NewAggregate(registrationErrors)
}
@ -141,8 +143,10 @@ func (g *APIGroupVersion) UpdateREST(container *restful.Container) error {
if ws == nil {
return apierrors.NewInternalError(fmt.Errorf("unable to find an existing webservice for prefix %s", installer.prefix))
}
return errors.NewAggregate(installer.Install(ws))
apiResources, registrationErrors := installer.Install(ws)
// TODO: g.Version only contains "version" now, it will contain "group/version" in the near future.
AddSupportedResourcesWebService(ws, g.Version, apiResources)
return errors.NewAggregate(registrationErrors)
}
// newInstaller is a helper to create the installer. Used by InstallREST and UpdateREST.
@ -232,7 +236,7 @@ func serviceErrorHandler(requestResolver *APIRequestInfoResolver, apiVersions []
errorJSON(apierrors.NewGenericServerResponse(serviceErr.Code, "", "", "", "", 0, false), codec, response.ResponseWriter)
}
// Adds a service to return the supported api versions.
// Adds a service to return the supported api versions at the legacy /api.
func AddApiWebService(container *restful.Container, apiPrefix string, versions []string) {
// TODO: InstallREST should register each version automatically
@ -248,6 +252,46 @@ func AddApiWebService(container *restful.Container, apiPrefix string, versions [
container.Add(ws)
}
// Adds a service to return the supported api versions at /apis.
func AddApisWebService(container *restful.Container, apiPrefix string, groups []api.APIGroup) {
rootAPIHandler := RootAPIHandler(groups)
ws := new(restful.WebService)
ws.Path(apiPrefix)
ws.Doc("get available API versions")
ws.Route(ws.GET("/").To(rootAPIHandler).
Doc("get available API versions").
Operation("getAPIVersions").
Produces(restful.MIME_JSON).
Consumes(restful.MIME_JSON))
container.Add(ws)
}
// Adds a service to return the supported versions, preferred version, and name
// of a group. E.g., a such web service will be registered at /apis/experimental.
func AddGroupWebService(container *restful.Container, path string, group api.APIGroup) {
groupHandler := GroupHandler(group)
ws := new(restful.WebService)
ws.Path(path)
ws.Doc("get information of a group")
ws.Route(ws.GET("/").To(groupHandler).
Doc("get information of a group").
Operation("getAPIGroup").
Produces(restful.MIME_JSON).
Consumes(restful.MIME_JSON))
container.Add(ws)
}
// Adds a service to return the supported resources, E.g., a such web service
// will be registered at /apis/experimental/v1.
func AddSupportedResourcesWebService(ws *restful.WebService, groupVersion string, apiResources []api.APIResource) {
resourceHandler := SupportedResourcesHandler(groupVersion, apiResources)
ws.Route(ws.GET("/").To(resourceHandler).
Doc("get available resources").
Operation("getAPIResources").
Produces(restful.MIME_JSON).
Consumes(restful.MIME_JSON))
}
// handleVersion writes the server's version information.
func handleVersion(req *restful.Request, resp *restful.Response) {
// TODO: use restful's Response methods
@ -262,6 +306,31 @@ func APIVersionHandler(versions ...string) restful.RouteFunction {
}
}
// RootAPIHandler returns a handler which will list the provided groups and versions as available.
func RootAPIHandler(groups []api.APIGroup) restful.RouteFunction {
return func(req *restful.Request, resp *restful.Response) {
// TODO: use restful's Response methods
writeRawJSON(http.StatusOK, api.APIGroupList{Groups: groups}, resp.ResponseWriter)
}
}
// GroupHandler returns a handler which will return the api.GroupAndVersion of
// the group.
func GroupHandler(group api.APIGroup) restful.RouteFunction {
return func(req *restful.Request, resp *restful.Response) {
// TODO: use restful's Response methods
writeRawJSON(http.StatusOK, group, resp.ResponseWriter)
}
}
// SupportedResourcesHandler returns a handler which will list the provided resources as available.
func SupportedResourcesHandler(groupVersion string, apiResources []api.APIResource) restful.RouteFunction {
return func(req *restful.Request, resp *restful.Response) {
// TODO: use restful's Response methods
writeRawJSON(http.StatusOK, api.APIResourceList{GroupVersion: groupVersion, APIResources: apiResources}, resp.ResponseWriter)
}
}
// write renders a returned runtime.Object to the response as a stream or an encoded object. If the object
// returned by the response implements rest.ResourceStreamer that interface will be used to render the
// response. The Accept header and current API version will be passed in, and the output will be copied

View File

@ -100,6 +100,8 @@ const (
type Config struct {
DatabaseStorage storage.Interface
ExpDatabaseStorage storage.Interface
// StorageVersions is a map between groups and their storage versions
StorageVersions map[string]string
EventTTL time.Duration
NodeRegexp string
KubeletClient client.KubeletClient
@ -570,16 +572,42 @@ func (m *Master) init(c *Config) {
requestInfoResolver := &apiserver.APIRequestInfoResolver{APIPrefixes: sets.NewString(strings.TrimPrefix(defaultVersion.Root, "/")), RestMapper: defaultVersion.Mapper}
apiserver.InstallServiceErrorHandler(m.handlerContainer, requestInfoResolver, apiVersions)
// allGroups records all supported groups at /apis
allGroups := []api.APIGroup{}
if m.exp {
expVersion := m.experimental(c)
if err := expVersion.InstallREST(m.handlerContainer); err != nil {
glog.Fatalf("Unable to setup experimental api: %v", err)
}
apiserver.AddApiWebService(m.handlerContainer, c.APIGroupPrefix+"/"+latest.GroupOrDie("experimental").Group+"/", []string{expVersion.Version})
g, err := latest.Group("experimental")
if err != nil {
glog.Fatalf("Unable to setup experimental api: %v", err)
}
expAPIVersions := []api.GroupVersion{
{
GroupVersion: g.Group + "/" + expVersion.Version,
Version: expVersion.Version,
},
}
storageVersion, found := c.StorageVersions[g.Group]
if !found {
glog.Fatalf("Couldn't find storage version of group %v", g.Group)
}
group := api.APIGroup{
Name: g.Group,
Versions: expAPIVersions,
PreferredVersion: api.GroupVersion{GroupVersion: g.Group + "/" + storageVersion, Version: storageVersion},
}
apiserver.AddGroupWebService(m.handlerContainer, c.APIGroupPrefix+"/"+latest.GroupOrDie("experimental").Group+"/", group)
allGroups = append(allGroups, group)
expRequestInfoResolver := &apiserver.APIRequestInfoResolver{APIPrefixes: sets.NewString(strings.TrimPrefix(expVersion.Root, "/")), RestMapper: expVersion.Mapper}
apiserver.InstallServiceErrorHandler(m.handlerContainer, expRequestInfoResolver, []string{expVersion.Version})
}
// This should be done after all groups are registered
// TODO: replace the hardcoded "apis".
apiserver.AddApisWebService(m.handlerContainer, "/apis", allGroups)
// 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
@ -784,7 +812,15 @@ func (m *Master) InstallThirdPartyAPI(rsrc *experimental.ThirdPartyResource) err
glog.Fatalf("Unable to setup thirdparty api: %v", err)
}
thirdPartyAPIPrefix := makeThirdPartyPath(group) + "/"
apiserver.AddApiWebService(m.handlerContainer, thirdPartyAPIPrefix, []string{rsrc.Versions[0].Name})
groupVersion := api.GroupVersion{
GroupVersion: group + "/" + rsrc.Versions[0].Name,
Version: rsrc.Versions[0].Name,
}
apiGroup := api.APIGroup{
Name: group,
Versions: []api.GroupVersion{groupVersion},
}
apiserver.AddGroupWebService(m.handlerContainer, thirdPartyAPIPrefix, apiGroup)
thirdPartyRequestInfoResolver := &apiserver.APIRequestInfoResolver{APIPrefixes: sets.NewString(strings.TrimPrefix(group, "/")), RestMapper: thirdparty.Mapper}
apiserver.InstallServiceErrorHandler(m.handlerContainer, thirdPartyRequestInfoResolver, []string{thirdparty.Version})
return nil

View File

@ -59,9 +59,12 @@ func setUp(t *testing.T) (Master, Config, *assert.Assertions) {
config := Config{}
fakeClient := tools.NewFakeEtcdClient(t)
fakeClient.Machines = []string{"http://machine1:4001", "http://machine2", "http://machine3:4003"}
storageVersions := make(map[string]string)
config.DatabaseStorage = etcdstorage.NewEtcdStorage(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix())
storageVersions[""] = testapi.Default.Version()
config.ExpDatabaseStorage = etcdstorage.NewEtcdStorage(fakeClient, testapi.Experimental.Codec(), etcdtest.PathPrefix())
storageVersions["experimental"] = testapi.Experimental.Version()
config.StorageVersions = storageVersions
master.nodeRegistry = registrytest.NewNodeRegistry([]string{"node1", "node2"}, api.NodeResources{})
return master, config, assert.New(t)

View File

@ -406,6 +406,7 @@ func TestAuthModeAlwaysAllow(t *testing.T) {
APIPrefix: "/api",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
transport := http.DefaultTransport
@ -522,6 +523,7 @@ func TestAuthModeAlwaysDeny(t *testing.T) {
APIPrefix: "/api",
Authorizer: apiserver.NewAlwaysDenyAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
transport := http.DefaultTransport
@ -590,6 +592,7 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) {
Authenticator: getTestTokenAuth(),
Authorizer: allowAliceAuthorizer{},
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
previousResourceVersion := make(map[string]float64)
@ -677,6 +680,7 @@ func TestBobIsForbidden(t *testing.T) {
Authenticator: getTestTokenAuth(),
Authorizer: allowAliceAuthorizer{},
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
transport := http.DefaultTransport
@ -738,6 +742,7 @@ func TestUnknownUserIsUnauthorized(t *testing.T) {
Authenticator: getTestTokenAuth(),
Authorizer: allowAliceAuthorizer{},
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
transport := http.DefaultTransport
@ -818,6 +823,7 @@ func TestNamespaceAuthorization(t *testing.T) {
Authenticator: getTestTokenAuth(),
Authorizer: a,
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
previousResourceVersion := make(map[string]float64)
@ -933,6 +939,7 @@ func TestKindAuthorization(t *testing.T) {
Authenticator: getTestTokenAuth(),
Authorizer: a,
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
previousResourceVersion := make(map[string]float64)
@ -1035,6 +1042,7 @@ func TestReadOnlyAuthorization(t *testing.T) {
Authenticator: getTestTokenAuth(),
Authorizer: a,
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
transport := http.DefaultTransport

View File

@ -130,11 +130,14 @@ func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Se
var err error
if masterConfig == nil {
etcdClient := NewEtcdClient()
storageVersions := make(map[string]string)
etcdStorage, err = master.NewEtcdStorage(etcdClient, latest.GroupOrDie("").InterfacesFor, latest.GroupOrDie("").Version, etcdtest.PathPrefix())
storageVersions[""] = latest.GroupOrDie("").Version
if err != nil {
glog.Fatalf("Failed to create etcd storage for master %v", err)
}
expEtcdStorage, err := master.NewEtcdStorage(etcdClient, latest.GroupOrDie("experimental").InterfacesFor, latest.GroupOrDie("experimental").Version, etcdtest.PathPrefix())
storageVersions["experimental"] = latest.GroupOrDie("experimental").Version
if err != nil {
glog.Fatalf("Failed to create etcd storage for master %v", err)
}
@ -142,6 +145,7 @@ func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Se
masterConfig = &master.Config{
DatabaseStorage: etcdStorage,
ExpDatabaseStorage: expEtcdStorage,
StorageVersions: storageVersions,
KubeletClient: client.FakeKubeletClient{},
EnableExp: true,
EnableLogsSupport: false,
@ -270,11 +274,14 @@ func StartPods(numPods int, host string, restClient *client.Client) error {
// TODO: Merge this into startMasterOrDie.
func RunAMaster(t *testing.T) (*master.Master, *httptest.Server) {
etcdClient := NewEtcdClient()
storageVersions := make(map[string]string)
etcdStorage, err := master.NewEtcdStorage(etcdClient, latest.GroupOrDie("").InterfacesFor, testapi.Default.Version(), etcdtest.PathPrefix())
storageVersions[""] = testapi.Default.Version()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expEtcdStorage, err := master.NewEtcdStorage(etcdClient, latest.GroupOrDie("experimental").InterfacesFor, latest.GroupOrDie("experimental").Version, etcdtest.PathPrefix())
storageVersions["experimental"] = testapi.Experimental.Version()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@ -291,6 +298,7 @@ func RunAMaster(t *testing.T) (*master.Master, *httptest.Server) {
EnableExp: true,
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: storageVersions,
})
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

View File

@ -75,6 +75,7 @@ func TestUnschedulableNodes(t *testing.T) {
APIPrefix: "/api",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
restClient := client.NewOrDie(&client.Config{Host: s.URL, Version: testapi.Default.Version()})

View File

@ -68,6 +68,7 @@ func TestSecrets(t *testing.T) {
APIPrefix: "/api",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
framework.DeleteAllEtcdKeys()

View File

@ -420,6 +420,7 @@ func startServiceAccountTestServer(t *testing.T) (*client.Client, client.Config,
Authenticator: authenticator,
Authorizer: authorizer,
AdmissionControl: serviceAccountAdmission,
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
// Start the service account and service account token controllers

View File

@ -81,6 +81,7 @@ func runAMaster(t *testing.T) (*master.Master, *httptest.Server) {
APIPrefix: "/api",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
StorageVersions: map[string]string{"": testapi.Default.Version()},
})
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {