Skip to content
Draft

Wars #616

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
6 changes: 6 additions & 0 deletions docker-compose/multiwar/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SHOULD_INIT_GEO_DATA=true
SHOULD_INIT_LINES_CATALOG=true

COMPOSE_PROJECT_NAME=gridstudy

TOMCAT_PORT=12345
58 changes: 58 additions & 0 deletions docker-compose/multiwar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# WAR Deploy Tool

Generated with little supervision by opus4.6.

Builds GridSuite microservices as WAR files for docker-compose deployment, **without modifying any original source files**.

## How it works

Instead of patching server sources, this tool generates **wrapper Maven projects** that:
1. Depend on the original server JAR (classes artifact)
2. Add a generated `SpringBootServletInitializer` class that delegates to the original Application class
3. Package everything as a WAR with a clean context-path name

```
Original server (unmodified) Wrapper project (generated)
┌─────────────────────────┐ ┌─────────────────────────┐
│ report-server/ │ │ war-wrappers/report-server/
│ pom.xml (jar) │◄─────────│ pom.xml (war) │
│ ReportApplication.java│ │ WarInitializer.java │
└─────────────────────────┘ └─────────────────────────┘
│ │
▼ ▼
gridsuite-report-server.jar report-server.war
```
Comment on lines +14 to +24
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language to the fenced code block.

Line 14 opens a fence without language; this triggers markdownlint MD040.

Suggested fix
-```
+```text
 Original server (unmodified)          Wrapper project (generated)
 ┌─────────────────────────┐          ┌─────────────────────────┐
 │ report-server/          │          │ war-wrappers/report-server/
 │   pom.xml (jar)         │◄─────────│   pom.xml (war)         │
 │   ReportApplication.java│          │   WarInitializer.java   │
 └─────────────────────────┘          └─────────────────────────┘
          │                                      │
          ▼                                      ▼
   gridsuite-report-server.jar          report-server.war
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.1)</summary>

[warning] 14-14: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docker-compose/multiwar/README.md around lines 14 - 24, The fenced code
block in docker-compose/multiwar/README.md is missing a language specifier which
triggers markdownlint MD040; update the opening fence (the triple backticks that
start the diagram block) to include a language token such as text (e.g., change
totext) so the block is explicitly marked and the linter warning is
resolved.


</details>

<!-- fingerprinting:phantom:poseidon:hawk -->

<!-- d98c2f50 -->

<!-- This is an auto-generated comment by CodeRabbit -->


## Quick Start

```bash
# Build all WARs and prepare for docker-compose
./wars.sh

# Start the stack
docker compose up
```

## Enabling/disabling servers

Edit the `MANIFEST` array in `war-deploy.sh`. Comment lines with `#` to disable:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix script filename in docs.

