@@ -86,277 +86,41 @@ spec:
8686 runAsUser : 1001
8787 steps :
8888 - name : create-advisory
89- image : quay.io/konflux-ci /release-service-utils@sha256:9460d206ab78a096679cf0d96bf812b3f9a5227dd2f7061e06e8e58c49cdad16
89+ image : quay.io/jbieren /release-service-utils:advmem_1
9090 computeResources :
9191 limits :
92- memory : 256Mi
92+ memory : 512Mi
9393 requests :
94- memory : 256Mi
94+ memory : 512Mi
9595 cpu : ' 1' # 1 is the max allowed by at least the staging cluster
9696 volumeMounts :
9797 - name : advisory-secret
9898 mountPath : /mnt/advisory_secret
9999 - name : errata-secret
100100 mountPath : /mnt/errata_secret
101101 env :
102- - name : " ADVISORY_JSON"
103- value : " $(params.advisory_json)"
104- script : |
105- #!/usr/bin/env bash
106- set -eo pipefail
107-
108- GITLAB_HOST="$(cat /mnt/advisory_secret/gitlab_host)"
109-
110- # This is a GitLab Project access token. Go to the settings/access_tokens page
111- # of your repository to create one. It should have the Developer role with read
112- # and write repository rights.
113- ACCESS_TOKEN="$(cat /mnt/advisory_secret/gitlab_access_token)"
114-
115- GIT_AUTHOR_NAME="$(cat /mnt/advisory_secret/git_author_name)"
116- GIT_AUTHOR_EMAIL="$(cat /mnt/advisory_secret/git_author_email)"
117- GIT_REPO="$(cat /mnt/advisory_secret/git_repo)"
118- ERRATA_API="$(cat /mnt/errata_secret/errata_api)"
119- SERVICE_ACCOUNT_NAME="$(cat /mnt/errata_secret/name)"
120- SERVICE_ACCOUNT_KEYTAB="$(cat /mnt/errata_secret/base64_keytab)"
121-
122- # export variables required by the called script "gitlab-functions" in release-service-utils
123- export GITLAB_HOST ACCESS_TOKEN GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
124-
125- STDERR_FILE=/tmp/stderr.txt
126- echo -n "$(params.internalRequestPipelineRunName)" > "$(results.internalRequestPipelineRunName.path)"
127- echo -n "$(context.taskRun.name)" > "$(results.internalRequestTaskRunName.path)"
128-
129- exitfunc() {
130- local err=$1
131- local line=$2
132- local command="$3"
133- if [ "$err" -eq 0 ] ; then
134- echo -n "Success" > "$(results.result.path)"
135- else
136- echo -n \
137- "$0: ERROR '$command' failed at line $line - exited with status $err" > "$(results.result.path)"
138- if [ -f "$STDERR_FILE" ] ; then
139- tail -n 20 "$STDERR_FILE" >> "$(results.result.path)"
140- fi
141- fi
142- echo -n "${ADVISORY_URL}" > "$(results.advisory_url.path)"
143- echo -n "${ADVISORY_INTERNAL_URL}" > "$(results.advisory_internal_url.path)"
144- exit 0 # exit the script cleanly as there is no point in proceeding past an error or exit call
145- }
146- # due to set -e, this catches all EXIT and ERR calls and the task should never fail with nonzero exit code
147- trap 'exitfunc $? $LINENO "$BASH_COMMAND"' EXIT
148-
149- REPO_BRANCH=main
150- ADVISORY_URL=""
151- ADVISORY_INTERNAL_URL=""
152- ADVISORY_BASE_DIR="data/advisories/$(params.origin)"
153- if [[ "${GIT_REPO}" == *"/rhtap-release/"* ]]; then
154- ADVISORY_URL_PREFIX="https://access.stage.redhat.com/errata"
155- else
156- ADVISORY_URL_PREFIX="https://access.redhat.com/errata"
157- fi
158-
159- # Switch to /tmp to avoid filesystem permission issues
160- cd /tmp
161-
162- # loading git and gitlab functions
163- # shellcheck source=/dev/null
164- . /home/utils/gitlab-functions
165- # shellcheck source=/dev/null
166- . /home/utils/git-functions
167- gitlab_init
168- git_functions_init
169-
170- # This also cds into the git repo
171- git_clone_and_checkout --repository "$GIT_REPO" --revision "$REPO_BRANCH" \
172- --sparse-dir "$ADVISORY_BASE_DIR" --sparse-dir schema
173-
174- if [ "$(params.contentType)" = "image" ]; then
175- echo "Content type is image."
176- spec_content_type=".content.images"
177- elif [ "$(params.contentType)" == "binary" ] || [ "$(params.contentType)" == "generic" ] \
178- || [ "$(params.contentType)" == "rpm" ]; then
179- echo "Content type is generic or rpm artifact."
180- spec_content_type=".content.artifacts"
181- else
182- echo "Unsupported contentType: $(params.contentType)"| tee -a "$STDERR_FILE"
183- echo "Exiting." | tee -a "$STDERR_FILE"
184- exit 1
185- fi
186- CONTENT_FILE=/tmp/content.json
187- # Write the advisory JSON parameter to a file to avoid argument length limits
188- printf '%s' "$ADVISORY_JSON" | base64 --decode | gunzip > /tmp/advisory_decoded.json
189- jq -c "${spec_content_type} // []" /tmp/advisory_decoded.json > "$CONTENT_FILE"
190-
191- # Use ISO 8601 format in UTC/Zulu time, e.g. 2024-03-06T17:27:38Z
192- SHIP_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
193- YEAR=${SHIP_DATE%%-*} # derive the year from the ship date
194- # Define advisory directory
195- echo "Checking advisories in directory: ${ADVISORY_BASE_DIR}"
196-
197- # Check existing advisories across ALL years
198- EXISTING_ADVISORIES=""
199- if [ -d "${ADVISORY_BASE_DIR}" ]; then
200- EXISTING_ADVISORIES=$(
201- # year/advisory dir with modified time
202- find "${ADVISORY_BASE_DIR}" -mindepth 2 -type d -printf "%T@ %p\n" |
203- sort -nr | # sort by latest modified first
204- cut -d' ' -f2- | # remove the timestamp, keep only path
205- sed "s|^${ADVISORY_BASE_DIR}/||" # keeping year/advisory format
206- )
207- fi
208-
209- if [[ -z "$EXISTING_ADVISORIES" ]]; then
210- echo "No existing advisories found."
211- fi
212-
213- # Track the latest advisory that contains matching content
214- # EXISTING_ADVISORIES is sorted by modification time (newest first)
215- LATEST_ADVISORY_FILE=""
216-
217- EXISTING_CONTENT=/tmp/existing_content.json
218- for ADVISORY_SUBDIR in $EXISTING_ADVISORIES; do
219- ADVISORY_FILE="${ADVISORY_BASE_DIR}/${ADVISORY_SUBDIR}/advisory.yaml"
220- yq -o=json ".spec${spec_content_type} // []" "${ADVISORY_FILE}" > "$EXISTING_CONTENT"
221- echo "Processing advisory: ${ADVISORY_FILE}"
222- echo "Existing content in advisory: "
223- cat "$EXISTING_CONTENT"
224-
225- # Check if this advisory contains any matching content before filtering
226- CONTENT_BEFORE_FILTER=$(cat "$CONTENT_FILE")
227-
228- # Update CONTENT by removing entries that already exist in the advisory
229- if [ "$(params.contentType)" == "generic" ] || [ "$(params.contentType)" == "binary" ]; then
230- # Use purl as unique key, but strip checksum= for comparison
231- # This allows re-releases (with new checksums from re-signing) to update existing advisories
232- # The filename= param (if present) ensures we match the correct file
233- jq --slurpfile existing "$EXISTING_CONTENT" '
234- # Function to strip checksum param from purl for comparison
235- def strip_checksum:
236- gsub("&checksum=[^&]*"; "") | gsub("\\?checksum=[^&]*&"; "?") | gsub("\\?checksum=[^&]*$"; "");
237- map(select(
238- (.purl | strip_checksum) as $p |
239- ($existing[0] | map(select((.purl | strip_checksum) == $p)) | length == 0)
240- ))' "$CONTENT_FILE" > /tmp/content_filtered.json
241- elif [ "$(params.contentType)" == "rpm" ] || [ "$(params.contentType)" == "disk-image" ]; then
242- # Use exact purl matching for RPM and disk-image (checksums are stable, no re-signing)
243- jq --slurpfile existing "$EXISTING_CONTENT" '
244- map(select(
245- .purl as $p |
246- ($existing[0] | map(select(.purl == $p)) | length == 0)
247- ))' "$CONTENT_FILE" > /tmp/content_filtered.json
248- else
249- jq --slurpfile existing "$EXISTING_CONTENT" '
250- map(select(
251- .containerImage as $ci |
252- .tags as $tags |
253- .repository as $repo |
254- ($existing[0] | map(select(
255- .containerImage == $ci and .tags == $tags and .repository == $repo
256- )) | length == 0)
257- ))' "$CONTENT_FILE" > /tmp/content_filtered.json
258- fi
259-
260- mv /tmp/content_filtered.json "$CONTENT_FILE"
261-
262- echo "Remaining entries after filtering:"
263- cat "$CONTENT_FILE"
264-
265- CONTENT_BEFORE_COUNT=$(jq 'length' <<< "$CONTENT_BEFORE_FILTER")
266- CONTENT_AFTER_COUNT=$(jq 'length' "$CONTENT_FILE")
267- if [[ $CONTENT_BEFORE_COUNT -gt $CONTENT_AFTER_COUNT ]]; then
268- if [[ -z "$LATEST_ADVISORY_FILE" ]]; then
269- LATEST_ADVISORY_FILE="$ADVISORY_FILE"
270- FILTERED_COUNT=$((CONTENT_BEFORE_COUNT - CONTENT_AFTER_COUNT))
271- echo "Tracked latest advisory: $LATEST_ADVISORY_FILE (filtered $FILTERED_COUNT items)"
272- fi
273- fi
274-
275- # If after filtering, no entries are left, then we can exit early
276- if jq -e 'length == 0' "$CONTENT_FILE" >/dev/null; then
277- echo "All content found in existing advisories. Skipping creation."
278- echo "Returning advisory: $LATEST_ADVISORY_FILE"
279-
280- ADVISORY_INTERNAL_URL="${GIT_REPO//\.git/}/-/raw/main/${LATEST_ADVISORY_FILE}"
281- ADVISORY_TYPE=$(yq -r '.spec.type' "${LATEST_ADVISORY_FILE}")
282- ADVISORY_NAME=$(yq -r '.metadata.name' "${LATEST_ADVISORY_FILE}")
283- ADVISORY_URL="${ADVISORY_URL_PREFIX}/${ADVISORY_TYPE}-${ADVISORY_NAME}"
284- echo -n "Success" > "$(results.result.path)"
285- echo -n "${ADVISORY_URL}" > "$(results.advisory_url.path)"
286- echo -n "$ADVISORY_INTERNAL_URL" > "$(results.advisory_internal_url.path)"
287- exit 0
288- fi
289- done
290-
291- NEW_ADVISORY_JSON=$(jq --slurpfile new_content "$CONTENT_FILE" \
292- "${spec_content_type} = \$new_content[0]" /tmp/advisory_decoded.json)
293-
294- signingKey=$(kubectl get configmap "$(params.config_map_name)" -o jsonpath="{.data.SIG_KEY_NAME}")
295- # Write to temp file to avoid argument length limits
296- echo "$NEW_ADVISORY_JSON" > /tmp/new_advisory.json
297- # Add signingKey only if not already present in the artifact (supports pre-populated values from
298- # populate-release-notes for RPM releases)
299- jq -c --arg key "$signingKey" \
300- "${spec_content_type} |= map(if .signingKey then . else . + {\"signingKey\": \$key} end)" \
301- /tmp/new_advisory.json > /tmp/advisory_with_key.json
302-
303- LIVE_ID=$(jq -r '.live_id' /tmp/advisory_decoded.json)
304- if [[ "$LIVE_ID" == null ]]; then
305- # write keytab to file
306- echo -n "${SERVICE_ACCOUNT_KEYTAB}" | base64 --decode > /tmp/keytab
307- # workaround kinit: Invalid UID in persistent keyring name while getting default ccache
308- KRB5CCNAME=$(mktemp)
309- export KRB5CCNAME
310- # see https://stackoverflow.com/a/12308187
311- KRB5_CONFIG=$(mktemp)
312- export KRB5_CONFIG
313- export KRB5_TRACE=/dev/stderr
314- sed '/\[libdefaults\]/a\ dns_canonicalize_hostname = false' /etc/krb5.conf > "${KRB5_CONFIG}"
315- retry 5 kinit "${SERVICE_ACCOUNT_NAME}" -k -t /tmp/keytab
316- REQUEST_URL="${ERRATA_API}/advisory/reserve_live_id"
317- LIVE_ID=$(curl --retry 3 --negotiate -u : "${REQUEST_URL}" -XPOST | jq -r '.live_id')
318- fi
319- ADVISORY_NUM=$(printf "%04d" "$LIVE_ID")
320-
321- # Check if the advisory number is already used
322- GIT_RESULT_FILE=$(mktemp)
323- git ls-tree -r --name-only origin/main > "$GIT_RESULT_FILE"
324- GREP_RESULT=$(grep "data/advisories/.*/${YEAR}/${ADVISORY_NUM}/" "$GIT_RESULT_FILE" || true)
325- if [[ -n "${GREP_RESULT}" ]]; then
326- echo "An advisory with number ${ADVISORY_NUM} already exists:" | tee -a "$STDERR_FILE"
327- echo "${GREP_RESULT}" | tee -a "$STDERR_FILE"
328- echo "Exiting." | tee -a "$STDERR_FILE"
329- exit 1
330- fi
331-
332- # group advisories by <origin workspace>/year
333- ADVISORY_DIR="data/advisories/$(params.origin)/${YEAR}/${ADVISORY_NUM}"
334- mkdir -p "${ADVISORY_DIR}"
335- JSON_ADVISORY_FILEPATH="${ADVISORY_DIR}/advisory.json"
336- YAML_ADVISORY_FILEPATH="${ADVISORY_DIR}/advisory.yaml"
337- ADVISORY_NAME="${YEAR}:${ADVISORY_NUM}"
338-
339- # Prepare variables for the advisory template
340- # Write to file to avoid argument length limits
341- jq -c '{"advisory":{"spec":.}}' /tmp/advisory_with_key.json > /tmp/template_data.json
342- jq -c --arg advisory_name "$ADVISORY_NAME" --arg advisory_ship_date "$SHIP_DATE" \
343- '$ARGS.named + .' /tmp/template_data.json > /tmp/template_data_final.json
344-
345- # Create advisory file using the apply_template.py script
346- /home/utils/apply_template.py -o "$JSON_ADVISORY_FILEPATH" --data-file /tmp/template_data_final.json \
347- --template /home/templates/advisory.yaml.jinja -v 2> "$STDERR_FILE"
348-
349- # Convert to yaml for readability
350- yq eval -o yaml "$JSON_ADVISORY_FILEPATH" | tee "$YAML_ADVISORY_FILEPATH"
351-
352- # Ensure the created advisory file passes the advisory schema
353- check-jsonschema --schemafile schema/advisory.json "$YAML_ADVISORY_FILEPATH" 2>&1 | tee "$STDERR_FILE"
354-
355- git add "${YAML_ADVISORY_FILEPATH}"
356- git commit -m "[Konflux Release] new advisory for $(params.componentGroup)"
357- echo "Pushing to ${REPO_BRANCH}..."
358- git_push_with_retries --branch $REPO_BRANCH --retries 5 --url origin 2> "$STDERR_FILE"
359- # Construct the advisory url on customer portal to report back to the user as a result
360- ADVISORY_TYPE=$(jq -r '.type' /tmp/advisory_decoded.json)
361- ADVISORY_URL="${ADVISORY_URL_PREFIX}/${ADVISORY_TYPE}-${ADVISORY_NAME}"
362- ADVISORY_INTERNAL_URL="${GIT_REPO//\.git/}/-/raw/${REPO_BRANCH}/${YAML_ADVISORY_FILEPATH}"
102+ - name : ADVISORY_JSON
103+ value : $(params.advisory_json)
104+ - name : PARAM_COMPONENT_GROUP
105+ value : $(params.componentGroup)
106+ - name : PARAM_ORIGIN
107+ value : $(params.origin)
108+ - name : PARAM_CONFIG_MAP_NAME
109+ value : $(params.config_map_name)
110+ - name : PARAM_CONTENT_TYPE
111+ value : $(params.contentType)
112+ - name : PARAM_INTERNAL_REQUEST_PIPELINE_RUN_NAME
113+ value : $(params.internalRequestPipelineRunName)
114+ - name : PARAM_TASK_RUN_NAME
115+ value : $(context.taskRun.name)
116+ - name : RESULT_RESULT
117+ value : $(results.result.path)
118+ - name : RESULT_ADVISORY_URL
119+ value : $(results.advisory_url.path)
120+ - name : RESULT_ADVISORY_INTERNAL_URL
121+ value : $(results.advisory_internal_url.path)
122+ - name : RESULT_INTERNAL_REQUEST_PIPELINE_RUN_NAME
123+ value : $(results.internalRequestPipelineRunName.path)
124+ - name : RESULT_INTERNAL_REQUEST_TASK_RUN_NAME
125+ value : $(results.internalRequestTaskRunName.path)
126+ command : ["/home/scripts/python/tasks/internal/create_advisory.py"]
0 commit comments