chore: сохранение текущих изменений (deploy local-bundle/local-jar, request-service, slots UI markedObjectIds)

Безопасный коммит незакоммиченной работы перед реализацией бронирования слотов.
Вне коммита оставлены: package-lock.json (пустой, мусор), requests.json (scratch-данные).
This commit is contained in:
Дмитрий Соловьев
2026-06-11 14:00:12 +03:00
parent 6689351ef0
commit 345529cde0
36 changed files with 1674 additions and 37 deletions
+24 -1
View File
@@ -1,5 +1,18 @@
COMPOSE_PROJECT_NAME=pcp-local
USE_EXTERNAL_INFRA=false
PCP_DOCKER_NETWORK=pcp-local_default
PCP_DOCKER_NETWORK_EXTERNAL=false
# Existing allinone infra example:
# USE_EXTERNAL_INFRA=true
# PCP_DOCKER_NETWORK=allinone_camunda
# PCP_DOCKER_NETWORK_EXTERNAL=true
# EXTERNAL_POSTGRES_CONTAINER=allinone-postgres-1
# EXTERNAL_KAFKA_CONTAINER=allinone-kafka-1
# CAMUNDA_CLIENT_ENABLED=true
# CAMUNDA_GRPC_ADDRESS=http://orchestration:26500
PCP_IMAGE_TAG=local
POSTGRES_IMAGE=postgis/postgis:16-3.4
@@ -8,14 +21,24 @@ KAFKA_IMAGE=apache/kafka:3.7.2
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
POSTGRES_PORT=5432
PCP_POSTGRES_HOST=postgres
PCP_POSTGRES_PORT=5432
EXTERNAL_POSTGRES_CONTAINER=
KAFKA_PORT=29092
PCP_KAFKA_HOST=kafka
PCP_KAFKA_PORT=9092
EXTERNAL_KAFKA_CONTAINER=
CAMUNDA_CLIENT_ENABLED=false
CAMUNDA_GRPC_ADDRESS=http://host.docker.internal:26500
CONFIG_SERVER_PORT=38888
PCP_ADMIN_PORT=7000
PCP_TLE_MONITORING_PORT=7001
PCP_COMPLEX_MISSION_PORT=7002
PCP_BALLISTICS_PORT=7003
PCP_ROUTE_PROCESSING_PORT=7004
PCP_REQUEST_PORT=7005
PCP_SLOTS_PORT=7006
PCP_UI_PORT=7008
+127 -2
View File
@@ -2,6 +2,9 @@
Локальный bundle предназначен для запуска PCP на машине с Docker без GitLab Config Repo, Kubernetes и Helm.
В bundle входят backend-сервисы PCP, кроме `tle-monitoring-service`; отдельный frontend
`pcp-tgu-ops-ui` также не включается.
## Состав
- `docker-compose.yml` - локальная схема запуска.
@@ -15,6 +18,72 @@
- `scripts/stop.sh` - остановка compose stack.
- `scripts/status.sh` - статус контейнеров.
## Локальный запуск без архива
Для запуска на той же машине, где собираются images, архив и `docker save` не нужны.
Используйте один скрипт:
```bash
./deploy/local-bundle/run-local.sh start
```
Скрипт:
- создаёт `.env` из `.env.example`, если его ещё нет;
- собирает `bootJar` в Gradle offline mode;
- собирает локальные Docker images `pcp/<service>:<PCP_IMAGE_TAG>`;
- запускает `docker compose`.
Для вашего allinone Postgres/Kafka/Camunda перед первым запуском настройте
`deploy/local-bundle/.env`:
```env
USE_EXTERNAL_INFRA=true
PCP_DOCKER_NETWORK=allinone_camunda
PCP_DOCKER_NETWORK_EXTERNAL=true
PCP_POSTGRES_HOST=postgres
PCP_POSTGRES_PORT=5432
PCP_KAFKA_HOST=kafka
PCP_KAFKA_PORT=9092
EXTERNAL_POSTGRES_CONTAINER=allinone-postgres-1
EXTERNAL_KAFKA_CONTAINER=allinone-kafka-1
CAMUNDA_CLIENT_ENABLED=true
CAMUNDA_GRPC_ADDRESS=http://orchestration:26500
```
Команды:
```bash
./deploy/local-bundle/run-local.sh start
./deploy/local-bundle/run-local.sh status
./deploy/local-bundle/run-local.sh logs
./deploy/local-bundle/run-local.sh stop
```
## Пересборка одного сервиса
После первого запуска всего стека можно пересобирать и перезапускать только один сервис:
```bash
./deploy/local-bundle/services/pcp-request-service.sh
```
Это выполнит:
- `bootJar` только для `pcp-request-service`;
- `docker build` только для `pcp/pcp-request-service:<PCP_IMAGE_TAG>`;
- `docker compose up -d --no-deps --force-recreate pcp-request-service`.
Доступны wrapper-скрипты в `deploy/local-bundle/services/` для каждого сервиса.
То же самое можно вызвать через общий скрипт:
```bash
./deploy/local-bundle/run-service.sh pcp-request-service
./deploy/local-bundle/run-service.sh pcp-request-service build
./deploy/local-bundle/run-service.sh pcp-request-service logs
./deploy/local-bundle/run-service.sh pcp-request-service stop
```
## Сборка архива
Из корня репозитория:
@@ -38,6 +107,7 @@ deploy/local-bundle/build/pcp-local-bundle-<version>.tar.gz
Скрипт сборки:
- собирает `bootJar` для разворачиваемых сервисов;
- запускает Gradle в offline mode по умолчанию;
- собирает Docker images сервисов;
- сохраняет PCP service images в один `images/pcp-services.tar`;
- по умолчанию не сохраняет публичные infra images, чтобы не раздувать архив;
@@ -61,6 +131,41 @@ KOTLIN_DAEMON_JVMARGS="-Xmx3g -XX:MaxMetaspaceSize=768m" \
./deploy/local-bundle/build-local-bundle.sh
```
Gradle offline mode включён по умолчанию:
```bash
GRADLE_OFFLINE=true ./deploy/local-bundle/build-local-bundle.sh
```
Если нужно один раз прогреть локальный Gradle cache, временно отключите offline mode:
```bash
GRADLE_OFFLINE=false ./deploy/local-bundle/build-local-bundle.sh
```
Важно: `GRADLE_OFFLINE=true` отключает сетевой резолв зависимостей Gradle, но Docker всё равно
должен иметь локально доступный base image. Для local bundle сервисные Dockerfile собираются с
локальным override:
```text
LOCAL_BUNDLE_RUNTIME_IMAGE=bellsoft/liberica-openjre-alpine:21.0.5
```
Поэтому перед полностью offline Docker-сборкой подтяните или импортируйте:
```bash
docker pull bellsoft/liberica-openjre-alpine:21.0.5
```
Для полностью переносимого архива с infra images заранее подтяните Postgres/Kafka и включите их
сохранение:
```bash
docker pull postgis/postgis:16-3.4
docker pull apache/kafka:3.7.2
INCLUDE_INFRA_IMAGES=true ./deploy/local-bundle/build-local-bundle.sh
```
## Запуск на локальной машине
Распаковать архив:
@@ -72,6 +177,26 @@ cd pcp-local-bundle-<version>
Проверить `.env` и при необходимости изменить порты.
Если Postgres/Kafka/Camunda уже подняты внешним allinone compose, например в сети
`allinone_camunda`, включите external infra mode в `.env`:
```env
USE_EXTERNAL_INFRA=true
PCP_DOCKER_NETWORK=allinone_camunda
PCP_DOCKER_NETWORK_EXTERNAL=true
PCP_POSTGRES_HOST=postgres
PCP_POSTGRES_PORT=5432
PCP_KAFKA_HOST=kafka
PCP_KAFKA_PORT=9092
EXTERNAL_POSTGRES_CONTAINER=allinone-postgres-1
EXTERNAL_KAFKA_CONTAINER=allinone-kafka-1
CAMUNDA_CLIENT_ENABLED=true
CAMUNDA_GRPC_ADDRESS=http://orchestration:26500
```
В этом режиме `scripts/start.sh` не поднимает свои `postgres` и `kafka`, а сервисы PCP
подключаются к уже существующим контейнерам через общую Docker network.
Импортировать images:
```bash
@@ -109,9 +234,9 @@ cd pcp-local-bundle-<version>
- Config Server: `http://localhost:38888`
- Spring Boot Admin: `http://localhost:7000`
- TLE Monitoring: `http://localhost:7001`
- Complex Mission: `http://localhost:7002`
- Ballistics: `http://localhost:7003`
- Route Processing: `http://localhost:7004`
- Request Service: `http://localhost:7005`
- Slots Service: `http://localhost:7006`
- UI: `http://localhost:7008`
@@ -178,13 +303,13 @@ SPRING_CLOUD_CONFIG_LABEL=
- `pcp_ballistics`
- `pcp_requests`
- `pcp_route_processing`
- `pcp_missions`
- `pcp_slots`
- `pcp_complex_plan`
- `pcp_satellite_catalog`
- `pcp_stations`
- `pcp_tgu`
- `pcp_tle`
- `pcp_satellites`
Таблицы, схемы и seed data вручную не создаются. Схемы создаются существующими Flyway migrations внутри сервисов при старте приложений.
+15 -3
View File
@@ -26,6 +26,7 @@ service_names=(
pcp-satellite-catalog-service
pcp-ballistics-service
pcp-request-service
pcp-route-processing-service
slots-service
pcp-complex-mission-service
pcp-mission-planing-service
@@ -33,13 +34,14 @@ service_names=(
pcp-dynamic-plan-service
pcp-stations-service
pcp-ui-service
tle-monitoring-service
pcp-tgu-service
)
gradle_max_workers="${GRADLE_MAX_WORKERS:-1}"
gradle_jvmargs="${GRADLE_JVMARGS:--Xmx2g -Dfile.encoding=UTF-8}"
kotlin_daemon_jvmargs="${KOTLIN_DAEMON_JVMARGS:--Xmx3g -XX:MaxMetaspaceSize=768m}"
gradle_offline="${GRADLE_OFFLINE:-true}"
runtime_image="${LOCAL_BUNDLE_RUNTIME_IMAGE:-bellsoft/liberica-openjre-alpine:21.0.5}"
include_infra_images="${INCLUDE_INFRA_IMAGES:-false}"
gradle_common_args=(
@@ -49,6 +51,10 @@ gradle_common_args=(
"-Pkotlin.daemon.jvmargs=$kotlin_daemon_jvmargs"
)
if [[ "$gradle_offline" == "true" ]]; then
gradle_common_args+=("--offline")
fi
infra_images=(
"${POSTGRES_IMAGE:-postgis/postgis:16-3.4}"
"${KAFKA_IMAGE:-apache/kafka:3.7.2}"
@@ -117,9 +123,11 @@ echo "Building bootJar artifacts..."
echo "Gradle max workers: $gradle_max_workers"
echo "Gradle JVM args: $gradle_jvmargs"
echo "Kotlin daemon JVM args: $kotlin_daemon_jvmargs"
echo "Gradle offline mode: $gradle_offline"
echo "Local bundle runtime image: $runtime_image"
for service_name in "${service_names[@]}"; do
echo "Building bootJar for $service_name..."
"$repo_root/gradlew" "${gradle_common_args[@]}" ":services:$service_name:bootJar"
"$repo_root/gradlew" --project-dir "$repo_root" "${gradle_common_args[@]}" ":services:$service_name:bootJar"
done
rm -rf "$dist_dir" "$docker_contexts_dir" "$archive_path"
@@ -132,7 +140,11 @@ for service_name in "${service_names[@]}"; do
context_dir="$docker_contexts_dir/$service_name"
copy_single_boot_jar "$service_name" "$context_dir"
docker build --file "$context_dir/Dockerfile" --tag "$image_name" "$context_dir"
docker build \
--build-arg "RUNTIME_IMAGE=$runtime_image" \
--file "$context_dir/Dockerfile" \
--tag "$image_name" \
"$context_dir"
service_images+=("$image_name")
done
+23 -13
View File
@@ -8,9 +8,17 @@ x-pcp-service-env: &pcp-service-env
SPRING_CLOUD_CONFIG_LABEL: ""
SPRING_KAFKA_ADMIN_AUTO_CREATE: "true"
SPRING_KAFKA_LISTENER_AUTO_STARTUP: "true"
PCP_POSTGRES_HOST: ${PCP_POSTGRES_HOST:-postgres}
PCP_POSTGRES_PORT: ${PCP_POSTGRES_PORT:-5432}
PCP_KAFKA_HOST: ${PCP_KAFKA_HOST:-kafka}
PCP_KAFKA_PORT: ${PCP_KAFKA_PORT:-9092}
CAMUNDA_CLIENT_ENABLED: ${CAMUNDA_CLIENT_ENABLED:-false}
CAMUNDA_GRPC_ADDRESS: ${CAMUNDA_GRPC_ADDRESS:-http://host.docker.internal:26500}
x-pcp-service-common: &pcp-service-common
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
spring-cloud-config-server:
condition: service_started
@@ -64,11 +72,8 @@ services:
spring-cloud-config-server:
image: pcp/spring-cloud-config-server:${PCP_IMAGE_TAG:-local}
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
kafka:
condition: service_started
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
SPRING_PROFILES_ACTIVE: local
CONFIG_NATIVE_SEARCH_LOCATIONS: file:/config-repo
@@ -86,14 +91,6 @@ services:
ports:
- "${PCP_ADMIN_PORT:-7000}:7000"
tle-monitoring-service:
<<: *pcp-service-common
image: pcp/tle-monitoring-service:${PCP_IMAGE_TAG:-local}
environment:
<<: *pcp-service-env
ports:
- "${PCP_TLE_MONITORING_PORT:-7001}:7001"
pcp-complex-mission-service:
<<: *pcp-service-common
image: pcp/pcp-complex-mission-service:${PCP_IMAGE_TAG:-local}
@@ -118,6 +115,14 @@ services:
ports:
- "${PCP_REQUEST_PORT:-7005}:7005"
pcp-route-processing-service:
<<: *pcp-service-common
image: pcp/pcp-route-processing-service:${PCP_IMAGE_TAG:-local}
environment:
<<: *pcp-service-env
ports:
- "${PCP_ROUTE_PROCESSING_PORT:-7004}:7004"
slots-service:
<<: *pcp-service-common
image: pcp/slots-service:${PCP_IMAGE_TAG:-local}
@@ -193,3 +198,8 @@ services:
volumes:
postgres-data:
kafka-data:
networks:
default:
name: ${PCP_DOCKER_NETWORK:-pcp-local_default}
external: ${PCP_DOCKER_NETWORK_EXTERNAL:-false}
+199
View File
@@ -0,0 +1,199 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "$script_dir/../.." && pwd)"
env_file="$script_dir/.env"
env_example_file="$script_dir/.env.example"
docker_contexts_dir="$script_dir/build/local-docker-contexts"
service_names=(
spring-cloud-config-server
pcp-srpring-boot-admin-server
pcp-satellite-catalog-service
pcp-ballistics-service
pcp-request-service
pcp-route-processing-service
slots-service
pcp-complex-mission-service
pcp-mission-planing-service
pcp-coverage-scheme-service
pcp-dynamic-plan-service
pcp-stations-service
pcp-ui-service
pcp-tgu-service
)
ensure_env_file() {
if [[ ! -f "$env_file" && -f "$env_example_file" ]]; then
cp "$env_example_file" "$env_file"
echo "Created $env_file from .env.example"
fi
}
ensure_source_config_repo_dir() {
if grep -q '^CONFIG_REPO_DIR=' "$env_file"; then
sed -i "s#^CONFIG_REPO_DIR=.*#CONFIG_REPO_DIR=$repo_root/config-repo#" "$env_file"
else
printf '\nCONFIG_REPO_DIR=%s/config-repo\n' "$repo_root" >> "$env_file"
fi
}
load_env() {
set -a
# shellcheck disable=SC1090
source "$env_file"
set +a
}
require_command() {
local command_name="$1"
if ! command -v "$command_name" >/dev/null 2>&1; then
echo "$command_name is required." >&2
exit 1
fi
}
compose() {
if docker compose version >/dev/null 2>&1; then
docker compose --project-directory "$script_dir" -f "$script_dir/docker-compose.yml" "$@"
elif command -v docker-compose >/dev/null 2>&1; then
docker-compose --project-directory "$script_dir" -f "$script_dir/docker-compose.yml" "$@"
else
echo "docker compose or docker-compose is required." >&2
exit 1
fi
}
copy_single_boot_jar() {
local service_name="$1"
local context_dir="$2"
local jar_count
local jar_file
jar_count="$(
find "$repo_root/services/$service_name/build/libs" -maxdepth 1 -type f -name '*.jar' ! -name '*-plain.jar' | wc -l
)"
if [[ "$jar_count" != "1" ]]; then
echo "Expected exactly one bootJar for $service_name, found $jar_count:" >&2
find "$repo_root/services/$service_name/build/libs" -maxdepth 1 -type f -name '*.jar' -print >&2 || true
exit 1
fi
jar_file="$(
find "$repo_root/services/$service_name/build/libs" -maxdepth 1 -type f -name '*.jar' ! -name '*-plain.jar' | head -n 1
)"
mkdir -p "$context_dir/build/libs"
cp "$repo_root/services/$service_name/Dockerfile" "$context_dir/Dockerfile"
cp "$jar_file" "$context_dir/build/libs/app.jar"
}
build_images() {
local gradle_max_workers="${GRADLE_MAX_WORKERS:-1}"
local gradle_jvmargs="${GRADLE_JVMARGS:--Xmx2g -Dfile.encoding=UTF-8}"
local kotlin_daemon_jvmargs="${KOTLIN_DAEMON_JVMARGS:--Xmx3g -XX:MaxMetaspaceSize=768m}"
local gradle_offline="${GRADLE_OFFLINE:-true}"
local runtime_image="${LOCAL_BUNDLE_RUNTIME_IMAGE:-bellsoft/liberica-openjre-alpine:21.0.5}"
local image_tag="${PCP_IMAGE_TAG:-local}"
local service_name
local image_name
local context_dir
local gradle_common_args=(
"--no-parallel"
"--max-workers=$gradle_max_workers"
"-Dorg.gradle.jvmargs=$gradle_jvmargs"
"-Pkotlin.daemon.jvmargs=$kotlin_daemon_jvmargs"
)
if [[ "$gradle_offline" == "true" ]]; then
gradle_common_args+=("--offline")
fi
echo "Building local Docker images..."
echo "Gradle offline mode: $gradle_offline"
echo "Local image tag: $image_tag"
echo "Runtime image: $runtime_image"
for service_name in "${service_names[@]}"; do
echo "Building bootJar for $service_name..."
"$repo_root/gradlew" --project-dir "$repo_root" "${gradle_common_args[@]}" ":services:$service_name:bootJar"
done
rm -rf "$docker_contexts_dir"
mkdir -p "$docker_contexts_dir"
for service_name in "${service_names[@]}"; do
image_name="pcp/$service_name:$image_tag"
context_dir="$docker_contexts_dir/$service_name"
copy_single_boot_jar "$service_name" "$context_dir"
echo "Building Docker image $image_name..."
docker build \
--build-arg "RUNTIME_IMAGE=$runtime_image" \
--file "$context_dir/Dockerfile" \
--tag "$image_name" \
"$context_dir"
done
}
usage() {
cat <<'EOF'
Usage:
./deploy/local-bundle/run-local.sh [command]
Commands:
start Build service images locally and start docker compose.
restart Stop compose stack, then build and start.
build Build service images locally, without starting compose.
stop Stop compose stack.
status Show compose status.
logs Follow compose logs.
EOF
}
start_local() {
build_images
"$script_dir/scripts/start.sh"
}
command="${1:-start}"
case "$command" in
help|-h|--help)
usage
exit 0
;;
esac
ensure_env_file
ensure_source_config_repo_dir
load_env
require_command docker
case "$command" in
start)
start_local
;;
restart)
"$script_dir/scripts/stop.sh"
start_local
;;
build)
build_images
;;
stop)
"$script_dir/scripts/stop.sh"
;;
status)
"$script_dir/scripts/status.sh"
;;
logs)
compose logs -f
;;
*)
usage >&2
exit 1
;;
esac
+242
View File
@@ -0,0 +1,242 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "$script_dir/../.." && pwd)"
env_file="$script_dir/.env"
env_example_file="$script_dir/.env.example"
docker_contexts_dir="$script_dir/build/local-docker-contexts"
service_names=(
spring-cloud-config-server
pcp-srpring-boot-admin-server
pcp-satellite-catalog-service
pcp-ballistics-service
pcp-request-service
pcp-route-processing-service
slots-service
pcp-complex-mission-service
pcp-mission-planing-service
pcp-coverage-scheme-service
pcp-dynamic-plan-service
pcp-stations-service
pcp-ui-service
pcp-tgu-service
)
usage() {
cat <<'EOF'
Usage:
./deploy/local-bundle/run-service.sh SERVICE [command]
Commands:
restart Build SERVICE bootJar, rebuild its Docker image and recreate only this compose service.
build Build SERVICE bootJar and Docker image, without restarting container.
stop Stop SERVICE container.
logs Follow SERVICE logs.
Examples:
./deploy/local-bundle/run-service.sh pcp-request-service
./deploy/local-bundle/run-service.sh pcp-request-service build
EOF
}
ensure_env_file() {
if [[ ! -f "$env_file" && -f "$env_example_file" ]]; then
cp "$env_example_file" "$env_file"
echo "Created $env_file from .env.example"
fi
}
ensure_source_config_repo_dir() {
if grep -q '^CONFIG_REPO_DIR=' "$env_file"; then
sed -i "s#^CONFIG_REPO_DIR=.*#CONFIG_REPO_DIR=$repo_root/config-repo#" "$env_file"
else
printf '\nCONFIG_REPO_DIR=%s/config-repo\n' "$repo_root" >> "$env_file"
fi
}
load_env() {
set -a
# shellcheck disable=SC1090
source "$env_file"
set +a
}
require_command() {
local command_name="$1"
if ! command -v "$command_name" >/dev/null 2>&1; then
echo "$command_name is required." >&2
exit 1
fi
}
compose() {
if docker compose version >/dev/null 2>&1; then
docker compose --project-directory "$script_dir" -f "$script_dir/docker-compose.yml" "$@"
elif command -v docker-compose >/dev/null 2>&1; then
docker-compose --project-directory "$script_dir" -f "$script_dir/docker-compose.yml" "$@"
else
echo "docker compose or docker-compose is required." >&2
exit 1
fi
}
is_known_service() {
local service_name="$1"
local known_service_name
for known_service_name in "${service_names[@]}"; do
if [[ "$known_service_name" == "$service_name" ]]; then
return 0
fi
done
return 1
}
copy_single_boot_jar() {
local service_name="$1"
local context_dir="$2"
local jar_count
local jar_file
jar_count="$(
find "$repo_root/services/$service_name/build/libs" -maxdepth 1 -type f -name '*.jar' ! -name '*-plain.jar' | wc -l
)"
if [[ "$jar_count" != "1" ]]; then
echo "Expected exactly one bootJar for $service_name, found $jar_count:" >&2
find "$repo_root/services/$service_name/build/libs" -maxdepth 1 -type f -name '*.jar' -print >&2 || true
exit 1
fi
jar_file="$(
find "$repo_root/services/$service_name/build/libs" -maxdepth 1 -type f -name '*.jar' ! -name '*-plain.jar' | head -n 1
)"
mkdir -p "$context_dir/build/libs"
cp "$repo_root/services/$service_name/Dockerfile" "$context_dir/Dockerfile"
cp "$jar_file" "$context_dir/build/libs/app.jar"
}
build_service_image() {
local service_name="$1"
local gradle_max_workers="${GRADLE_MAX_WORKERS:-1}"
local gradle_jvmargs="${GRADLE_JVMARGS:--Xmx2g -Dfile.encoding=UTF-8}"
local kotlin_daemon_jvmargs="${KOTLIN_DAEMON_JVMARGS:--Xmx3g -XX:MaxMetaspaceSize=768m}"
local gradle_offline="${GRADLE_OFFLINE:-true}"
local runtime_image="${LOCAL_BUNDLE_RUNTIME_IMAGE:-bellsoft/liberica-openjre-alpine:21.0.5}"
local image_tag="${PCP_IMAGE_TAG:-local}"
local image_name="pcp/$service_name:$image_tag"
local context_dir="$docker_contexts_dir/$service_name"
local gradle_common_args=(
"--no-parallel"
"--max-workers=$gradle_max_workers"
"-Dorg.gradle.jvmargs=$gradle_jvmargs"
"-Pkotlin.daemon.jvmargs=$kotlin_daemon_jvmargs"
)
if [[ "$gradle_offline" == "true" ]]; then
gradle_common_args+=("--offline")
fi
echo "Building bootJar for $service_name..."
"$repo_root/gradlew" --project-dir "$repo_root" "${gradle_common_args[@]}" ":services:$service_name:bootJar"
rm -rf "$context_dir"
copy_single_boot_jar "$service_name" "$context_dir"
echo "Building Docker image $image_name..."
docker build \
--build-arg "RUNTIME_IMAGE=$runtime_image" \
--file "$context_dir/Dockerfile" \
--tag "$image_name" \
"$context_dir"
}
http_get() {
local url="$1"
if command -v curl >/dev/null 2>&1; then
curl -fsS "$url" >/dev/null
elif command -v wget >/dev/null 2>&1; then
wget -q -O /dev/null "$url"
else
return 2
fi
}
wait_for_config_server() {
local attempt
local url="http://localhost:${CONFIG_SERVER_PORT:-38888}/actuator/health"
for attempt in {1..90}; do
if http_get "$url"; then
echo "spring-cloud-config-server is ready: $url"
return 0
fi
sleep 2
done
echo "spring-cloud-config-server is not ready after 180 seconds: $url" >&2
exit 1
}
restart_service() {
local service_name="$1"
build_service_image "$service_name"
if [[ "$service_name" == "spring-cloud-config-server" ]]; then
compose up -d --no-deps --force-recreate "$service_name"
wait_for_config_server
return 0
fi
compose up -d spring-cloud-config-server
wait_for_config_server
compose up -d --no-deps --force-recreate "$service_name"
}
service_name="${1:-}"
command="${2:-restart}"
if [[ -z "$service_name" || "$service_name" == "help" || "$service_name" == "-h" || "$service_name" == "--help" ]]; then
usage
exit 0
fi
if ! is_known_service "$service_name"; then
echo "Unknown service: $service_name" >&2
echo "Known services:" >&2
printf ' %s\n' "${service_names[@]}" >&2
exit 1
fi
ensure_env_file
ensure_source_config_repo_dir
load_env
require_command docker
case "$command" in
restart)
restart_service "$service_name"
;;
build)
build_service_image "$service_name"
;;
stop)
compose stop "$service_name"
;;
logs)
compose logs -f "$service_name"
;;
help|-h|--help)
usage
;;
*)
usage >&2
exit 1
;;
esac
+11 -2
View File
@@ -25,6 +25,10 @@ ensure_env_file() {
}
ensure_env_file
set -a
# shellcheck disable=SC1091
source "$bundle_dir/.env"
set +a
if [[ ! -d "$images_dir" ]]; then
echo "Images directory not found: $images_dir" >&2
@@ -49,7 +53,12 @@ if [[ -f "$images_dir/public-images.txt" ]]; then
fi
cd "$bundle_dir"
echo "Starting postgres, kafka and spring-cloud-config-server..."
compose up -d postgres kafka spring-cloud-config-server
if [[ "${USE_EXTERNAL_INFRA:-false}" == "true" ]]; then
echo "Using external infrastructure. Starting spring-cloud-config-server only."
compose up -d spring-cloud-config-server
else
echo "Starting postgres, kafka and spring-cloud-config-server..."
compose up -d postgres kafka spring-cloud-config-server
fi
echo "Images imported and base services requested."
+53 -6
View File
@@ -43,6 +43,26 @@ wait_for_postgres() {
exit 1
}
wait_for_external_postgres() {
local container_name="${EXTERNAL_POSTGRES_CONTAINER:-}"
local attempt
if [[ -z "$container_name" ]]; then
echo "EXTERNAL_POSTGRES_CONTAINER is required when USE_EXTERNAL_INFRA=true and SKIP_DB_INIT is not true." >&2
exit 1
fi
for attempt in {1..60}; do
if docker exec "$container_name" pg_isready -U "${POSTGRES_USER:-postgres}" -d postgres >/dev/null 2>&1; then
return 0
fi
sleep 2
done
echo "External Postgres is not ready after 120 seconds: $container_name" >&2
exit 1
}
create_database_if_missing() {
local db_name="$1"
local exists
@@ -61,16 +81,35 @@ create_database_if_missing() {
compose exec -T postgres createdb -U "${POSTGRES_USER:-postgres}" "$db_name"
}
create_external_database_if_missing() {
local db_name="$1"
local container_name="${EXTERNAL_POSTGRES_CONTAINER:-}"
local exists
exists="$(
docker exec "$container_name" psql -U "${POSTGRES_USER:-postgres}" -d postgres -tAc \
"SELECT 1 FROM pg_database WHERE datname = '$db_name'" | tr -d '[:space:]'
)"
if [[ "$exists" == "1" ]]; then
echo "Database already exists: $db_name"
return 0
fi
echo "Creating database: $db_name"
docker exec "$container_name" createdb -U "${POSTGRES_USER:-postgres}" "$db_name"
}
databases=(
pcp_ballistics
pcp_requests
pcp_route_processing
pcp_missions
pcp_slots
pcp_complex_plan
pcp_satellite_catalog
pcp_stations
pcp_tgu
pcp_tle
pcp_satellites
)
@@ -78,12 +117,20 @@ ensure_env_file
load_env
cd "$bundle_dir"
compose up -d postgres
wait_for_postgres
if [[ "${USE_EXTERNAL_INFRA:-false}" == "true" ]]; then
wait_for_external_postgres
for db_name in "${databases[@]}"; do
create_database_if_missing "$db_name"
done
for db_name in "${databases[@]}"; do
create_external_database_if_missing "$db_name"
done
else
compose up -d postgres
wait_for_postgres
for db_name in "${databases[@]}"; do
create_database_if_missing "$db_name"
done
fi
echo "Database initialization completed. Tables are left to service Flyway migrations."
echo "No schema objects or seed data were created by this script."
+37 -5
View File
@@ -72,11 +72,34 @@ wait_for_kafka() {
exit 1
}
wait_for_external_kafka() {
local container_name="${EXTERNAL_KAFKA_CONTAINER:-}"
local attempt
if [[ -z "$container_name" ]]; then
echo "External Kafka container is not configured; skipping Kafka readiness check."
return 0
fi
for attempt in {1..60}; do
if docker exec "$container_name" kafka-topics --bootstrap-server "${PCP_KAFKA_HOST:-kafka}:${PCP_KAFKA_PORT:-9092}" --list >/dev/null 2>&1 ||
docker exec "$container_name" kafka-topics.sh --bootstrap-server "${PCP_KAFKA_HOST:-kafka}:${PCP_KAFKA_PORT:-9092}" --list >/dev/null 2>&1; then
echo "External Kafka is ready in container $container_name."
return 0
fi
sleep 2
done
echo "External Kafka is not ready after 120 seconds: $container_name" >&2
exit 1
}
app_services=(
pcp-srpring-boot-admin-server
pcp-satellite-catalog-service
pcp-ballistics-service
pcp-request-service
pcp-route-processing-service
slots-service
pcp-complex-mission-service
pcp-mission-planing-service
@@ -84,7 +107,6 @@ app_services=(
pcp-dynamic-plan-service
pcp-stations-service
pcp-ui-service
tle-monitoring-service
pcp-tgu-service
)
@@ -93,9 +115,16 @@ load_env
cd "$bundle_dir"
echo "Starting infrastructure..."
compose up -d postgres kafka
wait_for_kafka
if [[ "${USE_EXTERNAL_INFRA:-false}" == "true" ]]; then
echo "Using external infrastructure. Skipping local postgres/kafka startup."
echo "Postgres endpoint for services: ${PCP_POSTGRES_HOST:-postgres}:${PCP_POSTGRES_PORT:-5432}"
echo "Kafka endpoint for services: ${PCP_KAFKA_HOST:-kafka}:${PCP_KAFKA_PORT:-9092}"
wait_for_external_kafka
else
echo "Starting infrastructure..."
compose up -d postgres kafka
wait_for_kafka
fi
if [[ "${SKIP_DB_INIT:-false}" != "true" ]]; then
"$script_dir/init-db.sh"
@@ -106,7 +135,10 @@ compose up -d spring-cloud-config-server
wait_for_http "spring-cloud-config-server" "http://localhost:${CONFIG_SERVER_PORT:-38888}/actuator/health"
echo "Starting application services..."
compose up -d "${app_services[@]}"
for service_name in "${app_services[@]}"; do
echo "Starting $service_name..."
compose up -d "$service_name"
done
echo "Application startup requested."
compose ps
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-ballistics-service "$@"
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-complex-mission-service "$@"
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-coverage-scheme-service "$@"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-dynamic-plan-service "$@"
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-mission-planing-service "$@"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-request-service "$@"
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-route-processing-service "$@"
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-satellite-catalog-service "$@"
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-srpring-boot-admin-server "$@"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-stations-service "$@"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-tgu-service "$@"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" pcp-ui-service "$@"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" slots-service "$@"
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$script_dir/run-service.sh" spring-cloud-config-server "$@"
+79
View File
@@ -0,0 +1,79 @@
# Local jar runner
Скрипт запускает backend-сервисы как Spring Boot jar'ы на локальной машине.
Spring Cloud Config Server стартует первым и читает конфигурацию из локального
`config-repo`.
## Запуск
Из корня репозитория:
```bash
./deploy/local-jar/run-services.sh start
```
Что делает `start`:
- собирает `bootJar` для `spring-cloud-config-server` и всех Spring Boot сервисов;
- поднимает локальные `postgres` и `kafka` через `deploy/local-bundle/docker-compose.yml`;
- создаёт сервисные базы данных, если их ещё нет;
- запускает `spring-cloud-config-server` на `http://localhost:38888`;
- проверяет lookup конфигурации из `config-repo`;
- запускает остальные jar'ы в заданной последовательности.
Runtime-файлы создаются в `build/local-jar-runtime`:
- `pids/` - PID процессов;
- `logs/` - stdout/stderr каждого сервиса.
## Команды
```bash
./deploy/local-jar/run-services.sh status
./deploy/local-jar/run-services.sh logs pcp-request-service
./deploy/local-jar/run-services.sh stop
./deploy/local-jar/run-services.sh restart
```
Только сборка:
```bash
./deploy/local-jar/run-services.sh build
```
Только инфраструктура:
```bash
./deploy/local-jar/run-services.sh infra-up
./deploy/local-jar/run-services.sh infra-down
```
## Настройки
По умолчанию config-server использует:
```text
SPRING_PROFILES_ACTIVE=local
CONFIG_NATIVE_SEARCH_LOCATIONS=file:<repo>/config-repo
SERVER_PORT=38888
```
Сервисы используют:
```text
SPRING_PROFILES_ACTIVE=local
SPRING_CLOUD_CONFIG_PROFILE=local
SPRING_CLOUD_CONFIG_LABEL=
CONFIG_SERVER_URI=http://localhost:38888
```
Полезные override'ы:
```bash
START_INFRA=false ./deploy/local-jar/run-services.sh start
INIT_DB=false ./deploy/local-jar/run-services.sh start
WAIT_FOR_APPS=false ./deploy/local-jar/run-services.sh start
JAVA_OPTS="-Xmx1g" ./deploy/local-jar/run-services.sh start
CONFIG_REPO_DIR=/path/to/config-repo ./deploy/local-jar/run-services.sh start
LOCAL_SERVICE_PROFILES=local,foo ./deploy/local-jar/run-services.sh start
```
+538
View File
@@ -0,0 +1,538 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "$script_dir/../.." && pwd)"
local_bundle_dir="$repo_root/deploy/local-bundle"
compose_file="$local_bundle_dir/docker-compose.yml"
local_bundle_env_file="$local_bundle_dir/.env"
if [[ -f "$local_bundle_env_file" ]]; then
set -a
# shellcheck disable=SC1090
source "$local_bundle_env_file"
set +a
fi
config_repo_dir="${CONFIG_REPO_DIR:-$repo_root/config-repo}"
runtime_dir="${LOCAL_JAR_RUNTIME_DIR:-$repo_root/build/local-jar-runtime}"
pid_dir="$runtime_dir/pids"
log_dir="$runtime_dir/logs"
if [[ "$config_repo_dir" != /* ]]; then
config_repo_dir="$repo_root/$config_repo_dir"
fi
if [[ "$runtime_dir" != /* ]]; then
runtime_dir="$repo_root/$runtime_dir"
pid_dir="$runtime_dir/pids"
log_dir="$runtime_dir/logs"
fi
config_service="spring-cloud-config-server"
app_services=(
pcp-srpring-boot-admin-server
pcp-stations-service
pcp-satellite-catalog-service
pcp-request-service
pcp-route-processing-service
pcp-ballistics-service
slots-service
pcp-complex-mission-service
pcp-mission-planing-service
pcp-coverage-scheme-service
pcp-dynamic-plan-service
tle-monitoring-service
pcp-tgu-service
pcp-ui-service
)
all_services=("$config_service" "${app_services[@]}")
databases=(
pcp_ballistics
pcp_requests
pcp_route_processing
pcp_missions
pcp_slots
pcp_complex_plan
pcp_satellite_catalog
pcp_stations
pcp_tgu
pcp_tle
pcp_satellites
)
declare -A service_ports=(
[spring-cloud-config-server]="${CONFIG_SERVER_PORT:-38888}"
[pcp-srpring-boot-admin-server]="${PCP_ADMIN_PORT:-7000}"
[tle-monitoring-service]="${PCP_TLE_MONITORING_PORT:-7001}"
[pcp-complex-mission-service]="${PCP_COMPLEX_MISSION_PORT:-7002}"
[pcp-ballistics-service]="${PCP_BALLISTICS_PORT:-7003}"
[pcp-route-processing-service]="${PCP_ROUTE_PROCESSING_PORT:-7004}"
[pcp-request-service]="${PCP_REQUEST_PORT:-7005}"
[slots-service]="${PCP_SLOTS_PORT:-7006}"
[pcp-ui-service]="${PCP_UI_PORT:-7008}"
[pcp-stations-service]="${PCP_STATIONS_PORT:-7009}"
[pcp-mission-planing-service]="${PCP_MISSION_PORT:-7010}"
[pcp-tgu-service]="${PCP_TGU_PORT:-7011}"
[pcp-coverage-scheme-service]="${PCP_COVERAGE_SCHEME_PORT:-7012}"
[pcp-satellite-catalog-service]="${PCP_SATELLITE_CATALOG_PORT:-7013}"
[pcp-dynamic-plan-service]="${PCP_DYNAMIC_PLAN_PORT:-7014}"
)
gradle_max_workers="${GRADLE_MAX_WORKERS:-1}"
gradle_jvmargs="${GRADLE_JVMARGS:--Xmx2g -Dfile.encoding=UTF-8}"
kotlin_daemon_jvmargs="${KOTLIN_DAEMON_JVMARGS:--Xmx3g -XX:MaxMetaspaceSize=768m}"
config_server_port="${service_ports[$config_service]}"
config_server_uri="${LOCAL_CONFIG_SERVER_URI:-http://localhost:$config_server_port}"
service_profiles="${LOCAL_SERVICE_PROFILES:-local}"
config_profiles="${LOCAL_CONFIG_PROFILES:-$service_profiles}"
postgres_host="${PCP_POSTGRES_HOST:-localhost}"
postgres_port="${PCP_POSTGRES_PORT:-5432}"
kafka_host="${PCP_KAFKA_HOST:-localhost}"
kafka_port="${PCP_KAFKA_PORT:-29092}"
gradle_common_args=(
"--no-parallel"
"--max-workers=$gradle_max_workers"
"-Dorg.gradle.jvmargs=$gradle_jvmargs"
"-Pkotlin.daemon.jvmargs=$kotlin_daemon_jvmargs"
)
usage() {
cat <<'EOF'
Usage:
./deploy/local-jar/run-services.sh [command]
Commands:
start Build bootJar artifacts and start local infra, config-server and services.
restart Stop local jar processes, then run start.
stop Stop jar processes started by this script.
status Show pid and log status for local jar processes.
build Build bootJar artifacts only.
infra-up Start local Postgres/Kafka via deploy/local-bundle/docker-compose.yml.
infra-down Stop local Postgres/Kafka containers.
logs NAME Tail log for a service.
Useful environment:
START_INFRA=false Do not start Postgres/Kafka containers during start.
INIT_DB=false Do not create local Postgres databases during start.
WAIT_FOR_APPS=false Do not wait for each app /actuator/health.
CONFIG_REPO_DIR=... Config repository used by spring-cloud-config-server.
JAVA_OPTS="-Xmx1g" Extra JVM options for every jar process.
LOCAL_SERVICE_PROFILES=local,foo
EOF
}
require_command() {
local command_name="$1"
if ! command -v "$command_name" >/dev/null 2>&1; then
echo "$command_name is required." >&2
exit 1
fi
}
compose() {
if docker compose version >/dev/null 2>&1; then
docker compose --project-directory "$local_bundle_dir" -f "$compose_file" "$@"
elif command -v docker-compose >/dev/null 2>&1; then
docker-compose --project-directory "$local_bundle_dir" -f "$compose_file" "$@"
else
echo "docker compose or docker-compose is required." >&2
exit 1
fi
}
http_get() {
local url="$1"
if command -v curl >/dev/null 2>&1; then
curl -fsS "$url" >/dev/null
elif command -v wget >/dev/null 2>&1; then
wget -q -O /dev/null "$url"
else
return 2
fi
}
wait_for_http() {
local name="$1"
local url="$2"
local attempts="${3:-90}"
local attempt
for ((attempt = 1; attempt <= attempts; attempt++)); do
if http_get "$url"; then
echo "$name is ready: $url"
return 0
fi
sleep 2
done
echo "$name is not ready after $((attempts * 2)) seconds: $url" >&2
exit 1
}
wait_for_postgres() {
local attempt
local postgres_user="${POSTGRES_USER:-postgres}"
for attempt in {1..60}; do
if compose exec -T postgres pg_isready -U "$postgres_user" -d postgres >/dev/null 2>&1; then
echo "Postgres is ready."
return 0
fi
sleep 2
done
echo "Postgres is not ready after 120 seconds." >&2
exit 1
}
wait_for_kafka() {
local attempt
for attempt in {1..60}; do
if compose exec -T kafka kafka-topics.sh --bootstrap-server kafka:9092 --list >/dev/null 2>&1; then
echo "Kafka is ready."
return 0
fi
sleep 2
done
echo "Kafka is not ready after 120 seconds." >&2
exit 1
}
create_database_if_missing() {
local db_name="$1"
local postgres_user="${POSTGRES_USER:-postgres}"
local exists
exists="$(
compose exec -T postgres psql -U "$postgres_user" -d postgres -tAc \
"SELECT 1 FROM pg_database WHERE datname = '$db_name'" | tr -d '[:space:]'
)"
if [[ "$exists" == "1" ]]; then
echo "Database already exists: $db_name"
return 0
fi
echo "Creating database: $db_name"
compose exec -T postgres createdb -U "$postgres_user" "$db_name"
}
start_infra() {
require_command docker
echo "Starting local Postgres and Kafka..."
compose up -d postgres kafka
wait_for_postgres
wait_for_kafka
}
init_databases() {
echo "Initializing local service databases..."
for db_name in "${databases[@]}"; do
create_database_if_missing "$db_name"
done
}
build_services() {
echo "Building bootJar artifacts..."
echo "Gradle max workers: $gradle_max_workers"
echo "Gradle JVM args: $gradle_jvmargs"
echo "Kotlin daemon JVM args: $kotlin_daemon_jvmargs"
for service_name in "${all_services[@]}"; do
echo "Building bootJar for $service_name..."
"$repo_root/gradlew" "${gradle_common_args[@]}" ":services:$service_name:bootJar"
done
}
service_pid_file() {
local service_name="$1"
printf '%s/%s.pid' "$pid_dir" "$service_name"
}
service_log_file() {
local service_name="$1"
printf '%s/%s.log' "$log_dir" "$service_name"
}
is_pid_running() {
local pid="$1"
[[ -n "$pid" ]] && kill -0 "$pid" >/dev/null 2>&1
}
read_service_pid() {
local service_name="$1"
local pid_file
pid_file="$(service_pid_file "$service_name")"
if [[ -f "$pid_file" ]]; then
tr -d '[:space:]' < "$pid_file"
fi
}
find_boot_jar() {
local service_name="$1"
local jar_dir="$repo_root/services/$service_name/build/libs"
local jar_count
local jar_file
if [[ ! -d "$jar_dir" ]]; then
echo "Missing build/libs for $service_name. Run build first." >&2
exit 1
fi
jar_count="$(find "$jar_dir" -maxdepth 1 -type f -name '*.jar' ! -name '*-plain.jar' | wc -l)"
if [[ "$jar_count" != "1" ]]; then
echo "Expected exactly one bootJar for $service_name, found $jar_count:" >&2
find "$jar_dir" -maxdepth 1 -type f -name '*.jar' -print >&2 || true
exit 1
fi
jar_file="$(find "$jar_dir" -maxdepth 1 -type f -name '*.jar' ! -name '*-plain.jar' | head -n 1)"
printf '%s' "$jar_file"
}
start_service() {
local service_name="$1"
local jar_file
local pid_file
local log_file
local existing_pid
local port="${service_ports[$service_name]}"
local java_cmd=(java)
local parsed_java_opts=()
pid_file="$(service_pid_file "$service_name")"
log_file="$(service_log_file "$service_name")"
existing_pid="$(read_service_pid "$service_name" || true)"
if is_pid_running "$existing_pid"; then
echo "$service_name is already running with pid $existing_pid"
return 0
fi
mkdir -p "$pid_dir" "$log_dir"
jar_file="$(find_boot_jar "$service_name")"
if [[ -n "${JAVA_OPTS:-}" ]]; then
read -r -a parsed_java_opts <<< "$JAVA_OPTS"
java_cmd+=("${parsed_java_opts[@]}")
fi
java_cmd+=(-jar "$jar_file")
echo "Starting $service_name on port $port..."
if [[ "$service_name" == "$config_service" ]]; then
(
cd "$repo_root"
env \
SPRING_PROFILES_ACTIVE=local \
CONFIG_NATIVE_SEARCH_LOCATIONS="file:$config_repo_dir" \
SERVER_PORT="$config_server_port" \
"${java_cmd[@]}" > "$log_file" 2>&1 &
echo "$!" > "$pid_file"
)
else
(
cd "$repo_root"
env \
CONFIG_SERVER_URI="$config_server_uri" \
CONFIG_SERVER_FAIL_FAST=true \
SPRING_PROFILES_ACTIVE="$service_profiles" \
SPRING_CLOUD_CONFIG_PROFILE="$config_profiles" \
SPRING_CLOUD_CONFIG_LABEL= \
SPRING_KAFKA_ADMIN_AUTO_CREATE=true \
SPRING_KAFKA_LISTENER_MISSING_TOPICS_FATAL=false \
CAMUNDA_CLIENT_ENABLED=false \
PLANNING_EXTERNAL_UPDATE_ENABLED=false \
TLE_POLLING_ENABLED=false \
PCP_POSTGRES_HOST="$postgres_host" \
PCP_POSTGRES_PORT="$postgres_port" \
PCP_KAFKA_HOST="$kafka_host" \
PCP_KAFKA_PORT="$kafka_port" \
PCP_PORTS_CONFIG="$config_server_port" \
PCP_PORTS_ADMIN="${service_ports[pcp-srpring-boot-admin-server]}" \
PCP_PORTS_TLE_MONITORING="${service_ports[tle-monitoring-service]}" \
PCP_PORTS_COMPLEX_MISSION_SERVICE="${service_ports[pcp-complex-mission-service]}" \
PCP_PORTS_BALLISTICS="${service_ports[pcp-ballistics-service]}" \
PCP_PORTS_ROUTE_PROCESSING="${service_ports[pcp-route-processing-service]}" \
PCP_PORTS_REQUEST="${service_ports[pcp-request-service]}" \
PCP_PORTS_SLOTS="${service_ports[slots-service]}" \
PCP_PORTS_UI="${service_ports[pcp-ui-service]}" \
PCP_PORTS_STATIONS="${service_ports[pcp-stations-service]}" \
PCP_PORTS_MISSION="${service_ports[pcp-mission-planing-service]}" \
PCP_PORTS_TGU="${service_ports[pcp-tgu-service]}" \
PCP_PORTS_COVERAGE_SCHEME="${service_ports[pcp-coverage-scheme-service]}" \
PCP_PORTS_SATELLITE_CATALOG="${service_ports[pcp-satellite-catalog-service]}" \
PCP_PORTS_DYNAMIC_PLAN="${service_ports[pcp-dynamic-plan-service]}" \
"${java_cmd[@]}" > "$log_file" 2>&1 &
echo "$!" > "$pid_file"
)
fi
echo "$service_name log: $log_file"
}
start_config_server() {
if [[ ! -d "$config_repo_dir" ]]; then
echo "Config repo directory does not exist: $config_repo_dir" >&2
exit 1
fi
start_service "$config_service"
wait_for_http "$config_service" "$config_server_uri/actuator/health"
wait_for_http "$config_service config-repo lookup" "$config_server_uri/pcp-request-service/local"
}
wait_for_service_health() {
local service_name="$1"
local port="${service_ports[$service_name]}"
wait_for_http "$service_name" "http://localhost:$port/actuator/health" "${APP_HEALTH_ATTEMPTS:-90}"
}
start_app_services() {
for service_name in "${app_services[@]}"; do
start_service "$service_name"
if [[ "${WAIT_FOR_APPS:-true}" == "true" ]]; then
wait_for_service_health "$service_name"
fi
done
}
stop_service() {
local service_name="$1"
local pid_file
local pid
local attempt
pid_file="$(service_pid_file "$service_name")"
pid="$(read_service_pid "$service_name" || true)"
if ! is_pid_running "$pid"; then
rm -f "$pid_file"
echo "$service_name is not running."
return 0
fi
echo "Stopping $service_name with pid $pid..."
kill "$pid" >/dev/null 2>&1 || true
for attempt in {1..30}; do
if ! is_pid_running "$pid"; then
rm -f "$pid_file"
return 0
fi
sleep 1
done
echo "Force stopping $service_name with pid $pid..."
kill -9 "$pid" >/dev/null 2>&1 || true
rm -f "$pid_file"
}
stop_services() {
local service_name
local idx
for ((idx = ${#app_services[@]} - 1; idx >= 0; idx--)); do
service_name="${app_services[$idx]}"
stop_service "$service_name"
done
stop_service "$config_service"
}
status_services() {
local service_name
local pid
local log_file
printf '%-36s %-10s %s\n' "service" "pid" "log"
for service_name in "${all_services[@]}"; do
pid="$(read_service_pid "$service_name" || true)"
log_file="$(service_log_file "$service_name")"
if is_pid_running "$pid"; then
printf '%-36s %-10s %s\n' "$service_name" "$pid" "$log_file"
else
printf '%-36s %-10s %s\n' "$service_name" "-" "$log_file"
fi
done
}
tail_logs() {
local service_name="${1:-}"
local log_file
if [[ -z "$service_name" ]]; then
echo "Service name is required for logs command." >&2
exit 1
fi
log_file="$(service_log_file "$service_name")"
if [[ ! -f "$log_file" ]]; then
echo "Log file does not exist: $log_file" >&2
exit 1
fi
tail -f "$log_file"
}
start_all() {
require_command java
build_services
if [[ "${START_INFRA:-true}" == "true" ]]; then
start_infra
if [[ "${INIT_DB:-true}" == "true" ]]; then
init_databases
fi
else
echo "Skipping local infra startup. Expecting Postgres at $postgres_host:$postgres_port and Kafka at $kafka_host:$kafka_port."
fi
start_config_server
start_app_services
echo "Local jar startup requested."
status_services
}
command="${1:-start}"
case "$command" in
start)
start_all
;;
restart)
stop_services
start_all
;;
stop)
stop_services
;;
status)
status_services
;;
build)
build_services
;;
infra-up)
start_infra
if [[ "${INIT_DB:-true}" == "true" ]]; then
init_databases
fi
;;
infra-down)
require_command docker
compose stop kafka postgres
;;
logs)
tail_logs "${2:-}"
;;
help|-h|--help)
usage
;;
*)
usage >&2
exit 1
;;
esac