Plumb restmapper properly through apiserver and tests

This commit is contained in:
derekwaynecarr 2015-01-29 17:46:54 -05:00
parent f911784c98
commit 4c33e36a88
13 changed files with 142 additions and 107 deletions

View File

@ -103,25 +103,6 @@ func init() {
return interfaces, true return interfaces, true
}, },
) )
// scopes that are used to qualify resources in the API
namespaceAsQueryParam := meta.RESTScope{
Name: "namespace",
ParamName: "namespace",
ParamPath: false,
ParamDescription: "object name and auth scope, such as for teams and projects",
}
namespaceAsPathParam := meta.RESTScope{
Name: "namespace",
ParamName: "ns",
ParamPath: true,
ParamDescription: "object name and auth scope, such as for teams and projects",
}
rootScope := meta.RESTScope{
Name: "root",
ParamName: "",
ParamPath: true,
}
// list of versions we support on the server // list of versions we support on the server
versions := []string{"v1beta1", "v1beta2", "v1beta3"} versions := []string{"v1beta1", "v1beta2", "v1beta3"}
@ -133,9 +114,9 @@ func init() {
// backwards compatibility, prior to v1beta3, we identified the namespace as a query parameter // backwards compatibility, prior to v1beta3, we identified the namespace as a query parameter
versionToNamespaceScope := map[string]meta.RESTScope{ versionToNamespaceScope := map[string]meta.RESTScope{
"v1beta1": namespaceAsQueryParam, "v1beta1": meta.RESTScopeNamespaceLegacy,
"v1beta2": namespaceAsQueryParam, "v1beta2": meta.RESTScopeNamespaceLegacy,
"v1beta3": namespaceAsPathParam, "v1beta3": meta.RESTScopeNamespace,
} }
// the list of kinds that are scoped at the root of the api hierarchy // the list of kinds that are scoped at the root of the api hierarchy
@ -155,7 +136,7 @@ func init() {
scope := versionToNamespaceScope[version] scope := versionToNamespaceScope[version]
_, found = kindToRootScope[kind] _, found = kindToRootScope[kind]
if found { if found {
scope = rootScope scope = meta.RESTScopeRoot
} }
mapper.Add(scope, kind, version, mixedCase) mapper.Add(scope, kind, version, mixedCase)
} }

View File

@ -94,19 +94,26 @@ type MetadataAccessor interface {
runtime.ResourceVersioner runtime.ResourceVersioner
} }
// RESTScope contains the information needed to deal with REST Resources that are in a resource hierarchy type RESTScopeName string
type RESTScope struct {
// Name of the scope (e.g. "cluster", "namespace", etc.) const (
Name string RESTScopeNameNamespace RESTScopeName = "namespace"
RESTScopeNameRoot RESTScopeName = "root"
)
// RESTScope contains the information needed to deal with REST resources that are in a resource hierarchy
type RESTScope interface {
// Name of the scope
Name() RESTScopeName
// ParamName is the optional name of the parameter that should be inserted in the resource url // ParamName is the optional name of the parameter that should be inserted in the resource url
// If empty, no param will be inserted // If empty, no param will be inserted
ParamName string ParamName() string
// ParamPath is a boolean that controls how the parameter is manifested in resource paths // ParamPath is a boolean that controls how the parameter is manifested in resource paths
// If true, this parameter is encoded in path (i.e. /{paramName}/{paramValue}) // If true, this parameter is encoded in path (i.e. /{paramName}/{paramValue})
// If false, this parameter is encoded in query (i.e. ?{paramName}={paramValue}) // If false, this parameter is encoded in query (i.e. ?{paramName}={paramValue})
ParamPath bool ParamPath() bool
// ParamDescription is the optional description to use to document the parameter in api documentation // ParamDescription is the optional description to use to document the parameter in api documentation
ParamDescription string ParamDescription() string
} }
// RESTMapping contains the information needed to deal with objects of a specific // RESTMapping contains the information needed to deal with objects of a specific

View File

