Skip to content

Commit e4be48a

Browse files
authored
Merge pull request #6 from 2uasimojo/conditional-container-push
OSD-7065: Conditional container push
2 parents 7761b62 + fd76f45 commit e4be48a

File tree

3 files changed

+107
-1
lines changed

3 files changed

+107
-1
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
- checkout
4343
- run: |
4444
echo "$DOCKER_PASS" | docker login quay.io --username $DOCKER_USER --password-stdin
45-
make container-push
45+
make conditional-container-push
4646
4747
container-release:
4848
machine:

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ container-push: container
103103
docker push $(DOCKER_REPO):$(VCS_BRANCH)-$(BUILD_DATE)-$(VERSION)
104104
docker push $(DOCKER_REPO):latest
105105

106+
.PHONY: conditional-container-push
107+
conditional-container-push:
108+
build/conditional-container-push.sh $(DOCKER_REPO):$(VCS_BRANCH)-$(BUILD_DATE)-$(VERSION)
109+
106110
.PHONY: container-release
107111
container-release: VERSION_TAG = $(strip $(shell [ -d .git ] && git tag --points-at HEAD))
108112
container-release: container
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/bin/bash
2+
3+
usage() {
4+
echo "Usage: $0 IMAGE_URI" >&2
5+
exit 1
6+
}
7+
8+
## image_exists_in_repo IMAGE_URI
9+
#
10+
# Checks whether IMAGE_URI -- e.g. quay.io/app-sre/osd-metrics-exporter:abcd123
11+
# -- exists in the remote repository.
12+
# If so, returns success.
13+
# If the image does not exist, but the query was otherwise successful, returns
14+
# failure.
15+
# If the query fails for any reason, prints an error and *exits* nonzero.
16+
#
17+
# This function cribbed from:
18+
# https://github.com/openshift/boilerplate/blob/0ba6566d544d0df9993a92b2286c131eb61f3e88/boilerplate/_lib/common.sh#L77-L135
19+
# ...then adapted to use docker rather than skopeo.
20+
image_exists_in_repo() {
21+
local image_uri=$1
22+
local output
23+
local rc
24+
local skopeo_stderr
25+
26+
skopeo_stderr=$(mktemp)
27+
output=$(docker image pull "${image_uri}" 2>"$skopeo_stderr")
28+
rc=$?
29+
# So we can delete the temp file right away...
30+
stderr=$(cat "$skopeo_stderr")
31+
rm -f "$skopeo_stderr"
32+
if [[ $rc -eq 0 ]]; then
33+
# The image exists. Sanity check the output.
34+
local report
35+
if report=$(docker image inspect "${image_uri}" 2>&1); then
36+
local digest
37+
digest=$(jq -r '.[].RepoDigests[0]' <<< "$report")
38+
if [[ "$digest" != *@* ]]; then
39+
echo "Unexpected error: docker inspect succeeded, but output contained no digest."
40+
echo "Here's the inspect output:"
41+
echo "$report"
42+
echo "...and stderr:"
43+
echo "$stderr"
44+
exit 1
45+
fi
46+
# Happy path: image exists
47+
echo "Image ${image_uri} exists with digest $digest."
48+
return 0
49+
fi
50+
echo "Unexpected error: docker inspect failed after docker pull succeded."
51+
echo "Here's the output:"
52+
echo "$report"
53+
exit 1
54+
elif [[ "$stderr" == *"manifest for"*"not found"* ]]; then
55+
# We were able to talk to the repository, but the tag doesn't exist.
56+
# This is the normal "green field" case.
57+
echo "Image ${image_uri} does not exist in the repository."
58+
return 1
59+
elif [[ "$stderr" == *"was deleted or has expired"* ]]; then
60+
# This should be rare, but accounts for cases where we had to
61+
# manually delete an image.
62+
echo "Image ${image_uri} was deleted from the repository."
63+
echo "Proceeding as if it never existed."
64+
return 1
65+
else
66+
# Any other error. For example:
67+
# - "unauthorized: access to the requested resource is not
68+
# authorized". This happens not just on auth errors, but if we
69+
# reference a repository that doesn't exist.
70+
# - "no such host".
71+
# - Network or other infrastructure failures.
72+
# In all these cases, we want to bail, because we don't know whether
73+
# the image exists (and we'd likely fail to push it anyway).
74+
echo "Error querying the repository for ${image_uri}:"
75+
echo "stdout: $output"
76+
echo "stderr: $stderr"
77+
exit 1
78+
fi
79+
}
80+
81+
set -exv
82+
83+
IMAGE_URI=$1
84+
[[ -z "$IMAGE_URI" ]] && usage
85+
86+
# NOTE(efried): Since we reference images by digest, rebuilding an image
87+
# with the same tag can be Bad. This is because the digest calculation
88+
# includes metadata such as date stamp, meaning that even though the
89+
# contents may be identical, the digest may change. In this situation,
90+
# the original digest URI no longer has any tags referring to it, so the
91+
# repository deletes it. This can break existing deployments referring
92+
# to the old digest. We could have solved this issue by generating a
93+
# permanent tag tied to each digest. We decided to do it this way
94+
# instead.
95+
# For testing purposes, if you need to force the build/push to rerun,
96+
# delete the image at $IMAGE_URI.
97+
if image_exists_in_repo "$IMAGE_URI"; then
98+
echo "Image ${IMAGE_URI} already exists. Nothing to do!"
99+
exit 0
100+
fi
101+
102+
make container-push

0 commit comments

Comments
 (0)