Line 38 references war-deploy.sh, while Quick Start uses ./wars.sh. Keep one canonical name to avoid copy/paste failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose/multiwar/README.md` at line 38, The README instruction
mentions editing the MANIFEST array in "war-deploy.sh" but the Quick Start and
repo use "./wars.sh"; update the documentation so the script name is
consistent—change the reference "war-deploy.sh" to "./wars.sh" (or vice‑versa if
you prefer the other canonical name) in the sentence that instructs editing the
MANIFEST array and commenting lines with "#", ensuring the MANIFEST and example
commands all use the same script name (referenced symbols: MANIFEST array,
war-deploy.sh, ./wars.sh).


```bash
"actions-server|actions-server||..." # enabled
#"timeseries-server|timeseries-server||..." # disabled
```

## Prerequisites

- Java 21+
- Maven 3.8+
- Server submodules checked out and buildable in the layout of Gridsuite Aggregator

## Excluded (Boot WebFlux — incompatible(??) with WAR)

- config-server
- config-notification-server
- directory-notification-server
- merge-notification-server
- study-notification-server
- gateway
252 changes: 252 additions & 0 deletions docker-compose/multiwar/docker-compose.multiwar.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
services:

# ===========================================================================
# SINGLE TOMCAT — all WAR-deployed microservices (33 webmvc servers)
# ===========================================================================
tomcat:
# JDK 21 required: servers are compiled with Java 21 (class file version 65)
image: tomcat:10.1-jdk21
ports:
- "${TOMCAT_PORT:-12345}:8080"
volumes:
# Custom server.xml: sets Host startStopThreads="10" for parallel WAR deployment
- ./server.xml:/usr/local/tomcat/conf/server.xml:Z
- ./gen/wars:/usr/local/tomcat/webapps:Z
# Per-WAR config overrides (base-uri rewrites) loaded via spring.config.additional-location
# in each WAR's SpringBootServletInitializer. Only contains service base-uri mappings.
- ./gen/externalized-war-configs:/config:Z
# Merged powsybl itools config (loadflow params, writeReferenceTerminals: false, etc.)
- ./itools-config.yml:/root/.itools/config.yml:Z
environment:
# spring.profiles.active=default: disables "local" profile so application-local.yml
# files embedded in server JARs are NOT loaded. Infrastructure defaults (rabbitmq,
# postgres, elasticsearch hosts) come from powsybl-ws-commons.jar application.yaml
# which already has the correct docker service names.
- CATALINA_OPTS=-Dspring.profiles.active=default -Xmx2g
depends_on:
- postgres
- rabbitmq
- elasticsearch
- s3-storage
# Wait for elasticsearch before starting Tomcat: WARs that depend on elasticsearch
# (study-server, directory-server, case-server, network-conversion-server) fail
# immediately if ES is not reachable at startup (no built-in retry).
entrypoint: ["/bin/bash", "-c", "until curl -sf http://elasticsearch:9200/_cluster/health; do echo 'Waiting for elasticsearch...'; sleep 3; done && catalina.sh run"]
restart: unless-stopped
memswap_limit: 5g
deploy:
resources:
limits:
memory: 5g
# Future: move shared JARs (Spring, Hibernate, powsybl-*) to Tomcat lib/ so
# they are loaded once by the common classloader, reducing Metaspace usage.
# Current setup: each WAR bundles ~200MB of dependencies, many identical across
# WARs. Shared classloading could cut memory by 30-50%.

# ===========================================================================
# NON-WAR SERVICES (WebFlux / notification servers)
# Future: avoid redeclaring these services here — find a way to reuse the
# definitions from docker-compose.base.yml directly.
# ===========================================================================
config-server:
image: gridsuite/config-server:latest
ports:
- 5025:80
volumes:
- ../../k8s/resources/common/config/config-server-application.yml:/config/specific/application.yml:Z
- ./nonwars-to-tomcat-config/common-application.yml:/config/common/application.yml:Z
restart: unless-stopped
environment:
- JAVA_TOOL_OPTIONS=-Xmx96m
command: --server.port=80 --spring.config.additional-location=/config/
sysctls:
- net.ipv4.ip_unprivileged_port_start=0
memswap_limit: 384m
deploy:
resources:
limits:
memory: 384m

config-notification-server:
image: gridsuite/config-notification-server:latest
ports:
- 5024:80
volumes:
- ../../k8s/resources/common/config/config-notification-server-application.yml:/config/specific/application.yml:Z
- ./nonwars-to-tomcat-config/common-application.yml:/config/common/application.yml:Z
restart: unless-stopped
environment:
- JAVA_TOOL_OPTIONS=-Xmx96m
command: --server.port=80 --spring.config.additional-location=/config/
sysctls:
- net.ipv4.ip_unprivileged_port_start=0
memswap_limit: 384m
deploy:
resources:
limits:
memory: 384m

study-notification-server:
image: gridsuite/study-notification-server:latest
ports:
- 5009:80
volumes:
- ../../k8s/resources/study/config/study-notification-server-application.yml:/config/specific/application.yml:Z
- ./nonwars-to-tomcat-config/common-application.yml:/config/common/application.yml:Z
restart: unless-stopped
environment:
- JAVA_TOOL_OPTIONS=-Xmx96m
command: --server.port=80 --spring.config.additional-location=/config/
sysctls:
- net.ipv4.ip_unprivileged_port_start=0
memswap_limit: 384m
deploy:
resources:
limits:
memory: 384m

directory-notification-server:
image: gridsuite/directory-notification-server:latest
ports:
- 5004:80
volumes:
- ../../k8s/resources/study/config/directory-notification-server-application.yml:/config/specific/application.yml:Z
- ./nonwars-to-tomcat-config/common-application.yml:/config/common/application.yml:Z
restart: unless-stopped
environment:
- JAVA_TOOL_OPTIONS=-Xmx96m
command: --server.port=80 --spring.config.additional-location=/config/
sysctls:
- net.ipv4.ip_unprivileged_port_start=0
memswap_limit: 384m
deploy:
resources:
limits:
memory: 384m

gateway:
image: gridsuite/gateway:latest
ports:
- 9000:80
volumes:
- ../../k8s/resources/common/config/gateway-application.yml:/config/specific/application.yml:Z
- ./nonwars-to-tomcat-config/common-application.yml:/config/common/application.yml:Z
- ../allowed-issuers.yml:/config/allowed-issuers.yml:Z
restart: unless-stopped
environment:
- JAVA_TOOL_OPTIONS=-Xmx96m
command: --server.port=80 --spring.config.additional-location=/config/
sysctls:
- net.ipv4.ip_unprivileged_port_start=0
memswap_limit: 384m
deploy:
resources:
limits:
memory: 384m

# ===========================================================================
# FRONTEND APPS
# ===========================================================================
gridstudy-app:
image: gridsuite/gridstudy-app:latest
ports:
- 84:8080
volumes:
- ../study/gridstudy-app-idpSettings.json:/opt/bitnami/apache/htdocs/gridstudy/idpSettings.json:Z
- ../env.json:/opt/bitnami/apache/htdocs/gridstudy/env.json:Z
memswap_limit: 128m
deploy:
resources:
limits:
memory: 128m
restart: unless-stopped

gridexplore-app:
image: gridsuite/gridexplore-app:latest
ports:
- 80:8080
volumes:
- ../study/gridexplore-app-idpSettings.json:/opt/bitnami/apache/htdocs/gridexplore/idpSettings.json:Z
- ../env.json:/opt/bitnami/apache/htdocs/gridexplore/env.json:Z
memswap_limit: 128m
deploy:
resources:
limits:
memory: 128m
restart: unless-stopped

gridadmin-app:
image: gridsuite/gridadmin-app:latest
ports:
- 82:8080
volumes:
- ../study/gridadmin-app-idpSettings.json:/opt/bitnami/apache/htdocs/gridadmin/idpSettings.json:Z
- ../env.json:/opt/bitnami/apache/htdocs/gridadmin/env.json:Z
memswap_limit: 128m
deploy:
resources:
limits:
memory: 128m
restart: unless-stopped

# ===========================================================================
# MOCK & METADATA
# ===========================================================================
mock-user-service:
image: gridsuite/oidc-mock-server
Comment on lines +8 to +196
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List image declarations that are not digest-pinned.
rg -n '^\s*image:\s+' docker-compose/multiwar/docker-compose.multiwar.yml | rg -v '@sha256:'

Repository: gridsuite/deployment

Length of output: 539


🏁 Script executed:

#!/bin/bash
# Get all image declarations and categorize them
echo "=== All image declarations ==="
rg -n '^\s*image:\s+' docker-compose/multiwar/docker-compose.multiwar.yml

echo -e "\n=== Digest-pinned images ==="
rg -n '^\s*image:\s+.*@sha256:' docker-compose/multiwar/docker-compose.multiwar.yml || echo "None found"

echo -e "\n=== :latest images ==="
rg -n '^\s*image:\s+.*:latest' docker-compose/multiwar/docker-compose.multiwar.yml || echo "None found"

echo -e "\n=== Untagged images (default to :latest) ==="
rg -n '^\s*image:\s+[^:]*$' docker-compose/multiwar/docker-compose.multiwar.yml || echo "None found"

echo -e "\n=== Other version tags ==="
rg -n '^\s*image:\s+' docker-compose/multiwar/docker-compose.multiwar.yml | grep -v '@sha256:' | grep -v ':latest' | grep ':' || echo "None found"

Repository: gridsuite/deployment

Length of output: 1459


Pin container images to immutable versions using digests.

Nine of eleven images lack digest pins, reducing reproducibility and increasing supply-chain drift risk. Eight images use explicit :latest tags (lines 52, 71, 90, 109, 128, 151, 165, 179), one is untagged and defaults to :latest (line 196), and one uses a major.minor version tag (line 8: tomcat:10.1-jdk21). Only the bitnami/apache image (line 239) is currently pinned by digest, demonstrating the pattern already in use.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose/multiwar/docker-compose.multiwar.yml` around lines 8 - 196,
Several services use mutable tags (e.g., tomcat:10.1-jdk21 and many
gridsuite/:latest images) which must be replaced with immutable digest-pinned
images; for each image reference in this compose (identify by service names: the
Tomcat service, config-server, config-notification-server,
study-notification-server, directory-notification-server, gateway,
gridstudy-app, gridexplore-app, gridadmin-app, mock-user-service) retrieve the
exact image digest from the registry (docker pull or registry API) and replace
the tag form (image: name:tag) with the digest form (image:
name@sha256:<digest>), keeping existing ports/volumes/env intact, and commit the
updated docker-compose.multiwar.yml so all images are pinned like the existing
bitnami/apache digest.