@ -21,6 +21,45 @@ import (
"strings" "strings"
) )
// Implements RESTScope interface
type restScope struct {
name RESTScopeName
paramName string
paramPath bool
paramDescription string
}
func (r *restScope) Name() RESTScopeName {
return r.name
}
func (r *restScope) ParamName() string {
return r.paramName
}
func (r *restScope) ParamPath() bool {
return r.paramPath
}
func (r *restScope) ParamDescription() string {
return r.paramDescription
}
var RESTScopeNamespaceLegacy = &restScope{
name: RESTScopeNameNamespace,
paramName: "namespace",
paramPath: false,
paramDescription: "object name and auth scope, such as for teams and projects",
}
var RESTScopeNamespace = &restScope{
name: RESTScopeNameNamespace,
paramName: "namespace",
paramPath: true,
paramDescription: "object name and auth scope, such as for teams and projects",
}
var RESTScopeRoot = &restScope{
name: RESTScopeNameRoot,
}
// typeMeta is used as a key for lookup in the mapping between REST path and // typeMeta is used as a key for lookup in the mapping between REST path and
// API object. // API object.
type typeMeta struct { type typeMeta struct {
@ -89,6 +128,9 @@ func (m *DefaultRESTMapper) Add(scope RESTScope, kind string, version string, mi
// kindToResource converts Kind to a resource name. // kindToResource converts Kind to a resource name.
func kindToResource(kind string, mixedCase bool) (plural, singular string) { func kindToResource(kind string, mixedCase bool) (plural, singular string) {
if len(kind) == 0 {
return
}
if mixedCase { if mixedCase {
// Legacy support for mixed case names // Legacy support for mixed case names
singular = strings.ToLower(kind[:1]) + kind[1:] singular = strings.ToLower(kind[:1]) + kind[1:]

View File

@ -75,10 +75,7 @@ func TestRESTMapperVersionAndKindForResource(t *testing.T) {
} }
for i, testCase := range testCases { for i, testCase := range testCases {
mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces) mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces)
scheme := runtime.NewScheme() mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
scheme.AddKnownTypes("test", &InternalObject{})
mapper.Add(scheme, testCase.MixedCase, "test")
v, k, err := mapper.VersionAndKindForResource(testCase.Resource) v, k, err := mapper.VersionAndKindForResource(testCase.Resource)
hasErr := err != nil hasErr := err != nil
if hasErr != testCase.Err { if hasErr != testCase.Err {
@ -150,10 +147,7 @@ func TestRESTMapperRESTMapping(t *testing.T) {
} }
for i, testCase := range testCases { for i, testCase := range testCases {
mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces) mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces)
scheme := runtime.NewScheme() mapper.Add(RESTScopeNamespace, "InternalObject", "test", testCase.MixedCase)
scheme.AddKnownTypes("test", &InternalObject{})
mapper.Add(scheme, testCase.MixedCase, "test")
mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...) mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...)
hasErr := err != nil hasErr := err != nil
if hasErr != testCase.Err { if hasErr != testCase.Err {
@ -180,11 +174,8 @@ func TestRESTMapperRESTMapping(t *testing.T) {
func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) { func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, fakeInterfaces) mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, fakeInterfaces)
scheme := runtime.NewScheme() mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
scheme.AddKnownTypes("test1", &InternalObject{}) mapper.Add(RESTScopeNamespace, "OtherObject", "test2", false)
scheme.AddKnownTypeWithName("test2", "OtherObject", &InternalObject{})
scheme.AddKnownTypeWithName("test3", "OtherObject", &InternalObject{})
mapper.Add(scheme, false, "test1", "test2")
// pick default matching object kind based on search order // pick default matching object kind based on search order
mapping, err := mapper.RESTMapping("OtherObject") mapping, err := mapper.RESTMapping("OtherObject")
@ -236,10 +227,7 @@ func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) { func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, unmatchedVersionInterfaces) mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, unmatchedVersionInterfaces)
scheme := runtime.NewScheme() mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
scheme.AddKnownTypes("test1", &InternalObject{})
mapper.Add(scheme, false, "test1")
_, err := mapper.RESTMapping("InternalObject", "test1") _, err := mapper.RESTMapping("InternalObject", "test1")
if err == nil { if err == nil {
t.Errorf("unexpected non-error") t.Errorf("unexpected non-error")

View File

@ -29,7 +29,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission" "github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "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/healthz" "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@ -56,9 +56,9 @@ type defaultAPIServer struct {
// as RESTful resources at prefix, serialized by codec, and also includes the support // as RESTful resources at prefix, serialized by codec, and also includes the support
// http resources. // http resources.
// Note: This method is used only in tests. // Note: This method is used only in tests.
func Handle(storage map[string]RESTStorage, codec runtime.Codec, root string, version string, selfLinker runtime.SelfLinker, admissionControl admission.Interface) http.Handler { func Handle(storage map[string]RESTStorage, codec runtime.Codec, root string, version string, selfLinker runtime.SelfLinker, admissionControl admission.Interface, mapper meta.RESTMapper) http.Handler {
prefix := root + "/" + version prefix := root + "/" + version
group := NewAPIGroupVersion(storage, codec, prefix, selfLinker, admissionControl) group := NewAPIGroupVersion(storage, codec, prefix, selfLinker, admissionControl, mapper)
container := restful.NewContainer() container := restful.NewContainer()
mux := container.ServeMux mux := container.ServeMux
group.InstallREST(container, mux, root, version) group.InstallREST(container, mux, root, version)
@ -77,6 +77,7 @@ func Handle(storage map[string]RESTStorage, codec runtime.Codec, root string, ve
// TODO: consider migrating this to go-restful which is a more full-featured version of the same thing. // TODO: consider migrating this to go-restful which is a more full-featured version of the same thing.
type APIGroupVersion struct { type APIGroupVersion struct {
handler RESTHandler handler RESTHandler
mapper meta.RESTMapper
} }
// NewAPIGroupVersion returns an object that will serve a set of REST resources and their // NewAPIGroupVersion returns an object that will serve a set of REST resources and their
@ -84,15 +85,18 @@ type APIGroupVersion struct {
// This is a helper method for registering multiple sets of REST handlers under different // This is a helper method for registering multiple sets of REST handlers under different
// prefixes onto a server. // prefixes onto a server.
// TODO: add multitype codec serialization // TODO: add multitype codec serialization
func NewAPIGroupVersion(storage map[string]RESTStorage, codec runtime.Codec, canonicalPrefix string, selfLinker runtime.SelfLinker, admissionControl admission.Interface) *APIGroupVersion { func NewAPIGroupVersion(storage map[string]RESTStorage, codec runtime.Codec, canonicalPrefix string, selfLinker runtime.SelfLinker, admissionControl admission.Interface, mapper meta.RESTMapper) *APIGroupVersion {
return &APIGroupVersion{RESTHandler{ return &APIGroupVersion{
storage: storage, handler: RESTHandler{
codec: codec, storage: storage,
canonicalPrefix: canonicalPrefix, codec: codec,
selfLinker: selfLinker, canonicalPrefix: canonicalPrefix,
ops: NewOperations(), selfLinker: selfLinker,
admissionControl: admissionControl, ops: NewOperations(),
}} admissionControl: admissionControl,
},
mapper: mapper,
}
} }
// This magic incantation returns *ptrToObject for an arbitrary pointer // This magic incantation returns *ptrToObject for an arbitrary pointer
@ -100,7 +104,7 @@ func indirectArbitraryPointer(ptrToObject interface{}) interface{} {
return reflect.Indirect(reflect.ValueOf(ptrToObject)).Interface() return reflect.Indirect(reflect.ValueOf(ptrToObject)).Interface()
} }
func registerResourceHandlers(ws *restful.WebService, version string, path string, storage RESTStorage, h restful.RouteFunction) error { func registerResourceHandlers(ws *restful.WebService, version string, path string, storage RESTStorage, h restful.RouteFunction, mapper meta.RESTMapper) error {
object := storage.New() object := storage.New()
_, kind, err := api.Scheme.ObjectVersionAndKind(object) _, kind, err := api.Scheme.ObjectVersionAndKind(object)
if err != nil { if err != nil {
@ -112,7 +116,6 @@ func registerResourceHandlers(ws *restful.WebService, version string, path strin
} }
versionedObject := indirectArbitraryPointer(versionedPtr) versionedObject := indirectArbitraryPointer(versionedPtr)
mapper := latest.RESTMapper
mapping, err := mapper.RESTMapping(kind, version) mapping, err := mapper.RESTMapping(kind, version)
if err != nil { if err != nil {
glog.V(1).Infof("OH NOES kind %s version %s err: %v", kind, version, err) glog.V(1).Infof("OH NOES kind %s version %s err: %v", kind, version, err)
@ -124,9 +127,9 @@ func registerResourceHandlers(ws *restful.WebService, version string, path strin
// check if this // check if this
scope := mapping.Scope scope := mapping.Scope
var scopeParam *restful.Parameter var scopeParam *restful.Parameter
if len(scope.ParamName) > 0 && scope.ParamPath { if len(scope.ParamName()) > 0 && scope.ParamPath() {
path = scope.ParamName + "/{" + scope.ParamName + "}/" + path path = scope.ParamName() + "/{" + scope.ParamName() + "}/" + path
scopeParam = ws.PathParameter(scope.ParamName, scope.ParamDescription).DataType("string") scopeParam = ws.PathParameter(scope.ParamName(), scope.ParamDescription()).DataType("string")
} }
glog.V(5).Infof("Installing version=/%s, kind=/%s, path=/%s", version, kind, path) glog.V(5).Infof("Installing version=/%s, kind=/%s, path=/%s", version, kind, path)
@ -263,7 +266,7 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container, mux Mux, roo
registrationErrors := make([]error, 0) registrationErrors := make([]error, 0)
for path, storage := range g.handler.storage { for path, storage := range g.handler.storage {
if err := registerResourceHandlers(ws, version, path, storage, h); err != nil { if err := registerResourceHandlers(ws, version, path, storage, h, g.mapper); err != nil {
registrationErrors = append(registrationErrors, err) registrationErrors = append(registrationErrors, err)
} }
} }

View File

@ -93,7 +93,15 @@ func init() {
return interfaces, true return interfaces, true
}, },
) )
defMapper.Add(api.Scheme, true, versions...) // 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) {
mixedCase := true
scope := meta.RESTScopeNamespaceLegacy
defMapper.Add(scope, kind, version, mixedCase)
}
}
mapper = defMapper mapper = defMapper
admissionControl = admit.NewAlwaysAdmit() admissionControl = admit.NewAlwaysAdmit()
} }
@ -270,7 +278,7 @@ func TestNotFound(t *testing.T) {
} }
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": &SimpleRESTStorage{}, "foo": &SimpleRESTStorage{},
}, codec, "/prefix", testVersion, selfLinker, admissionControl) }, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
client := http.Client{} client := http.Client{}
@ -287,6 +295,7 @@ func TestNotFound(t *testing.T) {
if response.StatusCode != v.Status { if response.StatusCode != v.Status {
t.Errorf("Expected %d for %s (%s), Got %#v", v.Status, v.Method, k, response) t.Errorf("Expected %d for %s (%s), Got %#v", v.Status, v.Method, k, response)
t.Errorf("MAPPER: %v", mapper)
} }
} }
} }
@ -315,7 +324,7 @@ func TestMethodNotAllowed(t *testing.T) {
} }
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": UnimplementedRESTStorage{}, "foo": UnimplementedRESTStorage{},
}, codec, "/prefix", testVersion, selfLinker, admissionControl) }, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
client := http.Client{} client := http.Client{}
@ -354,7 +363,7 @@ func TestMethodNotAllowed(t *testing.T) {
} }
func TestVersion(t *testing.T) { func TestVersion(t *testing.T) {
handler := Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
client := http.Client{} client := http.Client{}
@ -389,7 +398,7 @@ func TestSimpleList(t *testing.T) {
namespace: "other", namespace: "other",
expectedSet: "/prefix/version/simple?namespace=other", expectedSet: "/prefix/version/simple?namespace=other",
} }
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -412,7 +421,7 @@ func TestErrorList(t *testing.T) {
errors: map[string]error{"list": fmt.Errorf("test Error")}, errors: map[string]error{"list": fmt.Errorf("test Error")},
} }
storage["simple"] = &simpleStorage storage["simple"] = &simpleStorage
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -438,7 +447,7 @@ func TestNonEmptyList(t *testing.T) {
}, },
} }
storage["simple"] = &simpleStorage storage["simple"] = &simpleStorage
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -484,7 +493,7 @@ func TestGet(t *testing.T) {
expectedSet: "/prefix/version/simple/id", expectedSet: "/prefix/version/simple/id",
} }
storage["simple"] = &simpleStorage storage["simple"] = &simpleStorage
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -509,7 +518,7 @@ func TestGetMissing(t *testing.T) {
errors: map[string]error{"get": apierrs.NewNotFound("simple", "id")}, errors: map[string]error{"get": apierrs.NewNotFound("simple", "id")},
} }
storage["simple"] = &simpleStorage storage["simple"] = &simpleStorage
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -528,7 +537,7 @@ func TestDelete(t *testing.T) {
simpleStorage := SimpleRESTStorage{} simpleStorage := SimpleRESTStorage{}
ID := "id" ID := "id"
storage["simple"] = &simpleStorage storage["simple"] = &simpleStorage
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -549,7 +558,7 @@ func TestDeleteInvokesAdmissionControl(t *testing.T) {
simpleStorage := SimpleRESTStorage{} simpleStorage := SimpleRESTStorage{}
ID := "id" ID := "id"
storage["simple"] = &simpleStorage storage["simple"] = &simpleStorage
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, deny.NewAlwaysDeny()) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, deny.NewAlwaysDeny(), mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -571,7 +580,7 @@ func TestDeleteMissing(t *testing.T) {
errors: map[string]error{"delete": apierrs.NewNotFound("simple", ID)}, errors: map[string]error{"delete": apierrs.NewNotFound("simple", ID)},
} }
storage["simple"] = &simpleStorage storage["simple"] = &simpleStorage
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -596,7 +605,7 @@ func TestUpdate(t *testing.T) {
t: t, t: t,
expectedSet: "/prefix/version/simple/" + ID, expectedSet: "/prefix/version/simple/" + ID,
} }
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -633,7 +642,7 @@ func TestUpdateInvokesAdmissionControl(t *testing.T) {
t: t, t: t,
expectedSet: "/prefix/version/simple/" + ID, expectedSet: "/prefix/version/simple/" + ID,
} }
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, deny.NewAlwaysDeny()) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, deny.NewAlwaysDeny(), mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -664,7 +673,7 @@ func TestUpdateMissing(t *testing.T) {
errors: map[string]error{"update": apierrs.NewNotFound("simple", ID)}, errors: map[string]error{"update": apierrs.NewNotFound("simple", ID)},
} }
storage["simple"] = &simpleStorage storage["simple"] = &simpleStorage
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -695,7 +704,7 @@ func TestCreateNotFound(t *testing.T) {
// See https://github.com/GoogleCloudPlatform/kubernetes/pull/486#discussion_r15037092. // See https://github.com/GoogleCloudPlatform/kubernetes/pull/486#discussion_r15037092.
errors: map[string]error{"create": apierrs.NewNotFound("simple", "id")}, errors: map[string]error{"create": apierrs.NewNotFound("simple", "id")},
}, },
}, codec, "/prefix", testVersion, selfLinker, admissionControl) }, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
client := http.Client{} client := http.Client{}
@ -759,11 +768,11 @@ func TestCreate(t *testing.T) {
t: t, t: t,
name: "bar", name: "bar",
namespace: "other", namespace: "other",
expectedSet: "/prefix/version/ns/other/foo/bar", expectedSet: "/prefix/version/foo/bar?namespace=other",
} }
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": &storage, "foo": &storage,
}, codec, "/prefix", testVersion, selfLinker, admissionControl) }, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
client := http.Client{} client := http.Client{}
@ -772,7 +781,7 @@ func TestCreate(t *testing.T) {
Other: "bar", Other: "bar",
} }
data, _ := codec.Encode(simple) data, _ := codec.Encode(simple)
request, err := http.NewRequest("POST", server.URL+"/prefix/version/ns/other/foo", bytes.NewBuffer(data)) request, err := http.NewRequest("POST", server.URL+"/prefix/version/foo?namespace=other", bytes.NewBuffer(data))
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
@ -817,11 +826,11 @@ func TestCreateInvokesAdmissionControl(t *testing.T) {
t: t, t: t,
name: "bar", name: "bar",
namespace: "other", namespace: "other",
expectedSet: "/prefix/version/ns/other/foo/bar", expectedSet: "/prefix/version/foo/bar?namespace=other",
} }
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": &storage, "foo": &storage,
}, codec, "/prefix", testVersion, selfLinker, deny.NewAlwaysDeny()) }, codec, "/prefix", testVersion, selfLinker, deny.NewAlwaysDeny(), mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
client := http.Client{} client := http.Client{}
@ -830,7 +839,7 @@ func TestCreateInvokesAdmissionControl(t *testing.T) {
Other: "bar", Other: "bar",
} }
data, _ := codec.Encode(simple) data, _ := codec.Encode(simple)
request, err := http.NewRequest("POST", server.URL+"/prefix/version/ns/other/foo", bytes.NewBuffer(data)) request, err := http.NewRequest("POST", server.URL+"/prefix/version/foo?namespace=other", bytes.NewBuffer(data))
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
@ -881,7 +890,7 @@ func TestDelayReturnsError(t *testing.T) {
return nil, apierrs.NewAlreadyExists("foo", "bar") return nil, apierrs.NewAlreadyExists("foo", "bar")
}, },
} }
handler := Handle(map[string]RESTStorage{"foo": &storage}, codec, "/prefix", testVersion, selfLinker, admissionControl) handler := Handle(map[string]RESTStorage{"foo": &storage}, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -947,7 +956,7 @@ func TestCreateTimeout(t *testing.T) {
} }
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": &storage, "foo": &storage,
}, codec, "/prefix", testVersion, selfLinker, admissionControl) }, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -979,7 +988,7 @@ func TestCORSAllowedOrigins(t *testing.T) {
} }
handler := CORS( handler := CORS(
Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker, admissionControl), Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker, admissionControl, mapper),
allowedOriginRegexps, nil, nil, "true", allowedOriginRegexps, nil, nil, "true",
) )
server := httptest.NewServer(handler) server := httptest.NewServer(handler)

