rest mappings cannot logically be object converters

This commit is contained in:
David Eads 2018-04-24 18:31:41 -04:00
parent d4b678036f
commit 6900f8856f
44 changed files with 232 additions and 270 deletions

View File

@ -103,11 +103,6 @@ func TestRESTMapper(t *testing.T) {
t.Errorf("incorrect version: %v", mapping) t.Errorf("incorrect version: %v", mapping)
} }
interfaces, _ := legacyscheme.Registry.GroupOrDie(internal.GroupName).InterfacesFor(version)
if mapping.ObjectConvertor != interfaces.ObjectConvertor {
t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces)
}
rc := &internal.ReplicationController{ObjectMeta: metav1.ObjectMeta{Name: "foo"}} rc := &internal.ReplicationController{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
name, err := meta.NewAccessor().Name(rc) name, err := meta.NewAccessor().Name(rc)
if err != nil { if err != nil {

View File

@ -248,7 +248,7 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
var outputObj runtime.Object var outputObj runtime.Object
var obj runtime.Object var obj runtime.Object
obj, err = info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion()) obj, err = info.ObjectConverter.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil { if err != nil {
return err return err
} }

View File

@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -230,10 +231,11 @@ func (o *AutoscaleOptions) Run() error {
} }
resourceMapper := &resource.Mapper{ resourceMapper := &resource.Mapper{
ObjectTyper: o.Typer, ObjectTyper: o.Typer,
RESTMapper: o.Mapper, ObjectConverter: legacyscheme.Scheme,
ClientMapper: resource.ClientMapperFunc(o.ClientForMapping), RESTMapper: o.Mapper,
Decoder: cmdutil.InternalVersionDecoder(), ClientMapper: resource.ClientMapperFunc(o.ClientForMapping),
Decoder: cmdutil.InternalVersionDecoder(),
} }
hpa, err := resourceMapper.InfoForObject(object, nil) hpa, err := resourceMapper.InfoForObject(object, nil)
if err != nil { if err != nil {

View File

@ -282,7 +282,7 @@ func asVersionedObjects(infos []*resource.Info, specifiedOutputVersion schema.Gr
} }
} }
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, targetVersions...) converted, err := tryConvert(scheme.Scheme, info.Object, targetVersions...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -23,6 +23,7 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/create", importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/create",
visibility = ["//build/visible_to:pkg_kubectl_cmd_create_CONSUMERS"], visibility = ["//build/visible_to:pkg_kubectl_cmd_create_CONSUMERS"],
deps = [ deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
kruntime "k8s.io/apimachinery/pkg/runtime" kruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -404,9 +405,10 @@ func RunCreateSubcommand(f cmdutil.Factory, options *CreateSubcommandOptions) er
return err return err
} }
resourceMapper := &resource.Mapper{ resourceMapper := &resource.Mapper{
ObjectTyper: typer, ObjectTyper: typer,
RESTMapper: mapper, ObjectConverter: legacyscheme.Scheme,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
} }
info, err := resourceMapper.InfoForObject(obj, nil) info, err := resourceMapper.InfoForObject(obj, nil)
if err != nil { if err != nil {

View File

@ -718,7 +718,7 @@ func (o *DrainOptions) RunCordonOrUncordon(desired bool) error {
for _, nodeInfo := range o.nodeInfos { for _, nodeInfo := range o.nodeInfos {
if nodeInfo.Mapping.GroupVersionKind.Kind == "Node" { if nodeInfo.Mapping.GroupVersionKind.Kind == "Node" {
obj, err := nodeInfo.Mapping.ConvertToVersion(nodeInfo.Object, nodeInfo.Mapping.GroupVersionKind.GroupVersion()) obj, err := nodeInfo.ObjectConverter.ConvertToVersion(nodeInfo.Object, nodeInfo.Mapping.GroupVersionKind.GroupVersion())
if err != nil { if err != nil {
fmt.Printf("error: unable to %s node %q: %v", cordonOrUncordon, nodeInfo.Name, err) fmt.Printf("error: unable to %s node %q: %v", cordonOrUncordon, nodeInfo.Name, err)
continue continue

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -311,10 +312,11 @@ func (o *ExposeServiceOptions) RunExpose(cmd *cobra.Command, args []string) erro
} }
resourceMapper := &resource.Mapper{ resourceMapper := &resource.Mapper{
ObjectTyper: o.Typer, ObjectTyper: o.Typer,
RESTMapper: o.Mapper, ObjectConverter: legacyscheme.Scheme,
ClientMapper: resource.ClientMapperFunc(o.ClientForMapping), RESTMapper: o.Mapper,
Decoder: cmdutil.InternalVersionDecoder(), ClientMapper: resource.ClientMapperFunc(o.ClientForMapping),
Decoder: cmdutil.InternalVersionDecoder(),
} }
info, err = resourceMapper.InfoForObject(object, nil) info, err = resourceMapper.InfoForObject(object, nil)
if err != nil { if err != nil {

View File

@ -24,6 +24,7 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/get", importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/get",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library",
@ -66,7 +67,6 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library", "//pkg/api/testing:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/core/v1:go_default_library", "//pkg/apis/core/v1:go_default_library",
@ -77,8 +77,8 @@ go_test(
"//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/scheme:go_default_library", "//pkg/kubectl/scheme:go_default_library",
"//pkg/printers:go_default_library", "//pkg/printers:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",

View File

@ -38,6 +38,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
@ -428,7 +429,14 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
lastMapping = mapping lastMapping = mapping
} }
printer.PrintObj(info.AsInternal(), w) internalObj, err := legacyscheme.Scheme.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion())
if err != nil {
// if there's an error, try to print what you have (mirrors old behavior).
glog.V(1).Info(err)
printer.PrintObj(info.Object, w)
} else {
printer.PrintObj(internalObj, w)
}
} }
w.Flush() w.Flush()
if nonEmptyObjCount == 0 && !o.IgnoreNotFound { if nonEmptyObjCount == 0 && !o.IgnoreNotFound {
@ -542,7 +550,7 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
if !o.IsGeneric { if !o.IsGeneric {
// printing always takes the internal version, but the watch event uses externals // printing always takes the internal version, but the watch event uses externals
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion() internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
objToPrint = attemptToConvertToInternal(objToPrint, mapping, internalGV) objToPrint = attemptToConvertToInternal(objToPrint, legacyscheme.Scheme, internalGV)
} }
if err := printer.PrintObj(objToPrint, writer); err != nil { if err := printer.PrintObj(objToPrint, writer); err != nil {
return fmt.Errorf("unable to output the provided object: %v", err) return fmt.Errorf("unable to output the provided object: %v", err)
@ -570,7 +578,7 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
// printing always takes the internal version, but the watch event uses externals // printing always takes the internal version, but the watch event uses externals
// TODO fix printing to use server-side or be version agnostic // TODO fix printing to use server-side or be version agnostic
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion() internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
if err := printer.PrintObj(attemptToConvertToInternal(e.Object, mapping, internalGV), o.Out); err != nil { if err := printer.PrintObj(attemptToConvertToInternal(e.Object, legacyscheme.Scheme, internalGV), o.Out); err != nil {
return false, err return false, err
} }
return false, nil return false, nil

View File

@ -27,24 +27,22 @@ import (
"strings" "strings"
"testing" "testing"
api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming" "k8s.io/apimachinery/pkg/runtime/serializer/streaming"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake" "k8s.io/client-go/rest/fake"
restclientwatch "k8s.io/client-go/rest/watch" restclientwatch "k8s.io/client-go/rest/watch"
"k8s.io/kube-openapi/pkg/util/proto" "k8s.io/kube-openapi/pkg/util/proto"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing" apitesting "k8s.io/kubernetes/pkg/api/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/v1" "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -104,11 +102,11 @@ func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList)
Items: []api.Pod{ Items: []api.Pod{
{ {
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
{ {
ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"}, ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
}, },
} }
@ -126,6 +124,8 @@ func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList)
}, },
}, },
} }
one := int32(1)
rc := &api.ReplicationControllerList{ rc := &api.ReplicationControllerList{
ListMeta: metav1.ListMeta{ ListMeta: metav1.ListMeta{
ResourceVersion: "17", ResourceVersion: "17",
@ -134,7 +134,7 @@ func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList)
{ {
ObjectMeta: metav1.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"}, ObjectMeta: metav1.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"},
Spec: api.ReplicationControllerSpec{ Spec: api.ReplicationControllerSpec{
Replicas: 1, Replicas: &one,
}, },
}, },
}, },
@ -193,25 +193,6 @@ func TestGetUnknownSchemaObject(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig() tf.ClientConfigVal = defaultClientConfig()
mapper, _ := tf.Object()
m, err := mapper.RESTMapping(schema.GroupKind{Group: "apitest", Kind: "Type"})
if err != nil {
t.Fatal(err)
}
convertedObj, err := m.ConvertToVersion(&unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "Type",
"apiVersion": "apitest/unlikelyversion",
"name": "foo",
},
}, schema.GroupVersion{Group: "apitest", Version: "unlikelyversion"})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(convertedObj, obj) {
t.Fatalf("unexpected conversion of unstructured object to structured: %s", diff.ObjectReflectDiff(convertedObj, obj))
}
streams, _, buf, _ := genericclioptions.NewTestIOStreams() streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdGet("kubectl", tf, streams) cmd := NewCmdGet("kubectl", tf, streams)
cmd.SetOutput(buf) cmd.SetOutput(buf)
@ -246,7 +227,9 @@ func TestGetUnknownSchemaObject(t *testing.T) {
func TestGetSchemaObject(t *testing.T) { func TestGetSchemaObject(t *testing.T) {
tf := cmdtesting.NewTestFactory() tf := cmdtesting.NewTestFactory()
defer tf.Cleanup() defer tf.Cleanup()
codec := testapi.Default.Codec() codec := legacyscheme.Codecs.LegacyCodec(schema.GroupVersion{Version: "v1"})
t.Logf("%v", string(runtime.EncodeOrDie(codec, &api.ReplicationController{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})))
tf.UnstructuredClient = &fake.RESTClient{ tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer, NegotiatedSerializer: unstructuredSerializer,
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.ReplicationController{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})}, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.ReplicationController{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})},
@ -401,15 +384,15 @@ func TestGetSortedObjects(t *testing.T) {
Items: []api.Pod{ Items: []api.Pod{
{ {
ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: "test", ResourceVersion: "10"}, ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: "test", ResourceVersion: "10"},
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
{ {
ObjectMeta: metav1.ObjectMeta{Name: "b", Namespace: "test", ResourceVersion: "11"}, ObjectMeta: metav1.ObjectMeta{Name: "b", Namespace: "test", ResourceVersion: "11"},
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
{ {
ObjectMeta: metav1.ObjectMeta{Name: "a", Namespace: "test", ResourceVersion: "9"}, ObjectMeta: metav1.ObjectMeta{Name: "a", Namespace: "test", ResourceVersion: "9"},
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
}, },
} }
@ -701,7 +684,6 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
"containers": null, "containers": null,
"dnsPolicy": "ClusterFirst", "dnsPolicy": "ClusterFirst",
"restartPolicy": "Always", "restartPolicy": "Always",
"schedulerName": "default-scheduler",
"securityContext": {}, "securityContext": {},
"terminationGracePeriodSeconds": 30 "terminationGracePeriodSeconds": 30
}, },
@ -720,7 +702,6 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
"containers": null, "containers": null,
"dnsPolicy": "ClusterFirst", "dnsPolicy": "ClusterFirst",
"restartPolicy": "Always", "restartPolicy": "Always",
"schedulerName": "default-scheduler",
"securityContext": {}, "securityContext": {},
"terminationGracePeriodSeconds": 30 "terminationGracePeriodSeconds": 30
}, },
@ -752,7 +733,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
} }
` `
if e, a := expected, buf.String(); e != a { if e, a := expected, buf.String(); e != a {
t.Errorf("expected %v, got %v", e, a) t.Errorf("did not match: %v", diff.StringDiff(e, a))
} }
} }
@ -896,7 +877,7 @@ func watchTestData() ([]api.Pod, []watch.Event) {
Namespace: "test", Namespace: "test",
ResourceVersion: "9", ResourceVersion: "9",
}, },
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
{ {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@ -904,7 +885,7 @@ func watchTestData() ([]api.Pod, []watch.Event) {
Namespace: "test", Namespace: "test",
ResourceVersion: "10", ResourceVersion: "10",
}, },
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
} }
events := []watch.Event{ events := []watch.Event{
@ -917,7 +898,7 @@ func watchTestData() ([]api.Pod, []watch.Event) {
Namespace: "test", Namespace: "test",
ResourceVersion: "9", ResourceVersion: "9",
}, },
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
}, },
{ {
@ -928,7 +909,7 @@ func watchTestData() ([]api.Pod, []watch.Event) {
Namespace: "test", Namespace: "test",
ResourceVersion: "10", ResourceVersion: "10",
}, },
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
}, },
// resource events // resource events
@ -940,7 +921,7 @@ func watchTestData() ([]api.Pod, []watch.Event) {
Namespace: "test", Namespace: "test",
ResourceVersion: "11", ResourceVersion: "11",
}, },
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
}, },
{ {
@ -951,7 +932,7 @@ func watchTestData() ([]api.Pod, []watch.Event) {
Namespace: "test", Namespace: "test",
ResourceVersion: "12", ResourceVersion: "12",
}, },
Spec: apitesting.DeepEqualSafePodSpec(), Spec: apitesting.V1DeepEqualSafePodSpec(),
}, },
}, },
} }

