Skip to content

Commit e429292

Browse files
authored
Merge pull request #23 from sarus-suite/fc-image-lookup-hostname
Fc image lookup hostname
2 parents 039cd52 + 31609d0 commit e429292

File tree

7 files changed

+335
-26
lines changed

7 files changed

+335
-26
lines changed

.github/workflows/unified-vs-test.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ jobs:
1717
echo "Using temp directory: $TMP_DIR"
1818
1919
- name: Manually clone repo within tmp dir
20-
run: git clone --depth 1 --branch "${GITHUB_REF_NAME}" https://github.com/${GITHUB_REPOSITORY}.git
20+
run: |
21+
git clone \
22+
--depth 1 \
23+
--branch "${GITHUB_REF_NAME}" \
24+
--recurse-submodules \
25+
https://github.com/${GITHUB_REPOSITORY}.git
2126
working-directory: ${{ env.TMP_DIR }}
2227

2328
- name: List contents of TMP_DIR

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[submodule "tests/test_helper/bats-support"]
2+
path = tests/test_helper/bats-support
3+
url = https://github.com/ztombol/bats-support
4+
[submodule "tests/test_helper/bats-assert"]
5+
path = tests/test_helper/bats-assert
6+
url = https://github.com/bats-core/bats-assert.git

common/imageutil.go

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,48 +8,101 @@ import (
88
"github.com/containers/storage"
99
)
1010