View File

@ -230,7 +230,7 @@ func TestProxy(t *testing.T) {
} }
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": simpleStorage, "foo": simpleStorage,
}, codec, "/prefix", "version", selfLinker, admissionControl) }, codec, "/prefix", "version", selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()

View File

@ -31,7 +31,7 @@ func TestRedirect(t *testing.T) {
} }
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": simpleStorage, "foo": simpleStorage,
}, codec, "/prefix", "version", selfLinker, admissionControl) }, codec, "/prefix", "version", selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -84,7 +84,7 @@ func TestRedirectWithNamespaces(t *testing.T) {
} }
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": simpleStorage, "foo": simpleStorage,
}, codec, "/prefix", "version", selfLinker, admissionControl) }, codec, "/prefix", "version", selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()

View File

@ -50,7 +50,7 @@ func TestWatchWebsocket(t *testing.T) {
_ = ResourceWatcher(simpleStorage) // Give compile error if this doesn't work. _ = ResourceWatcher(simpleStorage) // Give compile error if this doesn't work.
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": simpleStorage, "foo": simpleStorage,
}, codec, "/api", "version", selfLinker, admissionControl) }, codec, "/api", "version", selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -104,7 +104,7 @@ func TestWatchHTTP(t *testing.T) {
simpleStorage := &SimpleRESTStorage{} simpleStorage := &SimpleRESTStorage{}
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": simpleStorage, "foo": simpleStorage,
}, codec, "/api", "version", selfLinker, admissionControl) }, codec, "/api", "version", selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
client := http.Client{} client := http.Client{}
@ -167,7 +167,7 @@ func TestWatchParamParsing(t *testing.T) {
simpleStorage := &SimpleRESTStorage{} simpleStorage := &SimpleRESTStorage{}
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": simpleStorage, "foo": simpleStorage,
}, codec, "/api", "version", selfLinker, admissionControl) }, codec, "/api", "version", selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
@ -239,7 +239,7 @@ func TestWatchProtocolSelection(t *testing.T) {
simpleStorage := &SimpleRESTStorage{} simpleStorage := &SimpleRESTStorage{}
handler := Handle(map[string]RESTStorage{ handler := Handle(map[string]RESTStorage{
"foo": simpleStorage, "foo": simpleStorage,
}, codec, "/api", "version", selfLinker, admissionControl) }, codec, "/api", "version", selfLinker, admissionControl, mapper)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
defer server.CloseClientConnections() defer server.CloseClientConnections()

View File

@ -66,7 +66,13 @@ func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
MetadataAccessor: meta.NewAccessor(), MetadataAccessor: meta.NewAccessor(),
}, (version == "unlikelyversion") }, (version == "unlikelyversion")
}) })
mapper.Add(scheme, false, "unlikelyversion") for _, version := range []string{"unlikelyversion"} {
for kind := range scheme.KnownTypes(version) {
mixedCase := false
scope := meta.RESTScopeNamespace
mapper.Add(scope, kind, version, mixedCase)
}
}
return scheme, mapper, codec return scheme, mapper, codec
} }

