podman

Форк
0
/
quadlet_test.go 
932 строки · 34.3 Кб
1
package integration
2

3
import (
4
	"encoding/csv"
5
	"fmt"
6
	"os"
7
	"path/filepath"
8
	"reflect"
9
	"regexp"
10
	"strings"
11

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"
16

17
	. "github.com/onsi/ginkgo/v2"
18
	. "github.com/onsi/gomega"
19
	. "github.com/onsi/gomega/gexec"
20
)
21

22
type quadletTestcase struct {
23
	data        []byte
24
	serviceName string
25
	checks      [][]string
26
}
27

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
35
	}
36
	return false, ""
37
}
38

39
func loadQuadletTestcase(path string) *quadletTestcase {
40
	data, err := os.ReadFile(path)
41
	Expect(err).ToNot(HaveOccurred())
42

43
	base := filepath.Base(path)
44
	ext := filepath.Ext(base)
45
	service := base[:len(base)-len(ext)]
46
	switch ext {
47
	case ".volume":
48
		service += "-volume"
49
	case ".network":
50
		service += "-network"
51
	case ".image":
52
		service += "-image"
53
	case ".pod":
54
		service += "-pod"
55
	}
56
	service += ".service"
57

58
	checks := make([][]string, 0)
59

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)
65
		}
66
	}
67

68
	return &quadletTestcase{
69
		data,
70
		service,
71
		checks,
72
	}
73
}
74

75
func matchSublistAt(full []string, pos int, sublist []string) bool {
76
	if len(sublist) > len(full)-pos {
77
		return false
78
	}
79

80
	for i := range sublist {
81
		if sublist[i] != full[pos+i] {
82
			return false
83
		}
84
	}
85
	return true
86
}
87

88
func matchSublistRegexAt(full []string, pos int, sublist []string) bool {
89
	if len(sublist) > len(full)-pos {
90
		return false
91
	}
92

93
	for i := range sublist {
94
		matched, err := regexp.MatchString(sublist[i], full[pos+i])
95
		if err != nil || !matched {
96
			return false
97
		}
98
	}
99
	return true
100
}
101

102
func findSublist(full []string, sublist []string) int {
103
	if len(sublist) > len(full) {
104
		return -1
105
	}
106
	if len(sublist) == 0 {
107
		return -1
108
	}
109
	for i := 0; i < len(full)-len(sublist)+1; i++ {
110
		if matchSublistAt(full, i, sublist) {
111
			return i
112
		}
113
	}
114
	return -1
115
}
116

117
func findSublistRegex(full []string, sublist []string) int {
118
	if len(sublist) > len(full) {
119
		return -1
120
	}
121
	if len(sublist) == 0 {
122
		return -1
123
	}
124
	for i := 0; i < len(full)-len(sublist)+1; i++ {
125
		if matchSublistRegexAt(full, i, sublist) {
126
			return i
127
		}
128
	}
129
	return -1
130
}
131

132
func (t *quadletTestcase) assertStdErrContains(args []string, session *PodmanSessionIntegration) bool {
133
	return strings.Contains(session.ErrorToString(), args[0])
134
}
135

136
func (t *quadletTestcase) assertKeyIs(args []string, unit *parser.UnitFile) bool {
137
	Expect(len(args)).To(BeNumerically(">=", 3))
138
	group := args[0]
139
	key := args[1]
140
	values := args[2:]
141

142
	realValues := unit.LookupAll(group, key)
143
	if len(realValues) != len(values) {
144
		return false
145
	}
146

147
	for i := range realValues {
148
		if realValues[i] != values[i] {
149
			return false
150
		}
151
	}
152
	return true
153
}
154

155
func (t *quadletTestcase) assertKeyIsRegex(args []string, unit *parser.UnitFile) bool {
156
	Expect(len(args)).To(BeNumerically(">=", 3))
157
	group := args[0]
158
	key := args[1]
159
	values := args[2:]
160

161
	realValues := unit.LookupAll(group, key)
162
	if len(realValues) != len(values) {
163
		return false
164
	}
165

166
	for i := range realValues {
167
		matched, _ := regexp.MatchString(values[i], realValues[i])
168
		if !matched {
169
			return false
170
		}
171
	}
172
	return true
173
}
174

