Expand architecture documentation: enhance preview.html with an application layer component registry, update tooltips, refine SVG rendering, and adjust Archimate model structure.
This commit is contained in:
parent
5ae853bbf2
commit
8fe6445be8
33
.gitlab-ci.yml
Normal file
33
.gitlab-ci.yml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
include:
|
||||||
|
- project: 'generic/common-ci'
|
||||||
|
file: '/common-build.yaml'
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
|
||||||
|
variables:
|
||||||
|
RELEASE_NAME: iac-offline
|
||||||
|
DOCKERFILE_PATH: Dockerfile
|
||||||
|
IMAGE_NAME: ${CR}/${RELEASE_NAME}:${CI_COMMIT_TAG}
|
||||||
|
GIT_DEPTH: "1"
|
||||||
|
|
||||||
|
build_offline_mirror:
|
||||||
|
extends: .base_docker_prod
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG =~ /^v?(?:\d+\.){2}\d+$/
|
||||||
|
allow_failure: false
|
||||||
|
script:
|
||||||
|
- printf '%s' "$CI_JOB_TOKEN" > /tmp/gl_token
|
||||||
|
- mkdir -p /tmp/emptyctx
|
||||||
|
- docker build -f ${DOCKERFILE_PATH} /tmp/emptyctx
|
||||||
|
--secret id=gl_token,src=/tmp/gl_token
|
||||||
|
--build-arg PUSHER_IMAGE="${PUSHER_IMAGE}"
|
||||||
|
--build-arg GIT_TAG="${CI_COMMIT_TAG}"
|
||||||
|
--build-arg EXPECT_COMMIT="${CI_COMMIT_SHA}"
|
||||||
|
--build-arg BUILT_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
${BUILD_ARGS}
|
||||||
|
-t ${IMAGE_NAME}
|
||||||
|
|
||||||
|
- docker run --rm -e DRY_RUN=true ${IMAGE_NAME}
|
||||||
|
- docker push ${IMAGE_NAME}
|
||||||
|
timeout: 30m
|
||||||
261
Dockerfile
Normal file
261
Dockerfile
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
#
|
||||||
|
# Самодостаточный образ переноса infra/iac в закрытый контур: несёт в себе
|
||||||
|
# и состояние репозитория на заданном теге, и логику push-а. Внешней базы
|
||||||
|
# нет — собирается от alpine, зависимостей от других репозиториев у сборки
|
||||||
|
# нет тоже.
|
||||||
|
#
|
||||||
|
# В образ попадают ровно два ref-а:
|
||||||
|
# refs/tags/<GIT_TAG> — сам тег
|
||||||
|
# refs/heads/<TARGET_BRANCH> — ветка на коммите тега; её читает Flux
|
||||||
|
# Остальных веток и их истории нет — только объекты, достижимые от тега.
|
||||||
|
#
|
||||||
|
# Куда пушить, образ узнаёт при запуске (см. `docker run ... info`):
|
||||||
|
# GIT_URL обязательно, полный git-URL цели
|
||||||
|
# GIT_USERNAME логин
|
||||||
|
# GIT_PASSWORD_FILE файл с токеном (предпочтительно) …
|
||||||
|
# GIT_PASSWORD … либо значение
|
||||||
|
# PUSH_TAGS true пушить теги
|
||||||
|
# PUSH_PRUNE true снимать на сервере лишние ветки и теги
|
||||||
|
# PUSH_FORCE true форсированный push
|
||||||
|
# VERIFY_AFTER_PUSH true сверить refs после push-а и упасть при расхождении
|
||||||
|
# GIT_SSL_NO_VERIFY false самоподписанный сертификат цели
|
||||||
|
#
|
||||||
|
# Образ умеет ровно одно — push, в двух режимах:
|
||||||
|
#
|
||||||
|
# DRY_RUN=true показать подпись образа, refs и рефспеки; ничего
|
||||||
|
# не менять. GIT_URL при этом необязателен, так что этим
|
||||||
|
# же режимом проверяют образ сразу после сборки.
|
||||||
|
# DRY_RUN=false (по умолчанию) запушить и сверить результат: если на
|
||||||
|
# сервере оказалось не то, контейнер падает.
|
||||||
|
#
|
||||||
|
# Требует BuildKit. Локально:
|
||||||
|
#
|
||||||
|
# export GITLAB_TOKEN=... # read_repository на infra/iac
|
||||||
|
# printf '%s' "$GITLAB_TOKEN" > /tmp/gl_token
|
||||||
|
# DOCKER_BUILDKIT=1 docker build -f Dockerfile \
|
||||||
|
# --secret id=gl_token,src=/tmp/gl_token \
|
||||||
|
# --build-arg GIT_TAG=3.9.0 \
|
||||||
|
# --build-arg BUILT_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||||
|
# -t cr.yandex/crp3ccidau046kdj8g9q/iac-offline:3.9.0 .
|
||||||
|
#
|
||||||
|
# В CI это делает job build_offline_mirror, см. .gitlab-ci.yml.
|
||||||
|
|
||||||
|
# ── этап 1: выкачать состояние тега ─────────────────────────────────────
|
||||||
|
FROM alpine/git:latest AS fetch
|
||||||
|
|
||||||
|
ARG GIT_HOST=gitlab.sarex.io
|
||||||
|
ARG GIT_PATH=infra/iac
|
||||||
|
ARG GIT_USERNAME=gitlab-ci-token
|
||||||
|
|
||||||
|
# Обязателен. Он же — ключ кэша: docker кэширует RUN ниже, и без
|
||||||
|
# меняющегося аргумента пересборка молча отдала бы старое состояние.
|
||||||
|
# Теги неизменяемы, поэтому пересборка того же тега берёт кэш намеренно.
|
||||||
|
# Если тег в GitLab передвинули — см. EXPECT_COMMIT.
|
||||||
|
ARG GIT_TAG
|
||||||
|
|
||||||
|
# Ветка, которую увидит Flux в закрытом контуре. Создаётся на коммите тега.
|
||||||
|
ARG TARGET_BRANCH=master
|
||||||
|
|
||||||
|
# Необязательно: ожидаемый SHA коммита тега. Не совпал — сборка падает.
|
||||||
|
# Страховка от передвинутого тега и от устаревшего кэша.
|
||||||
|
ARG EXPECT_COMMIT
|
||||||
|
|
||||||
|
RUN --mount=type=secret,id=gl_token \
|
||||||
|
set -eu; \
|
||||||
|
: "${GIT_TAG:?не задан --build-arg GIT_TAG}"; \
|
||||||
|
token="$(cat /run/secrets/gl_token)"; \
|
||||||
|
\
|
||||||
|
# Не clone --mirror: тот выкачивает все ветки и служебные
|
||||||
|
# refs/merge-requests/*, чтобы потом их выбросить. fetch одного тега
|
||||||
|
# тянет только объекты, достижимые от него.
|
||||||
|
git init --bare --quiet /repo; \
|
||||||
|
git -C /repo fetch --quiet --no-tags \
|
||||||
|
"https://${GIT_USERNAME}:${token}@${GIT_HOST}/${GIT_PATH}.git" \
|
||||||
|
"refs/tags/${GIT_TAG}:refs/tags/${GIT_TAG}" \
|
||||||
|
|| { echo "не удалось выкачать тег ${GIT_TAG} из ${GIT_PATH}" >&2; exit 1; }; \
|
||||||
|
\
|
||||||
|
# URL с токеном нигде не сохраняется: remote не заводится, fetch
|
||||||
|
# получил его аргументом. В образ уезжают только объекты.
|
||||||
|
\
|
||||||
|
commit="$(git -C /repo rev-parse "refs/tags/${GIT_TAG}^{commit}")"; \
|
||||||
|
if [ -n "${EXPECT_COMMIT:-}" ] && [ "${commit}" != "${EXPECT_COMMIT}" ]; then \
|
||||||
|
echo "тег ${GIT_TAG} указывает на ${commit}, ожидался ${EXPECT_COMMIT}" >&2; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
\
|
||||||
|
# Ветка на коммите тега. Без неё в образе нет ни одного refs/heads/*,
|
||||||
|
# а push с --prune снёс бы на сервере все ветки разом.
|
||||||
|
git -C /repo update-ref "refs/heads/${TARGET_BRANCH}" "${commit}"; \
|
||||||
|
git -C /repo symbolic-ref HEAD "refs/heads/${TARGET_BRANCH}"; \
|
||||||
|
git -C /repo gc --prune=now --quiet; \
|
||||||
|
\
|
||||||
|
# Коммит нужен следующему этапу как ENV, а ARG туда не пролезает.
|
||||||
|
echo "${commit}" > /repo-commit
|
||||||
|
|
||||||
|
# ── этап 2: собственно образ ────────────────────────────────────────────
|
||||||
|
FROM alpine:3.22
|
||||||
|
|
||||||
|
# Только то, что нужно для push по HTTP(S). Ни curl, ни jq: образ не ходит
|
||||||
|
# ни в какие API, он умеет ровно одно — git push.
|
||||||
|
RUN apk add --no-cache ca-certificates git tini \
|
||||||
|
&& addgroup -g 1000 mirror \
|
||||||
|
&& adduser -D -u 1000 -G mirror -h /home/mirror mirror \
|
||||||
|
&& mkdir -p /repo \
|
||||||
|
&& chown -R mirror:mirror /repo
|
||||||
|
|
||||||
|
# Логика push-а. Специализирована под этот образ: внутри заведомо bare-репо
|
||||||
|
# с одной веткой и одним тегом, поэтому ни определения bare-vs-worktree, ни
|
||||||
|
# выбора ветки здесь нет.
|
||||||
|
COPY <<'PUSH_SH' /app/push.sh
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
REPO_DIR=/repo
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "${TMP_DIR}"' EXIT
|
||||||
|
|
||||||
|
# Подпись образа: SOURCE_PATH, SOURCE_URL, GIT_TAG, COMMIT, BUILT_AT.
|
||||||
|
# Пишется при сборке; COMMIT вычисляется там же, через ARG в ENV он бы
|
||||||
|
# не пролез.
|
||||||
|
. /etc/mirror-meta
|
||||||
|
|
||||||
|
PUSH_TAGS="${PUSH_TAGS:-true}"
|
||||||
|
PUSH_PRUNE="${PUSH_PRUNE:-true}"
|
||||||
|
PUSH_FORCE="${PUSH_FORCE:-true}"
|
||||||
|
VERIFY_AFTER_PUSH="${VERIFY_AFTER_PUSH:-true}"
|
||||||
|
DRY_RUN="${DRY_RUN:-false}"
|
||||||
|
|
||||||
|
stamp() { date -u +%Y-%m-%dT%H:%M:%SZ; }
|
||||||
|
log() { printf '%s [mirror] %s\n' "$(stamp)" "$*"; }
|
||||||
|
die() { printf '%s [mirror] ERROR %s\n' "$(stamp)" "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
init_git() {
|
||||||
|
# Репозиторий в образе принадлежит сборщику, а не текущему uid.
|
||||||
|
# Конфиг — во временный каталог: корень образа read-only.
|
||||||
|
GIT_CONFIG_GLOBAL="${TMP_DIR}/gitconfig"
|
||||||
|
export GIT_CONFIG_GLOBAL
|
||||||
|
: > "${GIT_CONFIG_GLOBAL}"
|
||||||
|
git config --global --add safe.directory '*'
|
||||||
|
git config --global advice.detachedHead false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Пароль нигде не материализуется: ни в URL ремоута, ни в argv (виден
|
||||||
|
# в `ps`), ни в git-конфиге на диске — только в окружении процесса.
|
||||||
|
# Credential-хелпер, а не GIT_ASKPASS: askpass требует исполняемый файл во
|
||||||
|
# временном каталоге, а под Kubernetes тот может быть примонтирован noexec.
|
||||||
|
setup_auth() {
|
||||||
|
pass="${GIT_PASSWORD:-}"
|
||||||
|
if [ -z "${pass}" ] && [ -n "${GIT_PASSWORD_FILE:-}" ]; then
|
||||||
|
[ -r "${GIT_PASSWORD_FILE}" ] \
|
||||||
|
|| die "не читается GIT_PASSWORD_FILE=${GIT_PASSWORD_FILE}"
|
||||||
|
pass="$(tr -d '\r\n' < "${GIT_PASSWORD_FILE}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${pass}" ] || [ -n "${GIT_USERNAME:-}" ]; then
|
||||||
|
GIT_AUTH_USER="${GIT_USERNAME:-}"
|
||||||
|
GIT_AUTH_PASS="${pass}"
|
||||||
|
export GIT_AUTH_USER GIT_AUTH_PASS
|
||||||
|
git config --global credential.helper \
|
||||||
|
'!f() { printf "username=%s\npassword=%s\n" "${GIT_AUTH_USER}" "${GIT_AUTH_PASS}"; }; f'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${GIT_SSL_NO_VERIFY:-false}" = "true" ]; then
|
||||||
|
export GIT_SSL_NO_VERIFY=true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
local_refs() {
|
||||||
|
git -C "${REPO_DIR}" for-each-ref --format='%(objectname) %(refname)' \
|
||||||
|
refs/heads refs/tags | sort
|
||||||
|
}
|
||||||
|
|
||||||
|
remote_refs() {
|
||||||
|
git ls-remote "${GIT_URL}" 'refs/heads/*' 'refs/tags/*' \
|
||||||
|
| grep -v '\^{}$' | awk '{print $1 " " $2}' | sort
|
||||||
|
}
|
||||||
|
|
||||||
|
# Сверка делает Job самопроверяющимся: если что-то не доехало, контейнер
|
||||||
|
# падает, а не рапортует об успехе.
|
||||||
|
compare_refs() {
|
||||||
|
local_refs > "${TMP_DIR}/expected"
|
||||||
|
remote_refs > "${TMP_DIR}/actual"
|
||||||
|
if cmp -s "${TMP_DIR}/expected" "${TMP_DIR}/actual"; then
|
||||||
|
log "сверка: ok, refs $(wc -l < "${TMP_DIR}/expected" | tr -d ' ')"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
log "сверка: РАСХОЖДЕНИЕ (< образ, > сервер)"
|
||||||
|
diff "${TMP_DIR}/expected" "${TMP_DIR}/actual" >&2 || true
|
||||||
|
die "состояние цели не совпало с образом"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_push() {
|
||||||
|
init_git
|
||||||
|
|
||||||
|
prefix=""
|
||||||
|
if [ "${PUSH_FORCE}" = "true" ]; then prefix="+"; fi
|
||||||
|
|
||||||
|
set -- "${prefix}refs/heads/*:refs/heads/*"
|
||||||
|
if [ "${PUSH_TAGS}" = "true" ]; then
|
||||||
|
set -- "$@" "${prefix}refs/tags/*:refs/tags/*"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "${SOURCE_PATH} ${GIT_TAG} (${COMMIT}), собрано ${BUILT_AT}"
|
||||||
|
|
||||||
|
# Режим сухого прогона: показать всё, что образ собирается сделать, и
|
||||||
|
# ничего не менять. Цель необязательна — так этим режимом проверяют
|
||||||
|
# сам образ сразу после сборки, ещё не зная, в какой контур он поедет.
|
||||||
|
if [ "${DRY_RUN}" = "true" ]; then
|
||||||
|
log "dry-run: цель ${GIT_URL:-<не задана>}, рефспеки: $*"
|
||||||
|
git -C "${REPO_DIR}" for-each-ref \
|
||||||
|
--format=' %(objectname:short) %(refname)' refs/heads refs/tags
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -n "${GIT_URL:-}" ] || die "не задан GIT_URL"
|
||||||
|
setup_auth
|
||||||
|
|
||||||
|
# --prune снимает на сервере ветки и теги, которых нет в образе.
|
||||||
|
# Без него удаления из источника в закрытый контур не доезжают.
|
||||||
|
if [ "${PUSH_PRUNE}" = "true" ]; then
|
||||||
|
git -C "${REPO_DIR}" push --quiet --prune "${GIT_URL}" "$@"
|
||||||
|
else
|
||||||
|
git -C "${REPO_DIR}" push --quiet "${GIT_URL}" "$@"
|
||||||
|
fi
|
||||||
|
log "запушено в ${GIT_URL}"
|
||||||
|
|
||||||
|
if [ "${VERIFY_AFTER_PUSH}" = "true" ]; then compare_refs; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
do_push
|
||||||
|
PUSH_SH
|
||||||
|
RUN chmod 0755 /app/push.sh
|
||||||
|
|
||||||
|
COPY --from=fetch --chown=1000:1000 /repo /repo
|
||||||
|
|
||||||
|
# Подпись образа: видна в `docker run ... info` и в логе Job-а, так что по
|
||||||
|
# логу понятно, что именно уехало в контур.
|
||||||
|
ARG GIT_PATH=infra/iac
|
||||||
|
ARG GIT_TAG
|
||||||
|
ARG SOURCE_URL=https://gitlab.sarex.io/infra/iac
|
||||||
|
ARG BUILT_AT
|
||||||
|
COPY --from=fetch /repo-commit /tmp/repo-commit
|
||||||
|
RUN COMMIT="$(cat /tmp/repo-commit)" \
|
||||||
|
&& rm -f /tmp/repo-commit \
|
||||||
|
&& printf 'SOURCE_PATH=%s\nSOURCE_URL=%s\nGIT_TAG=%s\nCOMMIT=%s\nBUILT_AT=%s\n' \
|
||||||
|
"${GIT_PATH}" "${SOURCE_URL}" "${GIT_TAG}" "${COMMIT}" "${BUILT_AT}" \
|
||||||
|
> /etc/mirror-meta \
|
||||||
|
&& chmod 0444 /etc/mirror-meta
|
||||||
|
|
||||||
|
ENV HOME=/home/mirror \
|
||||||
|
GIT_TERMINAL_PROMPT=0 \
|
||||||
|
TZ=UTC
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.source="https://gitlab.sarex.io/infra/iac" \
|
||||||
|
org.opencontainers.image.version="${GIT_TAG}" \
|
||||||
|
org.opencontainers.image.created="${BUILT_AT}"
|
||||||
|
|
||||||
|
USER mirror
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
ENTRYPOINT ["/sbin/tini", "--", "/app/push.sh"]
|
||||||
Loading…
Reference in New Issue
Block a user