6.2 Promote to prod repo

On this page you prove that production desired state is a commit on a second Git remote, carrying the same signed digest you already admitted on stage.

Track 6 is the production container. This is the second page. The prod remote is student_prod_gitops_repo_url, repository lw-student/gitops-prod-spring-boot-lw-poc. The seed digest is sha256:REPLACE_ME_PROD_DIGEST, replicas is 0, and the prod tree has no TrustPolicy file. Admission on lw-poc-prod already landed from 6.1 GitOps + admission. You commit the signed digest. Application lw-poc-prod still tracking the stage remote fails the Check.

Why this control exists

Promotion is a deliberate commit to production desired state. Retargeting the prod Application at the stage remote would make production follow stage, which is a different control, and the Check rejects it. After this page, lw-poc-prod sources the prod remote, the ImagePolicy from 6.1 still holds, and the running digest is the one you signed.

oc tag still does not copy .sig tags. You copy the signature onto the prod ImageStream and sign that destination with the 5.3 key, the same sequence as stage. Konflux Release Service maps to this commit. The term map is Appendix: Konflux mapping.

Lightwell Network and Lightwell Clearinghouse Premier do not change the promote remote. The GAV is already inside the digest.

Stage versus prod Gitea remotes

The figure is the same two rows as 6.1. This page is the bottom row. You replace sha256:REPLACE_ME_PROD_DIGEST with the signed digest and set replicas: 1 on lw-student/gitops-prod-spring-boot-lw-poc. Application lw-poc-prod keeps sourcing that remote.

In a customer engagement, promotion is a merge to a prod GitOps repository, or a Release. You push the digest you already signed. Unsigned deny from 6.1 still holds.

Learning objectives

By the end of this module, you will be able to:

  • Tag the signed image into lw-poc-prod and copy its signature onto that repository

  • Commit the digest and replicas: 1 on the prod GitOps remote

  • Show Application lw-poc-prod sourcing gitops-prod-spring-boot-lw-poc, Synced and Healthy

  • Leave the stage Application and the TrustPolicy file on the stage remote

Your change

student_prod_namespace is lw-poc-prod. student_prod_gitops_repo_url is the remote you will clone. Success of the tag is a digest line from the prod ImageStream tag. That digest is the one you signed. cosign copy and cosign sign --key attach the signature ImagePolicy already trusts.

Tag, copy the signature, and sign the prod repository
BUILD_NS="$(oc -n gitea get configmap demo-userinfo-gitea -o jsonpath='{.data.student_build_namespace}')"
PROD_NS="$(oc -n gitea get configmap demo-userinfo-gitea -o jsonpath='{.data.student_prod_namespace}')"
PROD_GITOPS="$(oc -n gitea get configmap demo-userinfo-gitea -o jsonpath='{.data.student_prod_gitops_repo_url}')"
echo "PROD_NS=${PROD_NS}"
echo "PROD_GITOPS=${PROD_GITOPS}"
oc tag "${BUILD_NS}/spring-boot-lw-poc:latest" "${PROD_NS}/spring-boot-lw-poc:latest"
oc -n "${PROD_NS}" get istag spring-boot-lw-poc:latest \
  -o jsonpath='{.image.metadata.name}{"\n"}'
DIGEST="$(oc -n "${PROD_NS}" get istag spring-boot-lw-poc:latest \
  -o jsonpath='{.image.metadata.name}')"
SRC="image-registry.openshift-image-registry.svc:5000/${BUILD_NS}/spring-boot-lw-poc@${DIGEST}"
DST="image-registry.openshift-image-registry.svc:5000/${PROD_NS}/spring-boot-lw-poc@${DIGEST}"
export HOME=/tmp/lab-home
mkdir -p "${HOME}"
cosign login -u unused -p "$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  image-registry.openshift-image-registry.svc:5000
cosign copy --allow-http-registry --allow-insecure-registry "${SRC}" "${DST}"
COSIGN_PASSWORD="" cosign sign --key /home/lab-user/lab-trust/cosign.key \
  --tlog-upload=false --allow-http-registry --allow-insecure-registry \
  "${DST}"

Clone student_prod_gitops_repo_url. The password is the value of ConfigMap key student_password. It goes in the clone URL and stays out of the commit. A fresh clone does not inherit the application repository’s author, so set user.name and user.email the same way as 6.1. Success is a clone at /tmp/gitops-prod whose image.digest is still sha256:REPLACE_ME_PROD_DIGEST and whose replicas is still 0.

