mirror of
https://github.com/containers/podman.git
synced 2025-05-19 16:18:51 +08:00
Allow removing implicit quadlet systemd dependencies
Quadlet inserts network-online.target Wants/After dependencies to ensure pulling works. Those systemd statements cannot be subsequently reset. In the cases where those dependencies are not wanted, we add a new configuration item called `DefaultDependencies=` in a new section called [Quadlet]. This section is shared between different unit types. fixes #24193 Signed-off-by: Farya L. Maerten <me@ltow.me>
This commit is contained in:

committed by
Farya L. Maerten

parent
6b0ad8269c
commit
bac655a6b1
@ -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
|
||||
@ -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`:
|
||||
|
@ -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";
|
||||
@ -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}++;
|
||||
|
@ -38,6 +38,7 @@ const (
|
||||
VolumeGroup = "Volume"
|
||||
ImageGroup = "Image"
|
||||
BuildGroup = "Build"
|
||||
QuadletGroup = "Quadlet"
|
||||
XContainerGroup = "X-Container"
|
||||
XKubeGroup = "X-Kube"
|
||||
XNetworkGroup = "X-Network"
|
||||
@ -45,6 +46,7 @@ const (
|
||||
XVolumeGroup = "X-Volume"
|
||||
XImageGroup = "X-Image"
|
||||
XBuildGroup = "X-Build"
|
||||
XQuadletGroup = "X-Quadlet"
|
||||
)
|
||||
|
||||
// Systemd Unit file keys
|
||||
@ -70,6 +72,7 @@ const (
|
||||
KeyCopy = "Copy"
|
||||
KeyCreds = "Creds"
|
||||
KeyDecryptionKey = "DecryptionKey"
|
||||
KeyDefaultDependencies = "DefaultDependencies"
|
||||
KeyDevice = "Device"
|
||||
KeyDisableDNS = "DisableDNS"
|
||||
KeyDNS = "DNS"
|
||||
@ -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 {
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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 {
|
||||
@ -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 {
|
||||
@ -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")
|
||||
@ -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)
|
||||
@ -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")
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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")
|
||||
|
||||
|
11
test/e2e/quadlet/no_deps.build
Normal file
11
test/e2e/quadlet/no_deps.build
Normal file
@ -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
test/e2e/quadlet/no_deps.container
Normal file
9
test/e2e/quadlet/no_deps.container
Normal file
@ -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
test/e2e/quadlet/no_deps.image
Normal file
9
test/e2e/quadlet/no_deps.image
Normal file
@ -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
|
@ -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]
|
||||
@ -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":
|
||||
@ -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"),
|
||||
@ -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"),
|
||||
@ -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"),
|
||||
|
Reference in New Issue
Block a user