Skip to content

Commit 6dd7978

Browse files
committed
auto-update: support pods
Support auto updating containers running inside pods. Similar to containers, the systemd units need to be generated via `podman-generate-systemd --new $POD` to generate the pod's units. Note that auto updating a container inside a pod will restart the entire pod. Updates of multiple containers inside a pod are batched, such that a pod is restarted at most once. That is effectively the same mechanism for auto updating containers in a K8s YAML via the `podman-kube@` template or via Quadlet. Updating a single container unit without restarting the entire pod is not possible. The reasoning behind is that pods are created with --exit-policy=stop which will render the pod to be stopped when auto updating the only container inside the pod. The (reverse) dependencies between the pod and its containers unit have been carefully selected for robustness. Changes may entail undesired side effects or backward incompatibilities that I am not comfortable with. Fixes: containers#17181 Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
1 parent 71f3e98 commit 6dd7978

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

docs/source/markdown/podman-auto-update.1.md.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ At container-creation time, Podman looks up the `PODMAN_SYSTEMD_UNIT` environmen
2626
This variable is now set by all systemd units generated by **[podman-generate-systemd](podman-generate-systemd.1.md)** and is set to `%n` (i.e., the name of systemd unit starting the container).
2727
This data is then being used in the auto-update sequence to instruct systemd (via DBUS) to restart the unit and hence to restart the container.
2828

29+
If a container configured for auto updates is part of a pod, the pod's systemd unit will be restarted and hence the entire pod and all containers inside the pod. Container updates are batched, such that a pod gets restarted at most once.
30+
2931
Note that **podman auto-update** relies on systemd. The systemd units are expected to be generated with **[podman-generate-systemd --new](podman-generate-systemd.1.md#--new)**, or similar units that create new containers in order to run the updated images.
3032
Systemd units that start and stop a container cannot run a new image.
3133

pkg/autoupdate/autoupdate.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,11 @@ func (u *updater) assembleTasks(ctx context.Context) []error {
398398

399399
// Make sure the container runs in a systemd unit which is
400400
// stored as a label at container creation.
401-
unit, exists := labels[systemdDefine.EnvVariable]
401+
unit, exists, err := u.systemdUnitForContainer(ctr, labels)
402+
if err != nil {
403+
errors = append(errors, err)
404+
continue
405+
}
402406
if !exists {
403407
errors = append(errors, fmt.Errorf("auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable))
404408
continue
@@ -436,6 +440,31 @@ func (u *updater) assembleTasks(ctx context.Context) []error {
436440
return errors
437441
}
438442

443+
// systemdUnitForContainer returns the name of the container's systemd unit.
444+
// If the container is part of a pod, the pod's infra container's systemd unit
445+
// is returned. This allows for auto update to restart the pod's systemd unit.
446+
func (u *updater) systemdUnitForContainer(c *libpod.Container, labels map[string]string) (string, bool, error) {
447+
podID := c.ConfigNoCopy().Pod
448+
if podID == "" {
449+
unit, exists := labels[systemdDefine.EnvVariable]
450+
return unit, exists, nil
451+
}
452+
453+
pod, err := u.runtime.LookupPod(podID)
454+
if err != nil {
455+
return "", false, fmt.Errorf("looking up pod's systemd unit: %w", err)
456+
}
457+
458+
infra, err := pod.InfraContainer()
459+
if err != nil {
460+
return "", false, fmt.Errorf("looking up pod's systemd unit: %w", err)
461+
}
462+
463+
infraLabels := infra.Labels()
464+
unit, exists := infraLabels[systemdDefine.EnvVariable]
465+
return unit, exists, nil
466+
}
467+
439468
// assembleImageMap creates a map from `image ID -> *libimage.Image` for image lookups.
440469
func (u *updater) assembleImageMap(ctx context.Context) (map[string]*libimage.Image, error) {
441470
listOptions := &libimage.ListImagesOptions{

test/system/255-auto-update.bats

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,4 +525,72 @@ EOF
525525
rm -f $UNIT_DIR/$unit_name
526526
}
527527

528+
@test "podman auto-update - pod" {
529+
dockerfile=$PODMAN_TMPDIR/Dockerfile
530+
cat >$dockerfile <<EOF
531+
FROM $IMAGE
532+
RUN touch /123
533+
EOF
534+
535+
podname=$(random_string)
536+
ctrname=$(random_string)
537+
podunit="$UNIT_DIR/pod-$podname.service.*"
538+
ctrunit="$UNIT_DIR/container-$ctrname.service.*"
539+
local_image=localhost/image:$(random_string 10)
540+
541+
run_podman tag $IMAGE $local_image
542+
543+
run_podman pod create --name=$podname
544+
run_podman create --label "io.containers.autoupdate=local" --pod=$podname --name=$ctrname $local_image top
545+
546+
# cd into the unit dir to generate the two files.
547+
pushd "$UNIT_DIR"
548+
run_podman generate systemd --name --new --files $podname
549+
is "$output" ".*$podunit.*"
550+
is "$output" ".*$ctrunit.*"
551+
popd
552+
553+
systemctl daemon-reload
554+
555+
run systemctl start pod-$podname.service
556+
assert $status -eq 0 "Error starting pod systemd unit: $output"
557+
_wait_service_ready container-$ctrname.service
558+
559+
run_podman pod inspect --format "{{.State}}" $podname
560+
is "$output" "Running" "pod is in running state"
561+
run_podman container inspect --format "{{.State.Status}}" $ctrname
562+
is "$output" "running" "container is in running state"
563+
564+
run_podman pod inspect --format "{{.ID}}" $podname
565+
podid="$output"
566+
run_podman container inspect --format "{{.ID}}" $ctrname
567+
ctrid="$output"
568+
569+
# Note that the pod's unit is listed below, not the one of the container.
570+
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
571+
is "$output" ".*pod-$podname.service,$local_image,false,local.*" "No update available"
572+
573+
run_podman build -t $local_image -f $dockerfile
574+
575+
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
576+
is "$output" ".*pod-$podname.service,$local_image,pending,local.*" "Image updated is pending"
577+
578+
run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
579+
is "$output" ".*pod-$podname.service,$local_image,true,local.*" "Service has been restarted"
580+
_wait_service_ready container-$ctrname.service
581+
582+
run_podman pod inspect --format "{{.ID}}" $podname
583+
assert "$output" != "$podid" "pod has been recreated"
584+
run_podman container inspect --format "{{.ID}}" $ctrname
585+
assert "$output" != "$ctrid" "container has been recreated"
586+
587+
run systemctl stop pod-$podname.service
588+
assert $status -eq 0 "Error stopping pod systemd unit: $output"
589+
590+
run_podman pod rm -f $podname
591+
run_podman rmi $local_image $(pause_image)
592+
rm -f $podunit $ctrunit
593+
systemctl daemon-reload
594+
}
595+
528596
# vim: filetype=sh

0 commit comments

Comments
 (0)