Skip to content

Commit

Permalink
Quadlet - add support for relative path in Volume key in .container file
Browse files Browse the repository at this point in the history
If the volume source starts with . resolve the path relative to the
location of the unit file

Update the test code to allow verification of regex for the value in key
value arguments
Add the usage of relative paths to the volume and mount test cases
Update the man page

Signed-off-by: Ygal Blum <[email protected]>
  • Loading branch information
ygalblum committed Mar 20, 2023
1 parent 150977f commit 5382997
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 11 deletions.
2 changes: 2 additions & 0 deletions docs/source/markdown/podman-systemd.unit.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ If enabled, the container will have a fresh tmpfs mounted on `/tmp`.
Mount a volume in the container. This is equivalent to the Podman `--volume` option, and
generally has the form `[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]`.

If `SOURCE-VOLUME` starts with `.`, Quadlet will resolve the path relative to the location of the unit file.

As a special case, if `SOURCE-VOLUME` ends with `.volume`, a Podman named volume called
`systemd-$name` will be used as the source, and the generated systemd service will contain
a dependency on the `$name-volume.service`. Such a volume can be automatically be lazily
Expand Down
31 changes: 23 additions & 8 deletions pkg/systemd/quadlet/quadlet.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,11 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
}

if source != "" {
source = handleStorageSource(service, source)
var err error
source, err = handleStorageSource(container, service, source)
if err != nil {
return nil, err
}
}

podman.add("-v")
Expand Down Expand Up @@ -564,10 +568,14 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
}
if paramType, ok := paramsMap["type"]; ok {
if paramType == "volume" || paramType == "bind" {
var err error
if paramSource, ok := paramsMap["source"]; ok {
paramsMap["source"] = handleStorageSource(service, paramSource)
paramsMap["source"], err = handleStorageSource(container, service, paramSource)
} else if paramSource, ok = paramsMap["src"]; ok {
paramsMap["src"] = handleStorageSource(service, paramSource)
paramsMap["src"], err = handleStorageSource(container, service, paramSource)
}
if err != nil {
return nil, err
}
}
}
Expand Down Expand Up @@ -1047,10 +1055,17 @@ func handleLogDriver(unitFile *parser.UnitFile, groupName string, podman *Podman
podman.add("--log-driver", logDriver)
}

func handleStorageSource(unitFile *parser.UnitFile, source string) string {
func handleStorageSource(quadletUnitFile, serviceUnitFile *parser.UnitFile, source string) (string, error) {
if source[0] == '.' {
var err error
source, err = getAbsolutePath(quadletUnitFile, source)
if err != nil {
return "", err
}
}
if source[0] == '/' {
// Absolute path
unitFile.Add(UnitGroup, "RequiresMountsFor", source)
serviceUnitFile.Add(UnitGroup, "RequiresMountsFor", source)
} else if strings.HasSuffix(source, ".volume") {
// the podman volume name is systemd-$name
volumeName := replaceExtension(source, "", "systemd-", "")
Expand All @@ -1060,11 +1075,11 @@ func handleStorageSource(unitFile *parser.UnitFile, source string) string {

source = volumeName

unitFile.Add(UnitGroup, "Requires", volumeServiceName)
unitFile.Add(UnitGroup, "After", volumeServiceName)
serviceUnitFile.Add(UnitGroup, "Requires", volumeServiceName)
serviceUnitFile.Add(UnitGroup, "After", volumeServiceName)
}

return source
return source, nil
}

func handleHealth(unitFile *parser.UnitFile, groupName string, podman *PodmanCmdline) {
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/quadlet/mount.container
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ Mount=type=tmpfs,tmpfs-size=512M,destination=/path/in/container
Mount=type=image,source=fedora,destination=/fedora-image,rw=true
## assert-podman-args-key-val "--mount" "," "type=devpts,destination=/dev/pts"
Mount=type=devpts,destination=/dev/pts
## assert-podman-args-key-val-regex "--mount" "," "type=bind,source=.*/podman_test.*/quadlet/path/on/host,destination=/path/in/container"
Mount=type=bind,source=./path/on/host,destination=/path/in/container
2 changes: 2 additions & 0 deletions test/e2e/quadlet/volume.container
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
## assert-podman-args -v /host/dir:/container/volume
## assert-podman-args -v /host/dir2:/container/volume2:Z
## assert-podman-args-regex -v .*/podman_test.*/quadlet/host/dir3:/container/volume3
## assert-podman-args -v named:/container/named
## assert-podman-args -v systemd-quadlet:/container/quadlet localhost/imagename

[Container]
Image=localhost/imagename
Volume=/host/dir:/container/volume
Volume=/host/dir2:/container/volume2:Z
Volume=./host/dir3:/container/volume3
Volume=/container/empty
Volume=named:/container/named
Volume=quadlet.volume:/container/quadlet
33 changes: 30 additions & 3 deletions test/e2e/quadlet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,24 @@ func keyValueStringToMap(keyValueString, separator string) map[string]string {
return keyValMap
}

func (t *quadletTestcase) assertPodmanArgsKeyVal(args []string, unit *parser.UnitFile, key string) bool {
func keyValMapEqualRegex(expectedKeyValMap, actualKeyValMap map[string]string) bool {
if len(expectedKeyValMap) != len(actualKeyValMap) {
return false
}
for key, expectedValue := range expectedKeyValMap {
actualValue, ok := actualKeyValMap[key]
if !ok {
return false
}
matched, err := regexp.MatchString(expectedValue, actualValue)
if err != nil || !matched {
return false
}
}
return true
}

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

expectedKeyValMap := keyValueStringToMap(args[2], args[1])
Expand All @@ -200,7 +217,11 @@ func (t *quadletTestcase) assertPodmanArgsKeyVal(args []string, unit *parser.Uni

argKeyLocation += subListLocation
actualKeyValMap := keyValueStringToMap(podmanArgs[argKeyLocation+1], args[1])
if reflect.DeepEqual(expectedKeyValMap, actualKeyValMap) {
if allowRegex {
if keyValMapEqualRegex(expectedKeyValMap, actualKeyValMap) {
return true
}
} else if reflect.DeepEqual(expectedKeyValMap, actualKeyValMap) {
return true
}

Expand Down Expand Up @@ -239,7 +260,11 @@ func (t *quadletTestcase) assertStartPodmanArgsRegex(args []string, unit *parser
}

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

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

func (t *quadletTestcase) assertStartPodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
Expand Down Expand Up @@ -309,6 +334,8 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio
ok = t.assertStartPodmanArgsRegex(args, unit)
case "assert-podman-args-key-val":
ok = t.assertStartPodmanArgsKeyVal(args, unit)
case "assert-podman-args-key-val-regex":
ok = t.assertStartPodmanArgsKeyValRegex(args, unit)
case "assert-podman-final-args":
ok = t.assertStartPodmanFinalArgs(args, unit)
case "assert-podman-final-args-regex":
Expand Down

0 comments on commit 5382997

Please sign in to comment.