11+
12+
func splitNameTag(ref string) (string, string) {
13+
lastColon := strings.LastIndex(ref, ":")
14+
lastSlash := strings.LastIndex(ref, "/")
15+
16+
// Only pick tag as last content after ":" and non empty
17+
if lastColon > lastSlash && lastColon != -1 {
18+
return ref[:lastColon], ref[lastColon+1:]
19+
}
20+
21+
// we did not find a tag
22+
return ref, "latest"
23+
}
24+
25+
26+
func hasRegistry(name string) bool {
27+
first := name
28+
29+
// find first slash and keep what is before it
30+
i := strings.IndexRune(name, '/')
31+
if i != -1 {
32+
first = name[:i]
33+
}
34+
35+
isLocalhost := (first == "localhost")
36+
hasDomain := strings.Contains(first, ".")
37+
hasPort := strings.Contains(first, ":")
38+
39+
return isLocalhost || hasDomain || hasPort
40+
}
41+
42+
43+
1144
// We get fully qualified name with more robust Resolve()
1245
func CanonicalImageName(ref string) (string, error) {
13-
parts := strings.Split(ref, ":")
14-
name := parts[0]
46+
name, tag := splitNameTag(ref)
1547

16-
// default to "latest"
17-
tag := "latest"
18-
if len(parts) == 2 && parts[1] != "" {
19-
tag = parts[1]
48+
if hasRegistry(name) {
49+
return fmt.Sprintf("%s:%s", name, tag), nil
2050
}
2151

2252
if shortnames.IsShortName(ref) {
2353
resolved, err := shortnames.Resolve(nil, ref)
2454
if err != nil {
2555
return "", fmt.Errorf("failed to resolve short name %q: %w", ref, err)
2656
}
57+
2758
if len(resolved.PullCandidates) == 0 {
2859
return "", fmt.Errorf("no resolution candidates found for short name %q", ref)
2960
}
30-
candidate := resolved.PullCandidates[0] // take first candidate
31-
return candidate.Value.String(), nil // Already includes tag
61+
62+
// take first candidate
63+
candidate := resolved.PullCandidates[0]
64+
// Candidate already includes tag
65+
return candidate.Value.String(), nil
3266
}
3367

3468
return fmt.Sprintf("%s:%s", name, tag), nil
3569
}
3670

37-
func FindImage(store storage.Store, name string) (storage.Image, error){
38-
canonical, err := CanonicalImageName(name)
39-
if err != nil {
40-
return storage.Image{}, fmt.Errorf("Resolving canonical name %q: %w", name, err)
41-
}
42-
imgs, err := store.Images()
43-
if err != nil {
44-
return storage.Image{}, fmt.Errorf("Liust iamges: %w", err)
45-
}
46-
for _, img := range imgs {
47-
for _, tag := range img.Names {
48-
if tag == name || tag == canonical {
49-
return img, nil
50-
}
51-
}
52-
}
53-
return storage.Image{}, fmt.Errorf("Image not found: %q", name)
71+
func FindImage(store storage.Store, name string) (storage.Image, error) {
72+
imgs, err := store.Images()
73+
if err != nil {
74+
return storage.Image{}, fmt.Errorf("List images: %w", err)
75+
}
76+
77+
base, tag := splitNameTag(name)
78+
79+
// Build candidate name options
80+
normalized := fmt.Sprintf("%s:%s", base, tag)
81+
82+
localhostName := ""
83+
if !hasRegistry(base) {
84+
localhostName = fmt.Sprintf("localhost/%s:%s", base, tag)
85+
}
86+
87+
canonical := ""
88+
if fq, err := CanonicalImageName(name); err == nil {
89+
canonical = fq
90+
}
91+
92+
// loop over all images and its name for a match
93+
for _, img := range imgs {
94+
for _, n := range img.Names {
95+
isExactName := (n == name)
96+
isNormalized := (n == normalized)
97+
isLocalhost := (n == localhostName)
98+
isCanonical := (n == canonical)
99+
100+
if isExactName || isNormalized || isLocalhost || isCanonical {
101+
return img, nil
102+
}
103+
}
104+
}
105+
106+
return storage.Image{}, fmt.Errorf("Image not found: %q", name)
54107
}
55108

tests/helpers.bash

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ export MOUNT_PROGRAM_PATH="${MOUNT_PROGRAM_PATH:-/mnt/nfs/git/parallax/scripts/p
88
export PODMAN_RUN_OPTIONS="${PODMAN_RUN_OPTIONS:---security-opt seccomp=unconfined}"
99

1010
setup() {
11+
load 'test_helper/bats-support/load'
12+
load 'test_helper/bats-assert/load'
13+
1114
# Seting up temp dirs and env vars
1215
export PODMAN_ROOT="$(mktemp -d)"
1316
export PODMAN_RUNROOT="$(mktemp -d)"

tests/image_references.bats

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
load helpers.bash
2+
3+
# These tests are used to validate that different ways to reference images work
4+
# pull image with ref
5+
# migrates it using ref with parallax
6+
# basic run check
7+
# removes the image
8+
# verify we are clear
9+
10+
### Helpers
11+
pull_image() {
12+
local ref="$1"
13+
"$PODMAN_BINARY" \
14+
--root "$PODMAN_ROOT" \
15+
--runroot "$PODMAN_RUNROOT" \
16+
pull "$ref"
17+
}
18+
19+
migrate_image() {
20+
local ref="$1"
21+
"$PARALLAX_BINARY" \
22+
--podmanRoot "$PODMAN_ROOT" \
23+
--roStoragePath "$RO_STORAGE" \
24+
--mksquashfsPath "$MKSQUASHFS_PATH" \
25+
--log-level info \
26+
--migrate \
27+
--image "$ref"
28+
}
29+
30+
run_image() {
31+
local ref="$1"
32+
"$PODMAN_BINARY" \
33+
--root "$CLEAN_ROOT" \
34+
--runroot "$PODMAN_RUNROOT" \
35+
--storage-opt additionalimagestore="$RO_STORAGE" \
36+
--storage-opt mount_program="$MOUNT_PROGRAM_PATH" \
37+
run --rm $PODMAN_RUN_OPTIONS "$ref" echo ok
38+
}
39+
40+
rmi_image() {
41+
local ref="$1"
42+
"$PARALLAX_BINARY" \
43+
--podmanRoot "$CLEAN_ROOT" \
44+
--roStoragePath "$RO_STORAGE" \
45+
--mksquashfsPath "$MKSQUASHFS_PATH" \
46+
--log-level info \
47+
--rmi \
48+
--image "$ref"
49+
}
50+
51+
list_squash_files() {
52+
ls "$RO_STORAGE"/overlay/**/*.squash
53+
}
54+
55+
56+
setup_registries_conf_with_alpine_alias() {
57+
local confdir
58+
confdir="$(mktemp -d)"
59+
local conffile="$confdir/my-registries.conf"
60+
61+
cat >"$conffile" <<'EOF'
62+
unqualified-search-registries = ["public.ecr.aws"]
63+
short-name-mode = "enforcing"
64+
65+
[aliases]
66+
"alpine" = "public.ecr.aws/docker/library/alpine"
67+
EOF
68+
69+
export CONTAINERS_REGISTRIES_CONF="$conffile"
70+
export PODMAN_REGISTRIES_CONF_DIR="$confdir" # keep dir around for cleanup
71+
}
72+
73+
cleanup_registries_conf() {
74+
if [ -n "$PODMAN_REGISTRIES_CONF_DIR" ]; then
75+
rm -rf "$PODMAN_REGISTRIES_CONF_DIR"
76+
unset PODMAN_REGISTRIES_CONF_DIR
77+
fi
78+
unset CONTAINERS_REGISTRIES_CONF
79+
}
80+
81+
### Tests
82+
83+
@test "image name not tagged: alpine" {
84+
setup_registries_conf_with_alpine_alias
85+
86+
run pull_image "alpine"
87+
assert_success
88+
89+
run migrate_image "alpine"
90+
assert_success
91+
assert_output --regexp 'Migration successfully completed|Nothing to do\.'
92+
93+
run run_image "alpine"
94+
assert_success
95+
assert_output "ok"
96+
97+
run rmi_image "alpine"
98+
assert_success
99+
100+
run list_squash_files
101+
assert_failure
102+
103+
cleanup_registries_conf
104+
}
105+
106+
@test "image name tagged latest: alpine:latest" {
107+
setup_registries_conf_with_alpine_alias
108+
109+
run pull_image "alpine:latest"
110+
assert_success
111+
112+
run migrate_image "alpine:latest"
113+
assert_success
114+
assert_output --regexp 'Migration successfully completed|Nothing to do\.'
115+
116+
run run_image "alpine:latest"
117+
assert_success
118+
assert_output "ok"
119+
120+
run rmi_image "alpine:latest"
121+
assert_success
122+
123+
run list_squash_files
124+
assert_failure
125+
126+
cleanup_registries_conf
127+
}
128+
129+
@test "image name tagged non latest: alpine:3.22.1" {
130+
setup_registries_conf_with_alpine_alias
131+
132+
run pull_image "alpine:3.22.1"
133+
assert_success
134+
135+
run migrate_image "alpine:3.22.1"
136+
assert_success
137+
assert_output --regexp 'Migration successfully completed|Nothing to do\.'
138+
139+
run run_image "alpine:3.22.1"
140+
assert_success
141+
assert_output "ok"
142+
143+
run rmi_image "alpine:3.22.1"
144+
assert_success
145+
146+
run list_squash_files
147+
assert_failure
148+
149+
cleanup_registries_conf
150+
}
151+
152+
153+
@test "image name with registry: docker.io/library/alpine" {
154+
setup_registries_conf_with_alpine_alias
155+
156+
run pull_image "docker.io/library/alpine"
157+
assert_success
158+
159+
run migrate_image "docker.io/library/alpine"
160+
assert_success
161+
assert_output --regexp 'Migration successfully completed|Nothing to do\.'
162+
163+
run run_image "docker.io/library/alpine"
164+
assert_success
165+
assert_output "ok"
166+
167+
run rmi_image "docker.io/library/alpine"
168+
assert_success
169+
170+
run list_squash_files
171+
assert_failure
172+
173+
cleanup_registries_conf
174+
}
175+
176+
@test "image name with registry tagged: docker.io/library/alpine:3.22.1" {
177+
setup_registries_conf_with_alpine_alias
178+
179+
run pull_image "docker.io/library/alpine:3.22.1"
180+
assert_success
181+
182+
run migrate_image "docker.io/library/alpine:3.22.1"
183+
assert_success
184+
assert_output --regexp 'Migration successfully completed|Nothing to do\.'
185+
186+
run run_image "docker.io/library/alpine:3.22.1"
187+
assert_success
188+
assert_output "ok"
189+
190+
run rmi_image "docker.io/library/alpine:3.22.1"
191+
assert_success
192+
193+
run list_squash_files
194+
assert_failure
195+
196+
cleanup_registries_conf
197+
}
198+
199+
@test "podman build and migrate from implicit localhost/alpine" {
200+
setup_registries_conf_with_alpine_alias
201+
202+
newref="new-alpine"
203+
204+
run pull_image "alpine:latest"
205+
assert_success
206+
207+
# simple containerfile
208+
buildctx="$(mktemp -d)"
209+
cat > "$buildctx/Containerfile" <<'EOF'
210+
FROM alpine:latest
211+
ENV FOO=bar
212+
# tiny no-op layer to ensure a change
213+
RUN echo "hello" > /hello.txt
214+
EOF
215+
216+
# build the new container image
217+
run "$PODMAN_BINARY" \
218+
--root "$PODMAN_ROOT" \
219+
--runroot "$PODMAN_RUNROOT" \
220+
build --pull=never -f "$buildctx/Containerfile" -t "$newref" "$buildctx"
221+
assert_success
222+
223+
run migrate_image "$newref"
224+
assert_success
225+
assert_output --regexp 'Migration successfully completed|Nothing to do\.'
226+
227+
run run_image "$newref"
228+
assert_success
229+
assert_output "ok"
230+
231+
run rmi_image "$newref"
232+
assert_success
233+
234+
run list_squash_files
235+
assert_failure
236+
237+
rm -rf "$buildctx"
238+
239+
cleanup_registries_conf
240+
}

0 commit comments

Comments
 (0)