175
func (t *quadletTestcase) assertKeyContains(args []string, unit *parser.UnitFile) bool {
176
	Expect(args).To(HaveLen(3))
177
	group := args[0]
178
	key := args[1]
179
	value := args[2]
180

181
	realValue, ok := unit.LookupLast(group, key)
182
	return ok && strings.Contains(realValue, value)
183
}
184

185
func (t *quadletTestcase) assertPodmanArgs(args []string, unit *parser.UnitFile, key string, allowRegex, globalOnly bool) bool {
186
	podmanArgs, _ := unit.LookupLastArgs("Service", key)
187
	if globalOnly {
188
		podmanCmdLocation := findSublist(podmanArgs, []string{args[0]})
189
		if podmanCmdLocation == -1 {
190
			return false
191
		}
192

193
		podmanArgs = podmanArgs[:podmanCmdLocation]
194
		args = args[1:]
195
	}
196

197
	var location int
198
	if allowRegex {
199
		location = findSublistRegex(podmanArgs, args)
200
	} else {
201
		location = findSublist(podmanArgs, args)
202
	}
203

204
	return location != -1
205
}
206

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()
212
	if err != nil {
213
		return nil, err
214
	}
215
	for _, param := range keyVarList[0] {
216
		key, val, _ := strings.Cut(param, "=")
217
		keyValMap[key] = val
218
	}
219

220
	return keyValMap, nil
221
}
222

223
func keyValMapEqualRegex(expectedKeyValMap, actualKeyValMap map[string]string) bool {
224
	if len(expectedKeyValMap) != len(actualKeyValMap) {
225
		return false
226
	}
227
	for key, expectedValue := range expectedKeyValMap {
228
		actualValue, ok := actualKeyValMap[key]
229
		if !ok {
230
			return false
231
		}
232
		matched, err := regexp.MatchString(expectedValue, actualValue)
233
		if err != nil || !matched {
234
			return false
235
		}
236
	}
237
	return true
238
}
239

240
func (t *quadletTestcase) assertPodmanArgsKeyVal(args []string, unit *parser.UnitFile, key string, allowRegex, globalOnly bool) bool {
241
	podmanArgs, _ := unit.LookupLastArgs("Service", key)
242

243
	if globalOnly {
244
		podmanCmdLocation := findSublist(podmanArgs, []string{args[0]})
245
		if podmanCmdLocation == -1 {
246
			return false
247
		}
248

249
		podmanArgs = podmanArgs[:podmanCmdLocation]
250
		args = args[1:]
251
	}
252

253
	expectedKeyValMap, err := keyValueStringToMap(args[2], args[1])
254
	if err != nil {
255
		return false
256
	}
257
	argKeyLocation := 0
258
	for {
259
		subListLocation := findSublist(podmanArgs[argKeyLocation:], []string{args[0]})
260
		if subListLocation == -1 {
261
			break
262
		}
263

264
		argKeyLocation += subListLocation
265
		actualKeyValMap, err := keyValueStringToMap(podmanArgs[argKeyLocation+1], args[1])
266
		if err != nil {
267
			break
268
		}
269
		if allowRegex {
270
			if keyValMapEqualRegex(expectedKeyValMap, actualKeyValMap) {
271
				return true
272
			}
273
		} else if reflect.DeepEqual(expectedKeyValMap, actualKeyValMap) {
274
			return true
275
		}
276

277
		argKeyLocation += 2
278

279
		if argKeyLocation > len(podmanArgs) {
280
			break
281
		}
282
	}
283

284
	return false
285
}
286

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) {
290
		return false
291
	}
292
	return matchSublistAt(podmanArgs, len(podmanArgs)-len(args), args)
