client_ca_hook_test.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /*
  2. Copyright 2017 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 master
  14. import (
  15. "reflect"
  16. "testing"
  17. corev1 "k8s.io/api/core/v1"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. "k8s.io/apimachinery/pkg/runtime"
  20. "k8s.io/apimachinery/pkg/util/diff"
  21. "k8s.io/client-go/kubernetes/fake"
  22. clienttesting "k8s.io/client-go/testing"
  23. )
  24. func TestWriteClientCAs(t *testing.T) {
  25. tests := []struct {
  26. name string
  27. hook ClientCARegistrationHook
  28. preexistingObjs []runtime.Object
  29. expectedConfigMaps map[string]*corev1.ConfigMap
  30. expectUpdate bool
  31. }{
  32. {
  33. name: "basic",
  34. hook: ClientCARegistrationHook{
  35. ClientCA: []byte("foo"),
  36. RequestHeaderUsernameHeaders: []string{"alfa", "bravo", "charlie"},
  37. RequestHeaderGroupHeaders: []string{"delta"},
  38. RequestHeaderExtraHeaderPrefixes: []string{"echo", "foxtrot"},
  39. RequestHeaderCA: []byte("bar"),
  40. RequestHeaderAllowedNames: []string{"first", "second"},
  41. },
  42. expectedConfigMaps: map[string]*corev1.ConfigMap{
  43. "extension-apiserver-authentication": {
  44. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  45. Data: map[string]string{
  46. "client-ca-file": "foo",
  47. "requestheader-username-headers": `["alfa","bravo","charlie"]`,
  48. "requestheader-group-headers": `["delta"]`,
  49. "requestheader-extra-headers-prefix": `["echo","foxtrot"]`,
  50. "requestheader-client-ca-file": "bar",
  51. "requestheader-allowed-names": `["first","second"]`,
  52. },
  53. },
  54. },
  55. },
  56. {
  57. name: "skip extension-apiserver-authentication",
  58. hook: ClientCARegistrationHook{
  59. RequestHeaderCA: []byte("bar"),
  60. RequestHeaderAllowedNames: []string{"first", "second"},
  61. },
  62. expectedConfigMaps: map[string]*corev1.ConfigMap{
  63. "extension-apiserver-authentication": {
  64. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  65. Data: map[string]string{
  66. "requestheader-username-headers": `null`,
  67. "requestheader-group-headers": `null`,
  68. "requestheader-extra-headers-prefix": `null`,
  69. "requestheader-client-ca-file": "bar",
  70. "requestheader-allowed-names": `["first","second"]`,
  71. },
  72. },
  73. },
  74. },
  75. {
  76. name: "skip extension-apiserver-authentication",
  77. hook: ClientCARegistrationHook{
  78. ClientCA: []byte("foo"),
  79. },
  80. expectedConfigMaps: map[string]*corev1.ConfigMap{
  81. "extension-apiserver-authentication": {
  82. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  83. Data: map[string]string{
  84. "client-ca-file": "foo",
  85. },
  86. },
  87. },
  88. },
  89. {
  90. name: "empty allowed names",
  91. hook: ClientCARegistrationHook{
  92. RequestHeaderCA: []byte("bar"),
  93. },
  94. expectedConfigMaps: map[string]*corev1.ConfigMap{
  95. "extension-apiserver-authentication": {
  96. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  97. Data: map[string]string{
  98. "requestheader-username-headers": `null`,
  99. "requestheader-group-headers": `null`,
  100. "requestheader-extra-headers-prefix": `null`,
  101. "requestheader-client-ca-file": "bar",
  102. "requestheader-allowed-names": `null`,
  103. },
  104. },
  105. },
  106. },
  107. {
  108. name: "overwrite extension-apiserver-authentication",
  109. hook: ClientCARegistrationHook{
  110. ClientCA: []byte("foo"),
  111. },
  112. preexistingObjs: []runtime.Object{
  113. &corev1.ConfigMap{
  114. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  115. Data: map[string]string{
  116. "client-ca-file": "other",
  117. },
  118. },
  119. },
  120. expectedConfigMaps: map[string]*corev1.ConfigMap{
  121. "extension-apiserver-authentication": {
  122. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  123. Data: map[string]string{
  124. "client-ca-file": "foo",
  125. },
  126. },
  127. },
  128. expectUpdate: true,
  129. },
  130. {
  131. name: "overwrite extension-apiserver-authentication requestheader",
  132. hook: ClientCARegistrationHook{
  133. RequestHeaderUsernameHeaders: []string{},
  134. RequestHeaderGroupHeaders: []string{},
  135. RequestHeaderExtraHeaderPrefixes: []string{},
  136. RequestHeaderCA: []byte("bar"),
  137. RequestHeaderAllowedNames: []string{},
  138. },
  139. preexistingObjs: []runtime.Object{
  140. &corev1.ConfigMap{
  141. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  142. Data: map[string]string{
  143. "requestheader-username-headers": `null`,
  144. "requestheader-group-headers": `null`,
  145. "requestheader-extra-headers-prefix": `null`,
  146. "requestheader-client-ca-file": "something",
  147. "requestheader-allowed-names": `null`,
  148. },
  149. },
  150. },
  151. expectedConfigMaps: map[string]*corev1.ConfigMap{
  152. "extension-apiserver-authentication": {
  153. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  154. Data: map[string]string{
  155. "requestheader-username-headers": `[]`,
  156. "requestheader-group-headers": `[]`,
  157. "requestheader-extra-headers-prefix": `[]`,
  158. "requestheader-client-ca-file": "bar",
  159. "requestheader-allowed-names": `[]`,
  160. },
  161. },
  162. },
  163. expectUpdate: true,
  164. },
  165. {
  166. name: "namespace exists",
  167. hook: ClientCARegistrationHook{
  168. ClientCA: []byte("foo"),
  169. },
  170. preexistingObjs: []runtime.Object{
  171. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: metav1.NamespaceSystem}},
  172. },
  173. expectedConfigMaps: map[string]*corev1.ConfigMap{
  174. "extension-apiserver-authentication": {
  175. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  176. Data: map[string]string{
  177. "client-ca-file": "foo",
  178. },
  179. },
  180. },
  181. },
  182. {
  183. name: "skip on no change",
  184. hook: ClientCARegistrationHook{
  185. RequestHeaderUsernameHeaders: []string{},
  186. RequestHeaderGroupHeaders: []string{},
  187. RequestHeaderExtraHeaderPrefixes: []string{},
  188. RequestHeaderCA: []byte("bar"),
  189. RequestHeaderAllowedNames: []string{},
  190. },
  191. preexistingObjs: []runtime.Object{
  192. &corev1.ConfigMap{
  193. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
  194. Data: map[string]string{
  195. "requestheader-username-headers": `[]`,
  196. "requestheader-group-headers": `[]`,
  197. "requestheader-extra-headers-prefix": `[]`,
  198. "requestheader-client-ca-file": "bar",
  199. "requestheader-allowed-names": `[]`,
  200. },
  201. },
  202. },
  203. expectedConfigMaps: map[string]*corev1.ConfigMap{},
  204. expectUpdate: false,
  205. },
  206. }
  207. for _, test := range tests {
  208. t.Run(test.name, func(t *testing.T) {
  209. client := fake.NewSimpleClientset(test.preexistingObjs...)
  210. test.hook.tryToWriteClientCAs(client.CoreV1())
  211. actualConfigMaps, updated := getFinalConfigMaps(client)
  212. if !reflect.DeepEqual(test.expectedConfigMaps, actualConfigMaps) {
  213. t.Fatalf("%s: %v", test.name, diff.ObjectReflectDiff(test.expectedConfigMaps, actualConfigMaps))
  214. }
  215. if test.expectUpdate != updated {
  216. t.Fatalf("%s: expected %v, got %v", test.name, test.expectUpdate, updated)
  217. }
  218. })
  219. }
  220. }
  221. func getFinalConfigMaps(client *fake.Clientset) (map[string]*corev1.ConfigMap, bool) {
  222. ret := map[string]*corev1.ConfigMap{}
  223. updated := false
  224. for _, action := range client.Actions() {
  225. if action.Matches("create", "configmaps") {
  226. obj := action.(clienttesting.CreateAction).GetObject().(*corev1.ConfigMap)
  227. ret[obj.Name] = obj
  228. }
  229. if action.Matches("update", "configmaps") {
  230. updated = true
  231. obj := action.(clienttesting.UpdateAction).GetObject().(*corev1.ConfigMap)
  232. ret[obj.Name] = obj
  233. }
  234. }
  235. return ret, updated
  236. }