ports:
- 9090:9090
environment:
- PORT=9090
- ISSUER_HOST=172.17.0.1:9090
- USERS_PROFILE=UTILISATEURS|ADMIN|ADMIN_EXPLORE
- CLIENT_COUNT=8
- CLIENT_ID=gridexplore-client
- CLIENT_REDIRECT_URI=http://localhost:80/sign-in-callback
- CLIENT_LOGOUT_REDIRECT_URI=http://localhost:80/logout-callback
- CLIENT_SILENT_REDIRECT_URI=http://localhost:80/silent-renew-callback
- CLIENT_ID_2=gridadmin-client
- CLIENT_REDIRECT_URI_2=http://localhost:82/sign-in-callback
- CLIENT_LOGOUT_REDIRECT_URI_2=http://localhost:82/logout-callback
- CLIENT_SILENT_REDIRECT_URI_2=http://localhost:82/silent-renew-callback
- CLIENT_ID_3=griddyna-client
- CLIENT_REDIRECT_URI_3=http://localhost:83/sign-in-callback
- CLIENT_LOGOUT_REDIRECT_URI_3=http://localhost:83/logout-callback
- CLIENT_SILENT_REDIRECT_URI_3=http://localhost:83/silent-renew-callback
- CLIENT_ID_4=gridstudy-client
- CLIENT_REDIRECT_URI_4=http://localhost:84/sign-in-callback
- CLIENT_LOGOUT_REDIRECT_URI_4=http://localhost:84/logout-callback
- CLIENT_SILENT_REDIRECT_URI_4=http://localhost:84/silent-renew-callback
- CLIENT_ID_5=gridexplore-local
- CLIENT_REDIRECT_URI_5=http://localhost:3000/sign-in-callback
- CLIENT_LOGOUT_REDIRECT_URI_5=http://localhost:3000/logout-callback
- CLIENT_SILENT_REDIRECT_URI_5=http://localhost:3000/silent-renew-callback
- CLIENT_ID_6=gridadmin-local
- CLIENT_REDIRECT_URI_6=http://localhost:3002/sign-in-callback
- CLIENT_LOGOUT_REDIRECT_URI_6=http://localhost:3002/logout-callback
- CLIENT_SILENT_REDIRECT_URI_6=http://localhost:3002/silent-renew-callback
- CLIENT_ID_7=griddyna-local
- CLIENT_REDIRECT_URI_7=http://localhost:3003/sign-in-callback
- CLIENT_LOGOUT_REDIRECT_URI_7=http://localhost:3003/logout-callback
- CLIENT_SILENT_REDIRECT_URI_7=http://localhost:3003/silent-renew-callback
- CLIENT_ID_8=gridstudy-local
- CLIENT_REDIRECT_URI_8=http://localhost:3004/sign-in-callback
- CLIENT_LOGOUT_REDIRECT_URI_8=http://localhost:3004/logout-callback
- CLIENT_SILENT_REDIRECT_URI_8=http://localhost:3004/silent-renew-callback
restart: unless-stopped

