12
"github.com/containers/podman/v5/pkg/systemd/parser"
13
. "github.com/containers/podman/v5/test/utils"
14
"github.com/containers/podman/v5/version"
15
"github.com/mattn/go-shellwords"
17
. "github.com/onsi/ginkgo/v2"
18
. "github.com/onsi/gomega"
19
. "github.com/onsi/gomega/gexec"
22
type quadletTestcase struct {
28
// Converts "foo@bar.container" to "foo@.container"
29
func getGenericTemplateFile(fileName string) (bool, string) {
30
extension := filepath.Ext(fileName)
31
base := strings.TrimSuffix(fileName, extension)
32
parts := strings.SplitN(base, "@", 2)
33
if len(parts) == 2 && len(parts[1]) > 0 {
34
return true, parts[0] + "@" + extension
39
func loadQuadletTestcase(path string) *quadletTestcase {
40
data, err := os.ReadFile(path)
41
Expect(err).ToNot(HaveOccurred())
43
base := filepath.Base(path)
44
ext := filepath.Ext(base)
45
service := base[:len(base)-len(ext)]
58
checks := make([][]string, 0)
60
for _, line := range strings.Split(string(data), "\n") {
61
if strings.HasPrefix(line, "##") {
62
words, err := shellwords.Parse(line[2:])
63
Expect(err).ToNot(HaveOccurred())
64
checks = append(checks, words)
68
return &quadletTestcase{
75
func matchSublistAt(full []string, pos int, sublist []string) bool {
76
if len(sublist) > len(full)-pos {
80
for i := range sublist {
81
if sublist[i] != full[pos+i] {
88
func matchSublistRegexAt(full []string, pos int, sublist []string) bool {
89
if len(sublist) > len(full)-pos {
93
for i := range sublist {
94
matched, err := regexp.MatchString(sublist[i], full[pos+i])
95
if err != nil || !matched {
102
func findSublist(full []string, sublist []string) int {
103
if len(sublist) > len(full) {
106
if len(sublist) == 0 {
109
for i := 0; i < len(full)-len(sublist)+1; i++ {
110
if matchSublistAt(full, i, sublist) {
117
func findSublistRegex(full []string, sublist []string) int {
118
if len(sublist) > len(full) {
121
if len(sublist) == 0 {
124
for i := 0; i < len(full)-len(sublist)+1; i++ {
125
if matchSublistRegexAt(full, i, sublist) {
132
func (t *quadletTestcase) assertStdErrContains(args []string, session *PodmanSessionIntegration) bool {
133
return strings.Contains(session.ErrorToString(), args[0])
136
func (t *quadletTestcase) assertKeyIs(args []string, unit *parser.UnitFile) bool {
137
Expect(len(args)).To(BeNumerically(">=", 3))
142
realValues := unit.LookupAll(group, key)
143
if len(realValues) != len(values) {
147
for i := range realValues {
148
if realValues[i] != values[i] {
155
func (t *quadletTestcase) assertKeyIsRegex(args []string, unit *parser.UnitFile) bool {
156
Expect(len(args)).To(BeNumerically(">=", 3))
161
realValues := unit.LookupAll(group, key)
162
if len(realValues) != len(values) {
166
for i := range realValues {
167
matched, _ := regexp.MatchString(values[i], realValues[i])
175
func (t *quadletTestcase) assertKeyContains(args []string, unit *parser.UnitFile) bool {
176
Expect(args).To(HaveLen(3))
181
realValue, ok := unit.LookupLast(group, key)
182
return ok && strings.Contains(realValue, value)
185
func (t *quadletTestcase) assertPodmanArgs(args []string, unit *parser.UnitFile, key string, allowRegex, globalOnly bool) bool {
186
podmanArgs, _ := unit.LookupLastArgs("Service", key)
188
podmanCmdLocation := findSublist(podmanArgs, []string{args[0]})
189
if podmanCmdLocation == -1 {
193
podmanArgs = podmanArgs[:podmanCmdLocation]
199
location = findSublistRegex(podmanArgs, args)
201
location = findSublist(podmanArgs, args)
204
return location != -1
207
func keyValueStringToMap(keyValueString, separator string) (map[string]string, error) {
208
keyValMap := make(map[string]string)
209
csvReader := csv.NewReader(strings.NewReader(keyValueString))
210
csvReader.Comma = []rune(separator)[0]
211
keyVarList, err := csvReader.ReadAll()
215
for _, param := range keyVarList[0] {
216
key, val, _ := strings.Cut(param, "=")
220
return keyValMap, nil
223
func keyValMapEqualRegex(expectedKeyValMap, actualKeyValMap map[string]string) bool {
224
if len(expectedKeyValMap) != len(actualKeyValMap) {
227
for key, expectedValue := range expectedKeyValMap {
228
actualValue, ok := actualKeyValMap[key]
232
matched, err := regexp.MatchString(expectedValue, actualValue)
233
if err != nil || !matched {
240
func (t *quadletTestcase) assertPodmanArgsKeyVal(args []string, unit *parser.UnitFile, key string, allowRegex, globalOnly bool) bool {
241
podmanArgs, _ := unit.LookupLastArgs("Service", key)
244
podmanCmdLocation := findSublist(podmanArgs, []string{args[0]})
245
if podmanCmdLocation == -1 {
249
podmanArgs = podmanArgs[:podmanCmdLocation]
253
expectedKeyValMap, err := keyValueStringToMap(args[2], args[1])
259
subListLocation := findSublist(podmanArgs[argKeyLocation:], []string{args[0]})
260
if subListLocation == -1 {
264
argKeyLocation += subListLocation
265
actualKeyValMap, err := keyValueStringToMap(podmanArgs[argKeyLocation+1], args[1])
270
if keyValMapEqualRegex(expectedKeyValMap, actualKeyValMap) {
273
} else if reflect.DeepEqual(expectedKeyValMap, actualKeyValMap) {
279
if argKeyLocation > len(podmanArgs) {
287
func (t *quadletTestcase) assertPodmanFinalArgs(args []string, unit *parser.UnitFile, key string) bool {
288
podmanArgs, _ := unit.LookupLastArgs("Service", key)
289
if len(podmanArgs) < len(args) {
292
return matchSublistAt(podmanArgs, len(podmanArgs)-len(args), args)
295
func (t *quadletTestcase) assertPodmanFinalArgsRegex(args []string, unit *parser.UnitFile, key string) bool {
296
podmanArgs, _ := unit.LookupLastArgs("Service", key)
297
if len(podmanArgs) < len(args) {
300
return matchSublistRegexAt(podmanArgs, len(podmanArgs)-len(args), args)
303
func (t *quadletTestcase) assertStartPodmanArgs(args []string, unit *parser.UnitFile) bool {
304
return t.assertPodmanArgs(args, unit, "ExecStart", false, false)
307
func (t *quadletTestcase) assertStartPodmanArgsRegex(args []string, unit *parser.UnitFile) bool {
308
return t.assertPodmanArgs(args, unit, "ExecStart", true, false)
311
func (t *quadletTestcase) assertStartPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool {
312
return t.assertPodmanArgs(args, unit, "ExecStart", false, true)
315
func (t *quadletTestcase) assertStartPodmanGlobalArgsRegex(args []string, unit *parser.UnitFile) bool {
316
return t.assertPodmanArgs(args, unit, "ExecStart", true, true)
319
func (t *quadletTestcase) assertStartPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool {
320
return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", false, false)
323
func (t *quadletTestcase) assertStartPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
324
return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", true, false)
327
func (t *quadletTestcase) assertStartPodmanGlobalArgsKeyVal(args []string, unit *parser.UnitFile) bool {
328
return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", false, true)
331
func (t *quadletTestcase) assertStartPodmanGlobalArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
332
return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", true, true)
335
func (t *quadletTestcase) assertStartPodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
336
return t.assertPodmanFinalArgs(args, unit, "ExecStart")
339
func (t *quadletTestcase) assertStartPodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool {
340
return t.assertPodmanFinalArgsRegex(args, unit, "ExecStart")
343
func (t *quadletTestcase) assertStartPrePodmanArgs(args []string, unit *parser.UnitFile) bool {
344
return t.assertPodmanArgs(args, unit, "ExecStartPre", false, false)
347
func (t *quadletTestcase) assertStartPrePodmanArgsRegex(args []string, unit *parser.UnitFile) bool {
348
return t.assertPodmanArgs(args, unit, "ExecStartPre", true, false)
351
func (t *quadletTestcase) assertStartPrePodmanGlobalArgs(args []string, unit *parser.UnitFile) bool {
352
return t.assertPodmanArgs(args, unit, "ExecStartPre", false, true)
355
func (t *quadletTestcase) assertStartPrePodmanGlobalArgsRegex(args []string, unit *parser.UnitFile) bool {
356
return t.assertPodmanArgs(args, unit, "ExecStartPre", true, true)
359
func (t *quadletTestcase) assertStartPrePodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool {
360
return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", false, false)
363
func (t *quadletTestcase) assertStartPrePodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
364
return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", true, false)
367
func (t *quadletTestcase) assertStartPrePodmanGlobalArgsKeyVal(args []string, unit *parser.UnitFile) bool {
368
return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", false, true)
371
func (t *quadletTestcase) assertStartPrePodmanGlobalArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
372
return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", true, true)
375
func (t *quadletTestcase) assertStartPrePodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
376
return t.assertPodmanFinalArgs(args, unit, "ExecStartPre")
379
func (t *quadletTestcase) assertStartPrePodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool {
380
return t.assertPodmanFinalArgsRegex(args, unit, "ExecStartPre")
383
func (t *quadletTestcase) assertStopPodmanArgs(args []string, unit *parser.UnitFile) bool {
384
return t.assertPodmanArgs(args, unit, "ExecStop", false, false)
387
func (t *quadletTestcase) assertStopPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool {
388
return t.assertPodmanArgs(args, unit, "ExecStop", false, true)
391
func (t *quadletTestcase) assertStopPodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
392
return t.assertPodmanFinalArgs(args, unit, "ExecStop")
395
func (t *quadletTestcase) assertStopPodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool {
396
return t.assertPodmanFinalArgsRegex(args, unit, "ExecStop")
399
func (t *quadletTestcase) assertStopPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool {
400
return t.assertPodmanArgsKeyVal(args, unit, "ExecStop", false, false)
403
func (t *quadletTestcase) assertStopPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
404
return t.assertPodmanArgsKeyVal(args, unit, "ExecStop", true, false)
407
func (t *quadletTestcase) assertStopPostPodmanArgs(args []string, unit *parser.UnitFile) bool {
408
return t.assertPodmanArgs(args, unit, "ExecStopPost", false, false)
411
func (t *quadletTestcase) assertStopPostPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool {
412
return t.assertPodmanArgs(args, unit, "ExecStopPost", false, true)
415
func (t *quadletTestcase) assertStopPostPodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
416
return t.assertPodmanFinalArgs(args, unit, "ExecStopPost")
419
func (t *quadletTestcase) assertStopPostPodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool {
420
return t.assertPodmanFinalArgsRegex(args, unit, "ExecStopPost")
423
func (t *quadletTestcase) assertStopPostPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool {
424
return t.assertPodmanArgsKeyVal(args, unit, "ExecStopPost", false, false)
427
func (t *quadletTestcase) assertStopPostPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
428
return t.assertPodmanArgsKeyVal(args, unit, "ExecStopPost", true, false)
431
func (t *quadletTestcase) assertSymlink(args []string, unit *parser.UnitFile) bool {
432
Expect(args).To(HaveLen(2))
434
expectedTarget := args[1]
436
dir := filepath.Dir(unit.Path)
438
target, err := os.Readlink(filepath.Join(dir, symlink))
439
Expect(err).ToNot(HaveOccurred())
441
return expectedTarget == target
444
func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, session *PodmanSessionIntegration) error {
445
Expect(check).ToNot(BeEmpty())
447
args := make([]string, 0)
448
for _, a := range check[1:] {
449
// Apply \n and \t as they are used in the testcases
450
a = strings.ReplaceAll(a, "\\n", "\n")
451
a = strings.ReplaceAll(a, "\\t", "\t")
452
args = append(args, a)
462
case "assert-failed":
463
ok = true /* Handled separately */
464
case "assert-stderr-contains":
465
ok = t.assertStdErrContains(args, session)
466
case "assert-key-is":
467
ok = t.assertKeyIs(args, unit)
468
case "assert-key-is-regex":
469
ok = t.assertKeyIsRegex(args, unit)
470
case "assert-key-contains":
471
ok = t.assertKeyContains(args, unit)
472
case "assert-podman-args":
473
ok = t.assertStartPodmanArgs(args, unit)
474
case "assert-podman-args-regex":
475
ok = t.assertStartPodmanArgsRegex(args, unit)
476
case "assert-podman-args-key-val":
477
ok = t.assertStartPodmanArgsKeyVal(args, unit)
478
case "assert-podman-args-key-val-regex":
479
ok = t.assertStartPodmanArgsKeyValRegex(args, unit)
480
case "assert-podman-global-args":
481
ok = t.assertStartPodmanGlobalArgs(args, unit)
482
case "assert-podman-global-args-regex":
483
ok = t.assertStartPodmanGlobalArgsRegex(args, unit)
484
case "assert-podman-global-args-key-val":
485
ok = t.assertStartPodmanGlobalArgsKeyVal(args, unit)
486
case "assert-podman-global-args-key-val-regex":
487
ok = t.assertStartPodmanGlobalArgsKeyValRegex(args, unit)
488
case "assert-podman-final-args":
489
ok = t.assertStartPodmanFinalArgs(args, unit)
490
case "assert-podman-final-args-regex":
491
ok = t.assertStartPodmanFinalArgsRegex(args, unit)
492
case "assert-podman-pre-args":
493
ok = t.assertStartPrePodmanArgs(args, unit)
494
case "assert-podman-pre-args-regex":
495
ok = t.assertStartPrePodmanArgsRegex(args, unit)
496
case "assert-podman-pre-args-key-val":
497
ok = t.assertStartPrePodmanArgsKeyVal(args, unit)
498
case "assert-podman-pre-args-key-val-regex":
499
ok = t.assertStartPrePodmanArgsKeyValRegex(args, unit)
500
case "assert-podman-pre-global-args":
501
ok = t.assertStartPrePodmanGlobalArgs(args, unit)
502
case "assert-podman-pre-global-args-regex":
503
ok = t.assertStartPrePodmanGlobalArgsRegex(args, unit)
504
case "assert-podman-pre-global-args-key-val":
505
ok = t.assertStartPrePodmanGlobalArgsKeyVal(args, unit)
506
case "assert-podman-pre-global-args-key-val-regex":
507
ok = t.assertStartPrePodmanGlobalArgsKeyValRegex(args, unit)
508
case "assert-podman-pre-final-args":
509
ok = t.assertStartPrePodmanFinalArgs(args, unit)
510
case "assert-podman-pre-final-args-regex":
511
ok = t.assertStartPrePodmanFinalArgsRegex(args, unit)
512
case "assert-symlink":
513
ok = t.assertSymlink(args, unit)
514
case "assert-podman-stop-args":
515
ok = t.assertStopPodmanArgs(args, unit)
516
case "assert-podman-stop-global-args":
517
ok = t.assertStopPodmanGlobalArgs(args, unit)
518
case "assert-podman-stop-final-args":
519
ok = t.assertStopPodmanFinalArgs(args, unit)
520
case "assert-podman-stop-final-args-regex":
521
ok = t.assertStopPodmanFinalArgsRegex(args, unit)
522
case "assert-podman-stop-args-key-val":
523
ok = t.assertStopPodmanArgsKeyVal(args, unit)
524
case "assert-podman-stop-args-key-val-regex":
525
ok = t.assertStopPodmanArgsKeyValRegex(args, unit)
526
case "assert-podman-stop-post-args":
527
ok = t.assertStopPostPodmanArgs(args, unit)
528
case "assert-podman-stop-post-global-args":
529
ok = t.assertStopPostPodmanGlobalArgs(args, unit)
530
case "assert-podman-stop-post-final-args":
531
ok = t.assertStopPostPodmanFinalArgs(args, unit)
532
case "assert-podman-stop-post-final-args-regex":
533
ok = t.assertStopPostPodmanFinalArgsRegex(args, unit)
534
case "assert-podman-stop-post-args-key-val":
535
ok = t.assertStopPostPodmanArgsKeyVal(args, unit)
536
case "assert-podman-stop-post-args-key-val-regex":
537
ok = t.assertStopPostPodmanArgsKeyValRegex(args, unit)
540
return fmt.Errorf("Unsupported assertion %s", op)
549
s, _ = unit.ToString()
551
return fmt.Errorf("Failed assertion for %s: %s\n\n%s", t.serviceName, strings.Join(check, " "), s)
556
func (t *quadletTestcase) check(generateDir string, session *PodmanSessionIntegration) {
558
for _, c := range t.checks {
559
if c[0] == "assert-failed" {
564
file := filepath.Join(generateDir, t.serviceName)
565
_, err := os.Stat(file)
567
Expect(err).To(MatchError(os.ErrNotExist))
569
Expect(err).ToNot(HaveOccurred())
572
var unit *parser.UnitFile
574
unit, err = parser.ParseUnitFile(file)
575
Expect(err).ToNot(HaveOccurred())
578
for _, check := range t.checks {
579
err := t.doAssert(check, unit, session)
580
Expect(err).ToNot(HaveOccurred())
584
var _ = Describe("quadlet system generator", func() {
592
generatedDir = filepath.Join(podmanTest.TempDir, "generated")
593
err = os.Mkdir(generatedDir, os.ModePerm)
594
Expect(err).ToNot(HaveOccurred())
596
quadletDir = filepath.Join(podmanTest.TempDir, "quadlet")
597
err = os.Mkdir(quadletDir, os.ModePerm)
598
Expect(err).ToNot(HaveOccurred())
601
Describe("quadlet -version", func() {
602
It("Should print correct version", func() {
603
session := podmanTest.Quadlet([]string{"-version"}, "/something")
604
session.WaitWithDefaultTimeout()
605
Expect(session).Should(ExitCleanly())
606
Expect(session.OutputToString()).To(Equal(version.Version.String()))
610
Describe("Running quadlet dryrun tests", func() {
611
It("Should exit with an error because of no files are found to parse", func() {
612
fileName := "basic.kube"
613
testcase := loadQuadletTestcase(filepath.Join("quadlet", fileName))
615
// Write the tested file to the quadlet dir
616
err = os.WriteFile(filepath.Join(quadletDir, fileName), testcase.data, 0644)
617
Expect(err).ToNot(HaveOccurred())
619
session := podmanTest.Quadlet([]string{"-dryrun"}, "/something")
620
session.WaitWithDefaultTimeout()
621
Expect(session).Should(Exit(0))
623
current := session.ErrorToStringArray()
624
expected := "No files parsed from [/something]"
627
for _, line := range current {
628
if strings.Contains(line, expected) {
633
Expect(found).To(BeTrue())
636
It("Should fail on bad quadlet", func() {
637
quadletfile := fmt.Sprintf(`[Container]
642
quadletfilePath := filepath.Join(podmanTest.TempDir, "bogus.container")
643
err = os.WriteFile(quadletfilePath, []byte(quadletfile), 0644)
644
Expect(err).ToNot(HaveOccurred())
645
defer os.Remove(quadletfilePath)
646
session := podmanTest.Quadlet([]string{"-dryrun"}, podmanTest.TempDir)
647
session.WaitWithDefaultTimeout()
648
Expect(session).Should(Exit(1))
649
Expect(session.ErrorToString()).To(ContainSubstring("converting \"bogus.container\": unsupported key 'BOGUS' in group 'Container' in " + quadletfilePath))
652
It("Should scan and return output for files in subdirectories", func() {
653
dirName := "test_subdir"
655
err = CopyDirectory(filepath.Join("quadlet", dirName), quadletDir)
658
GinkgoWriter.Println("error:", err)
661
session := podmanTest.Quadlet([]string{"-dryrun", "-user"}, quadletDir)
662
session.WaitWithDefaultTimeout()
664
current := session.OutputToStringArray()
665
expected := []string{
666
"---mysleep.service---",
667
"---mysleep_1.service---",
668
"---mysleep_2.service---",
671
Expect(current).To(ContainElements(expected))
674
It("Should parse a kube file and print it to stdout", func() {
675
fileName := "basic.kube"
676
testcase := loadQuadletTestcase(filepath.Join("quadlet", fileName))
678
// quadlet uses PODMAN env to get a stable podman path
679
podmanPath, found := os.LookupEnv("PODMAN")
681
podmanPath = podmanTest.PodmanBinary
684
// Write the tested file to the quadlet dir
685
err = os.WriteFile(filepath.Join(quadletDir, fileName), testcase.data, 0644)
686
Expect(err).ToNot(HaveOccurred())
688
session := podmanTest.Quadlet([]string{"-dryrun"}, quadletDir)
689
session.WaitWithDefaultTimeout()
690
Expect(session).Should(Exit(0))
691
Expect(session.ErrorToString()).To(ContainSubstring("Loading source unit file "))
693
current := session.OutputToStringArray()
694
expected := []string{
695
"---basic.service---",
696
"## assert-podman-args \"kube\"",
697
"## assert-podman-args \"play\"",
698
"## assert-podman-final-args-regex .*/podman_test.*/quadlet/deployment.yml",
699
"## assert-podman-args \"--replace\"",
700
"## assert-podman-args \"--service-container=true\"",
701
"## assert-podman-stop-post-args \"kube\"",
702
"## assert-podman-stop-post-args \"down\"",
703
"## assert-podman-stop-post-final-args-regex .*/podman_test.*/quadlet/deployment.yml",
704
"## assert-key-is \"Unit\" \"RequiresMountsFor\" \"%t/containers\"",
705
"## assert-key-is \"Service\" \"KillMode\" \"mixed\"",
706
"## assert-key-is \"Service\" \"Type\" \"notify\"",
707
"## assert-key-is \"Service\" \"NotifyAccess\" \"all\"",
708
"## assert-key-is \"Service\" \"Environment\" \"PODMAN_SYSTEMD_UNIT=%n\"",
709
"## assert-key-is \"Service\" \"SyslogIdentifier\" \"%N\"",
711
"Yaml=deployment.yml",
713
fmt.Sprintf("SourcePath=%s/basic.kube", quadletDir),
714
"RequiresMountsFor=%t/containers",
717
"Environment=PODMAN_SYSTEMD_UNIT=%n",
720
"SyslogIdentifier=%N",
721
fmt.Sprintf("ExecStart=%s kube play --replace --service-container=true %s/deployment.yml", podmanPath, quadletDir),
722
fmt.Sprintf("ExecStopPost=%s kube down %s/deployment.yml", podmanPath, quadletDir),
725
Expect(current).To(Equal(expected))
729
DescribeTable("Running quadlet test case",
730
func(fileName string, exitCode int, errString string) {
731
testcase := loadQuadletTestcase(filepath.Join("quadlet", fileName))
733
// Write the tested file to the quadlet dir
734
err = os.WriteFile(filepath.Join(quadletDir, fileName), testcase.data, 0644)
735
Expect(err).ToNot(HaveOccurred())
737
// Also copy any extra snippets
738
snippetdirs := []string{fileName + ".d"}
739
if ok, genericFileName := getGenericTemplateFile(fileName); ok {
740
snippetdirs = append(snippetdirs, genericFileName+".d")
742
for _, snippetdir := range snippetdirs {
743
dotdDir := filepath.Join("quadlet", snippetdir)
744
if s, err := os.Stat(dotdDir); err == nil && s.IsDir() {
745
dotdDirDest := filepath.Join(quadletDir, snippetdir)
746
err = os.Mkdir(dotdDirDest, os.ModePerm)
747
Expect(err).ToNot(HaveOccurred())
748
err = CopyDirectory(dotdDir, dotdDirDest)
749
Expect(err).ToNot(HaveOccurred())
753
// Run quadlet to convert the file
754
session := podmanTest.Quadlet([]string{"--user", "--no-kmsg-log", generatedDir}, quadletDir)
755
session.WaitWithDefaultTimeout()
756
Expect(session).Should(Exit(exitCode))
758
// Print any stderr output
759
errs := session.ErrorToString()
761
GinkgoWriter.Println("error:", session.ErrorToString())
763
Expect(errs).Should(ContainSubstring(errString))
765
testcase.check(generatedDir, session)
767
Entry("Basic container", "basic.container", 0, ""),
768
Entry("annotation.container", "annotation.container", 0, ""),
769
Entry("autoupdate.container", "autoupdate.container", 0, ""),
770
Entry("basepodman.container", "basepodman.container", 0, ""),
771
Entry("capabilities.container", "capabilities.container", 0, ""),
772
Entry("capabilities2.container", "capabilities2.container", 0, ""),
773
Entry("comment-with-continuation.container", "comment-with-continuation.container", 0, ""),
774
Entry("devices.container", "devices.container", 0, ""),
775
Entry("disableselinux.container", "disableselinux.container", 0, ""),
776
Entry("dns-options.container", "dns-options.container", 0, ""),
777
Entry("dns-search.container", "dns-search.container", 0, ""),
778
Entry("dns.container", "dns.container", 0, ""),
779
Entry("env-file.container", "env-file.container", 0, ""),
780
Entry("env-host-false.container", "env-host-false.container", 0, ""),
781
Entry("env-host.container", "env-host.container", 0, ""),
782
Entry("env.container", "env.container", 0, ""),
783
Entry("entrypoint.container", "entrypoint.container", 0, ""),
784
Entry("escapes.container", "escapes.container", 0, ""),
785
Entry("exec.container", "exec.container", 0, ""),
786
Entry("health.container", "health.container", 0, ""),
787
Entry("hostname.container", "hostname.container", 0, ""),
788
Entry("idmapping.container", "idmapping.container", 0, ""),
789
Entry("idmapping-with-remap.container", "idmapping-with-remap.container", 1, "converting \"idmapping-with-remap.container\": deprecated Remap keys are set along with explicit mapping keys"),
790
Entry("image.container", "image.container", 0, ""),
791
Entry("install.container", "install.container", 0, ""),
792
Entry("ip.container", "ip.container", 0, ""),
793
Entry("label.container", "label.container", 0, ""),
794
Entry("line-continuation-whitespace.container", "line-continuation-whitespace.container", 0, ""),
795
Entry("logdriver.container", "logdriver.container", 0, ""),
796
Entry("mask.container", "mask.container", 0, ""),
797
Entry("mount.container", "mount.container", 0, ""),
798
Entry("name.container", "name.container", 0, ""),
799
Entry("nestedselinux.container", "nestedselinux.container", 0, ""),
800
Entry("network.container", "network.container", 0, ""),
801
Entry("network.quadlet.container", "network.quadlet.container", 0, ""),
802
Entry("noimage.container", "noimage.container", 1, "converting \"noimage.container\": no Image or Rootfs key specified"),
803
Entry("notify.container", "notify.container", 0, ""),
804
Entry("notify-healthy.container", "notify-healthy.container", 0, ""),
805
Entry("oneshot.container", "oneshot.container", 0, ""),
806
Entry("other-sections.container", "other-sections.container", 0, ""),
807
Entry("pod.non-quadlet.container", "pod.non-quadlet.container", 1, "converting \"pod.non-quadlet.container\": pod test-pod is not Quadlet based"),
808
Entry("pod.not-found.container", "pod.not-found.container", 1, "converting \"pod.not-found.container\": quadlet pod unit not-found.pod does not exist"),
809
Entry("podmanargs.container", "podmanargs.container", 0, ""),
810
Entry("ports.container", "ports.container", 0, ""),
811
Entry("ports_ipv6.container", "ports_ipv6.container", 0, ""),
812
Entry("pull.container", "pull.container", 0, ""),
813
Entry("quotes.container", "quotes.container", 0, ""),
814
Entry("readonly.container", "readonly.container", 0, ""),
815
Entry("readonly-tmpfs.container", "readonly-tmpfs.container", 0, ""),
816
Entry("readonly-notmpfs.container", "readonly-notmpfs.container", 0, ""),
817
Entry("readwrite-notmpfs.container", "readwrite-notmpfs.container", 0, ""),
818
Entry("volatiletmp-readwrite.container", "volatiletmp-readwrite.container", 0, ""),
819
Entry("volatiletmp-readonly.container", "volatiletmp-readonly.container", 0, ""),
820
Entry("remap-auto.container", "remap-auto.container", 0, ""),
821
Entry("remap-auto2.container", "remap-auto2.container", 0, ""),
822
Entry("remap-keep-id.container", "remap-keep-id.container", 0, ""),
823
Entry("remap-keep-id2.container", "remap-keep-id2.container", 0, ""),
824
Entry("remap-manual.container", "remap-manual.container", 0, ""),
825
Entry("rootfs.container", "rootfs.container", 0, ""),
826
Entry("seccomp.container", "seccomp.container", 0, ""),
827
Entry("secrets.container", "secrets.container", 0, ""),
828
Entry("selinux.container", "selinux.container", 0, ""),
829
Entry("shmsize.container", "shmsize.container", 0, ""),
830
Entry("shortname.container", "shortname.container", 0, "Warning: shortname.container specifies the image \"shortname\" which not a fully qualified image name. This is not ideal for performance and security reasons. See the podman-pull manpage discussion of short-name-aliases.conf for details."),
831
Entry("stoptimeout.container", "stoptimeout.container", 0, ""),
832
Entry("subidmapping.container", "subidmapping.container", 0, ""),
833
Entry("subidmapping-with-remap.container", "subidmapping-with-remap.container", 1, "converting \"subidmapping-with-remap.container\": deprecated Remap keys are set along with explicit mapping keys"),
834
Entry("sysctl.container", "sysctl.container", 0, ""),
835
Entry("timezone.container", "timezone.container", 0, ""),
836
Entry("ulimit.container", "ulimit.container", 0, ""),
837
Entry("unmask.container", "unmask.container", 0, ""),
838
Entry("user.container", "user.container", 0, ""),
839
Entry("userns.container", "userns.container", 0, ""),
840
Entry("userns-with-remap.container", "userns-with-remap.container", 1, "converting \"userns-with-remap.container\": deprecated Remap keys are set along with explicit mapping keys"),
841
Entry("volume.container", "volume.container", 0, ""),
842
Entry("workingdir.container", "workingdir.container", 0, ""),
843
Entry("Container - global args", "globalargs.container", 0, ""),
844
Entry("Container - Containers Conf Modules", "containersconfmodule.container", 0, ""),
845
Entry("merged.container", "merged.container", 0, ""),
846
Entry("merged-override.container", "merged-override.container", 0, ""),
847
Entry("template@.container", "template@.container", 0, ""),
848
Entry("template@instance.container", "template@instance.container", 0, ""),
850
Entry("basic.volume", "basic.volume", 0, ""),
851
Entry("device-copy.volume", "device-copy.volume", 0, ""),
852
Entry("device.volume", "device.volume", 0, ""),
853
Entry("label.volume", "label.volume", 0, ""),
854
Entry("name.volume", "name.volume", 0, ""),
855
Entry("podmanargs.volume", "podmanargs.volume", 0, ""),
856
Entry("uid.volume", "uid.volume", 0, ""),
857
Entry("image.volume", "image.volume", 0, ""),
858
Entry("image-no-image.volume", "image-no-image.volume", 1, "converting \"image-no-image.volume\": the key Image is mandatory when using the image driver"),
859
Entry("Volume - global args", "globalargs.volume", 0, ""),
860
Entry("Volume - Containers Conf Modules", "containersconfmodule.volume", 0, ""),
862
Entry("Absolute Path", "absolute.path.kube", 0, ""),
863
Entry("Basic kube", "basic.kube", 0, ""),
864
Entry("Kube - ConfigMap", "configmap.kube", 0, ""),
865
Entry("Kube - Exit Code Propagation", "exit_code_propagation.kube", 0, ""),
866
Entry("Kube - Logdriver", "logdriver.kube", 0, ""),
867
Entry("Kube - Network", "network.kube", 0, ""),
868
Entry("Kube - PodmanArgs", "podmanargs.kube", 0, ""),
869
Entry("Kube - Publish IPv4 ports", "ports.kube", 0, ""),
870
Entry("Kube - Publish IPv6 ports", "ports_ipv6.kube", 0, ""),
871
Entry("Kube - Quadlet Network", "network.quadlet.kube", 0, ""),
872
Entry("Kube - User Remap Auto with IDs", "remap-auto2.kube", 0, ""),
873
Entry("Kube - User Remap Auto", "remap-auto.kube", 0, ""),
874
Entry("Kube - User Remap Manual", "remap-manual.kube", 1, "converting \"remap-manual.kube\": RemapUsers=manual is not supported"),
875
Entry("Syslog Identifier", "syslog.identifier.kube", 0, ""),
876
Entry("Kube - Working Directory YAML Absolute Path", "workingdir-yaml-abs.kube", 0, ""),
877
Entry("Kube - Working Directory YAML Relative Path", "workingdir-yaml-rel.kube", 0, ""),
878
Entry("Kube - Working Directory Unit", "workingdir-unit.kube", 0, ""),
879
Entry("Kube - Working Directory already in Service", "workingdir-service.kube", 0, ""),
880
Entry("Kube - global args", "globalargs.kube", 0, ""),
881
Entry("Kube - Containers Conf Modules", "containersconfmodule.kube", 0, ""),
882
Entry("Kube - Service Type=oneshot", "oneshot.kube", 0, ""),
883
Entry("Kube - Down force", "downforce.kube", 0, ""),
885
Entry("Network - Basic", "basic.network", 0, ""),
886
Entry("Network - Disable DNS", "disable-dns.network", 0, ""),
887
Entry("Network - DNS", "dns.network", 0, ""),
888
Entry("Network - Driver", "driver.network", 0, ""),
889
Entry("Network - Gateway not enough Subnet", "gateway.less-subnet.network", 1, "converting \"gateway.less-subnet.network\": cannot set more gateways than subnets"),
890
Entry("Network - Gateway without Subnet", "gateway.no-subnet.network", 1, "converting \"gateway.no-subnet.network\": cannot set gateway or range without subnet"),
891
Entry("Network - Gateway", "gateway.network", 0, ""),
892
Entry("Network - IPAM Driver", "ipam-driver.network", 0, ""),
893
Entry("Network - IPv6", "ipv6.network", 0, ""),
894
Entry("Network - Internal network", "internal.network", 0, ""),
895
Entry("Network - Label", "label.network", 0, ""),
896
Entry("Network - Multiple Options", "options.multiple.network", 0, ""),
897
Entry("Network - Name", "name.network", 0, ""),
898
Entry("Network - Options", "options.network", 0, ""),
899
Entry("Network - PodmanArgs", "podmanargs.network", 0, ""),
900
Entry("Network - Range not enough Subnet", "range.less-subnet.network", 1, "converting \"range.less-subnet.network\": cannot set more ranges than subnets"),
901
Entry("Network - Range without Subnet", "range.no-subnet.network", 1, "converting \"range.no-subnet.network\": cannot set gateway or range without subnet"),
902
Entry("Network - Range", "range.network", 0, ""),
903
Entry("Network - Subnets", "subnets.network", 0, ""),
904
Entry("Network - multiple subnet, gateway and range", "subnet-trio.multiple.network", 0, ""),
905
Entry("Network - subnet, gateway and range", "subnet-trio.network", 0, ""),
906
Entry("Network - global args", "globalargs.network", 0, ""),
907
Entry("Network - Containers Conf Modules", "containersconfmodule.network", 0, ""),
909
Entry("Image - Basic", "basic.image", 0, ""),
910
Entry("Image - No Image", "no-image.image", 1, "converting \"no-image.image\": no Image key specified"),
911
Entry("Image - Architecture", "arch.image", 0, ""),
912
Entry("Image - Auth File", "auth.image", 0, ""),
913
Entry("Image - Certificates", "certs.image", 0, ""),
914
Entry("Image - Credentials", "creds.image", 0, ""),
915
Entry("Image - Decryption Key", "decrypt.image", 0, ""),
916
Entry("Image - OS Key", "os.image", 0, ""),
917
Entry("Image - Variant Key", "variant.image", 0, ""),
918
Entry("Image - All Tags", "all-tags.image", 0, ""),
919
Entry("Image - TLS Verify", "tls-verify.image", 0, ""),
920
Entry("Image - Arch and OS", "arch-os.image", 0, ""),
921
Entry("Image - global args", "globalargs.image", 0, ""),
922
Entry("Image - Containers Conf Modules", "containersconfmodule.image", 0, ""),
924
Entry("basic.pod", "basic.pod", 0, ""),
925
Entry("name.pod", "name.pod", 0, ""),
926
Entry("network.pod", "network.pod", 0, ""),
927
Entry("network-quadlet.pod", "network.quadlet.pod", 0, ""),
928
Entry("podmanargs.pod", "podmanargs.pod", 0, ""),
929
Entry("volume.pod", "volume.pod", 0, ""),