View File

@ -220,7 +220,12 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args
switch t := infos[0].AsVersioned().(type) { switch t := infos[0].AsVersioned().(type) {
case *v1.ReplicationController: case *v1.ReplicationController:
replicasDefaulted = t.Spec.Replicas == nil replicasDefaulted = t.Spec.Replicas == nil
newRc, _ = infos[0].AsInternal().(*api.ReplicationController)
// previous code ignored the error. Seem like it's very unlikely to fail, so ok for now.
uncastObj, err := legacyscheme.Scheme.ConvertToVersion(t, api.SchemeGroupVersion)
if err == nil {
newRc, _ = uncastObj.(*api.ReplicationController)
}
} }
if newRc == nil { if newRc == nil {
glog.V(4).Infof("Object %T is not a ReplicationController", infos[0].Object) glog.V(4).Infof("Object %T is not a ReplicationController", infos[0].Object)

View File

@ -32,6 +32,7 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
@ -673,10 +674,11 @@ func (o *RunOptions) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command
versioned := obj versioned := obj
if !o.DryRun { if !o.DryRun {
resourceMapper := &resource.Mapper{ resourceMapper := &resource.Mapper{
ObjectTyper: typer, ObjectTyper: typer,
RESTMapper: mapper, ObjectConverter: legacyscheme.Scheme,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), RESTMapper: mapper,
Decoder: cmdutil.InternalVersionDecoder(), ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: cmdutil.InternalVersionDecoder(),
} }
info, err := resourceMapper.InfoForObject(obj, nil) info, err := resourceMapper.InfoForObject(obj, nil)
if err != nil { if err != nil {

View File

@ -272,7 +272,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
} }
for _, info := range infos { for _, info := range infos {
versionedObject, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion()) versionedObject, err := info.ObjectConverter.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil { if err != nil {
return err return err
} }

View File

@ -233,7 +233,7 @@ func (o TaintOptions) RunTaint() error {
return err return err
} }
obj, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion()) obj, err := info.ObjectConverter.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil { if err != nil {
return err return err
} }

View File

@ -346,16 +346,18 @@ func (f *TestFactory) NewBuilder() *resource.Builder {
return resource.NewBuilder( return resource.NewBuilder(
&resource.Mapper{ &resource.Mapper{
RESTMapper: mapper, RESTMapper: mapper,
ObjectTyper: typer, ObjectTyper: typer,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: cmdutil.InternalVersionDecoder(), ObjectConverter: legacyscheme.Scheme,
Decoder: cmdutil.InternalVersionDecoder(),
}, },
&resource.Mapper{ &resource.Mapper{
RESTMapper: mapper, RESTMapper: mapper,
ObjectTyper: typer, ObjectTyper: typer,
ClientMapper: resource.ClientMapperFunc(f.UnstructuredClientForMapping), ClientMapper: resource.ClientMapperFunc(f.UnstructuredClientForMapping),
Decoder: unstructured.UnstructuredJSONScheme, ObjectConverter: unstructured.UnstructuredObjectConverter{},
Decoder: unstructured.UnstructuredJSONScheme,
}, },
f.CategoryExpander(), f.CategoryExpander(),
) )

View File

@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
scaleclient "k8s.io/client-go/scale" scaleclient "k8s.io/client-go/scale"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/plugins" "k8s.io/kubernetes/pkg/kubectl/plugins"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
@ -55,16 +56,18 @@ func (f *ring2Factory) NewBuilder() *resource.Builder {
return resource.NewBuilder( return resource.NewBuilder(
&resource.Mapper{ &resource.Mapper{
RESTMapper: mapper, RESTMapper: mapper,
ObjectTyper: typer, ObjectTyper: typer,
ClientMapper: clientMapperFunc, ObjectConverter: legacyscheme.Scheme,
Decoder: InternalVersionDecoder(), ClientMapper: clientMapperFunc,
Decoder: InternalVersionDecoder(),
}, },
&resource.Mapper{ &resource.Mapper{
RESTMapper: mapper, RESTMapper: mapper,
ObjectTyper: typer, ObjectTyper: typer,
ClientMapper: unstructuredClientMapperFunc, ObjectConverter: unstructured.UnstructuredObjectConverter{},
Decoder: unstructured.UnstructuredJSONScheme, ClientMapper: unstructuredClientMapperFunc,
Decoder: unstructured.UnstructuredJSONScheme,
}, },
categoryExpander, categoryExpander,
) )

View File

@ -474,10 +474,11 @@ func TestDiscoveryReplaceAliases(t *testing.T) {
mapper := NewShortcutExpander(testapi.Default.RESTMapper(), ds) mapper := NewShortcutExpander(testapi.Default.RESTMapper(), ds)
b := resource.NewBuilder( b := resource.NewBuilder(
&resource.Mapper{ &resource.Mapper{
RESTMapper: mapper, RESTMapper: mapper,
ObjectTyper: legacyscheme.Scheme, ObjectTyper: legacyscheme.Scheme,
ClientMapper: fakeClient(), ObjectConverter: legacyscheme.Scheme,
Decoder: testapi.Default.Codec(), ClientMapper: fakeClient(),
Decoder: testapi.Default.Codec(),
}, },
nil, nil,
categories.LegacyCategoryExpander, categories.LegacyCategoryExpander,

View File

@ -760,7 +760,7 @@ func (b *Builder) visitBySelector() *Result {
if mapping.Scope.Name() != meta.RESTScopeNameNamespace { if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = "" selectorNamespace = ""
} }
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, labelSelector, fieldSelector, b.export, b.includeUninitialized, b.limitChunks)) visitors = append(visitors, NewSelector(client, mapping, b.mapper.ObjectConverter, selectorNamespace, labelSelector, fieldSelector, b.export, b.includeUninitialized, b.limitChunks))
} }
if b.continueOnError { if b.continueOnError {
result.visitor = EagerVisitorList(visitors) result.visitor = EagerVisitorList(visitors)
@ -835,11 +835,12 @@ func (b *Builder) visitByResource() *Result {
} }
info := &Info{ info := &Info{
Client: client, Client: client,
Mapping: mapping, Mapping: mapping,
Namespace: selectorNamespace, ObjectConverter: b.mapper.ObjectConverter,
Name: tuple.Name, Namespace: selectorNamespace,
Export: b.export, Name: tuple.Name,
Export: b.export,
} }
items = append(items, info) items = append(items, info)
} }
@ -900,11 +901,12 @@ func (b *Builder) visitByName() *Result {
visitors := []Visitor{} visitors := []Visitor{}
for _, name := range b.names { for _, name := range b.names {
info := &Info{ info := &Info{
Client: client, Client: client,
Mapping: mapping, Mapping: mapping,
Namespace: selectorNamespace, ObjectConverter: b.mapper.ObjectConverter,
Name: name, Namespace: selectorNamespace,
Export: b.export, Name: name,
Export: b.export,
} }
visitors = append(visitors, info) visitors = append(visitors, info)
} }

View File

@ -270,10 +270,11 @@ func newDefaultBuilder() *Builder {
func newDefaultBuilderWith(client ClientMapper) *Builder { func newDefaultBuilderWith(client ClientMapper) *Builder {
return NewBuilder( return NewBuilder(
&Mapper{ &Mapper{
RESTMapper: restmapper, RESTMapper: restmapper,
ObjectTyper: scheme.Scheme, ObjectTyper: scheme.Scheme,
ClientMapper: client, ObjectConverter: scheme.Scheme,
Decoder: corev1Codec, ClientMapper: client,
Decoder: corev1Codec,
}, },
nil, nil,
categories.LegacyCategoryExpander, categories.LegacyCategoryExpander,

View File

@ -29,6 +29,8 @@ import (
// needed to create Info for arbitrary objects. // needed to create Info for arbitrary objects.
type Mapper struct { type Mapper struct {
runtime.ObjectTyper runtime.ObjectTyper
ObjectConverter runtime.ObjectConvertor
meta.RESTMapper meta.RESTMapper
ClientMapper ClientMapper
runtime.Decoder runtime.Decoder
@ -70,8 +72,9 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
resourceVersion, _ := metadataAccessor.ResourceVersion(obj) resourceVersion, _ := metadataAccessor.ResourceVersion(obj)
return &Info{ return &Info{
Client: client, Client: client,
Mapping: mapping, Mapping: mapping,
ObjectConverter: m.ObjectConverter,
Source: source, Source: source,
Namespace: namespace, Namespace: namespace,
@ -109,8 +112,9 @@ func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []schema.GroupV
namespace, _ := metadataAccessor.Namespace(obj) namespace, _ := metadataAccessor.Namespace(obj)
resourceVersion, _ := metadataAccessor.ResourceVersion(obj) resourceVersion, _ := metadataAccessor.ResourceVersion(obj)
return &Info{ return &Info{
Client: client, Client: client,
Mapping: mapping, Mapping: mapping,
ObjectConverter: m.ObjectConverter,
Namespace: namespace, Namespace: namespace,
Name: name, Name: name,
@ -199,7 +203,6 @@ func (m relaxedMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*me
return &meta.RESTMapping{ return &meta.RESTMapping{
GroupVersionKind: gk.WithVersion(versions[0]), GroupVersionKind: gk.WithVersion(versions[0]),
Scope: meta.RESTScopeRoot, Scope: meta.RESTScopeRoot,
ObjectConvertor: identityConvertor{},
}, nil }, nil
} }
return mapping, err return mapping, err
@ -211,7 +214,6 @@ func (m relaxedMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]
{ {
GroupVersionKind: gk.WithVersion(versions[0]), GroupVersionKind: gk.WithVersion(versions[0]),
Scope: meta.RESTScopeRoot, Scope: meta.RESTScopeRoot,
ObjectConvertor: identityConvertor{},
}, },
}, nil }, nil
} }

View File

@ -22,6 +22,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
) )
@ -29,6 +30,7 @@ import (
type Selector struct { type Selector struct {
Client RESTClient Client RESTClient
Mapping *meta.RESTMapping Mapping *meta.RESTMapping
ObjectConverter runtime.ObjectConvertor
Namespace string Namespace string
LabelSelector string LabelSelector string
FieldSelector string FieldSelector string
@ -38,10 +40,11 @@ type Selector struct {
} }
// NewSelector creates a resource selector which hides details of getting items by their label selector. // NewSelector creates a resource selector which hides details of getting items by their label selector.
func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace, labelSelector, fieldSelector string, export, includeUninitialized bool, limitChunks int64) *Selector { func NewSelector(client RESTClient, mapping *meta.RESTMapping, objectConverter runtime.ObjectConvertor, namespace, labelSelector, fieldSelector string, export, includeUninitialized bool, limitChunks int64) *Selector {
return &Selector{ return &Selector{
Client: client, Client: client,
Mapping: mapping, Mapping: mapping,
ObjectConverter: objectConverter,
Namespace: namespace, Namespace: namespace,
LabelSelector: labelSelector, LabelSelector: labelSelector,
FieldSelector: fieldSelector, FieldSelector: fieldSelector,
@ -91,8 +94,9 @@ func (r *Selector) Visit(fn VisitorFunc) error {
resourceVersion, _ := metadataAccessor.ResourceVersion(list) resourceVersion, _ := metadataAccessor.ResourceVersion(list)
nextContinueToken, _ := metadataAccessor.Continue(list) nextContinueToken, _ := metadataAccessor.Continue(list)
info := &Info{ info := &Info{
Client: r.Client, Client: r.Client,
Mapping: r.Mapping, Mapping: r.Mapping,
ObjectConverter: r.ObjectConverter,
Namespace: r.Namespace, Namespace: r.Namespace,
ResourceVersion: resourceVersion, ResourceVersion: resourceVersion,

View File

@ -78,6 +78,9 @@ type Info struct {
// Mapping may be nil if the object has no available metadata, but is still parseable // Mapping may be nil if the object has no available metadata, but is still parseable
// from disk. // from disk.
Mapping *meta.RESTMapping Mapping *meta.RESTMapping
// ObjectConverter allows conversion
ObjectConverter runtime.ObjectConvertor
// Namespace will be set if the object is namespaced and has a specified value. // Namespace will be set if the object is namespaced and has a specified value.
Namespace string Namespace string
Name string Name string
@ -180,23 +183,9 @@ func (i *Info) ResourceMapping() *meta.RESTMapping {
return i.Mapping return i.Mapping
} }
// Internal attempts to convert the provided object to an internal type or returns an error.
func (i *Info) Internal() (runtime.Object, error) {
return i.Mapping.ConvertToVersion(i.Object, i.Mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion())
}
// AsInternal returns the object in internal form if possible, or i.Object if it cannot be
// converted.
func (i *Info) AsInternal() runtime.Object {
if obj, err := i.Internal(); err == nil {
return obj
}
return i.Object
}
// Versioned returns the object as a Go type in the mapping's version or returns an error. // Versioned returns the object as a Go type in the mapping's version or returns an error.
func (i *Info) Versioned() (runtime.Object, error) { func (i *Info) Versioned() (runtime.Object, error) {
return i.Mapping.ConvertToVersion(i.Object, i.Mapping.GroupVersionKind.GroupVersion()) return i.ObjectConverter.ConvertToVersion(i.Object, i.Mapping.GroupVersionKind.GroupVersion())
} }
// AsVersioned returns the object as a Go object in the external form if possible (matching the // AsVersioned returns the object as a Go object in the external form if possible (matching the
@ -219,7 +208,7 @@ func (i *Info) Unstructured() (runtime.Unstructured, error) {
return out.(runtime.Unstructured), err return out.(runtime.Unstructured), err
default: default:
out := &unstructured.Unstructured{} out := &unstructured.Unstructured{}
if err := i.Mapping.Convert(i.Object, out, nil); err != nil { if err := i.ObjectConverter.Convert(i.Object, out, nil); err != nil {
return nil, err return nil, err
} }
return out, nil return out, nil

View File

@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -297,7 +298,7 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
// check if the object is unstructured. If so, let's attempt to convert it to a type we can understand before // check if the object is unstructured. If so, let's attempt to convert it to a type we can understand before
// trying to print, since the printers are keyed by type. This is extremely expensive. // trying to print, since the printers are keyed by type. This is extremely expensive.
if h.encoder != nil && h.decoder != nil { if h.encoder != nil && h.decoder != nil {
obj, _ = decodeUnknownObject(obj, h.encoder, h.decoder) obj, _ = decodeUnknownObject(obj, h.decoder)
} }
// print with a registered handler // print with a registered handler
@ -817,15 +818,19 @@ func AppendAllLabels(showLabels bool, itemLabels map[string]string) string {
} }
// check if the object is unstructured. If so, attempt to convert it to a type we can understand. // check if the object is unstructured. If so, attempt to convert it to a type we can understand.
func decodeUnknownObject(obj runtime.Object, encoder runtime.Encoder, decoder runtime.Decoder) (runtime.Object, error) { func decodeUnknownObject(obj runtime.Object, decoder runtime.Decoder) (runtime.Object, error) {
var err error var err error
switch obj.(type) { switch t := obj.(type) {
case runtime.Unstructured, *runtime.Unknown: case runtime.Unstructured:
if objBytes, err := runtime.Encode(encoder, obj); err == nil { if objBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj); err == nil {
if decodedObj, err := runtime.Decode(decoder, objBytes); err == nil { if decodedObj, err := runtime.Decode(decoder, objBytes); err == nil {
obj = decodedObj obj = decodedObj
} }
} }
case *runtime.Unknown:
if decodedObj, err := runtime.Decode(decoder, t.Raw); err == nil {
obj = decodedObj
}
} }
return obj, err return obj, err

View File

@ -223,6 +223,10 @@ func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.Gr
} }
} }
func (*ScaleREST) ClusterScoped() bool {
return false
}
// New creates a new Scale object // New creates a new Scale object
func (r *ScaleREST) New() runtime.Object { func (r *ScaleREST) New() runtime.Object {
return &autoscaling.Scale{} return &autoscaling.Scale{}

View File

@ -151,6 +151,10 @@ func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.Gr
} }
} }
func (*ScaleREST) ClusterScoped() bool {
return false
}
// New creates a new Scale object // New creates a new Scale object
func (r *ScaleREST) New() runtime.Object { func (r *ScaleREST) New() runtime.Object {
return &autoscaling.Scale{} return &autoscaling.Scale{}

View File

@ -140,6 +140,10 @@ func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.Gr
} }
} }
func (*ScaleREST) ClusterScoped() bool {
return false
}
// New creates a new Scale object // New creates a new Scale object
func (r *ScaleREST) New() runtime.Object { func (r *ScaleREST) New() runtime.Object {
return &autoscaling.Scale{} return &autoscaling.Scale{}

View File

@ -72,6 +72,11 @@ func (r *EvictionREST) GroupVersionKind(containingGV schema.GroupVersion) schema
return schema.GroupVersionKind{Group: "policy", Version: "v1beta1", Kind: "Eviction"} return schema.GroupVersionKind{Group: "policy", Version: "v1beta1", Kind: "Eviction"}
} }
// ClusterScoped fulfills GroupVersionKindProvider
func (*EvictionREST) ClusterScoped() bool {
return false
}
// New creates a new eviction resource // New creates a new eviction resource
func (r *EvictionREST) New() runtime.Object { func (r *EvictionREST) New() runtime.Object {
return &policy.Eviction{} return &policy.Eviction{}

View File

@ -140,6 +140,10 @@ func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.Gr
} }
} }
func (*ScaleREST) ClusterScoped() bool {
return false
}
// New creates a new Scale object // New creates a new Scale object
func (r *ScaleREST) New() runtime.Object { func (r *ScaleREST) New() runtime.Object {
return &autoscaling.Scale{} return &autoscaling.Scale{}

View File

@ -46,6 +46,7 @@ type TokenREST struct {
} }
var _ = rest.NamedCreater(&TokenREST{}) var _ = rest.NamedCreater(&TokenREST{})
var _ = rest.GroupVersionKindProvider(&TokenREST{})
func (r *TokenREST) Create(ctx context.Context, name string, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { func (r *TokenREST) Create(ctx context.Context, name string, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
if err := createValidation(obj); err != nil { if err := createValidation(obj); err != nil {
@ -120,6 +121,10 @@ func (r *TokenREST) GroupVersionKind(containingGV schema.GroupVersion) schema.Gr
} }
} }
func (*TokenREST) ClusterScoped() bool {
return false
}
type getter interface { type getter interface {
Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error)
} }

View File

@ -145,6 +145,10 @@ func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.Gr
return autoscalingv1.SchemeGroupVersion.WithKind("Scale") return autoscalingv1.SchemeGroupVersion.WithKind("Scale")
} }
func (*ScaleREST) ClusterScoped() bool {
return false
}
// New creates a new Scale object // New creates a new Scale object
func (r *ScaleREST) New() runtime.Object { func (r *ScaleREST) New() runtime.Object {
return &autoscalingv1.Scale{} return &autoscalingv1.Scale{}

View File

@ -25,7 +25,7 @@ import (
// VersionInterfaces contains the interfaces one should use for dealing with types of a particular version. // VersionInterfaces contains the interfaces one should use for dealing with types of a particular version.
type VersionInterfaces struct { type VersionInterfaces struct {
runtime.ObjectConvertor ObjectConvertor runtime.ObjectConvertor
} }
type ListMetaAccessor interface { type ListMetaAccessor interface {
@ -91,13 +91,6 @@ const (
type RESTScope interface { type RESTScope interface {
// Name of the scope // Name of the scope
Name() RESTScopeName Name() RESTScopeName
// ParamName is the optional name of the parameter that should be inserted in the resource url
// If empty, no param will be inserted
ParamName() string
// ArgumentName is the optional name that should be used for the variable holding the value.
ArgumentName() string
// ParamDescription is the optional description to use to document the parameter in api documentation
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
@ -110,8 +103,6 @@ type RESTMapping struct {
// Scope contains the information needed to deal with REST Resources that are in a resource hierarchy // Scope contains the information needed to deal with REST Resources that are in a resource hierarchy
Scope RESTScope Scope RESTScope
runtime.ObjectConvertor
} }
// RESTMapper allows clients to map resources to kind, and map kind and version // RESTMapper allows clients to map resources to kind, and map kind and version

View File

@ -28,30 +28,15 @@ import (
// Implements RESTScope interface // Implements RESTScope interface
type restScope struct { type restScope struct {
name RESTScopeName name RESTScopeName
paramName string
argumentName string
paramDescription string
} }
func (r *restScope) Name() RESTScopeName { func (r *restScope) Name() RESTScopeName {
return r.name return r.name
} }
func (r *restScope) ParamName() string {
return r.paramName
}
func (r *restScope) ArgumentName() string {
return r.argumentName
}
func (r *restScope) ParamDescription() string {
return r.paramDescription
}
var RESTScopeNamespace = &restScope{ var RESTScopeNamespace = &restScope{
name: RESTScopeNameNamespace, name: RESTScopeNameNamespace,
paramName: "namespaces",
argumentName: "namespace",
paramDescription: "object name and auth scope, such as for teams and projects",
} }
var RESTScopeRoot = &restScope{ var RESTScopeRoot = &restScope{
@ -526,17 +511,10 @@ func (m *DefaultRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string
return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", gvk.GroupVersion(), gvk.Kind) return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", gvk.GroupVersion(), gvk.Kind)
} }
interfaces, err := m.interfacesFunc(gvk.GroupVersion())
if err != nil {
return nil, fmt.Errorf("the provided version %q has no relevant versions: %v", gvk.GroupVersion().String(), err)
}
mappings = append(mappings, &RESTMapping{ mappings = append(mappings, &RESTMapping{
Resource: res.Resource, Resource: res.Resource,
GroupVersionKind: gvk, GroupVersionKind: gvk,
Scope: scope, Scope: scope,
ObjectConvertor: interfaces.ObjectConvertor,
}) })
} }

View File

@ -577,10 +577,6 @@ func TestRESTMapperRESTMapping(t *testing.T) {
t.Errorf("%d: unexpected resource: %#v", i, mapping) t.Errorf("%d: unexpected resource: %#v", i, mapping)
} }
if mapping.ObjectConvertor == nil {
t.Errorf("%d: missing codec: %#v", i, mapping)
}
groupVersion := testCase.ExpectedGroupVersion groupVersion := testCase.ExpectedGroupVersion
if groupVersion == nil { if groupVersion == nil {
groupVersion = &testCase.APIGroupVersions[0] groupVersion = &testCase.APIGroupVersions[0]
@ -727,9 +723,6 @@ func TestRESTMapperRESTMappings(t *testing.T) {
if mapping.Resource != exp.Resource { if mapping.Resource != exp.Resource {
t.Errorf("%d - %d: unexpected resource: %#v", i, j, mapping) t.Errorf("%d - %d: unexpected resource: %#v", i, j, mapping)
} }
if mapping.ObjectConvertor == nil {
t.Errorf("%d - %d: missing codec: %#v", i, j, mapping)
}
if mapping.GroupVersionKind != exp.GroupVersionKind { if mapping.GroupVersionKind != exp.GroupVersionKind {
t.Errorf("%d - %d: unexpected GroupVersionKind: %#v", i, j, mapping) t.Errorf("%d - %d: unexpected GroupVersionKind: %#v", i, j, mapping)
} }
@ -744,7 +737,7 @@ func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
mapper := NewDefaultRESTMapper([]schema.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, unmatchedVersionInterfaces) mapper := NewDefaultRESTMapper([]schema.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, unmatchedVersionInterfaces)
mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace) mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace)
_, err := mapper.RESTMapping(internalObjectGK, expectedGroupVersion1.Version) _, err := mapper.RESTMapping(internalObjectGK, "test3")
if err == nil { if err == nil {
t.Errorf("unexpected non-error") t.Errorf("unexpected non-error")
} }

View File

@ -24,6 +24,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
], ],
) )

View File

@ -153,8 +153,9 @@ func (gmf *GroupMetaFactory) Register(m *registered.APIRegistrationManager, sche
accessor := meta.NewAccessor() accessor := meta.NewAccessor()
groupMeta := &apimachinery.GroupMeta{ groupMeta := &apimachinery.GroupMeta{
GroupVersions: externalVersions, GroupVersions: externalVersions,
SelfLinker: runtime.SelfLinker(accessor), SelfLinker: runtime.SelfLinker(accessor),
RootScopedKinds: gmf.GroupArgs.RootScopedKinds,
} }
for _, v := range externalVersions { for _, v := range externalVersions {
gvf := gmf.VersionArgs[v.Version] gvf := gmf.VersionArgs[v.Version]

View File

@ -22,6 +22,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
) )
// GroupMeta stores the metadata of a group. // GroupMeta stores the metadata of a group.
@ -29,6 +30,8 @@ type GroupMeta struct {
// GroupVersions is Group + all versions in that group. // GroupVersions is Group + all versions in that group.
GroupVersions []schema.GroupVersion GroupVersions []schema.GroupVersion
RootScopedKinds sets.String
// SelfLinker can set or get the SelfLink field of all API types. // SelfLinker can set or get the SelfLink field of all API types.
// TODO: when versioning changes, make this part of each API definition. // TODO: when versioning changes, make this part of each API definition.
// TODO(lavalamp): Combine SelfLinker & ResourceVersioner interfaces, force all uses // TODO(lavalamp): Combine SelfLinker & ResourceVersioner interfaces, force all uses

View File

@ -65,13 +65,13 @@ go_library(
importpath = "k8s.io/apiserver/pkg/endpoints", importpath = "k8s.io/apiserver/pkg/endpoints",
deps = [ deps = [
"//vendor/github.com/emicklei/go-restful:go_default_library", "//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/handlers:go_default_library",

View File

@ -117,7 +117,6 @@ var parameterCodec = runtime.NewParameterCodec(scheme)
var accessor = meta.NewAccessor() var accessor = meta.NewAccessor()
var selfLinker runtime.SelfLinker = accessor var selfLinker runtime.SelfLinker = accessor
var mapper, namespaceMapper meta.RESTMapper // The mappers with namespace and with legacy namespace scopes.
var admissionControl admission.Interface var admissionControl admission.Interface
func init() { func init() {
@ -203,25 +202,6 @@ func init() {
addTestTypes() addTestTypes()
addNewTestTypes() addNewTestTypes()
nsMapper := newMapper()
// enumerate all supported versions, get the kinds, and register with
// the mapper how to address our resources
for _, gv := range groupVersions {
for kind := range scheme.KnownTypes(gv) {
gvk := gv.WithKind(kind)
root := bool(kind == "SimpleRoot")
if root {
nsMapper.Add(gvk, meta.RESTScopeRoot)
} else {
nsMapper.Add(gvk, meta.RESTScopeNamespace)
}
}
}
mapper = nsMapper
namespaceMapper = nsMapper
scheme.AddFieldLabelConversionFunc(grouplessGroupVersion.String(), "Simple", scheme.AddFieldLabelConversionFunc(grouplessGroupVersion.String(), "Simple",
func(label, value string) (string, string, error) { func(label, value string) (string, string, error) {
return label, value, nil return label, value, nil
@ -263,12 +243,12 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission.
template := APIGroupVersion{ template := APIGroupVersion{
Storage: storage, Storage: storage,
Creater: scheme, Creater: scheme,
Convertor: scheme, Convertor: scheme,
Defaulter: scheme, Defaulter: scheme,
Typer: scheme, Typer: scheme,
Linker: selfLinker, Linker: selfLinker,
Mapper: namespaceMapper, RootScopedKinds: sets.NewString("SimpleRoot"),
ParameterCodec: parameterCodec, ParameterCodec: parameterCodec,
@ -841,7 +821,6 @@ 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)
} }
} }
} }
@ -3188,15 +3167,15 @@ func TestParentResourceIsRequired(t *testing.T) {
Storage: map[string]rest.Storage{ Storage: map[string]rest.Storage{
"simple/sub": storage, "simple/sub": storage,
}, },
Root: "/" + prefix, Root: "/" + prefix,
Creater: scheme, Creater: scheme,
Convertor: scheme, Convertor: scheme,
Defaulter: scheme, Defaulter: scheme,
Typer: scheme, Typer: scheme,
Linker: selfLinker, Linker: selfLinker,
RootScopedKinds: sets.NewString("SimpleRoot"),
Admit: admissionControl, Admit: admissionControl,
Mapper: namespaceMapper,
GroupVersion: newGroupVersion, GroupVersion: newGroupVersion,
OptionsExternalVersion: &newGroupVersion, OptionsExternalVersion: &newGroupVersion,
@ -3225,8 +3204,7 @@ func TestParentResourceIsRequired(t *testing.T) {
Typer: scheme, Typer: scheme,
Linker: selfLinker, Linker: selfLinker,
Admit: admissionControl, Admit: admissionControl,
Mapper: namespaceMapper,
GroupVersion: newGroupVersion, GroupVersion: newGroupVersion,
OptionsExternalVersion: &newGroupVersion, OptionsExternalVersion: &newGroupVersion,
@ -3806,6 +3784,8 @@ type SimpleXGSubresourceRESTStorage struct {
itemGVK schema.GroupVersionKind itemGVK schema.GroupVersionKind
} }
var _ = rest.GroupVersionKindProvider(&SimpleXGSubresourceRESTStorage{})
func (storage *SimpleXGSubresourceRESTStorage) New() runtime.Object { func (storage *SimpleXGSubresourceRESTStorage) New() runtime.Object {
return &genericapitesting.SimpleXGSubresource{} return &genericapitesting.SimpleXGSubresource{}
} }
@ -3814,12 +3794,14 @@ func (storage *SimpleXGSubresourceRESTStorage) Get(ctx context.Context, id strin
return storage.item.DeepCopyObject(), nil return storage.item.DeepCopyObject(), nil
} }
var _ = rest.GroupVersionKindProvider(&SimpleXGSubresourceRESTStorage{})
func (storage *SimpleXGSubresourceRESTStorage) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind { func (storage *SimpleXGSubresourceRESTStorage) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
return storage.itemGVK return storage.itemGVK
} }
func (*SimpleXGSubresourceRESTStorage) ClusterScoped() bool {
return false
}
func TestXGSubresource(t *testing.T) { func TestXGSubresource(t *testing.T) {
container := restful.NewContainer() container := restful.NewContainer()
container.Router(restful.CurlyRouter{}) container.Router(restful.CurlyRouter{})
@ -3845,7 +3827,6 @@ func TestXGSubresource(t *testing.T) {
Defaulter: scheme, Defaulter: scheme,
Typer: scheme, Typer: scheme,
Linker: selfLinker, Linker: selfLinker,
Mapper: namespaceMapper,
ParameterCodec: parameterCodec, ParameterCodec: parameterCodec,

View File

@ -22,11 +22,11 @@ import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/endpoints/discovery" "k8s.io/apiserver/pkg/endpoints/discovery"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
@ -55,7 +55,8 @@ type APIGroupVersion struct {
// version (for when the inevitable meta/v2 group emerges). // version (for when the inevitable meta/v2 group emerges).
MetaGroupVersion *schema.GroupVersion MetaGroupVersion *schema.GroupVersion
Mapper meta.RESTMapper // RootScopedKinds are the root scoped kinds for the primary GroupVersion
RootScopedKinds sets.String
// Serializer is used to determine how to convert responses from API methods into bytes to send over // Serializer is used to determine how to convert responses from API methods into bytes to send over
// the wire. // the wire.

View File

@ -28,7 +28,6 @@ import (
restful "github.com/emicklei/go-restful" restful "github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -133,18 +132,18 @@ func (a *APIInstaller) newWebService() *restful.WebService {
} }
// getResourceKind returns the external group version kind registered for the given storage // getResourceKind returns the external group version kind registered for the given storage
// object. If the storage object is a subresource and has an override supplied for it, it returns // object and whether or not the kind is cluster scoped. If the storage object is a subresource and has an override supplied for it, it returns
// the group version kind supplied in the override. // the group version kind supplied in the override.
func (a *APIInstaller) getResourceKind(path string, storage rest.Storage) (schema.GroupVersionKind, error) { func (a *APIInstaller) getResourceKind(path string, storage rest.Storage) (schema.GroupVersionKind, bool, error) {
// Let the storage tell us exactly what GVK it has // Let the storage tell us exactly what GVK it has
if gvkProvider, ok := storage.(rest.GroupVersionKindProvider); ok { if gvkProvider, ok := storage.(rest.GroupVersionKindProvider); ok {
return gvkProvider.GroupVersionKind(a.group.GroupVersion), nil return gvkProvider.GroupVersionKind(a.group.GroupVersion), gvkProvider.ClusterScoped(), nil
} }
object := storage.New() object := storage.New()
fqKinds, _, err := a.group.Typer.ObjectKinds(object) fqKinds, _, err := a.group.Typer.ObjectKinds(object)
if err != nil { if err != nil {
return schema.GroupVersionKind{}, err return schema.GroupVersionKind{}, false, err
} }
// a given go type can have multiple potential fully qualified kinds. Find the one that corresponds with the group // a given go type can have multiple potential fully qualified kinds. Find the one that corresponds with the group
@ -157,32 +156,11 @@ func (a *APIInstaller) getResourceKind(path string, storage rest.Storage) (schem
} }
} }
if fqKindToRegister.Empty() { if fqKindToRegister.Empty() {
return schema.GroupVersionKind{}, fmt.Errorf("unable to locate fully qualified kind for %v: found %v when registering for %v", reflect.TypeOf(object), fqKinds, a.group.GroupVersion) return schema.GroupVersionKind{}, false, fmt.Errorf("unable to locate fully qualified kind for %v: found %v when registering for %v", reflect.TypeOf(object), fqKinds, a.group.GroupVersion)
} }
return fqKindToRegister, nil
}
// restMapping returns rest mapper for the resource. // group is guaranteed to match based on the check above
// Example REST paths that this mapper maps. return fqKindToRegister, a.group.RootScopedKinds.Has(fqKindToRegister.Kind), nil
// 1. Resource only, no subresource:
// Resource Type: batch/v1.Job (input args: resource = "jobs")
// REST path: /apis/batch/v1/namespaces/{namespace}/job/{name}
// 2. Subresource and its parent belong to different API groups and/or versions:
// Resource Type: extensions/v1beta1.ReplicaSet (input args: resource = "replicasets")
// Subresource Type: autoscaling/v1.Scale
// REST path: /apis/extensions/v1beta1/namespaces/{namespace}/replicaset/{name}/scale
func (a *APIInstaller) restMapping(resource string) (*meta.RESTMapping, error) {
// subresources must have parent resources, and follow the namespacing rules of their parent.
// So get the storage of the resource (which is the parent resource in case of subresources)
storage, ok := a.group.Storage[resource]
if !ok {
return nil, fmt.Errorf("unable to locate the storage object for resource: %s", resource)
}
fqKindToRegister, err := a.getResourceKind(resource, storage)
if err != nil {
return nil, fmt.Errorf("unable to locate fully qualified kind for mapper resource %s: %v", resource, err)
}
return a.group.Mapper.RESTMapping(fqKindToRegister.GroupKind(), fqKindToRegister.Version)
} }
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService) (*metav1.APIResource, error) { func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService) (*metav1.APIResource, error) {
@ -198,12 +176,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
return nil, err return nil, err
} }
mapping, err := a.restMapping(resource) fqKindToRegister, clusterScoped, err := a.getResourceKind(path, storage)
if err != nil {
return nil, err
}
fqKindToRegister, err := a.getResourceKind(path, storage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -338,7 +311,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
} }
allowWatchList := isWatcher && isLister // watching on lists is allowed only for kinds that support both watch and list. allowWatchList := isWatcher && isLister // watching on lists is allowed only for kinds that support both watch and list.
scope := mapping.Scope
nameParam := ws.PathParameter("name", "name of the "+kind).DataType("string") nameParam := ws.PathParameter("name", "name of the "+kind).DataType("string")
pathParam := ws.PathParameter("path", "path to the resource").DataType("string") pathParam := ws.PathParameter("path", "path to the resource").DataType("string")
@ -357,8 +329,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
var apiResource metav1.APIResource var apiResource metav1.APIResource
// Get the list of actions for the given scope. // Get the list of actions for the given scope.
switch scope.Name() { switch {
case meta.RESTScopeNameRoot: case clusterScoped:
// Handle non-namespace scoped resources like nodes. // Handle non-namespace scoped resources like nodes.
resourcePath := resource resourcePath := resource
resourceParams := params resourceParams := params
@ -402,10 +374,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
actions = appendIf(actions, action{"CONNECT", itemPath, nameParams, namer, false}, isConnecter) actions = appendIf(actions, action{"CONNECT", itemPath, nameParams, namer, false}, isConnecter)
actions = appendIf(actions, action{"CONNECT", itemPath + "/{path:*}", proxyParams, namer, false}, isConnecter && connectSubpath) actions = appendIf(actions, action{"CONNECT", itemPath + "/{path:*}", proxyParams, namer, false}, isConnecter && connectSubpath)
break break
case meta.RESTScopeNameNamespace: default:
namespaceParamName := "namespaces"
// Handler for standard REST verbs (GET, PUT, POST and DELETE). // Handler for standard REST verbs (GET, PUT, POST and DELETE).
namespaceParam := ws.PathParameter(scope.ArgumentName(), scope.ParamDescription()).DataType("string") namespaceParam := ws.PathParameter("namespace", "object name and auth scope, such as for teams and projects").DataType("string")
namespacedPath := scope.ParamName() + "/{" + scope.ArgumentName() + "}/" + resource namespacedPath := namespaceParamName + "/{" + "namespace" + "}/" + resource
namespaceParams := []*restful.Parameter{namespaceParam} namespaceParams := []*restful.Parameter{namespaceParam}
resourcePath := namespacedPath resourcePath := namespacedPath
@ -426,7 +399,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
namer := handlers.ContextBasedNaming{ namer := handlers.ContextBasedNaming{
SelfLinker: a.group.Linker, SelfLinker: a.group.Linker,
ClusterScoped: false, ClusterScoped: false,
SelfLinkPathPrefix: gpath.Join(a.prefix, scope.ParamName()) + "/", SelfLinkPathPrefix: gpath.Join(a.prefix, namespaceParamName) + "/",
SelfLinkPathSuffix: itemPathSuffix, SelfLinkPathSuffix: itemPathSuffix,
} }
@ -455,8 +428,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
actions = appendIf(actions, action{"WATCHLIST", "watch/" + resource, params, namer, true}, allowWatchList) actions = appendIf(actions, action{"WATCHLIST", "watch/" + resource, params, namer, true}, allowWatchList)
} }
break break
default:
return nil, fmt.Errorf("unsupported restscope: %s", scope.Name())
} }
// Create Routes for the actions. // Create Routes for the actions.
@ -539,7 +510,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
// If there is a subresource, kind should be the parent's kind. // If there is a subresource, kind should be the parent's kind.
if hasSubresource { if hasSubresource {
fqParentKind, err := a.getResourceKind(resource, a.group.Storage[resource]) parentStorage, ok := a.group.Storage[resource]
if !ok {
return nil, fmt.Errorf("missing parent storage: %q", resource)
}
fqParentKind, _, err := a.getResourceKind(resource, parentStorage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -654,7 +629,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
string(types.MergePatchType), string(types.MergePatchType),
string(types.StrategicMergePatchType), string(types.StrategicMergePatchType),
} }
handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulPatchResource(patcher, reqScope, admit, mapping.ObjectConvertor, supportedTypes)) handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulPatchResource(patcher, reqScope, admit, a.group.Convertor, supportedTypes))
route := ws.PATCH(action.Path).To(handler). route := ws.PATCH(action.Path).To(handler).
Doc(doc). Doc(doc).
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).

View File

@ -83,6 +83,7 @@ type CategoriesProvider interface {
// TODO KindProvider (only used by federation) should be removed and replaced with this, but that presents greater risk late in 1.8. // TODO KindProvider (only used by federation) should be removed and replaced with this, but that presents greater risk late in 1.8.
type GroupVersionKindProvider interface { type GroupVersionKindProvider interface {
GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind
ClusterScoped() bool
} }
// Lister is an object that can retrieve resources that match the provided field and label criteria. // Lister is an object that can retrieve resources that match the provided field and label criteria.

View File

@ -423,7 +423,7 @@ func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV
Defaulter: apiGroupInfo.Scheme, Defaulter: apiGroupInfo.Scheme,
Typer: apiGroupInfo.Scheme, Typer: apiGroupInfo.Scheme,
Linker: apiGroupInfo.GroupMeta.SelfLinker, Linker: apiGroupInfo.GroupMeta.SelfLinker,
Mapper: apiGroupInfo.GroupMeta.RESTMapper, RootScopedKinds: apiGroupInfo.GroupMeta.RootScopedKinds,
Admit: s.admissionControl, Admit: s.admissionControl,
MinRequestTimeout: s.minRequestTimeout, MinRequestTimeout: s.minRequestTimeout,

View File

@ -192,7 +192,7 @@ func TestServerSidePrint(t *testing.T) {
continue continue
} }
intGV := gvk.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion() intGV := gvk.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
intObj, err := mapping.ConvertToVersion(obj, intGV) intObj, err := legacyscheme.Scheme.ConvertToVersion(obj, intGV)
if err != nil { if err != nil {
t.Errorf("unexpected error converting %s to internal: %v", gvk, err) t.Errorf("unexpected error converting %s to internal: %v", gvk, err)
continue continue