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
7 changes: 7 additions & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# AVD-KSV-0125: S3 job uses OpenShift built-in cli ImageStream (cluster-internal registry)
AVD-KSV-0125
# AVD-KSV-0013: same ImageStream reference has no fixed tag; it tracks the cluster
AVD-KSV-0013
# AVD-KSV-0113: S3 setup Role intentionally needs secret get/create/patch for quay config bundle
AVD-KSV-0113
# AVD-KSV-0020: Job uses OpenShift namespace default UID at runtime (high UID, no anyuid needed)
AVD-KSV-0020
# AVD-KSV-0021: Job uses OpenShift namespace default GID at runtime
AVD-KSV-0021
# AVD-KSV-0109: Ansible playbook parameter names (password) live in the ConfigMap.
# Secret values are file lookups from an ExternalSecret, not literals.
AVD-KSV-0109
# AVD-KSV-01010: playbook also names usernames and emails. Those are not credentials.
AVD-KSV-01010
2 changes: 1 addition & 1 deletion Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v2
name: quay
description: Red Hat Quay Registry Resources
type: application
version: 0.1.3
version: 0.2.0
appVersion: "3.9"
home: https://github.com/validatedpatterns/quay-chart
maintainers:
Expand Down
97 changes: 74 additions & 23 deletions README.md

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ This chart is used to serve as the template for Validated Patterns Charts

## Notable changes

### 0.2.0

- Standalone Multicloud Object Gateway is the default object storage
backend (`objectStorage.mode=mcg`). Set `objectStorage.mode=odf` to use
an existing OpenShift Data Foundation StorageCluster instead.
- An OpenShift console link points at the Quay route and embeds the Quay icon.
- Users, organizations, repositories, and robot accounts are applied by
a Job and CronJob using the `infra.quay_configuration` Ansible
collection. Passwords are projected with an ExternalSecret.
- `quay.setup` and `quay_config` moved to `quayConfig`.

{{ template "chart.homepageLine" . }}

{{ template "chart.maintainersSection" . }}
Expand Down
14 changes: 14 additions & 0 deletions files/quay-config-install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
# Install infra.quay_configuration onto the shared work volume.
set -euo pipefail

collections_path="${COLLECTIONS_PATH:?COLLECTIONS_PATH is required}"
requirements="/quay-config/requirements.yml"

if [[ ! -f "${requirements}" ]]; then
echo "ERROR: ${requirements} is missing" >&2
exit 1
fi

mkdir -p "${collections_path}"
ansible-galaxy collection install -r "${requirements}" -p "${collections_path}"
61 changes: 61 additions & 0 deletions files/quay-config-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/bin/bash
# Wait for the Quay route, then apply the configuration playbook.
set -euo pipefail

quay_namespace="${QUAY_NAMESPACE:?QUAY_NAMESPACE is required}"
quay_route="${QUAY_ROUTE:?QUAY_ROUTE is required}"
install_collection="${INSTALL_COLLECTION:?INSTALL_COLLECTION is required}"
validate_certs="${VALIDATE_CERTS:?VALIDATE_CERTS is required}"
home_dir="${HOME_DIR:-/pattern-home}"

route_host=""
echo "Waiting for route ${quay_route} in ${quay_namespace}..."
for _ in $(seq 1 60); do
route_host="$(
oc get route "${quay_route}" -n "${quay_namespace}" \
-o jsonpath='{.status.ingress[0].host}' 2>/dev/null || true
)"
if [[ -n "${route_host}" ]]; then
break
fi
sleep 10
done

if [[ -z "${route_host}" ]]; then
echo "ERROR: route ${quay_route} has no admitted host" >&2
oc describe route "${quay_route}" -n "${quay_namespace}" || true
exit 1
fi

quay_host="https://${route_host}"
echo "Quay host is ${quay_host}"

ready=""
for _ in $(seq 1 60); do
if curl -kfsS --max-time 5 "${quay_host}/health/instance" >/dev/null; then
ready="yes"
break
fi
sleep 10
done

if [[ -z "${ready}" ]]; then
echo "ERROR: Quay health endpoint did not become ready" >&2
exit 1
fi

if [[ "${install_collection}" == "true" ]]; then
collections_path="${COLLECTIONS_PATH:?COLLECTIONS_PATH is required}"
export ANSIBLE_COLLECTIONS_PATH="${collections_path}"
fi

