mirror of
https://github.com/containers/podman.git
synced 2025-12-06 05:37:49 +08:00
Merge pull request #22057 from jbtrystram/quadlet-image-network
quadlet: Add a network requirement on .image and .containers units
This commit is contained in:
@@ -1333,6 +1333,11 @@ exists on the host, pulling it if needed.
|
|||||||
Using image units allows containers and volumes to depend on images being automatically pulled. This is
|
Using image units allows containers and volumes to depend on images being automatically pulled. This is
|
||||||
particularly interesting when using special options to control image pulls.
|
particularly interesting when using special options to control image pulls.
|
||||||
|
|
||||||
|
Note: The generated service have a dependency on `network-online.target` assuring the network is reachable if
|
||||||
|
an image needs to be pulled.
|
||||||
|
If the image service needs to run without available network (e.g. early in boot), the requirement can be
|
||||||
|
overriden simply by adding an empty `After=` in the unit file. This will unset all previously set After's.
|
||||||
|
|
||||||
Valid options for `[Image]` are listed below:
|
Valid options for `[Image]` are listed below:
|
||||||
|
|
||||||
| **[Image] options** | **podman image pull equivalent** |
|
| **[Image] options** | **podman image pull equivalent** |
|
||||||
|
|||||||
@@ -90,6 +90,11 @@ func (g *unitGroup) addLine(line *unitLine) {
|
|||||||
g.lines = append(g.lines, line)
|
g.lines = append(g.lines, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *unitGroup) prependLine(line *unitLine) {
|
||||||
|
n := []*unitLine{line}
|
||||||
|
g.lines = append(n, g.lines...)
|
||||||
|
}
|
||||||
|
|
||||||
func (g *unitGroup) addComment(line *unitLine) {
|
func (g *unitGroup) addComment(line *unitLine) {
|
||||||
g.comments = append(g.comments, line)
|
g.comments = append(g.comments, line)
|
||||||
}
|
}
|
||||||
@@ -923,6 +928,17 @@ func (f *UnitFile) PrependComment(groupName string, comments ...string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *UnitFile) PrependUnitLine(groupName string, key string, value string) {
|
||||||
|
var group *unitGroup
|
||||||
|
if groupName == "" && len(f.groups) > 0 {
|
||||||
|
group = f.groups[0]
|
||||||
|
} else {
|
||||||
|
// Uses magic "" for first comment-only group if no other groups
|
||||||
|
group = f.ensureGroup(groupName)
|
||||||
|
}
|
||||||
|
group.prependLine(newUnitLine(key, value, false))
|
||||||
|
}
|
||||||
|
|
||||||
func (f *UnitFile) GetTemplateParts() (string, string) {
|
func (f *UnitFile) GetTemplateParts() (string, string) {
|
||||||
ext := filepath.Ext(f.Filename)
|
ext := filepath.Ext(f.Filename)
|
||||||
basename := strings.TrimSuffix(f.Filename, ext)
|
basename := strings.TrimSuffix(f.Filename, ext)
|
||||||
|
|||||||
@@ -411,6 +411,14 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse
|
|||||||
service := container.Dup()
|
service := container.Dup()
|
||||||
service.Filename = replaceExtension(container.Filename, ".service", "", "")
|
service.Filename = replaceExtension(container.Filename, ".service", "", "")
|
||||||
|
|
||||||
|
// 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 container.Path != "" {
|
if container.Path != "" {
|
||||||
service.Add(UnitGroup, "SourcePath", container.Path)
|
service.Add(UnitGroup, "SourcePath", container.Path)
|
||||||
}
|
}
|
||||||
@@ -1182,6 +1190,14 @@ func ConvertImage(image *parser.UnitFile) (*parser.UnitFile, string, error) {
|
|||||||
service := image.Dup()
|
service := image.Dup()
|
||||||
service.Filename = replaceExtension(image.Filename, ".service", "", "-image")
|
service.Filename = replaceExtension(image.Filename, ".service", "", "-image")
|
||||||
|
|
||||||
|
// 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 image.Path != "" {
|
if image.Path != "" {
|
||||||
service.Add(UnitGroup, "SourcePath", image.Path)
|
service.Add(UnitGroup, "SourcePath", image.Path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
## assert-key-is-regex "Service" "ExecStopPost" "-[/S].*/podman rm -v -f -i --cidfile=%t/%N.cid"
|
## assert-key-is-regex "Service" "ExecStopPost" "-[/S].*/podman rm -v -f -i --cidfile=%t/%N.cid"
|
||||||
## assert-key-is-regex "Service" "ExecStop" ".*/podman rm -v -f -i --cidfile=%t/%N.cid"
|
## assert-key-is-regex "Service" "ExecStop" ".*/podman rm -v -f -i --cidfile=%t/%N.cid"
|
||||||
## assert-key-is "Service" "Environment" "PODMAN_SYSTEMD_UNIT=%n"
|
## assert-key-is "Service" "Environment" "PODMAN_SYSTEMD_UNIT=%n"
|
||||||
|
## assert-key-is "Unit" "After" "network-online.target"
|
||||||
|
## assert-key-is "Unit" "Wants" "network-online.target"
|
||||||
|
|
||||||
[Container]
|
[Container]
|
||||||
Image=localhost/imagename
|
Image=localhost/imagename
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
## assert-podman-final-args localhost/imagename
|
## assert-podman-final-args localhost/imagename
|
||||||
|
## assert-key-is "Unit" "After" "network-online.target"
|
||||||
|
## assert-key-is "Unit" "Wants" "network-online.target"
|
||||||
## assert-key-is "Unit" "RequiresMountsFor" "%t/containers"
|
## assert-key-is "Unit" "RequiresMountsFor" "%t/containers"
|
||||||
## assert-key-is "Service" "Type" "oneshot"
|
## assert-key-is "Service" "Type" "oneshot"
|
||||||
## assert-key-is "Service" "RemainAfterExit" "yes"
|
## assert-key-is "Service" "RemainAfterExit" "yes"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Mount=type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared,U=true
|
|||||||
Mount=type=volume,source=vol1,destination=/path/in/container,ro=true
|
Mount=type=volume,source=vol1,destination=/path/in/container,ro=true
|
||||||
## assert-podman-args-key-val "--mount" "," "type=volume,source=systemd-vol2,destination=/path/in/container,ro=true"
|
## assert-podman-args-key-val "--mount" "," "type=volume,source=systemd-vol2,destination=/path/in/container,ro=true"
|
||||||
## assert-key-is "Unit" "Requires" "vol2-volume.service"
|
## assert-key-is "Unit" "Requires" "vol2-volume.service"
|
||||||
## assert-key-is "Unit" "After" "vol2-volume.service"
|
## assert-key-is "Unit" "After" "network-online.target" "vol2-volume.service"
|
||||||
Mount=type=volume,source=vol2.volume,destination=/path/in/container,ro=true
|
Mount=type=volume,source=vol2.volume,destination=/path/in/container,ro=true
|
||||||
## assert-podman-args-key-val "--mount" "," "type=tmpfs,tmpfs-size=512M,destination=/path/in/container"
|
## assert-podman-args-key-val "--mount" "," "type=tmpfs,tmpfs-size=512M,destination=/path/in/container"
|
||||||
Mount=type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
Mount=type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## assert-podman-args "--network=systemd-basic"
|
## assert-podman-args "--network=systemd-basic"
|
||||||
## assert-key-is "Unit" "Requires" "basic-network.service"
|
## assert-key-is "Unit" "Requires" "basic-network.service"
|
||||||
## assert-key-is "Unit" "After" "basic-network.service"
|
## assert-key-is "Unit" "After" "network-online.target" "basic-network.service"
|
||||||
|
|
||||||
[Container]
|
[Container]
|
||||||
Image=localhost/imagename
|
Image=localhost/imagename
|
||||||
|
|||||||
7
test/e2e/quadlet/unit-after-override.container
Normal file
7
test/e2e/quadlet/unit-after-override.container
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
## assert-last-key-is-regex "Unit" "After" "^$"
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
After=
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image=localhost/imagename
|
||||||
7
test/e2e/quadlet/unit-after-override.image
Normal file
7
test/e2e/quadlet/unit-after-override.image
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
## assert-last-key-is-regex "Unit" "After" "^$"
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
After=
|
||||||
|
|
||||||
|
[Image]
|
||||||
|
Image=localhost/imagename
|
||||||
@@ -172,6 +172,24 @@ func (t *quadletTestcase) assertKeyIsRegex(args []string, unit *parser.UnitFile)
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *quadletTestcase) assertLastKeyIsRegex(args []string, unit *parser.UnitFile) bool {
|
||||||
|
Expect(len(args)).To(BeNumerically(">=", 3))
|
||||||
|
group := args[0]
|
||||||
|
key := args[1]
|
||||||
|
regex := args[2]
|
||||||
|
|
||||||
|
value, ok := unit.LookupLast(group, key)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
matched, err := regexp.MatchString(regex, value)
|
||||||
|
if err != nil || !matched {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (t *quadletTestcase) assertKeyContains(args []string, unit *parser.UnitFile) bool {
|
func (t *quadletTestcase) assertKeyContains(args []string, unit *parser.UnitFile) bool {
|
||||||
Expect(args).To(HaveLen(3))
|
Expect(args).To(HaveLen(3))
|
||||||
group := args[0]
|
group := args[0]
|
||||||
@@ -469,6 +487,8 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio
|
|||||||
ok = t.assertKeyIsRegex(args, unit)
|
ok = t.assertKeyIsRegex(args, unit)
|
||||||
case "assert-key-contains":
|
case "assert-key-contains":
|
||||||
ok = t.assertKeyContains(args, unit)
|
ok = t.assertKeyContains(args, unit)
|
||||||
|
case "assert-last-key-is-regex":
|
||||||
|
ok = t.assertLastKeyIsRegex(args, unit)
|
||||||
case "assert-podman-args":
|
case "assert-podman-args":
|
||||||
ok = t.assertStartPodmanArgs(args, unit)
|
ok = t.assertStartPodmanArgs(args, unit)
|
||||||
case "assert-podman-args-regex":
|
case "assert-podman-args-regex":
|
||||||
@@ -847,6 +867,7 @@ BOGUS=foo
|
|||||||
Entry("merged-override.container", "merged-override.container", 0, ""),
|
Entry("merged-override.container", "merged-override.container", 0, ""),
|
||||||
Entry("template@.container", "template@.container", 0, ""),
|
Entry("template@.container", "template@.container", 0, ""),
|
||||||
Entry("template@instance.container", "template@instance.container", 0, ""),
|
Entry("template@instance.container", "template@instance.container", 0, ""),
|
||||||
|
Entry("Unit After Override", "unit-after-override.container", 0, ""),
|
||||||
|
|
||||||
Entry("basic.volume", "basic.volume", 0, ""),
|
Entry("basic.volume", "basic.volume", 0, ""),
|
||||||
Entry("device-copy.volume", "device-copy.volume", 0, ""),
|
Entry("device-copy.volume", "device-copy.volume", 0, ""),
|
||||||
@@ -921,6 +942,7 @@ BOGUS=foo
|
|||||||
Entry("Image - Arch and OS", "arch-os.image", 0, ""),
|
Entry("Image - Arch and OS", "arch-os.image", 0, ""),
|
||||||
Entry("Image - global args", "globalargs.image", 0, ""),
|
Entry("Image - global args", "globalargs.image", 0, ""),
|
||||||
Entry("Image - Containers Conf Modules", "containersconfmodule.image", 0, ""),
|
Entry("Image - Containers Conf Modules", "containersconfmodule.image", 0, ""),
|
||||||
|
Entry("Image - Unit After Override", "unit-after-override.image", 0, ""),
|
||||||
|
|
||||||
Entry("basic.pod", "basic.pod", 0, ""),
|
Entry("basic.pod", "basic.pod", 0, ""),
|
||||||
Entry("name.pod", "name.pod", 0, ""),
|
Entry("name.pod", "name.pod", 0, ""),
|
||||||
|
|||||||
Reference in New Issue
Block a user