mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Replace negotiation with a new method that can extract info
Alter how runtime.SerializeInfo is represented to simplify negotiation and reduce the need to allocate during negotiation. Simplify the dynamic client's logic around negotiating type. Add more tests for media type handling where necessary.
This commit is contained in:
parent
f9f680a937
commit
ca2f1b87ad
@ -545,7 +545,7 @@ func StartControllers(s *options.CMServer, kubeconfig *restclient.Config, rootCl
|
|||||||
config := restclient.AddUserAgent(kubeconfig, "generic-garbage-collector")
|
config := restclient.AddUserAgent(kubeconfig, "generic-garbage-collector")
|
||||||
config.ContentConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: metaonly.NewMetadataCodecFactory()}
|
config.ContentConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: metaonly.NewMetadataCodecFactory()}
|
||||||
metaOnlyClientPool := dynamic.NewClientPool(config, restMapper, dynamic.LegacyAPIPathResolverFunc)
|
metaOnlyClientPool := dynamic.NewClientPool(config, restMapper, dynamic.LegacyAPIPathResolverFunc)
|
||||||
config.ContentConfig.NegotiatedSerializer = nil
|
config.ContentConfig = dynamic.ContentConfig()
|
||||||
clientPool := dynamic.NewClientPool(config, restMapper, dynamic.LegacyAPIPathResolverFunc)
|
clientPool := dynamic.NewClientPool(config, restMapper, dynamic.LegacyAPIPathResolverFunc)
|
||||||
garbageCollector, err := garbagecollector.NewGarbageCollector(metaOnlyClientPool, clientPool, restMapper, groupVersionResources)
|
garbageCollector, err := garbagecollector.NewGarbageCollector(metaOnlyClientPool, clientPool, restMapper, groupVersionResources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,12 +54,12 @@ func TestUniversalDeserializer(t *testing.T) {
|
|||||||
expected := &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "test"}}
|
expected := &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "test"}}
|
||||||
d := api.Codecs.UniversalDeserializer()
|
d := api.Codecs.UniversalDeserializer()
|
||||||
for _, mediaType := range []string{"application/json", "application/yaml", "application/vnd.kubernetes.protobuf"} {
|
for _, mediaType := range []string{"application/json", "application/yaml", "application/vnd.kubernetes.protobuf"} {
|
||||||
e, ok := api.Codecs.SerializerForMediaType(mediaType, nil)
|
info, ok := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), mediaType)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal(mediaType)
|
t.Fatal(mediaType)
|
||||||
}
|
}
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
if err := e.Encode(expected, buf); err != nil {
|
if err := info.Serializer.Encode(expected, buf); err != nil {
|
||||||
t.Fatalf("%s: %v", mediaType, err)
|
t.Fatalf("%s: %v", mediaType, err)
|
||||||
}
|
}
|
||||||
obj, _, err := d.Decode(buf.Bytes(), &unversioned.GroupVersionKind{Kind: "Pod", Version: "v1"}, nil)
|
obj, _, err := d.Decode(buf.Bytes(), &unversioned.GroupVersionKind{Kind: "Pod", Version: "v1"}, nil)
|
||||||
|
@ -380,12 +380,15 @@ func TestObjectWatchFraming(t *testing.T) {
|
|||||||
secret.Data["long"] = bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x00}, 1000)
|
secret.Data["long"] = bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x00}, 1000)
|
||||||
converted, _ := api.Scheme.ConvertToVersion(secret, v1.SchemeGroupVersion)
|
converted, _ := api.Scheme.ConvertToVersion(secret, v1.SchemeGroupVersion)
|
||||||
v1secret := converted.(*v1.Secret)
|
v1secret := converted.(*v1.Secret)
|
||||||
for _, streamingMediaType := range api.Codecs.SupportedStreamingMediaTypes() {
|
for _, info := range api.Codecs.SupportedMediaTypes() {
|
||||||
s, _ := api.Codecs.StreamingSerializerForMediaType(streamingMediaType, nil)
|
if info.StreamSerializer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s := info.StreamSerializer
|
||||||
framer := s.Framer
|
framer := s.Framer
|
||||||
embedded := s.Embedded.Serializer
|
embedded := info.Serializer
|
||||||
if embedded == nil {
|
if embedded == nil {
|
||||||
t.Errorf("no embedded serializer for %s", streamingMediaType)
|
t.Errorf("no embedded serializer for %s", info.MediaType)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
innerDecode := api.Codecs.DecoderToVersion(embedded, api.SchemeGroupVersion)
|
innerDecode := api.Codecs.DecoderToVersion(embedded, api.SchemeGroupVersion)
|
||||||
@ -442,7 +445,7 @@ func TestObjectWatchFraming(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !api.Semantic.DeepEqual(secret, outEvent.Object.Object) {
|
if !api.Semantic.DeepEqual(secret, outEvent.Object.Object) {
|
||||||
t.Fatalf("%s: did not match after frame decoding: %s", streamingMediaType, diff.ObjectGoPrintDiff(secret, outEvent.Object.Object))
|
t.Fatalf("%s: did not match after frame decoding: %s", info.MediaType, diff.ObjectGoPrintDiff(secret, outEvent.Object.Object))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,11 +93,11 @@ type TestGroup struct {
|
|||||||
func init() {
|
func init() {
|
||||||
if apiMediaType := os.Getenv("KUBE_TEST_API_TYPE"); len(apiMediaType) > 0 {
|
if apiMediaType := os.Getenv("KUBE_TEST_API_TYPE"); len(apiMediaType) > 0 {
|
||||||
var ok bool
|
var ok bool
|
||||||
mediaType, options, err := mime.ParseMediaType(apiMediaType)
|
mediaType, _, err := mime.ParseMediaType(apiMediaType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
serializer, ok = api.Codecs.SerializerForMediaType(mediaType, options)
|
serializer, ok = runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), mediaType)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Sprintf("no serializer for %s", apiMediaType))
|
panic(fmt.Sprintf("no serializer for %s", apiMediaType))
|
||||||
}
|
}
|
||||||
@ -105,11 +105,11 @@ func init() {
|
|||||||
|
|
||||||
if storageMediaType := StorageMediaType(); len(storageMediaType) > 0 {
|
if storageMediaType := StorageMediaType(); len(storageMediaType) > 0 {
|
||||||
var ok bool
|
var ok bool
|
||||||
mediaType, options, err := mime.ParseMediaType(storageMediaType)
|
mediaType, _, err := mime.ParseMediaType(storageMediaType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
storageSerializer, ok = api.Codecs.SerializerForMediaType(mediaType, options)
|
storageSerializer, ok = runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), mediaType)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Sprintf("no serializer for %s", storageMediaType))
|
panic(fmt.Sprintf("no serializer for %s", storageMediaType))
|
||||||
}
|
}
|
||||||
@ -312,7 +312,7 @@ func (g TestGroup) Codec() runtime.Codec {
|
|||||||
if serializer.Serializer == nil {
|
if serializer.Serializer == nil {
|
||||||
return api.Codecs.LegacyCodec(g.externalGroupVersion)
|
return api.Codecs.LegacyCodec(g.externalGroupVersion)
|
||||||
}
|
}
|
||||||
return api.Codecs.CodecForVersions(serializer, api.Codecs.UniversalDeserializer(), unversioned.GroupVersions{g.externalGroupVersion}, nil)
|
return api.Codecs.CodecForVersions(serializer.Serializer, api.Codecs.UniversalDeserializer(), unversioned.GroupVersions{g.externalGroupVersion}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NegotiatedSerializer returns the negotiated serializer for the server.
|
// NegotiatedSerializer returns the negotiated serializer for the server.
|
||||||
@ -452,11 +452,11 @@ func GetCodecForObject(obj runtime.Object) (runtime.Codec, error) {
|
|||||||
}
|
}
|
||||||
// Codec used for unversioned types
|
// Codec used for unversioned types
|
||||||
if api.Scheme.Recognizes(kind) {
|
if api.Scheme.Recognizes(kind) {
|
||||||
serializer, ok := api.Codecs.SerializerForFileExtension("json")
|
serializer, ok := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("no serializer registered for json")
|
return nil, fmt.Errorf("no serializer registered for json")
|
||||||
}
|
}
|
||||||
return serializer, nil
|
return serializer.Serializer, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unexpected kind: %v", kind)
|
return nil, fmt.Errorf("unexpected kind: %v", kind)
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,8 @@ func (a *APIInstaller) NewWebService() *restful.WebService {
|
|||||||
// If we stop using go-restful, we can default empty content-type to application/json on an
|
// If we stop using go-restful, we can default empty content-type to application/json on an
|
||||||
// endpoint by endpoint basis
|
// endpoint by endpoint basis
|
||||||
ws.Consumes("*/*")
|
ws.Consumes("*/*")
|
||||||
ws.Produces(a.group.Serializer.SupportedMediaTypes()...)
|
mediaTypes, streamMediaTypes := mediaTypesForSerializer(a.group.Serializer)
|
||||||
|
ws.Produces(append(mediaTypes, streamMediaTypes...)...)
|
||||||
ws.ApiVersion(a.group.GroupVersion.String())
|
ws.ApiVersion(a.group.GroupVersion.String())
|
||||||
|
|
||||||
return ws
|
return ws
|
||||||
@ -472,6 +473,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
//
|
//
|
||||||
// test/integration/auth_test.go is currently the most comprehensive status code test
|
// test/integration/auth_test.go is currently the most comprehensive status code test
|
||||||
|
|
||||||
|
mediaTypes, streamMediaTypes := mediaTypesForSerializer(a.group.Serializer)
|
||||||
|
allMediaTypes := append(mediaTypes, streamMediaTypes...)
|
||||||
|
ws.Produces(allMediaTypes...)
|
||||||
|
|
||||||
reqScope := RequestScope{
|
reqScope := RequestScope{
|
||||||
ContextFunc: ctxFn,
|
ContextFunc: ctxFn,
|
||||||
Serializer: a.group.Serializer,
|
Serializer: a.group.Serializer,
|
||||||
@ -517,7 +522,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
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.")).
|
||||||
Operation("read"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
Operation("read"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
||||||
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...).
|
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
|
||||||
Returns(http.StatusOK, "OK", versionedObject).
|
Returns(http.StatusOK, "OK", versionedObject).
|
||||||
Writes(versionedObject)
|
Writes(versionedObject)
|
||||||
if isGetterWithOptions {
|
if isGetterWithOptions {
|
||||||
@ -542,7 +547,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
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.")).
|
||||||
Operation("list"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
Operation("list"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
||||||
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...).
|
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), allMediaTypes...)...).
|
||||||
Returns(http.StatusOK, "OK", versionedList).
|
Returns(http.StatusOK, "OK", versionedList).
|
||||||
Writes(versionedList)
|
Writes(versionedList)
|
||||||
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
|
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
|
||||||
@ -574,7 +579,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
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.")).
|
||||||
Operation("replace"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
Operation("replace"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
||||||
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...).
|
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
|
||||||
Returns(http.StatusOK, "OK", versionedObject).
|
Returns(http.StatusOK, "OK", versionedObject).
|
||||||
Reads(versionedObject).
|
Reads(versionedObject).
|
||||||
Writes(versionedObject)
|
Writes(versionedObject)
|
||||||
@ -591,7 +596,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||||
Consumes(string(api.JSONPatchType), string(api.MergePatchType), string(api.StrategicMergePatchType)).
|
Consumes(string(api.JSONPatchType), string(api.MergePatchType), string(api.StrategicMergePatchType)).
|
||||||
Operation("patch"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
Operation("patch"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
||||||
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...).
|
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
|
||||||
Returns(http.StatusOK, "OK", versionedObject).
|
Returns(http.StatusOK, "OK", versionedObject).
|
||||||
Reads(unversioned.Patch{}).
|
Reads(unversioned.Patch{}).
|
||||||
Writes(versionedObject)
|
Writes(versionedObject)
|
||||||
@ -613,7 +618,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
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.")).
|
||||||
Operation("create"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
Operation("create"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
||||||
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...).
|
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
|
||||||
Returns(http.StatusOK, "OK", versionedObject).
|
Returns(http.StatusOK, "OK", versionedObject).
|
||||||
Reads(versionedObject).
|
Reads(versionedObject).
|
||||||
Writes(versionedObject)
|
Writes(versionedObject)
|
||||||
@ -629,7 +634,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
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.")).
|
||||||
Operation("delete"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
Operation("delete"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
||||||
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...).
|
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
|
||||||
Writes(versionedStatus).
|
Writes(versionedStatus).
|
||||||
Returns(http.StatusOK, "OK", versionedStatus)
|
Returns(http.StatusOK, "OK", versionedStatus)
|
||||||
if isGracefulDeleter {
|
if isGracefulDeleter {
|
||||||
@ -647,7 +652,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
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.")).
|
||||||
Operation("deletecollection"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
Operation("deletecollection"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
||||||
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...).
|
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
|
||||||
Writes(versionedStatus).
|
Writes(versionedStatus).
|
||||||
Returns(http.StatusOK, "OK", versionedStatus)
|
Returns(http.StatusOK, "OK", versionedStatus)
|
||||||
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
|
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
|
||||||
@ -666,7 +671,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
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.")).
|
||||||
Operation("watch"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
Operation("watch"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
||||||
Produces(a.group.Serializer.SupportedStreamingMediaTypes()...).
|
Produces(allMediaTypes...).
|
||||||
Returns(http.StatusOK, "OK", versionedWatchEvent).
|
Returns(http.StatusOK, "OK", versionedWatchEvent).
|
||||||
Writes(versionedWatchEvent)
|
Writes(versionedWatchEvent)
|
||||||
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
|
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
|
||||||
@ -685,7 +690,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
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.")).
|
||||||
Operation("watch"+namespaced+kind+strings.Title(subresource)+"List"+operationSuffix).
|
Operation("watch"+namespaced+kind+strings.Title(subresource)+"List"+operationSuffix).
|
||||||
Produces(a.group.Serializer.SupportedStreamingMediaTypes()...).
|
Produces(allMediaTypes...).
|
||||||
Returns(http.StatusOK, "OK", versionedWatchEvent).
|
Returns(http.StatusOK, "OK", versionedWatchEvent).
|
||||||
Writes(versionedWatchEvent)
|
Writes(versionedWatchEvent)
|
||||||
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
|
if err := addObjectParams(ws, route, versionedListOptions); err != nil {
|
||||||
|
@ -211,6 +211,7 @@ func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Contain
|
|||||||
// Because in release 1.1, /api returns response with empty APIVersion, we
|
// Because in release 1.1, /api returns response with empty APIVersion, we
|
||||||
// use StripVersionNegotiatedSerializer to keep the response backwards
|
// use StripVersionNegotiatedSerializer to keep the response backwards
|
||||||
// compatible.
|
// compatible.
|
||||||
|
mediaTypes, _ := mediaTypesForSerializer(s)
|
||||||
ss := StripVersionNegotiatedSerializer{s}
|
ss := StripVersionNegotiatedSerializer{s}
|
||||||
versionHandler := APIVersionHandler(ss, getAPIVersionsFunc)
|
versionHandler := APIVersionHandler(ss, getAPIVersionsFunc)
|
||||||
ws := new(restful.WebService)
|
ws := new(restful.WebService)
|
||||||
@ -219,8 +220,8 @@ func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Contain
|
|||||||
ws.Route(ws.GET("/").To(versionHandler).
|
ws.Route(ws.GET("/").To(versionHandler).
|
||||||
Doc("get available API versions").
|
Doc("get available API versions").
|
||||||
Operation("getAPIVersions").
|
Operation("getAPIVersions").
|
||||||
Produces(s.SupportedMediaTypes()...).
|
Produces(mediaTypes...).
|
||||||
Consumes(s.SupportedMediaTypes()...).
|
Consumes(mediaTypes...).
|
||||||
Writes(unversioned.APIVersions{}))
|
Writes(unversioned.APIVersions{}))
|
||||||
container.Add(ws)
|
container.Add(ws)
|
||||||
}
|
}
|
||||||
@ -277,6 +278,7 @@ func NewApisWebService(s runtime.NegotiatedSerializer, apiPrefix string, f func(
|
|||||||
// use StripVersionNegotiatedSerializer to keep the response backwards
|
// use StripVersionNegotiatedSerializer to keep the response backwards
|
||||||
// compatible.
|
// compatible.
|
||||||
ss := StripVersionNegotiatedSerializer{s}
|
ss := StripVersionNegotiatedSerializer{s}
|
||||||
|
mediaTypes, _ := mediaTypesForSerializer(s)
|
||||||
rootAPIHandler := RootAPIHandler(ss, f)
|
rootAPIHandler := RootAPIHandler(ss, f)
|
||||||
ws := new(restful.WebService)
|
ws := new(restful.WebService)
|
||||||
ws.Path(apiPrefix)
|
ws.Path(apiPrefix)
|
||||||
@ -284,8 +286,8 @@ func NewApisWebService(s runtime.NegotiatedSerializer, apiPrefix string, f func(
|
|||||||
ws.Route(ws.GET("/").To(rootAPIHandler).
|
ws.Route(ws.GET("/").To(rootAPIHandler).
|
||||||
Doc("get available API versions").
|
Doc("get available API versions").
|
||||||
Operation("getAPIVersions").
|
Operation("getAPIVersions").
|
||||||
Produces(s.SupportedMediaTypes()...).
|
Produces(mediaTypes...).
|
||||||
Consumes(s.SupportedMediaTypes()...).
|
Consumes(mediaTypes...).
|
||||||
Writes(unversioned.APIGroupList{}))
|
Writes(unversioned.APIGroupList{}))
|
||||||
return ws
|
return ws
|
||||||
}
|
}
|
||||||
@ -300,6 +302,7 @@ func NewGroupWebService(s runtime.NegotiatedSerializer, path string, group unver
|
|||||||
// response backwards compatible.
|
// response backwards compatible.
|
||||||
ss = StripVersionNegotiatedSerializer{s}
|
ss = StripVersionNegotiatedSerializer{s}
|
||||||
}
|
}
|
||||||
|
mediaTypes, _ := mediaTypesForSerializer(s)
|
||||||
groupHandler := GroupHandler(ss, group)
|
groupHandler := GroupHandler(ss, group)
|
||||||
ws := new(restful.WebService)
|
ws := new(restful.WebService)
|
||||||
ws.Path(path)
|
ws.Path(path)
|
||||||
@ -307,8 +310,8 @@ func NewGroupWebService(s runtime.NegotiatedSerializer, path string, group unver
|
|||||||
ws.Route(ws.GET("/").To(groupHandler).
|
ws.Route(ws.GET("/").To(groupHandler).
|
||||||
Doc("get information of a group").
|
Doc("get information of a group").
|
||||||
Operation("getAPIGroup").
|
Operation("getAPIGroup").
|
||||||
Produces(s.SupportedMediaTypes()...).
|
Produces(mediaTypes...).
|
||||||
Consumes(s.SupportedMediaTypes()...).
|
Consumes(mediaTypes...).
|
||||||
Writes(unversioned.APIGroup{}))
|
Writes(unversioned.APIGroup{}))
|
||||||
return ws
|
return ws
|
||||||
}
|
}
|
||||||
@ -323,12 +326,13 @@ func AddSupportedResourcesWebService(s runtime.NegotiatedSerializer, ws *restful
|
|||||||
// keep the response backwards compatible.
|
// keep the response backwards compatible.
|
||||||
ss = StripVersionNegotiatedSerializer{s}
|
ss = StripVersionNegotiatedSerializer{s}
|
||||||
}
|
}
|
||||||
|
mediaTypes, _ := mediaTypesForSerializer(s)
|
||||||
resourceHandler := SupportedResourcesHandler(ss, groupVersion, lister)
|
resourceHandler := SupportedResourcesHandler(ss, groupVersion, lister)
|
||||||
ws.Route(ws.GET("/").To(resourceHandler).
|
ws.Route(ws.GET("/").To(resourceHandler).
|
||||||
Doc("get available resources").
|
Doc("get available resources").
|
||||||
Operation("getAPIResources").
|
Operation("getAPIResources").
|
||||||
Produces(s.SupportedMediaTypes()...).
|
Produces(mediaTypes...).
|
||||||
Consumes(s.SupportedMediaTypes()...).
|
Consumes(mediaTypes...).
|
||||||
Writes(unversioned.APIResourceList{}))
|
Writes(unversioned.APIResourceList{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,7 +421,7 @@ func writeNegotiated(s runtime.NegotiatedSerializer, gv unversioned.GroupVersion
|
|||||||
w.Header().Set("Content-Type", serializer.MediaType)
|
w.Header().Set("Content-Type", serializer.MediaType)
|
||||||
w.WriteHeader(statusCode)
|
w.WriteHeader(statusCode)
|
||||||
|
|
||||||
encoder := s.EncoderForVersion(serializer, gv)
|
encoder := s.EncoderForVersion(serializer.Serializer, gv)
|
||||||
if err := encoder.Encode(object, w); err != nil {
|
if err := encoder.Encode(object, w); err != nil {
|
||||||
errorJSONFatal(err, encoder, w)
|
errorJSONFatal(err, encoder, w)
|
||||||
}
|
}
|
||||||
|
@ -1224,11 +1224,12 @@ func TestMetadata(t *testing.T) {
|
|||||||
matches[s] = i + 1
|
matches[s] = i + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches["text/plain,application/json,application/yaml,application/vnd.kubernetes.protobuf"] == 0 ||
|
if matches["text/plain,application/json,application/yaml,application/vnd.kubernetes.protobuf"] == 0 ||
|
||||||
matches["application/json,application/json;stream=watch,application/vnd.kubernetes.protobuf,application/vnd.kubernetes.protobuf;stream=watch"] == 0 ||
|
matches["application/json,application/yaml,application/vnd.kubernetes.protobuf,application/json;stream=watch,application/vnd.kubernetes.protobuf;stream=watch"] == 0 ||
|
||||||
matches["application/json,application/yaml,application/vnd.kubernetes.protobuf"] == 0 ||
|
matches["application/json,application/yaml,application/vnd.kubernetes.protobuf"] == 0 ||
|
||||||
matches["*/*"] == 0 ||
|
matches["*/*"] == 0 ||
|
||||||
len(matches) != 4 {
|
len(matches) != 5 {
|
||||||
t.Errorf("unexpected mime types: %v", matches)
|
t.Errorf("unexpected mime types: %v", matches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1321,6 +1322,89 @@ func TestGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetPretty(t *testing.T) {
|
||||||
|
storage := map[string]rest.Storage{}
|
||||||
|
simpleStorage := SimpleRESTStorage{
|
||||||
|
item: apiservertesting.Simple{
|
||||||
|
Other: "foo",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
selfLinker := &setTestSelfLinker{
|
||||||
|
t: t,
|
||||||
|
expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
|
||||||
|
name: "id",
|
||||||
|
namespace: "default",
|
||||||
|
}
|
||||||
|
storage["simple"] = &simpleStorage
|
||||||
|
handler := handleLinker(storage, selfLinker)
|
||||||
|
server := httptest.NewServer(handler)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
accept string
|
||||||
|
userAgent string
|
||||||
|
params url.Values
|
||||||
|
pretty bool
|
||||||
|
}{
|
||||||
|
{accept: runtime.ContentTypeJSON},
|
||||||
|
{accept: runtime.ContentTypeJSON + ";pretty=0"},
|
||||||
|
{accept: runtime.ContentTypeJSON, userAgent: "kubectl"},
|
||||||
|
{accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"0"}}},
|
||||||
|
|
||||||
|
{pretty: true, accept: runtime.ContentTypeJSON, userAgent: "curl"},
|
||||||
|
{pretty: true, accept: runtime.ContentTypeJSON, userAgent: "Mozilla/5.0"},
|
||||||
|
{pretty: true, accept: runtime.ContentTypeJSON, userAgent: "Wget"},
|
||||||
|
{pretty: true, accept: runtime.ContentTypeJSON + ";pretty=1"},
|
||||||
|
{pretty: true, accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"1"}}},
|
||||||
|
{pretty: true, accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"true"}}},
|
||||||
|
}
|
||||||
|
for i, test := range tests {
|
||||||
|
u, err := url.Parse(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
u.RawQuery = test.params.Encode()
|
||||||
|
req := &http.Request{Method: "GET", URL: u}
|
||||||
|
req.Header = http.Header{}
|
||||||
|
req.Header.Set("Accept", test.accept)
|
||||||
|
req.Header.Set("User-Agent", test.userAgent)
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var itemOut apiservertesting.Simple
|
||||||
|
body, err := extractBody(resp, &itemOut)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// to get stable ordering we need to use a go type
|
||||||
|
unstructured := apiservertesting.Simple{}
|
||||||
|
if err := json.Unmarshal([]byte(body), &unstructured); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var expect string
|
||||||
|
if test.pretty {
|
||||||
|
out, err := json.MarshalIndent(unstructured, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expect = string(out)
|
||||||
|
} else {
|
||||||
|
out, err := json.Marshal(unstructured)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expect = string(out) + "\n"
|
||||||
|
}
|
||||||
|
if expect != body {
|
||||||
|
t.Errorf("%d: body did not match expected:\n%s\n%s", i, body, expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetBinary(t *testing.T) {
|
func TestGetBinary(t *testing.T) {
|
||||||
simpleStorage := SimpleRESTStorage{
|
simpleStorage := SimpleRESTStorage{
|
||||||
stream: &SimpleStream{
|
stream: &SimpleStream{
|
||||||
@ -2719,12 +2803,12 @@ func TestCreateYAML(t *testing.T) {
|
|||||||
simple := &apiservertesting.Simple{
|
simple := &apiservertesting.Simple{
|
||||||
Other: "bar",
|
Other: "bar",
|
||||||
}
|
}
|
||||||
serializer, ok := api.Codecs.SerializerForMediaType("application/yaml", nil)
|
info, ok := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), "application/yaml")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("No yaml serializer")
|
t.Fatal("No yaml serializer")
|
||||||
}
|
}
|
||||||
encoder := api.Codecs.EncoderForVersion(serializer, testGroupVersion)
|
encoder := api.Codecs.EncoderForVersion(info.Serializer, testGroupVersion)
|
||||||
decoder := api.Codecs.DecoderToVersion(serializer, testInternalGroupVersion)
|
decoder := api.Codecs.DecoderToVersion(info.Serializer, testInternalGroupVersion)
|
||||||
|
|
||||||
data, err := runtime.Encode(encoder, simple)
|
data, err := runtime.Encode(encoder, simple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -3216,7 +3300,7 @@ func BenchmarkUpdateProtobuf(b *testing.B) {
|
|||||||
dest.Path = "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/foo/simples/bar"
|
dest.Path = "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/foo/simples/bar"
|
||||||
dest.RawQuery = ""
|
dest.RawQuery = ""
|
||||||
|
|
||||||
info, _ := api.Codecs.SerializerForMediaType("application/vnd.kubernetes.protobuf", nil)
|
info, _ := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), "application/vnd.kubernetes.protobuf")
|
||||||
e := api.Codecs.EncoderForVersion(info.Serializer, newGroupVersion)
|
e := api.Codecs.EncoderForVersion(info.Serializer, newGroupVersion)
|
||||||
data, err := runtime.Encode(e, &items[0])
|
data, err := runtime.Encode(e, &items[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -24,71 +24,66 @@ import (
|
|||||||
|
|
||||||
"bitbucket.org/ww/goautoneg"
|
"bitbucket.org/ww/goautoneg"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func negotiateOutput(req *http.Request, supported []string) (string, map[string]string, error) {
|
// mediaTypesForSerializer returns a list of media and stream media types for the server.
|
||||||
acceptHeader := req.Header.Get("Accept")
|
func mediaTypesForSerializer(ns runtime.NegotiatedSerializer) (mediaTypes, streamMediaTypes []string) {
|
||||||
if len(acceptHeader) == 0 && len(supported) > 0 {
|
for _, info := range ns.SupportedMediaTypes() {
|
||||||
acceptHeader = supported[0]
|
mediaTypes = append(mediaTypes, info.MediaType)
|
||||||
|
if info.StreamSerializer != nil {
|
||||||
|
// stream=watch is the existing mime-type parameter for watch
|
||||||
|
streamMediaTypes = append(streamMediaTypes, info.MediaType+";stream=watch")
|
||||||
}
|
}
|
||||||
accept, ok := negotiate(acceptHeader, supported)
|
|
||||||
if !ok {
|
|
||||||
return "", nil, errNotAcceptable{supported}
|
|
||||||
}
|
}
|
||||||
|
return mediaTypes, streamMediaTypes
|
||||||
pretty := isPrettyPrint(req)
|
|
||||||
if _, ok := accept.Params["pretty"]; !ok && pretty {
|
|
||||||
accept.Params["pretty"] = "1"
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaType := accept.Type
|
|
||||||
if len(accept.SubType) > 0 {
|
|
||||||
mediaType += "/" + accept.SubType
|
|
||||||
}
|
|
||||||
|
|
||||||
return mediaType, accept.Params, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func negotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
|
func negotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
|
||||||
supported := ns.SupportedMediaTypes()
|
mediaType, ok := negotiateMediaTypeOptions(req.Header.Get("Accept"), acceptedMediaTypesForEndpoint(ns), defaultEndpointRestrictions)
|
||||||
mediaType, params, err := negotiateOutput(req, supported)
|
if !ok {
|
||||||
if err != nil {
|
supported, _ := mediaTypesForSerializer(ns)
|
||||||
return runtime.SerializerInfo{}, err
|
|
||||||
}
|
|
||||||
if s, ok := ns.SerializerForMediaType(mediaType, params); ok {
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
return runtime.SerializerInfo{}, errNotAcceptable{supported}
|
return runtime.SerializerInfo{}, errNotAcceptable{supported}
|
||||||
}
|
}
|
||||||
|
// TODO: move into resthandler
|
||||||
func negotiateOutputStreamSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.StreamSerializerInfo, error) {
|
info := mediaType.accepted.Serializer
|
||||||
supported := ns.SupportedMediaTypes()
|
if (mediaType.pretty || isPrettyPrint(req)) && info.PrettySerializer != nil {
|
||||||
mediaType, params, err := negotiateOutput(req, supported)
|
info.Serializer = info.PrettySerializer
|
||||||
if err != nil {
|
|
||||||
return runtime.StreamSerializerInfo{}, err
|
|
||||||
}
|
}
|
||||||
if s, ok := ns.StreamingSerializerForMediaType(mediaType, params); ok {
|
return info, nil
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
return runtime.StreamSerializerInfo{}, errNotAcceptable{supported}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func negotiateInputSerializer(req *http.Request, s runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
|
func negotiateOutputStreamSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
|
||||||
supported := s.SupportedMediaTypes()
|
mediaType, ok := negotiateMediaTypeOptions(req.Header.Get("Accept"), acceptedMediaTypesForEndpoint(ns), defaultEndpointRestrictions)
|
||||||
|
if !ok || mediaType.accepted.Serializer.StreamSerializer == nil {
|
||||||
|
_, supported := mediaTypesForSerializer(ns)
|
||||||
|
return runtime.SerializerInfo{}, errNotAcceptable{supported}
|
||||||
|
}
|
||||||
|
return mediaType.accepted.Serializer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func negotiateInputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
|
||||||
|
mediaTypes := ns.SupportedMediaTypes()
|
||||||
mediaType := req.Header.Get("Content-Type")
|
mediaType := req.Header.Get("Content-Type")
|
||||||
if len(mediaType) == 0 {
|
if len(mediaType) == 0 {
|
||||||
mediaType = supported[0]
|
mediaType = mediaTypes[0].MediaType
|
||||||
}
|
}
|
||||||
mediaType, options, err := mime.ParseMediaType(mediaType)
|
mediaType, _, err := mime.ParseMediaType(mediaType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_, supported := mediaTypesForSerializer(ns)
|
||||||
return runtime.SerializerInfo{}, errUnsupportedMediaType{supported}
|
return runtime.SerializerInfo{}, errUnsupportedMediaType{supported}
|
||||||
}
|
}
|
||||||
out, ok := s.SerializerForMediaType(mediaType, options)
|
|
||||||
if !ok {
|
for _, info := range mediaTypes {
|
||||||
return runtime.SerializerInfo{}, errUnsupportedMediaType{supported}
|
if info.MediaType != mediaType {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
return out, nil
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, supported := mediaTypesForSerializer(ns)
|
||||||
|
return runtime.SerializerInfo{}, errUnsupportedMediaType{supported}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPrettyPrint returns true if the "pretty" query parameter is true or if the User-Agent
|
// isPrettyPrint returns true if the "pretty" query parameter is true or if the User-Agent
|
||||||
@ -135,3 +130,176 @@ func negotiate(header string, alternatives []string) (goautoneg.Accept, bool) {
|
|||||||
}
|
}
|
||||||
return goautoneg.Accept{}, false
|
return goautoneg.Accept{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endpointRestrictions is an interface that allows content-type negotiation
|
||||||
|
// to verify server support for specific options
|
||||||
|
type endpointRestrictions interface {
|
||||||
|
// AllowsConversion should return true if the specified group version kind
|
||||||
|
// is an allowed target object.
|
||||||
|
AllowsConversion(unversioned.GroupVersionKind) bool
|
||||||
|
// AllowsServerVersion should return true if the specified version is valid
|
||||||
|
// for the server group.
|
||||||
|
AllowsServerVersion(version string) bool
|
||||||
|
// AllowsStreamSchema should return true if the specified stream schema is
|
||||||
|
// valid for the server group.
|
||||||
|
AllowsStreamSchema(schema string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultEndpointRestrictions = emptyEndpointRestrictions{}
|
||||||
|
|
||||||
|
type emptyEndpointRestrictions struct{}
|
||||||
|
|
||||||
|
func (emptyEndpointRestrictions) AllowsConversion(unversioned.GroupVersionKind) bool { return false }
|
||||||
|
func (emptyEndpointRestrictions) AllowsServerVersion(string) bool { return false }
|
||||||
|
func (emptyEndpointRestrictions) AllowsStreamSchema(s string) bool { return s == "watch" }
|
||||||
|
|
||||||
|
// acceptedMediaType contains information about a valid media type that the
|
||||||
|
// server can serialize.
|
||||||
|
type acceptedMediaType struct {
|
||||||
|
// Type is the first part of the media type ("application")
|
||||||
|
Type string
|
||||||
|
// SubType is the second part of the media type ("json")
|
||||||
|
SubType string
|
||||||
|
// Serializer is the serialization info this object accepts
|
||||||
|
Serializer runtime.SerializerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// mediaTypeOptions describes information for a given media type that may alter
|
||||||
|
// the server response
|
||||||
|
type mediaTypeOptions struct {
|
||||||
|
// pretty is true if the requested representation should be formatted for human
|
||||||
|
// viewing
|
||||||
|
pretty bool
|
||||||
|
|
||||||
|
// stream, if set, indicates that a streaming protocol variant of this encoding
|
||||||
|
// is desired. The only currently supported value is watch which returns versioned
|
||||||
|
// events. In the future, this may refer to other stream protocols.
|
||||||
|
stream string
|
||||||
|
|
||||||
|
// convert is a request to alter the type of object returned by the server from the
|
||||||
|
// normal response
|
||||||
|
convert *unversioned.GroupVersionKind
|
||||||
|
// useServerVersion is an optional version for the server group
|
||||||
|
useServerVersion string
|
||||||
|
|
||||||
|
// export is true if the representation requested should exclude fields the server
|
||||||
|
// has set
|
||||||
|
export bool
|
||||||
|
|
||||||
|
// unrecognized is a list of all unrecognized keys
|
||||||
|
unrecognized []string
|
||||||
|
|
||||||
|
// the accepted media type from the client
|
||||||
|
accepted *acceptedMediaType
|
||||||
|
}
|
||||||
|
|
||||||
|
// acceptMediaTypeOptions returns an options object that matches the provided media type params. If
|
||||||
|
// it returns false, the provided options are not allowed and the media type must be skipped. These
|
||||||
|
// parameters are unversioned and may not be changed.
|
||||||
|
func acceptMediaTypeOptions(params map[string]string, accepts *acceptedMediaType, endpoint endpointRestrictions) (mediaTypeOptions, bool) {
|
||||||
|
var options mediaTypeOptions
|
||||||
|
|
||||||
|
// extract all known parameters
|
||||||
|
for k, v := range params {
|
||||||
|
switch k {
|
||||||
|
|
||||||
|
// controls transformation of the object when returned
|
||||||
|
case "as":
|
||||||
|
if options.convert == nil {
|
||||||
|
options.convert = &unversioned.GroupVersionKind{}
|
||||||
|
}
|
||||||
|
options.convert.Kind = v
|
||||||
|
case "g":
|
||||||
|
if options.convert == nil {
|
||||||
|
options.convert = &unversioned.GroupVersionKind{}
|
||||||
|
}
|
||||||
|
options.convert.Group = v
|
||||||
|
case "v":
|
||||||
|
if options.convert == nil {
|
||||||
|
options.convert = &unversioned.GroupVersionKind{}
|
||||||
|
}
|
||||||
|
options.convert.Version = v
|
||||||
|
|
||||||
|
// controls the streaming schema
|
||||||
|
case "stream":
|
||||||
|
if len(v) > 0 && (accepts.Serializer.StreamSerializer == nil || !endpoint.AllowsStreamSchema(v)) {
|
||||||
|
return mediaTypeOptions{}, false
|
||||||
|
}
|
||||||
|
options.stream = v
|
||||||
|
|
||||||
|
// controls the version of the server API group used
|
||||||
|
// for generic output
|
||||||
|
case "sv":
|
||||||
|
if len(v) > 0 && !endpoint.AllowsServerVersion(v) {
|
||||||
|
return mediaTypeOptions{}, false
|
||||||
|
}
|
||||||
|
options.useServerVersion = v
|
||||||
|
|
||||||
|
// if specified, the server should transform the returned
|
||||||
|
// output and remove fields that are always server specified,
|
||||||
|
// or which fit the default behavior.
|
||||||
|
case "export":
|
||||||
|
options.export = v == "1"
|
||||||
|
|
||||||
|
// if specified, the pretty serializer will be used
|
||||||
|
case "pretty":
|
||||||
|
options.pretty = v == "1"
|
||||||
|
|
||||||
|
default:
|
||||||
|
options.unrecognized = append(options.unrecognized, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.convert != nil && !endpoint.AllowsConversion(*options.convert) {
|
||||||
|
return mediaTypeOptions{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
options.accepted = accepts
|
||||||
|
|
||||||
|
return options, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// negotiateMediaTypeOptions returns the most appropriate content type given the accept header and
|
||||||
|
// a list of alternatives along with the accepted media type parameters.
|
||||||
|
func negotiateMediaTypeOptions(header string, accepted []acceptedMediaType, endpoint endpointRestrictions) (mediaTypeOptions, bool) {
|
||||||
|
if len(header) == 0 && len(accepted) > 0 {
|
||||||
|
return mediaTypeOptions{
|
||||||
|
accepted: &accepted[0],
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
clauses := goautoneg.ParseAccept(header)
|
||||||
|
for _, clause := range clauses {
|
||||||
|
for i := range accepted {
|
||||||
|
accepts := &accepted[i]
|
||||||
|
switch {
|
||||||
|
case clause.Type == accepts.Type && clause.SubType == accepts.SubType,
|
||||||
|
clause.Type == accepts.Type && clause.SubType == "*",
|
||||||
|
clause.Type == "*" && clause.SubType == "*":
|
||||||
|
// TODO: should we prefer the first type with no unrecognized options? Do we need to ignore unrecognized
|
||||||
|
// parameters.
|
||||||
|
return acceptMediaTypeOptions(clause.Params, accepts, endpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mediaTypeOptions{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// acceptedMediaTypesForEndpoint returns an array of structs that are used to efficiently check which
|
||||||
|
// allowed media types the server exposes.
|
||||||
|
func acceptedMediaTypesForEndpoint(ns runtime.NegotiatedSerializer) []acceptedMediaType {
|
||||||
|
var acceptedMediaTypes []acceptedMediaType
|
||||||
|
for _, info := range ns.SupportedMediaTypes() {
|
||||||
|
segments := strings.SplitN(info.MediaType, "/", 2)
|
||||||
|
if len(segments) == 1 {
|
||||||
|
segments = append(segments, "*")
|
||||||
|
}
|
||||||
|
t := acceptedMediaType{
|
||||||
|
Type: segments[0],
|
||||||
|
SubType: segments[1],
|
||||||
|
Serializer: info,
|
||||||
|
}
|
||||||
|
acceptedMediaTypes = append(acceptedMediaTypes, t)
|
||||||
|
}
|
||||||
|
return acceptedMediaTypes
|
||||||
|
}
|
||||||
|
@ -19,7 +19,6 @@ package apiserver
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
@ -30,38 +29,24 @@ type fakeNegotiater struct {
|
|||||||
serializer, streamSerializer runtime.Serializer
|
serializer, streamSerializer runtime.Serializer
|
||||||
framer runtime.Framer
|
framer runtime.Framer
|
||||||
types, streamTypes []string
|
types, streamTypes []string
|
||||||
mediaType, streamMediaType string
|
|
||||||
options, streamOptions map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fakeNegotiater) SupportedMediaTypes() []string {
|
func (n *fakeNegotiater) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||||
return n.types
|
var out []runtime.SerializerInfo
|
||||||
}
|
for _, s := range n.types {
|
||||||
func (n *fakeNegotiater) SupportedStreamingMediaTypes() []string {
|
info := runtime.SerializerInfo{Serializer: n.serializer, MediaType: s, EncodesAsText: true}
|
||||||
return n.streamTypes
|
for _, t := range n.streamTypes {
|
||||||
}
|
if t == s {
|
||||||
|
info.StreamSerializer = &runtime.StreamSerializerInfo{
|
||||||
func (n *fakeNegotiater) SerializerForMediaType(mediaType string, options map[string]string) (runtime.SerializerInfo, bool) {
|
|
||||||
n.mediaType = mediaType
|
|
||||||
if len(options) > 0 {
|
|
||||||
n.options = options
|
|
||||||
}
|
|
||||||
return runtime.SerializerInfo{Serializer: n.serializer, MediaType: n.mediaType, EncodesAsText: true}, n.serializer != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *fakeNegotiater) StreamingSerializerForMediaType(mediaType string, options map[string]string) (runtime.StreamSerializerInfo, bool) {
|
|
||||||
n.streamMediaType = mediaType
|
|
||||||
if len(options) > 0 {
|
|
||||||
n.streamOptions = options
|
|
||||||
}
|
|
||||||
return runtime.StreamSerializerInfo{
|
|
||||||
SerializerInfo: runtime.SerializerInfo{
|
|
||||||
Serializer: n.serializer,
|
|
||||||
MediaType: mediaType,
|
|
||||||
EncodesAsText: true,
|
EncodesAsText: true,
|
||||||
},
|
|
||||||
Framer: n.framer,
|
Framer: n.framer,
|
||||||
}, n.streamSerializer != nil
|
Serializer: n.streamSerializer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out = append(out, info)
|
||||||
|
}
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||||
@ -201,12 +186,6 @@ func TestNegotiate(t *testing.T) {
|
|||||||
return err.Error() == "only the following media types are accepted: application"
|
return err.Error() == "only the following media types are accepted: application"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
ns: &fakeNegotiater{types: []string{"a/b/c"}},
|
|
||||||
errFn: func(err error) bool {
|
|
||||||
return err.Error() == "only the following media types are accepted: a/b/c"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
ns: &fakeNegotiater{},
|
ns: &fakeNegotiater{},
|
||||||
errFn: func(err error) bool {
|
errFn: func(err error) bool {
|
||||||
@ -220,13 +199,6 @@ func TestNegotiate(t *testing.T) {
|
|||||||
return err.Error() == "only the following media types are accepted: "
|
return err.Error() == "only the following media types are accepted: "
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
accept: "application/json",
|
|
||||||
ns: &fakeNegotiater{types: []string{"application/json"}},
|
|
||||||
errFn: func(err error) bool {
|
|
||||||
return err.Error() == "only the following media types are accepted: application/json"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
@ -264,8 +236,5 @@ func TestNegotiate(t *testing.T) {
|
|||||||
if s.Serializer != test.serializer {
|
if s.Serializer != test.serializer {
|
||||||
t.Errorf("%d: unexpected %s %s", i, test.serializer, s.Serializer)
|
t.Errorf("%d: unexpected %s %s", i, test.serializer, s.Serializer)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(test.params, test.ns.options) {
|
|
||||||
t.Errorf("%d: unexpected %#v %#v", i, test.params, test.ns.options)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,7 +364,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
|
|||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, res.ResponseWriter, req.Request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
decoder := scope.Serializer.DecoderToVersion(s, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal})
|
decoder := scope.Serializer.DecoderToVersion(s.Serializer, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal})
|
||||||
|
|
||||||
body, err := readBody(req.Request)
|
body, err := readBody(req.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -480,15 +480,15 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s, ok := scope.Serializer.SerializerForMediaType("application/json", nil)
|
s, ok := runtime.SerializerInfoForMediaType(scope.Serializer.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
if !ok {
|
if !ok {
|
||||||
scope.err(fmt.Errorf("no serializer defined for JSON"), res.ResponseWriter, req.Request)
|
scope.err(fmt.Errorf("no serializer defined for JSON"), res.ResponseWriter, req.Request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gv := scope.Kind.GroupVersion()
|
gv := scope.Kind.GroupVersion()
|
||||||
codec := runtime.NewCodec(
|
codec := runtime.NewCodec(
|
||||||
scope.Serializer.EncoderForVersion(s, gv),
|
scope.Serializer.EncoderForVersion(s.Serializer, gv),
|
||||||
scope.Serializer.DecoderToVersion(s, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}),
|
scope.Serializer.DecoderToVersion(s.Serializer, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}),
|
||||||
)
|
)
|
||||||
|
|
||||||
updateAdmit := func(updatedObject runtime.Object, currentObject runtime.Object) error {
|
updateAdmit := func(updatedObject runtime.Object, currentObject runtime.Object) error {
|
||||||
@ -685,7 +685,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType
|
|||||||
defaultGVK := scope.Kind
|
defaultGVK := scope.Kind
|
||||||
original := r.New()
|
original := r.New()
|
||||||
trace.Step("About to convert to expected version")
|
trace.Step("About to convert to expected version")
|
||||||
obj, gvk, err := scope.Serializer.DecoderToVersion(s, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, original)
|
obj, gvk, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, original)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = transformDecodeError(typer, err, original, gvk, body)
|
err = transformDecodeError(typer, err, original, gvk, body)
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, res.ResponseWriter, req.Request)
|
||||||
@ -772,7 +772,7 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions")
|
defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions")
|
||||||
obj, _, err := scope.Serializer.DecoderToVersion(s, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
|
obj, _, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, res.ResponseWriter, req.Request)
|
||||||
return
|
return
|
||||||
@ -889,7 +889,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions")
|
defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions")
|
||||||
obj, _, err := scope.Serializer.DecoderToVersion(s, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
|
obj, _, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, res.ResponseWriter, req.Request)
|
||||||
return
|
return
|
||||||
|
@ -68,24 +68,33 @@ func serveWatch(watcher watch.Interface, scope RequestScope, req *restful.Reques
|
|||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, res.ResponseWriter, req.Request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if serializer.Framer == nil {
|
framer := serializer.StreamSerializer.Framer
|
||||||
|
streamSerializer := serializer.StreamSerializer.Serializer
|
||||||
|
embedded := serializer.Serializer
|
||||||
|
if framer == nil {
|
||||||
scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", serializer.MediaType), res.ResponseWriter, req.Request)
|
scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", serializer.MediaType), res.ResponseWriter, req.Request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
encoder := scope.Serializer.EncoderForVersion(serializer.Serializer, scope.Kind.GroupVersion())
|
encoder := scope.Serializer.EncoderForVersion(streamSerializer, scope.Kind.GroupVersion())
|
||||||
|
|
||||||
useTextFraming := serializer.EncodesAsText
|
useTextFraming := serializer.EncodesAsText
|
||||||
|
|
||||||
// find the embedded serializer matching the media type
|
// find the embedded serializer matching the media type
|
||||||
embeddedEncoder := scope.Serializer.EncoderForVersion(serializer.Embedded.Serializer, scope.Kind.GroupVersion())
|
embeddedEncoder := scope.Serializer.EncoderForVersion(embedded, scope.Kind.GroupVersion())
|
||||||
|
|
||||||
|
// TODO: next step, get back mediaTypeOptions from negotiate and return the exact value here
|
||||||
|
mediaType := serializer.MediaType
|
||||||
|
if mediaType != runtime.ContentTypeJSON {
|
||||||
|
mediaType += ";stream=watch"
|
||||||
|
}
|
||||||
|
|
||||||
server := &WatchServer{
|
server := &WatchServer{
|
||||||
watching: watcher,
|
watching: watcher,
|
||||||
scope: scope,
|
scope: scope,
|
||||||
|
|
||||||
useTextFraming: useTextFraming,
|
useTextFraming: useTextFraming,
|
||||||
mediaType: serializer.MediaType,
|
mediaType: mediaType,
|
||||||
framer: serializer.Framer,
|
framer: framer,
|
||||||
encoder: encoder,
|
encoder: encoder,
|
||||||
embeddedEncoder: embeddedEncoder,
|
embeddedEncoder: embeddedEncoder,
|
||||||
fixup: func(obj runtime.Object) {
|
fixup: func(obj runtime.Object) {
|
||||||
|
@ -234,7 +234,8 @@ func TestWatchRead(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if response.StatusCode != http.StatusOK {
|
if response.StatusCode != http.StatusOK {
|
||||||
t.Fatalf("Unexpected response %#v", response)
|
b, _ := ioutil.ReadAll(response.Body)
|
||||||
|
t.Fatalf("Unexpected response for accept: %q: %#v\n%s", accept, response, string(b))
|
||||||
}
|
}
|
||||||
return response.Body, response.Header.Get("Content-Type")
|
return response.Body, response.Header.Get("Content-Type")
|
||||||
}
|
}
|
||||||
@ -264,6 +265,11 @@ func TestWatchRead(t *testing.T) {
|
|||||||
ExpectedContentType: "application/json",
|
ExpectedContentType: "application/json",
|
||||||
MediaType: "application/json",
|
MediaType: "application/json",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Accept: "application/json;stream=watch",
|
||||||
|
ExpectedContentType: "application/json", // legacy behavior
|
||||||
|
MediaType: "application/json",
|
||||||
|
},
|
||||||
// TODO: yaml stream serialization requires that RawExtension.MarshalJSON
|
// TODO: yaml stream serialization requires that RawExtension.MarshalJSON
|
||||||
// be able to understand nested encoding (since yaml calls json.Marshal
|
// be able to understand nested encoding (since yaml calls json.Marshal
|
||||||
// rather than yaml.Marshal, which results in the raw bytes being in yaml).
|
// rather than yaml.Marshal, which results in the raw bytes being in yaml).
|
||||||
@ -295,10 +301,11 @@ func TestWatchRead(t *testing.T) {
|
|||||||
|
|
||||||
for _, protocol := range protocols {
|
for _, protocol := range protocols {
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
serializer, ok := api.Codecs.StreamingSerializerForMediaType(test.MediaType, nil)
|
info, ok := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), test.MediaType)
|
||||||
if !ok {
|
if !ok || info.StreamSerializer == nil {
|
||||||
t.Fatal(serializer)
|
t.Fatal(info)
|
||||||
}
|
}
|
||||||
|
streamSerializer := info.StreamSerializer
|
||||||
|
|
||||||
r, contentType := protocol.fn(test.Accept)
|
r, contentType := protocol.fn(test.Accept)
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
@ -306,17 +313,13 @@ func TestWatchRead(t *testing.T) {
|
|||||||
if contentType != "__default__" && contentType != test.ExpectedContentType {
|
if contentType != "__default__" && contentType != test.ExpectedContentType {
|
||||||
t.Errorf("Unexpected content type: %#v", contentType)
|
t.Errorf("Unexpected content type: %#v", contentType)
|
||||||
}
|
}
|
||||||
objectSerializer, ok := api.Codecs.SerializerForMediaType(test.MediaType, nil)
|
objectCodec := api.Codecs.DecoderToVersion(info.Serializer, testInternalGroupVersion)
|
||||||
if !ok {
|
|
||||||
t.Fatal(objectSerializer)
|
|
||||||
}
|
|
||||||
objectCodec := api.Codecs.DecoderToVersion(objectSerializer, testInternalGroupVersion)
|
|
||||||
|
|
||||||
var fr io.ReadCloser = r
|
var fr io.ReadCloser = r
|
||||||
if !protocol.selfFraming {
|
if !protocol.selfFraming {
|
||||||
fr = serializer.Framer.NewFrameReader(r)
|
fr = streamSerializer.Framer.NewFrameReader(r)
|
||||||
}
|
}
|
||||||
d := streaming.NewDecoder(fr, serializer)
|
d := streaming.NewDecoder(fr, streamSerializer.Serializer)
|
||||||
|
|
||||||
var w *watch.FakeWatcher
|
var w *watch.FakeWatcher
|
||||||
for w == nil {
|
for w == nil {
|
||||||
@ -568,10 +571,11 @@ func TestWatchHTTPTimeout(t *testing.T) {
|
|||||||
timeoutCh := make(chan time.Time)
|
timeoutCh := make(chan time.Time)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
serializer, ok := api.Codecs.StreamingSerializerForMediaType("application/json", nil)
|
info, ok := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
if !ok {
|
if !ok || info.StreamSerializer == nil {
|
||||||
t.Fatal(serializer)
|
t.Fatal(info)
|
||||||
}
|
}
|
||||||
|
serializer := info.StreamSerializer
|
||||||
|
|
||||||
// Setup a new watchserver
|
// Setup a new watchserver
|
||||||
watchServer := &WatchServer{
|
watchServer := &WatchServer{
|
||||||
|
@ -18,6 +18,7 @@ package restclient
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@ -153,34 +154,48 @@ func readExpBackoffConfig() BackoffManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createSerializers creates all necessary serializers for given contentType.
|
// createSerializers creates all necessary serializers for given contentType.
|
||||||
|
// TODO: the negotiated serializer passed to this method should probably return
|
||||||
|
// serializers that control decoding and versioning without this package
|
||||||
|
// being aware of the types. Depends on whether RESTClient must deal with
|
||||||
|
// generic infrastructure.
|
||||||
func createSerializers(config ContentConfig) (*Serializers, error) {
|
func createSerializers(config ContentConfig) (*Serializers, error) {
|
||||||
negotiated := config.NegotiatedSerializer
|
mediaTypes := config.NegotiatedSerializer.SupportedMediaTypes()
|
||||||
contentType := config.ContentType
|
contentType := config.ContentType
|
||||||
info, ok := negotiated.SerializerForMediaType(contentType, nil)
|
mediaType, _, err := mime.ParseMediaType(contentType)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("serializer for %s not registered", contentType)
|
return nil, fmt.Errorf("the content type specified in the client configuration is not recognized: %v", err)
|
||||||
}
|
}
|
||||||
streamInfo, ok := negotiated.StreamingSerializerForMediaType(contentType, nil)
|
info, ok := runtime.SerializerInfoForMediaType(mediaTypes, mediaType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("streaming serializer for %s not registered", contentType)
|
if len(contentType) != 0 || len(mediaTypes) == 0 {
|
||||||
|
return nil, fmt.Errorf("no serializers registered for %s", contentType)
|
||||||
}
|
}
|
||||||
|
info = mediaTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
internalGV := unversioned.GroupVersion{
|
internalGV := unversioned.GroupVersion{
|
||||||
Group: config.GroupVersion.Group,
|
Group: config.GroupVersion.Group,
|
||||||
Version: runtime.APIVersionInternal,
|
Version: runtime.APIVersionInternal,
|
||||||
}
|
}
|
||||||
return &Serializers{
|
|
||||||
Encoder: negotiated.EncoderForVersion(info.Serializer, *config.GroupVersion),
|
s := &Serializers{
|
||||||
Decoder: negotiated.DecoderToVersion(info.Serializer, internalGV),
|
Encoder: config.NegotiatedSerializer.EncoderForVersion(info.Serializer, *config.GroupVersion),
|
||||||
StreamingSerializer: streamInfo.Serializer,
|
Decoder: config.NegotiatedSerializer.DecoderToVersion(info.Serializer, internalGV),
|
||||||
Framer: streamInfo.Framer,
|
|
||||||
RenegotiatedDecoder: func(contentType string, params map[string]string) (runtime.Decoder, error) {
|
RenegotiatedDecoder: func(contentType string, params map[string]string) (runtime.Decoder, error) {
|
||||||
renegotiated, ok := negotiated.SerializerForMediaType(contentType, params)
|
info, ok := runtime.SerializerInfoForMediaType(mediaTypes, contentType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("serializer for %s not registered", contentType)
|
return nil, fmt.Errorf("serializer for %s not registered", contentType)
|
||||||
}
|
}
|
||||||
return negotiated.DecoderToVersion(renegotiated.Serializer, internalGV), nil
|
return config.NegotiatedSerializer.DecoderToVersion(info.Serializer, internalGV), nil
|
||||||
},
|
},
|
||||||
}, nil
|
}
|
||||||
|
if info.StreamSerializer != nil {
|
||||||
|
s.StreamingSerializer = info.StreamSerializer.Serializer
|
||||||
|
s.Framer = info.StreamSerializer.Framer
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verb begins a request with a verb (GET, POST, PUT, DELETE).
|
// Verb begins a request with a verb (GET, POST, PUT, DELETE).
|
||||||
|
@ -153,20 +153,8 @@ var fakeWrapperFunc = func(http.RoundTripper) http.RoundTripper {
|
|||||||
|
|
||||||
type fakeNegotiatedSerializer struct{}
|
type fakeNegotiatedSerializer struct{}
|
||||||
|
|
||||||
func (n *fakeNegotiatedSerializer) SupportedMediaTypes() []string {
|
func (n *fakeNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||||
return []string{}
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
func (n *fakeNegotiatedSerializer) SerializerForMediaType(mediaType string, params map[string]string) (s runtime.SerializerInfo, ok bool) {
|
|
||||||
return runtime.SerializerInfo{}, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *fakeNegotiatedSerializer) SupportedStreamingMediaTypes() []string {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *fakeNegotiatedSerializer) StreamingSerializerForMediaType(mediaType string, params map[string]string) (s runtime.StreamSerializerInfo, ok bool) {
|
|
||||||
return runtime.StreamSerializerInfo{}, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fakeNegotiatedSerializer) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
func (n *fakeNegotiatedSerializer) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||||
|
@ -306,10 +306,7 @@ func setDiscoveryDefaults(config *restclient.Config) error {
|
|||||||
config.APIPath = ""
|
config.APIPath = ""
|
||||||
config.GroupVersion = nil
|
config.GroupVersion = nil
|
||||||
codec := runtime.NoopEncoder{Decoder: api.Codecs.UniversalDecoder()}
|
codec := runtime.NoopEncoder{Decoder: api.Codecs.UniversalDecoder()}
|
||||||
config.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(
|
config.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
|
||||||
runtime.SerializerInfo{Serializer: codec},
|
|
||||||
runtime.StreamSerializerInfo{},
|
|
||||||
)
|
|
||||||
if len(config.UserAgent) == 0 {
|
if len(config.UserAgent) == 0 {
|
||||||
config.UserAgent = restclient.DefaultKubernetesUserAgent()
|
config.UserAgent = restclient.DefaultKubernetesUserAgent()
|
||||||
}
|
}
|
||||||
|
@ -241,13 +241,22 @@ func (dynamicCodec) Encode(obj runtime.Object, w io.Writer) error {
|
|||||||
|
|
||||||
// ContentConfig returns a restclient.ContentConfig for dynamic types.
|
// ContentConfig returns a restclient.ContentConfig for dynamic types.
|
||||||
func ContentConfig() restclient.ContentConfig {
|
func ContentConfig() restclient.ContentConfig {
|
||||||
// TODO: it's questionable that this should be using anything other than unstructured schema and JSON
|
var jsonInfo runtime.SerializerInfo
|
||||||
codec := dynamicCodec{}
|
// TODO: api.Codecs here should become "pkg/apis/server/scheme" which is the minimal core you need
|
||||||
streamingInfo, _ := api.Codecs.StreamingSerializerForMediaType("application/json;stream=watch", nil)
|
// to talk to a kubernetes server
|
||||||
|
for _, info := range api.Codecs.SupportedMediaTypes() {
|
||||||
|
if info.MediaType == runtime.ContentTypeJSON {
|
||||||
|
jsonInfo = info
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonInfo.Serializer = dynamicCodec{}
|
||||||
|
jsonInfo.PrettySerializer = nil
|
||||||
return restclient.ContentConfig{
|
return restclient.ContentConfig{
|
||||||
AcceptContentTypes: runtime.ContentTypeJSON,
|
AcceptContentTypes: runtime.ContentTypeJSON,
|
||||||
ContentType: runtime.ContentTypeJSON,
|
ContentType: runtime.ContentTypeJSON,
|
||||||
NegotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec}, streamingInfo),
|
NegotiatedSerializer: serializer.NegotiatedSerializerWrapper(jsonInfo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,9 @@ package dynamic
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
|
||||||
"k8s.io/kubernetes/pkg/api/meta"
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/client/restclient"
|
"k8s.io/kubernetes/pkg/client/restclient"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClientPool manages a pool of dynamic clients.
|
// ClientPool manages a pool of dynamic clients.
|
||||||
@ -64,6 +61,7 @@ type clientPoolImpl struct {
|
|||||||
// resources or groups.
|
// resources or groups.
|
||||||
func NewClientPool(config *restclient.Config, mapper meta.RESTMapper, apiPathResolverFunc APIPathResolverFunc) ClientPool {
|
func NewClientPool(config *restclient.Config, mapper meta.RESTMapper, apiPathResolverFunc APIPathResolverFunc) ClientPool {
|
||||||
confCopy := *config
|
confCopy := *config
|
||||||
|
|
||||||
return &clientPoolImpl{
|
return &clientPoolImpl{
|
||||||
config: &confCopy,
|
config: &confCopy,
|
||||||
clients: map[unversioned.GroupVersion]*Client{},
|
clients: map[unversioned.GroupVersion]*Client{},
|
||||||
@ -108,11 +106,6 @@ func (c *clientPoolImpl) ClientForGroupVersionKind(kind unversioned.GroupVersion
|
|||||||
// we need to make a client
|
// we need to make a client
|
||||||
conf.GroupVersion = &gv
|
conf.GroupVersion = &gv
|
||||||
|
|
||||||
if conf.NegotiatedSerializer == nil {
|
|
||||||
streamingInfo, _ := api.Codecs.StreamingSerializerForMediaType("application/json;stream=watch", nil)
|
|
||||||
conf.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: dynamicCodec{}}, streamingInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamicClient, err := NewClient(conf)
|
dynamicClient, err := NewClient(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -529,6 +529,7 @@ func TestPatch(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Write(data)
|
w.Write(data)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -92,25 +92,25 @@ func (c *RESTClient) request(verb string) *restclient.Request {
|
|||||||
GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion,
|
GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion,
|
||||||
NegotiatedSerializer: c.NegotiatedSerializer,
|
NegotiatedSerializer: c.NegotiatedSerializer,
|
||||||
}
|
}
|
||||||
ns := c.NegotiatedSerializer
|
|
||||||
serializer, _ := ns.SerializerForMediaType(runtime.ContentTypeJSON, nil)
|
|
||||||
streamingSerializer, _ := ns.StreamingSerializerForMediaType(runtime.ContentTypeJSON, nil)
|
|
||||||
|
|
||||||
groupName := api.GroupName
|
groupName := api.GroupName
|
||||||
if c.GroupName != "" {
|
if c.GroupName != "" {
|
||||||
groupName = c.GroupName
|
groupName = c.GroupName
|
||||||
}
|
}
|
||||||
|
ns := c.NegotiatedSerializer
|
||||||
|
info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
internalVersion := unversioned.GroupVersion{
|
internalVersion := unversioned.GroupVersion{
|
||||||
Group: registered.GroupOrDie(groupName).GroupVersion.Group,
|
Group: registered.GroupOrDie(groupName).GroupVersion.Group,
|
||||||
Version: runtime.APIVersionInternal,
|
Version: runtime.APIVersionInternal,
|
||||||
}
|
}
|
||||||
internalVersion.Version = runtime.APIVersionInternal
|
internalVersion.Version = runtime.APIVersionInternal
|
||||||
serializers := restclient.Serializers{
|
serializers := restclient.Serializers{
|
||||||
Encoder: ns.EncoderForVersion(serializer, registered.GroupOrDie(api.GroupName).GroupVersion),
|
Encoder: ns.EncoderForVersion(info.Serializer, registered.GroupOrDie(api.GroupName).GroupVersion),
|
||||||
Decoder: ns.DecoderToVersion(serializer, internalVersion),
|
Decoder: ns.DecoderToVersion(info.Serializer, internalVersion),
|
||||||
StreamingSerializer: streamingSerializer,
|
}
|
||||||
Framer: streamingSerializer.Framer,
|
if info.StreamSerializer != nil {
|
||||||
|
serializers.StreamingSerializer = info.StreamSerializer.Serializer
|
||||||
|
serializers.Framer = info.StreamSerializer.Framer
|
||||||
}
|
}
|
||||||
return restclient.NewRequest(c, verb, &url.URL{Host: "localhost"}, "", config, serializers, nil, nil)
|
return restclient.NewRequest(c, verb, &url.URL{Host: "localhost"}, "", config, serializers, nil, nil)
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,7 @@ func (f *fakeActionHandler) ServeHTTP(response http.ResponseWriter, request *htt
|
|||||||
fakeResponse.statusCode = 200
|
fakeResponse.statusCode = 200
|
||||||
fakeResponse.content = []byte("{\"kind\": \"List\"}")
|
fakeResponse.content = []byte("{\"kind\": \"List\"}")
|
||||||
}
|
}
|
||||||
|
response.Header().Set("Content-Type", "application/json")
|
||||||
response.WriteHeader(fakeResponse.statusCode)
|
response.WriteHeader(fakeResponse.statusCode)
|
||||||
response.Write(fakeResponse.content)
|
response.Write(fakeResponse.content)
|
||||||
}
|
}
|
||||||
|
@ -87,11 +87,11 @@ func verfiyMetadata(description string, t *testing.T, in *MetadataOnlyObject) {
|
|||||||
func TestDecodeToMetadataOnlyObject(t *testing.T) {
|
func TestDecodeToMetadataOnlyObject(t *testing.T) {
|
||||||
data := getPodJson(t)
|
data := getPodJson(t)
|
||||||
cf := serializer.DirectCodecFactory{CodecFactory: NewMetadataCodecFactory()}
|
cf := serializer.DirectCodecFactory{CodecFactory: NewMetadataCodecFactory()}
|
||||||
serializer, ok := cf.SerializerForMediaType(runtime.ContentTypeJSON, nil)
|
info, ok := runtime.SerializerInfoForMediaType(cf.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("expected to get a JSON serializer")
|
t.Fatalf("expected to get a JSON serializer")
|
||||||
}
|
}
|
||||||
codec := cf.DecoderToVersion(serializer, unversioned.GroupVersion{Group: "SOMEGROUP", Version: "SOMEVERSION"})
|
codec := cf.DecoderToVersion(info.Serializer, unversioned.GroupVersion{Group: "SOMEGROUP", Version: "SOMEVERSION"})
|
||||||
// decode with into
|
// decode with into
|
||||||
into := &MetadataOnlyObject{}
|
into := &MetadataOnlyObject{}
|
||||||
ret, _, err := codec.Decode(data, nil, into)
|
ret, _, err := codec.Decode(data, nil, into)
|
||||||
@ -133,11 +133,11 @@ func verifyListMetadata(t *testing.T, metaOnlyList *MetadataOnlyObjectList) {
|
|||||||
func TestDecodeToMetadataOnlyObjectList(t *testing.T) {
|
func TestDecodeToMetadataOnlyObjectList(t *testing.T) {
|
||||||
data := getPodListJson(t)
|
data := getPodListJson(t)
|
||||||
cf := serializer.DirectCodecFactory{CodecFactory: NewMetadataCodecFactory()}
|
cf := serializer.DirectCodecFactory{CodecFactory: NewMetadataCodecFactory()}
|
||||||
serializer, ok := cf.SerializerForMediaType(runtime.ContentTypeJSON, nil)
|
info, ok := runtime.SerializerInfoForMediaType(cf.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("expected to get a JSON serializer")
|
t.Fatalf("expected to get a JSON serializer")
|
||||||
}
|
}
|
||||||
codec := cf.DecoderToVersion(serializer, unversioned.GroupVersion{Group: "SOMEGROUP", Version: "SOMEVERSION"})
|
codec := cf.DecoderToVersion(info.Serializer, unversioned.GroupVersion{Group: "SOMEGROUP", Version: "SOMEVERSION"})
|
||||||
// decode with into
|
// decode with into
|
||||||
into := &MetadataOnlyObjectList{}
|
into := &MetadataOnlyObjectList{}
|
||||||
ret, _, err := codec.Decode(data, nil, into)
|
ret, _, err := codec.Decode(data, nil, into)
|
||||||
|
@ -243,11 +243,11 @@ func (s *DefaultStorageFactory) Backends() []string {
|
|||||||
// NewStorageCodec assembles a storage codec for the provided storage media type, the provided serializer, and the requested
|
// NewStorageCodec assembles a storage codec for the provided storage media type, the provided serializer, and the requested
|
||||||
// storage and memory versions.
|
// storage and memory versions.
|
||||||
func NewStorageCodec(storageMediaType string, ns runtime.StorageSerializer, storageVersion, memoryVersion unversioned.GroupVersion, config storagebackend.Config) (runtime.Codec, error) {
|
func NewStorageCodec(storageMediaType string, ns runtime.StorageSerializer, storageVersion, memoryVersion unversioned.GroupVersion, config storagebackend.Config) (runtime.Codec, error) {
|
||||||
mediaType, options, err := mime.ParseMediaType(storageMediaType)
|
mediaType, _, err := mime.ParseMediaType(storageMediaType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%q is not a valid mime-type", storageMediaType)
|
return nil, fmt.Errorf("%q is not a valid mime-type", storageMediaType)
|
||||||
}
|
}
|
||||||
serializer, ok := ns.SerializerForMediaType(mediaType, options)
|
serializer, ok := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), mediaType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unable to find serializer for %q", storageMediaType)
|
return nil, fmt.Errorf("unable to find serializer for %q", storageMediaType)
|
||||||
}
|
}
|
||||||
|
@ -193,9 +193,7 @@ func TestGetUnknownSchemaObjectListGeneric(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f, tf, codec := cmdtesting.NewMixedFactory(regularClient)
|
f, tf, codec := cmdtesting.NewMixedFactory(regularClient)
|
||||||
negotiatedSerializer := serializer.NegotiatedSerializerWrapper(
|
negotiatedSerializer := serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
|
||||||
runtime.SerializerInfo{Serializer: codec},
|
|
||||||
runtime.StreamSerializerInfo{})
|
|
||||||
tf.Printer = &testPrinter{}
|
tf.Printer = &testPrinter{}
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
NegotiatedSerializer: negotiatedSerializer,
|
NegotiatedSerializer: negotiatedSerializer,
|
||||||
|
@ -157,9 +157,7 @@ func NewTestFactory() (cmdutil.Factory, *TestFactory, runtime.Codec, runtime.Neg
|
|||||||
Mapper: mapper,
|
Mapper: mapper,
|
||||||
Typer: scheme,
|
Typer: scheme,
|
||||||
}
|
}
|
||||||
negotiatedSerializer := serializer.NegotiatedSerializerWrapper(
|
negotiatedSerializer := serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
|
||||||
runtime.SerializerInfo{Serializer: codec},
|
|
||||||
runtime.StreamSerializerInfo{})
|
|
||||||
return &FakeFactory{
|
return &FakeFactory{
|
||||||
tf: t,
|
tf: t,
|
||||||
Codec: codec,
|
Codec: codec,
|
||||||
|
@ -71,8 +71,8 @@ func TestDecodeSinglePod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, gv := range registered.EnabledVersionsForGroup(api.GroupName) {
|
for _, gv := range registered.EnabledVersionsForGroup(api.GroupName) {
|
||||||
s, _ := api.Codecs.SerializerForFileExtension("yaml")
|
info, _ := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), "application/yaml")
|
||||||
encoder := api.Codecs.EncoderForVersion(s, gv)
|
encoder := api.Codecs.EncoderForVersion(info.Serializer, gv)
|
||||||
yaml, err := runtime.Encode(encoder, pod)
|
yaml, err := runtime.Encode(encoder, pod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
@ -134,8 +134,8 @@ func TestDecodePodList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, gv := range registered.EnabledVersionsForGroup(api.GroupName) {
|
for _, gv := range registered.EnabledVersionsForGroup(api.GroupName) {
|
||||||
s, _ := api.Codecs.SerializerForFileExtension("yaml")
|
info, _ := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), "application/yaml")
|
||||||
encoder := api.Codecs.EncoderForVersion(s, gv)
|
encoder := api.Codecs.EncoderForVersion(info.Serializer, gv)
|
||||||
yaml, err := runtime.Encode(encoder, podList)
|
yaml, err := runtime.Encode(encoder, podList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
@ -33,7 +33,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
|
||||||
"k8s.io/kubernetes/pkg/util/yaml"
|
"k8s.io/kubernetes/pkg/util/yaml"
|
||||||
"k8s.io/kubernetes/pkg/watch/versioned"
|
"k8s.io/kubernetes/pkg/watch/versioned"
|
||||||
)
|
)
|
||||||
@ -206,32 +205,13 @@ func NewNegotiatedSerializer(s runtime.NegotiatedSerializer, kind string, encode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodecFactory) SupportedMediaTypes() []string {
|
func (t *thirdPartyResourceDataCodecFactory) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||||
supported := sets.NewString(t.delegate.SupportedMediaTypes()...)
|
for _, info := range t.delegate.SupportedMediaTypes() {
|
||||||
return supported.Intersection(sets.NewString("application/json", "application/yaml")).List()
|
if info.MediaType == runtime.ContentTypeJSON {
|
||||||
}
|
return []runtime.SerializerInfo{info}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodecFactory) SerializerForMediaType(mediaType string, params map[string]string) (runtime.SerializerInfo, bool) {
|
|
||||||
switch mediaType {
|
|
||||||
case "application/json", "application/yaml":
|
|
||||||
return t.delegate.SerializerForMediaType(mediaType, params)
|
|
||||||
default:
|
|
||||||
return runtime.SerializerInfo{}, false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
func (t *thirdPartyResourceDataCodecFactory) SupportedStreamingMediaTypes() []string {
|
|
||||||
supported := sets.NewString(t.delegate.SupportedStreamingMediaTypes()...)
|
|
||||||
return supported.Intersection(sets.NewString("application/json", "application/json;stream=watch")).List()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodecFactory) StreamingSerializerForMediaType(mediaType string, params map[string]string) (runtime.StreamSerializerInfo, bool) {
|
|
||||||
switch mediaType {
|
|
||||||
case "application/json", "application/json;stream=watch":
|
|
||||||
return t.delegate.StreamingSerializerForMediaType(mediaType, params)
|
|
||||||
default:
|
|
||||||
return runtime.StreamSerializerInfo{}, false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||||
|
@ -217,6 +217,22 @@ func (s base64Serializer) Decode(data []byte, defaults *unversioned.GroupVersion
|
|||||||
return s.Serializer.Decode(out[:n], defaults, into)
|
return s.Serializer.Decode(out[:n], defaults, into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SerializerInfoForMediaType returns the first info in types that has a matching media type (which cannot
|
||||||
|
// include media-type parameters), or the first info with an empty media type, or false if no type matches.
|
||||||
|
func SerializerInfoForMediaType(types []SerializerInfo, mediaType string) (SerializerInfo, bool) {
|
||||||
|
for _, info := range types {
|
||||||
|
if info.MediaType == mediaType {
|
||||||
|
return info, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, info := range types {
|
||||||
|
if len(info.MediaType) == 0 {
|
||||||
|
return info, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SerializerInfo{}, false
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// InternalGroupVersioner will always prefer the internal version for a given group version kind.
|
// InternalGroupVersioner will always prefer the internal version for a given group version kind.
|
||||||
InternalGroupVersioner GroupVersioner = internalGroupVersioner{}
|
InternalGroupVersioner GroupVersioner = internalGroupVersioner{}
|
||||||
|
@ -89,20 +89,28 @@ type Framer interface {
|
|||||||
|
|
||||||
// SerializerInfo contains information about a specific serialization format
|
// SerializerInfo contains information about a specific serialization format
|
||||||
type SerializerInfo struct {
|
type SerializerInfo struct {
|
||||||
Serializer
|
|
||||||
// EncodesAsText indicates this serializer can be encoded to UTF-8 safely.
|
|
||||||
EncodesAsText bool
|
|
||||||
// MediaType is the value that represents this serializer over the wire.
|
// MediaType is the value that represents this serializer over the wire.
|
||||||
MediaType string
|
MediaType string
|
||||||
|
// EncodesAsText indicates this serializer can be encoded to UTF-8 safely.
|
||||||
|
EncodesAsText bool
|
||||||
|
// Serializer is the individual object serializer for this media type.
|
||||||
|
Serializer Serializer
|
||||||
|
// PrettySerializer, if set, can serialize this object in a form biased towards
|
||||||
|
// readability.
|
||||||
|
PrettySerializer Serializer
|
||||||
|
// StreamSerializer, if set, describes the streaming serialization format
|
||||||
|
// for this media type.
|
||||||
|
StreamSerializer *StreamSerializerInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// StreamSerializerInfo contains information about a specific stream serialization format
|
// StreamSerializerInfo contains information about a specific stream serialization format
|
||||||
type StreamSerializerInfo struct {
|
type StreamSerializerInfo struct {
|
||||||
SerializerInfo
|
// EncodesAsText indicates this serializer can be encoded to UTF-8 safely.
|
||||||
|
EncodesAsText bool
|
||||||
|
// Serializer is the top level object serializer for this type when streaming
|
||||||
|
Serializer
|
||||||
// Framer is the factory for retrieving streams that separate objects on the wire
|
// Framer is the factory for retrieving streams that separate objects on the wire
|
||||||
Framer
|
Framer
|
||||||
// Embedded is the type of the nested serialization that should be used.
|
|
||||||
Embedded SerializerInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NegotiatedSerializer is an interface used for obtaining encoders, decoders, and serializers
|
// NegotiatedSerializer is an interface used for obtaining encoders, decoders, and serializers
|
||||||
@ -110,21 +118,7 @@ type StreamSerializerInfo struct {
|
|||||||
// that performs HTTP content negotiation to accept multiple formats.
|
// that performs HTTP content negotiation to accept multiple formats.
|
||||||
type NegotiatedSerializer interface {
|
type NegotiatedSerializer interface {
|
||||||
// SupportedMediaTypes is the media types supported for reading and writing single objects.
|
// SupportedMediaTypes is the media types supported for reading and writing single objects.
|
||||||
SupportedMediaTypes() []string
|
SupportedMediaTypes() []SerializerInfo
|
||||||
// SerializerForMediaType returns a serializer for the provided media type. params is the set of
|
|
||||||
// parameters applied to the media type that may modify the resulting output. ok will be false
|
|
||||||
// if no serializer matched the media type.
|
|
||||||
SerializerForMediaType(mediaType string, params map[string]string) (s SerializerInfo, ok bool)
|
|
||||||
|
|
||||||
// SupportedStreamingMediaTypes returns the media types of the supported streaming serializers.
|
|
||||||
// Streaming serializers control how multiple objects are written to a stream output.
|
|
||||||
SupportedStreamingMediaTypes() []string
|
|
||||||
// StreamingSerializerForMediaType returns a serializer for the provided media type that supports
|
|
||||||
// reading and writing multiple objects to a stream. It returns a framer and serializer, or an
|
|
||||||
// error if no such serializer can be created. Params is the set of parameters applied to the
|
|
||||||
// media type that may modify the resulting output. ok will be false if no serializer matched
|
|
||||||
// the media type.
|
|
||||||
StreamingSerializerForMediaType(mediaType string, params map[string]string) (s StreamSerializerInfo, ok bool)
|
|
||||||
|
|
||||||
// EncoderForVersion returns an encoder that ensures objects being written to the provided
|
// EncoderForVersion returns an encoder that ensures objects being written to the provided
|
||||||
// serializer are in the provided group version.
|
// serializer are in the provided group version.
|
||||||
@ -138,9 +132,8 @@ type NegotiatedSerializer interface {
|
|||||||
// that can read and write data at rest. This would commonly be used by client tools that must
|
// that can read and write data at rest. This would commonly be used by client tools that must
|
||||||
// read files, or server side storage interfaces that persist restful objects.
|
// read files, or server side storage interfaces that persist restful objects.
|
||||||
type StorageSerializer interface {
|
type StorageSerializer interface {
|
||||||
// SerializerForMediaType returns a serializer for the provided media type. Options is a set of
|
// SupportedMediaTypes are the media types supported for reading and writing objects.
|
||||||
// parameters applied to the media type that may modify the resulting output.
|
SupportedMediaTypes() []SerializerInfo
|
||||||
SerializerForMediaType(mediaType string, options map[string]string) (SerializerInfo, bool)
|
|
||||||
|
|
||||||
// UniversalDeserializer returns a Serializer that can read objects in multiple supported formats
|
// UniversalDeserializer returns a Serializer that can read objects in multiple supported formats
|
||||||
// by introspecting the data at rest.
|
// by introspecting the data at rest.
|
||||||
|
@ -84,7 +84,8 @@ func TestScheme(t *testing.T) {
|
|||||||
|
|
||||||
codecs := serializer.NewCodecFactory(scheme)
|
codecs := serializer.NewCodecFactory(scheme)
|
||||||
codec := codecs.LegacyCodec(externalGV)
|
codec := codecs.LegacyCodec(externalGV)
|
||||||
jsonserializer, _ := codecs.SerializerForFileExtension("json")
|
info, _ := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
|
jsonserializer := info.Serializer
|
||||||
|
|
||||||
simple := &InternalSimple{
|
simple := &InternalSimple{
|
||||||
TestString: "foo",
|
TestString: "foo",
|
||||||
@ -150,7 +151,8 @@ func TestScheme(t *testing.T) {
|
|||||||
func TestBadJSONRejection(t *testing.T) {
|
func TestBadJSONRejection(t *testing.T) {
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
codecs := serializer.NewCodecFactory(scheme)
|
codecs := serializer.NewCodecFactory(scheme)
|
||||||
jsonserializer, _ := codecs.SerializerForFileExtension("json")
|
info, _ := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
|
jsonserializer := info.Serializer
|
||||||
|
|
||||||
badJSONMissingKind := []byte(`{ }`)
|
badJSONMissingKind := []byte(`{ }`)
|
||||||
if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil {
|
if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil {
|
||||||
|
@ -36,13 +36,6 @@ type serializerType struct {
|
|||||||
|
|
||||||
Serializer runtime.Serializer
|
Serializer runtime.Serializer
|
||||||
PrettySerializer runtime.Serializer
|
PrettySerializer runtime.Serializer
|
||||||
// RawSerializer serializes an object without adding a type wrapper. Some serializers, like JSON
|
|
||||||
// automatically include identifying type information with the JSON. Others, like Protobuf, need
|
|
||||||
// a wrapper object that includes type information. This serializer should be set if the serializer
|
|
||||||
// can serialize / deserialize objects without type info. Note that this serializer will always
|
|
||||||
// be expected to pass into or a gvk to Decode, since no type information will be available on
|
|
||||||
// the object itself.
|
|
||||||
RawSerializer runtime.Serializer
|
|
||||||
|
|
||||||
AcceptStreamContentTypes []string
|
AcceptStreamContentTypes []string
|
||||||
StreamContentType string
|
StreamContentType string
|
||||||
@ -65,8 +58,6 @@ func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory) []seri
|
|||||||
Serializer: jsonSerializer,
|
Serializer: jsonSerializer,
|
||||||
PrettySerializer: jsonPrettySerializer,
|
PrettySerializer: jsonPrettySerializer,
|
||||||
|
|
||||||
AcceptStreamContentTypes: []string{"application/json", "application/json;stream=watch"},
|
|
||||||
StreamContentType: "application/json",
|
|
||||||
Framer: json.Framer,
|
Framer: json.Framer,
|
||||||
StreamSerializer: jsonSerializer,
|
StreamSerializer: jsonSerializer,
|
||||||
},
|
},
|
||||||
@ -76,13 +67,6 @@ func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory) []seri
|
|||||||
FileExtensions: []string{"yaml"},
|
FileExtensions: []string{"yaml"},
|
||||||
EncodesAsText: true,
|
EncodesAsText: true,
|
||||||
Serializer: yamlSerializer,
|
Serializer: yamlSerializer,
|
||||||
|
|
||||||
// TODO: requires runtime.RawExtension to properly distinguish when the nested content is
|
|
||||||
// yaml, because the yaml encoder invokes MarshalJSON first
|
|
||||||
//AcceptStreamContentTypes: []string{"application/yaml", "application/yaml;stream=watch"},
|
|
||||||
//StreamContentType: "application/yaml;stream=watch",
|
|
||||||
//Framer: json.YAMLFramer,
|
|
||||||
//StreamSerializer: yamlSerializer,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +84,7 @@ type CodecFactory struct {
|
|||||||
scheme *runtime.Scheme
|
scheme *runtime.Scheme
|
||||||
serializers []serializerType
|
serializers []serializerType
|
||||||
universal runtime.Decoder
|
universal runtime.Decoder
|
||||||
accepts []string
|
accepts []runtime.SerializerInfo
|
||||||
streamingAccepts []string
|
|
||||||
|
|
||||||
legacySerializer runtime.Serializer
|
legacySerializer runtime.Serializer
|
||||||
}
|
}
|
||||||
@ -120,7 +103,7 @@ func NewCodecFactory(scheme *runtime.Scheme) CodecFactory {
|
|||||||
// newCodecFactory is a helper for testing that allows a different metafactory to be specified.
|
// newCodecFactory is a helper for testing that allows a different metafactory to be specified.
|
||||||
func newCodecFactory(scheme *runtime.Scheme, serializers []serializerType) CodecFactory {
|
func newCodecFactory(scheme *runtime.Scheme, serializers []serializerType) CodecFactory {
|
||||||
decoders := make([]runtime.Decoder, 0, len(serializers))
|
decoders := make([]runtime.Decoder, 0, len(serializers))
|
||||||
accepts := []string{}
|
var accepts []runtime.SerializerInfo
|
||||||
alreadyAccepted := make(map[string]struct{})
|
alreadyAccepted := make(map[string]struct{})
|
||||||
|
|
||||||
var legacySerializer runtime.Serializer
|
var legacySerializer runtime.Serializer
|
||||||
@ -131,8 +114,21 @@ func newCodecFactory(scheme *runtime.Scheme, serializers []serializerType) Codec
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
alreadyAccepted[mediaType] = struct{}{}
|
alreadyAccepted[mediaType] = struct{}{}
|
||||||
accepts = append(accepts, mediaType)
|
info := runtime.SerializerInfo{
|
||||||
if mediaType == "application/json" {
|
MediaType: d.ContentType,
|
||||||
|
EncodesAsText: d.EncodesAsText,
|
||||||
|
Serializer: d.Serializer,
|
||||||
|
PrettySerializer: d.PrettySerializer,
|
||||||
|
}
|
||||||
|
if d.StreamSerializer != nil {
|
||||||
|
info.StreamSerializer = &runtime.StreamSerializerInfo{
|
||||||
|
Serializer: d.StreamSerializer,
|
||||||
|
EncodesAsText: d.EncodesAsText,
|
||||||
|
Framer: d.Framer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accepts = append(accepts, info)
|
||||||
|
if mediaType == runtime.ContentTypeJSON {
|
||||||
legacySerializer = d.Serializer
|
legacySerializer = d.Serializer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,45 +137,22 @@ func newCodecFactory(scheme *runtime.Scheme, serializers []serializerType) Codec
|
|||||||
legacySerializer = serializers[0].Serializer
|
legacySerializer = serializers[0].Serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
streamAccepts := []string{}
|
|
||||||
alreadyAccepted = make(map[string]struct{})
|
|
||||||
for _, d := range serializers {
|
|
||||||
if len(d.StreamContentType) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, mediaType := range d.AcceptStreamContentTypes {
|
|
||||||
if _, ok := alreadyAccepted[mediaType]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
alreadyAccepted[mediaType] = struct{}{}
|
|
||||||
streamAccepts = append(streamAccepts, mediaType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CodecFactory{
|
return CodecFactory{
|
||||||
scheme: scheme,
|
scheme: scheme,
|
||||||
serializers: serializers,
|
serializers: serializers,
|
||||||
universal: recognizer.NewDecoder(decoders...),
|
universal: recognizer.NewDecoder(decoders...),
|
||||||
|
|
||||||
accepts: accepts,
|
accepts: accepts,
|
||||||
streamingAccepts: streamAccepts,
|
|
||||||
|
|
||||||
legacySerializer: legacySerializer,
|
legacySerializer: legacySerializer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ runtime.NegotiatedSerializer = &CodecFactory{}
|
|
||||||
|
|
||||||
// SupportedMediaTypes returns the RFC2046 media types that this factory has serializers for.
|
// SupportedMediaTypes returns the RFC2046 media types that this factory has serializers for.
|
||||||
func (f CodecFactory) SupportedMediaTypes() []string {
|
func (f CodecFactory) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||||
return f.accepts
|
return f.accepts
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportedStreamingMediaTypes returns the RFC2046 media types that this factory has stream serializers for.
|
|
||||||
func (f CodecFactory) SupportedStreamingMediaTypes() []string {
|
|
||||||
return f.streamingAccepts
|
|
||||||
}
|
|
||||||
|
|
||||||
// LegacyCodec encodes output to a given API versions, and decodes output into the internal form from
|
// LegacyCodec encodes output to a given API versions, and decodes output into the internal form from
|
||||||
// any recognized source. The returned codec will always encode output to JSON. If a type is not
|
// any recognized source. The returned codec will always encode output to JSON. If a type is not
|
||||||
// found in the list of versions an error will be returned.
|
// found in the list of versions an error will be returned.
|
||||||
@ -242,64 +215,6 @@ func (f CodecFactory) EncoderForVersion(encoder runtime.Encoder, gv runtime.Grou
|
|||||||
return f.CodecForVersions(encoder, nil, gv, nil)
|
return f.CodecForVersions(encoder, nil, gv, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such
|
|
||||||
// serializer exists
|
|
||||||
func (f CodecFactory) SerializerForMediaType(mediaType string, params map[string]string) (runtime.SerializerInfo, bool) {
|
|
||||||
for _, s := range f.serializers {
|
|
||||||
for _, accepted := range s.AcceptContentTypes {
|
|
||||||
if accepted == mediaType {
|
|
||||||
// legacy support for ?pretty=1 continues, but this is more formally defined
|
|
||||||
if v, ok := params["pretty"]; ok && v == "1" && s.PrettySerializer != nil {
|
|
||||||
return runtime.SerializerInfo{Serializer: s.PrettySerializer, MediaType: s.ContentType, EncodesAsText: s.EncodesAsText}, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the base variant
|
|
||||||
return runtime.SerializerInfo{Serializer: s.Serializer, MediaType: s.ContentType, EncodesAsText: s.EncodesAsText}, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return runtime.SerializerInfo{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// StreamingSerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such
|
|
||||||
// serializer exists
|
|
||||||
func (f CodecFactory) StreamingSerializerForMediaType(mediaType string, params map[string]string) (runtime.StreamSerializerInfo, bool) {
|
|
||||||
for _, s := range f.serializers {
|
|
||||||
for _, accepted := range s.AcceptStreamContentTypes {
|
|
||||||
if accepted == mediaType {
|
|
||||||
// TODO: accept params
|
|
||||||
nested, ok := f.SerializerForMediaType(s.ContentType, nil)
|
|
||||||
if !ok {
|
|
||||||
panic("no serializer defined for internal content type")
|
|
||||||
}
|
|
||||||
|
|
||||||
return runtime.StreamSerializerInfo{
|
|
||||||
SerializerInfo: runtime.SerializerInfo{
|
|
||||||
Serializer: s.StreamSerializer,
|
|
||||||
MediaType: s.StreamContentType,
|
|
||||||
EncodesAsText: s.EncodesAsText,
|
|
||||||
},
|
|
||||||
Framer: s.Framer,
|
|
||||||
Embedded: nested,
|
|
||||||
}, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return runtime.StreamSerializerInfo{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// SerializerForFileExtension returns a serializer for the provided extension, or false if no serializer matches.
|
|
||||||
func (f CodecFactory) SerializerForFileExtension(extension string) (runtime.Serializer, bool) {
|
|
||||||
for _, s := range f.serializers {
|
|
||||||
for _, ext := range s.FileExtensions {
|
|
||||||
if extension == ext {
|
|
||||||
return s.Serializer, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectCodecFactory provides methods for retrieving "DirectCodec"s, which do not do conversion.
|
// DirectCodecFactory provides methods for retrieving "DirectCodec"s, which do not do conversion.
|
||||||
type DirectCodecFactory struct {
|
type DirectCodecFactory struct {
|
||||||
CodecFactory
|
CodecFactory
|
||||||
|
@ -252,7 +252,8 @@ func TestTypes(t *testing.T) {
|
|||||||
func TestVersionedEncoding(t *testing.T) {
|
func TestVersionedEncoding(t *testing.T) {
|
||||||
s, _ := GetTestScheme()
|
s, _ := GetTestScheme()
|
||||||
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
|
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
|
||||||
encoder, _ := cf.SerializerForFileExtension("json")
|
info, _ := runtime.SerializerInfoForMediaType(cf.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
|
encoder := info.Serializer
|
||||||
|
|
||||||
codec := cf.CodecForVersions(encoder, nil, unversioned.GroupVersion{Version: "v2"}, nil)
|
codec := cf.CodecForVersions(encoder, nil, unversioned.GroupVersion{Version: "v2"}, nil)
|
||||||
out, err := runtime.Encode(codec, &TestType1{})
|
out, err := runtime.Encode(codec, &TestType1{})
|
||||||
@ -415,7 +416,8 @@ func GetDirectCodecTestScheme() *runtime.Scheme {
|
|||||||
func TestDirectCodec(t *testing.T) {
|
func TestDirectCodec(t *testing.T) {
|
||||||
s := GetDirectCodecTestScheme()
|
s := GetDirectCodecTestScheme()
|
||||||
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
|
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
|
||||||
serializer, _ := cf.SerializerForFileExtension("json")
|
info, _ := runtime.SerializerInfoForMediaType(cf.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
|
serializer := info.Serializer
|
||||||
df := DirectCodecFactory{cf}
|
df := DirectCodecFactory{cf}
|
||||||
ignoredGV, err := unversioned.ParseGroupVersion("ignored group/ignored version")
|
ignoredGV, err := unversioned.ParseGroupVersion("ignored group/ignored version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -20,31 +20,18 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: We should figure out what happens when someone asks
|
// TODO: We should split negotiated serializers that we can change versions on from those we can change
|
||||||
// encoder for version and it conflicts with the raw serializer.
|
// serialization formats on
|
||||||
type negotiatedSerializerWrapper struct {
|
type negotiatedSerializerWrapper struct {
|
||||||
info runtime.SerializerInfo
|
info runtime.SerializerInfo
|
||||||
streamInfo runtime.StreamSerializerInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NegotiatedSerializerWrapper(info runtime.SerializerInfo, streamInfo runtime.StreamSerializerInfo) runtime.NegotiatedSerializer {
|
func NegotiatedSerializerWrapper(info runtime.SerializerInfo) runtime.NegotiatedSerializer {
|
||||||
return &negotiatedSerializerWrapper{info, streamInfo}
|
return &negotiatedSerializerWrapper{info}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) SupportedMediaTypes() []string {
|
func (n *negotiatedSerializerWrapper) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||||
return []string{}
|
return []runtime.SerializerInfo{n.info}
|
||||||
}
|
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) SerializerForMediaType(mediaType string, options map[string]string) (runtime.SerializerInfo, bool) {
|
|
||||||
return n.info, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) SupportedStreamingMediaTypes() []string {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) StreamingSerializerForMediaType(mediaType string, options map[string]string) (runtime.StreamSerializerInfo, bool) {
|
|
||||||
return n.streamInfo, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) EncoderForVersion(e runtime.Encoder, _ runtime.GroupVersioner) runtime.Encoder {
|
func (n *negotiatedSerializerWrapper) EncoderForVersion(e runtime.Encoder, _ runtime.GroupVersioner) runtime.Encoder {
|
||||||
|
@ -27,7 +27,6 @@ const (
|
|||||||
// TODO: potentially move to pkg/api (since it's part of the Kube public API) and pass it in to the
|
// TODO: potentially move to pkg/api (since it's part of the Kube public API) and pass it in to the
|
||||||
// CodecFactory on initialization.
|
// CodecFactory on initialization.
|
||||||
contentTypeProtobuf = "application/vnd.kubernetes.protobuf"
|
contentTypeProtobuf = "application/vnd.kubernetes.protobuf"
|
||||||
contentTypeProtobufWatch = contentTypeProtobuf + ";stream=watch"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func protobufSerializer(scheme *runtime.Scheme) (serializerType, bool) {
|
func protobufSerializer(scheme *runtime.Scheme) (serializerType, bool) {
|
||||||
@ -38,10 +37,7 @@ func protobufSerializer(scheme *runtime.Scheme) (serializerType, bool) {
|
|||||||
ContentType: contentTypeProtobuf,
|
ContentType: contentTypeProtobuf,
|
||||||
FileExtensions: []string{"pb"},
|
FileExtensions: []string{"pb"},
|
||||||
Serializer: serializer,
|
Serializer: serializer,
|
||||||
RawSerializer: raw,
|
|
||||||
|
|
||||||
AcceptStreamContentTypes: []string{contentTypeProtobuf, contentTypeProtobufWatch},
|
|
||||||
StreamContentType: contentTypeProtobufWatch,
|
|
||||||
Framer: protobuf.LengthDelimitedFramer,
|
Framer: protobuf.LengthDelimitedFramer,
|
||||||
StreamSerializer: raw,
|
StreamSerializer: raw,
|
||||||
}, true
|
}, true
|
||||||
|
@ -56,10 +56,7 @@ func NewGenericWebhook(kubeConfigFile string, groupVersions []unversioned.GroupV
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
codec := api.Codecs.LegacyCodec(groupVersions...)
|
codec := api.Codecs.LegacyCodec(groupVersions...)
|
||||||
clientConfig.ContentConfig.NegotiatedSerializer = runtimeserializer.NegotiatedSerializerWrapper(
|
clientConfig.ContentConfig.NegotiatedSerializer = runtimeserializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
|
||||||
runtime.SerializerInfo{Serializer: codec},
|
|
||||||
runtime.StreamSerializerInfo{},
|
|
||||||
)
|
|
||||||
|
|
||||||
restClient, err := restclient.UnversionedRESTClientFor(clientConfig)
|
restClient, err := restclient.UnversionedRESTClientFor(clientConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -303,45 +303,46 @@ func NewMasterConfig() *master.Config {
|
|||||||
Prefix: uuid.New(),
|
Prefix: uuid.New(),
|
||||||
}
|
}
|
||||||
|
|
||||||
negotiatedSerializer := NewSingleContentTypeSerializer(api.Scheme, testapi.Default.Codec(), runtime.ContentTypeJSON)
|
info, _ := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
|
ns := NewSingleContentTypeSerializer(api.Scheme, info)
|
||||||
|
|
||||||
storageFactory := genericapiserver.NewDefaultStorageFactory(config, runtime.ContentTypeJSON, negotiatedSerializer, genericapiserver.NewDefaultResourceEncodingConfig(), master.DefaultAPIResourceConfigSource())
|
storageFactory := genericapiserver.NewDefaultStorageFactory(config, runtime.ContentTypeJSON, ns, genericapiserver.NewDefaultResourceEncodingConfig(), master.DefaultAPIResourceConfigSource())
|
||||||
storageFactory.SetSerializer(
|
storageFactory.SetSerializer(
|
||||||
unversioned.GroupResource{Group: api.GroupName, Resource: genericapiserver.AllResources},
|
unversioned.GroupResource{Group: api.GroupName, Resource: genericapiserver.AllResources},
|
||||||
"",
|
"",
|
||||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Default.Codec(), runtime.ContentTypeJSON))
|
ns)
|
||||||
storageFactory.SetSerializer(
|
storageFactory.SetSerializer(
|
||||||
unversioned.GroupResource{Group: autoscaling.GroupName, Resource: genericapiserver.AllResources},
|
unversioned.GroupResource{Group: autoscaling.GroupName, Resource: genericapiserver.AllResources},
|
||||||
"",
|
"",
|
||||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Autoscaling.Codec(), runtime.ContentTypeJSON))
|
ns)
|
||||||
storageFactory.SetSerializer(
|
storageFactory.SetSerializer(
|
||||||
unversioned.GroupResource{Group: batch.GroupName, Resource: genericapiserver.AllResources},
|
unversioned.GroupResource{Group: batch.GroupName, Resource: genericapiserver.AllResources},
|
||||||
"",
|
"",
|
||||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Batch.Codec(), runtime.ContentTypeJSON))
|
ns)
|
||||||
storageFactory.SetSerializer(
|
storageFactory.SetSerializer(
|
||||||
unversioned.GroupResource{Group: apps.GroupName, Resource: genericapiserver.AllResources},
|
unversioned.GroupResource{Group: apps.GroupName, Resource: genericapiserver.AllResources},
|
||||||
"",
|
"",
|
||||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Apps.Codec(), runtime.ContentTypeJSON))
|
ns)
|
||||||
storageFactory.SetSerializer(
|
storageFactory.SetSerializer(
|
||||||
unversioned.GroupResource{Group: extensions.GroupName, Resource: genericapiserver.AllResources},
|
unversioned.GroupResource{Group: extensions.GroupName, Resource: genericapiserver.AllResources},
|
||||||
"",
|
"",
|
||||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Extensions.Codec(), runtime.ContentTypeJSON))
|
ns)
|
||||||
storageFactory.SetSerializer(
|
storageFactory.SetSerializer(
|
||||||
unversioned.GroupResource{Group: policy.GroupName, Resource: genericapiserver.AllResources},
|
unversioned.GroupResource{Group: policy.GroupName, Resource: genericapiserver.AllResources},
|
||||||
"",
|
"",
|
||||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Policy.Codec(), runtime.ContentTypeJSON))
|
ns)
|
||||||
storageFactory.SetSerializer(
|
storageFactory.SetSerializer(
|
||||||
unversioned.GroupResource{Group: rbac.GroupName, Resource: genericapiserver.AllResources},
|
unversioned.GroupResource{Group: rbac.GroupName, Resource: genericapiserver.AllResources},
|
||||||
"",
|
"",
|
||||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Rbac.Codec(), runtime.ContentTypeJSON))
|
ns)
|
||||||
storageFactory.SetSerializer(
|
storageFactory.SetSerializer(
|
||||||
unversioned.GroupResource{Group: certificates.GroupName, Resource: genericapiserver.AllResources},
|
unversioned.GroupResource{Group: certificates.GroupName, Resource: genericapiserver.AllResources},
|
||||||
"",
|
"",
|
||||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Certificates.Codec(), runtime.ContentTypeJSON))
|
ns)
|
||||||
storageFactory.SetSerializer(
|
storageFactory.SetSerializer(
|
||||||
unversioned.GroupResource{Group: storage.GroupName, Resource: genericapiserver.AllResources},
|
unversioned.GroupResource{Group: storage.GroupName, Resource: genericapiserver.AllResources},
|
||||||
"",
|
"",
|
||||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Storage.Codec(), runtime.ContentTypeJSON))
|
ns)
|
||||||
|
|
||||||
genericConfig := genericapiserver.NewConfig()
|
genericConfig := genericapiserver.NewConfig()
|
||||||
kubeVersion := version.Get()
|
kubeVersion := version.Get()
|
||||||
|
@ -22,39 +22,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NewSingleContentTypeSerializer wraps a serializer in a NegotiatedSerializer that handles one content type
|
// NewSingleContentTypeSerializer wraps a serializer in a NegotiatedSerializer that handles one content type
|
||||||
func NewSingleContentTypeSerializer(scheme *runtime.Scheme, serializer runtime.Serializer, contentType string) runtime.StorageSerializer {
|
func NewSingleContentTypeSerializer(scheme *runtime.Scheme, info runtime.SerializerInfo) runtime.StorageSerializer {
|
||||||
return &wrappedSerializer{
|
return &wrappedSerializer{
|
||||||
scheme: scheme,
|
scheme: scheme,
|
||||||
serializer: serializer,
|
info: info,
|
||||||
contentType: contentType,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type wrappedSerializer struct {
|
type wrappedSerializer struct {
|
||||||
scheme *runtime.Scheme
|
scheme *runtime.Scheme
|
||||||
serializer runtime.Serializer
|
info runtime.SerializerInfo
|
||||||
contentType string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ runtime.StorageSerializer = &wrappedSerializer{}
|
var _ runtime.StorageSerializer = &wrappedSerializer{}
|
||||||
|
|
||||||
func (s *wrappedSerializer) SupportedMediaTypes() []string {
|
func (s *wrappedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||||
return []string{s.contentType}
|
return []runtime.SerializerInfo{s.info}
|
||||||
}
|
|
||||||
func (s *wrappedSerializer) SerializerForMediaType(mediaType string, options map[string]string) (runtime.SerializerInfo, bool) {
|
|
||||||
if mediaType != s.contentType {
|
|
||||||
return runtime.SerializerInfo{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return runtime.SerializerInfo{
|
|
||||||
Serializer: s.serializer,
|
|
||||||
MediaType: mediaType,
|
|
||||||
EncodesAsText: true, // TODO: this should be parameterized
|
|
||||||
}, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wrappedSerializer) UniversalDeserializer() runtime.Decoder {
|
func (s *wrappedSerializer) UniversalDeserializer() runtime.Decoder {
|
||||||
return s.serializer
|
return s.info.Serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wrappedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
func (s *wrappedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||||
|
Loading…
Reference in New Issue
Block a user