Skip to content

Commit 70dda9c

Browse files
committed
Quadlet - Support template dependency
Add support for Volumes and Networks Add e2e and system tests Resolves: #25136 Signed-off-by: Ygal Blum <ygal.blum@gmail.com>
1 parent a118fdf commit 70dda9c

File tree

7 files changed

+141
-10
lines changed

7 files changed

+141
-10
lines changed

pkg/systemd/quadlet/quadlet.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,19 @@ func getContainerName(container *parser.UnitFile) string {
908908
return containerName
909909
}
910910

911+
// Get the unresolved resource name that may contain '%'.
912+
func getResourceName(unit *parser.UnitFile, group, key string) string {
913+
resourceName, ok := unit.Lookup(group, key)
914+
if !ok || len(resourceName) == 0 {
915+
resourceName = removeExtension(unit.Filename, "systemd-", "")
916+
// By default, We want to name the resource by the service name.
917+
if strings.Contains(unit.Filename, "@") {
918+
resourceName = resourceName[:len(resourceName)-1] + "-%i"
919+
}
920+
}
921+
return resourceName
922+
}
923+
911924
// Get the resolved container name that contains no '%'.
912925
// Returns an empty string if not resolvable.
913926
func GetContainerResourceName(container *parser.UnitFile) string {
@@ -954,10 +967,7 @@ func ConvertNetwork(network *parser.UnitFile, name string, unitsInfoMap map[stri
954967
}
955968

956969
// Derive network name from unit name (with added prefix), or use user-provided name.
957-
networkName, ok := network.Lookup(NetworkGroup, KeyNetworkName)
958-
if !ok || len(networkName) == 0 {
959-
networkName = removeExtension(name, "systemd-", "")
960-
}
970+
networkName := getResourceName(network, NetworkGroup, KeyNetworkName)
961971

962972
if network.LookupBooleanWithDefault(NetworkGroup, KeyNetworkDeleteOnStop, false) {
963973
serviceStopPostCmd := createBasePodmanCommand(network, NetworkGroup)
@@ -1046,10 +1056,7 @@ func ConvertVolume(volume *parser.UnitFile, name string, unitsInfoMap map[string
10461056
}
10471057

10481058
// Derive volume name from unit name (with added prefix), or use user-provided name.
1049-
volumeName, ok := volume.Lookup(VolumeGroup, KeyVolumeName)
1050-
if !ok || len(volumeName) == 0 {
1051-
volumeName = removeExtension(name, "systemd-", "")
1052-
}
1059+
volumeName := getResourceName(volume, VolumeGroup, KeyVolumeName)
10531060

10541061
podman := createBasePodmanCommand(volume, VolumeGroup)
10551062

@@ -1503,7 +1510,12 @@ func getServiceName(quadletUnitFile *parser.UnitFile, groupName string, defaultE
15031510
if serviceName, ok := quadletUnitFile.Lookup(groupName, KeyServiceName); ok {
15041511
return serviceName
15051512
}
1506-
return removeExtension(quadletUnitFile.Filename, "", defaultExtraSuffix)
1513+
baseServiceName := removeExtension(quadletUnitFile.Filename, "", "")
1514+
if baseServiceName[len(baseServiceName)-1] == '@' {
1515+
baseServiceName = baseServiceName[:len(baseServiceName)-1]
1516+
defaultExtraSuffix += "@"
1517+
}
1518+
return baseServiceName + defaultExtraSuffix
15071519
}
15081520

15091521
func GetPodResourceName(podUnit *parser.UnitFile) string {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
## assert-podman-args-key-val "--mount" "," "type=volume,source=systemd-template-dependency-%i,destination=/path/in/container,ro=true"
2+
## assert-podman-args -v systemd-template-dependency-%i:/container/quadlet
3+
## assert-podman-args "--network" "systemd-template-dependency-%i"
4+
## assert-key-is "Unit" "Requires" "template-dependency-network@.service" "template-dependency-volume@.service" "template-dependency-volume@.service"
5+
6+
[Container]
7+
Image=localhost/imagename
8+
Mount=type=volume,source=template-dependency@.volume,destination=/path/in/container,ro=true
9+
Volume=template-dependency@.volume:/container/quadlet
10+
Network=template-dependency@.network
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[Network]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[Volume]

test/e2e/quadlet_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,14 @@ BOGUS=foo
11951195
"basic.volume",
11961196
},
11971197
),
1198+
Entry(
1199+
"Container - Template with Volume Template dependency",
1200+
"template-dependency@.container",
1201+
[]string{
1202+
"template-dependency@.volume",
1203+
"template-dependency@.network",
1204+
},
1205+
),
11981206

11991207
Entry("Volume - Quadlet image (.build)", "build.quadlet.volume", []string{"basic.build"}),
12001208
Entry("Volume - Quadlet image (.image)", "image.quadlet.volume", []string{"basic.image"}),

test/system/252-quadlet.bats

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,98 @@ EOF
437437
run_podman volume rm $volume_name
438438
}
439439

440+
# A quadlet container template depends on a quadlet volume and network templates
441+
@test "quadlet - template dependency" {
442+
# Save the unit name to use as the volume template for the container template
443+
local quadlet_vol_unit=dep_$(safename)@.volume
444+
local quadlet_vol_file=$PODMAN_TMPDIR/${quadlet_vol_unit}
445+
cat > $quadlet_vol_file <<EOF
446+
[Volume]
447+
EOF
448+
449+
local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
450+
# Have quadlet create the systemd unit file for the volume template unit
451+
run_quadlet "$quadlet_vol_file" "$quadlet_tmpdir"
452+
453+
# Save the volume service name since the variable will be overwritten
454+
local vol_service=$QUADLET_SERVICE_NAME
455+
local volume_name=systemd-$(basename $quadlet_vol_file .volume)
456+
# For template units, the volume name should have -%i appended
457+
volume_name=${volume_name%@}-%i
458+
459+
# Save the unit name to use as the volume template for the container template
460+
local quadlet_net_unit=dep_$(safename)@.network
461+
local quadlet_net_file=$PODMAN_TMPDIR/${quadlet_net_unit}
462+
cat > $quadlet_net_file <<EOF
463+
[Network]
464+
EOF
465+
466+
# Have quadlet create the systemd unit file for the network template unit
467+
run_quadlet "$quadlet_net_file" "$quadlet_tmpdir"
468+
469+
# Save the network service name since the variable will be overwritten
470+
local net_service=$QUADLET_SERVICE_NAME
471+
local network_name=systemd-$(basename $quadlet_net_file .network)
472+
# For template units, the network name should have -%i appended
473+
network_name=${network_name%@}-%i
474+
475+
local quadlet_file=$PODMAN_TMPDIR/user_$(safename)@.container
476+
cat > $quadlet_file <<EOF
477+
[Container]
478+
Image=$IMAGE
479+
Exec=top
480+
Volume=$quadlet_vol_unit:/tmp
481+
Network=$quadlet_net_unit
482+
EOF
483+
484+
# Have quadlet create the systemd unit file for the container template unit
485+
run_quadlet "$quadlet_file" "$quadlet_tmpdir"
486+
487+
# Save the container service name for readability
488+
local container_service=$QUADLET_SERVICE_NAME
489+
490+
# Create instance names for the template units
491+
local instance_name="test"
492+
local vol_service_instance="${vol_service%@*}@${instance_name}.service"
493+
local net_service_instance="${net_service%@*}@${instance_name}.service"
494+
local container_service_instance="${container_service%@*}@${instance_name}.service"
495+
local volume_name_instance="systemd-dep_$(safename)-${instance_name}"
496+
local network_name_instance="systemd-dep_$(safename)-${instance_name}"
497+
498+
# Volume should not exist
499+
run_podman 1 volume exists ${volume_name_instance}
500+
# Network should not exist
501+
run_podman 1 network exists ${network_name_instance}
502+
503+
# Start the container service instance which should also trigger the start of the volume service instance
504+
service_setup $container_service_instance
505+
506+
# Volume system unit instance should be active
507+
run systemctl show --property=ActiveState "$vol_service_instance"
508+
assert "$output" = "ActiveState=active" \
509+
"volume template instance should be active via dependency"
510+
511+
# Network system unit instance should be active
512+
run systemctl show --property=ActiveState "$net_service_instance"
513+
assert "$output" = "ActiveState=active" \
514+
"network template instance should be active via dependency"
515+
516+
# Volume should exist
517+
run_podman volume exists ${volume_name_instance}
518+
519+
# Network should exist
520+
run_podman network exists ${network_name_instance}
521+
522+
# Shutdown the service and remove the volume and network
523+
service_cleanup $container_service_instance failed
524+
run_podman volume rm $volume_name_instance
525+
run_podman network rm $network_name_instance
526+
for UNIT_FILE in ${UNIT_FILES[@]}; do
527+
rm $UNIT_FILE
528+
done
529+
UNIT_FILES=()
530+
}
531+
440532
# A quadlet container depends on a named quadlet volume
441533
@test "quadlet - named volume dependency" {
442534
local volume_name="v-$(safename)"

test/system/helpers.systemd.bash

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ quadlet_to_service_name() {
115115
local extension="${filename##*.}"
116116
local filename="${filename%.*}"
117117
local suffix=""
118+
local is_template=""
119+
120+
# Check if this is a template unit (ends with @)
121+
if [[ "$filename" == *@ ]]; then
122+
is_template="@"
123+
filename="${filename%@}"
124+
fi
118125

119126
if [ "$extension" == "volume" ]; then
120127
suffix="-volume"
@@ -128,5 +135,5 @@ quadlet_to_service_name() {
128135
suffix="-build"
129136
fi
130137

131-
echo "$filename$suffix.service"
138+
echo "$filename$suffix$is_template.service"
132139
}

0 commit comments

Comments
 (0)