Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ super-linter-output
github_conf

# Editor and IDE specific files
.cursor/
.cursorrules
.vscode/
8 changes: 4 additions & 4 deletions charts/qtodo/templates/registry-external-secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ spec:
.dockerconfigjson: |
{
"auths": {
"{{ .Values.app.images.main.registry.domain | default (printf "quay-registry-quay-quay-enterprise.%s" .Values.global.hubClusterDomain) }}": {
"{{ tpl (required "app.images.main.registry.domain is required when registry.auth is enabled" .Values.app.images.main.registry.domain) $ }}": {
"auth": "{{ `{{ printf "%s:%s" "` }}{{ .Values.app.images.main.registry.user }}{{ `" .password | b64enc }}` }}"
}
}
}
data:
- secretKey: password
remoteRef:
key: {{ .Values.app.images.main.registry.vaultPath }}
property: {{ .Values.app.images.main.registry.passwordVaultKey }}
{{- end }}
key: {{ required "app.images.main.registry.vaultPath is required when registry.auth is enabled" .Values.app.images.main.registry.vaultPath }}
property: {{ required "app.images.main.registry.passwordVaultKey is required when registry.auth is enabled" .Values.app.images.main.registry.passwordVaultKey }}
{{- end }}
11 changes: 6 additions & 5 deletions charts/qtodo/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ app:
# Modified to Always to force a pull so we can test changes to the container image without requiring manual deletion of images or restarts of argo
pullPolicy: Always
registry:
# Set to true to create registry auth secret for image pulls
auth: false
secretName: qtodo-registry-auth
user: quay-user
# domain: quay-registry-quay-quay-enterprise.apps.example.com
# Registry credentials - stored in quay path
vaultPath: secret/data/hub/infra/quay/quay-users
passwordVaultKey: quay-user-password
user: registry-user
# domain: registry.example.com # REQUIRED when auth is enabled
# Vault path and key for registry password (set for your scenario)
vaultPath: ""
passwordVaultKey: ""
spiffeHelper:
name: registry.redhat.io/zero-trust-workload-identity-manager/spiffe-helper-rhel9
version: v0.10.0
Expand Down
2 changes: 1 addition & 1 deletion charts/supply-chain/templates/pipeline-qtodo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ spec:
- name: image-target
type: string
description: qtodo image push destination (e.g. quay.io/ztvp/qtodo:latest)
default: {{ .Values.registry.domain | default (printf "quay-registry-quay-quay-enterprise.%s" .Values.global.hubClusterDomain) }}/{{ .Values.registry.org }}/{{ .Values.registry.repo }}:{{ .Values.qtodo.tag }}
default: {{ tpl (required "registry.domain is required" .Values.registry.domain) $ }}/{{ .Values.registry.org }}/{{ .Values.registry.repo }}:{{ .Values.qtodo.tag }}
- name: image-tls-verify
type: string
description: Whether to verify TLS when pushing to the OCI registry
Expand Down
21 changes: 21 additions & 0 deletions charts/supply-chain/templates/pipelinerun-qtodo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{{- if .Values.pipelinerun.enabled }}
---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: qtodo-supply-chain-
namespace: {{ .Values.global.namespace }}
annotations:
argocd.argoproj.io/hook: PostSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
pipelineRef:
name: qtodo-supply-chain
workspaces:
- name: qtodo-source
persistentVolumeClaim:
claimName: qtodo-workspace-source
- name: registry-auth-config
secret:
secretName: {{ .Values.registry.authSecretName }}
{{- end }}
2 changes: 1 addition & 1 deletion charts/supply-chain/templates/quay/quay-user-job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ spec:
command: ["python3", "/app/create_user.py"]
env:
- name: QUAY_HOST
value: {{ .Values.registry.domain | default (printf "quay-registry-quay-quay-enterprise.%s" .Values.global.hubClusterDomain) }}
value: {{ tpl (.Values.registry.domain | default (printf "quay-registry-quay-quay-enterprise.%s" .Values.global.hubClusterDomain)) $ }}
- name: QUAY_ADMIN_USER
value: {{ .Values.registry.user }}
- name: QUAY_ADMIN_PASSWORD
Expand Down
119 changes: 119 additions & 0 deletions charts/supply-chain/templates/rbac/registry-image-namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
{{- if and (index .Values.registry "embeddedOCP") (index .Values.registry.embeddedOCP "ensureImageNamespaceRBAC") }}
# When using the embedded OCP image registry, the pipeline pushes to a namespace
# that matches registry.org (e.g. ztvp). This ensures that namespace exists and
# the pipeline SA has system:image-builder so the push succeeds (transparent to the user).
---
apiVersion: v1
kind: Namespace
metadata:
name: {{ .Values.registry.org }}
annotations:
argocd.argoproj.io/sync-wave: "0"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pipeline-image-builder
namespace: {{ .Values.registry.org }}
annotations:
argocd.argoproj.io/sync-wave: "0"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:image-builder
subjects:
- kind: ServiceAccount
name: pipeline
namespace: {{ .Values.global.namespace }}
---
# Enable the default route on the embedded OCP image registry so that
# the pipeline can push and external clients can pull images via the route.
# Uses a Job because the imageregistry config is a cluster-singleton managed
# by the image-registry operator; declarative ownership would conflict.
apiVersion: v1
kind: ServiceAccount
metadata:
name: registry-route-enabler
namespace: {{ .Values.global.namespace }}
annotations:
argocd.argoproj.io/sync-wave: "0"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ .Values.global.namespace }}-registry-route-enabler
annotations:
argocd.argoproj.io/sync-wave: "0"
rules:
- apiGroups: ["imageregistry.operator.openshift.io"]
resources: ["configs"]
verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ .Values.global.namespace }}-registry-route-enabler
annotations:
argocd.argoproj.io/sync-wave: "0"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ .Values.global.namespace }}-registry-route-enabler
subjects:
- kind: ServiceAccount
name: registry-route-enabler
namespace: {{ .Values.global.namespace }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: enable-registry-default-route
namespace: {{ .Values.global.namespace }}
annotations:
argocd.argoproj.io/sync-wave: "1"
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
backoffLimit: 3
template:
spec:
serviceAccountName: registry-route-enabler
restartPolicy: Never
containers:
- name: enable-route
image: registry.access.redhat.com/ubi9/ubi:9.7-1764794285
command:
- /bin/sh
- -ce
- |
APISERVER="https://kubernetes.default.svc"
TOKEN="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
CACERT="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
RESOURCE_URL="${APISERVER}/apis/imageregistry.operator.openshift.io/v1/configs/cluster"
AUTH_HEADER="Authorization: Bearer ${TOKEN}"

echo "Checking current defaultRoute status..."
BODY=$(curl -sS --cacert "${CACERT}" -H "${AUTH_HEADER}" "${RESOURCE_URL}")
rc=$?; if [ $rc -ne 0 ]; then echo "ERROR: GET failed (curl rc=${rc})"; exit 1; fi

# Parse defaultRoute from JSON without jq/grep dependency
case "${BODY}" in
*'"defaultRoute":true'*) echo "Default route already enabled, nothing to do."; exit 0 ;;
esac