View File

@ -30,6 +30,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"

View File

@ -54,9 +54,6 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
} }
name, _ := mapping.MetadataAccessor.Name(obj) name, _ := mapping.MetadataAccessor.Name(obj)
namespace, _ := mapping.MetadataAccessor.Namespace(obj) namespace, _ := mapping.MetadataAccessor.Namespace(obj)
if mapping.Scope.Name != "namespace" {
namespace = ""
}
resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj) resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj)
return &Info{ return &Info{
Mapping: mapping, Mapping: mapping,

View File

@ -31,6 +31,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission" "github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
@ -491,25 +492,25 @@ func (m *Master) getServersToValidate(c *Config) map[string]apiserver.Server {
} }
// api_v1beta1 returns the resources and codec for API version v1beta1. // api_v1beta1 returns the resources and codec for API version v1beta1.
func (m *Master) api_v1beta1() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker, admission.Interface) { func (m *Master) api_v1beta1() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker, admission.Interface, meta.RESTMapper) {
storage := make(map[string]apiserver.RESTStorage) storage := make(map[string]apiserver.RESTStorage)
for k, v := range m.storage { for k, v := range m.storage {
storage[k] = v storage[k] = v
} }
return storage, v1beta1.Codec, "/api/v1beta1", latest.SelfLinker, m.admissionControl return storage, v1beta1.Codec, "/api/v1beta1", latest.SelfLinker, m.admissionControl, latest.RESTMapper
} }
// api_v1beta2 returns the resources and codec for API version v1beta2. // api_v1beta2 returns the resources and codec for API version v1beta2.
func (m *Master) api_v1beta2() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker, admission.Interface) { func (m *Master) api_v1beta2() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker, admission.Interface, meta.RESTMapper) {
storage := make(map[string]apiserver.RESTStorage) storage := make(map[string]apiserver.RESTStorage)
for k, v := range m.storage { for k, v := range m.storage {
storage[k] = v storage[k] = v
} }
return storage, v1beta2.Codec, "/api/v1beta2", latest.SelfLinker, m.admissionControl return storage, v1beta2.Codec, "/api/v1beta2", latest.SelfLinker, m.admissionControl, latest.RESTMapper
} }
// api_v1beta3 returns the resources and codec for API version v1beta3. // api_v1beta3 returns the resources and codec for API version v1beta3.
func (m *Master) api_v1beta3() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker, admission.Interface) { func (m *Master) api_v1beta3() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker, admission.Interface, meta.RESTMapper) {
storage := make(map[string]apiserver.RESTStorage) storage := make(map[string]apiserver.RESTStorage)
for k, v := range m.storage { for k, v := range m.storage {
if k == "minions" { if k == "minions" {
@ -517,5 +518,5 @@ func (m *Master) api_v1beta3() (map[string]apiserver.RESTStorage, runtime.Codec,
} }
storage[strings.ToLower(k)] = v storage[strings.ToLower(k)] = v
} }
return storage, v1beta3.Codec, "/api/v1beta3", latest.SelfLinker, m.admissionControl return storage, v1beta3.Codec, "/api/v1beta3", latest.SelfLinker, m.admissionControl, latest.RESTMapper
} }