manifests_test.go 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. /*
  2. Copyright 2016 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 controlplane
  14. import (
  15. "fmt"
  16. "os"
  17. "path/filepath"
  18. "reflect"
  19. "sort"
  20. "strings"
  21. "testing"
  22. "k8s.io/apimachinery/pkg/util/sets"
  23. "k8s.io/apimachinery/pkg/util/version"
  24. kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
  25. kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  26. "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
  27. authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
  28. testutil "k8s.io/kubernetes/cmd/kubeadm/test"
  29. )
  30. const (
  31. testCertsDir = "/var/lib/certs"
  32. )
  33. var cpVersion = kubeadmconstants.MinimumControlPlaneVersion.WithPreRelease("beta.2").String()
  34. func TestGetStaticPodSpecs(t *testing.T) {
  35. // Creates a Cluster Configuration
  36. cfg := &kubeadmapi.ClusterConfiguration{
  37. KubernetesVersion: "v1.9.0",
  38. }
  39. // Executes GetStaticPodSpecs
  40. // TODO: Move the "pkg/util/version".Version object into the internal API instead of always parsing the string
  41. k8sVersion, _ := version.ParseSemantic(cfg.KubernetesVersion)
  42. specs := GetStaticPodSpecs(cfg, &kubeadmapi.APIEndpoint{}, k8sVersion)
  43. var tests = []struct {
  44. name string
  45. staticPodName string
  46. }{
  47. {
  48. name: "KubeAPIServer",
  49. staticPodName: kubeadmconstants.KubeAPIServer,
  50. },
  51. {
  52. name: "KubeControllerManager",
  53. staticPodName: kubeadmconstants.KubeControllerManager,
  54. },
  55. {
  56. name: "KubeScheduler",
  57. staticPodName: kubeadmconstants.KubeScheduler,
  58. },
  59. }
  60. for _, tc := range tests {
  61. t.Run(tc.name, func(t *testing.T) {
  62. // assert the spec for the staticPodName exists
  63. if spec, ok := specs[tc.staticPodName]; ok {
  64. // Assert each specs refers to the right pod
  65. if spec.Spec.Containers[0].Name != tc.staticPodName {
  66. t.Errorf("getKubeConfigSpecs spec for %s contains pod %s, expects %s", tc.staticPodName, spec.Spec.Containers[0].Name, tc.staticPodName)
  67. }
  68. } else {
  69. t.Errorf("getStaticPodSpecs didn't create spec for %s ", tc.staticPodName)
  70. }
  71. })
  72. }
  73. }
  74. func TestCreateStaticPodFilesAndWrappers(t *testing.T) {
  75. var tests = []struct {
  76. name string
  77. components []string
  78. }{
  79. {
  80. name: "KubeAPIServer KubeAPIServer KubeScheduler",
  81. components: []string{kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeControllerManager, kubeadmconstants.KubeScheduler},
  82. },
  83. {
  84. name: "KubeAPIServer",
  85. components: []string{kubeadmconstants.KubeAPIServer},
  86. },
  87. {
  88. name: "KubeControllerManager",
  89. components: []string{kubeadmconstants.KubeControllerManager},
  90. },
  91. {
  92. name: "KubeScheduler",
  93. components: []string{kubeadmconstants.KubeScheduler},
  94. },
  95. }
  96. for _, test := range tests {
  97. t.Run(test.name, func(t *testing.T) {
  98. // Create temp folder for the test case
  99. tmpdir := testutil.SetupTempDir(t)
  100. defer os.RemoveAll(tmpdir)
  101. // Creates a Cluster Configuration
  102. cfg := &kubeadmapi.ClusterConfiguration{
  103. KubernetesVersion: "v1.9.0",
  104. }
  105. // Execute createStaticPodFunction
  106. manifestPath := filepath.Join(tmpdir, kubeadmconstants.ManifestsSubDirName)
  107. err := CreateStaticPodFiles(manifestPath, cfg, &kubeadmapi.APIEndpoint{}, test.components...)
  108. if err != nil {
  109. t.Errorf("Error executing createStaticPodFunction: %v", err)
  110. return
  111. }
  112. // Assert expected files are there
  113. testutil.AssertFilesCount(t, manifestPath, len(test.components))
  114. for _, fileName := range test.components {
  115. testutil.AssertFileExists(t, manifestPath, fileName+".yaml")
  116. }
  117. })
  118. }
  119. }
  120. func TestGetAPIServerCommand(t *testing.T) {
  121. var tests = []struct {
  122. name string
  123. cfg *kubeadmapi.ClusterConfiguration
  124. endpoint *kubeadmapi.APIEndpoint
  125. expected []string
  126. }{
  127. {
  128. name: "testing defaults",
  129. cfg: &kubeadmapi.ClusterConfiguration{
  130. Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
  131. CertificatesDir: testCertsDir,
  132. },
  133. endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
  134. expected: []string{
  135. "kube-apiserver",
  136. "--insecure-port=0",
  137. "--enable-admission-plugins=NodeRestriction",
  138. "--service-cluster-ip-range=bar",
  139. "--service-account-key-file=" + testCertsDir + "/sa.pub",
  140. "--client-ca-file=" + testCertsDir + "/ca.crt",
  141. "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
  142. "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
  143. "--kubelet-client-certificate=" + testCertsDir + "/apiserver-kubelet-client.crt",
  144. "--kubelet-client-key=" + testCertsDir + "/apiserver-kubelet-client.key",
  145. "--enable-bootstrap-token-auth=true",
  146. "--secure-port=123",
  147. "--allow-privileged=true",
  148. "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
  149. "--proxy-client-cert-file=/var/lib/certs/front-proxy-client.crt",
  150. "--proxy-client-key-file=/var/lib/certs/front-proxy-client.key",
  151. "--requestheader-username-headers=X-Remote-User",
  152. "--requestheader-group-headers=X-Remote-Group",
  153. "--requestheader-extra-headers-prefix=X-Remote-Extra-",
  154. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  155. "--requestheader-allowed-names=front-proxy-client",
  156. "--authorization-mode=Node,RBAC",
  157. "--advertise-address=1.2.3.4",
  158. fmt.Sprintf("--etcd-servers=https://127.0.0.1:%d", kubeadmconstants.EtcdListenClientPort),
  159. "--etcd-cafile=" + testCertsDir + "/etcd/ca.crt",
  160. "--etcd-certfile=" + testCertsDir + "/apiserver-etcd-client.crt",
  161. "--etcd-keyfile=" + testCertsDir + "/apiserver-etcd-client.key",
  162. },
  163. },
  164. {
  165. name: "ipv6 advertise address",
  166. cfg: &kubeadmapi.ClusterConfiguration{
  167. Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
  168. CertificatesDir: testCertsDir,
  169. },
  170. endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
  171. expected: []string{
  172. "kube-apiserver",
  173. "--insecure-port=0",
  174. "--enable-admission-plugins=NodeRestriction",
  175. "--service-cluster-ip-range=bar",
  176. "--service-account-key-file=" + testCertsDir + "/sa.pub",
  177. "--client-ca-file=" + testCertsDir + "/ca.crt",
  178. "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
  179. "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
  180. "--kubelet-client-certificate=" + testCertsDir + "/apiserver-kubelet-client.crt",
  181. "--kubelet-client-key=" + testCertsDir + "/apiserver-kubelet-client.key",
  182. "--enable-bootstrap-token-auth=true",
  183. fmt.Sprintf("--secure-port=%d", 123),
  184. "--allow-privileged=true",
  185. "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
  186. "--proxy-client-cert-file=/var/lib/certs/front-proxy-client.crt",
  187. "--proxy-client-key-file=/var/lib/certs/front-proxy-client.key",
  188. "--requestheader-username-headers=X-Remote-User",
  189. "--requestheader-group-headers=X-Remote-Group",
  190. "--requestheader-extra-headers-prefix=X-Remote-Extra-",
  191. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  192. "--requestheader-allowed-names=front-proxy-client",
  193. "--authorization-mode=Node,RBAC",
  194. "--advertise-address=2001:db8::1",
  195. fmt.Sprintf("--etcd-servers=https://127.0.0.1:%d", kubeadmconstants.EtcdListenClientPort),
  196. "--etcd-cafile=" + testCertsDir + "/etcd/ca.crt",
  197. "--etcd-certfile=" + testCertsDir + "/apiserver-etcd-client.crt",
  198. "--etcd-keyfile=" + testCertsDir + "/apiserver-etcd-client.key",
  199. },
  200. },
  201. {
  202. name: "an external etcd with custom ca, certs and keys",
  203. cfg: &kubeadmapi.ClusterConfiguration{
  204. Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
  205. Etcd: kubeadmapi.Etcd{
  206. External: &kubeadmapi.ExternalEtcd{
  207. Endpoints: []string{"https://8.6.4.1:2379", "https://8.6.4.2:2379"},
  208. CAFile: "fuz",
  209. CertFile: "fiz",
  210. KeyFile: "faz",
  211. },
  212. },
  213. CertificatesDir: testCertsDir,
  214. },
  215. endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
  216. expected: []string{
  217. "kube-apiserver",
  218. "--insecure-port=0",
  219. "--enable-admission-plugins=NodeRestriction",
  220. "--service-cluster-ip-range=bar",
  221. "--service-account-key-file=" + testCertsDir + "/sa.pub",
  222. "--client-ca-file=" + testCertsDir + "/ca.crt",
  223. "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
  224. "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
  225. "--kubelet-client-certificate=" + testCertsDir + "/apiserver-kubelet-client.crt",
  226. "--kubelet-client-key=" + testCertsDir + "/apiserver-kubelet-client.key",
  227. fmt.Sprintf("--secure-port=%d", 123),
  228. "--allow-privileged=true",
  229. "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
  230. "--enable-bootstrap-token-auth=true",
  231. "--proxy-client-cert-file=/var/lib/certs/front-proxy-client.crt",
  232. "--proxy-client-key-file=/var/lib/certs/front-proxy-client.key",
  233. "--requestheader-username-headers=X-Remote-User",
  234. "--requestheader-group-headers=X-Remote-Group",
  235. "--requestheader-extra-headers-prefix=X-Remote-Extra-",
  236. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  237. "--requestheader-allowed-names=front-proxy-client",
  238. "--authorization-mode=Node,RBAC",
  239. "--advertise-address=2001:db8::1",
  240. "--etcd-servers=https://8.6.4.1:2379,https://8.6.4.2:2379",
  241. "--etcd-cafile=fuz",
  242. "--etcd-certfile=fiz",
  243. "--etcd-keyfile=faz",
  244. },
  245. },
  246. {
  247. name: "an insecure etcd",
  248. cfg: &kubeadmapi.ClusterConfiguration{
  249. Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
  250. Etcd: kubeadmapi.Etcd{
  251. External: &kubeadmapi.ExternalEtcd{
  252. Endpoints: []string{"http://127.0.0.1:2379", "http://127.0.0.1:2380"},
  253. },
  254. },
  255. CertificatesDir: testCertsDir,
  256. },
  257. endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
  258. expected: []string{
  259. "kube-apiserver",
  260. "--insecure-port=0",
  261. "--enable-admission-plugins=NodeRestriction",
  262. "--service-cluster-ip-range=bar",
  263. "--service-account-key-file=" + testCertsDir + "/sa.pub",
  264. "--client-ca-file=" + testCertsDir + "/ca.crt",
  265. "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
  266. "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
  267. "--kubelet-client-certificate=" + testCertsDir + "/apiserver-kubelet-client.crt",
  268. "--kubelet-client-key=" + testCertsDir + "/apiserver-kubelet-client.key",
  269. fmt.Sprintf("--secure-port=%d", 123),
  270. "--allow-privileged=true",
  271. "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
  272. "--enable-bootstrap-token-auth=true",
  273. "--proxy-client-cert-file=/var/lib/certs/front-proxy-client.crt",
  274. "--proxy-client-key-file=/var/lib/certs/front-proxy-client.key",
  275. "--requestheader-username-headers=X-Remote-User",
  276. "--requestheader-group-headers=X-Remote-Group",
  277. "--requestheader-extra-headers-prefix=X-Remote-Extra-",
  278. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  279. "--requestheader-allowed-names=front-proxy-client",
  280. "--authorization-mode=Node,RBAC",
  281. "--advertise-address=2001:db8::1",
  282. "--etcd-servers=http://127.0.0.1:2379,http://127.0.0.1:2380",
  283. },
  284. },
  285. {
  286. name: "test APIServer.ExtraArgs works as expected",
  287. cfg: &kubeadmapi.ClusterConfiguration{
  288. Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
  289. CertificatesDir: testCertsDir,
  290. APIServer: kubeadmapi.APIServer{
  291. ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
  292. ExtraArgs: map[string]string{
  293. "service-cluster-ip-range": "baz",
  294. "advertise-address": "9.9.9.9",
  295. "audit-policy-file": "/etc/config/audit.yaml",
  296. "audit-log-path": "/var/log/kubernetes",
  297. },
  298. },
  299. },
  300. },
  301. endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
  302. expected: []string{
  303. "kube-apiserver",
  304. "--insecure-port=0",
  305. "--enable-admission-plugins=NodeRestriction",
  306. "--service-cluster-ip-range=baz",
  307. "--service-account-key-file=" + testCertsDir + "/sa.pub",
  308. "--client-ca-file=" + testCertsDir + "/ca.crt",
  309. "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
  310. "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
  311. "--kubelet-client-certificate=" + testCertsDir + "/apiserver-kubelet-client.crt",
  312. "--kubelet-client-key=" + testCertsDir + "/apiserver-kubelet-client.key",
  313. "--enable-bootstrap-token-auth=true",
  314. "--secure-port=123",
  315. "--allow-privileged=true",
  316. "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
  317. "--proxy-client-cert-file=/var/lib/certs/front-proxy-client.crt",
  318. "--proxy-client-key-file=/var/lib/certs/front-proxy-client.key",
  319. "--requestheader-username-headers=X-Remote-User",
  320. "--requestheader-group-headers=X-Remote-Group",
  321. "--requestheader-extra-headers-prefix=X-Remote-Extra-",
  322. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  323. "--requestheader-allowed-names=front-proxy-client",
  324. "--authorization-mode=Node,RBAC",
  325. "--advertise-address=9.9.9.9",
  326. fmt.Sprintf("--etcd-servers=https://127.0.0.1:%d", kubeadmconstants.EtcdListenClientPort),
  327. "--etcd-cafile=" + testCertsDir + "/etcd/ca.crt",
  328. "--etcd-certfile=" + testCertsDir + "/apiserver-etcd-client.crt",
  329. "--etcd-keyfile=" + testCertsDir + "/apiserver-etcd-client.key",
  330. "--audit-policy-file=/etc/config/audit.yaml",
  331. "--audit-log-path=/var/log/kubernetes",
  332. },
  333. },
  334. {
  335. name: "authorization-mode extra-args ABAC",
  336. cfg: &kubeadmapi.ClusterConfiguration{
  337. Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
  338. CertificatesDir: testCertsDir,
  339. APIServer: kubeadmapi.APIServer{
  340. ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
  341. ExtraArgs: map[string]string{
  342. "authorization-mode": authzmodes.ModeABAC,
  343. },
  344. },
  345. },
  346. },
  347. endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
  348. expected: []string{
  349. "kube-apiserver",
  350. "--insecure-port=0",
  351. "--enable-admission-plugins=NodeRestriction",
  352. "--service-cluster-ip-range=bar",
  353. "--service-account-key-file=" + testCertsDir + "/sa.pub",
  354. "--client-ca-file=" + testCertsDir + "/ca.crt",
  355. "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
  356. "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
  357. "--kubelet-client-certificate=" + testCertsDir + "/apiserver-kubelet-client.crt",
  358. "--kubelet-client-key=" + testCertsDir + "/apiserver-kubelet-client.key",
  359. "--enable-bootstrap-token-auth=true",
  360. "--secure-port=123",
  361. "--allow-privileged=true",
  362. "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
  363. "--proxy-client-cert-file=/var/lib/certs/front-proxy-client.crt",
  364. "--proxy-client-key-file=/var/lib/certs/front-proxy-client.key",
  365. "--requestheader-username-headers=X-Remote-User",
  366. "--requestheader-group-headers=X-Remote-Group",
  367. "--requestheader-extra-headers-prefix=X-Remote-Extra-",
  368. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  369. "--requestheader-allowed-names=front-proxy-client",
  370. "--authorization-mode=Node,RBAC,ABAC",
  371. "--advertise-address=1.2.3.4",
  372. fmt.Sprintf("--etcd-servers=https://127.0.0.1:%d", kubeadmconstants.EtcdListenClientPort),
  373. "--etcd-cafile=" + testCertsDir + "/etcd/ca.crt",
  374. "--etcd-certfile=" + testCertsDir + "/apiserver-etcd-client.crt",
  375. "--etcd-keyfile=" + testCertsDir + "/apiserver-etcd-client.key",
  376. },
  377. },
  378. {
  379. name: "insecure-port extra-args",
  380. cfg: &kubeadmapi.ClusterConfiguration{
  381. Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
  382. CertificatesDir: testCertsDir,
  383. APIServer: kubeadmapi.APIServer{
  384. ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
  385. ExtraArgs: map[string]string{
  386. "insecure-port": "1234",
  387. },
  388. },
  389. },
  390. },
  391. endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
  392. expected: []string{
  393. "kube-apiserver",
  394. "--insecure-port=1234",
  395. "--enable-admission-plugins=NodeRestriction",
  396. "--service-cluster-ip-range=bar",
  397. "--service-account-key-file=" + testCertsDir + "/sa.pub",
  398. "--client-ca-file=" + testCertsDir + "/ca.crt",
  399. "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
  400. "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
  401. "--kubelet-client-certificate=" + testCertsDir + "/apiserver-kubelet-client.crt",
  402. "--kubelet-client-key=" + testCertsDir + "/apiserver-kubelet-client.key",
  403. "--enable-bootstrap-token-auth=true",
  404. "--secure-port=123",
  405. "--allow-privileged=true",
  406. "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
  407. "--proxy-client-cert-file=/var/lib/certs/front-proxy-client.crt",
  408. "--proxy-client-key-file=/var/lib/certs/front-proxy-client.key",
  409. "--requestheader-username-headers=X-Remote-User",
  410. "--requestheader-group-headers=X-Remote-Group",
  411. "--requestheader-extra-headers-prefix=X-Remote-Extra-",
  412. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  413. "--requestheader-allowed-names=front-proxy-client",
  414. "--authorization-mode=Node,RBAC",
  415. "--advertise-address=1.2.3.4",
  416. fmt.Sprintf("--etcd-servers=https://127.0.0.1:%d", kubeadmconstants.EtcdListenClientPort),
  417. "--etcd-cafile=" + testCertsDir + "/etcd/ca.crt",
  418. "--etcd-certfile=" + testCertsDir + "/apiserver-etcd-client.crt",
  419. "--etcd-keyfile=" + testCertsDir + "/apiserver-etcd-client.key",
  420. },
  421. },
  422. {
  423. name: "authorization-mode extra-args Webhook",
  424. cfg: &kubeadmapi.ClusterConfiguration{
  425. Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
  426. CertificatesDir: testCertsDir,
  427. APIServer: kubeadmapi.APIServer{
  428. ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
  429. ExtraArgs: map[string]string{
  430. "authorization-mode": authzmodes.ModeWebhook,
  431. },
  432. },
  433. },
  434. },
  435. endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
  436. expected: []string{
  437. "kube-apiserver",
  438. "--insecure-port=0",
  439. "--enable-admission-plugins=NodeRestriction",
  440. "--service-cluster-ip-range=bar",
  441. "--service-account-key-file=" + testCertsDir + "/sa.pub",
  442. "--client-ca-file=" + testCertsDir + "/ca.crt",
  443. "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
  444. "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
  445. "--kubelet-client-certificate=" + testCertsDir + "/apiserver-kubelet-client.crt",
  446. "--kubelet-client-key=" + testCertsDir + "/apiserver-kubelet-client.key",
  447. "--enable-bootstrap-token-auth=true",
  448. "--secure-port=123",
  449. "--allow-privileged=true",
  450. "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
  451. "--proxy-client-cert-file=/var/lib/certs/front-proxy-client.crt",
  452. "--proxy-client-key-file=/var/lib/certs/front-proxy-client.key",
  453. "--requestheader-username-headers=X-Remote-User",
  454. "--requestheader-group-headers=X-Remote-Group",
  455. "--requestheader-extra-headers-prefix=X-Remote-Extra-",
  456. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  457. "--requestheader-allowed-names=front-proxy-client",
  458. "--authorization-mode=Node,RBAC,Webhook",
  459. "--advertise-address=1.2.3.4",
  460. fmt.Sprintf("--etcd-servers=https://127.0.0.1:%d", kubeadmconstants.EtcdListenClientPort),
  461. "--etcd-cafile=" + testCertsDir + "/etcd/ca.crt",
  462. "--etcd-certfile=" + testCertsDir + "/apiserver-etcd-client.crt",
  463. "--etcd-keyfile=" + testCertsDir + "/apiserver-etcd-client.key",
  464. },
  465. },
  466. }
  467. for _, rt := range tests {
  468. t.Run(rt.name, func(t *testing.T) {
  469. actual := getAPIServerCommand(rt.cfg, rt.endpoint)
  470. sort.Strings(actual)
  471. sort.Strings(rt.expected)
  472. if !reflect.DeepEqual(actual, rt.expected) {
  473. errorDiffArguments(t, rt.name, actual, rt.expected)
  474. }
  475. })
  476. }
  477. }
  478. func errorDiffArguments(t *testing.T, name string, actual, expected []string) {
  479. expectedShort := removeCommon(expected, actual)
  480. actualShort := removeCommon(actual, expected)
  481. t.Errorf(
  482. "[%s] failed getAPIServerCommand:\nexpected:\n%v\nsaw:\n%v"+
  483. "\nexpectedShort:\n%v\nsawShort:\n%v\n",
  484. name, expected, actual,
  485. expectedShort, actualShort)
  486. }
  487. // removeCommon removes common items from left list
  488. // makes compairing two cmdline (with lots of arguments) easier
  489. func removeCommon(left, right []string) []string {
  490. origSet := sets.NewString(left...)
  491. origSet.Delete(right...)
  492. return origSet.List()
  493. }
  494. func TestGetControllerManagerCommand(t *testing.T) {
  495. var tests = []struct {
  496. name string
  497. cfg *kubeadmapi.ClusterConfiguration
  498. expected []string
  499. }{
  500. {
  501. name: "custom certs dir for " + cpVersion,
  502. cfg: &kubeadmapi.ClusterConfiguration{
  503. CertificatesDir: testCertsDir,
  504. KubernetesVersion: cpVersion,
  505. },
  506. expected: []string{
  507. "kube-controller-manager",
  508. "--bind-address=127.0.0.1",
  509. "--leader-elect=true",
  510. "--kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  511. "--root-ca-file=" + testCertsDir + "/ca.crt",
  512. "--service-account-private-key-file=" + testCertsDir + "/sa.key",
  513. "--cluster-signing-cert-file=" + testCertsDir + "/ca.crt",
  514. "--cluster-signing-key-file=" + testCertsDir + "/ca.key",
  515. "--use-service-account-credentials=true",
  516. "--controllers=*,bootstrapsigner,tokencleaner",
  517. "--authentication-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  518. "--authorization-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  519. "--client-ca-file=" + testCertsDir + "/ca.crt",
  520. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  521. },
  522. },
  523. {
  524. name: "custom cluster-cidr for " + cpVersion,
  525. cfg: &kubeadmapi.ClusterConfiguration{
  526. Networking: kubeadmapi.Networking{PodSubnet: "10.0.1.15/16"},
  527. CertificatesDir: testCertsDir,
  528. KubernetesVersion: cpVersion,
  529. },
  530. expected: []string{
  531. "kube-controller-manager",
  532. "--bind-address=127.0.0.1",
  533. "--leader-elect=true",
  534. "--kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  535. "--root-ca-file=" + testCertsDir + "/ca.crt",
  536. "--service-account-private-key-file=" + testCertsDir + "/sa.key",
  537. "--cluster-signing-cert-file=" + testCertsDir + "/ca.crt",
  538. "--cluster-signing-key-file=" + testCertsDir + "/ca.key",
  539. "--use-service-account-credentials=true",
  540. "--controllers=*,bootstrapsigner,tokencleaner",
  541. "--authentication-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  542. "--authorization-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  543. "--client-ca-file=" + testCertsDir + "/ca.crt",
  544. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  545. "--allocate-node-cidrs=true",
  546. "--cluster-cidr=10.0.1.15/16",
  547. "--node-cidr-mask-size=24",
  548. },
  549. },
  550. {
  551. name: "custom extra-args for " + cpVersion,
  552. cfg: &kubeadmapi.ClusterConfiguration{
  553. Networking: kubeadmapi.Networking{PodSubnet: "10.0.1.15/16"},
  554. ControllerManager: kubeadmapi.ControlPlaneComponent{
  555. ExtraArgs: map[string]string{"node-cidr-mask-size": "20"},
  556. },
  557. CertificatesDir: testCertsDir,
  558. KubernetesVersion: cpVersion,
  559. },
  560. expected: []string{
  561. "kube-controller-manager",
  562. "--bind-address=127.0.0.1",
  563. "--leader-elect=true",
  564. "--kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  565. "--root-ca-file=" + testCertsDir + "/ca.crt",
  566. "--service-account-private-key-file=" + testCertsDir + "/sa.key",
  567. "--cluster-signing-cert-file=" + testCertsDir + "/ca.crt",
  568. "--cluster-signing-key-file=" + testCertsDir + "/ca.key",
  569. "--use-service-account-credentials=true",
  570. "--controllers=*,bootstrapsigner,tokencleaner",
  571. "--authentication-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  572. "--authorization-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  573. "--client-ca-file=" + testCertsDir + "/ca.crt",
  574. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  575. "--allocate-node-cidrs=true",
  576. "--cluster-cidr=10.0.1.15/16",
  577. "--node-cidr-mask-size=20",
  578. },
  579. },
  580. {
  581. name: "custom IPv6 networking for " + cpVersion,
  582. cfg: &kubeadmapi.ClusterConfiguration{
  583. Networking: kubeadmapi.Networking{PodSubnet: "2001:db8::/64"},
  584. CertificatesDir: testCertsDir,
  585. KubernetesVersion: cpVersion,
  586. },
  587. expected: []string{
  588. "kube-controller-manager",
  589. "--bind-address=127.0.0.1",
  590. "--leader-elect=true",
  591. "--kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  592. "--root-ca-file=" + testCertsDir + "/ca.crt",
  593. "--service-account-private-key-file=" + testCertsDir + "/sa.key",
  594. "--cluster-signing-cert-file=" + testCertsDir + "/ca.crt",
  595. "--cluster-signing-key-file=" + testCertsDir + "/ca.key",
  596. "--use-service-account-credentials=true",
  597. "--controllers=*,bootstrapsigner,tokencleaner",
  598. "--authentication-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  599. "--authorization-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  600. "--client-ca-file=" + testCertsDir + "/ca.crt",
  601. "--requestheader-client-ca-file=" + testCertsDir + "/front-proxy-ca.crt",
  602. "--allocate-node-cidrs=true",
  603. "--cluster-cidr=2001:db8::/64",
  604. "--node-cidr-mask-size=80",
  605. },
  606. },
  607. }
  608. for _, rt := range tests {
  609. t.Run(rt.name, func(t *testing.T) {
  610. actual := getControllerManagerCommand(rt.cfg, version.MustParseSemantic(rt.cfg.KubernetesVersion))
  611. sort.Strings(actual)
  612. sort.Strings(rt.expected)
  613. if !reflect.DeepEqual(actual, rt.expected) {
  614. errorDiffArguments(t, rt.name, actual, rt.expected)
  615. }
  616. })
  617. }
  618. }
  619. func TestCalcNodeCidrSize(t *testing.T) {
  620. tests := []struct {
  621. name string
  622. podSubnet string
  623. expectedPrefix string
  624. }{
  625. {
  626. name: "Malformed pod subnet",
  627. podSubnet: "10.10.10/160",
  628. expectedPrefix: "24",
  629. },
  630. {
  631. name: "V4: Always uses 24",
  632. podSubnet: "10.10.10.10/16",
  633. expectedPrefix: "24",
  634. },
  635. {
  636. name: "V6: Use pod subnet size, when not enough space",
  637. podSubnet: "2001:db8::/128",
  638. expectedPrefix: "128",
  639. },
  640. {
  641. name: "V6: Use pod subnet size, when not enough space",
  642. podSubnet: "2001:db8::/113",
  643. expectedPrefix: "113",
  644. },
  645. {
  646. name: "V6: Special case with 256 nodes",
  647. podSubnet: "2001:db8::/112",
  648. expectedPrefix: "120",
  649. },
  650. {
  651. name: "V6: Using /120 for node CIDR",
  652. podSubnet: "2001:db8::/104",
  653. expectedPrefix: "120",
  654. },
  655. {
  656. name: "V6: Using /112 for node CIDR",
  657. podSubnet: "2001:db8::/103",
  658. expectedPrefix: "112",
  659. },
  660. {
  661. name: "V6: Using /112 for node CIDR",
  662. podSubnet: "2001:db8::/96",
  663. expectedPrefix: "112",
  664. },
  665. {
  666. name: "V6: Using /104 for node CIDR",
  667. podSubnet: "2001:db8::/95",
  668. expectedPrefix: "104",
  669. },
  670. {
  671. name: "V6: For /64 pod net, use /80",
  672. podSubnet: "2001:db8::/64",
  673. expectedPrefix: "80",
  674. },
  675. {
  676. name: "V6: For /48 pod net, use /64",
  677. podSubnet: "2001:db8::/48",
  678. expectedPrefix: "64",
  679. },
  680. {
  681. name: "V6: For /32 pod net, use /48",
  682. podSubnet: "2001:db8::/32",
  683. expectedPrefix: "48",
  684. },
  685. }
  686. for _, test := range tests {
  687. t.Run(test.name, func(t *testing.T) {
  688. actualPrefix := calcNodeCidrSize(test.podSubnet)
  689. if actualPrefix != test.expectedPrefix {
  690. t.Errorf("Case [%s]\nCalc of node CIDR size for pod subnet %q failed: Expected %q, saw %q",
  691. test.name, test.podSubnet, test.expectedPrefix, actualPrefix)
  692. }
  693. })
  694. }
  695. }
  696. func TestGetControllerManagerCommandExternalCA(t *testing.T) {
  697. tests := []struct {
  698. name string
  699. cfg *kubeadmapi.InitConfiguration
  700. caKeyPresent bool
  701. expectedArgFunc func(dir string) []string
  702. }{
  703. {
  704. name: "caKeyPresent-false for " + cpVersion,
  705. cfg: &kubeadmapi.InitConfiguration{
  706. LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
  707. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  708. KubernetesVersion: cpVersion,
  709. Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"},
  710. },
  711. },
  712. caKeyPresent: false,
  713. expectedArgFunc: func(tmpdir string) []string {
  714. return []string{
  715. "kube-controller-manager",
  716. "--bind-address=127.0.0.1",
  717. "--leader-elect=true",
  718. "--kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  719. "--root-ca-file=" + tmpdir + "/ca.crt",
  720. "--service-account-private-key-file=" + tmpdir + "/sa.key",
  721. "--cluster-signing-cert-file=",
  722. "--cluster-signing-key-file=",
  723. "--use-service-account-credentials=true",
  724. "--controllers=*,bootstrapsigner,tokencleaner",
  725. "--authentication-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  726. "--authorization-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  727. "--client-ca-file=" + tmpdir + "/ca.crt",
  728. "--requestheader-client-ca-file=" + tmpdir + "/front-proxy-ca.crt",
  729. }
  730. },
  731. },
  732. {
  733. name: "caKeyPresent true for " + cpVersion,
  734. cfg: &kubeadmapi.InitConfiguration{
  735. LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
  736. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  737. KubernetesVersion: cpVersion,
  738. Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"},
  739. },
  740. },
  741. caKeyPresent: true,
  742. expectedArgFunc: func(tmpdir string) []string {
  743. return []string{
  744. "kube-controller-manager",
  745. "--bind-address=127.0.0.1",
  746. "--leader-elect=true",
  747. "--kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  748. "--root-ca-file=" + tmpdir + "/ca.crt",
  749. "--service-account-private-key-file=" + tmpdir + "/sa.key",
  750. "--cluster-signing-cert-file=" + tmpdir + "/ca.crt",
  751. "--cluster-signing-key-file=" + tmpdir + "/ca.key",
  752. "--use-service-account-credentials=true",
  753. "--controllers=*,bootstrapsigner,tokencleaner",
  754. "--authentication-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  755. "--authorization-kubeconfig=" + kubeadmconstants.KubernetesDir + "/controller-manager.conf",
  756. "--client-ca-file=" + tmpdir + "/ca.crt",
  757. "--requestheader-client-ca-file=" + tmpdir + "/front-proxy-ca.crt",
  758. }
  759. },
  760. },
  761. }
  762. for _, test := range tests {
  763. t.Run(test.name, func(t *testing.T) {
  764. // Create temp folder for the test case
  765. tmpdir := testutil.SetupTempDir(t)
  766. defer os.RemoveAll(tmpdir)
  767. test.cfg.CertificatesDir = tmpdir
  768. if err := certs.CreatePKIAssets(test.cfg); err != nil {
  769. t.Errorf("failed creating pki assets: %v", err)
  770. }
  771. // delete ca.key and front-proxy-ca.key if test.caKeyPresent is false
  772. if !test.caKeyPresent {
  773. if err := os.Remove(filepath.Join(test.cfg.CertificatesDir, kubeadmconstants.CAKeyName)); err != nil {
  774. t.Errorf("failed removing %s: %v", kubeadmconstants.CAKeyName, err)
  775. }
  776. if err := os.Remove(filepath.Join(test.cfg.CertificatesDir, kubeadmconstants.FrontProxyCAKeyName)); err != nil {
  777. t.Errorf("failed removing %s: %v", kubeadmconstants.FrontProxyCAKeyName, err)
  778. }
  779. }
  780. actual := getControllerManagerCommand(&test.cfg.ClusterConfiguration, version.MustParseSemantic(test.cfg.KubernetesVersion))
  781. expected := test.expectedArgFunc(tmpdir)
  782. sort.Strings(actual)
  783. sort.Strings(expected)
  784. if !reflect.DeepEqual(actual, expected) {
  785. errorDiffArguments(t, test.name, actual, expected)
  786. }
  787. })
  788. }
  789. }
  790. func TestGetSchedulerCommand(t *testing.T) {
  791. var tests = []struct {
  792. name string
  793. cfg *kubeadmapi.ClusterConfiguration
  794. expected []string
  795. }{
  796. {
  797. name: "scheduler defaults",
  798. cfg: &kubeadmapi.ClusterConfiguration{},
  799. expected: []string{
  800. "kube-scheduler",
  801. "--bind-address=127.0.0.1",
  802. "--leader-elect=true",
  803. "--kubeconfig=" + kubeadmconstants.KubernetesDir + "/scheduler.conf",
  804. },
  805. },
  806. }
  807. for _, rt := range tests {
  808. t.Run(rt.name, func(t *testing.T) {
  809. actual := getSchedulerCommand(rt.cfg)
  810. sort.Strings(actual)
  811. sort.Strings(rt.expected)
  812. if !reflect.DeepEqual(actual, rt.expected) {
  813. errorDiffArguments(t, rt.name, actual, rt.expected)
  814. }
  815. })
  816. }
  817. }
  818. func TestGetAuthzModes(t *testing.T) {
  819. var tests = []struct {
  820. name string
  821. authMode []string
  822. expected string
  823. }{
  824. {
  825. name: "default if empty",
  826. authMode: []string{},
  827. expected: "Node,RBAC",
  828. },
  829. {
  830. name: "add missing Node",
  831. authMode: []string{authzmodes.ModeRBAC},
  832. expected: "Node,RBAC",
  833. },
  834. {
  835. name: "add missing RBAC",
  836. authMode: []string{authzmodes.ModeNode},
  837. expected: "Node,RBAC",
  838. },
  839. {
  840. name: "add defaults to ABAC",
  841. authMode: []string{authzmodes.ModeABAC},
  842. expected: "Node,RBAC,ABAC",
  843. },
  844. {
  845. name: "add defaults to RBAC+Webhook",
  846. authMode: []string{authzmodes.ModeRBAC, authzmodes.ModeWebhook},
  847. expected: "Node,RBAC,Webhook",
  848. },
  849. {
  850. name: "add default to Webhook",
  851. authMode: []string{authzmodes.ModeWebhook},
  852. expected: "Node,RBAC,Webhook",
  853. },
  854. {
  855. name: "AlwaysAllow ignored",
  856. authMode: []string{authzmodes.ModeAlwaysAllow},
  857. expected: "Node,RBAC",
  858. },
  859. {
  860. name: "AlwaysDeny ignored",
  861. authMode: []string{authzmodes.ModeAlwaysDeny},
  862. expected: "Node,RBAC",
  863. },
  864. {
  865. name: "Unspecified ignored",
  866. authMode: []string{"FooAuthzMode"},
  867. expected: "Node,RBAC",
  868. },
  869. {
  870. name: "Multiple ignored",
  871. authMode: []string{authzmodes.ModeAlwaysAllow, authzmodes.ModeAlwaysDeny, "foo"},
  872. expected: "Node,RBAC",
  873. },
  874. {
  875. name: "all",
  876. authMode: []string{authzmodes.ModeNode, authzmodes.ModeRBAC, authzmodes.ModeWebhook, authzmodes.ModeABAC},
  877. expected: "Node,RBAC,ABAC,Webhook",
  878. },
  879. }
  880. for _, rt := range tests {
  881. t.Run(rt.name, func(t *testing.T) {
  882. actual := getAuthzModes(strings.Join(rt.authMode, ","))
  883. if actual != rt.expected {
  884. t.Errorf("failed getAuthzParameters:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual)
  885. }
  886. })
  887. }
  888. }