Split the storage and negotiation parts of Codecs
The codec factory should support two distinct interfaces - negotiating for a serializer with a client, vs reading or writing data to a storage form (etcd, disk, etc). Make the EncodeForVersion and DecodeToVersion methods only take Encoder and Decoder, and slight refactoring elsewhere. In the storage factory, use a content type to control what serializer to pick, and use the universal deserializer. This ensures that storage can read JSON (which might be from older objects) while only writing protobuf. Add exceptions for those resources that may not be able to write to protobuf (specifically third party resources, but potentially others in the future).
This commit is contained in:
@@ -44,6 +44,7 @@ type APIServer struct {
|
|||||||
AuthorizationMode string
|
AuthorizationMode string
|
||||||
AuthorizationConfig apiserver.AuthorizationConfig
|
AuthorizationConfig apiserver.AuthorizationConfig
|
||||||
BasicAuthFile string
|
BasicAuthFile string
|
||||||
|
DefaultStorageMediaType string
|
||||||
DeleteCollectionWorkers int
|
DeleteCollectionWorkers int
|
||||||
DeprecatedStorageVersion string
|
DeprecatedStorageVersion string
|
||||||
EtcdServersOverrides []string
|
EtcdServersOverrides []string
|
||||||
@@ -76,6 +77,7 @@ func NewAPIServer() *APIServer {
|
|||||||
ServerRunOptions: genericapiserver.NewServerRunOptions(),
|
ServerRunOptions: genericapiserver.NewServerRunOptions(),
|
||||||
AdmissionControl: "AlwaysAdmit",
|
AdmissionControl: "AlwaysAdmit",
|
||||||
AuthorizationMode: "AlwaysAllow",
|
AuthorizationMode: "AlwaysAllow",
|
||||||
|
DefaultStorageMediaType: "application/json",
|
||||||
DeleteCollectionWorkers: 1,
|
DeleteCollectionWorkers: 1,
|
||||||
EventTTL: 1 * time.Hour,
|
EventTTL: 1 * time.Hour,
|
||||||
MasterServiceNamespace: api.NamespaceDefault,
|
MasterServiceNamespace: api.NamespaceDefault,
|
||||||
@@ -153,6 +155,7 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
|
|||||||
"In the case where objects are moved from one group to the other, you may specify the format \"group1=group2/v1beta1,group3/v1beta1,...\". "+
|
"In the case where objects are moved from one group to the other, you may specify the format \"group1=group2/v1beta1,group3/v1beta1,...\". "+
|
||||||
"You only need to pass the groups you wish to change from the defaults. "+
|
"You only need to pass the groups you wish to change from the defaults. "+
|
||||||
"It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.")
|
"It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.")
|
||||||
|
fs.StringVar(&s.DefaultStorageMediaType, "storage-media-type", s.DefaultStorageMediaType, "The media type to use to store objects in storage. Defaults to application/json. Some resources may only support a specific media type and will ignore this setting.")
|
||||||
fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL, "Amount of time to retain events. Default 1 hour.")
|
fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL, "Amount of time to retain events. Default 1 hour.")
|
||||||
fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.")
|
fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.")
|
||||||
fs.StringVar(&s.TokenAuthFile, "token-auth-file", s.TokenAuthFile, "If set, the file that will be used to secure the secure port of the API server via token authentication.")
|
fs.StringVar(&s.TokenAuthFile, "token-auth-file", s.TokenAuthFile, "If set, the file that will be used to secure the secure port of the API server via token authentication.")
|
||||||
|
@@ -167,7 +167,9 @@ func Run(s *options.APIServer) error {
|
|||||||
resourceEncoding.SetVersionEncoding(group, storageEncodingVersion, unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
|
resourceEncoding.SetVersionEncoding(group, storageEncodingVersion, unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
|
||||||
}
|
}
|
||||||
|
|
||||||
storageFactory := genericapiserver.NewDefaultStorageFactory(s.StorageConfig, api.Codecs, resourceEncoding, apiResourceConfigSource)
|
storageFactory := genericapiserver.NewDefaultStorageFactory(s.StorageConfig, s.DefaultStorageMediaType, api.Codecs, resourceEncoding, apiResourceConfigSource)
|
||||||
|
// third party resources are always serialized to storage using JSON
|
||||||
|
storageFactory.SetSerializer(extensions.Resource("thirdpartyresources"), "application/json", nil)
|
||||||
storageFactory.AddCohabitatingResources(batch.Resource("jobs"), extensions.Resource("jobs"))
|
storageFactory.AddCohabitatingResources(batch.Resource("jobs"), extensions.Resource("jobs"))
|
||||||
storageFactory.AddCohabitatingResources(autoscaling.Resource("horizontalpodautoscalers"), extensions.Resource("horizontalpodautoscalers"))
|
storageFactory.AddCohabitatingResources(autoscaling.Resource("horizontalpodautoscalers"), extensions.Resource("horizontalpodautoscalers"))
|
||||||
for _, override := range s.EtcdServersOverrides {
|
for _, override := range s.EtcdServersOverrides {
|
||||||
|
@@ -110,6 +110,7 @@ kube-apiserver
|
|||||||
--ssh-keyfile="": If non-empty, use secure SSH proxy to the nodes, using this user keyfile
|
--ssh-keyfile="": If non-empty, use secure SSH proxy to the nodes, using this user keyfile
|
||||||
--ssh-user="": If non-empty, use secure SSH proxy to the nodes, using this user name
|
--ssh-user="": If non-empty, use secure SSH proxy to the nodes, using this user name
|
||||||
--storage-backend="": The storage backend for persistence. Options: 'etcd2' (default), 'etcd3'.
|
--storage-backend="": The storage backend for persistence. Options: 'etcd2' (default), 'etcd3'.
|
||||||
|
--storage-media-type="application/json": The media type to use to store objects in storage. Defaults to application/json. Some resources may only support a specific media type and will ignore this setting.
|
||||||
--storage-versions="apps/v1alpha1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,v1": The per-group version to store resources in. Specified in the format "group1/version1,group2/version2,...". In the case where objects are moved from one group to the other, you may specify the format "group1=group2/v1beta1,group3/v1beta1,...". You only need to pass the groups you wish to change from the defaults. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.
|
--storage-versions="apps/v1alpha1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,v1": The per-group version to store resources in. Specified in the format "group1/version1,group2/version2,...". In the case where objects are moved from one group to the other, you may specify the format "group1=group2/v1beta1,group3/v1beta1,...". You only need to pass the groups you wish to change from the defaults. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.
|
||||||
--tls-cert-file="": File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes.
|
--tls-cert-file="": File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes.
|
||||||
--tls-private-key-file="": File containing x509 private key matching --tls-cert-file.
|
--tls-private-key-file="": File containing x509 private key matching --tls-cert-file.
|
||||||
|
@@ -45,7 +45,7 @@ func newStorageFactory() genericapiserver.StorageFactory {
|
|||||||
Prefix: genericapiserver.DefaultEtcdPathPrefix,
|
Prefix: genericapiserver.DefaultEtcdPathPrefix,
|
||||||
ServerList: []string{"http://127.0.0.1:4001"},
|
ServerList: []string{"http://127.0.0.1:4001"},
|
||||||
}
|
}
|
||||||
storageFactory := genericapiserver.NewDefaultStorageFactory(config, api.Codecs, genericapiserver.NewDefaultResourceEncodingConfig(), genericapiserver.NewResourceConfig())
|
storageFactory := genericapiserver.NewDefaultStorageFactory(config, "application/json", api.Codecs, genericapiserver.NewDefaultResourceEncodingConfig(), genericapiserver.NewResourceConfig())
|
||||||
|
|
||||||
return storageFactory
|
return storageFactory
|
||||||
}
|
}
|
||||||
|
@@ -44,6 +44,7 @@ type APIServer struct {
|
|||||||
AuthorizationMode string
|
AuthorizationMode string
|
||||||
AuthorizationConfig apiserver.AuthorizationConfig
|
AuthorizationConfig apiserver.AuthorizationConfig
|
||||||
BasicAuthFile string
|
BasicAuthFile string
|
||||||
|
DefaultStorageMediaType string
|
||||||
DeleteCollectionWorkers int
|
DeleteCollectionWorkers int
|
||||||
DeprecatedStorageVersion string
|
DeprecatedStorageVersion string
|
||||||
EtcdServersOverrides []string
|
EtcdServersOverrides []string
|
||||||
@@ -76,6 +77,7 @@ func NewAPIServer() *APIServer {
|
|||||||
ServerRunOptions: genericapiserver.NewServerRunOptions(),
|
ServerRunOptions: genericapiserver.NewServerRunOptions(),
|
||||||
AdmissionControl: "AlwaysAdmit",
|
AdmissionControl: "AlwaysAdmit",
|
||||||
AuthorizationMode: "AlwaysAllow",
|
AuthorizationMode: "AlwaysAllow",
|
||||||
|
DefaultStorageMediaType: "application/json",
|
||||||
DeleteCollectionWorkers: 1,
|
DeleteCollectionWorkers: 1,
|
||||||
EventTTL: 1 * time.Hour,
|
EventTTL: 1 * time.Hour,
|
||||||
MasterServiceNamespace: api.NamespaceDefault,
|
MasterServiceNamespace: api.NamespaceDefault,
|
||||||
@@ -153,6 +155,7 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
|
|||||||
"In the case where objects are moved from one group to the other, you may specify the format \"group1=group2/v1beta1,group3/v1beta1,...\". "+
|
"In the case where objects are moved from one group to the other, you may specify the format \"group1=group2/v1beta1,group3/v1beta1,...\". "+
|
||||||
"You only need to pass the groups you wish to change from the defaults. "+
|
"You only need to pass the groups you wish to change from the defaults. "+
|
||||||
"It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.")
|
"It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.")
|
||||||
|
fs.StringVar(&s.DefaultStorageMediaType, "storage-media-type", s.DefaultStorageMediaType, "The media type to use to store objects in storage. Defaults to application/json. Some resources may only support a specific media type and will ignore this setting.")
|
||||||
fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL, "Amount of time to retain events. Default 1 hour.")
|
fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL, "Amount of time to retain events. Default 1 hour.")
|
||||||
fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.")
|
fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.")
|
||||||
fs.StringVar(&s.TokenAuthFile, "token-auth-file", s.TokenAuthFile, "If set, the file that will be used to secure the secure port of the API server via token authentication.")
|
fs.StringVar(&s.TokenAuthFile, "token-auth-file", s.TokenAuthFile, "If set, the file that will be used to secure the secure port of the API server via token authentication.")
|
||||||
|
@@ -76,7 +76,7 @@ func Run(s *options.APIServer) error {
|
|||||||
resourceEncoding.SetVersionEncoding(group, storageEncodingVersion, unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
|
resourceEncoding.SetVersionEncoding(group, storageEncodingVersion, unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
|
||||||
}
|
}
|
||||||
|
|
||||||
storageFactory := genericapiserver.NewDefaultStorageFactory(s.StorageConfig, api.Codecs, resourceEncoding, apiResourceConfigSource)
|
storageFactory := genericapiserver.NewDefaultStorageFactory(s.StorageConfig, s.DefaultStorageMediaType, api.Codecs, resourceEncoding, apiResourceConfigSource)
|
||||||
for _, override := range s.EtcdServersOverrides {
|
for _, override := range s.EtcdServersOverrides {
|
||||||
tokens := strings.Split(override, "#")
|
tokens := strings.Split(override, "#")
|
||||||
if len(tokens) != 2 {
|
if len(tokens) != 2 {
|
||||||
|
@@ -192,6 +192,7 @@ ADMISSION_CONTROL="NamespaceLifecycle,LimitRanger,ResourceQuota"
|
|||||||
--public-address-override="127.0.0.1" \
|
--public-address-override="127.0.0.1" \
|
||||||
--kubelet-port=${KUBELET_PORT} \
|
--kubelet-port=${KUBELET_PORT} \
|
||||||
--runtime-config=api/v1 \
|
--runtime-config=api/v1 \
|
||||||
|
--storage-media-type="${KUBE_TEST_API_STORAGE_TYPE-}" \
|
||||||
--cert-dir="${TMPDIR:-/tmp/}" \
|
--cert-dir="${TMPDIR:-/tmp/}" \
|
||||||
--service-cluster-ip-range="10.0.0.0/24" 1>&2 &
|
--service-cluster-ip-range="10.0.0.0/24" 1>&2 &
|
||||||
APISERVER_PID=$!
|
APISERVER_PID=$!
|
||||||
@@ -202,6 +203,7 @@ kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver"
|
|||||||
kube::log::status "Starting controller-manager"
|
kube::log::status "Starting controller-manager"
|
||||||
"${KUBE_OUTPUT_HOSTBIN}/kube-controller-manager" \
|
"${KUBE_OUTPUT_HOSTBIN}/kube-controller-manager" \
|
||||||
--port="${CTLRMGR_PORT}" \
|
--port="${CTLRMGR_PORT}" \
|
||||||
|
--kube-api-content-type="${KUBE_TEST_API_TYPE-}" \
|
||||||
--master="127.0.0.1:${API_PORT}" 1>&2 &
|
--master="127.0.0.1:${API_PORT}" 1>&2 &
|
||||||
CTLRMGR_PID=$!
|
CTLRMGR_PID=$!
|
||||||
|
|
||||||
|
@@ -394,6 +394,7 @@ static-pods-config
|
|||||||
stats-port
|
stats-port
|
||||||
stop-services
|
stop-services
|
||||||
storage-backend
|
storage-backend
|
||||||
|
storage-media-type
|
||||||
storage-version
|
storage-version
|
||||||
storage-versions
|
storage-versions
|
||||||
streaming-connection-idle-timeout
|
streaming-connection-idle-timeout
|
||||||
|
@@ -38,7 +38,7 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
codecsToTest = append(codecsToTest, func(version unversioned.GroupVersion, item runtime.Object) (runtime.Codec, error) {
|
codecsToTest = append(codecsToTest, func(version unversioned.GroupVersion, item runtime.Object) (runtime.Codec, error) {
|
||||||
s := protobuf.NewSerializer(api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), "application/arbitrary.content.type")
|
s := protobuf.NewSerializer(api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), "application/arbitrary.content.type")
|
||||||
return api.Codecs.CodecForVersions(s, testapi.ExternalGroupVersions(), nil), nil
|
return api.Codecs.CodecForVersions(s, s, testapi.ExternalGroupVersions(), nil), nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -284,18 +284,14 @@ func TestObjectWatchFraming(t *testing.T) {
|
|||||||
converted, _ := api.Scheme.ConvertToVersion(secret, "v1")
|
converted, _ := api.Scheme.ConvertToVersion(secret, "v1")
|
||||||
v1secret := converted.(*v1.Secret)
|
v1secret := converted.(*v1.Secret)
|
||||||
for _, streamingMediaType := range api.Codecs.SupportedStreamingMediaTypes() {
|
for _, streamingMediaType := range api.Codecs.SupportedStreamingMediaTypes() {
|
||||||
s, framer, mediaType, _ := api.Codecs.StreamingSerializerForMediaType(streamingMediaType, nil)
|
s, _ := api.Codecs.StreamingSerializerForMediaType(streamingMediaType, nil)
|
||||||
// TODO: remove this when the runtime.SerializerInfo PR lands
|
framer := s.Framer
|
||||||
if mediaType == "application/vnd.kubernetes.protobuf;stream=watch" {
|
embedded := s.Embedded.Serializer
|
||||||
mediaType = "application/vnd.kubernetes.protobuf"
|
if embedded == nil {
|
||||||
}
|
t.Errorf("no embedded serializer for %s", streamingMediaType)
|
||||||
embedded, ok := api.Codecs.SerializerForMediaType(mediaType, nil)
|
continue
|
||||||
if !ok {
|
|
||||||
t.Logf("no embedded serializer for %s", mediaType)
|
|
||||||
embedded = s
|
|
||||||
}
|
}
|
||||||
innerDecode := api.Codecs.DecoderToVersion(embedded, api.SchemeGroupVersion)
|
innerDecode := api.Codecs.DecoderToVersion(embedded, api.SchemeGroupVersion)
|
||||||
//innerEncode := api.Codecs.EncoderForVersion(embedded, api.SchemeGroupVersion)
|
|
||||||
|
|
||||||
// write a single object through the framer and back out
|
// write a single object through the framer and back out
|
||||||
obj := &bytes.Buffer{}
|
obj := &bytes.Buffer{}
|
||||||
|
@@ -19,6 +19,7 @@ package testapi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"mime"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -33,6 +34,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime/serializer/recognizer"
|
||||||
|
|
||||||
_ "k8s.io/kubernetes/federation/apis/federation/install"
|
_ "k8s.io/kubernetes/federation/apis/federation/install"
|
||||||
_ "k8s.io/kubernetes/pkg/api/install"
|
_ "k8s.io/kubernetes/pkg/api/install"
|
||||||
@@ -52,7 +54,9 @@ var (
|
|||||||
Extensions TestGroup
|
Extensions TestGroup
|
||||||
Apps TestGroup
|
Apps TestGroup
|
||||||
Federation TestGroup
|
Federation TestGroup
|
||||||
NegotiatedSerializer = api.Codecs
|
|
||||||
|
serializer runtime.SerializerInfo
|
||||||
|
storageSerializer runtime.SerializerInfo
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestGroup struct {
|
type TestGroup struct {
|
||||||
@@ -62,6 +66,30 @@ type TestGroup struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
if apiMediaType := os.Getenv("KUBE_TEST_API_TYPE"); len(apiMediaType) > 0 {
|
||||||
|
var ok bool
|
||||||
|
mediaType, options, err := mime.ParseMediaType(apiMediaType)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
serializer, ok = api.Codecs.SerializerForMediaType(mediaType, options)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("no serializer for %s", apiMediaType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if storageMediaType := StorageMediaType(); len(storageMediaType) > 0 {
|
||||||
|
var ok bool
|
||||||
|
mediaType, options, err := mime.ParseMediaType(storageMediaType)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
storageSerializer, ok = api.Codecs.SerializerForMediaType(mediaType, options)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("no serializer for %s", storageMediaType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kubeTestAPI := os.Getenv("KUBE_TEST_API")
|
kubeTestAPI := os.Getenv("KUBE_TEST_API")
|
||||||
if len(kubeTestAPI) != 0 {
|
if len(kubeTestAPI) != 0 {
|
||||||
testGroupVersions := strings.Split(kubeTestAPI, ",")
|
testGroupVersions := strings.Split(kubeTestAPI, ",")
|
||||||
@@ -173,10 +201,41 @@ func (g TestGroup) InternalTypes() map[string]reflect.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Codec returns the codec for the API version to test against, as set by the
|
// Codec returns the codec for the API version to test against, as set by the
|
||||||
// KUBE_TEST_API env var.
|
// KUBE_TEST_API_TYPE env var.
|
||||||
func (g TestGroup) Codec() runtime.Codec {
|
func (g TestGroup) Codec() runtime.Codec {
|
||||||
|
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.GroupVersion{g.externalGroupVersion}, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NegotiatedSerializer returns the negotiated serializer for the server.
|
||||||
|
func (g TestGroup) NegotiatedSerializer() runtime.NegotiatedSerializer {
|
||||||
|
return api.Codecs
|
||||||
|
}
|
||||||
|
|
||||||
|
func StorageMediaType() string {
|
||||||
|
return os.Getenv("KUBE_TEST_API_STORAGE_TYPE")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageCodec returns the codec for the API version to store in etcd, as set by the
|
||||||
|
// KUBE_TEST_API_STORAGE_TYPE env var.
|
||||||
|
func (g TestGroup) StorageCodec() runtime.Codec {
|
||||||
|
s := storageSerializer.Serializer
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return api.Codecs.LegacyCodec(g.externalGroupVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// etcd2 only supports string data - we must wrap any result before returning
|
||||||
|
// TODO: remove for etcd3 / make parameterizable
|
||||||
|
if !storageSerializer.EncodesAsText {
|
||||||
|
s = runtime.NewBase64Serializer(s)
|
||||||
|
}
|
||||||
|
ds := recognizer.NewDecoder(s, api.Codecs.UniversalDeserializer())
|
||||||
|
|
||||||
|
return api.Codecs.CodecForVersions(s, ds, []unversioned.GroupVersion{g.externalGroupVersion}, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// Converter returns the api.Scheme for the API version to test against, as set by the
|
// Converter returns the api.Scheme for the API version to test against, as set by the
|
||||||
// KUBE_TEST_API env var.
|
// KUBE_TEST_API env var.
|
||||||
|
@@ -274,9 +274,16 @@ type StripVersionNegotiatedSerializer struct {
|
|||||||
runtime.NegotiatedSerializer
|
runtime.NegotiatedSerializer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n StripVersionNegotiatedSerializer) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
|
func (n StripVersionNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
||||||
encoder := n.NegotiatedSerializer.EncoderForVersion(serializer, gv)
|
serializer, ok := encoder.(runtime.Serializer)
|
||||||
return stripVersionEncoder{encoder, serializer}
|
if !ok {
|
||||||
|
// The stripVersionEncoder needs both an encoder and decoder, but is called from a context that doesn't have access to the
|
||||||
|
// decoder. We do a best effort cast here (since this code path is only for backwards compatibility) to get access to the caller's
|
||||||
|
// decoder.
|
||||||
|
panic(fmt.Sprintf("Unable to extract serializer from %#v", encoder))
|
||||||
|
}
|
||||||
|
versioned := n.NegotiatedSerializer.EncoderForVersion(encoder, gv)
|
||||||
|
return stripVersionEncoder{versioned, serializer}
|
||||||
}
|
}
|
||||||
|
|
||||||
func keepUnversioned(group string) bool {
|
func keepUnversioned(group string) bool {
|
||||||
@@ -422,14 +429,14 @@ func write(statusCode int, gv unversioned.GroupVersion, s runtime.NegotiatedSeri
|
|||||||
|
|
||||||
// writeNegotiated renders an object in the content type negotiated by the client
|
// writeNegotiated renders an object in the content type negotiated by the client
|
||||||
func writeNegotiated(s runtime.NegotiatedSerializer, gv unversioned.GroupVersion, w http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object) {
|
func writeNegotiated(s runtime.NegotiatedSerializer, gv unversioned.GroupVersion, w http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object) {
|
||||||
serializer, contentType, err := negotiateOutputSerializer(req, s)
|
serializer, err := negotiateOutputSerializer(req, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status := errToAPIStatus(err)
|
status := errToAPIStatus(err)
|
||||||
writeRawJSON(int(status.Code), status, w)
|
writeRawJSON(int(status.Code), status, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", serializer.MediaType)
|
||||||
w.WriteHeader(statusCode)
|
w.WriteHeader(statusCode)
|
||||||
|
|
||||||
encoder := s.EncoderForVersion(serializer, gv)
|
encoder := s.EncoderForVersion(serializer, gv)
|
||||||
|
@@ -50,31 +50,31 @@ func negotiateOutput(req *http.Request, supported []string) (string, map[string]
|
|||||||
return mediaType, accept.Params, nil
|
return mediaType, accept.Params, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func negotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.Serializer, string, error) {
|
func negotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
|
||||||
supported := ns.SupportedMediaTypes()
|
supported := ns.SupportedMediaTypes()
|
||||||
mediaType, params, err := negotiateOutput(req, supported)
|
mediaType, params, err := negotiateOutput(req, supported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return runtime.SerializerInfo{}, err
|
||||||
}
|
}
|
||||||
if s, ok := ns.SerializerForMediaType(mediaType, params); ok {
|
if s, ok := ns.SerializerForMediaType(mediaType, params); ok {
|
||||||
return s, mediaType, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
return nil, "", errNotAcceptable{supported}
|
return runtime.SerializerInfo{}, errNotAcceptable{supported}
|
||||||
}
|
}
|
||||||
|
|
||||||
func negotiateOutputStreamSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.Serializer, runtime.Framer, string, string, error) {
|
func negotiateOutputStreamSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.StreamSerializerInfo, error) {
|
||||||
supported := ns.SupportedMediaTypes()
|
supported := ns.SupportedMediaTypes()
|
||||||
mediaType, params, err := negotiateOutput(req, supported)
|
mediaType, params, err := negotiateOutput(req, supported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", "", err
|
return runtime.StreamSerializerInfo{}, err
|
||||||
}
|
}
|
||||||
if s, f, exactMediaType, ok := ns.StreamingSerializerForMediaType(mediaType, params); ok {
|
if s, ok := ns.StreamingSerializerForMediaType(mediaType, params); ok {
|
||||||
return s, f, mediaType, exactMediaType, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
return nil, nil, "", "", errNotAcceptable{supported}
|
return runtime.StreamSerializerInfo{}, errNotAcceptable{supported}
|
||||||
}
|
}
|
||||||
|
|
||||||
func negotiateInputSerializer(req *http.Request, s runtime.NegotiatedSerializer) (runtime.Serializer, error) {
|
func negotiateInputSerializer(req *http.Request, s runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
|
||||||
supported := s.SupportedMediaTypes()
|
supported := s.SupportedMediaTypes()
|
||||||
mediaType := req.Header.Get("Content-Type")
|
mediaType := req.Header.Get("Content-Type")
|
||||||
if len(mediaType) == 0 {
|
if len(mediaType) == 0 {
|
||||||
@@ -82,11 +82,11 @@ func negotiateInputSerializer(req *http.Request, s runtime.NegotiatedSerializer)
|
|||||||
}
|
}
|
||||||
mediaType, options, err := mime.ParseMediaType(mediaType)
|
mediaType, options, err := mime.ParseMediaType(mediaType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errUnsupportedMediaType{supported}
|
return runtime.SerializerInfo{}, errUnsupportedMediaType{supported}
|
||||||
}
|
}
|
||||||
out, ok := s.SerializerForMediaType(mediaType, options)
|
out, ok := s.SerializerForMediaType(mediaType, options)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errUnsupportedMediaType{supported}
|
return runtime.SerializerInfo{}, errUnsupportedMediaType{supported}
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
@@ -41,27 +41,34 @@ func (n *fakeNegotiater) SupportedStreamingMediaTypes() []string {
|
|||||||
return n.streamTypes
|
return n.streamTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fakeNegotiater) SerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, bool) {
|
func (n *fakeNegotiater) SerializerForMediaType(mediaType string, options map[string]string) (runtime.SerializerInfo, bool) {
|
||||||
n.mediaType = mediaType
|
n.mediaType = mediaType
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
n.options = options
|
n.options = options
|
||||||
}
|
}
|
||||||
return n.serializer, n.serializer != nil
|
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.Serializer, runtime.Framer, string, bool) {
|
func (n *fakeNegotiater) StreamingSerializerForMediaType(mediaType string, options map[string]string) (runtime.StreamSerializerInfo, bool) {
|
||||||
n.streamMediaType = mediaType
|
n.streamMediaType = mediaType
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
n.streamOptions = options
|
n.streamOptions = options
|
||||||
}
|
}
|
||||||
return n.streamSerializer, n.framer, mediaType, n.streamSerializer != nil
|
return runtime.StreamSerializerInfo{
|
||||||
|
SerializerInfo: runtime.SerializerInfo{
|
||||||
|
Serializer: n.serializer,
|
||||||
|
MediaType: mediaType,
|
||||||
|
EncodesAsText: true,
|
||||||
|
},
|
||||||
|
Framer: n.framer,
|
||||||
|
}, n.streamSerializer != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
|
func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
||||||
return n.serializer
|
return n.serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fakeNegotiater) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder {
|
func (n *fakeNegotiater) DecoderToVersion(serializer runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
||||||
return n.serializer
|
return n.serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +235,7 @@ func TestNegotiate(t *testing.T) {
|
|||||||
req = &http.Request{Header: http.Header{}}
|
req = &http.Request{Header: http.Header{}}
|
||||||
req.Header.Set("Accept", test.accept)
|
req.Header.Set("Accept", test.accept)
|
||||||
}
|
}
|
||||||
s, contentType, err := negotiateOutputSerializer(req, test.ns)
|
s, err := negotiateOutputSerializer(req, test.ns)
|
||||||
switch {
|
switch {
|
||||||
case err == nil && test.errFn != nil:
|
case err == nil && test.errFn != nil:
|
||||||
t.Errorf("%d: failed: expected error", i)
|
t.Errorf("%d: failed: expected error", i)
|
||||||
@@ -251,11 +258,11 @@ func TestNegotiate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if test.contentType != contentType {
|
if test.contentType != s.MediaType {
|
||||||
t.Errorf("%d: unexpected %s %s", i, test.contentType, contentType)
|
t.Errorf("%d: unexpected %s %s", i, test.contentType, s.MediaType)
|
||||||
}
|
}
|
||||||
if s != test.serializer {
|
if s.Serializer != test.serializer {
|
||||||
t.Errorf("%d: unexpected %s %s", i, test.serializer, s)
|
t.Errorf("%d: unexpected %s %s", i, test.serializer, s.Serializer)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(test.params, test.ns.options) {
|
if !reflect.DeepEqual(test.params, test.ns.options) {
|
||||||
t.Errorf("%d: unexpected %#v %#v", i, test.params, test.ns.options)
|
t.Errorf("%d: unexpected %#v %#v", i, test.params, test.ns.options)
|
||||||
|
@@ -59,47 +59,33 @@ func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) {
|
|||||||
return t.C, t.Stop
|
return t.C, t.Stop
|
||||||
}
|
}
|
||||||
|
|
||||||
type textEncodable interface {
|
|
||||||
// EncodesAsText should return true if objects should be transmitted as a WebSocket Text
|
|
||||||
// frame (otherwise, they will be sent as a Binary frame).
|
|
||||||
EncodesAsText() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// serveWatch handles serving requests to the server
|
// serveWatch handles serving requests to the server
|
||||||
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
|
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
|
||||||
func serveWatch(watcher watch.Interface, scope RequestScope, req *restful.Request, res *restful.Response, timeout time.Duration) {
|
func serveWatch(watcher watch.Interface, scope RequestScope, req *restful.Request, res *restful.Response, timeout time.Duration) {
|
||||||
// negotiate for the stream serializer
|
// negotiate for the stream serializer
|
||||||
serializer, framer, mediaType, exactMediaType, err := negotiateOutputStreamSerializer(req.Request, scope.Serializer)
|
serializer, err := negotiateOutputStreamSerializer(req.Request, scope.Serializer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, res.ResponseWriter, req.Request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if framer == nil {
|
if serializer.Framer == nil {
|
||||||
scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", 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, scope.Kind.GroupVersion())
|
encoder := scope.Serializer.EncoderForVersion(serializer.Serializer, scope.Kind.GroupVersion())
|
||||||
|
|
||||||
useTextFraming := false
|
useTextFraming := serializer.EncodesAsText
|
||||||
if encodable, ok := serializer.(textEncodable); ok && encodable.EncodesAsText() {
|
|
||||||
useTextFraming = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the embedded serializer matching the media type
|
// find the embedded serializer matching the media type
|
||||||
embeddedSerializer, ok := scope.Serializer.SerializerForMediaType(mediaType, nil)
|
embeddedEncoder := scope.Serializer.EncoderForVersion(serializer.Embedded.Serializer, scope.Kind.GroupVersion())
|
||||||
if !ok {
|
|
||||||
scope.err(fmt.Errorf("no serializer defined for %q available for embedded encoding", mediaType), res.ResponseWriter, req.Request)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
embeddedEncoder := scope.Serializer.EncoderForVersion(embeddedSerializer, scope.Kind.GroupVersion())
|
|
||||||
|
|
||||||
server := &WatchServer{
|
server := &WatchServer{
|
||||||
watching: watcher,
|
watching: watcher,
|
||||||
scope: scope,
|
scope: scope,
|
||||||
|
|
||||||
useTextFraming: useTextFraming,
|
useTextFraming: useTextFraming,
|
||||||
mediaType: exactMediaType,
|
mediaType: serializer.MediaType,
|
||||||
framer: framer,
|
framer: serializer.Framer,
|
||||||
encoder: encoder,
|
encoder: encoder,
|
||||||
embeddedEncoder: embeddedEncoder,
|
embeddedEncoder: embeddedEncoder,
|
||||||
fixup: func(obj runtime.Object) {
|
fixup: func(obj runtime.Object) {
|
||||||
|
@@ -222,9 +222,9 @@ func TestWatchRead(t *testing.T) {
|
|||||||
|
|
||||||
for _, protocol := range protocols {
|
for _, protocol := range protocols {
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
serializer, framer, _, ok := api.Codecs.StreamingSerializerForMediaType(test.MediaType, nil)
|
serializer, ok := api.Codecs.StreamingSerializerForMediaType(test.MediaType, nil)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal(framer)
|
t.Fatal(serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, contentType := protocol.fn(test.Accept)
|
r, contentType := protocol.fn(test.Accept)
|
||||||
@@ -235,13 +235,13 @@ func TestWatchRead(t *testing.T) {
|
|||||||
}
|
}
|
||||||
objectSerializer, ok := api.Codecs.SerializerForMediaType(test.MediaType, nil)
|
objectSerializer, ok := api.Codecs.SerializerForMediaType(test.MediaType, nil)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal(framer)
|
t.Fatal(objectSerializer)
|
||||||
}
|
}
|
||||||
objectCodec := api.Codecs.DecoderToVersion(objectSerializer, testInternalGroupVersion)
|
objectCodec := api.Codecs.DecoderToVersion(objectSerializer, testInternalGroupVersion)
|
||||||
|
|
||||||
var fr io.ReadCloser = r
|
var fr io.ReadCloser = r
|
||||||
if !protocol.selfFraming {
|
if !protocol.selfFraming {
|
||||||
fr = framer.NewFrameReader(r)
|
fr = serializer.Framer.NewFrameReader(r)
|
||||||
}
|
}
|
||||||
d := streaming.NewDecoder(fr, serializer)
|
d := streaming.NewDecoder(fr, serializer)
|
||||||
|
|
||||||
@@ -495,9 +495,9 @@ func TestWatchHTTPTimeout(t *testing.T) {
|
|||||||
timeoutCh := make(chan time.Time)
|
timeoutCh := make(chan time.Time)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
_, framer, _, ok := api.Codecs.StreamingSerializerForMediaType("application/json", nil)
|
serializer, ok := api.Codecs.StreamingSerializerForMediaType("application/json", nil)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal(framer)
|
t.Fatal(serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup a new watchserver
|
// Setup a new watchserver
|
||||||
@@ -505,7 +505,7 @@ func TestWatchHTTPTimeout(t *testing.T) {
|
|||||||
watching: watcher,
|
watching: watcher,
|
||||||
|
|
||||||
mediaType: "testcase/json",
|
mediaType: "testcase/json",
|
||||||
framer: framer,
|
framer: serializer.Framer,
|
||||||
encoder: newCodec,
|
encoder: newCodec,
|
||||||
embeddedEncoder: newCodec,
|
embeddedEncoder: newCodec,
|
||||||
|
|
||||||
|
@@ -139,26 +139,23 @@ func readExpBackoffConfig() BackoffManager {
|
|||||||
func createSerializers(config ContentConfig) (*Serializers, error) {
|
func createSerializers(config ContentConfig) (*Serializers, error) {
|
||||||
negotiated := config.NegotiatedSerializer
|
negotiated := config.NegotiatedSerializer
|
||||||
contentType := config.ContentType
|
contentType := config.ContentType
|
||||||
serializer, ok := negotiated.SerializerForMediaType(contentType, nil)
|
info, ok := negotiated.SerializerForMediaType(contentType, nil)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("serializer for %s not registered", contentType)
|
return nil, fmt.Errorf("serializer for %s not registered", contentType)
|
||||||
}
|
}
|
||||||
streamingSerializer, framer, _, ok := negotiated.StreamingSerializerForMediaType(contentType, nil)
|
streamInfo, ok := negotiated.StreamingSerializerForMediaType(contentType, nil)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("streaming serializer for %s not registered", contentType)
|
return nil, fmt.Errorf("streaming serializer for %s not registered", contentType)
|
||||||
}
|
}
|
||||||
if framer == nil {
|
|
||||||
return nil, fmt.Errorf("no framer for %s", contentType)
|
|
||||||
}
|
|
||||||
internalGV := unversioned.GroupVersion{
|
internalGV := unversioned.GroupVersion{
|
||||||
Group: config.GroupVersion.Group,
|
Group: config.GroupVersion.Group,
|
||||||
Version: runtime.APIVersionInternal,
|
Version: runtime.APIVersionInternal,
|
||||||
}
|
}
|
||||||
return &Serializers{
|
return &Serializers{
|
||||||
Encoder: negotiated.EncoderForVersion(serializer, *config.GroupVersion),
|
Encoder: negotiated.EncoderForVersion(info.Serializer, *config.GroupVersion),
|
||||||
Decoder: negotiated.DecoderToVersion(serializer, internalGV),
|
Decoder: negotiated.DecoderToVersion(info.Serializer, internalGV),
|
||||||
StreamingSerializer: streamingSerializer,
|
StreamingSerializer: streamInfo.Serializer,
|
||||||
Framer: framer,
|
Framer: streamInfo.Framer,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -47,7 +47,7 @@ func TestDoRequestSuccess(t *testing.T) {
|
|||||||
Host: testServer.URL,
|
Host: testServer.URL,
|
||||||
ContentConfig: ContentConfig{
|
ContentConfig: ContentConfig{
|
||||||
GroupVersion: testapi.Default.GroupVersion(),
|
GroupVersion: testapi.Default.GroupVersion(),
|
||||||
NegotiatedSerializer: testapi.NegotiatedSerializer,
|
NegotiatedSerializer: testapi.Default.NegotiatedSerializer(),
|
||||||
},
|
},
|
||||||
Username: "user",
|
Username: "user",
|
||||||
Password: "pass",
|
Password: "pass",
|
||||||
@@ -92,7 +92,7 @@ func TestDoRequestFailed(t *testing.T) {
|
|||||||
Host: testServer.URL,
|
Host: testServer.URL,
|
||||||
ContentConfig: ContentConfig{
|
ContentConfig: ContentConfig{
|
||||||
GroupVersion: testapi.Default.GroupVersion(),
|
GroupVersion: testapi.Default.GroupVersion(),
|
||||||
NegotiatedSerializer: testapi.NegotiatedSerializer,
|
NegotiatedSerializer: testapi.Default.NegotiatedSerializer(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -130,7 +130,7 @@ func TestDoRequestCreated(t *testing.T) {
|
|||||||
Host: testServer.URL,
|
Host: testServer.URL,
|
||||||
ContentConfig: ContentConfig{
|
ContentConfig: ContentConfig{
|
||||||
GroupVersion: testapi.Default.GroupVersion(),
|
GroupVersion: testapi.Default.GroupVersion(),
|
||||||
NegotiatedSerializer: testapi.NegotiatedSerializer,
|
NegotiatedSerializer: testapi.Default.NegotiatedSerializer(),
|
||||||
},
|
},
|
||||||
Username: "user",
|
Username: "user",
|
||||||
Password: "pass",
|
Password: "pass",
|
||||||
|
@@ -87,13 +87,13 @@ func TestSetKubernetesDefaultsUserAgent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRESTClientRequires(t *testing.T) {
|
func TestRESTClientRequires(t *testing.T) {
|
||||||
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{NegotiatedSerializer: testapi.NegotiatedSerializer}}); err == nil {
|
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{NegotiatedSerializer: testapi.Default.NegotiatedSerializer()}}); err == nil {
|
||||||
t.Errorf("unexpected non-error")
|
t.Errorf("unexpected non-error")
|
||||||
}
|
}
|
||||||
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}); err == nil {
|
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}); err == nil {
|
||||||
t.Errorf("unexpected non-error")
|
t.Errorf("unexpected non-error")
|
||||||
}
|
}
|
||||||
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{GroupVersion: testapi.Default.GroupVersion(), NegotiatedSerializer: testapi.NegotiatedSerializer}}); err != nil {
|
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{GroupVersion: testapi.Default.GroupVersion(), NegotiatedSerializer: testapi.Default.NegotiatedSerializer()}}); err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -639,6 +639,10 @@ func (r *Request) Watch() (watch.Interface, error) {
|
|||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return nil, r.err
|
return nil, r.err
|
||||||
}
|
}
|
||||||
|
if r.serializers.Framer == nil {
|
||||||
|
return nil, fmt.Errorf("watching resources is not possible with this client (content-type: %s)", r.content.ContentType)
|
||||||
|
}
|
||||||
|
|
||||||
url := r.URL().String()
|
url := r.URL().String()
|
||||||
req, err := http.NewRequest(r.verb, url, r.body)
|
req, err := http.NewRequest(r.verb, url, r.body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -247,7 +247,7 @@ func defaultContentConfig() ContentConfig {
|
|||||||
return ContentConfig{
|
return ContentConfig{
|
||||||
GroupVersion: testapi.Default.GroupVersion(),
|
GroupVersion: testapi.Default.GroupVersion(),
|
||||||
Codec: testapi.Default.Codec(),
|
Codec: testapi.Default.Codec(),
|
||||||
NegotiatedSerializer: testapi.NegotiatedSerializer,
|
NegotiatedSerializer: testapi.Default.NegotiatedSerializer(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,6 +549,7 @@ func TestRequestWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Request: &Request{
|
Request: &Request{
|
||||||
|
serializers: defaultSerializers(),
|
||||||
client: clientFunc(func(req *http.Request) (*http.Response, error) {
|
client: clientFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
}),
|
}),
|
||||||
@@ -558,6 +559,7 @@ func TestRequestWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Request: &Request{
|
Request: &Request{
|
||||||
|
serializers: defaultSerializers(),
|
||||||
client: clientFunc(func(req *http.Request) (*http.Response, error) {
|
client: clientFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
return nil, &url.Error{Err: io.EOF}
|
return nil, &url.Error{Err: io.EOF}
|
||||||
}),
|
}),
|
||||||
@@ -567,6 +569,7 @@ func TestRequestWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Request: &Request{
|
Request: &Request{
|
||||||
|
serializers: defaultSerializers(),
|
||||||
client: clientFunc(func(req *http.Request) (*http.Response, error) {
|
client: clientFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
return nil, errors.New("http: can't write HTTP request on broken connection")
|
return nil, errors.New("http: can't write HTTP request on broken connection")
|
||||||
}),
|
}),
|
||||||
@@ -576,6 +579,7 @@ func TestRequestWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Request: &Request{
|
Request: &Request{
|
||||||
|
serializers: defaultSerializers(),
|
||||||
client: clientFunc(func(req *http.Request) (*http.Response, error) {
|
client: clientFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
return nil, errors.New("foo: connection reset by peer")
|
return nil, errors.New("foo: connection reset by peer")
|
||||||
}),
|
}),
|
||||||
|
@@ -215,7 +215,10 @@ 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(codec, codec, runtime.DefaultFramer)
|
config.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(
|
||||||
|
runtime.SerializerInfo{Serializer: codec},
|
||||||
|
runtime.StreamSerializerInfo{},
|
||||||
|
)
|
||||||
if len(config.UserAgent) == 0 {
|
if len(config.UserAgent) == 0 {
|
||||||
config.UserAgent = restclient.DefaultKubernetesUserAgent()
|
config.UserAgent = restclient.DefaultKubernetesUserAgent()
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/conversion/queryparams"
|
"k8s.io/kubernetes/pkg/conversion/queryparams"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer"
|
"k8s.io/kubernetes/pkg/runtime/serializer"
|
||||||
serializerjson "k8s.io/kubernetes/pkg/runtime/serializer/json"
|
|
||||||
"k8s.io/kubernetes/pkg/watch"
|
"k8s.io/kubernetes/pkg/watch"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -51,8 +50,10 @@ func NewClient(conf *restclient.Config) (*Client, error) {
|
|||||||
conf = &confCopy
|
conf = &confCopy
|
||||||
|
|
||||||
codec := dynamicCodec{}
|
codec := dynamicCodec{}
|
||||||
legacyCodec := api.Codecs.LegacyCodec(v1.SchemeGroupVersion)
|
|
||||||
conf.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(codec, legacyCodec, serializerjson.Framer)
|
// TODO: it's questionable that this should be using anything other than unstructured schema and JSON
|
||||||
|
streamingInfo, _ := api.Codecs.StreamingSerializerForMediaType("application/json;stream=watch", nil)
|
||||||
|
conf.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec}, streamingInfo)
|
||||||
|
|
||||||
if conf.APIPath == "" {
|
if conf.APIPath == "" {
|
||||||
conf.APIPath = "/api"
|
conf.APIPath = "/api"
|
||||||
|
@@ -42,7 +42,7 @@ func TestSetKubernetesDefaults(t *testing.T) {
|
|||||||
ContentConfig: restclient.ContentConfig{
|
ContentConfig: restclient.ContentConfig{
|
||||||
GroupVersion: testapi.Default.GroupVersion(),
|
GroupVersion: testapi.Default.GroupVersion(),
|
||||||
Codec: testapi.Default.Codec(),
|
Codec: testapi.Default.Codec(),
|
||||||
NegotiatedSerializer: testapi.NegotiatedSerializer,
|
NegotiatedSerializer: testapi.Default.NegotiatedSerializer(),
|
||||||
},
|
},
|
||||||
QPS: 5,
|
QPS: 5,
|
||||||
Burst: 10,
|
Burst: 10,
|
||||||
@@ -126,7 +126,7 @@ func TestHelperGetServerAPIVersions(t *testing.T) {
|
|||||||
w.Write(output)
|
w.Write(output)
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
got, err := restclient.ServerAPIVersions(&restclient.Config{Host: server.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &unversioned.GroupVersion{Group: "invalid version", Version: "one"}, NegotiatedSerializer: testapi.NegotiatedSerializer}})
|
got, err := restclient.ServerAPIVersions(&restclient.Config{Host: server.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &unversioned.GroupVersion{Group: "invalid version", Version: "one"}, NegotiatedSerializer: testapi.Default.NegotiatedSerializer()}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected encoding error: %v", err)
|
t.Fatalf("unexpected encoding error: %v", err)
|
||||||
}
|
}
|
||||||
|
@@ -215,7 +215,7 @@ func TestStream(t *testing.T) {
|
|||||||
url, _ := url.ParseRequestURI(server.URL)
|
url, _ := url.ParseRequestURI(server.URL)
|
||||||
config := restclient.ContentConfig{
|
config := restclient.ContentConfig{
|
||||||
GroupVersion: &unversioned.GroupVersion{Group: "x"},
|
GroupVersion: &unversioned.GroupVersion{Group: "x"},
|
||||||
NegotiatedSerializer: testapi.NegotiatedSerializer,
|
NegotiatedSerializer: testapi.Default.NegotiatedSerializer(),
|
||||||
}
|
}
|
||||||
c, err := restclient.NewRESTClient(url, "", config, -1, -1, nil, nil)
|
c, err := restclient.NewRESTClient(url, "", config, -1, -1, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -18,9 +18,11 @@ package genericapiserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"mime"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime/serializer/recognizer"
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
|
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
|
||||||
"k8s.io/kubernetes/pkg/storage"
|
"k8s.io/kubernetes/pkg/storage"
|
||||||
"k8s.io/kubernetes/pkg/storage/storagebackend"
|
"k8s.io/kubernetes/pkg/storage/storagebackend"
|
||||||
@@ -50,19 +52,25 @@ type DefaultStorageFactory struct {
|
|||||||
|
|
||||||
Overrides map[unversioned.GroupResource]groupResourceOverrides
|
Overrides map[unversioned.GroupResource]groupResourceOverrides
|
||||||
|
|
||||||
|
// DefaultMediaType is the media type used to store resources. If it is not set, "application/json" is used.
|
||||||
|
DefaultMediaType string
|
||||||
|
|
||||||
// DefaultSerializer is used to create encoders and decoders for the storage.Interface.
|
// DefaultSerializer is used to create encoders and decoders for the storage.Interface.
|
||||||
DefaultSerializer runtime.NegotiatedSerializer
|
DefaultSerializer runtime.StorageSerializer
|
||||||
|
|
||||||
// ResourceEncodingConfig describes how to encode a particular GroupVersionResource
|
// ResourceEncodingConfig describes how to encode a particular GroupVersionResource
|
||||||
ResourceEncodingConfig ResourceEncodingConfig
|
ResourceEncodingConfig ResourceEncodingConfig
|
||||||
|
|
||||||
// APIResourceConfigSource indicates whether the *storage* is enabled, NOT the API
|
// APIResourceConfigSource indicates whether the *storage* is enabled, NOT the API
|
||||||
// This is discrete from resource enablement because those are separate concerns. How it is surfaced to the user via flags
|
// This is discrete from resource enablement because those are separate concerns. How this source is configured
|
||||||
// or config is up to whoever is building this.
|
// is left to the caller.
|
||||||
APIResourceConfigSource APIResourceConfigSource
|
APIResourceConfigSource APIResourceConfigSource
|
||||||
|
|
||||||
// newEtcdFn exists to be overwritten for unit testing. You should never set this in a normal world.
|
// newStorageCodecFn exists to be overwritten for unit testing.
|
||||||
newEtcdFn func(ns runtime.NegotiatedSerializer, storageVersion, memoryVersion unversioned.GroupVersion, config storagebackend.Config) (etcdStorage storage.Interface, err error)
|
newStorageCodecFn func(storageMediaType string, ns runtime.StorageSerializer, storageVersion, memoryVersion unversioned.GroupVersion, config storagebackend.Config) (codec runtime.Codec, err error)
|
||||||
|
|
||||||
|
// newStorageFn exists to be overwritten for unit testing.
|
||||||
|
newStorageFn func(config storagebackend.Config) (etcdStorage storage.Interface, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type groupResourceOverrides struct {
|
type groupResourceOverrides struct {
|
||||||
@@ -71,8 +79,10 @@ type groupResourceOverrides struct {
|
|||||||
etcdLocation []string
|
etcdLocation []string
|
||||||
// etcdPrefix contains the list of "special" prefixes for a GroupResource. Resource=* means for the entire group
|
// etcdPrefix contains the list of "special" prefixes for a GroupResource. Resource=* means for the entire group
|
||||||
etcdPrefix string
|
etcdPrefix string
|
||||||
|
// mediaType is the desired serializer to choose. If empty, the default is chosen.
|
||||||
|
mediaType string
|
||||||
// serializer contains the list of "special" serializers for a GroupResource. Resource=* means for the entire group
|
// serializer contains the list of "special" serializers for a GroupResource. Resource=* means for the entire group
|
||||||
serializer runtime.NegotiatedSerializer
|
serializer runtime.StorageSerializer
|
||||||
// cohabitatingResources keeps track of which resources must be stored together. This happens when we have multiple ways
|
// cohabitatingResources keeps track of which resources must be stored together. This happens when we have multiple ways
|
||||||
// of exposing one set of concepts. autoscaling.HPA and extensions.HPA as a for instance
|
// of exposing one set of concepts. autoscaling.HPA and extensions.HPA as a for instance
|
||||||
// The order of the slice matters! It is the priority order of lookup for finding a storage location
|
// The order of the slice matters! It is the priority order of lookup for finding a storage location
|
||||||
@@ -83,15 +93,20 @@ var _ StorageFactory = &DefaultStorageFactory{}
|
|||||||
|
|
||||||
const AllResources = "*"
|
const AllResources = "*"
|
||||||
|
|
||||||
func NewDefaultStorageFactory(config storagebackend.Config, defaultSerializer runtime.NegotiatedSerializer, resourceEncodingConfig ResourceEncodingConfig, resourceConfig APIResourceConfigSource) *DefaultStorageFactory {
|
func NewDefaultStorageFactory(config storagebackend.Config, defaultMediaType string, defaultSerializer runtime.StorageSerializer, resourceEncodingConfig ResourceEncodingConfig, resourceConfig APIResourceConfigSource) *DefaultStorageFactory {
|
||||||
|
if len(defaultMediaType) == 0 {
|
||||||
|
defaultMediaType = runtime.ContentTypeJSON
|
||||||
|
}
|
||||||
return &DefaultStorageFactory{
|
return &DefaultStorageFactory{
|
||||||
StorageConfig: config,
|
StorageConfig: config,
|
||||||
Overrides: map[unversioned.GroupResource]groupResourceOverrides{},
|
Overrides: map[unversioned.GroupResource]groupResourceOverrides{},
|
||||||
|
DefaultMediaType: defaultMediaType,
|
||||||
DefaultSerializer: defaultSerializer,
|
DefaultSerializer: defaultSerializer,
|
||||||
ResourceEncodingConfig: resourceEncodingConfig,
|
ResourceEncodingConfig: resourceEncodingConfig,
|
||||||
APIResourceConfigSource: resourceConfig,
|
APIResourceConfigSource: resourceConfig,
|
||||||
|
|
||||||
newEtcdFn: newEtcd,
|
newStorageCodecFn: NewStorageCodec,
|
||||||
|
newStorageFn: newStorage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +122,9 @@ func (s *DefaultStorageFactory) SetEtcdPrefix(groupResource unversioned.GroupRes
|
|||||||
s.Overrides[groupResource] = overrides
|
s.Overrides[groupResource] = overrides
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultStorageFactory) SetSerializer(groupResource unversioned.GroupResource, serializer runtime.NegotiatedSerializer) {
|
func (s *DefaultStorageFactory) SetSerializer(groupResource unversioned.GroupResource, mediaType string, serializer runtime.StorageSerializer) {
|
||||||
overrides := s.Overrides[groupResource]
|
overrides := s.Overrides[groupResource]
|
||||||
|
overrides.mediaType = mediaType
|
||||||
overrides.serializer = serializer
|
overrides.serializer = serializer
|
||||||
s.Overrides[groupResource] = overrides
|
s.Overrides[groupResource] = overrides
|
||||||
}
|
}
|
||||||
@@ -160,6 +176,14 @@ func (s *DefaultStorageFactory) New(groupResource unversioned.GroupResource) (st
|
|||||||
etcdPrefix = exactResourceOverride.etcdPrefix
|
etcdPrefix = exactResourceOverride.etcdPrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
etcdMediaType := s.DefaultMediaType
|
||||||
|
if len(groupOverride.mediaType) != 0 {
|
||||||
|
etcdMediaType = groupOverride.mediaType
|
||||||
|
}
|
||||||
|
if len(exactResourceOverride.mediaType) != 0 {
|
||||||
|
etcdMediaType = exactResourceOverride.mediaType
|
||||||
|
}
|
||||||
|
|
||||||
etcdSerializer := s.DefaultSerializer
|
etcdSerializer := s.DefaultSerializer
|
||||||
if groupOverride.serializer != nil {
|
if groupOverride.serializer != nil {
|
||||||
etcdSerializer = groupOverride.serializer
|
etcdSerializer = groupOverride.serializer
|
||||||
@@ -183,27 +207,19 @@ func (s *DefaultStorageFactory) New(groupResource unversioned.GroupResource) (st
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.V(3).Infof("storing %v in %v, reading as %v from %v", groupResource, storageEncodingVersion, internalVersion, config)
|
codec, err := s.newStorageCodecFn(etcdMediaType, etcdSerializer, storageEncodingVersion, internalVersion, config)
|
||||||
return s.newEtcdFn(etcdSerializer, storageEncodingVersion, internalVersion, config)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func newEtcd(ns runtime.NegotiatedSerializer, storageVersion, memoryVersion unversioned.GroupVersion, config storagebackend.Config) (etcdStorage storage.Interface, err error) {
|
config.Codec = codec
|
||||||
s, ok := ns.SerializerForMediaType("application/json", nil)
|
|
||||||
if !ok {
|
glog.V(3).Infof("storing %v in %v, reading as %v from %v", groupResource, storageEncodingVersion, internalVersion, config)
|
||||||
return nil, fmt.Errorf("unable to find serializer for JSON")
|
return s.newStorageFn(config)
|
||||||
}
|
}
|
||||||
encoder := ns.EncoderForVersion(s, storageVersion)
|
|
||||||
decoder := ns.DecoderToVersion(s, memoryVersion)
|
// newStorage is the default implementation for creating a storage backend.
|
||||||
if memoryVersion.Group != storageVersion.Group {
|
func newStorage(config storagebackend.Config) (etcdStorage storage.Interface, err error) {
|
||||||
// Allow this codec to translate between groups.
|
|
||||||
if err = versioning.EnableCrossGroupEncoding(encoder, memoryVersion.Group, storageVersion.Group); err != nil {
|
|
||||||
return nil, fmt.Errorf("error setting up encoder from %v to %v: %v", memoryVersion, storageVersion, err)
|
|
||||||
}
|
|
||||||
if err = versioning.EnableCrossGroupDecoding(decoder, storageVersion.Group, memoryVersion.Group); err != nil {
|
|
||||||
return nil, fmt.Errorf("error setting up decoder from %v to %v: %v", storageVersion, memoryVersion, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.Codec = runtime.NewCodec(encoder, decoder)
|
|
||||||
return storagebackend.Create(config)
|
return storagebackend.Create(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,3 +233,39 @@ func (s *DefaultStorageFactory) Backends() []string {
|
|||||||
}
|
}
|
||||||
return backends.List()
|
return backends.List()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewStorageCodec assembles a storage codec for the provided storage media type, the provided serializer, and the requested
|
||||||
|
// storage and memory versions.
|
||||||
|
func NewStorageCodec(storageMediaType string, ns runtime.StorageSerializer, storageVersion, memoryVersion unversioned.GroupVersion, config storagebackend.Config) (runtime.Codec, error) {
|
||||||
|
mediaType, options, err := mime.ParseMediaType(storageMediaType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%q is not a valid mime-type", storageMediaType)
|
||||||
|
}
|
||||||
|
serializer, ok := ns.SerializerForMediaType(mediaType, options)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unable to find serializer for %q", storageMediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := serializer.Serializer
|
||||||
|
|
||||||
|
// etcd2 only supports string data - we must wrap any result before returning
|
||||||
|
// TODO: storagebackend should return a boolean indicating whether it supports binary data
|
||||||
|
if !serializer.EncodesAsText && (config.Type == storagebackend.StorageTypeUnset || config.Type == storagebackend.StorageTypeETCD2) {
|
||||||
|
glog.V(4).Infof("Wrapping the underlying binary storage serializer with a base64 encoding for etcd2")
|
||||||
|
s = runtime.NewBase64Serializer(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
ds := recognizer.NewDecoder(s, ns.UniversalDeserializer())
|
||||||
|
encoder := ns.EncoderForVersion(s, storageVersion)
|
||||||
|
decoder := ns.DecoderToVersion(ds, memoryVersion)
|
||||||
|
if memoryVersion.Group != storageVersion.Group {
|
||||||
|
// Allow this codec to translate between groups.
|
||||||
|
if err := versioning.EnableCrossGroupEncoding(encoder, memoryVersion.Group, storageVersion.Group); err != nil {
|
||||||
|
return nil, fmt.Errorf("error setting up encoder from %v to %v: %v", memoryVersion, storageVersion, err)
|
||||||
|
}
|
||||||
|
if err := versioning.EnableCrossGroupDecoding(decoder, storageVersion.Group, memoryVersion.Group); err != nil {
|
||||||
|
return nil, fmt.Errorf("error setting up decoder from %v to %v: %v", storageVersion, memoryVersion, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return runtime.NewCodec(encoder, decoder), nil
|
||||||
|
}
|
||||||
|
@@ -23,7 +23,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
|
||||||
"k8s.io/kubernetes/pkg/storage"
|
"k8s.io/kubernetes/pkg/storage"
|
||||||
"k8s.io/kubernetes/pkg/storage/storagebackend"
|
"k8s.io/kubernetes/pkg/storage/storagebackend"
|
||||||
)
|
)
|
||||||
@@ -50,7 +49,7 @@ func TestUpdateEtcdOverrides(t *testing.T) {
|
|||||||
defaultEtcdLocation := []string{"http://127.0.0.1"}
|
defaultEtcdLocation := []string{"http://127.0.0.1"}
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
actualConfig := storagebackend.Config{}
|
actualConfig := storagebackend.Config{}
|
||||||
newEtcdFn := func(ns runtime.NegotiatedSerializer, storageVersion, memoryVersion unversioned.GroupVersion, config storagebackend.Config) (etcdStorage storage.Interface, err error) {
|
newStorageFn := func(config storagebackend.Config) (_ storage.Interface, err error) {
|
||||||
actualConfig = config
|
actualConfig = config
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -59,8 +58,8 @@ func TestUpdateEtcdOverrides(t *testing.T) {
|
|||||||
Prefix: DefaultEtcdPathPrefix,
|
Prefix: DefaultEtcdPathPrefix,
|
||||||
ServerList: defaultEtcdLocation,
|
ServerList: defaultEtcdLocation,
|
||||||
}
|
}
|
||||||
storageFactory := NewDefaultStorageFactory(defaultConfig, api.Codecs, NewDefaultResourceEncodingConfig(), NewResourceConfig())
|
storageFactory := NewDefaultStorageFactory(defaultConfig, "", api.Codecs, NewDefaultResourceEncodingConfig(), NewResourceConfig())
|
||||||
storageFactory.newEtcdFn = newEtcdFn
|
storageFactory.newStorageFn = newStorageFn
|
||||||
storageFactory.SetEtcdLocation(test.resource, test.servers)
|
storageFactory.SetEtcdLocation(test.resource, test.servers)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@@ -55,7 +55,7 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
|
|||||||
var obj runtime.Object
|
var obj runtime.Object
|
||||||
var versioned runtime.Object
|
var versioned runtime.Object
|
||||||
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
|
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
|
||||||
obj, err = runtime.Decode(thirdpartyresourcedata.NewCodec(nil, gvk.Kind), data)
|
obj, err = runtime.Decode(thirdpartyresourcedata.NewDecoder(nil, gvk.Kind), data)
|
||||||
versioned = obj
|
versioned = obj
|
||||||
} else {
|
} else {
|
||||||
obj, versioned = versions.Last(), versions.First()
|
obj, versioned = versions.Last(), versions.First()
|
||||||
|
@@ -677,7 +677,14 @@ func (m *Master) InstallThirdPartyResource(rsrc *extensions.ThirdPartyResource)
|
|||||||
|
|
||||||
func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupVersion {
|
func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupVersion {
|
||||||
resourceStorage := thirdpartyresourcedataetcd.NewREST(
|
resourceStorage := thirdpartyresourcedataetcd.NewREST(
|
||||||
generic.RESTOptions{Storage: m.thirdPartyStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: m.deleteCollectionWorkers}, group, kind)
|
generic.RESTOptions{
|
||||||
|
Storage: m.thirdPartyStorage,
|
||||||
|
Decorator: generic.UndecoratedStorage,
|
||||||
|
DeleteCollectionWorkers: m.deleteCollectionWorkers,
|
||||||
|
},
|
||||||
|
group,
|
||||||
|
kind,
|
||||||
|
)
|
||||||
|
|
||||||
apiRoot := makeThirdPartyPath("")
|
apiRoot := makeThirdPartyPath("")
|
||||||
|
|
||||||
|
@@ -88,7 +88,7 @@ func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.
|
|||||||
resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
|
resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
|
||||||
resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal})
|
resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal})
|
||||||
resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal})
|
resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal})
|
||||||
storageFactory := genericapiserver.NewDefaultStorageFactory(storageConfig, api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource())
|
storageFactory := genericapiserver.NewDefaultStorageFactory(storageConfig, testapi.StorageMediaType(), api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource())
|
||||||
|
|
||||||
config.StorageFactory = storageFactory
|
config.StorageFactory = storageFactory
|
||||||
config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
|
config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
|
||||||
|
@@ -90,7 +90,7 @@ func hasCreated(t *testing.T, pod *api.Pod) func(runtime.Object) bool {
|
|||||||
func NewTestGenericStoreRegistry(t *testing.T) (*etcdtesting.EtcdTestServer, *Store) {
|
func NewTestGenericStoreRegistry(t *testing.T) (*etcdtesting.EtcdTestServer, *Store) {
|
||||||
podPrefix := "/pods"
|
podPrefix := "/pods"
|
||||||
server := etcdtesting.NewEtcdTestClientServer(t)
|
server := etcdtesting.NewEtcdTestClientServer(t)
|
||||||
s := etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)
|
s := etcdstorage.NewEtcdStorage(server.Client, testapi.Default.StorageCodec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)
|
||||||
strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true}
|
strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true}
|
||||||
|
|
||||||
return server, &Store{
|
return server, &Store{
|
||||||
|
@@ -38,7 +38,7 @@ import (
|
|||||||
|
|
||||||
func NewEtcdStorage(t *testing.T, group string) (storage.Interface, *etcdtesting.EtcdTestServer) {
|
func NewEtcdStorage(t *testing.T, group string) (storage.Interface, *etcdtesting.EtcdTestServer) {
|
||||||
server := etcdtesting.NewEtcdTestClientServer(t)
|
server := etcdtesting.NewEtcdTestClientServer(t)
|
||||||
storage := etcdstorage.NewEtcdStorage(server.Client, testapi.Groups[group].Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)
|
storage := etcdstorage.NewEtcdStorage(server.Client, testapi.Groups[group].StorageCodec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)
|
||||||
return storage, server
|
return storage, server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -33,6 +33,7 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type thirdPartyObjectConverter struct {
|
type thirdPartyObjectConverter struct {
|
||||||
@@ -183,31 +184,39 @@ func NewNegotiatedSerializer(s runtime.NegotiatedSerializer, kind string, encode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
|
func (t *thirdPartyResourceDataCodecFactory) SupportedMediaTypes() []string {
|
||||||
return NewCodec(runtime.NewCodec(
|
supported := sets.NewString(t.NegotiatedSerializer.SupportedMediaTypes()...)
|
||||||
t.NegotiatedSerializer.EncoderForVersion(s, gv),
|
return supported.Intersection(sets.NewString("application/json", "application/yaml")).List()
|
||||||
t.NegotiatedSerializer.DecoderToVersion(s, t.decodeGV),
|
|
||||||
), t.kind)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodecFactory) DecoderToVersion(s runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder {
|
func (t *thirdPartyResourceDataCodecFactory) SupportedStreamingMediaTypes() []string {
|
||||||
return NewCodec(runtime.NewCodec(
|
supported := sets.NewString(t.NegotiatedSerializer.SupportedStreamingMediaTypes()...)
|
||||||
t.NegotiatedSerializer.EncoderForVersion(s, t.encodeGV),
|
return supported.Intersection(sets.NewString("application/json", "application/json;stream=watch")).List()
|
||||||
t.NegotiatedSerializer.DecoderToVersion(s, gv),
|
|
||||||
), t.kind)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type thirdPartyResourceDataCodec struct {
|
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
||||||
delegate runtime.Codec
|
return &thirdPartyResourceDataEncoder{delegate: t.NegotiatedSerializer.EncoderForVersion(s, gv), kind: t.kind}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *thirdPartyResourceDataCodecFactory) DecoderToVersion(s runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
||||||
|
return NewDecoder(t.NegotiatedSerializer.DecoderToVersion(s, gv), t.kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCodec(delegate runtime.Codec, kind string) runtime.Codec {
|
||||||
|
return runtime.NewCodec(NewEncoder(delegate, kind), NewDecoder(delegate, kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
type thirdPartyResourceDataDecoder struct {
|
||||||
|
delegate runtime.Decoder
|
||||||
kind string
|
kind string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ runtime.Codec = &thirdPartyResourceDataCodec{}
|
func NewDecoder(delegate runtime.Decoder, kind string) runtime.Decoder {
|
||||||
|
return &thirdPartyResourceDataDecoder{delegate: delegate, kind: kind}
|
||||||
func NewCodec(codec runtime.Codec, kind string) runtime.Codec {
|
|
||||||
return &thirdPartyResourceDataCodec{codec, kind}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ runtime.Decoder = &thirdPartyResourceDataDecoder{}
|
||||||
|
|
||||||
func parseObject(data []byte) (map[string]interface{}, error) {
|
func parseObject(data []byte) (map[string]interface{}, error) {
|
||||||
var obj interface{}
|
var obj interface{}
|
||||||
if err := json.Unmarshal(data, &obj); err != nil {
|
if err := json.Unmarshal(data, &obj); err != nil {
|
||||||
@@ -221,7 +230,7 @@ func parseObject(data []byte) (map[string]interface{}, error) {
|
|||||||
return mapObj, nil
|
return mapObj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodec) populate(data []byte) (runtime.Object, error) {
|
func (t *thirdPartyResourceDataDecoder) populate(data []byte) (runtime.Object, error) {
|
||||||
mapObj, err := parseObject(data)
|
mapObj, err := parseObject(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -229,7 +238,7 @@ func (t *thirdPartyResourceDataCodec) populate(data []byte) (runtime.Object, err
|
|||||||
return t.populateFromObject(mapObj, data)
|
return t.populateFromObject(mapObj, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodec) populateFromObject(mapObj map[string]interface{}, data []byte) (runtime.Object, error) {
|
func (t *thirdPartyResourceDataDecoder) populateFromObject(mapObj map[string]interface{}, data []byte) (runtime.Object, error) {
|
||||||
typeMeta := unversioned.TypeMeta{}
|
typeMeta := unversioned.TypeMeta{}
|
||||||
if err := json.Unmarshal(data, &typeMeta); err != nil {
|
if err := json.Unmarshal(data, &typeMeta); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -252,7 +261,7 @@ func (t *thirdPartyResourceDataCodec) populateFromObject(mapObj map[string]inter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodec) populateResource(objIn *extensions.ThirdPartyResourceData, mapObj map[string]interface{}, data []byte) error {
|
func (t *thirdPartyResourceDataDecoder) populateResource(objIn *extensions.ThirdPartyResourceData, mapObj map[string]interface{}, data []byte) error {
|
||||||
metadata, ok := mapObj["metadata"].(map[string]interface{})
|
metadata, ok := mapObj["metadata"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unexpected object for metadata: %#v", mapObj["metadata"])
|
return fmt.Errorf("unexpected object for metadata: %#v", mapObj["metadata"])
|
||||||
@@ -274,7 +283,7 @@ func (t *thirdPartyResourceDataCodec) populateResource(objIn *extensions.ThirdPa
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodec) Decode(data []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) {
|
func (t *thirdPartyResourceDataDecoder) Decode(data []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) {
|
||||||
if into == nil {
|
if into == nil {
|
||||||
obj, err := t.populate(data)
|
obj, err := t.populate(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -347,7 +356,7 @@ func (t *thirdPartyResourceDataCodec) Decode(data []byte, gvk *unversioned.Group
|
|||||||
return thirdParty, actual, nil
|
return thirdParty, actual, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodec) populateListResource(objIn *extensions.ThirdPartyResourceDataList, mapObj map[string]interface{}) error {
|
func (t *thirdPartyResourceDataDecoder) populateListResource(objIn *extensions.ThirdPartyResourceDataList, mapObj map[string]interface{}) error {
|
||||||
items, ok := mapObj["items"].([]interface{})
|
items, ok := mapObj["items"].([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unexpected object for items: %#v", mapObj["items"])
|
return fmt.Errorf("unexpected object for items: %#v", mapObj["items"])
|
||||||
@@ -374,6 +383,17 @@ const template = `{
|
|||||||
"items": [ %s ]
|
"items": [ %s ]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
type thirdPartyResourceDataEncoder struct {
|
||||||
|
delegate runtime.Encoder
|
||||||
|
kind string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEncoder(delegate runtime.Encoder, kind string) runtime.Encoder {
|
||||||
|
return &thirdPartyResourceDataEncoder{delegate: delegate, kind: kind}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ runtime.Encoder = &thirdPartyResourceDataEncoder{}
|
||||||
|
|
||||||
func encodeToJSON(obj *extensions.ThirdPartyResourceData, stream io.Writer) error {
|
func encodeToJSON(obj *extensions.ThirdPartyResourceData, stream io.Writer) error {
|
||||||
var objOut interface{}
|
var objOut interface{}
|
||||||
if err := json.Unmarshal(obj.Data, &objOut); err != nil {
|
if err := json.Unmarshal(obj.Data, &objOut); err != nil {
|
||||||
@@ -388,7 +408,7 @@ func encodeToJSON(obj *extensions.ThirdPartyResourceData, stream io.Writer) erro
|
|||||||
return encoder.Encode(objMap)
|
return encoder.Encode(objMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodec) EncodeToStream(obj runtime.Object, stream io.Writer, overrides ...unversioned.GroupVersion) (err error) {
|
func (t *thirdPartyResourceDataEncoder) EncodeToStream(obj runtime.Object, stream io.Writer, overrides ...unversioned.GroupVersion) (err error) {
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *extensions.ThirdPartyResourceData:
|
case *extensions.ThirdPartyResourceData:
|
||||||
return encodeToJSON(obj, stream)
|
return encodeToJSON(obj, stream)
|
||||||
|
@@ -87,13 +87,14 @@ func TestCodec(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
codec := &thirdPartyResourceDataCodec{kind: "Foo", delegate: testapi.Extensions.Codec()}
|
d := &thirdPartyResourceDataDecoder{kind: "Foo", delegate: testapi.Extensions.Codec()}
|
||||||
|
e := &thirdPartyResourceDataEncoder{kind: "Foo", delegate: testapi.Extensions.Codec()}
|
||||||
data, err := json.Marshal(test.obj)
|
data, err := json.Marshal(test.obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("[%s] unexpected error: %v", test.name, err)
|
t.Errorf("[%s] unexpected error: %v", test.name, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
obj, err := runtime.Decode(codec, data)
|
obj, err := runtime.Decode(d, data)
|
||||||
if err != nil && !test.expectErr {
|
if err != nil && !test.expectErr {
|
||||||
t.Errorf("[%s] unexpected error: %v", test.name, err)
|
t.Errorf("[%s] unexpected error: %v", test.name, err)
|
||||||
continue
|
continue
|
||||||
@@ -121,7 +122,7 @@ func TestCodec(t *testing.T) {
|
|||||||
t.Errorf("[%s]\nexpected\n%v\nsaw\n%v\n", test.name, test.obj, &output)
|
t.Errorf("[%s]\nexpected\n%v\nsaw\n%v\n", test.name, test.obj, &output)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err = runtime.Encode(codec, rsrcObj)
|
data, err = runtime.Encode(e, rsrcObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("[%s] unexpected error: %v", test.name, err)
|
t.Errorf("[%s] unexpected error: %v", test.name, err)
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -169,3 +170,27 @@ func (c *parameterCodec) EncodeParameters(obj Object, to unversioned.GroupVersio
|
|||||||
}
|
}
|
||||||
return queryparams.Convert(obj)
|
return queryparams.Convert(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type base64Serializer struct {
|
||||||
|
Serializer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBase64Serializer(s Serializer) Serializer {
|
||||||
|
return &base64Serializer{s}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s base64Serializer) EncodeToStream(obj Object, stream io.Writer, overrides ...unversioned.GroupVersion) error {
|
||||||
|
e := base64.NewEncoder(base64.StdEncoding, stream)
|
||||||
|
err := s.Serializer.EncodeToStream(obj, e, overrides...)
|
||||||
|
e.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s base64Serializer) Decode(data []byte, defaults *unversioned.GroupVersionKind, into Object) (Object, *unversioned.GroupVersionKind, error) {
|
||||||
|
out := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
|
||||||
|
n, err := base64.StdEncoding.Decode(out, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return s.Serializer.Decode(out[:n], defaults, into)
|
||||||
|
}
|
||||||
|
@@ -118,3 +118,33 @@ func DeepCopy_runtime_Scheme(in Scheme, out *Scheme, c *conversion.Cloner) error
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeepCopy_runtime_SerializerInfo(in SerializerInfo, out *SerializerInfo, c *conversion.Cloner) error {
|
||||||
|
if in.Serializer == nil {
|
||||||
|
out.Serializer = nil
|
||||||
|
} else if newVal, err := c.DeepCopy(in.Serializer); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
out.Serializer = newVal.(Serializer)
|
||||||
|
}
|
||||||
|
out.EncodesAsText = in.EncodesAsText
|
||||||
|
out.MediaType = in.MediaType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeepCopy_runtime_StreamSerializerInfo(in StreamSerializerInfo, out *StreamSerializerInfo, c *conversion.Cloner) error {
|
||||||
|
if err := DeepCopy_runtime_SerializerInfo(in.SerializerInfo, &out.SerializerInfo, c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if in.Framer == nil {
|
||||||
|
out.Framer = nil
|
||||||
|
} else if newVal, err := c.DeepCopy(in.Framer); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
out.Framer = newVal.(Framer)
|
||||||
|
}
|
||||||
|
if err := DeepCopy_runtime_SerializerInfo(in.Embedded, &out.Embedded, c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -86,32 +86,75 @@ type Framer interface {
|
|||||||
NewFrameWriter(w io.Writer) io.Writer
|
NewFrameWriter(w io.Writer) io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SerializerInfo contains information about a specific serialization format
|
||||||
|
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 string
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamSerializerInfo contains information about a specific stream serialization format
|
||||||
|
type StreamSerializerInfo struct {
|
||||||
|
SerializerInfo
|
||||||
|
// Framer is the factory for retrieving streams that separate objects on the wire
|
||||||
|
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
|
||||||
// for multiple supported media types.
|
// for multiple supported media types. This would commonly be accepted by a server component
|
||||||
|
// 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() []string
|
||||||
// SerializerForMediaType returns a serializer for the provided media type. Options is a set of
|
// 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.
|
// parameters applied to the media type that may modify the resulting output. ok will be false
|
||||||
SerializerForMediaType(mediaType string, options map[string]string) (Serializer, bool)
|
// 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.
|
// SupportedStreamingMediaTypes returns the media types of the supported streaming serializers.
|
||||||
// Streaming serializers control how multiple objects are written to a stream output.
|
// Streaming serializers control how multiple objects are written to a stream output.
|
||||||
SupportedStreamingMediaTypes() []string
|
SupportedStreamingMediaTypes() []string
|
||||||
// StreamingSerializerForMediaType returns a serializer for the provided media type that supports
|
// 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
|
// reading and writing multiple objects to a stream. It returns a framer and serializer, or an
|
||||||
// error if no such serializer can be created. Options is a set of parameters applied to the
|
// 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.
|
// media type that may modify the resulting output. ok will be false if no serializer matched
|
||||||
StreamingSerializerForMediaType(mediaType string, options map[string]string) (Serializer, Framer, string, bool)
|
// 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.
|
||||||
// TODO: take multiple group versions
|
// TODO: take multiple group versions
|
||||||
EncoderForVersion(serializer Serializer, gv unversioned.GroupVersion) Encoder
|
EncoderForVersion(serializer Encoder, gv unversioned.GroupVersion) Encoder
|
||||||
// DecoderForVersion returns a decoder that ensures objects being read by the provided
|
// DecoderForVersion returns a decoder that ensures objects being read by the provided
|
||||||
// serializer are in the provided group version by default.
|
// serializer are in the provided group version by default.
|
||||||
// TODO: take multiple group versions
|
// TODO: take multiple group versions
|
||||||
DecoderToVersion(serializer Serializer, gv unversioned.GroupVersion) Decoder
|
DecoderToVersion(serializer Decoder, gv unversioned.GroupVersion) Decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageSerializer is an interface used for obtaining encoders, decoders, and serializers
|
||||||
|
// 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.
|
||||||
|
type StorageSerializer interface {
|
||||||
|
// SerializerForMediaType returns a serializer for the provided media type. Options is a set of
|
||||||
|
// parameters applied to the media type that may modify the resulting output.
|
||||||
|
SerializerForMediaType(mediaType string, options map[string]string) (SerializerInfo, bool)
|
||||||
|
|
||||||
|
// UniversalDeserializer returns a Serializer that can read objects in multiple supported formats
|
||||||
|
// by introspecting the data at rest.
|
||||||
|
UniversalDeserializer() Decoder
|
||||||
|
|
||||||
|
// EncoderForVersion returns an encoder that ensures objects being written to the provided
|
||||||
|
// serializer are in the provided group version.
|
||||||
|
// TODO: take multiple group versions
|
||||||
|
EncoderForVersion(serializer Encoder, gv unversioned.GroupVersion) Encoder
|
||||||
|
// DecoderForVersion returns a decoder that ensures objects being read by the provided
|
||||||
|
// serializer are in the provided group version by default.
|
||||||
|
// TODO: take multiple group versions
|
||||||
|
DecoderToVersion(serializer Decoder, gv unversioned.GroupVersion) Decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
@@ -31,6 +31,8 @@ type serializerType struct {
|
|||||||
AcceptContentTypes []string
|
AcceptContentTypes []string
|
||||||
ContentType string
|
ContentType string
|
||||||
FileExtensions []string
|
FileExtensions []string
|
||||||
|
// EncodesAsText should be true if this content type can be represented safely in UTF-8
|
||||||
|
EncodesAsText bool
|
||||||
|
|
||||||
Serializer runtime.Serializer
|
Serializer runtime.Serializer
|
||||||
PrettySerializer runtime.Serializer
|
PrettySerializer runtime.Serializer
|
||||||
@@ -65,6 +67,7 @@ func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory) []seri
|
|||||||
AcceptContentTypes: []string{"application/json"},
|
AcceptContentTypes: []string{"application/json"},
|
||||||
ContentType: "application/json",
|
ContentType: "application/json",
|
||||||
FileExtensions: []string{"json"},
|
FileExtensions: []string{"json"},
|
||||||
|
EncodesAsText: true,
|
||||||
Serializer: jsonSerializer,
|
Serializer: jsonSerializer,
|
||||||
PrettySerializer: jsonPrettySerializer,
|
PrettySerializer: jsonPrettySerializer,
|
||||||
|
|
||||||
@@ -77,6 +80,7 @@ func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory) []seri
|
|||||||
AcceptContentTypes: []string{"application/yaml"},
|
AcceptContentTypes: []string{"application/yaml"},
|
||||||
ContentType: "application/yaml",
|
ContentType: "application/yaml",
|
||||||
FileExtensions: []string{"yaml"},
|
FileExtensions: []string{"yaml"},
|
||||||
|
EncodesAsText: true,
|
||||||
Serializer: yamlSerializer,
|
Serializer: yamlSerializer,
|
||||||
|
|
||||||
// TODO: requires runtime.RawExtension to properly distinguish when the nested content is
|
// TODO: requires runtime.RawExtension to properly distinguish when the nested content is
|
||||||
@@ -206,62 +210,91 @@ func (f CodecFactory) UniversalDeserializer() runtime.Decoder {
|
|||||||
//
|
//
|
||||||
// TODO: the decoder will eventually be removed in favor of dealing with objects in their versioned form
|
// TODO: the decoder will eventually be removed in favor of dealing with objects in their versioned form
|
||||||
func (f CodecFactory) UniversalDecoder(versions ...unversioned.GroupVersion) runtime.Decoder {
|
func (f CodecFactory) UniversalDecoder(versions ...unversioned.GroupVersion) runtime.Decoder {
|
||||||
return f.CodecForVersions(runtime.NoopEncoder{Decoder: f.universal}, nil, versions)
|
return f.CodecForVersions(nil, f.universal, nil, versions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodecFor creates a codec with the provided serializer. If an object is decoded and its group is not in the list,
|
// CodecFor creates a codec with the provided serializer. If an object is decoded and its group is not in the list,
|
||||||
// it will default to runtime.APIVersionInternal. If encode is not specified for an object's group, the object is not
|
// it will default to runtime.APIVersionInternal. If encode is not specified for an object's group, the object is not
|
||||||
// converted. If encode or decode are nil, no conversion is performed.
|
// converted. If encode or decode are nil, no conversion is performed.
|
||||||
func (f CodecFactory) CodecForVersions(serializer runtime.Serializer, encode []unversioned.GroupVersion, decode []unversioned.GroupVersion) runtime.Codec {
|
func (f CodecFactory) CodecForVersions(encoder runtime.Encoder, decoder runtime.Decoder, encode []unversioned.GroupVersion, decode []unversioned.GroupVersion) runtime.Codec {
|
||||||
return versioning.NewCodecForScheme(f.scheme, serializer, serializer, encode, decode)
|
return versioning.NewCodecForScheme(f.scheme, encoder, decoder, encode, decode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecoderToVersion returns a decoder that targets the provided group version.
|
// DecoderToVersion returns a decoder that targets the provided group version.
|
||||||
func (f CodecFactory) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder {
|
func (f CodecFactory) DecoderToVersion(decoder runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
||||||
return f.CodecForVersions(serializer, nil, []unversioned.GroupVersion{gv})
|
return f.CodecForVersions(nil, decoder, nil, []unversioned.GroupVersion{gv})
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncoderForVersion returns an encoder that targets the provided group version.
|
// EncoderForVersion returns an encoder that targets the provided group version.
|
||||||
func (f CodecFactory) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
|
func (f CodecFactory) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
||||||
return f.CodecForVersions(serializer, []unversioned.GroupVersion{gv}, nil)
|
return f.CodecForVersions(encoder, nil, []unversioned.GroupVersion{gv}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such
|
// SerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such
|
||||||
// serializer exists
|
// serializer exists
|
||||||
func (f CodecFactory) SerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, bool) {
|
func (f CodecFactory) SerializerForMediaType(mediaType string, params map[string]string) (runtime.SerializerInfo, bool) {
|
||||||
for _, s := range f.serializers {
|
for _, s := range f.serializers {
|
||||||
for _, accepted := range s.AcceptContentTypes {
|
for _, accepted := range s.AcceptContentTypes {
|
||||||
if accepted == mediaType {
|
if accepted == mediaType {
|
||||||
if s.Specialize != nil && len(options) > 0 {
|
// specialization abstracts variants to the content type
|
||||||
serializer, ok := s.Specialize(options)
|
if s.Specialize != nil && len(params) > 0 {
|
||||||
return serializer, ok
|
serializer, ok := s.Specialize(params)
|
||||||
|
// TODO: return formatted mediaType+params
|
||||||
|
return runtime.SerializerInfo{Serializer: serializer, MediaType: s.ContentType, EncodesAsText: s.EncodesAsText}, ok
|
||||||
}
|
}
|
||||||
if v, ok := options["pretty"]; ok && v == "1" && s.PrettySerializer != nil {
|
|
||||||
return s.PrettySerializer, true
|
// 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 s.Serializer, true
|
|
||||||
|
// return the base variant
|
||||||
|
return runtime.SerializerInfo{Serializer: s.Serializer, MediaType: s.ContentType, EncodesAsText: s.EncodesAsText}, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, false
|
return runtime.SerializerInfo{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// StreamingSerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such
|
// StreamingSerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such
|
||||||
// serializer exists
|
// serializer exists
|
||||||
func (f CodecFactory) StreamingSerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, runtime.Framer, string, bool) {
|
func (f CodecFactory) StreamingSerializerForMediaType(mediaType string, params map[string]string) (runtime.StreamSerializerInfo, bool) {
|
||||||
for _, s := range f.serializers {
|
for _, s := range f.serializers {
|
||||||
for _, accepted := range s.AcceptStreamContentTypes {
|
for _, accepted := range s.AcceptStreamContentTypes {
|
||||||
if accepted == mediaType {
|
if accepted == mediaType {
|
||||||
if s.StreamSpecialize != nil && len(options) > 0 {
|
// TODO: accept params
|
||||||
serializer, ok := s.StreamSpecialize(options)
|
nested, ok := f.SerializerForMediaType(s.ContentType, nil)
|
||||||
// TODO: have StreamSpecialize return exact content type
|
if !ok {
|
||||||
return serializer, s.Framer, s.StreamContentType, ok
|
panic("no serializer defined for internal content type")
|
||||||
}
|
}
|
||||||
return s.StreamSerializer, s.Framer, s.StreamContentType, true
|
|
||||||
|
if s.StreamSpecialize != nil && len(params) > 0 {
|
||||||
|
serializer, ok := s.StreamSpecialize(params)
|
||||||
|
// TODO: return formatted mediaType+params
|
||||||
|
return runtime.StreamSerializerInfo{
|
||||||
|
SerializerInfo: runtime.SerializerInfo{
|
||||||
|
Serializer: serializer,
|
||||||
|
MediaType: s.StreamContentType,
|
||||||
|
EncodesAsText: s.EncodesAsText,
|
||||||
|
},
|
||||||
|
Framer: s.Framer,
|
||||||
|
Embedded: nested,
|
||||||
|
}, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtime.StreamSerializerInfo{
|
||||||
|
SerializerInfo: runtime.SerializerInfo{
|
||||||
|
Serializer: s.StreamSerializer,
|
||||||
|
MediaType: s.StreamContentType,
|
||||||
|
EncodesAsText: s.EncodesAsText,
|
||||||
|
},
|
||||||
|
Framer: s.Framer,
|
||||||
|
Embedded: nested,
|
||||||
|
}, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil, "", false
|
return runtime.StreamSerializerInfo{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// SerializerForFileExtension returns a serializer for the provided extension, or false if no serializer matches.
|
// SerializerForFileExtension returns a serializer for the provided extension, or false if no serializer matches.
|
||||||
|
@@ -267,7 +267,7 @@ func TestVersionedEncoding(t *testing.T) {
|
|||||||
encoder, _ := cf.SerializerForFileExtension("json")
|
encoder, _ := cf.SerializerForFileExtension("json")
|
||||||
|
|
||||||
// codec that is unversioned uses the target version
|
// codec that is unversioned uses the target version
|
||||||
unversionedCodec := cf.CodecForVersions(encoder, nil, nil)
|
unversionedCodec := cf.CodecForVersions(encoder, nil, nil, nil)
|
||||||
_, err = runtime.Encode(unversionedCodec, &TestType1{}, unversioned.GroupVersion{Version: "v3"})
|
_, err = runtime.Encode(unversionedCodec, &TestType1{}, unversioned.GroupVersion{Version: "v3"})
|
||||||
if err == nil || !runtime.IsNotRegisteredError(err) {
|
if err == nil || !runtime.IsNotRegisteredError(err) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@@ -195,14 +195,6 @@ func (s *Serializer) RecognizesData(peek io.Reader) (ok, unknown bool, err error
|
|||||||
return ok, false, nil
|
return ok, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodesAsText returns true because both JSON and YAML are considered textual representations
|
|
||||||
// of data. This is used to determine whether the serialized object should be transmitted over
|
|
||||||
// a WebSocket Text or Binary frame. This must remain true for legacy compatibility with v1.1
|
|
||||||
// watch over websocket implementations.
|
|
||||||
func (s *Serializer) EncodesAsText() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Framer is the default JSON framing behavior, with newlines delimiting individual objects.
|
// Framer is the default JSON framing behavior, with newlines delimiting individual objects.
|
||||||
var Framer = jsonFramer{}
|
var Framer = jsonFramer{}
|
||||||
|
|
||||||
|
@@ -24,35 +24,34 @@ import (
|
|||||||
// TODO: We should figure out what happens when someone asks
|
// TODO: We should figure out what happens when someone asks
|
||||||
// encoder for version and it conflicts with the raw serializer.
|
// encoder for version and it conflicts with the raw serializer.
|
||||||
type negotiatedSerializerWrapper struct {
|
type negotiatedSerializerWrapper struct {
|
||||||
serializer runtime.Serializer
|
info runtime.SerializerInfo
|
||||||
streamingSerializer runtime.Serializer
|
streamInfo runtime.StreamSerializerInfo
|
||||||
framer runtime.Framer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NegotiatedSerializerWrapper(serializer, streamingSerializer runtime.Serializer, framer runtime.Framer) runtime.NegotiatedSerializer {
|
func NegotiatedSerializerWrapper(info runtime.SerializerInfo, streamInfo runtime.StreamSerializerInfo) runtime.NegotiatedSerializer {
|
||||||
return &negotiatedSerializerWrapper{serializer, streamingSerializer, framer}
|
return &negotiatedSerializerWrapper{info, streamInfo}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) SupportedMediaTypes() []string {
|
func (n *negotiatedSerializerWrapper) SupportedMediaTypes() []string {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) SerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, bool) {
|
func (n *negotiatedSerializerWrapper) SerializerForMediaType(mediaType string, options map[string]string) (runtime.SerializerInfo, bool) {
|
||||||
return n.serializer, true
|
return n.info, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) SupportedStreamingMediaTypes() []string {
|
func (n *negotiatedSerializerWrapper) SupportedStreamingMediaTypes() []string {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) StreamingSerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, runtime.Framer, string, bool) {
|
func (n *negotiatedSerializerWrapper) StreamingSerializerForMediaType(mediaType string, options map[string]string) (runtime.StreamSerializerInfo, bool) {
|
||||||
return n.streamingSerializer, n.framer, "", true
|
return n.streamInfo, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
|
func (n *negotiatedSerializerWrapper) EncoderForVersion(e runtime.Encoder, _ unversioned.GroupVersion) runtime.Encoder {
|
||||||
return n.serializer
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder {
|
func (n *negotiatedSerializerWrapper) DecoderToVersion(d runtime.Decoder, _gv unversioned.GroupVersion) runtime.Decoder {
|
||||||
return n.serializer
|
return d
|
||||||
}
|
}
|
||||||
|
@@ -297,6 +297,18 @@ func TestDecodeObjects(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unk2 := &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: "v1"},
|
||||||
|
}
|
||||||
|
wire2 := make([]byte, len(wire1)*2)
|
||||||
|
n, err := unk2.NestedMarshalTo(wire2, obj1, uint64(obj1.Size()))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if n != len(wire1) || !bytes.Equal(wire1, wire2[:n]) {
|
||||||
|
t.Fatalf("unexpected wire:\n%s", hex.Dump(wire2[:n]))
|
||||||
|
}
|
||||||
|
|
||||||
wire1 = append([]byte{0x6b, 0x38, 0x73, 0x00}, wire1...)
|
wire1 = append([]byte{0x6b, 0x38, 0x73, 0x00}, wire1...)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
@@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package recognizer
|
package recognizer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@@ -27,6 +27,7 @@ import (
|
|||||||
// EnableCrossGroupDecoding modifies the given decoder in place, if it is a codec
|
// EnableCrossGroupDecoding modifies the given decoder in place, if it is a codec
|
||||||
// from this package. It allows objects from one group to be auto-decoded into
|
// from this package. It allows objects from one group to be auto-decoded into
|
||||||
// another group. 'destGroup' must already exist in the codec.
|
// another group. 'destGroup' must already exist in the codec.
|
||||||
|
// TODO: this is an encapsulation violation and should be refactored
|
||||||
func EnableCrossGroupDecoding(d runtime.Decoder, sourceGroup, destGroup string) error {
|
func EnableCrossGroupDecoding(d runtime.Decoder, sourceGroup, destGroup string) error {
|
||||||
internal, ok := d.(*codec)
|
internal, ok := d.(*codec)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -45,6 +46,7 @@ func EnableCrossGroupDecoding(d runtime.Decoder, sourceGroup, destGroup string)
|
|||||||
// EnableCrossGroupEncoding modifies the given encoder in place, if it is a codec
|
// EnableCrossGroupEncoding modifies the given encoder in place, if it is a codec
|
||||||
// from this package. It allows objects from one group to be auto-decoded into
|
// from this package. It allows objects from one group to be auto-decoded into
|
||||||
// another group. 'destGroup' must already exist in the codec.
|
// another group. 'destGroup' must already exist in the codec.
|
||||||
|
// TODO: this is an encapsulation violation and should be refactored
|
||||||
func EnableCrossGroupEncoding(e runtime.Encoder, sourceGroup, destGroup string) error {
|
func EnableCrossGroupEncoding(e runtime.Encoder, sourceGroup, destGroup string) error {
|
||||||
internal, ok := e.(*codec)
|
internal, ok := e.(*codec)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@@ -86,9 +86,13 @@ func New(kubeConfigFile string) (*WebhookAuthorizer, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer := json.NewSerializer(json.DefaultMetaFactory, api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), false)
|
serializer := json.NewSerializer(json.DefaultMetaFactory, api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), false)
|
||||||
codec := versioning.NewCodecForScheme(api.Scheme, serializer, serializer, encodeVersions, decodeVersions)
|
codec := versioning.NewCodecForScheme(api.Scheme, serializer, serializer, encodeVersions, decodeVersions)
|
||||||
clientConfig.ContentConfig.NegotiatedSerializer = runtimeserializer.NegotiatedSerializerWrapper(codec, codec, json.Framer)
|
clientConfig.ContentConfig.NegotiatedSerializer = runtimeserializer.NegotiatedSerializerWrapper(
|
||||||
|
runtime.SerializerInfo{Serializer: codec},
|
||||||
|
runtime.StreamSerializerInfo{},
|
||||||
|
)
|
||||||
|
|
||||||
restClient, err := restclient.UnversionedRESTClientFor(clientConfig)
|
restClient, err := restclient.UnversionedRESTClientFor(clientConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -155,24 +155,29 @@ func NewMasterConfig() *master.Config {
|
|||||||
Prefix: etcdtest.PathPrefix(),
|
Prefix: etcdtest.PathPrefix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
negotiatedSerializer := NewSingleContentTypeSerializer(api.Scheme, testapi.Default.Codec(), "application/json")
|
negotiatedSerializer := NewSingleContentTypeSerializer(api.Scheme, testapi.Default.Codec(), runtime.ContentTypeJSON)
|
||||||
|
|
||||||
storageFactory := genericapiserver.NewDefaultStorageFactory(config, negotiatedSerializer, genericapiserver.NewDefaultResourceEncodingConfig(), master.DefaultAPIResourceConfigSource())
|
storageFactory := genericapiserver.NewDefaultStorageFactory(config, runtime.ContentTypeJSON, negotiatedSerializer, 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(), "application/json"))
|
"",
|
||||||
|
NewSingleContentTypeSerializer(api.Scheme, testapi.Default.Codec(), runtime.ContentTypeJSON))
|
||||||
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(), "application/json"))
|
"",
|
||||||
|
NewSingleContentTypeSerializer(api.Scheme, testapi.Autoscaling.Codec(), runtime.ContentTypeJSON))
|
||||||
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(), "application/json"))
|
"",
|
||||||
|
NewSingleContentTypeSerializer(api.Scheme, testapi.Batch.Codec(), runtime.ContentTypeJSON))
|
||||||
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(), "application/json"))
|
"",
|
||||||
|
NewSingleContentTypeSerializer(api.Scheme, testapi.Apps.Codec(), runtime.ContentTypeJSON))
|
||||||
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(), "application/json"))
|
"",
|
||||||
|
NewSingleContentTypeSerializer(api.Scheme, testapi.Extensions.Codec(), runtime.ContentTypeJSON))
|
||||||
|
|
||||||
return &master.Config{
|
return &master.Config{
|
||||||
Config: &genericapiserver.Config{
|
Config: &genericapiserver.Config{
|
||||||
|
@@ -23,7 +23,7 @@ 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.NegotiatedSerializer {
|
func NewSingleContentTypeSerializer(scheme *runtime.Scheme, serializer runtime.Serializer, contentType string) runtime.StorageSerializer {
|
||||||
return &wrappedSerializer{
|
return &wrappedSerializer{
|
||||||
scheme: scheme,
|
scheme: scheme,
|
||||||
serializer: serializer,
|
serializer: serializer,
|
||||||
@@ -37,29 +37,31 @@ type wrappedSerializer struct {
|
|||||||
contentType string
|
contentType string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ runtime.NegotiatedSerializer = &wrappedSerializer{}
|
var _ runtime.StorageSerializer = &wrappedSerializer{}
|
||||||
|
|
||||||
func (s *wrappedSerializer) SupportedMediaTypes() []string {
|
func (s *wrappedSerializer) SupportedMediaTypes() []string {
|
||||||
return []string{s.contentType}
|
return []string{s.contentType}
|
||||||
}
|
}
|
||||||
func (s *wrappedSerializer) SerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, bool) {
|
func (s *wrappedSerializer) SerializerForMediaType(mediaType string, options map[string]string) (runtime.SerializerInfo, bool) {
|
||||||
if mediaType != s.contentType {
|
if mediaType != s.contentType {
|
||||||
return nil, false
|
return runtime.SerializerInfo{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.serializer, true
|
return runtime.SerializerInfo{
|
||||||
}
|
Serializer: s.serializer,
|
||||||
func (s *wrappedSerializer) SupportedStreamingMediaTypes() []string {
|
MediaType: mediaType,
|
||||||
return nil
|
EncodesAsText: true, // TODO: this should be parameterized
|
||||||
}
|
}, true
|
||||||
func (s *wrappedSerializer) StreamingSerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, runtime.Framer, string, bool) {
|
|
||||||
return nil, nil, "", false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wrappedSerializer) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
|
func (s *wrappedSerializer) UniversalDeserializer() runtime.Decoder {
|
||||||
return versioning.NewCodec(s.serializer, s.serializer, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), []unversioned.GroupVersion{gv}, nil)
|
return s.serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wrappedSerializer) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder {
|
func (s *wrappedSerializer) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
||||||
return versioning.NewCodec(s.serializer, s.serializer, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), nil, []unversioned.GroupVersion{gv})
|
return versioning.NewCodec(encoder, nil, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), []unversioned.GroupVersion{gv}, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *wrappedSerializer) DecoderToVersion(decoder runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
||||||
|
return versioning.NewCodec(nil, decoder, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), nil, []unversioned.GroupVersion{gv})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user