293
}
294

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) {
298
		return false
299
	}
300
	return matchSublistRegexAt(podmanArgs, len(podmanArgs)-len(args), args)
301
}
302

303
func (t *quadletTestcase) assertStartPodmanArgs(args []string, unit *parser.UnitFile) bool {
304
	return t.assertPodmanArgs(args, unit, "ExecStart", false, false)
305
}
306

307
func (t *quadletTestcase) assertStartPodmanArgsRegex(args []string, unit *parser.UnitFile) bool {
308
	return t.assertPodmanArgs(args, unit, "ExecStart", true, false)
309
}
310

311
func (t *quadletTestcase) assertStartPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool {
312
	return t.assertPodmanArgs(args, unit, "ExecStart", false, true)
313
}
314

315
func (t *quadletTestcase) assertStartPodmanGlobalArgsRegex(args []string, unit *parser.UnitFile) bool {
316
	return t.assertPodmanArgs(args, unit, "ExecStart", true, true)
317
}
318

319
func (t *quadletTestcase) assertStartPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool {
320
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", false, false)
321
}
322

323
func (t *quadletTestcase) assertStartPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
324
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", true, false)
325
}
326

327
func (t *quadletTestcase) assertStartPodmanGlobalArgsKeyVal(args []string, unit *parser.UnitFile) bool {
328
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", false, true)
329
}
330

331
func (t *quadletTestcase) assertStartPodmanGlobalArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
332
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", true, true)
333
}
334

335
func (t *quadletTestcase) assertStartPodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
336
	return t.assertPodmanFinalArgs(args, unit, "ExecStart")
337
}
338

339
func (t *quadletTestcase) assertStartPodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool {
340
	return t.assertPodmanFinalArgsRegex(args, unit, "ExecStart")
341
}
342

343
func (t *quadletTestcase) assertStartPrePodmanArgs(args []string, unit *parser.UnitFile) bool {
344
	return t.assertPodmanArgs(args, unit, "ExecStartPre", false, false)
345
}
346

347
func (t *quadletTestcase) assertStartPrePodmanArgsRegex(args []string, unit *parser.UnitFile) bool {
348
	return t.assertPodmanArgs(args, unit, "ExecStartPre", true, false)
349
}
350

351
func (t *quadletTestcase) assertStartPrePodmanGlobalArgs(args []string, unit *parser.UnitFile) bool {
352
	return t.assertPodmanArgs(args, unit, "ExecStartPre", false, true)
353
}
354

355
func (t *quadletTestcase) assertStartPrePodmanGlobalArgsRegex(args []string, unit *parser.UnitFile) bool {
356
	return t.assertPodmanArgs(args, unit, "ExecStartPre", true, true)
357
}
358

359
func (t *quadletTestcase) assertStartPrePodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool {
360
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", false, false)
361
}
362

363
func (t *quadletTestcase) assertStartPrePodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
364
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", true, false)
365
}
366

367
func (t *quadletTestcase) assertStartPrePodmanGlobalArgsKeyVal(args []string, unit *parser.UnitFile) bool {
368
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", false, true)
369
}
370

371
func (t *quadletTestcase) assertStartPrePodmanGlobalArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
372
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", true, true)
373
}
374

375
func (t *quadletTestcase) assertStartPrePodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
376
	return t.assertPodmanFinalArgs(args, unit, "ExecStartPre")
377
}
378

379
func (t *quadletTestcase) assertStartPrePodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool {
380
	return t.assertPodmanFinalArgsRegex(args, unit, "ExecStartPre")
381
}
382

383
func (t *quadletTestcase) assertStopPodmanArgs(args []string, unit *parser.UnitFile) bool {
384
	return t.assertPodmanArgs(args, unit, "ExecStop", false, false)
385
}
386

387
func (t *quadletTestcase) assertStopPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool {
388
	return t.assertPodmanArgs(args, unit, "ExecStop", false, true)
389
}
390