apps-metadata-server:
image: bitnami/apache:2.4.55-debian-11-r3@sha256:bbe50190eb3bbf3be6f61318004480b3230846bfd52dec9286bd1862254c1719
ports:
- 8070:8080
volumes:
- ../apps-metadata.json:/opt/bitnami/apache/htdocs/apps-metadata.json:Z
- ../../k8s/resources/common/config/apps-metadata-base-voltages.json:/opt/bitnami/apache/htdocs/apps-metadata-base-voltages.json:Z
- ../version.json:/opt/bitnami/apache/htdocs/version.json:Z
- ../gridapps-metadata-httpd.conf:/opt/bitnami/apache/conf/bitnami/bitnami.conf:Z
memswap_limit: 128m
deploy:
resources:
limits:
memory: 128m
restart: unless-stopped
3 changes: 3 additions & 0 deletions docker-compose/multiwar/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include:
- ../technical/docker-compose.technical.yml
- docker-compose.multiwar.yml
66 changes: 66 additions & 0 deletions docker-compose/multiwar/itools-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Merged itools config for all WAR-deployed servers in Tomcat.
# Mounted at /root/.itools/config.yml (Tomcat container runs as root).
#
# In k8s, each server has its own config.yml. Here we merge them all because
# all 33 WARs share a single JVM. Key settings:
# - writeReferenceTerminals: false — workaround for ReferenceTerminals extension
# not implemented in network-store (causes InvalidClassException without it)
# - network.default-impl-name: NetworkStore — forces iidm to use network-store
# instead of in-memory impl (which is on classpath due to cvg-extension)
# - computation-local.available-core: 4 — limits concurrent computation processes
#
# Source files merged:
# k8s/resources/common/config/loadflow-server-config.yml
# k8s/resources/common/config/network-conversion-server-config.yml
# k8s/resources/study/config/network-modification-server-config.yml
# k8s/resources/study/config/dynawo-itools-config.yml
# k8s/resources/study/config/voltage-init-server-config.yml
# k8s/resources/study/config/sensitivity-analysis-server-config.yml
# k8s/resources/study/config/security-analysis-server-config.yml
# k8s/resources/study/config/shortcircuit-server-config.yml
# k8s/resources/monitor/config/monitor-lf-worker-server-config.yml
# k8s/resources/monitor/config/monitor-sa-worker-server-config.yml

hades2:
homeDir: /hades2
debug: false

dynaflow:
homeDir: /dynaflow-launcher
debug: false

dynawo:
homeDir: /dynaflow-launcher
debug: false

dynawo-algorithms:
homeDir: /dynaflow-launcher
debug: false

network:
default-impl-name: NetworkStore

dynaflow-default-parameters:
mergeLoads: false

computation-local:
available-core: 4

load-flow-default-parameters:
voltageInitMode: DC_VALUES
phaseShifterRegulationOn: true
twtSplitShuntAdmittance: true
dcUseTransformerRatio: false
countriesToBalance:
- FR

open-loadflow-default-parameters:
writeReferenceTerminals: false
slackDistributionFailureBehavior: FAIL

open-security-analysis-default-parameters:
threadCount: 2

import-export-parameters-default-value:
iidm.import.cgmes.cgm-with-subnetworks: false
ucte.import.create-areas: false
Loading