Skip to content

Commit

Permalink
Merge pull request #17508 from vrothberg/fix-17181
Browse files Browse the repository at this point in the history
auto-update: support pods
  • Loading branch information
openshift-merge-robot authored Feb 20, 2023
2 parents 4ffaa2a + 6dd7978 commit 8d9517c
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/source/markdown/podman-auto-update.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ At container-creation time, Podman looks up the `PODMAN_SYSTEMD_UNIT` environmen
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).
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.

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.

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.
Systemd units that start and stop a container cannot run a new image.

Expand Down
31 changes: 30 additions & 1 deletion pkg/autoupdate/autoupdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,11 @@ func (u *updater) assembleTasks(ctx context.Context) []error {

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

// systemdUnitForContainer returns the name of the container's systemd unit.
// If the container is part of a pod, the pod's infra container's systemd unit
// is returned. This allows for auto update to restart the pod's systemd unit.
func (u *updater) systemdUnitForContainer(c *libpod.Container, labels map[string]string) (string, bool, error) {
podID := c.ConfigNoCopy().Pod
if podID == "" {
unit, exists := labels[systemdDefine.EnvVariable]
return unit, exists, nil
}

pod, err := u.runtime.LookupPod(podID)
if err != nil {
return "", false, fmt.Errorf("looking up pod's systemd unit: %w", err)
}

infra, err := pod.InfraContainer()
if err != nil {
return "", false, fmt.Errorf("looking up pod's systemd unit: %w", err)
}

infraLabels := infra.Labels()
unit, exists := infraLabels[systemdDefine.EnvVariable]
return unit, exists, nil
}

// assembleImageMap creates a map from `image ID -> *libimage.Image` for image lookups.
func (u *updater) assembleImageMap(ctx context.Context) (map[string]*libimage.Image, error) {
listOptions := &libimage.ListImagesOptions{
Expand Down
68 changes: 68 additions & 0 deletions test/system/255-auto-update.bats
Original file line number Diff line number Diff line change
Expand Up @@ -525,4 +525,72 @@ EOF
rm -f $UNIT_DIR/$unit_name
}

@test "podman auto-update - pod" {
dockerfile=$PODMAN_TMPDIR/Dockerfile
cat >$dockerfile <<EOF
FROM $IMAGE
RUN touch /123
EOF

podname=$(random_string)
ctrname=$(random_string)
podunit="$UNIT_DIR/pod-$podname.service.*"
ctrunit="$UNIT_DIR/container-$ctrname.service.*"
local_image=localhost/image:$(random_string 10)

run_podman tag $IMAGE $local_image

run_podman pod create --name=$podname
run_podman create --label "io.containers.autoupdate=local" --pod=$podname --name=$ctrname $local_image top

# cd into the unit dir to generate the two files.
pushd "$UNIT_DIR"
run_podman generate systemd --name --new --files $podname
is "$output" ".*$podunit.*"
is "$output" ".*$ctrunit.*"
popd

systemctl daemon-reload

run systemctl start pod-$podname.service
assert $status -eq 0 "Error starting pod systemd unit: $output"
_wait_service_ready container-$ctrname.service

run_podman pod inspect --format "{{.State}}" $podname
is "$output" "Running" "pod is in running state"
run_podman container inspect --format "{{.State.Status}}" $ctrname
is "$output" "running" "container is in running state"

run_podman pod inspect --format "{{.ID}}" $podname
podid="$output"
run_podman container inspect --format "{{.ID}}" $ctrname
ctrid="$output"

# Note that the pod's unit is listed below, not the one of the container.
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
is "$output" ".*pod-$podname.service,$local_image,false,local.*" "No update available"

run_podman build -t $local_image -f $dockerfile

run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
is "$output" ".*pod-$podname.service,$local_image,pending,local.*" "Image updated is pending"

run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
is "$output" ".*pod-$podname.service,$local_image,true,local.*" "Service has been restarted"
_wait_service_ready container-$ctrname.service

run_podman pod inspect --format "{{.ID}}" $podname
assert "$output" != "$podid" "pod has been recreated"
run_podman container inspect --format "{{.ID}}" $ctrname
assert "$output" != "$ctrid" "container has been recreated"

run systemctl stop pod-$podname.service
assert $status -eq 0 "Error stopping pod systemd unit: $output"

run_podman pod rm -f $podname
run_podman rmi $local_image $(pause_image)
rm -f $podunit $ctrunit
systemctl daemon-reload
}

# vim: filetype=sh

0 comments on commit 8d9517c

Please sign in to comment.