Skip to content

Commit

Permalink
Merge pull request #24194 from lambinoo/quadlet-disable-default-depen…
Browse files Browse the repository at this point in the history
…dencies

Allow removing implicit quadlet systemd dependencies
  • Loading branch information
openshift-merge-bot[bot] authored Oct 9, 2024
2 parents 3460ec4 + bac655a commit 5890190
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 15 deletions.
24 changes: 24 additions & 0 deletions docs/source/markdown/podman-systemd.unit.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ QUADLET_UNIT_DIRS=<Directory> /usr/lib/systemd/system-generators/podman-system-g
This will instruct Quadlet to look for units in this directory instead of the common ones and by
that limit the output to only the units you are debugging.

### Implicit network dependencies

In the case of Container, Image and Build units, Quadlet will add dependencies on the `network-online.target`
by adding `After=` and `Wants=` properties to the unit. This is to ensure that the network is reachable if
an image needs to be pulled.

This behavior can be disabled by adding `DefaultDependencies=false` in the `Quadlet` section.

## Container units [Container]

Container units are named with a `.container` extension and contain a `[Container]` section describing
Expand Down Expand Up @@ -1914,6 +1922,22 @@ Override the default architecture variant of the container image.

This is equivalent to the Podman `--variant` option.

## Quadlet section [Quadlet]
Some quadlet specific configuration is shared between different unit types. Those settings
can be configured in the `[Quadlet]` section.

Valid options for `[Quadlet]` are listed below:

| **[Quadlet] options** | **Description** |
|----------------------------|---------------------------------------------------|
| DefaultDependencies=false | Disable implicit network dependencies to the unit |

### `DefaultDependencies=`

Add Quadlet's default network dependencies to the unit (default is `true`).

When set to false, Quadlet will **not** add a dependency (After=, Wants=) to `network-online.target` to the generated unit.

## EXAMPLES