export HOME="${home_dir}"
# The imperative container sets ANSIBLE_REMOTE_TMP to ${HOME}/.ansible/tmp.
# Recreate that directory on the writable home mount before the playbook runs.
export ANSIBLE_REMOTE_TMP="${home_dir}/.ansible/tmp"
export ANSIBLE_LOCAL_TEMP="${home_dir}/.ansible/tmp"
mkdir -p "${ANSIBLE_REMOTE_TMP}"

ansible-playbook /quay-config/playbook.yml \
-e "quay_host=${quay_host}" \
-e "validate_certs=${validate_certs}"
Binary file added files/quay-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 80 additions & 0 deletions files/s3-credentials-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/bin/bash
# Fill the Quay config template from a bound ObjectBucketClaim.
set -euo pipefail

quay_namespace="${QUAY_NAMESPACE:?QUAY_NAMESPACE is required}"
obc_name="${OBC_NAME:?OBC_NAME is required}"
config_secret="${CONFIG_SECRET:?CONFIG_SECRET is required}"
output_secret="${OUTPUT_SECRET:?OUTPUT_SECRET is required}"

echo "Setting up S3 credentials for Quay from ObjectBucketClaim ${obc_name}..."

oc get objectbucketclaim "${obc_name}" -n "${quay_namespace}"

echo "Waiting for ObjectBucketClaim ${obc_name} to be Bound (timeout: 10 minutes)..."
if ! oc wait --for=jsonpath='{.status.phase}'=Bound \
"objectbucketclaim/${obc_name}" -n "${quay_namespace}" --timeout=600s; then
echo "ERROR: ObjectBucketClaim failed to reach Bound state within timeout" >&2
oc describe objectbucketclaim "${obc_name}" -n "${quay_namespace}"
exit 1
fi

access_key="$(
oc get secret "${obc_name}" -n "${quay_namespace}" \
-o jsonpath='{.data.AWS_ACCESS_KEY_ID}' | base64 -d
)"
secret_key="$(
oc get secret "${obc_name}" -n "${quay_namespace}" \
-o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 -d
)"
bucket_name="$(
oc get configmap "${obc_name}" -n "${quay_namespace}" \
-o jsonpath='{.data.BUCKET_NAME}'
)"
bucket_host="$(
oc get configmap "${obc_name}" -n "${quay_namespace}" \
-o jsonpath='{.data.BUCKET_HOST}'
)"
bucket_port="$(
oc get configmap "${obc_name}" -n "${quay_namespace}" \
-o jsonpath='{.data.BUCKET_PORT}'
)"

if [[ -z "${bucket_port}" ]]; then
bucket_port="443"
fi

if [[ "${bucket_port}" == "443" ]]; then
is_secure="true"
else
is_secure="false"
fi

if [[ -z "${bucket_host}" || -z "${bucket_name}" || -z "${access_key}" || -z "${secret_key}" ]]; then
echo "ERROR: ObjectBucketClaim ${obc_name} is missing endpoint or credentials" >&2
exit 1
fi

echo "Retrieved S3 credentials successfully"
echo "Bucket: ${bucket_name}"
echo "Endpoint: ${bucket_host}:${bucket_port}"

oc get secret "${config_secret}" -n "${quay_namespace}" \
-o jsonpath='{.data.config\.yaml}' | base64 -d >/tmp/config.yaml

sed -i \
-e "s|PLACEHOLDER_ACCESS_KEY|${access_key}|g" \
-e "s|PLACEHOLDER_SECRET_KEY|${secret_key}|g" \
-e "s|PLACEHOLDER_BUCKET_NAME|${bucket_name}|g" \
-e "s|PLACEHOLDER_BUCKET_HOST|${bucket_host}|g" \
-e "s|PLACEHOLDER_BUCKET_PORT|${bucket_port}|g" \
-e "s|PLACEHOLDER_IS_SECURE|${is_secure}|g" \
/tmp/config.yaml

echo "Creating ${output_secret} with credentials from the ObjectBucketClaim..."
oc create secret generic "${output_secret}" \
--from-file=config.yaml=/tmp/config.yaml \
-n "${quay_namespace}" \
--dry-run=client -o yaml | oc apply -f -

echo "Quay S3 credentials setup completed successfully"
Loading
Loading