391
func (t *quadletTestcase) assertStopPodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
392
	return t.assertPodmanFinalArgs(args, unit, "ExecStop")
393
}
394

395
func (t *quadletTestcase) assertStopPodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool {
396
	return t.assertPodmanFinalArgsRegex(args, unit, "ExecStop")
397
}
398

399
func (t *quadletTestcase) assertStopPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool {
400
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStop", false, false)
401
}
402

403
func (t *quadletTestcase) assertStopPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
404
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStop", true, false)
405
}
406

407
func (t *quadletTestcase) assertStopPostPodmanArgs(args []string, unit *parser.UnitFile) bool {
408
	return t.assertPodmanArgs(args, unit, "ExecStopPost", false, false)
409
}
410

411
func (t *quadletTestcase) assertStopPostPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool {
412
	return t.assertPodmanArgs(args, unit, "ExecStopPost", false, true)
413
}
414

415
func (t *quadletTestcase) assertStopPostPodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
416
	return t.assertPodmanFinalArgs(args, unit, "ExecStopPost")
417
}
418

419
func (t *quadletTestcase) assertStopPostPodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool {
420
	return t.assertPodmanFinalArgsRegex(args, unit, "ExecStopPost")
421
}
422

423
func (t *quadletTestcase) assertStopPostPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool {
424
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStopPost", false, false)
425
}
426

427
func (t *quadletTestcase) assertStopPostPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool {
428
	return t.assertPodmanArgsKeyVal(args, unit, "ExecStopPost", true, false)
429
}
430

431
func (t *quadletTestcase) assertSymlink(args []string, unit *parser.UnitFile) bool {
432
	Expect(args).To(HaveLen(2))
433
	symlink := args[0]
434
	expectedTarget := args[1]
435

436
	dir := filepath.Dir(unit.Path)
437

438
	target, err := os.Readlink(filepath.Join(dir, symlink))
439
	Expect(err).ToNot(HaveOccurred())
440

441
	return expectedTarget == target
442
}
443

444
func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, session *PodmanSessionIntegration) error {
445
	Expect(check).ToNot(BeEmpty())
446
	op := check[0]
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)
453
	}
454
	invert := false
455
	if op[0] == '!' {
456
		invert = true
457
		op = op[1:]
458
	}
459

460
	var ok bool
461
	switch op {
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)
538

539
	default:
540
		return fmt.Errorf("Unsupported assertion %s", op)
541
	}
542
	if invert {
543
		ok = !ok
544
	}
545

546
	if !ok {
547
		s := "(nil)"
548
		if unit != nil {
549
			s, _ = unit.ToString()
550
		}
551
		return fmt.Errorf("Failed assertion for %s: %s\n\n%s", t.serviceName, strings.Join(check, " "), s)
552
	}
553
	return nil
554
}
555

556
func (t *quadletTestcase) check(generateDir string, session *PodmanSessionIntegration) {
557
	expectFail := false
558
	for _, c := range t.checks {
559
		if c[0] == "assert-failed" {
560
			expectFail = true
561
		}
562
	}
563

564
	file := filepath.Join(generateDir, t.serviceName)
565
	_, err := os.Stat(file)
566
	if expectFail {
567
		Expect(err).To(MatchError(os.ErrNotExist))
568
	} else {
569
		Expect(err).ToNot(HaveOccurred())
570
	}
571

572
	var unit *parser.UnitFile
573
	if !expectFail {
574
		unit, err = parser.ParseUnitFile(file)
575
		Expect(err).ToNot(HaveOccurred())
576
	}
577

578
	for _, check := range t.checks {
579
		err := t.doAssert(check, unit, session)
580
		Expect(err).ToNot(HaveOccurred())
581
	}
582
}
583

