Skip to content

Commit d9dc1b5

Browse files
committed
fix(RELEASE-2650): add timestamp tag to data-acceptable-bundles
The update-trusted-tasks task now creates timestamp-based tags for the data-acceptable-bundles OCI artifact to prevent garbage collection. This follows the pattern used in build-definitions. The task now: - Generates a timestamp at the start (unix epoch format) - Outputs ec track bundle to the timestamp tag - Copies the timestamp tag to :latest after each bundle is added - Optimizes by checking :latest existence once per repository This ensures that even when :latest moves to a new digest, the old digest remains tagged and won't be garbage collected, preventing breakage of digest-pinned references. Assisted-by: Claude Code Signed-off-by: Martin Malina <mmalina@redhat.com>
1 parent cf2fc0e commit d9dc1b5

7 files changed

Lines changed: 120 additions & 36 deletions

tasks/managed/update-trusted-tasks/tests/mocks.sh

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
set -eux
33

44
# mocks to be injected into task step scripts
5+
function date() {
6+
# Return a fixed timestamp for testing
7+
if [[ "$*" == "'+%s'" ]]; then
8+
echo "1234567890"
9+
return
10+
fi
11+
12+
# Fall back to real date for other uses
13+
command date "$@"
14+
}
15+
516
function skopeo() {
617
echo Mock skopeo called with: $* >&2
718
echo $* >> "$(params.dataDir)/mock_skopeo.txt"
@@ -12,6 +23,9 @@ function skopeo() {
1223
elif [[ "$*" =~ list-tags\ docker://quay.io ]]; then
1324
echo '{"Tags": ["v2.0.0-4", "v2.0.0-3", "v2.0.0-2"]}'
1425
return
26+
elif [[ "$*" =~ ^copy\ oci:.* ]]; then
27+
# Mock skopeo copy - just record that it was called
28+
return
1529
fi
1630

1731
echo Error: Unexpected call
@@ -24,11 +38,11 @@ function ec() {
2438

2539
if [[ "$*" =~ "track bundle".*fail-image.* ]]; then
2640
exit 1
27-
41+
2842
elif [[ "$*" =~ "track bundle".* ]]; then
2943
return
3044
fi
31-
45+
3246
echo Error: Unexpected call
3347
exit 1
3448
}

tasks/managed/update-trusted-tasks/tests/test-update-trusted-tasks-fail-ec.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,14 @@ spec:
131131
#!/bin/bash
132132
set -eux
133133
134+
# Expect only 1 skopeo call for list-tags (task fails before copy)
134135
if [ "$(wc -l < "$(params.dataDir)/mock_skopeo.txt")" != 1 ]; then
135-
echo Error: skopeo was expected to be called 1 times. Actual calls:
136+
echo Error: skopeo was expected to be called 1 time. Actual calls:
136137
cat "$(params.dataDir)/mock_skopeo.txt"
137138
exit 1
138139
fi
139140
141+
# Expect 1 ec call which should fail
140142
if [ "$(wc -l < "$(params.dataDir)/mock_ec.txt")" != 1 ]; then
141143
echo Error: ec was expected to be called 1 time. Actual calls:
142144
cat "$(params.dataDir)/mock_ec.txt"

tasks/managed/update-trusted-tasks/tests/test-update-trusted-tasks-success-latest.yaml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ spec:
163163
#!/bin/bash
164164
set -eux
165165
166-
if [ "$(wc -l < "$(params.dataDir)/mock_skopeo.txt")" != 1 ]; then
167-
echo Error: skopeo was expected to be called 1 times. Actual calls:
166+
# Expect 2 skopeo calls: 1 for list-tags, 1 for copy to tag as :latest
167+
if [ "$(wc -l < "$(params.dataDir)/mock_skopeo.txt")" != 2 ]; then
168+
echo Error: skopeo was expected to be called 2 times. Actual calls:
168169
cat "$(params.dataDir)/mock_skopeo.txt"
169170
exit 1
170171
fi
@@ -175,12 +176,21 @@ spec:
175176
exit 1
176177
fi
177178
179+
# Expect ec track bundle to use :latest as input and output to timestamp tag
178180
out="track bundle --bundle quay.io/exists/task-echo:0.1@sha256:abcde \
179181
--input oci:quay.io/exists/data-acceptable-bundles:latest \
180-
--output oci:quay.io/exists/data-acceptable-bundles:latest"
182+
--output oci:quay.io/exists/data-acceptable-bundles:1234567890"
181183
182184
if ! grep -qF -- "$out" "$(params.dataDir)/mock_ec.txt"; then
183185
echo "Error: $out was not found in the ec command"
184186
cat "$(params.dataDir)/mock_ec.txt"
185187
exit 1
186188
fi
189+
190+
# Expect skopeo copy to tag the timestamp image as :latest
191+
copy_cmd="copy oci:quay.io/exists/data-acceptable-bundles:1234567890 oci:quay.io/exists/data-acceptable-bundles:latest"
192+
if ! grep -qF -- "$copy_cmd" "$(params.dataDir)/mock_skopeo.txt"; then
193+
echo "Error: $copy_cmd was not found in the skopeo commands"
194+
cat "$(params.dataDir)/mock_skopeo.txt"
195+
exit 1
196+
fi

tasks/managed/update-trusted-tasks/tests/test-update-trusted-tasks-success-multiple-components.yaml

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,30 +195,33 @@ spec:
195195
#!/bin/bash
196196
set -eux
197197
198-
if [ "$(wc -l < "$(params.dataDir)/mock_skopeo.txt")" != 3 ]; then
199-
echo Error: skopeo was expected to be called 1 times. Actual calls:
198+
# Expect 5 skopeo calls:
199+
# 2 for list-tags (one for notexist, one for exists repo)
200+
# 3 for copy (one per component)
201+
if [ "$(wc -l < "$(params.dataDir)/mock_skopeo.txt")" != 5 ]; then
202+
echo Error: skopeo was expected to be called 5 times. Actual calls:
200203
cat "$(params.dataDir)/mock_skopeo.txt"
201204
exit 1
202205
fi
203206
204207
if [ "$(wc -l < "$(params.dataDir)/mock_ec.txt")" != 3 ]; then
205-
echo Error: ec was expected to be called 1 time. Actual calls:
208+
echo Error: ec was expected to be called 3 times. Actual calls:
206209
cat "$(params.dataDir)/mock_ec.txt"
207210
exit 1
208211
fi
209212
210213
all_found=true
211214
outs=(
212215
"track bundle --bundle quay.io/notexist/task-echo-v01:0.1@sha256:abcde \
213-
--output oci:quay.io/notexist/data-acceptable-bundles:latest"
216+
--output oci:quay.io/notexist/data-acceptable-bundles:1234567890"
214217
215218
"track bundle --bundle quay.io/exists/task-echo-v02:0.2@sha256:abcde \
216219
--input oci:quay.io/exists/data-acceptable-bundles:latest \
217-
--output oci:quay.io/exists/data-acceptable-bundles:latest"
220+
--output oci:quay.io/exists/data-acceptable-bundles:1234567890"
218221
219222
"track bundle --bundle quay.io/exists/task-echo:0.3@sha256:abcde \
220223
--input oci:quay.io/exists/data-acceptable-bundles:latest \
221-
--output oci:quay.io/exists/data-acceptable-bundles:latest"
224+
--output oci:quay.io/exists/data-acceptable-bundles:1234567890"
222225
)
223226
224227
for out in "${outs[@]}"; do
@@ -231,3 +234,17 @@ spec:
231234
cat "$(params.dataDir)/mock_ec.txt"
232235
exit 1
233236
fi
237+
238+
# Expect 3 skopeo copy commands
239+
if ! grep -qF "copy oci:quay.io/notexist/data-acceptable-bundles:1234567890 oci:quay.io/notexist/data-acceptable-bundles:latest" "$(params.dataDir)/mock_skopeo.txt"; then
240+
echo "Error: expected copy for notexist repo"
241+
cat "$(params.dataDir)/mock_skopeo.txt"
242+
exit 1
243+
fi
244+
245+
copy_count=$(grep -cF "copy oci:quay.io/exists/data-acceptable-bundles:1234567890 oci:quay.io/exists/data-acceptable-bundles:latest" "$(params.dataDir)/mock_skopeo.txt" || true)
246+
if [ "$copy_count" != 2 ]; then
247+
echo "Error: expected 2 copy commands for exists repo, found $copy_count"
248+
cat "$(params.dataDir)/mock_skopeo.txt"
249+
exit 1
250+
fi

tasks/managed/update-trusted-tasks/tests/test-update-trusted-tasks-success-multiple-tags.yaml

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,28 +159,32 @@ spec:
159159
#!/bin/bash
160160
set -eux
161161
162-
if [ "$(wc -l < "$(params.dataDir)/mock_skopeo.txt")" != 3 ]; then
163-
echo Error: skopeo was expected to be called 1 times. Actual calls:
162+
# Expect 4 skopeo calls: 1 for list-tags, 3 for copy (one per tag)
163+
if [ "$(wc -l < "$(params.dataDir)/mock_skopeo.txt")" != 4 ]; then
164+
echo Error: skopeo was expected to be called 4 times. Actual calls:
164165
cat "$(params.dataDir)/mock_skopeo.txt"
165166
exit 1
166167
fi
167168
168169
if [ "$(wc -l < "$(params.dataDir)/mock_ec.txt")" != 3 ]; then
169-
echo Error: ec was expected to be called 1 time. Actual calls:
170+
echo Error: ec was expected to be called 3 times. Actual calls:
170171
cat "$(params.dataDir)/mock_ec.txt"
171172
exit 1
172173
fi
173174
174175
all_found=true
176+
# First tag creates the bundle (no input), subsequent ones use :latest as input
175177
outs=(
176178
"track bundle --bundle quay.io/notexist/task-echo:0.1@sha256:abcde \
177-
--output oci:quay.io/notexist/data-acceptable-bundles:latest"
179+
--output oci:quay.io/notexist/data-acceptable-bundles:1234567890"
178180
179181
"track bundle --bundle quay.io/notexist/task-echo:test@sha256:abcde \
180-
--output oci:quay.io/notexist/data-acceptable-bundles:latest"
182+
--input oci:quay.io/notexist/data-acceptable-bundles:latest \
183+
--output oci:quay.io/notexist/data-acceptable-bundles:1234567890"
181184
182185
"track bundle --bundle quay.io/notexist/task-echo:stage@sha256:abcde \
183-
--output oci:quay.io/notexist/data-acceptable-bundles:latest"
186+
--input oci:quay.io/notexist/data-acceptable-bundles:latest \
187+
--output oci:quay.io/notexist/data-acceptable-bundles:1234567890"
184188
)
185189
186190
for out in "${outs[@]}"; do
@@ -193,3 +197,11 @@ spec:
193197
cat "$(params.dataDir)/mock_ec.txt"
194198
exit 1
195199
fi
200+
201+
# Expect 3 skopeo copy commands (one after each ec track bundle)
202+
copy_count=$(grep -cF "copy oci:quay.io/notexist/data-acceptable-bundles:1234567890 oci:quay.io/notexist/data-acceptable-bundles:latest" "$(params.dataDir)/mock_skopeo.txt" || true)
203+
if [ "$copy_count" != 3 ]; then
204+
echo "Error: expected 3 skopeo copy commands, found $copy_count"
205+
cat "$(params.dataDir)/mock_skopeo.txt"
206+
exit 1
207+
fi

tasks/managed/update-trusted-tasks/tests/test-update-trusted-tasks-success-no-latest.yaml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,9 @@ spec:
158158
#!/bin/bash
159159
set -eux
160160
161-
if [ "$(wc -l < "$(params.dataDir)/mock_skopeo.txt")" != 1 ]; then
162-
echo Error: skopeo was expected to be called 1 times. Actual calls:
161+
# Expect 2 skopeo calls: 1 for list-tags, 1 for copy to tag as :latest
162+
if [ "$(wc -l < "$(params.dataDir)/mock_skopeo.txt")" != 2 ]; then
163+
echo Error: skopeo was expected to be called 2 times. Actual calls:
163164
cat "$(params.dataDir)/mock_skopeo.txt"
164165
exit 1
165166
fi
@@ -170,11 +171,20 @@ spec:
170171
exit 1
171172
fi
172173
174+
# Expect ec track bundle to output to timestamp tag (1234567890 from mocked date)
173175
out="track bundle --bundle quay.io/notexist/task-echo:0.1@sha256:abcde \
174-
--output oci:quay.io/notexist/data-acceptable-bundles:latest"
176+
--output oci:quay.io/notexist/data-acceptable-bundles:1234567890"
175177
176178
if ! grep -qF -- "$out" "$(params.dataDir)/mock_ec.txt"; then
177179
echo "Error: $out was not found in the ec command"
178180
cat "$(params.dataDir)/mock_ec.txt"
179181
exit 1
180182
fi
183+
184+
# Expect skopeo copy to tag the timestamp image as :latest
185+
copy_cmd="copy oci:quay.io/notexist/data-acceptable-bundles:1234567890 oci:quay.io/notexist/data-acceptable-bundles:latest"
186+
if ! grep -qF -- "$copy_cmd" "$(params.dataDir)/mock_skopeo.txt"; then
187+
echo "Error: $copy_cmd was not found in the skopeo commands"
188+
cat "$(params.dataDir)/mock_skopeo.txt"
189+
exit 1
190+
fi

tasks/managed/update-trusted-tasks/update-trusted-tasks.yaml

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,19 @@ spec:
127127
set -eux
128128
129129
SNAPSHOT_SPEC_FILE="$(params.dataDir)/$(params.snapshotPath)"
130-
TAG="latest"
131130
132131
# check if snapshot exsits
133132
if [ ! -f "${SNAPSHOT_SPEC_FILE}" ] ; then
134133
echo "No valid snapshot file was found."
135134
exit 1
136135
fi
137136
137+
# The OPA data bundle is tagged with the current timestamp. This has two main
138+
# advantages. First, it prevents the image from accidentally not having any tags,
139+
# and getting garbage collected. Second, it helps us create a timeline of the
140+
# changes done to the data over time.
141+
TIMESTAMP_TAG=$(date '+%s')
142+
138143
# Extract the componentGroup from the snapshot
139144
componentGroup=$(jq -r '.componentGroup' "${SNAPSHOT_SPEC_FILE}")
140145
@@ -165,6 +170,20 @@ spec:
165170
ACCEPTABLE_BUNDLES=$(echo "${repository}" \
166171
| awk -F'/' '{$NF="data-acceptable-bundles"; print $0}' OFS='/')
167172
173+
# Check once if :latest exists for this ACCEPTABLE_BUNDLES repo
174+
set +e
175+
skopeo list-tags docker://"${ACCEPTABLE_BUNDLES}" | jq -r '.Tags[]' | grep "^latest$" &> /dev/null
176+
RESULT=$?
177+
set -e
178+
179+
if [ ${RESULT} -eq 0 ]; then
180+
echo "${ACCEPTABLE_BUNDLES}:latest exists - using it as an input"
181+
LATEST_EXISTS="true"
182+
else
183+
echo "${ACCEPTABLE_BUNDLES}:latest does not exist"
184+
LATEST_EXISTS="false"
185+
fi
186+
168187
# Get the number of tags from the repository
169188
NUM_TAGS=$(jq --argjson j "$j" '.repositories[$j].tags | length' <<< "$component")
170189
@@ -173,24 +192,24 @@ spec:
173192
do
174193
imageTag=$(jq -c -r --argjson j "$j" --argjson k "$k" '.repositories[$j].tags[$k]' <<< "$component")
175194
176-
set +e
177-
# Check if ACCEPTABLE_BUNDLES OCI artifact has a latest tag
178-
skopeo list-tags docker://"${ACCEPTABLE_BUNDLES}" | jq -r '.Tags[]' | grep "^${TAG}$" &> /dev/null
179-
RESULT=$?
180-
set -e
181-
182-
# If it has a latest tag, use it as an --input for the ec track bundle command
183-
if [ $RESULT -eq 0 ]; then
184-
echo "${ACCEPTABLE_BUNDLES}:${TAG} exists - using it as an input"
195+
# If :latest exists, use it as an --input for the ec track bundle command
196+
# Output to the timestamp tag to prevent garbage collection
197+
if [[ "${LATEST_EXISTS}" == "true" ]]; then
198+
echo "Adding ${repository}:${imageTag}${sha} to ${ACCEPTABLE_BUNDLES}:${TIMESTAMP_TAG}"
185199
ec track bundle --bundle "${repository}:${imageTag}${sha}" \
186-
--input oci:"${ACCEPTABLE_BUNDLES}:${TAG}" --output oci:"${ACCEPTABLE_BUNDLES}:${TAG}"
187-
188-
# Else - Do not use it as an --input
200+
--input oci:"${ACCEPTABLE_BUNDLES}:latest" --output oci:"${ACCEPTABLE_BUNDLES}:${TIMESTAMP_TAG}"
189201
else
190-
echo "${ACCEPTABLE_BUNDLES}:${TAG} does not exist"
202+
echo "Creating ${ACCEPTABLE_BUNDLES}:${TIMESTAMP_TAG} with ${repository}:${imageTag}${sha}"
191203
ec track bundle --bundle "${repository}:${imageTag}${sha}" \
192-
--output oci:"${ACCEPTABLE_BUNDLES}:${TAG}"
204+
--output oci:"${ACCEPTABLE_BUNDLES}:${TIMESTAMP_TAG}"
193205
fi
206+
207+
# Tag the timestamp-based image as :latest for the next iteration
208+
echo "Tagging ${ACCEPTABLE_BUNDLES}:${TIMESTAMP_TAG} as :latest"
209+
skopeo copy "oci:${ACCEPTABLE_BUNDLES}:${TIMESTAMP_TAG}" "oci:${ACCEPTABLE_BUNDLES}:latest"
210+
211+
# Now that we've created :latest, mark it as existing for subsequent tags
212+
LATEST_EXISTS="true"
194213
done
195214
done
196215
done

0 commit comments

Comments
 (0)