kubeproxy_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /*
  2. Copyright 2019 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 componentconfigs
  14. import (
  15. "reflect"
  16. "strings"
  17. "testing"
  18. "github.com/lithammer/dedent"
  19. v1 "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. clientsetfake "k8s.io/client-go/kubernetes/fake"
  22. componentbaseconfig "k8s.io/component-base/config/v1alpha1"
  23. kubeproxyconfig "k8s.io/kube-proxy/config/v1alpha1"
  24. kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
  25. kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
  26. "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  27. "k8s.io/kubernetes/cmd/kubeadm/app/features"
  28. kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
  29. )
  30. // kubeProxyMarshalCases holds common marshal test cases for both the marshal and unmarshal tests
  31. var kubeProxyMarshalCases = []struct {
  32. name string
  33. obj *kubeProxyConfig
  34. yaml string
  35. }{
  36. {
  37. name: "Empty config",
  38. obj: &kubeProxyConfig{
  39. config: kubeproxyconfig.KubeProxyConfiguration{},
  40. },
  41. yaml: dedent.Dedent(`
  42. apiVersion: kubeproxy.config.k8s.io/v1alpha1
  43. bindAddress: ""
  44. clientConnection:
  45. acceptContentTypes: ""
  46. burst: 0
  47. contentType: ""
  48. kubeconfig: ""
  49. qps: 0
  50. clusterCIDR: ""
  51. configSyncPeriod: 0s
  52. conntrack:
  53. maxPerCore: null
  54. min: null
  55. tcpCloseWaitTimeout: null
  56. tcpEstablishedTimeout: null
  57. detectLocalMode: ""
  58. enableProfiling: false
  59. healthzBindAddress: ""
  60. hostnameOverride: ""
  61. iptables:
  62. masqueradeAll: false
  63. masqueradeBit: null
  64. minSyncPeriod: 0s
  65. syncPeriod: 0s
  66. ipvs:
  67. excludeCIDRs: null
  68. minSyncPeriod: 0s
  69. scheduler: ""
  70. strictARP: false
  71. syncPeriod: 0s
  72. tcpFinTimeout: 0s
  73. tcpTimeout: 0s
  74. udpTimeout: 0s
  75. kind: KubeProxyConfiguration
  76. metricsBindAddress: ""
  77. mode: ""
  78. nodePortAddresses: null
  79. oomScoreAdj: null
  80. portRange: ""
  81. showHiddenMetricsForVersion: ""
  82. udpIdleTimeout: 0s
  83. winkernel:
  84. enableDSR: false
  85. networkName: ""
  86. sourceVip: ""
  87. `),
  88. },
  89. {
  90. name: "Non empty config",
  91. obj: &kubeProxyConfig{
  92. config: kubeproxyconfig.KubeProxyConfiguration{
  93. BindAddress: "1.2.3.4",
  94. EnableProfiling: true,
  95. },
  96. },
  97. yaml: dedent.Dedent(`
  98. apiVersion: kubeproxy.config.k8s.io/v1alpha1
  99. bindAddress: 1.2.3.4
  100. clientConnection:
  101. acceptContentTypes: ""
  102. burst: 0
  103. contentType: ""
  104. kubeconfig: ""
  105. qps: 0
  106. clusterCIDR: ""
  107. configSyncPeriod: 0s
  108. conntrack:
  109. maxPerCore: null
  110. min: null
  111. tcpCloseWaitTimeout: null
  112. tcpEstablishedTimeout: null
  113. detectLocalMode: ""
  114. enableProfiling: true
  115. healthzBindAddress: ""
  116. hostnameOverride: ""
  117. iptables:
  118. masqueradeAll: false
  119. masqueradeBit: null
  120. minSyncPeriod: 0s
  121. syncPeriod: 0s
  122. ipvs:
  123. excludeCIDRs: null
  124. minSyncPeriod: 0s
  125. scheduler: ""
  126. strictARP: false
  127. syncPeriod: 0s
  128. tcpFinTimeout: 0s
  129. tcpTimeout: 0s
  130. udpTimeout: 0s
  131. kind: KubeProxyConfiguration
  132. metricsBindAddress: ""
  133. mode: ""
  134. nodePortAddresses: null
  135. oomScoreAdj: null
  136. portRange: ""
  137. showHiddenMetricsForVersion: ""
  138. udpIdleTimeout: 0s
  139. winkernel:
  140. enableDSR: false
  141. networkName: ""
  142. sourceVip: ""
  143. `),
  144. },
  145. }
  146. func TestKubeProxyMarshal(t *testing.T) {
  147. for _, test := range kubeProxyMarshalCases {
  148. t.Run(test.name, func(t *testing.T) {
  149. b, err := test.obj.Marshal()
  150. if err != nil {
  151. t.Fatalf("Marshal failed: %v", err)
  152. }
  153. got := strings.TrimSpace(string(b))
  154. expected := strings.TrimSpace(test.yaml)
  155. if expected != string(got) {
  156. t.Fatalf("Missmatch between expected and got:\nExpected:\n%s\n---\nGot:\n%s", expected, string(got))
  157. }
  158. })
  159. }
  160. }
  161. func TestKubeProxyUnmarshal(t *testing.T) {
  162. for _, test := range kubeProxyMarshalCases {
  163. t.Run(test.name, func(t *testing.T) {
  164. gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test.yaml))
  165. if err != nil {
  166. t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
  167. }
  168. got := &kubeProxyConfig{}
  169. if err = got.Unmarshal(gvkmap); err != nil {
  170. t.Fatalf("unexpected failure of Unmarshal: %v", err)
  171. }
  172. expected := test.obj.DeepCopy().(*kubeProxyConfig)
  173. expected.config.APIVersion = kubeProxyHandler.GroupVersion.String()
  174. expected.config.Kind = "KubeProxyConfiguration"
  175. if !reflect.DeepEqual(got, expected) {
  176. t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", expected, got)
  177. }
  178. })
  179. }
  180. }
  181. func TestKubeProxyDefault(t *testing.T) {
  182. tests := []struct {
  183. name string
  184. clusterCfg kubeadmapi.ClusterConfiguration
  185. endpoint kubeadmapi.APIEndpoint
  186. expected kubeProxyConfig
  187. }{
  188. {
  189. name: "No specific defaulting works",
  190. clusterCfg: kubeadmapi.ClusterConfiguration{},
  191. endpoint: kubeadmapi.APIEndpoint{},
  192. expected: kubeProxyConfig{
  193. config: kubeproxyconfig.KubeProxyConfiguration{
  194. FeatureGates: map[string]bool{},
  195. BindAddress: kubeadmapiv1beta2.DefaultProxyBindAddressv6,
  196. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  197. Kubeconfig: kubeproxyKubeConfigFileName,
  198. },
  199. },
  200. },
  201. },
  202. {
  203. name: "IPv4 bind address",
  204. clusterCfg: kubeadmapi.ClusterConfiguration{},
  205. endpoint: kubeadmapi.APIEndpoint{
  206. AdvertiseAddress: "1.2.3.4",
  207. },
  208. expected: kubeProxyConfig{
  209. config: kubeproxyconfig.KubeProxyConfiguration{
  210. FeatureGates: map[string]bool{},
  211. BindAddress: kubeadmapiv1beta2.DefaultProxyBindAddressv4,
  212. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  213. Kubeconfig: kubeproxyKubeConfigFileName,
  214. },
  215. },
  216. },
  217. },
  218. {
  219. name: "ClusterCIDR is fetched from PodSubnet",
  220. clusterCfg: kubeadmapi.ClusterConfiguration{
  221. Networking: kubeadmapi.Networking{
  222. PodSubnet: "192.168.0.0/16",
  223. },
  224. },
  225. endpoint: kubeadmapi.APIEndpoint{},
  226. expected: kubeProxyConfig{
  227. config: kubeproxyconfig.KubeProxyConfiguration{
  228. FeatureGates: map[string]bool{},
  229. BindAddress: kubeadmapiv1beta2.DefaultProxyBindAddressv6,
  230. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  231. Kubeconfig: kubeproxyKubeConfigFileName,
  232. },
  233. ClusterCIDR: "192.168.0.0/16",
  234. },
  235. },
  236. },
  237. {
  238. name: "IPv6DualStack feature gate set to true",
  239. clusterCfg: kubeadmapi.ClusterConfiguration{
  240. FeatureGates: map[string]bool{
  241. features.IPv6DualStack: true,
  242. },
  243. },
  244. endpoint: kubeadmapi.APIEndpoint{},
  245. expected: kubeProxyConfig{
  246. config: kubeproxyconfig.KubeProxyConfiguration{
  247. FeatureGates: map[string]bool{
  248. features.IPv6DualStack: true,
  249. },
  250. BindAddress: kubeadmapiv1beta2.DefaultProxyBindAddressv6,
  251. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  252. Kubeconfig: kubeproxyKubeConfigFileName,
  253. },
  254. },
  255. },
  256. },
  257. {
  258. name: "IPv6DualStack feature gate set to false",
  259. clusterCfg: kubeadmapi.ClusterConfiguration{
  260. FeatureGates: map[string]bool{
  261. features.IPv6DualStack: false,
  262. },
  263. },
  264. endpoint: kubeadmapi.APIEndpoint{},
  265. expected: kubeProxyConfig{
  266. config: kubeproxyconfig.KubeProxyConfiguration{
  267. FeatureGates: map[string]bool{
  268. features.IPv6DualStack: false,
  269. },
  270. BindAddress: kubeadmapiv1beta2.DefaultProxyBindAddressv6,
  271. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  272. Kubeconfig: kubeproxyKubeConfigFileName,
  273. },
  274. },
  275. },
  276. },
  277. }
  278. for _, test := range tests {
  279. t.Run(test.name, func(t *testing.T) {
  280. got := &kubeProxyConfig{}
  281. got.Default(&test.clusterCfg, &test.endpoint)
  282. if !reflect.DeepEqual(got, &test.expected) {
  283. t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.expected, got)
  284. }
  285. })
  286. }
  287. }
  288. // runKubeProxyFromTest holds common test case data and evaluation code for kubeProxyHandler.From* functions
  289. func runKubeProxyFromTest(t *testing.T, perform func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error)) {
  290. tests := []struct {
  291. name string
  292. in string
  293. out *kubeProxyConfig
  294. expectErr bool
  295. }{
  296. {
  297. name: "Empty document map should return nothing successfully",
  298. },
  299. {
  300. name: "Non-empty non-kube-proxy document map returns nothing successfully",
  301. in: dedent.Dedent(`
  302. apiVersion: api.example.com/v1
  303. kind: Configuration
  304. `),
  305. },
  306. {
  307. name: "Old kube-proxy version returns an error",
  308. in: dedent.Dedent(`
  309. apiVersion: kubeproxy.config.k8s.io/v1alpha0
  310. kind: KubeProxyConfiguration
  311. `),
  312. expectErr: true,
  313. },
  314. {
  315. name: "New kube-proxy version returns an error",
  316. in: dedent.Dedent(`
  317. apiVersion: kubeproxy.config.k8s.io/v1beta1
  318. kind: KubeProxyConfiguration
  319. `),
  320. expectErr: true,
  321. },
  322. {
  323. name: "Wrong kube-proxy kind returns an error",
  324. in: dedent.Dedent(`
  325. apiVersion: kubeproxy.config.k8s.io/v1alpha1
  326. kind: Configuration
  327. `),
  328. expectErr: true,
  329. },
  330. {
  331. name: "Valid kube-proxy only config gets loaded",
  332. in: dedent.Dedent(`
  333. apiVersion: kubeproxy.config.k8s.io/v1alpha1
  334. kind: KubeProxyConfiguration
  335. bindAddress: 1.2.3.4
  336. enableProfiling: true
  337. `),
  338. out: &kubeProxyConfig{
  339. config: kubeproxyconfig.KubeProxyConfiguration{
  340. TypeMeta: metav1.TypeMeta{
  341. APIVersion: kubeProxyHandler.GroupVersion.String(),
  342. Kind: "KubeProxyConfiguration",
  343. },
  344. BindAddress: "1.2.3.4",
  345. EnableProfiling: true,
  346. },
  347. },
  348. },
  349. {
  350. name: "Valid kube-proxy config gets loaded when coupled with an extra document",
  351. in: dedent.Dedent(`
  352. apiVersion: api.example.com/v1
  353. kind: Configuration
  354. ---
  355. apiVersion: kubeproxy.config.k8s.io/v1alpha1
  356. kind: KubeProxyConfiguration
  357. bindAddress: 1.2.3.4
  358. enableProfiling: true
  359. `),
  360. out: &kubeProxyConfig{
  361. config: kubeproxyconfig.KubeProxyConfiguration{
  362. TypeMeta: metav1.TypeMeta{
  363. APIVersion: kubeProxyHandler.GroupVersion.String(),
  364. Kind: "KubeProxyConfiguration",
  365. },
  366. BindAddress: "1.2.3.4",
  367. EnableProfiling: true,
  368. },
  369. },
  370. },
  371. }
  372. for _, test := range tests {
  373. t.Run(test.name, func(t *testing.T) {
  374. componentCfg, err := perform(t, test.in)
  375. if err != nil {
  376. if !test.expectErr {
  377. t.Errorf("unexpected failure: %v", err)
  378. }
  379. } else {
  380. if test.expectErr {
  381. t.Error("unexpected success")
  382. } else {
  383. if componentCfg == nil {
  384. if test.out != nil {
  385. t.Error("unexpected nil result")
  386. }
  387. } else {
  388. if got, ok := componentCfg.(*kubeProxyConfig); !ok {
  389. t.Error("different result type")
  390. } else {
  391. if test.out == nil {
  392. t.Errorf("unexpected result: %v", got)
  393. } else if !reflect.DeepEqual(test.out, got) {
  394. t.Errorf("missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.out, got)
  395. }
  396. }
  397. }
  398. }
  399. }
  400. })
  401. }
  402. }
  403. func TestKubeProxyFromDocumentMap(t *testing.T) {
  404. runKubeProxyFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
  405. gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(in))
  406. if err != nil {
  407. t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
  408. }
  409. return kubeProxyHandler.FromDocumentMap(gvkmap)
  410. })
  411. }
  412. func TestKubeProxyFromCluster(t *testing.T) {
  413. runKubeProxyFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
  414. client := clientsetfake.NewSimpleClientset(
  415. &v1.ConfigMap{
  416. ObjectMeta: metav1.ObjectMeta{
  417. Name: constants.KubeProxyConfigMap,
  418. Namespace: metav1.NamespaceSystem,
  419. },
  420. Data: map[string]string{
  421. constants.KubeProxyConfigMapKey: in,
  422. },
  423. },
  424. )
  425. return kubeProxyHandler.FromCluster(client, &kubeadmapi.ClusterConfiguration{})
  426. })
  427. }