pod_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  1. // +build kube
  2. /*
  3. Copyright 2020 Docker Compose CLI authors
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package kubernetes
  15. import (
  16. "fmt"
  17. "os"
  18. "runtime"
  19. "testing"
  20. "github.com/compose-spec/compose-go/loader"
  21. "github.com/compose-spec/compose-go/types"
  22. "github.com/stretchr/testify/assert"
  23. apiv1 "k8s.io/api/core/v1"
  24. "k8s.io/apimachinery/pkg/api/resource"
  25. )
  26. func loadYAML(yaml string) (*loader.Config, error) {
  27. dict, err := loader.ParseYAML([]byte(yaml))
  28. if err != nil {
  29. return nil, err
  30. }
  31. workingDir, err := os.Getwd()
  32. if err != nil {
  33. panic(err)
  34. }
  35. configs := []types.ConfigFiles{}
  36. config := types.ConfigDetails{
  37. WorkingDir: workingDir,
  38. ConfigFiles: configs,
  39. Environment: utils.Environment(),
  40. }
  41. model, err := loader.Load(config)
  42. return model
  43. }
  44. func podTemplate(t *testing.T, yaml string) apiv1.PodTemplateSpec {
  45. res, err := podTemplateWithError(yaml)
  46. assert.NoError(t, err)
  47. return res
  48. }
  49. func podTemplateWithError(yaml string) (apiv1.PodTemplateSpec, error) {
  50. model, err := loadYAML(yaml)
  51. if err != nil {
  52. return apiv1.PodTemplateSpec{}, err
  53. }
  54. return toPodTemplate(model.Services[0], nil, model)
  55. }
  56. func TestToPodWithDockerSocket(t *testing.T) {
  57. if runtime.GOOS == "windows" {
  58. t.Skip("on windows, source path validation is broken (and actually, source validation for windows workload is broken too). Skip it for now, as we don't support it yet")
  59. return
  60. }
  61. podTemplate := podTemplate(t, `
  62. version: "3"
  63. services:
  64. redis:
  65. image: "redis:alpine"
  66. volumes:
  67. - "/var/run/docker.sock:/var/run/docker.sock"
  68. `)
  69. expectedVolume := apiv1.Volume{
  70. Name: "mount-0",
  71. VolumeSource: apiv1.VolumeSource{
  72. HostPath: &apiv1.HostPathVolumeSource{
  73. Path: "/var/run",
  74. },
  75. },
  76. }
  77. expectedMount := apiv1.VolumeMount{
  78. Name: "mount-0",
  79. MountPath: "/var/run/docker.sock",
  80. SubPath: "docker.sock",
  81. }
  82. assert.Len(t, podTemplate.Spec.Volumes, 1)
  83. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  84. assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
  85. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  86. }
  87. func TestToPodWithFunkyCommand(t *testing.T) {
  88. podTemplate := podTemplate(t, `
  89. version: "3"
  90. services:
  91. redis:
  92. image: basi/node-exporter
  93. command: ["-collector.procfs", "/host/proc", "-collector.sysfs", "/host/sys"]
  94. `)
  95. expectedArgs := []string{
  96. `-collector.procfs`,
  97. `/host/proc`, // ?
  98. `-collector.sysfs`,
  99. `/host/sys`, // ?
  100. }
  101. assert.Equal(t, expectedArgs, podTemplate.Spec.Containers[0].Args)
  102. }
  103. func TestToPodWithGlobalVolume(t *testing.T) {
  104. podTemplate := podTemplate(t, `
  105. version: "3"
  106. services:
  107. db:
  108. image: "postgres:9.4"
  109. volumes:
  110. - dbdata:/var/lib/postgresql/data
  111. `)
  112. expectedMount := apiv1.VolumeMount{
  113. Name: "dbdata",
  114. MountPath: "/var/lib/postgresql/data",
  115. }
  116. assert.Len(t, podTemplate.Spec.Volumes, 0)
  117. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  118. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  119. }
  120. func TestToPodWithResources(t *testing.T) {
  121. podTemplate := podTemplate(t, `
  122. version: "3"
  123. services:
  124. db:
  125. image: "postgres:9.4"
  126. deploy:
  127. resources:
  128. limits:
  129. cpus: "0.001"
  130. memory: 50Mb
  131. reservations:
  132. cpus: "0.0001"
  133. memory: 20Mb
  134. `)
  135. expectedResourceRequirements := apiv1.ResourceRequirements{
  136. Limits: map[apiv1.ResourceName]resource.Quantity{
  137. apiv1.ResourceCPU: resource.MustParse("0.001"),
  138. apiv1.ResourceMemory: resource.MustParse(fmt.Sprintf("%d", 50*1024*1024)),
  139. },
  140. Requests: map[apiv1.ResourceName]resource.Quantity{
  141. apiv1.ResourceCPU: resource.MustParse("0.0001"),
  142. apiv1.ResourceMemory: resource.MustParse(fmt.Sprintf("%d", 20*1024*1024)),
  143. },
  144. }
  145. assert.Equal(t, expectedResourceRequirements, podTemplate.Spec.Containers[0].Resources)
  146. }
  147. func TestToPodWithCapabilities(t *testing.T) {
  148. podTemplate := podTemplate(t, `
  149. version: "3"
  150. services:
  151. redis:
  152. image: "redis:alpine"
  153. cap_add:
  154. - ALL
  155. cap_drop:
  156. - NET_ADMIN
  157. - SYS_ADMIN
  158. `)
  159. expectedSecurityContext := &apiv1.SecurityContext{
  160. Capabilities: &apiv1.Capabilities{
  161. Add: []apiv1.Capability{"ALL"},
  162. Drop: []apiv1.Capability{"NET_ADMIN", "SYS_ADMIN"},
  163. },
  164. }
  165. assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext)
  166. }
  167. func TestToPodWithReadOnly(t *testing.T) {
  168. podTemplate := podTemplate(t, `
  169. version: "3"
  170. services:
  171. redis:
  172. image: "redis:alpine"
  173. read_only: true
  174. `)
  175. yes := true
  176. expectedSecurityContext := &apiv1.SecurityContext{
  177. ReadOnlyRootFilesystem: &yes,
  178. }
  179. assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext)
  180. }
  181. func TestToPodWithPrivileged(t *testing.T) {
  182. podTemplate := podTemplate(t, `
  183. version: "3"
  184. services:
  185. redis:
  186. image: "redis:alpine"
  187. privileged: true
  188. `)
  189. yes := true
  190. expectedSecurityContext := &apiv1.SecurityContext{
  191. Privileged: &yes,
  192. }
  193. assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext)
  194. }
  195. func TestToPodWithEnvNilShouldErrorOut(t *testing.T) {
  196. _, err := podTemplateWithError(`
  197. version: "3"
  198. services:
  199. redis:
  200. image: "redis:alpine"
  201. environment:
  202. - SESSION_SECRET
  203. `)
  204. assert.Error(t, err)
  205. }
  206. func TestToPodWithEnv(t *testing.T) {
  207. podTemplate := podTemplate(t, `
  208. version: "3"
  209. services:
  210. redis:
  211. image: "redis:alpine"
  212. environment:
  213. - RACK_ENV=development
  214. - SHOW=true
  215. `)
  216. expectedEnv := []apiv1.EnvVar{
  217. {
  218. Name: "RACK_ENV",
  219. Value: "development",
  220. },
  221. {
  222. Name: "SHOW",
  223. Value: "true",
  224. },
  225. }
  226. assert.Equal(t, expectedEnv, podTemplate.Spec.Containers[0].Env)
  227. }
  228. func TestToPodWithVolume(t *testing.T) {
  229. if runtime.GOOS == "windows" {
  230. t.Skip("on windows, source path validation is broken (and actually, source validation for windows workload is broken too). Skip it for now, as we don't support it yet")
  231. return
  232. }
  233. podTemplate := podTemplate(t, `
  234. version: "3"
  235. services:
  236. nginx:
  237. image: nginx
  238. volumes:
  239. - /ignore:/ignore
  240. - /opt/data:/var/lib/mysql:ro
  241. `)
  242. assert.Len(t, podTemplate.Spec.Volumes, 2)
  243. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 2)
  244. }
  245. func /*FIXME Test*/ ToPodWithRelativeVolumes(t *testing.T) {
  246. if runtime.GOOS == "windows" {
  247. t.Skip("on windows, source path validation is broken (and actually, source validation for windows workload is broken too). Skip it for now, as we don't support it yet")
  248. return
  249. }
  250. _, err := podTemplateWithError(`
  251. version: "3"
  252. services:
  253. nginx:
  254. image: nginx
  255. volumes:
  256. - ./fail:/ignore
  257. `)
  258. assert.Error(t, err)
  259. }
  260. func TestToPodWithHealthCheck(t *testing.T) {
  261. podTemplate := podTemplate(t, `
  262. version: "3"
  263. services:
  264. nginx:
  265. image: nginx
  266. healthcheck:
  267. test: ["CMD", "curl", "-f", "http://localhost"]
  268. interval: 90s
  269. timeout: 10s
  270. retries: 3
  271. `)
  272. expectedLivenessProbe := &apiv1.Probe{
  273. TimeoutSeconds: 10,
  274. PeriodSeconds: 90,
  275. FailureThreshold: 3,
  276. Handler: apiv1.Handler{
  277. Exec: &apiv1.ExecAction{
  278. Command: []string{"curl", "-f", "http://localhost"},
  279. },
  280. },
  281. }
  282. assert.Equal(t, expectedLivenessProbe, podTemplate.Spec.Containers[0].LivenessProbe)
  283. }
  284. func TestToPodWithShellHealthCheck(t *testing.T) {
  285. podTemplate := podTemplate(t, `
  286. version: "3"
  287. services:
  288. nginx:
  289. image: nginx
  290. healthcheck:
  291. test: ["CMD-SHELL", "curl -f http://localhost"]
  292. `)
  293. expectedLivenessProbe := &apiv1.Probe{
  294. TimeoutSeconds: 1,
  295. PeriodSeconds: 1,
  296. FailureThreshold: 3,
  297. Handler: apiv1.Handler{
  298. Exec: &apiv1.ExecAction{
  299. Command: []string{"sh", "-c", "curl -f http://localhost"},
  300. },
  301. },
  302. }
  303. assert.Equal(t, expectedLivenessProbe, podTemplate.Spec.Containers[0].LivenessProbe)
  304. }
  305. func TestToPodWithTargetlessExternalSecret(t *testing.T) {
  306. podTemplate := podTemplate(t, `
  307. version: "3"
  308. services:
  309. nginx:
  310. image: nginx
  311. secrets:
  312. - my_secret
  313. `)
  314. expectedVolume := apiv1.Volume{
  315. Name: "secret-0",
  316. VolumeSource: apiv1.VolumeSource{
  317. Secret: &apiv1.SecretVolumeSource{
  318. SecretName: "my_secret",
  319. Items: []apiv1.KeyToPath{
  320. {
  321. Key: "file", // TODO: This is the key we assume external secrets use
  322. Path: "secret-0",
  323. },
  324. },
  325. },
  326. },
  327. }
  328. expectedMount := apiv1.VolumeMount{
  329. Name: "secret-0",
  330. ReadOnly: true,
  331. MountPath: "/run/secrets/my_secret",
  332. SubPath: "secret-0",
  333. }
  334. assert.Len(t, podTemplate.Spec.Volumes, 1)
  335. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  336. assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
  337. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  338. }
  339. func TestToPodWithExternalSecret(t *testing.T) {
  340. podTemplate := podTemplate(t, `
  341. version: "3"
  342. services:
  343. nginx:
  344. image: nginx
  345. secrets:
  346. - source: my_secret
  347. target: nginx_secret
  348. `)
  349. expectedVolume := apiv1.Volume{
  350. Name: "secret-0",
  351. VolumeSource: apiv1.VolumeSource{
  352. Secret: &apiv1.SecretVolumeSource{
  353. SecretName: "my_secret",
  354. Items: []apiv1.KeyToPath{
  355. {
  356. Key: "file", // TODO: This is the key we assume external secrets use
  357. Path: "secret-0",
  358. },
  359. },
  360. },
  361. },
  362. }
  363. expectedMount := apiv1.VolumeMount{
  364. Name: "secret-0",
  365. ReadOnly: true,
  366. MountPath: "/run/secrets/nginx_secret",
  367. SubPath: "secret-0",
  368. }
  369. assert.Len(t, podTemplate.Spec.Volumes, 1)
  370. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  371. assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
  372. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  373. }
  374. func /*FIXME Test*/ ToPodWithFileBasedSecret(t *testing.T) {
  375. podTemplate := podTemplate(t, `
  376. version: "3"
  377. services:
  378. nginx:
  379. image: nginx
  380. secrets:
  381. - source: my_secret
  382. secrets:
  383. my_secret:
  384. file: ./secret.txt
  385. `)
  386. expectedVolume := apiv1.Volume{
  387. Name: "secret-0",
  388. VolumeSource: apiv1.VolumeSource{
  389. Secret: &apiv1.SecretVolumeSource{
  390. SecretName: "my_secret",
  391. Items: []apiv1.KeyToPath{
  392. {
  393. Key: "secret.txt",
  394. Path: "secret-0",
  395. },
  396. },
  397. },
  398. },
  399. }
  400. expectedMount := apiv1.VolumeMount{
  401. Name: "secret-0",
  402. ReadOnly: true,
  403. MountPath: "/run/secrets/my_secret",
  404. SubPath: "secret-0",
  405. }
  406. assert.Len(t, podTemplate.Spec.Volumes, 1)
  407. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  408. assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
  409. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  410. }
  411. func /*FIXME Test*/ ToPodWithTwoFileBasedSecrets(t *testing.T) {
  412. podTemplate := podTemplate(t, `
  413. version: "3"
  414. services:
  415. nginx:
  416. image: nginx
  417. secrets:
  418. - source: my_secret1
  419. - source: my_secret2
  420. target: secret2
  421. secrets:
  422. my_secret1:
  423. file: ./secret1.txt
  424. my_secret2:
  425. file: ./secret2.txt
  426. `)
  427. expectedVolumes := []apiv1.Volume{
  428. {
  429. Name: "secret-0",
  430. VolumeSource: apiv1.VolumeSource{
  431. Secret: &apiv1.SecretVolumeSource{
  432. SecretName: "my_secret1",
  433. Items: []apiv1.KeyToPath{
  434. {
  435. Key: "secret1.txt",
  436. Path: "secret-0",
  437. },
  438. },
  439. },
  440. },
  441. },
  442. {
  443. Name: "secret-1",
  444. VolumeSource: apiv1.VolumeSource{
  445. Secret: &apiv1.SecretVolumeSource{
  446. SecretName: "my_secret2",
  447. Items: []apiv1.KeyToPath{
  448. {
  449. Key: "secret2.txt",
  450. Path: "secret-1",
  451. },
  452. },
  453. },
  454. },
  455. },
  456. }
  457. expectedMounts := []apiv1.VolumeMount{
  458. {
  459. Name: "secret-0",
  460. ReadOnly: true,
  461. MountPath: "/run/secrets/my_secret1",
  462. SubPath: "secret-0",
  463. },
  464. {
  465. Name: "secret-1",
  466. ReadOnly: true,
  467. MountPath: "/run/secrets/secret2",
  468. SubPath: "secret-1",
  469. },
  470. }
  471. assert.Equal(t, expectedVolumes, podTemplate.Spec.Volumes)
  472. assert.Equal(t, expectedMounts, podTemplate.Spec.Containers[0].VolumeMounts)
  473. }
  474. func TestToPodWithTerminationGracePeriod(t *testing.T) {
  475. podTemplate := podTemplate(t, `
  476. version: "3"
  477. services:
  478. redis:
  479. image: "redis:alpine"
  480. stop_grace_period: 100s
  481. `)
  482. expected := int64(100)
  483. assert.Equal(t, &expected, podTemplate.Spec.TerminationGracePeriodSeconds)
  484. }
  485. func TestToPodWithTmpfs(t *testing.T) {
  486. podTemplate := podTemplate(t, `
  487. version: "3"
  488. services:
  489. redis:
  490. image: "redis:alpine"
  491. tmpfs:
  492. - /tmp
  493. `)
  494. expectedVolume := apiv1.Volume{
  495. Name: "tmp-0",
  496. VolumeSource: apiv1.VolumeSource{
  497. EmptyDir: &apiv1.EmptyDirVolumeSource{
  498. Medium: "Memory",
  499. },
  500. },
  501. }
  502. expectedMount := apiv1.VolumeMount{
  503. Name: "tmp-0",
  504. MountPath: "/tmp",
  505. }
  506. assert.Len(t, podTemplate.Spec.Volumes, 1)
  507. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  508. assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
  509. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  510. }
  511. func TestToPodWithNumericalUser(t *testing.T) {
  512. podTemplate := podTemplate(t, `
  513. version: "3"
  514. services:
  515. redis:
  516. image: "redis:alpine"
  517. user: "1000"
  518. `)
  519. userID := int64(1000)
  520. expectedSecurityContext := &apiv1.SecurityContext{
  521. RunAsUser: &userID,
  522. }
  523. assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext)
  524. }
  525. func TestToPodWithGitVolume(t *testing.T) {
  526. podTemplate := podTemplate(t, `
  527. version: "3"
  528. services:
  529. redis:
  530. image: "redis:alpine"
  531. volumes:
  532. - source: "[email protected]:moby/moby.git"
  533. target: /sources
  534. type: git
  535. `)
  536. expectedVolume := apiv1.Volume{
  537. Name: "mount-0",
  538. VolumeSource: apiv1.VolumeSource{
  539. GitRepo: &apiv1.GitRepoVolumeSource{
  540. Repository: "[email protected]:moby/moby.git",
  541. },
  542. },
  543. }
  544. expectedMount := apiv1.VolumeMount{
  545. Name: "mount-0",
  546. ReadOnly: false,
  547. MountPath: "/sources",
  548. }
  549. assert.Len(t, podTemplate.Spec.Volumes, 1)
  550. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  551. assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
  552. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  553. }
  554. func /*FIXME Test*/ ToPodWithFileBasedConfig(t *testing.T) {
  555. podTemplate := podTemplate(t, `
  556. version: "3"
  557. services:
  558. redis:
  559. image: "redis:alpine"
  560. configs:
  561. - source: my_config
  562. target: /usr/share/nginx/html/index.html
  563. uid: "103"
  564. gid: "103"
  565. mode: 0440
  566. configs:
  567. my_config:
  568. file: ./file.html
  569. `)
  570. mode := int32(0440)
  571. expectedVolume := apiv1.Volume{
  572. Name: "config-0",
  573. VolumeSource: apiv1.VolumeSource{
  574. ConfigMap: &apiv1.ConfigMapVolumeSource{
  575. LocalObjectReference: apiv1.LocalObjectReference{
  576. Name: "my_config",
  577. },
  578. Items: []apiv1.KeyToPath{
  579. {
  580. Key: "file.html",
  581. Path: "config-0",
  582. Mode: &mode,
  583. },
  584. },
  585. },
  586. },
  587. }
  588. expectedMount := apiv1.VolumeMount{
  589. Name: "config-0",
  590. ReadOnly: true,
  591. MountPath: "/usr/share/nginx/html/index.html",
  592. SubPath: "config-0",
  593. }
  594. assert.Len(t, podTemplate.Spec.Volumes, 1)
  595. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  596. assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
  597. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  598. }
  599. func /*FIXME Test*/ ToPodWithTargetlessFileBasedConfig(t *testing.T) {
  600. podTemplate := podTemplate(t, `
  601. version: "3"
  602. services:
  603. redis:
  604. image: "redis:alpine"
  605. configs:
  606. - my_config
  607. configs:
  608. my_config:
  609. file: ./file.html
  610. `)
  611. expectedVolume := apiv1.Volume{
  612. Name: "config-0",
  613. VolumeSource: apiv1.VolumeSource{
  614. ConfigMap: &apiv1.ConfigMapVolumeSource{
  615. LocalObjectReference: apiv1.LocalObjectReference{
  616. Name: "myconfig",
  617. },
  618. Items: []apiv1.KeyToPath{
  619. {
  620. Key: "file.html",
  621. Path: "config-0",
  622. },
  623. },
  624. },
  625. },
  626. }
  627. expectedMount := apiv1.VolumeMount{
  628. Name: "config-0",
  629. ReadOnly: true,
  630. MountPath: "/myconfig",
  631. SubPath: "config-0",
  632. }
  633. assert.Len(t, podTemplate.Spec.Volumes, 1)
  634. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  635. assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
  636. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  637. }
  638. func TestToPodWithExternalConfig(t *testing.T) {
  639. podTemplate := podTemplate(t, `
  640. version: "3"
  641. services:
  642. redis:
  643. image: "redis:alpine"
  644. configs:
  645. - source: my_config
  646. target: /usr/share/nginx/html/index.html
  647. uid: "103"
  648. gid: "103"
  649. mode: 0440
  650. configs:
  651. my_config:
  652. external: true
  653. `)
  654. mode := int32(0440)
  655. expectedVolume := apiv1.Volume{
  656. Name: "config-0",
  657. VolumeSource: apiv1.VolumeSource{
  658. ConfigMap: &apiv1.ConfigMapVolumeSource{
  659. LocalObjectReference: apiv1.LocalObjectReference{
  660. Name: "my_config",
  661. },
  662. Items: []apiv1.KeyToPath{
  663. {
  664. Key: "file", // TODO: This is the key we assume external config use
  665. Path: "config-0",
  666. Mode: &mode,
  667. },
  668. },
  669. },
  670. },
  671. }
  672. expectedMount := apiv1.VolumeMount{
  673. Name: "config-0",
  674. ReadOnly: true,
  675. MountPath: "/usr/share/nginx/html/index.html",
  676. SubPath: "config-0",
  677. }
  678. assert.Len(t, podTemplate.Spec.Volumes, 1)
  679. assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
  680. assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
  681. assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
  682. }
  683. func /*FIXME Test*/ ToPodWithTwoConfigsSameMountPoint(t *testing.T) {
  684. podTemplate := podTemplate(t, `
  685. version: "3"
  686. services:
  687. nginx:
  688. image: nginx
  689. configs:
  690. - source: first
  691. target: /data/first.json
  692. mode: "0440"
  693. - source: second
  694. target: /data/second.json
  695. mode: "0550"
  696. configs:
  697. first:
  698. file: ./file1
  699. secondv:
  700. file: ./file2
  701. `)
  702. mode0440 := int32(0440)
  703. mode0550 := int32(0550)
  704. expectedVolumes := []apiv1.Volume{
  705. {
  706. Name: "config-0",
  707. VolumeSource: apiv1.VolumeSource{
  708. ConfigMap: &apiv1.ConfigMapVolumeSource{
  709. LocalObjectReference: apiv1.LocalObjectReference{
  710. Name: "first",
  711. },
  712. Items: []apiv1.KeyToPath{
  713. {
  714. Key: "file1",
  715. Path: "config-0",
  716. Mode: &mode0440,
  717. },
  718. },
  719. },
  720. },
  721. },
  722. {
  723. Name: "config-1",
  724. VolumeSource: apiv1.VolumeSource{
  725. ConfigMap: &apiv1.ConfigMapVolumeSource{
  726. LocalObjectReference: apiv1.LocalObjectReference{
  727. Name: "second",
  728. },
  729. Items: []apiv1.KeyToPath{
  730. {
  731. Key: "file2",
  732. Path: "config-1",
  733. Mode: &mode0550,
  734. },
  735. },
  736. },
  737. },
  738. },
  739. }
  740. expectedMounts := []apiv1.VolumeMount{
  741. {
  742. Name: "config-0",
  743. ReadOnly: true,
  744. MountPath: "/data/first.json",
  745. SubPath: "config-0",
  746. },
  747. {
  748. Name: "config-1",
  749. ReadOnly: true,
  750. MountPath: "/data/second.json",
  751. SubPath: "config-1",
  752. },
  753. }
  754. assert.Equal(t, expectedVolumes, podTemplate.Spec.Volumes)
  755. assert.Equal(t, expectedMounts, podTemplate.Spec.Containers[0].VolumeMounts)
  756. }
  757. func TestToPodWithTwoExternalConfigsSameMountPoint(t *testing.T) {
  758. podTemplate := podTemplate(t, `
  759. version: "3"
  760. services:
  761. nginx:
  762. image: nginx
  763. configs:
  764. - source: first
  765. target: /data/first.json
  766. - source: second
  767. target: /data/second.json
  768. configs:
  769. first:
  770. file: ./file1
  771. second:
  772. file: ./file2
  773. `)
  774. expectedVolumes := []apiv1.Volume{
  775. {
  776. Name: "config-0",
  777. VolumeSource: apiv1.VolumeSource{
  778. ConfigMap: &apiv1.ConfigMapVolumeSource{
  779. LocalObjectReference: apiv1.LocalObjectReference{
  780. Name: "first",
  781. },
  782. Items: []apiv1.KeyToPath{
  783. {
  784. Key: "file",
  785. Path: "config-0",
  786. },
  787. },
  788. },
  789. },
  790. },
  791. {
  792. Name: "config-1",
  793. VolumeSource: apiv1.VolumeSource{
  794. ConfigMap: &apiv1.ConfigMapVolumeSource{
  795. LocalObjectReference: apiv1.LocalObjectReference{
  796. Name: "second",
  797. },
  798. Items: []apiv1.KeyToPath{
  799. {
  800. Key: "file",
  801. Path: "config-1",
  802. },
  803. },
  804. },
  805. },
  806. },
  807. }
  808. expectedMounts := []apiv1.VolumeMount{
  809. {
  810. Name: "config-0",
  811. ReadOnly: true,
  812. MountPath: "/data/first.json",
  813. SubPath: "config-0",
  814. },
  815. {
  816. Name: "config-1",
  817. ReadOnly: true,
  818. MountPath: "/data/second.json",
  819. SubPath: "config-1",
  820. },
  821. }
  822. assert.Equal(t, expectedVolumes, podTemplate.Spec.Volumes)
  823. assert.Equal(t, expectedMounts, podTemplate.Spec.Containers[0].VolumeMounts)
  824. }
  825. func /*FIXME Test*/ ToPodWithPullSecret(t *testing.T) {
  826. podTemplateWithSecret := podTemplate(t, `
  827. version: "3"
  828. services:
  829. nginx:
  830. image: nginx
  831. x-kubernetes.pull-secret: test-pull-secret
  832. `)
  833. assert.Equal(t, 1, len(podTemplateWithSecret.Spec.ImagePullSecrets))
  834. assert.Equal(t, "test-pull-secret", podTemplateWithSecret.Spec.ImagePullSecrets[0].Name)
  835. podTemplateNoSecret := podTemplate(t, `
  836. version: "3"
  837. services:
  838. nginx:
  839. image: nginx
  840. `)
  841. assert.Nil(t, podTemplateNoSecret.Spec.ImagePullSecrets)
  842. }
  843. func /*FIXME Test*/ ToPodWithPullPolicy(t *testing.T) {
  844. cases := []struct {
  845. name string
  846. stack string
  847. expectedPolicy apiv1.PullPolicy
  848. expectedError string
  849. }{
  850. {
  851. name: "specific tag",
  852. stack: `
  853. version: "3"
  854. services:
  855. nginx:
  856. image: nginx:specific
  857. `,
  858. expectedPolicy: apiv1.PullIfNotPresent,
  859. },
  860. {
  861. name: "latest tag",
  862. stack: `
  863. version: "3"
  864. services:
  865. nginx:
  866. image: nginx:latest
  867. `,
  868. expectedPolicy: apiv1.PullAlways,
  869. },
  870. {
  871. name: "explicit policy",
  872. stack: `
  873. version: "3"
  874. services:
  875. nginx:
  876. image: nginx:specific
  877. x-kubernetes.pull-policy: Never
  878. `,
  879. expectedPolicy: apiv1.PullNever,
  880. },
  881. {
  882. name: "invalid policy",
  883. stack: `
  884. version: "3"
  885. services:
  886. nginx:
  887. image: nginx:specific
  888. x-kubernetes.pull-policy: Invalid
  889. `,
  890. expectedError: `invalid pull policy "Invalid", must be "Always", "IfNotPresent" or "Never"`,
  891. },
  892. }
  893. for _, c := range cases {
  894. t.Run(c.name, func(t *testing.T) {
  895. pod, err := podTemplateWithError(c.stack)
  896. if c.expectedError != "" {
  897. assert.EqualError(t, err, c.expectedError)
  898. } else {
  899. assert.NoError(t, err)
  900. assert.Equal(t, pod.Spec.Containers[0].ImagePullPolicy, c.expectedPolicy)
  901. }
  902. })
  903. }
  904. }