selfhosting_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  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 selfhosting
  14. import (
  15. "bytes"
  16. "io/ioutil"
  17. "os"
  18. "testing"
  19. "github.com/pkg/errors"
  20. apps "k8s.io/api/apps/v1"
  21. "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  22. kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
  23. )
  24. const (
  25. testAPIServerPod = `
  26. apiVersion: v1
  27. kind: Pod
  28. metadata:
  29. creationTimestamp: null
  30. name: kube-apiserver
  31. namespace: kube-system
  32. spec:
  33. containers:
  34. - command:
  35. - kube-apiserver
  36. - --service-account-key-file=/etc/kubernetes/pki/sa.pub
  37. - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
  38. - --secure-port=6443
  39. - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
  40. - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
  41. - --requestheader-group-headers=X-Remote-Group
  42. - --service-cluster-ip-range=10.96.0.0/12
  43. - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
  44. - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
  45. - --advertise-address=192.168.1.115
  46. - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
  47. - --insecure-port=0
  48. - --experimental-bootstrap-token-auth=true
  49. - --requestheader-username-headers=X-Remote-User
  50. - --requestheader-extra-headers-prefix=X-Remote-Extra-
  51. - --requestheader-allowed-names=front-proxy-client
  52. - --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota
  53. - --allow-privileged=true
  54. - --client-ca-file=/etc/kubernetes/pki/ca.crt
  55. - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
  56. - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
  57. - --authorization-mode=Node,RBAC
  58. - --etcd-servers=http://127.0.0.1:2379
  59. image: k8s.gcr.io/kube-apiserver-amd64:v1.7.4
  60. livenessProbe:
  61. failureThreshold: 8
  62. httpGet:
  63. host: 127.0.0.1
  64. path: /healthz
  65. port: 6443
  66. scheme: HTTPS
  67. initialDelaySeconds: 15
  68. timeoutSeconds: 15
  69. name: kube-apiserver
  70. resources:
  71. requests:
  72. cpu: 250m
  73. volumeMounts:
  74. - mountPath: /etc/kubernetes/pki
  75. name: k8s-certs
  76. readOnly: true
  77. - mountPath: /etc/ssl/certs
  78. name: ca-certs
  79. readOnly: true
  80. - mountPath: /etc/pki
  81. name: ca-certs-etc-pki
  82. readOnly: true
  83. hostNetwork: true
  84. priorityClassName: system-cluster-critical
  85. volumes:
  86. - hostPath:
  87. path: /etc/kubernetes/pki
  88. name: k8s-certs
  89. - hostPath:
  90. path: /etc/ssl/certs
  91. name: ca-certs
  92. - hostPath:
  93. path: /etc/pki
  94. name: ca-certs-etc-pki
  95. status: {}
  96. `
  97. testAPIServerDaemonSet = `apiVersion: apps/v1
  98. kind: DaemonSet
  99. metadata:
  100. creationTimestamp: null
  101. labels:
  102. k8s-app: self-hosted-kube-apiserver
  103. name: self-hosted-kube-apiserver
  104. namespace: kube-system
  105. spec:
  106. selector:
  107. matchLabels:
  108. k8s-app: self-hosted-kube-apiserver
  109. template:
  110. metadata:
  111. creationTimestamp: null
  112. labels:
  113. k8s-app: self-hosted-kube-apiserver
  114. spec:
  115. containers:
  116. - command:
  117. - kube-apiserver
  118. - --service-account-key-file=/etc/kubernetes/pki/sa.pub
  119. - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
  120. - --secure-port=6443
  121. - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
  122. - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
  123. - --requestheader-group-headers=X-Remote-Group
  124. - --service-cluster-ip-range=10.96.0.0/12
  125. - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
  126. - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
  127. - --advertise-address=$(HOST_IP)
  128. - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
  129. - --insecure-port=0
  130. - --experimental-bootstrap-token-auth=true
  131. - --requestheader-username-headers=X-Remote-User
  132. - --requestheader-extra-headers-prefix=X-Remote-Extra-
  133. - --requestheader-allowed-names=front-proxy-client
  134. - --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota
  135. - --allow-privileged=true
  136. - --client-ca-file=/etc/kubernetes/pki/ca.crt
  137. - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
  138. - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
  139. - --authorization-mode=Node,RBAC
  140. - --etcd-servers=http://127.0.0.1:2379
  141. env:
  142. - name: HOST_IP
  143. valueFrom:
  144. fieldRef:
  145. fieldPath: status.hostIP
  146. image: k8s.gcr.io/kube-apiserver-amd64:v1.7.4
  147. livenessProbe:
  148. failureThreshold: 8
  149. httpGet:
  150. host: 127.0.0.1
  151. path: /healthz
  152. port: 6443
  153. scheme: HTTPS
  154. initialDelaySeconds: 15
  155. timeoutSeconds: 15
  156. name: kube-apiserver
  157. resources:
  158. requests:
  159. cpu: 250m
  160. volumeMounts:
  161. - mountPath: /etc/kubernetes/pki
  162. name: k8s-certs
  163. readOnly: true
  164. - mountPath: /etc/ssl/certs
  165. name: ca-certs
  166. readOnly: true
  167. - mountPath: /etc/pki
  168. name: ca-certs-etc-pki
  169. readOnly: true
  170. dnsPolicy: ClusterFirstWithHostNet
  171. hostNetwork: true
  172. nodeSelector:
  173. node-role.kubernetes.io/master: ""
  174. priorityClassName: system-cluster-critical
  175. tolerations:
  176. - effect: NoSchedule
  177. key: node-role.kubernetes.io/master
  178. volumes:
  179. - hostPath:
  180. path: /etc/kubernetes/pki
  181. name: k8s-certs
  182. - hostPath:
  183. path: /etc/ssl/certs
  184. name: ca-certs
  185. - hostPath:
  186. path: /etc/pki
  187. name: ca-certs-etc-pki
  188. updateStrategy:
  189. type: RollingUpdate
  190. status:
  191. currentNumberScheduled: 0
  192. desiredNumberScheduled: 0
  193. numberMisscheduled: 0
  194. numberReady: 0
  195. `
  196. testControllerManagerPod = `
  197. apiVersion: v1
  198. kind: Pod
  199. metadata:
  200. creationTimestamp: null
  201. name: kube-controller-manager
  202. namespace: kube-system
  203. spec:
  204. containers:
  205. - command:
  206. - kube-controller-manager
  207. - --leader-elect=true
  208. - --controllers=*,bootstrapsigner,tokencleaner
  209. - --kubeconfig=/etc/kubernetes/controller-manager.conf
  210. - --root-ca-file=/etc/kubernetes/pki/ca.crt
  211. - --service-account-private-key-file=/etc/kubernetes/pki/sa.key
  212. - --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
  213. - --cluster-signing-key-file=/etc/kubernetes/pki/ca.key
  214. - --bind-address=127.0.0.1
  215. - --use-service-account-credentials=true
  216. - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
  217. image: k8s.gcr.io/kube-controller-manager-amd64:v1.7.4
  218. livenessProbe:
  219. failureThreshold: 8
  220. httpGet:
  221. host: 127.0.0.1
  222. path: /healthz
  223. port: 10257
  224. scheme: HTTPS
  225. initialDelaySeconds: 15
  226. timeoutSeconds: 15
  227. name: kube-controller-manager
  228. resources:
  229. requests:
  230. cpu: 200m
  231. volumeMounts:
  232. - mountPath: /etc/kubernetes/pki
  233. name: k8s-certs
  234. readOnly: true
  235. - mountPath: /etc/ssl/certs
  236. name: ca-certs
  237. readOnly: true
  238. - mountPath: /etc/kubernetes/controller-manager.conf
  239. name: kubeconfig
  240. readOnly: true
  241. - mountPath: /etc/pki
  242. name: ca-certs-etc-pki
  243. readOnly: true
  244. hostNetwork: true
  245. priorityClassName: system-cluster-critical
  246. volumes:
  247. - hostPath:
  248. path: /etc/kubernetes/pki
  249. name: k8s-certs
  250. - hostPath:
  251. path: /etc/ssl/certs
  252. name: ca-certs
  253. - hostPath:
  254. path: /etc/kubernetes/controller-manager.conf
  255. type: FileOrCreate
  256. name: kubeconfig
  257. - hostPath:
  258. path: /etc/pki
  259. name: ca-certs-etc-pki
  260. status: {}
  261. `
  262. testControllerManagerDaemonSet = `apiVersion: apps/v1
  263. kind: DaemonSet
  264. metadata:
  265. creationTimestamp: null
  266. labels:
  267. k8s-app: self-hosted-kube-controller-manager
  268. name: self-hosted-kube-controller-manager
  269. namespace: kube-system
  270. spec:
  271. selector:
  272. matchLabels:
  273. k8s-app: self-hosted-kube-controller-manager
  274. template:
  275. metadata:
  276. creationTimestamp: null
  277. labels:
  278. k8s-app: self-hosted-kube-controller-manager
  279. spec:
  280. containers:
  281. - command:
  282. - kube-controller-manager
  283. - --leader-elect=true
  284. - --controllers=*,bootstrapsigner,tokencleaner
  285. - --kubeconfig=/etc/kubernetes/controller-manager.conf
  286. - --root-ca-file=/etc/kubernetes/pki/ca.crt
  287. - --service-account-private-key-file=/etc/kubernetes/pki/sa.key
  288. - --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
  289. - --cluster-signing-key-file=/etc/kubernetes/pki/ca.key
  290. - --bind-address=127.0.0.1
  291. - --use-service-account-credentials=true
  292. - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
  293. image: k8s.gcr.io/kube-controller-manager-amd64:v1.7.4
  294. livenessProbe:
  295. failureThreshold: 8
  296. httpGet:
  297. host: 127.0.0.1
  298. path: /healthz
  299. port: 10257
  300. scheme: HTTPS
  301. initialDelaySeconds: 15
  302. timeoutSeconds: 15
  303. name: kube-controller-manager
  304. resources:
  305. requests:
  306. cpu: 200m
  307. volumeMounts:
  308. - mountPath: /etc/kubernetes/pki
  309. name: k8s-certs
  310. readOnly: true
  311. - mountPath: /etc/ssl/certs
  312. name: ca-certs
  313. readOnly: true
  314. - mountPath: /etc/kubernetes/controller-manager.conf
  315. name: kubeconfig
  316. readOnly: true
  317. - mountPath: /etc/pki
  318. name: ca-certs-etc-pki
  319. readOnly: true
  320. dnsPolicy: ClusterFirstWithHostNet
  321. hostNetwork: true
  322. nodeSelector:
  323. node-role.kubernetes.io/master: ""
  324. priorityClassName: system-cluster-critical
  325. tolerations:
  326. - effect: NoSchedule
  327. key: node-role.kubernetes.io/master
  328. volumes:
  329. - hostPath:
  330. path: /etc/kubernetes/pki
  331. name: k8s-certs
  332. - hostPath:
  333. path: /etc/ssl/certs
  334. name: ca-certs
  335. - hostPath:
  336. path: /etc/kubernetes/controller-manager.conf
  337. type: FileOrCreate
  338. name: kubeconfig
  339. - hostPath:
  340. path: /etc/pki
  341. name: ca-certs-etc-pki
  342. updateStrategy:
  343. type: RollingUpdate
  344. status:
  345. currentNumberScheduled: 0
  346. desiredNumberScheduled: 0
  347. numberMisscheduled: 0
  348. numberReady: 0
  349. `
  350. testSchedulerPod = `
  351. apiVersion: v1
  352. kind: Pod
  353. metadata:
  354. creationTimestamp: null
  355. name: kube-scheduler
  356. namespace: kube-system
  357. spec:
  358. containers:
  359. - command:
  360. - kube-scheduler
  361. - --leader-elect=true
  362. - --kubeconfig=/etc/kubernetes/scheduler.conf
  363. - --bind-address=127.0.0.1
  364. image: k8s.gcr.io/kube-scheduler-amd64:v1.7.4
  365. livenessProbe:
  366. failureThreshold: 8
  367. httpGet:
  368. host: 127.0.0.1
  369. path: /healthz
  370. port: 10259
  371. scheme: HTTPS
  372. initialDelaySeconds: 15
  373. timeoutSeconds: 15
  374. name: kube-scheduler
  375. resources:
  376. requests:
  377. cpu: 100m
  378. volumeMounts:
  379. - mountPath: /etc/kubernetes/scheduler.conf
  380. name: kubeconfig
  381. readOnly: true
  382. hostNetwork: true
  383. priorityClassName: system-cluster-critical
  384. volumes:
  385. - hostPath:
  386. path: /etc/kubernetes/scheduler.conf
  387. type: FileOrCreate
  388. name: kubeconfig
  389. status: {}
  390. `
  391. testSchedulerDaemonSet = `apiVersion: apps/v1
  392. kind: DaemonSet
  393. metadata:
  394. creationTimestamp: null
  395. labels:
  396. k8s-app: self-hosted-kube-scheduler
  397. name: self-hosted-kube-scheduler
  398. namespace: kube-system
  399. spec:
  400. selector:
  401. matchLabels:
  402. k8s-app: self-hosted-kube-scheduler
  403. template:
  404. metadata:
  405. creationTimestamp: null
  406. labels:
  407. k8s-app: self-hosted-kube-scheduler
  408. spec:
  409. containers:
  410. - command:
  411. - kube-scheduler
  412. - --leader-elect=true
  413. - --kubeconfig=/etc/kubernetes/scheduler.conf
  414. - --bind-address=127.0.0.1
  415. image: k8s.gcr.io/kube-scheduler-amd64:v1.7.4
  416. livenessProbe:
  417. failureThreshold: 8
  418. httpGet:
  419. host: 127.0.0.1
  420. path: /healthz
  421. port: 10259
  422. scheme: HTTPS
  423. initialDelaySeconds: 15
  424. timeoutSeconds: 15
  425. name: kube-scheduler
  426. resources:
  427. requests:
  428. cpu: 100m
  429. volumeMounts:
  430. - mountPath: /etc/kubernetes/scheduler.conf
  431. name: kubeconfig
  432. readOnly: true
  433. dnsPolicy: ClusterFirstWithHostNet
  434. hostNetwork: true
  435. nodeSelector:
  436. node-role.kubernetes.io/master: ""
  437. priorityClassName: system-cluster-critical
  438. tolerations:
  439. - effect: NoSchedule
  440. key: node-role.kubernetes.io/master
  441. volumes:
  442. - hostPath:
  443. path: /etc/kubernetes/scheduler.conf
  444. type: FileOrCreate
  445. name: kubeconfig
  446. updateStrategy:
  447. type: RollingUpdate
  448. status:
  449. currentNumberScheduled: 0
  450. desiredNumberScheduled: 0
  451. numberMisscheduled: 0
  452. numberReady: 0
  453. `
  454. )
  455. func TestBuildDaemonSet(t *testing.T) {
  456. var tests = []struct {
  457. component string
  458. podBytes []byte
  459. dsBytes []byte
  460. }{
  461. {
  462. component: constants.KubeAPIServer,
  463. podBytes: []byte(testAPIServerPod),
  464. dsBytes: []byte(testAPIServerDaemonSet),
  465. },
  466. {
  467. component: constants.KubeControllerManager,
  468. podBytes: []byte(testControllerManagerPod),
  469. dsBytes: []byte(testControllerManagerDaemonSet),
  470. },
  471. {
  472. component: constants.KubeScheduler,
  473. podBytes: []byte(testSchedulerPod),
  474. dsBytes: []byte(testSchedulerDaemonSet),
  475. },
  476. }
  477. for _, rt := range tests {
  478. t.Run(rt.component, func(t *testing.T) {
  479. tempFile, err := createTempFileWithContent(rt.podBytes)
  480. if err != nil {
  481. t.Errorf("error creating tempfile with content:%v", err)
  482. }
  483. defer os.Remove(tempFile)
  484. podSpec, err := loadPodSpecFromFile(tempFile)
  485. if err != nil {
  486. t.Fatalf("couldn't load the specified Pod Spec")
  487. }
  488. ds := BuildDaemonSet(rt.component, podSpec, GetDefaultMutators())
  489. dsBytes, err := kubeadmutil.MarshalToYaml(ds, apps.SchemeGroupVersion)
  490. if err != nil {
  491. t.Fatalf("failed to marshal daemonset to YAML: %v", err)
  492. }
  493. if !bytes.Equal(dsBytes, rt.dsBytes) {
  494. t.Errorf("failed TestBuildDaemonSet:\nexpected:\n%s\nsaw:\n%s", rt.dsBytes, dsBytes)
  495. }
  496. })
  497. }
  498. }
  499. func TestLoadPodSpecFromFile(t *testing.T) {
  500. tests := []struct {
  501. name string
  502. content string
  503. expectError bool
  504. }{
  505. {
  506. name: "no content",
  507. content: "",
  508. expectError: true,
  509. },
  510. {
  511. name: "valid YAML",
  512. content: `
  513. apiVersion: v1
  514. kind: Pod
  515. metadata:
  516. name: testpod
  517. spec:
  518. containers:
  519. - image: k8s.gcr.io/busybox
  520. `,
  521. expectError: false,
  522. },
  523. {
  524. name: "valid JSON",
  525. content: `
  526. {
  527. "apiVersion": "v1",
  528. "kind": "Pod",
  529. "metadata": {
  530. "name": "testpod"
  531. },
  532. "spec": {
  533. "containers": [
  534. {
  535. "image": "k8s.gcr.io/busybox"
  536. }
  537. ]
  538. }
  539. }`,
  540. expectError: false,
  541. },
  542. {
  543. name: "incorrect PodSpec",
  544. content: `
  545. apiVersion: v1
  546. kind: Pod
  547. metadata:
  548. name: testpod
  549. spec:
  550. - image: k8s.gcr.io/busybox
  551. `,
  552. expectError: true,
  553. },
  554. }
  555. for _, rt := range tests {
  556. t.Run(rt.name, func(t *testing.T) {
  557. tempFile, err := createTempFileWithContent([]byte(rt.content))
  558. if err != nil {
  559. t.Errorf("error creating tempfile with content:%v", err)
  560. }
  561. defer os.Remove(tempFile)
  562. _, err = loadPodSpecFromFile(tempFile)
  563. if (err != nil) != rt.expectError {
  564. t.Errorf("failed TestLoadPodSpecFromFile:\nexpected error:\n%t\nsaw:\n%v", rt.expectError, err)
  565. }
  566. })
  567. }
  568. t.Run("empty file name", func(t *testing.T) {
  569. _, err := loadPodSpecFromFile("")
  570. if err == nil {
  571. t.Error("unexpected success: loadPodSpecFromFile should return error when no file is given")
  572. }
  573. })
  574. }
  575. func createTempFileWithContent(content []byte) (string, error) {
  576. tempFile, err := ioutil.TempFile("", "")
  577. if err != nil {
  578. return "", errors.Wrap(err, "cannot create temporary file")
  579. }
  580. if _, err = tempFile.Write([]byte(content)); err != nil {
  581. return "", errors.Wrap(err, "cannot save temporary file")
  582. }
  583. if err = tempFile.Close(); err != nil {
  584. return "", errors.Wrap(err, "cannot close temporary file")
  585. }
  586. return tempFile.Name(), nil
  587. }