584
var _ = Describe("quadlet system generator", func() {
585
	var (
586
		err          error
587
		generatedDir string
588
		quadletDir   string
589
	)
590

591
	BeforeEach(func() {
592
		generatedDir = filepath.Join(podmanTest.TempDir, "generated")
593
		err = os.Mkdir(generatedDir, os.ModePerm)
594
		Expect(err).ToNot(HaveOccurred())
595

596
		quadletDir = filepath.Join(podmanTest.TempDir, "quadlet")
597
		err = os.Mkdir(quadletDir, os.ModePerm)
598
		Expect(err).ToNot(HaveOccurred())
599
	})
600

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()))
607
		})
608
	})
609

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))
614

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())
618

619
			session := podmanTest.Quadlet([]string{"-dryrun"}, "/something")
620
			session.WaitWithDefaultTimeout()
621
			Expect(session).Should(Exit(0))
622

623
			current := session.ErrorToStringArray()
624
			expected := "No files parsed from [/something]"
625

626
			found := false
627
			for _, line := range current {
628
				if strings.Contains(line, expected) {
629
					found = true
630
					break
631
				}
632
			}
633
			Expect(found).To(BeTrue())
634
		})
635

636
		It("Should fail on bad quadlet", func() {
637
			quadletfile := fmt.Sprintf(`[Container]
638
Image=%s
639
BOGUS=foo
640
`, ALPINE)
641

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))
650
		})
651

652
		It("Should scan and return output for files in subdirectories", func() {
653
			dirName := "test_subdir"
654

655
			err = CopyDirectory(filepath.Join("quadlet", dirName), quadletDir)
656

657
			if err != nil {
658
				GinkgoWriter.Println("error:", err)
659
			}
660

661
			session := podmanTest.Quadlet([]string{"-dryrun", "-user"}, quadletDir)
662
			session.WaitWithDefaultTimeout()
663

664
			current := session.OutputToStringArray()
665
			expected := []string{
666
				"---mysleep.service---",
667
				"---mysleep_1.service---",
668
				"---mysleep_2.service---",
669
			}
670

671
			Expect(current).To(ContainElements(expected))
672
		})
673

674
		It("Should parse a kube file and print it to stdout", func() {
675
			fileName := "basic.kube"
676
			testcase := loadQuadletTestcase(filepath.Join("quadlet", fileName))
677

678
			// quadlet uses PODMAN env to get a stable podman path
679
			podmanPath, found := os.LookupEnv("PODMAN")
680
			if !found {
681
				podmanPath = podmanTest.PodmanBinary
682
			}
683

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())
687

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 "))
692

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\"",
710
				"[X-Kube]",
711
				"Yaml=deployment.yml",
712
				"[Unit]",
713
				fmt.Sprintf("SourcePath=%s/basic.kube", quadletDir),
714
				"RequiresMountsFor=%t/containers",
715
				"[Service]",
716
				"KillMode=mixed",
717
				"Environment=PODMAN_SYSTEMD_UNIT=%n",
718
				"Type=notify",
719
				"NotifyAccess=all",
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),
723
			}
724

725
			Expect(current).To(Equal(expected))
726
		})
727
	})
728

729
	DescribeTable("Running quadlet test case",
730
		func(fileName string, exitCode int, errString string) {
731
			testcase := loadQuadletTestcase(filepath.Join("quadlet", fileName))
732

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())
736

737
			// Also copy any extra snippets
738
			snippetdirs := []string{fileName + ".d"}
739
			if ok, genericFileName := getGenericTemplateFile(fileName); ok {
740
				snippetdirs = append(snippetdirs, genericFileName+".d")
741
			}
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())
750
				}
751
			}
752

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))
757

758
			// Print any stderr output
759
			errs := session.ErrorToString()
760
			if errs != "" {
761
				GinkgoWriter.Println("error:", session.ErrorToString())
762
			}
763
			Expect(errs).Should(ContainSubstring(errString))
764

765
			testcase.check(generatedDir, session)
766
		},
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, ""),
849

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, ""),
861

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, ""),
884

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, ""),
908

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, ""),
923

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, ""),
930
	)
931

932
})
933

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.