/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/negotiate_test.go

https://gitlab.com/unofficial-mirrors/kubernetes · Go · 264 lines · 230 code · 13 blank · 21 comment · 28 complexity · e849385a72a84e82ebfff878321b3e48 MD5 · raw file

  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package negotiation
  14. import (
  15. "net/http"
  16. "net/url"
  17. "testing"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. "k8s.io/apimachinery/pkg/runtime"
  20. )
  21. // statusError is an object that can be converted into an metav1.Status
  22. type statusError interface {
  23. Status() metav1.Status
  24. }
  25. type fakeNegotiater struct {
  26. serializer, streamSerializer runtime.Serializer
  27. framer runtime.Framer
  28. types, streamTypes []string
  29. }
  30. func (n *fakeNegotiater) SupportedMediaTypes() []runtime.SerializerInfo {
  31. var out []runtime.SerializerInfo
  32. for _, s := range n.types {
  33. info := runtime.SerializerInfo{Serializer: n.serializer, MediaType: s, EncodesAsText: true}
  34. for _, t := range n.streamTypes {
  35. if t == s {
  36. info.StreamSerializer = &runtime.StreamSerializerInfo{
  37. EncodesAsText: true,
  38. Framer: n.framer,
  39. Serializer: n.streamSerializer,
  40. }
  41. }
  42. }
  43. out = append(out, info)
  44. }
  45. return out
  46. }
  47. func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
  48. return n.serializer
  49. }
  50. func (n *fakeNegotiater) DecoderToVersion(serializer runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
  51. return n.serializer
  52. }
  53. var fakeCodec = runtime.NewCodec(runtime.NoopEncoder{}, runtime.NoopDecoder{})
  54. func TestNegotiate(t *testing.T) {
  55. testCases := []struct {
  56. accept string
  57. req *http.Request
  58. ns *fakeNegotiater
  59. serializer runtime.Serializer
  60. contentType string
  61. params map[string]string
  62. errFn func(error) bool
  63. }{
  64. // pick a default
  65. {
  66. req: &http.Request{},
  67. contentType: "application/json",
  68. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  69. serializer: fakeCodec,
  70. },
  71. {
  72. accept: "",
  73. contentType: "application/json",
  74. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  75. serializer: fakeCodec,
  76. },
  77. {
  78. accept: "*/*",
  79. contentType: "application/json",
  80. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  81. serializer: fakeCodec,
  82. },
  83. {
  84. accept: "application/*",
  85. contentType: "application/json",
  86. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  87. serializer: fakeCodec,
  88. },
  89. {
  90. accept: "application/json",
  91. contentType: "application/json",
  92. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  93. serializer: fakeCodec,
  94. },
  95. {
  96. accept: "application/json",
  97. contentType: "application/json",
  98. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json", "application/protobuf"}},
  99. serializer: fakeCodec,
  100. },
  101. {
  102. accept: "application/protobuf",
  103. contentType: "application/protobuf",
  104. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json", "application/protobuf"}},
  105. serializer: fakeCodec,
  106. },
  107. {
  108. accept: "application/json; pretty=1",
  109. contentType: "application/json",
  110. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  111. serializer: fakeCodec,
  112. params: map[string]string{"pretty": "1"},
  113. },
  114. {
  115. accept: "unrecognized/stuff,application/json; pretty=1",
  116. contentType: "application/json",
  117. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  118. serializer: fakeCodec,
  119. params: map[string]string{"pretty": "1"},
  120. },
  121. // query param triggers pretty
  122. {
  123. req: &http.Request{
  124. Header: http.Header{"Accept": []string{"application/json"}},
  125. URL: &url.URL{RawQuery: "pretty=1"},
  126. },
  127. contentType: "application/json",
  128. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  129. serializer: fakeCodec,
  130. params: map[string]string{"pretty": "1"},
  131. },
  132. // certain user agents trigger pretty
  133. {
  134. req: &http.Request{
  135. Header: http.Header{
  136. "Accept": []string{"application/json"},
  137. "User-Agent": []string{"curl"},
  138. },
  139. },
  140. contentType: "application/json",
  141. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  142. serializer: fakeCodec,
  143. params: map[string]string{"pretty": "1"},
  144. },
  145. {
  146. req: &http.Request{
  147. Header: http.Header{
  148. "Accept": []string{"application/json"},
  149. "User-Agent": []string{"Wget"},
  150. },
  151. },
  152. contentType: "application/json",
  153. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  154. serializer: fakeCodec,
  155. params: map[string]string{"pretty": "1"},
  156. },
  157. {
  158. req: &http.Request{
  159. Header: http.Header{
  160. "Accept": []string{"application/json"},
  161. "User-Agent": []string{"Mozilla/5.0"},
  162. },
  163. },
  164. contentType: "application/json",
  165. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  166. serializer: fakeCodec,
  167. params: map[string]string{"pretty": "1"},
  168. },
  169. {
  170. req: &http.Request{
  171. Header: http.Header{
  172. "Accept": []string{"application/json;as=BOGUS;v=v1beta1;g=meta.k8s.io, application/json"},
  173. },
  174. },
  175. contentType: "application/json",
  176. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  177. serializer: fakeCodec,
  178. },
  179. {
  180. req: &http.Request{
  181. Header: http.Header{
  182. "Accept": []string{"application/BOGUS, application/json"},
  183. },
  184. },
  185. contentType: "application/json",
  186. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}},
  187. serializer: fakeCodec,
  188. },
  189. // "application" is not a valid media type, so the server will reject the response during
  190. // negotiation (the server, in error, has specified an invalid media type)
  191. {
  192. accept: "application",
  193. ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application"}},
  194. errFn: func(err error) bool {
  195. return err.Error() == "only the following media types are accepted: application"
  196. },
  197. },
  198. {
  199. ns: &fakeNegotiater{},
  200. errFn: func(err error) bool {
  201. return err.Error() == "only the following media types are accepted: "
  202. },
  203. },
  204. {
  205. accept: "*/*",
  206. ns: &fakeNegotiater{},
  207. errFn: func(err error) bool {
  208. return err.Error() == "only the following media types are accepted: "
  209. },
  210. },
  211. }
  212. for i, test := range testCases {
  213. req := test.req
  214. if req == nil {
  215. req = &http.Request{Header: http.Header{}}
  216. req.Header.Set("Accept", test.accept)
  217. }
  218. s, err := NegotiateOutputSerializer(req, test.ns)
  219. switch {
  220. case err == nil && test.errFn != nil:
  221. t.Errorf("%d: failed: expected error", i)
  222. continue
  223. case err != nil && test.errFn == nil:
  224. t.Errorf("%d: failed: %v", i, err)
  225. continue
  226. case err != nil:
  227. if !test.errFn(err) {
  228. t.Errorf("%d: failed: %v", i, err)
  229. }
  230. status, ok := err.(statusError)
  231. if !ok {
  232. t.Errorf("%d: failed, error should be statusError: %v", i, err)
  233. continue
  234. }
  235. if status.Status().Status != metav1.StatusFailure || status.Status().Code != http.StatusNotAcceptable {
  236. t.Errorf("%d: failed: %v", i, err)
  237. continue
  238. }
  239. continue
  240. }
  241. if test.contentType != s.MediaType {
  242. t.Errorf("%d: unexpected %s %s", i, test.contentType, s.MediaType)
  243. }
  244. if s.Serializer != test.serializer {
  245. t.Errorf("%d: unexpected %s %s", i, test.serializer, s.Serializer)
  246. }
  247. }
  248. }