blob: 159b301206a7bf807e4f972f48803bf768258d6e [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
2Copyright 2019 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package runtime
18
19import (
20 "fmt"
21
22 "k8s.io/apimachinery/pkg/runtime/schema"
23)
24
25// NegotiateError is returned when a ClientNegotiator is unable to locate
26// a serializer for the requested operation.
27type NegotiateError struct {
28 ContentType string
29 Stream bool
30}
31
32func (e NegotiateError) Error() string {
33 if e.Stream {
34 return fmt.Sprintf("no stream serializers registered for %s", e.ContentType)
35 }
36 return fmt.Sprintf("no serializers registered for %s", e.ContentType)
37}
38
39type clientNegotiator struct {
40 serializer NegotiatedSerializer
41 encode, decode GroupVersioner
42}
43
44func (n *clientNegotiator) Encoder(contentType string, params map[string]string) (Encoder, error) {
45 // TODO: `pretty=1` is handled in NegotiateOutputMediaType, consider moving it to this method
46 // if client negotiators truly need to use it
47 mediaTypes := n.serializer.SupportedMediaTypes()
48 info, ok := SerializerInfoForMediaType(mediaTypes, contentType)
49 if !ok {
50 if len(contentType) != 0 || len(mediaTypes) == 0 {
51 return nil, NegotiateError{ContentType: contentType}
52 }
53 info = mediaTypes[0]
54 }
55 return n.serializer.EncoderForVersion(info.Serializer, n.encode), nil
56}
57
58func (n *clientNegotiator) Decoder(contentType string, params map[string]string) (Decoder, error) {
59 mediaTypes := n.serializer.SupportedMediaTypes()
60 info, ok := SerializerInfoForMediaType(mediaTypes, contentType)
61 if !ok {
62 if len(contentType) != 0 || len(mediaTypes) == 0 {
63 return nil, NegotiateError{ContentType: contentType}
64 }
65 info = mediaTypes[0]
66 }
67 return n.serializer.DecoderToVersion(info.Serializer, n.decode), nil
68}
69
70func (n *clientNegotiator) StreamDecoder(contentType string, params map[string]string) (Decoder, Serializer, Framer, error) {
71 mediaTypes := n.serializer.SupportedMediaTypes()
72 info, ok := SerializerInfoForMediaType(mediaTypes, contentType)
73 if !ok {
74 if len(contentType) != 0 || len(mediaTypes) == 0 {
75 return nil, nil, nil, NegotiateError{ContentType: contentType, Stream: true}
76 }
77 info = mediaTypes[0]
78 }
79 if info.StreamSerializer == nil {
80 return nil, nil, nil, NegotiateError{ContentType: info.MediaType, Stream: true}
81 }
82 return n.serializer.DecoderToVersion(info.Serializer, n.decode), info.StreamSerializer.Serializer, info.StreamSerializer.Framer, nil
83}
84
85// NewClientNegotiator will attempt to retrieve the appropriate encoder, decoder, or
86// stream decoder for a given content type. Does not perform any conversion, but will
87// encode the object to the desired group, version, and kind. Use when creating a client.
88func NewClientNegotiator(serializer NegotiatedSerializer, gv schema.GroupVersion) ClientNegotiator {
89 return &clientNegotiator{
90 serializer: serializer,
91 encode: gv,
92 }
93}
94
95// NewInternalClientNegotiator applies the default client rules for connecting to a Kubernetes apiserver
96// where objects are converted to gv prior to sending and decoded to their internal representation prior
97// to retrieval.
98//
99// DEPRECATED: Internal clients are deprecated and will be removed in a future Kubernetes release.
100func NewInternalClientNegotiator(serializer NegotiatedSerializer, gv schema.GroupVersion) ClientNegotiator {
101 decode := schema.GroupVersions{
102 {
103 Group: gv.Group,
104 Version: APIVersionInternal,
105 },
106 // always include the legacy group as a decoding target to handle non-error `Status` return types
107 {
108 Group: "",
109 Version: APIVersionInternal,
110 },
111 }
112 return &clientNegotiator{
113 encode: gv,
114 decode: decode,
115 serializer: serializer,
116 }
117}
118
119// NewSimpleClientNegotiator will negotiate for a single serializer. This should only be used
120// for testing or when the caller is taking responsibility for setting the GVK on encoded objects.
121func NewSimpleClientNegotiator(info SerializerInfo, gv schema.GroupVersion) ClientNegotiator {
122 return &clientNegotiator{
123 serializer: &simpleNegotiatedSerializer{info: info},
124 encode: gv,
125 }
126}
127
128type simpleNegotiatedSerializer struct {
129 info SerializerInfo
130}
131
132func NewSimpleNegotiatedSerializer(info SerializerInfo) NegotiatedSerializer {
133 return &simpleNegotiatedSerializer{info: info}
134}
135
136func (n *simpleNegotiatedSerializer) SupportedMediaTypes() []SerializerInfo {
137 return []SerializerInfo{n.info}
138}
139
140func (n *simpleNegotiatedSerializer) EncoderForVersion(e Encoder, _ GroupVersioner) Encoder {
141 return e
142}
143
144func (n *simpleNegotiatedSerializer) DecoderToVersion(d Decoder, _gv GroupVersioner) Decoder {
145 return d
146}