pod_test.go 22 KB

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