echo "Enabling default route on embedded OCP image registry..."
RESP=$(curl -sS -w "\n%{http_code}" --cacert "${CACERT}" \
-H "${AUTH_HEADER}" \
-H "Content-Type: application/merge-patch+json" \
-X PATCH -d '{"spec":{"defaultRoute":true}}' \
"${RESOURCE_URL}")
HTTP_CODE=$(echo "${RESP}" | tail -1)

if [ "${HTTP_CODE}" -ge 200 ] 2>/dev/null && [ "${HTTP_CODE}" -lt 300 ] 2>/dev/null; then
echo "Default route enabled successfully (HTTP ${HTTP_CODE})."
else
echo "ERROR: PATCH failed (HTTP ${HTTP_CODE})."
echo "${RESP}" | head -5
exit 1
fi
{{- end }}
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
{{/*
Quay User Provisioner Secret
Purpose: Provides password for the Quay user provisioner job to create/update users in built-in Quay
Used by: quay-user-job.yaml (CronJob that provisions Quay users)
Only created when: quay.enabled=true (built-in Quay registry)
Uses unified registry.vaultPath and registry.passwordVaultKey
*/}}
{{- if eq .Values.quay.enabled true }}
---
apiVersion: "external-secrets.io/v1beta1"
Expand All @@ -21,4 +28,4 @@ spec:
remoteRef:
key: {{ .Values.registry.vaultPath }}
property: {{ .Values.registry.passwordVaultKey }}
{{- end }}
{{- end }}
20 changes: 15 additions & 5 deletions charts/supply-chain/templates/secrets/qtodo-registry-auth.yaml
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
{{/*
Pipeline Registry Auth Secret
Purpose: Provides dockerconfigjson for pipeline to push/pull images
Used by: Tekton pipeline tasks (build-image, sign-image, verify-image)
Created when: registry.enabled=true
Registry-agnostic: works for built-in Quay, BYO (quay.io, ghcr.io), or embedded OCP.
Set registry.domain, registry.vaultPath, and registry.passwordVaultKey for your scenario.
*/}}
{{- if .Values.registry.enabled }}
---
apiVersion: "external-secrets.io/v1beta1"
kind: ExternalSecret
metadata:
name: qtodo-registry-auth
name: {{ .Values.registry.authSecretName }}
namespace: {{ .Release.Namespace | default .Values.global.namespace }}
spec:
refreshInterval: 15s
secretStoreRef:
name: {{ .Values.global.secretStore.name }}
kind: {{ .Values.global.secretStore.kind }}
target:
name: qtodo-registry-auth
name: {{ .Values.registry.authSecretName }}
template:
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: |
{
"auths": {
"{{ .Values.registry.domain | default (printf "quay-registry-quay-quay-enterprise.%s" .Values.global.hubClusterDomain) }}": {
"{{ tpl (required "registry.domain is required when registry.enabled=true" .Values.registry.domain) $ }}": {
"auth": "{{ `{{ printf "%s:%s" "` }}{{ .Values.registry.user }}{{ `" .password | b64enc }}` }}"
}
}
}
data:
- secretKey: password
remoteRef:
key: {{ .Values.registry.vaultPath }}
property: {{ .Values.registry.passwordVaultKey }}
key: {{ required "registry.vaultPath is required when registry.enabled=true" .Values.registry.vaultPath }}
property: {{ required "registry.passwordVaultKey is required when registry.enabled=true" .Values.registry.passwordVaultKey }}
{{- end }}
61 changes: 51 additions & 10 deletions charts/supply-chain/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,67 @@ qtodo:
buildCmd: "./mvnw -s settings.xml package -DskipTests -Dquarkus.package.jar.type=uber-jar"
containerfile: "./Containerfile"

# quay registry configuration
# used to create a new user in quay. Generic registry configuration is below.
# ===========================================================================
# QUAY USER PROVISIONER (only for built-in Quay registry)
# When enabled, runs a CronJob that provisions users in the built-in Quay instance.
# This is Quay-specific and not needed for BYO or embedded OCP registries.
# ===========================================================================
quay:
enabled: true
enabled: false
email: "quay-user@example.com"
job:
image: registry.access.redhat.com/ubi9/ubi:9.7-1764794285
schedule: "*/5 * * * *"

# container registry configuration
# ===========================================================================
# REGISTRY CONFIGURATION (option-agnostic)
# Works for all registry types: built-in Quay, BYO (quay.io, ghcr.io, etc.),
# or embedded OCP image registry. Set the values for your scenario.
#
# Scenario-specific values (set in values-hub.yaml overrides):
# Built-in Quay:
# domain: quay-registry-quay-quay-enterprise.apps.<clusterDomain>
# vaultPath: secret/data/hub/infra/quay/quay-users
# passwordVaultKey: quay-user-password
# BYO (quay.io, ghcr.io, etc.):
# domain: quay.io (or your registry hostname)
# vaultPath: secret/data/hub/infra/registry/registry-user
# passwordVaultKey: registry-password
# Embedded OCP:
# domain: default-route-openshift-image-registry.apps.<clusterDomain>
# vaultPath: secret/data/hub/infra/registry/registry-user
# passwordVaultKey: registry-password
# embeddedOCP.ensureImageNamespaceRBAC: true
# ===========================================================================
registry:
# Commented to generate it dynamically
# domain: "quay-registry-quay-quay-enterprise.hub.example.com"
# Set to true to create the registry auth secret (dockerconfigjson)
enabled: false
# Registry hostname (REQUIRED when enabled)
domain: ""
# Organization/namespace within the registry
org: "ztvp"
# Repository name
repo: "qtodo"
# Whether to verify TLS when pushing to the registry
tlsVerify: "true"
user: "quay-user"
passwordVaultKey: "quay-user-password"
# Infrastructure secrets - stored in quay path
vaultPath: "secret/data/hub/infra/quay/quay-users"
# Registry username
user: "registry-user"
# Vault path to the secret containing the registry password
vaultPath: ""
# Key within the Vault secret that holds the password
passwordVaultKey: ""
# Secret name for registry auth (dockerconfigjson)
authSecretName: "qtodo-registry-auth"
# Embedded OCP registry only: create image namespace (registry.org) and grant
# pipeline SA system:image-builder so the pipeline can push. Set to true only when
# using the in-cluster OpenShift image registry; leave false for other registries.
embeddedOCP:
ensureImageNamespaceRBAC: false

# pipeline run configuration
pipelinerun:
# Set to true to automatically trigger a pipeline run on ArgoCD sync
enabled: false

# spire configuration
spire:
Expand Down
Loading