Clone the prod remote
PROD_GITOPS="$(oc -n gitea get configmap demo-userinfo-gitea -o jsonpath='{.data.student_prod_gitops_repo_url}')"
: "${STUDENT_USER:=$(oc -n gitea get configmap demo-userinfo-gitea -o jsonpath='{.data.student_username}')}"
: "${STUDENT_PASS:=$(oc -n gitea get configmap demo-userinfo-gitea -o jsonpath='{.data.student_password}')}"
rm -rf /tmp/gitops-prod
hostpath="${PROD_GITOPS#https://}"; hostpath="${hostpath#http://}"
git clone "https://${STUDENT_USER}:${STUDENT_PASS}@${hostpath}" /tmp/gitops-prod
cd /tmp/gitops-prod
git config user.name student
git config user.email student@workshop.local

Replace sha256:REPLACE_ME_PROD_DIGEST with the digest you tagged. Set replicas: 1. Commit and push this remote only. Copying values.yaml or PROMOTE.md from the stage clone onto this remote fails the Check. The block below is that seed. Leaving it in place fails the Check. Pointing Application lw-poc-prod at gitops-spring-boot-lw-poc fails it too.

# Worked example ONLY — still the seed; do not copy this onto the prod remote.
image:
  digest: sha256:REPLACE_ME_PROD_DIGEST
replicas: 0
# Also not the Check: point Application lw-poc-prod at gitops-spring-boot-lw-poc (stage).

Check

A push alone may not sync. Hard-refresh Application lw-poc-prod after replicas: 1 is on the prod remote. Success is three facts. .spec.source.repoURL is student_prod_gitops_repo_url, which contains gitops-prod-spring-boot-lw-poc. The Application is Synced and Healthy, and REPLACE_ME_PROD_DIGEST is gone. ImagePolicy from 6.1 is still present, so an unsigned tag is still denied.

The repository name without the -prod- segment, gitops-spring-boot-lw-poc.git, is the stage remote. A prod Application on that URL fails the Check.

PROD_GITOPS="$(oc -n gitea get configmap demo-userinfo-gitea -o jsonpath='{.data.student_prod_gitops_repo_url}')"
# Same as 6.1: push alone may not sync. Hard-refresh after replicas: 1 is on the prod remote.
oc -n openshift-gitops annotate applications.argoproj.io lw-poc-prod \
  argocd.argoproj.io/refresh=hard --overwrite
oc -n openshift-gitops get applications.argoproj.io lw-poc-prod \
  -o jsonpath='repo={.spec.source.repoURL}{"\n"}sync={.status.sync.status}{"\n"}health={.status.health.status}{"\n"}'
oc -n openshift-gitops get applications.argoproj.io lw-poc-prod \
  -o jsonpath='{.spec.source.repoURL}{"\n"}' | grep -F 'gitops-prod-spring-boot-lw-poc' \
  && echo 'prod remote OK' || echo 'STILL TRACKING STAGE OR WRONG REPO'
echo "expected prefix ${PROD_GITOPS}"
oc -n lw-poc-prod get deploy,pods,route,imagepolicy
Argo Application lw-poc-prod after 6.2: Synced and Healthy on the signed digest

The capture is Application lw-poc-prod after the refresh: Synced and Healthy, on the signed digest, sourced from the prod remote.

Fill the report

The Job also grades ConfigMap report-16-prod-gitops key how_promote. Set it to prod-remote. Empty or REPLACE_ME fails even if cluster state is correct. Do not copy validate-docs.

oc -n lw-poc-validate edit configmap report-16-prod-gitops

The scored Check is Job validate-16-prod-gitops in lw-poc-validate. Direct nav URLs still work (honor system). Unlimited retries. There is no Solve.

Run the Validate Job
oc -n lw-poc-validate delete job validate-16-prod-gitops --ignore-not-found
TPL='job-16.yaml'
oc -n lw-poc-validate get configmap validate-job-templates \
  -o go-template="{{ index .data \"${TPL}\" }}" | oc create -f -
oc -n lw-poc-validate logs -f job/validate-16-prod-gitops

Expect CHECK PASSED: before you continue. CHECK FAILED: names the object to fix. Delete the Job and create it again as often as you need.

The Validate Job grades the prod Application’s source URL and the signed digest. Track 7 still opens if you skip this page. The stage Application and the TrustPolicy file stay where 6.1 left them.

Official documentation

Key takeaways

  • Production promotion is a git commit to gitops-prod-spring-boot-lw-poc.

  • The digest is the one you signed. ImagePolicy from 6.1 still applies.

  • Lightwell Network and Clearinghouse Premier travel inside that digest.

  • The next track records the SBOM for this promoted image.