Example `test.container`:
Expand Down
4 changes: 2 additions & 2 deletions hack/xref-quadlet-docs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ sub crossref_doc {
chomp $line;

# New section, with its own '| table |' and '### Keyword blocks'
if ($line =~ /^##\s+(\S+)\s+units\s+\[(\S+)\]/) {
if ($line =~ /^##\s+(\S+)\s+(?:units|section)\s+\[(\S+)\]/) {
my $new_unit = $1;
$new_unit eq $2
or warn "$ME: $path:$.: inconsistent block names in '$line'\n";
Expand Down Expand Up @@ -227,7 +227,7 @@ sub crossref_doc {
}

grep { $_ eq $key } @found_in_table
or warn "$ME: $path:$.: key '$key' is not listed in table for unit '$unit'\n";
or warn "$ME: $path:$.: key '$key' is not listed in table for unit/section '$unit'\n";

push @described, $key;
$documented{$key}++;
Expand Down
65 changes: 52 additions & 13 deletions pkg/systemd/quadlet/quadlet.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ const (
VolumeGroup = "Volume"
ImageGroup = "Image"
BuildGroup = "Build"
QuadletGroup = "Quadlet"
XContainerGroup = "X-Container"
XKubeGroup = "X-Kube"
XNetworkGroup = "X-Network"
XPodGroup = "X-Pod"
XVolumeGroup = "X-Volume"
XImageGroup = "X-Image"
XBuildGroup = "X-Build"
XQuadletGroup = "X-Quadlet"
)

// Systemd Unit file keys
Expand All @@ -70,6 +72,7 @@ const (
KeyCopy = "Copy"
KeyCreds = "Creds"
KeyDecryptionKey = "DecryptionKey"
KeyDefaultDependencies = "DefaultDependencies"
KeyDevice = "Device"
KeyDisableDNS = "DisableDNS"
KeyDNS = "DNS"
Expand Down Expand Up @@ -414,6 +417,11 @@ var (
KeyUserNS: true,
KeyVolume: true,
}

// Supported keys in "Quadlet" group
supportedQuadletKeys = map[string]bool{
KeyDefaultDependencies: true,
}
)

func (u *UnitInfo) ServiceFileName() string {
Expand All @@ -439,16 +447,26 @@ func isPortRange(port string) bool {
return validPortRange.MatchString(port)
}

func checkForUnknownKeys(unit *parser.UnitFile, groupName string, supportedKeys map[string]bool) error {
func checkForUnknownKeysInSpecificGroup(unit *parser.UnitFile, groupName string, supportedKeys map[string]bool) error {
keys := unit.ListKeys(groupName)
for _, key := range keys {
if !supportedKeys[key] {
return fmt.Errorf("unsupported key '%s' in group '%s' in %s", key, groupName, unit.Path)
}
}

return nil
}

func checkForUnknownKeys(unit *parser.UnitFile, groupName string, supportedKeys map[string]bool) error {
err := checkForUnknownKeysInSpecificGroup(unit, groupName, supportedKeys)
if err == nil {
return checkForUnknownKeysInSpecificGroup(unit, QuadletGroup, supportedQuadletKeys)
}

return err
}

func splitPorts(ports string) []string {
parts := make([]string, 0)

Expand Down Expand Up @@ -509,10 +527,10 @@ func ConvertContainer(container *parser.UnitFile, isUser bool, unitsInfoMap map[
// Add a dependency on network-online.target so the image pull does not happen
// before network is ready
// https://github.com/containers/podman/issues/21873
// Prepend the lines, so the user-provided values
// override the default ones.
service.PrependUnitLine(UnitGroup, "After", "network-online.target")
service.PrependUnitLine(UnitGroup, "Wants", "network-online.target")
if service.LookupBooleanWithDefault(QuadletGroup, KeyDefaultDependencies, true) {
service.PrependUnitLine(UnitGroup, "After", "network-online.target")
service.PrependUnitLine(UnitGroup, "Wants", "network-online.target")
}

if container.Path != "" {
service.Add(UnitGroup, "SourcePath", container.Path)
Expand All @@ -525,6 +543,9 @@ func ConvertContainer(container *parser.UnitFile, isUser bool, unitsInfoMap map[
// Rename old Container group to x-Container so that systemd ignores it
service.RenameGroup(ContainerGroup, XContainerGroup)

// Rename common quadlet group
service.RenameGroup(QuadletGroup, XQuadletGroup)

// One image or rootfs must be specified for the container
image, _ := container.Lookup(ContainerGroup, KeyImage)
rootfs, _ := container.Lookup(ContainerGroup, KeyRootfs)
Expand Down Expand Up @@ -887,6 +908,9 @@ func ConvertNetwork(network *parser.UnitFile, name string, unitsInfoMap map[stri
/* Rename old Network group to x-Network so that systemd ignores it */
service.RenameGroup(NetworkGroup, XNetworkGroup)

// Rename common quadlet group
service.RenameGroup(QuadletGroup, XQuadletGroup)

// Derive network name from unit name (with added prefix), or use user-provided name.
networkName, ok := network.Lookup(NetworkGroup, KeyNetworkName)
if !ok || len(networkName) == 0 {
Expand Down Expand Up @@ -994,6 +1018,9 @@ func ConvertVolume(volume *parser.UnitFile, name string, unitsInfoMap map[string
/* Rename old Volume group to x-Volume so that systemd ignores it */
service.RenameGroup(VolumeGroup, XVolumeGroup)

// Rename common quadlet group
service.RenameGroup(QuadletGroup, XQuadletGroup)

// Derive volume name from unit name (with added prefix), or use user-provided name.
volumeName, ok := volume.Lookup(VolumeGroup, KeyVolumeName)
if !ok || len(volumeName) == 0 {
Expand Down Expand Up @@ -1132,6 +1159,9 @@ func ConvertKube(kube *parser.UnitFile, unitsInfoMap map[string]*UnitInfo, isUse
// Rename old Kube group to x-Kube so that systemd ignores it
service.RenameGroup(KubeGroup, XKubeGroup)

// Rename common quadlet group
service.RenameGroup(QuadletGroup, XQuadletGroup)

yamlPath, ok := kube.Lookup(KubeGroup, KeyYaml)
if !ok || len(yamlPath) == 0 {
return nil, fmt.Errorf("no Yaml key specified")
Expand Down Expand Up @@ -1264,10 +1294,10 @@ func ConvertImage(image *parser.UnitFile, unitsInfoMap map[string]*UnitInfo) (*p
// Add a dependency on network-online.target so the image pull does not happen
// before network is ready
// https://github.com/containers/podman/issues/21873
// Prepend the lines, so the user-provided values
// override the default ones.
service.PrependUnitLine(UnitGroup, "After", "network-online.target")
service.PrependUnitLine(UnitGroup, "Wants", "network-online.target")
if service.LookupBooleanWithDefault(QuadletGroup, KeyDefaultDependencies, true) {
service.PrependUnitLine(UnitGroup, "After", "network-online.target")
service.PrependUnitLine(UnitGroup, "Wants", "network-online.target")
}

if image.Path != "" {
service.Add(UnitGroup, "SourcePath", image.Path)
Expand All @@ -1285,6 +1315,9 @@ func ConvertImage(image *parser.UnitFile, unitsInfoMap map[string]*UnitInfo) (*p
/* Rename old Network group to x-Network so that systemd ignores it */
service.RenameGroup(ImageGroup, XImageGroup)

// Rename common quadlet group
service.RenameGroup(QuadletGroup, XQuadletGroup)

// Need the containers filesystem mounted to start podman
service.Add(UnitGroup, "RequiresMountsFor", "%t/containers")

Expand Down Expand Up @@ -1349,14 +1382,17 @@ func ConvertBuild(build *parser.UnitFile, unitsInfoMap map[string]*UnitInfo) (*p
// Add a dependency on network-online.target so the image pull does not happen
// before network is ready
// https://github.com/containers/podman/issues/21873
// Prepend the lines, so the user-provided values
// override the default ones.
service.PrependUnitLine(UnitGroup, "After", "network-online.target")
service.PrependUnitLine(UnitGroup, "Wants", "network-online.target")
if service.LookupBooleanWithDefault(QuadletGroup, KeyDefaultDependencies, true) {
service.PrependUnitLine(UnitGroup, "After", "network-online.target")
service.PrependUnitLine(UnitGroup, "Wants", "network-online.target")
}

/* Rename old Build group to X-Build so that systemd ignores it */
service.RenameGroup(BuildGroup, XBuildGroup)

// Rename common quadlet group
service.RenameGroup(QuadletGroup, XQuadletGroup)

// Need the containers filesystem mounted to start podman
service.Add(UnitGroup, "RequiresMountsFor", "%t/containers")

Expand Down Expand Up @@ -1531,6 +1567,9 @@ func ConvertPod(podUnit *parser.UnitFile, name string, unitsInfoMap map[string]*
/* Rename old Pod group to x-Pod so that systemd ignores it */
service.RenameGroup(PodGroup, XPodGroup)

// Rename common quadlet group
service.RenameGroup(QuadletGroup, XQuadletGroup)

// Need the containers filesystem mounted to start podman
service.Add(UnitGroup, "RequiresMountsFor", "%t/containers")

Expand Down
11 changes: 11 additions & 0 deletions test/e2e/quadlet/no_deps.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## assert-key-is-empty "Unit" "Wants"
## assert-key-is-empty "Unit" "After"
## assert-key-is-empty "Unit" "Before"

[Quadlet]
DefaultDependencies=no

[Build]
ImageTag=localhost/imagename
File=Containerfile
SetWorkingDirectory=dir
9 changes: 9 additions & 0 deletions test/e2e/quadlet/no_deps.container
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## assert-key-is-empty "Unit" "Wants"
## assert-key-is-empty "Unit" "After"
## assert-key-is-empty "Unit" "Before"

[Quadlet]
DefaultDependencies=no

[Container]
Image=localhost/imagename
9 changes: 9 additions & 0 deletions test/e2e/quadlet/no_deps.image
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## assert-key-is-empty "Unit" "Wants"
## assert-key-is-empty "Unit" "After"
## assert-key-is-empty "Unit" "Before"

[Quadlet]
DefaultDependencies=no

[Image]
Image=localhost/imagename
14 changes: 14 additions & 0 deletions test/e2e/quadlet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ func (t *quadletTestcase) assertKeyIs(args []string, unit *parser.UnitFile) bool
return true
}

func (t *quadletTestcase) assertKeyIsEmpty(args []string, unit *parser.UnitFile) bool {
Expect(args).To(HaveLen(2))
group := args[0]
key := args[1]

realValues := unit.LookupAll(group, key)
return len(realValues) == 0
}

func (t *quadletTestcase) assertKeyIsRegex(args []string, unit *parser.UnitFile) bool {
Expect(len(args)).To(BeNumerically(">=", 3))
group := args[0]
Expand Down Expand Up @@ -501,6 +510,8 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio
ok = t.assertStdErrContains(args, session)
case "assert-key-is":
ok = t.assertKeyIs(args, unit)
case "assert-key-is-empty":
ok = t.assertKeyIsEmpty(args, unit)
case "assert-key-is-regex":
ok = t.assertKeyIsRegex(args, unit)
case "assert-key-contains":
Expand Down Expand Up @@ -899,6 +910,7 @@ BOGUS=foo
Entry("Unit After Override", "unit-after-override.container"),
Entry("NetworkAlias", "network-alias.container"),
Entry("CgroupMode", "cgroups-mode.container"),
Entry("Container - No Default Dependencies", "no_deps.container"),

Entry("basic.volume", "basic.volume"),
Entry("device-copy.volume", "device-copy.volume"),
Expand Down Expand Up @@ -967,6 +979,7 @@ BOGUS=foo
Entry("Image - global args", "globalargs.image"),
Entry("Image - Containers Conf Modules", "containersconfmodule.image"),
Entry("Image - Unit After Override", "unit-after-override.image"),
Entry("Image - No Default Dependencies", "no_deps.image"),

Entry("Build - Basic", "basic.build"),
Entry("Build - Annotation Key", "annotation.build"),
Expand Down Expand Up @@ -1000,6 +1013,7 @@ BOGUS=foo
Entry("Build - Target Key", "target.build"),
Entry("Build - TLSVerify Key", "tls-verify.build"),
Entry("Build - Variant Key", "variant.build"),
Entry("Build - No Default Dependencies", "no_deps.build"),

Entry("Pod - Basic", "basic.pod"),
Entry("Pod - DNS", "dns.pod"),
Expand Down

1 comment on commit 5890190

@packit-as-a-service
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

podman-next COPR build failed. @containers/packit-build please check.

Please sign in to comment.