Skip to content

Commit 0f689bd

Browse files
[patch] Preserve Docker rollback images (#25)
1 parent 23ebd73 commit 0f689bd

2 files changed

Lines changed: 99 additions & 5 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package contracttest
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
"path/filepath"
7+
"slices"
8+
"strings"
9+
"testing"
10+
)
11+
12+
func TestDockerPrunePreservesNamedRollbackImages(t *testing.T) {
13+
root := repositoryRoot(t)
14+
script := filepath.Join(root, "rootfs", "home", "cloud-compose", "docker-prune.sh")
15+
temporary := t.TempDir()
16+
profile := filepath.Join(temporary, "profile.sh")
17+
lock := filepath.Join(temporary, "docker-prune.lock")
18+
logPath := filepath.Join(temporary, "docker.log")
19+
binDirectory := filepath.Join(temporary, "bin")
20+
21+
if err := os.Mkdir(binDirectory, 0o755); err != nil {
22+
t.Fatalf("create mock binary directory: %v", err)
23+
}
24+
if err := os.WriteFile(profile, []byte("# test profile\n"), 0o600); err != nil {
25+
t.Fatalf("write test profile: %v", err)
26+
}
27+
mockDocker := []byte("#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >> \"${DOCKER_PRUNE_TEST_LOG:?}\"\n")
28+
if err := os.WriteFile(filepath.Join(binDirectory, "docker"), mockDocker, 0o755); err != nil {
29+
t.Fatalf("write mock Docker binary: %v", err)
30+
}
31+
32+
command := exec.Command("bash", script)
33+
command.Env = append(os.Environ(),
34+
"CLOUD_COMPOSE_DOCKER_PRUNE_ENABLED=true",
35+
"CLOUD_COMPOSE_DOCKER_PRUNE_LOCK_PATH="+lock,
36+
"CLOUD_COMPOSE_DOCKER_PRUNE_UNTIL=72h",
37+
"CLOUD_COMPOSE_PROFILE_PATH="+profile,
38+
"DOCKER_PRUNE_TEST_LOG="+logPath,
39+
"PATH="+binDirectory+":"+os.Getenv("PATH"),
40+
)
41+
if output, err := command.CombinedOutput(); err != nil {
42+
t.Fatalf("run Docker prune policy: %v\n%s", err, output)
43+
}
44+
45+
logBytes, err := os.ReadFile(logPath)
46+
if err != nil {
47+
t.Fatalf("read Docker invocation log: %v", err)
48+
}
49+
commands := strings.Split(strings.TrimSpace(string(logBytes)), "\n")
50+
want := []string{
51+
"container prune --force --filter until=72h",
52+
"network prune --force --filter until=72h",
53+
"image prune --force --filter until=72h",
54+
"builder prune --force --filter until=72h",
55+
}
56+
if !slices.Equal(commands, want) {
57+
t.Fatalf("Docker prune commands = %#v, want %#v", commands, want)
58+
}
59+
for _, invocation := range commands {
60+
if strings.Contains(invocation, "system prune") || strings.Contains(invocation, "image prune --all") {
61+
t.Fatalf("destructive Docker prune invocation remains: %s", invocation)
62+
}
63+
}
64+
}
65+
66+
func TestDockerPruneIsDisabledByDefault(t *testing.T) {
67+
root := repositoryRoot(t)
68+
script := filepath.Join(root, "rootfs", "home", "cloud-compose", "docker-prune.sh")
69+
temporary := t.TempDir()
70+
profile := filepath.Join(temporary, "profile.sh")
71+
if err := os.WriteFile(profile, []byte("# test profile\n"), 0o600); err != nil {
72+
t.Fatalf("write test profile: %v", err)
73+
}
74+
75+
command := exec.Command("bash", script)
76+
command.Env = append(os.Environ(),
77+
"CLOUD_COMPOSE_DOCKER_PRUNE_ENABLED=",
78+
"CLOUD_COMPOSE_PROFILE_PATH="+profile,
79+
)
80+
output, err := command.CombinedOutput()
81+
if err != nil {
82+
t.Fatalf("run disabled Docker prune policy: %v\n%s", err, output)
83+
}
84+
if !strings.Contains(string(output), "Docker pruning is disabled") {
85+
t.Fatalf("disabled Docker prune output = %q", output)
86+
}
87+
}

rootfs/home/cloud-compose/docker-prune.sh

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
set -euo pipefail
44

5-
# shellcheck disable=SC1091
6-
source /home/cloud-compose/profile.sh
5+
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
6+
profile_path="${CLOUD_COMPOSE_PROFILE_PATH:-$script_dir/profile.sh}"
7+
8+
# shellcheck disable=SC1090
9+
source "$profile_path"
710

811
case "${CLOUD_COMPOSE_DOCKER_PRUNE_ENABLED:-false}" in
912
true | TRUE | 1 | yes | YES) ;;
@@ -19,11 +22,15 @@ if [[ ! "$prune_until" =~ ^[0-9]+(s|m|h)$ ]]; then
1922
exit 2
2023
fi
2124

22-
exec 9>/run/cloud-compose-docker-prune.lock
25+
lock_path="${CLOUD_COMPOSE_DOCKER_PRUNE_LOCK_PATH:-/run/cloud-compose-docker-prune.lock}"
26+
exec 9>"$lock_path"
2327
if command -v flock >/dev/null 2>&1 && ! flock -n 9; then
2428
echo "A Cloud Compose Docker prune is already running"
2529
exit 0
2630
fi
2731

28-
echo "Pruning unused Docker data older than $prune_until"
29-
docker system prune --all --force --filter "until=$prune_until"
32+
echo "Pruning stopped containers, unused networks, dangling images, and build cache older than $prune_until"
33+
docker container prune --force --filter "until=$prune_until"
34+
docker network prune --force --filter "until=$prune_until"
35+
docker image prune --force --filter "until=$prune_until"
36+
docker builder prune --force --filter "until=$prune_until"

0 commit comments

Comments
 (0)