160 lines
5.6 KiB
Bash
Executable File
160 lines
5.6 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Deploy de um ambiente do ConectaSUS V2.
|
|
#
|
|
# ./deploy.sh <ambiente> <tag>
|
|
#
|
|
# Le os parametros do ambiente de docker/deploy/<ambiente>.env, troca a imagem
|
|
# do container e so considera o deploy bem-sucedido depois de verificar que a
|
|
# aplicacao responde. Se nao responder, volta sozinho para a imagem anterior.
|
|
#
|
|
# Roda a partir do runner, que usa o docker.sock do host — por isso todos os
|
|
# caminhos de mount sao caminhos do HOST, nao do job.
|
|
|
|
set -euo pipefail
|
|
|
|
AMBIENTE="${1:-}"
|
|
TAG="${2:-}"
|
|
REGISTRY="${REGISTRY:-192.168.0.41:3001}"
|
|
IMAGE_NAME="${IMAGE_NAME:-rkm/conectasus-api}"
|
|
HEALTH_TIMEOUT="${HEALTH_TIMEOUT:-90}"
|
|
|
|
AQUI="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
log() { echo "[deploy] $*"; }
|
|
fail() { echo "[deploy][ERRO] $*" >&2; exit 1; }
|
|
|
|
[ -n "${AMBIENTE}" ] || fail "uso: $0 <ambiente> <tag>"
|
|
[ -n "${TAG}" ] || fail "uso: $0 <ambiente> <tag>"
|
|
|
|
ENV_FILE="${AQUI}/${AMBIENTE}.env"
|
|
[ -f "${ENV_FILE}" ] || fail "ambiente desconhecido: ${AMBIENTE} (esperado ${ENV_FILE})"
|
|
|
|
# shellcheck disable=SC1090
|
|
set -a; . "${ENV_FILE}"; set +a
|
|
|
|
# Gabarito nao preenchido: melhor recusar do que implantar em lugar errado.
|
|
if grep -q 'TROCAR' "${ENV_FILE}"; then
|
|
fail "${ENV_FILE} ainda tem valores de gabarito (TROCAR)."
|
|
fi
|
|
|
|
for v in CONTAINER_NAME CONFIG_DIR PORT_API PORT_SPA PORT_WS; do
|
|
[ -n "${!v:-}" ] || fail "${v} nao definido em ${ENV_FILE}"
|
|
done
|
|
|
|
IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}"
|
|
|
|
log "ambiente : ${AMBIENTE}"
|
|
log "container: ${CONTAINER_NAME}"
|
|
log "imagem : ${IMAGE}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Guarda a imagem atual para poder voltar. Vale o digest, nao a tag: a tag e
|
|
# mutavel e pode ja apontar para outra coisa na hora do rollback.
|
|
# ---------------------------------------------------------------------------
|
|
ANTERIOR=""
|
|
if docker inspect "${CONTAINER_NAME}" >/dev/null 2>&1; then
|
|
ANTERIOR=$(docker inspect "${CONTAINER_NAME}" --format '{{.Image}}')
|
|
log "imagem atual: ${ANTERIOR}"
|
|
else
|
|
log "container ainda nao existe; primeiro deploy"
|
|
fi
|
|
|
|
log "puxando ${IMAGE}"
|
|
docker pull "${IMAGE}" >/dev/null || fail "pull falhou (registry acessivel? credencial presente?)"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Config do host. O shell do job pode rodar dentro de um container do
|
|
# act_runner e, portanto, nao enxergar os caminhos do host. A verificacao
|
|
# precisa passar pelo daemon Docker: e ele que avaliara o bind mount no
|
|
# host e depois subira a aplicacao com esses mesmos arquivos.
|
|
#
|
|
# --mount, diferentemente de -v, recusa uma origem inexistente em vez de
|
|
# criar um diretorio vazio silenciosamente.
|
|
# ---------------------------------------------------------------------------
|
|
log "validando configuracao no host Docker"
|
|
if ! docker run --rm \
|
|
--mount "type=bind,src=${CONFIG_DIR},dst=/config,readonly" \
|
|
--entrypoint sh \
|
|
"${IMAGE}" \
|
|
-c '
|
|
set -eu
|
|
for f in .env connections.json config.js; do
|
|
[ -f "/config/${f}" ] || {
|
|
echo "arquivo obrigatorio ausente: /config/${f}" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
[ -d /config/storage ] || {
|
|
echo "diretorio obrigatorio ausente: /config/storage" >&2
|
|
exit 1
|
|
}
|
|
'
|
|
then
|
|
fail "configuracao invalida ou inacessivel no host Docker: ${CONFIG_DIR}"
|
|
fi
|
|
|
|
subir() {
|
|
local img="$1"
|
|
docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true
|
|
docker run -d \
|
|
--name "${CONTAINER_NAME}" \
|
|
--restart unless-stopped \
|
|
${NETWORK:+--network "${NETWORK}"} \
|
|
-p "${PORT_API}:80" \
|
|
-p "${PORT_SPA}:8080" \
|
|
-p "${PORT_WS}:6001" \
|
|
-v "${CONFIG_DIR}/.env:/var/www/api/.env:ro" \
|
|
-v "${CONFIG_DIR}/connections.json:/var/www/api/connections.json:ro" \
|
|
-v "${CONFIG_DIR}/config.js:/var/www/frontend/config.js:ro" \
|
|
-v "${CONFIG_DIR}/storage:/var/www/api/storage" \
|
|
"${img}" >/dev/null
|
|
}
|
|
|
|
saudavel() {
|
|
local fim=$((SECONDS + HEALTH_TIMEOUT))
|
|
while [ ${SECONDS} -lt ${fim} ]; do
|
|
if [ "$(docker inspect -f '{{.State.Running}}' "${CONTAINER_NAME}" 2>/dev/null)" != "true" ]; then
|
|
return 1
|
|
fi
|
|
local spa api
|
|
spa=$(docker exec "${CONTAINER_NAME}" curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/ 2>/dev/null || echo 000)
|
|
api=$(docker exec "${CONTAINER_NAME}" curl -s -o /dev/null -w '%{http_code}' http://localhost:80/ 2>/dev/null || echo 000)
|
|
# A API nao tem rota "/", entao 404 e resposta valida; 000 e que nao e.
|
|
if [ "${spa}" = "200" ] && [ "${api}" != "000" ]; then
|
|
log "saudavel (spa=${spa} api=${api})"
|
|
return 0
|
|
fi
|
|
sleep 3
|
|
done
|
|
return 1
|
|
}
|
|
|
|
log "subindo ${CONTAINER_NAME}"
|
|
subir "${IMAGE}"
|
|
|
|
if saudavel; then
|
|
log "deploy de ${AMBIENTE} concluido em ${IMAGE}"
|
|
exit 0
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Nao respondeu. Um container no ar mas quebrado e pior do que a versao
|
|
# anterior no ar, entao volta.
|
|
# ---------------------------------------------------------------------------
|
|
echo "::error::deploy de ${AMBIENTE} falhou na verificacao de saude"
|
|
docker logs "${CONTAINER_NAME}" 2>&1 | tail -30 || true
|
|
|
|
if [ -z "${ANTERIOR}" ]; then
|
|
fail "sem versao anterior para voltar; container deixado no ar para diagnostico"
|
|
fi
|
|
|
|
log "revertendo para ${ANTERIOR}"
|
|
subir "${ANTERIOR}"
|
|
|
|
if saudavel; then
|
|
fail "deploy revertido: ${AMBIENTE} voltou para a versao anterior"
|
|
fi
|
|
|
|
fail "deploy falhou E a reversao tambem nao respondeu — ${AMBIENTE} precisa de atencao manual"
|