Proxy cache (harbor_proxy_cache_enabled: true): - Автоматически создаёт registry endpoints + proxy cache проекты для: docker.io, gcr.io, quay.io, ghcr.io, registry.k8s.io, mcr.microsoft.com, public.ecr.aws - При pull образа через harbor.example.com/<registry>/<image> он кэшируется - Реализовано через alpine:3.19 + curl + jq Kubernetes Job (вызывает Harbor REST API изнутри кластера) Tag retention (harbor_retention_enabled: true, harbor_retention_max_tags: 3): - Политика "latestPushedN=3" применяется ко ВСЕМ проектам (включая proxy cache) - Пропускает проекты с уже существующей политикой (idempotent) - Запуск: ежедневно в 03:00 UTC (cron schedule в Harbor) Механизм: Job запускается после Helm install, достучивается до harbor-core по internal service DNS, ждёт API готовности (40 попыток × 15 сек = 10 мин max).
142 lines
5.9 KiB
Django/Jinja
142 lines
5.9 KiB
Django/Jinja
---
|
||
# ConfigMap со скриптом конфигурации Harbor
|
||
# Настраивает proxy-cache registry и tag retention policies через REST API
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: harbor-configure
|
||
namespace: {{ harbor_namespace }}
|
||
data:
|
||
configure.sh: |
|
||
#!/bin/sh
|
||
set -e
|
||
|
||
apk add -q curl jq
|
||
|
||
BASE="http://harbor.{{ harbor_namespace }}.svc.cluster.local"
|
||
AUTH="admin:{{ harbor_admin_password }}"
|
||
|
||
# ── Ждём готовности Harbor API ────────────────────────────────────────────
|
||
echo ">>> Ожидаю готовности Harbor API..."
|
||
for i in $(seq 1 40); do
|
||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -u "$AUTH" \
|
||
"$BASE/api/v2.0/systeminfo" 2>/dev/null || echo "000")
|
||
[ "$STATUS" = "200" ] && echo "Harbor API готов" && break
|
||
echo "Попытка $i/40 (HTTP $STATUS), жду 15 сек..."
|
||
sleep 15
|
||
[ "$i" -eq 40 ] && echo "ОШИБКА: Harbor API недоступен" && exit 1
|
||
done
|
||
|
||
{% if harbor_proxy_cache_enabled %}
|
||
# ── Proxy Cache: создать registry endpoints и проекты ─────────────────────
|
||
echo ""
|
||
echo ">>> Настройка proxy-cache реестров..."
|
||
|
||
{% for reg in harbor_proxy_cache_registries %}
|
||
echo "--- {{ reg.description }} ({{ reg.name }}) ---"
|
||
|
||
# Создать registry endpoint (409 = уже существует — ок)
|
||
curl -sf -X POST -u "$AUTH" -H "Content-Type: application/json" \
|
||
-d '{"name":"{{ reg.name }}","type":"{{ reg.type }}","url":"{{ reg.url }}","description":"{{ reg.description }}","insecure":false}' \
|
||
"$BASE/api/v2.0/registries" \
|
||
-o /dev/null -w " Endpoint: HTTP %{http_code}\n" || true
|
||
|
||
# Получить ID endpoint-а
|
||
REG_ID=$(curl -sf -u "$AUTH" \
|
||
"$BASE/api/v2.0/registries?name={{ reg.name }}" | jq -e '.[0].id' 2>/dev/null || echo "")
|
||
|
||
if [ -z "$REG_ID" ] || [ "$REG_ID" = "null" ]; then
|
||
echo " ПРЕДУПРЕЖДЕНИЕ: не удалось получить ID для {{ reg.name }}, пропускаю"
|
||
else
|
||
# Создать proxy cache проект (409 = уже существует — ок)
|
||
curl -sf -X POST -u "$AUTH" -H "Content-Type: application/json" \
|
||
-d "{\"project_name\":\"{{ reg.name }}\",\"public\":true,\"registry_id\":$REG_ID,\"metadata\":{\"public\":\"true\"}}" \
|
||
"$BASE/api/v2.0/projects" \
|
||
-o /dev/null -w " Проект: HTTP %{http_code}\n" || true
|
||
fi
|
||
|
||
{% endfor %}
|
||
echo ">>> Proxy-cache реестры настроены"
|
||
echo " Примеры pull через Harbor:"
|
||
{% for reg in harbor_proxy_cache_registries %}
|
||
echo " {{ harbor_ingress_host }}/{{ reg.name }}/<image>:<tag>"
|
||
{% endfor %}
|
||
{% endif %}
|
||
|
||
{% if harbor_retention_enabled %}
|
||
# ── Tag Retention: применить ко всем проектам ─────────────────────────────
|
||
echo ""
|
||
echo ">>> Настройка retention policy (max {{ harbor_retention_max_tags }} тегов на репозиторий)..."
|
||
|
||
# Получить все проекты и сохранить в файл (избегаем subshell через pipe)
|
||
curl -sf -u "$AUTH" "$BASE/api/v2.0/projects?page_size=100" \
|
||
| jq -c '.[]' > /tmp/projects.json
|
||
|
||
while IFS= read -r project; do
|
||
PROJ_ID=$(echo "$project" | jq '.id')
|
||
PROJ_NAME=$(echo "$project" | jq -r '.name')
|
||
|
||
# Пропустить если retention уже настроен
|
||
EXISTING_RET=$(echo "$project" | jq -r '.metadata.retention_id // empty')
|
||
if [ -n "$EXISTING_RET" ]; then
|
||
echo " $PROJ_NAME: retention уже настроен (id: $EXISTING_RET), пропускаю"
|
||
continue
|
||
fi
|
||
|
||
# Создать retention policy для проекта
|
||
RET_RESP=$(curl -sf -X POST -u "$AUTH" -H "Content-Type: application/json" \
|
||
-d "{
|
||
\"algorithm\": \"or\",
|
||
\"rules\": [{
|
||
\"disabled\": false,
|
||
\"action\": \"retain\",
|
||
\"template\": \"latestPushedN\",
|
||
\"params\": {\"latestPushedN\": {{ harbor_retention_max_tags }}},
|
||
\"tag_selectors\": [{\"kind\": \"doublestar\", \"decoration\": \"matches\", \"pattern\": \"**\"}],
|
||
\"scope_selectors\": {
|
||
\"repository\": [{\"kind\": \"doublestar\", \"decoration\": \"repoMatches\", \"pattern\": \"**\"}]
|
||
}
|
||
}],
|
||
\"trigger\": {\"kind\": \"Schedule\", \"settings\": {\"cron\": \"0 0 3 * * *\"}},
|
||
\"scope\": {\"level\": \"project\", \"ref\": $PROJ_ID}
|
||
}" \
|
||
"$BASE/api/v2.0/retentions" 2>/dev/null || echo "{}")
|
||
|
||
RET_ID=$(echo "$RET_RESP" | jq '.id // empty' 2>/dev/null || echo "")
|
||
if [ -n "$RET_ID" ] && [ "$RET_ID" != "null" ]; then
|
||
echo " $PROJ_NAME (id: $PROJ_ID): retention policy #$RET_ID установлена"
|
||
else
|
||
echo " $PROJ_NAME: не удалось создать retention (возможно уже настроено)"
|
||
fi
|
||
done < /tmp/projects.json
|
||
|
||
echo ">>> Retention policies настроены (запуск: ежедневно в 03:00 UTC)"
|
||
{% endif %}
|
||
|
||
echo ""
|
||
echo ">>> Конфигурация Harbor завершена!"
|
||
---
|
||
apiVersion: batch/v1
|
||
kind: Job
|
||
metadata:
|
||
name: harbor-configure
|
||
namespace: {{ harbor_namespace }}
|
||
spec:
|
||
ttlSecondsAfterFinished: 600
|
||
backoffLimit: 3
|
||
template:
|
||
spec:
|
||
restartPolicy: OnFailure
|
||
containers:
|
||
- name: configure
|
||
image: alpine:3.19
|
||
command: ["/bin/sh", "/scripts/configure.sh"]
|
||
volumeMounts:
|
||
- name: scripts
|
||
mountPath: /scripts
|
||
volumes:
|
||
- name: scripts
|
||
configMap:
|
||
name: harbor-configure
|
||
defaultMode: 0755
|