From 91f4f007d389c4e1a1367073a63ce12ebaaa146e Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Mon, 14 Sep 2026 14:13:45 +0200 Subject: [PATCH 01/18] fix(backend): corrige la syntaxe du bloc except de la sonde de disponibilite `except SQLAlchemyError, OSError:` est de la syntaxe Python 2. Le module health.py ne s'importait pas, ce qui cassait make dev, make test, make typecheck et alembic. --- apps/backend/app/api/v1/endpoints/health.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/app/api/v1/endpoints/health.py b/apps/backend/app/api/v1/endpoints/health.py index b3c8523..711bf89 100644 --- a/apps/backend/app/api/v1/endpoints/health.py +++ b/apps/backend/app/api/v1/endpoints/health.py @@ -24,7 +24,7 @@ async def liveness(settings: SettingsDep) -> LivenessStatus: async def readiness(session: SessionDep) -> ReadinessStatus: try: await session.execute(text("SELECT 1")) - except SQLAlchemyError, OSError: + except (SQLAlchemyError, OSError): logger.exception("Base de donnees injoignable") raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, From 351e928309bc9d69a580a2b98bf9d1d3bb375458 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Mon, 14 Sep 2026 14:19:25 +0200 Subject: [PATCH 02/18] feat(db): bootstrap PostgreSQL et extension TimescaleDB Service `db` du docker-compose racine sur timescale/timescaledb-ha:pg17, volume nomme et cibles Makefile db-up / db-down / db-reset / db-logs / db-psql. - db/init/100-extensions.sql declare l'extension attendue, db/init/110 cree la base enervision_test utilisee par la suite de tests du backend. - Numerotation a partir de 100 : l'image depose ses propres scripts 000, 001 et 010, et un prefixe a deux chiffres se trie avant 010 en locale C. - Volume monte sur /home/postgres/pgdata/data, PGDATA de cette image. Monte au chemin habituel de l'image postgres, il ne retiendrait rien sans erreur. - Port publie 5433 par defaut, 5432 etant souvent deja pris sur un poste. --- .env.example | 15 ++++++++++++ Makefile | 26 +++++++++++++++++++-- db/README.md | 26 ++++++++++++++++++++- db/init/.gitkeep | 0 db/init/100-extensions.sql | 4 ++++ db/init/110-test-database.sql | 8 +++++++ docker-compose.yml | 43 +++++++++++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 .env.example delete mode 100644 db/init/.gitkeep create mode 100644 db/init/100-extensions.sql create mode 100644 db/init/110-test-database.sql create mode 100644 docker-compose.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4d97678 --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +# Variables lues par docker-compose.yml a la racine. +# Le backend lance hors conteneur (`make dev`) lit apps/backend/.env, pas ce fichier. + +POSTGRES_USER=enervision +POSTGRES_PASSWORD=change_me +POSTGRES_DB=enervision +# 5432 est souvent deja pris par une autre base du poste. +POSTGRES_PORT=5433 + +APP_ENV=local +APP_DEBUG=true +APP_LOG_LEVEL=INFO +APP_SECRET_KEY=change_me +APP_CORS_ORIGINS=http://localhost:4200 +BACKEND_PORT=8000 diff --git a/Makefile b/Makefile index 71dea97..aa29df8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ BACKEND := apps/backend .DEFAULT_GOAL := help -.PHONY: help install dev lint format typecheck test check docker-build +.PHONY: help install dev lint format typecheck test test-integration check docker-build \ + db-up db-down db-reset db-logs db-psql migrate help: ## Liste les cibles disponibles @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}' @@ -21,10 +22,31 @@ format: ## Formate et corrige le backend typecheck: ## Verifie le typage du backend cd $(BACKEND) && uv run mypy app -test: ## Execute les tests backend +test: ## Execute les tests backend ne demandant pas de base cd $(BACKEND) && uv run pytest +test-integration: ## Execute les tests exigeant une base joignable + cd $(BACKEND) && uv run pytest -m integration + check: lint typecheck test ## Chaine de verification complete docker-build: ## Construit l'image du backend docker build -t enervision-backend:local $(BACKEND) + +db-up: ## Demarre la base PostgreSQL TimescaleDB + docker compose up -d db + +db-down: ## Arrete la base en conservant ses donnees + docker compose stop db + +db-reset: ## Detruit la base et rejoue db/init + docker compose down -v && docker compose up -d db + +db-logs: ## Suit les journaux de la base + docker compose logs -f db + +db-psql: ## Ouvre une session psql sur la base applicative + docker compose exec db psql -U $${POSTGRES_USER:-enervision} -d $${POSTGRES_DB:-enervision} + +migrate: ## Applique les migrations Alembic + cd $(BACKEND) && uv run alembic upgrade head diff --git a/db/README.md b/db/README.md index e80cb3c..c21a87d 100644 --- a/db/README.md +++ b/db/README.md @@ -1,6 +1,7 @@ # Base de donnees -PostgreSQL avec l'extension TimescaleDB. Non initialise, voir le ticket dedie. +PostgreSQL 17 avec l'extension TimescaleDB, servie en local par le service `db` du +`docker-compose.yml` racine (image `timescale/timescaledb-ha:pg17`). - `init` : scripts de bootstrap joues au premier demarrage du conteneur. - `migrations` : migrations SQL versionnees. @@ -8,3 +9,26 @@ PostgreSQL avec l'extension TimescaleDB. Non initialise, voir le ticket dedie. Les migrations du schema applicatif expose par l'API vivent dans `apps/backend/alembic`, pas ici. + +## `init` ne rejoue jamais + +Le dossier est monte sur `/docker-entrypoint-initdb.d`, dont PostgreSQL ne joue le +contenu qu'a la toute premiere initialisation, quand `PGDATA` est vide. Modifier ou +ajouter un script ensuite reste sans effet sur une base existante : + +```bash +docker compose down -v && docker compose up -d db +``` + +L'image joue d'abord ses propres scripts (`000_`, `001_`, `010_`), dont un +`CREATE EXTENSION IF NOT EXISTS timescaledb_toolkit CASCADE` qui installe `timescaledb` +au passage dans `postgres`, `template1` et la base applicative. Nos fichiers sont +numerotes a partir de `100` pour passer apres, quelle que soit la locale de tri. + +| Script | Role | +|---|---| +| `100-extensions.sql` | Declare explicitement les extensions attendues. | +| `110-test-database.sql` | Cree `enervision_test`, attendue par la suite de tests du backend. | + +Comme un bootstrap peut toujours avoir ete saute, c'est `/api/v1/health/ready` qui fait +foi : la sonde refuse de repondre 200 si l'extension n'est pas chargee. diff --git a/db/init/.gitkeep b/db/init/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/db/init/100-extensions.sql b/db/init/100-extensions.sql new file mode 100644 index 0000000..2326d5d --- /dev/null +++ b/db/init/100-extensions.sql @@ -0,0 +1,4 @@ +-- Piege : ce script ne rejoue qu'a la premiere initialisation, quand PGDATA est vide. +-- Le modifier ensuite reste sans effet tant que le volume n'est pas detruit. + +CREATE EXTENSION IF NOT EXISTS timescaledb; diff --git a/db/init/110-test-database.sql b/db/init/110-test-database.sql new file mode 100644 index 0000000..0f47b63 --- /dev/null +++ b/db/init/110-test-database.sql @@ -0,0 +1,8 @@ +-- Contrainte : le nom de cette base est code en dur dans apps/backend/tests/conftest.py. +-- Elle sert la suite de tests de la stack locale, pas un deploiement. + +CREATE DATABASE enervision_test; + +\connect enervision_test + +CREATE EXTENSION IF NOT EXISTS timescaledb; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e6854d8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,43 @@ +# Piege : PGDATA de l'image timescaledb-ha vaut /home/postgres/pgdata/data, pas le chemin +# habituel de l'image postgres. Monte ailleurs, le volume ne retient rien, sans erreur. + +name: enervision + +services: + db: + image: timescale/timescaledb-ha:pg17 + environment: + POSTGRES_USER: ${POSTGRES_USER:?} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?} + POSTGRES_DB: ${POSTGRES_DB:?} + ports: + - "${POSTGRES_PORT:-5433}:5432" + volumes: + - pgdata:/home/postgres/pgdata/data + - ./db/init:/docker-entrypoint-initdb.d:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 40s + restart: unless-stopped + + backend: + build: ./apps/backend + depends_on: + db: + condition: service_healthy + environment: + APP_ENV: ${APP_ENV:-local} + APP_DEBUG: ${APP_DEBUG:-false} + APP_LOG_LEVEL: ${APP_LOG_LEVEL:-INFO} + APP_SECRET_KEY: ${APP_SECRET_KEY:?} + APP_CORS_ORIGINS: ${APP_CORS_ORIGINS:-http://localhost:4200} + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} + ports: + - "${BACKEND_PORT:-8000}:8000" + restart: unless-stopped + +volumes: + pgdata: From 20e7374d90b9c6fdded2e4cf3c62a3061d5c0f78 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Mon, 14 Sep 2026 14:19:25 +0200 Subject: [PATCH 03/18] feat(backend): verifie l extension TimescaleDB sur la sonde de disponibilite /api/v1/health/ready interrogeait la base par un SELECT 1, qui ne distingue pas un PostgreSQL nu d'un PostgreSQL avec TimescaleDB. La sonde lit desormais pg_extension et repond 503 si l'extension manque, cas qui survient quand db/init n'a pas ete joue. - Premiere revision Alembic : aucune table, une garde qui refuse de s'appliquer sans l'extension. - Tests du chemin nominal et de l'extension absente, plus un test marque `integration` contre la vraie base. pytest ecarte ce marqueur par defaut pour que make check reste jouable sans Docker. - conftest recycle l'engine entre les tests : get_engine est lru_cache alors que pytest-asyncio ouvre une boucle par test, et les connexions asyncpg sont liees a leur boucle. --- apps/backend/.env.example | 2 +- apps/backend/README.md | 14 ++++- ...4f094_socle_garde_extension_timescaledb.py | 37 ++++++++++++++ apps/backend/app/api/v1/endpoints/health.py | 16 ++++-- apps/backend/app/schemas/health.py | 1 + apps/backend/pyproject.toml | 3 +- apps/backend/tests/api/test_health.py | 51 ++++++++++++++++++- apps/backend/tests/conftest.py | 14 ++++- 8 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 apps/backend/alembic/versions/5353c0e4f094_socle_garde_extension_timescaledb.py diff --git a/apps/backend/.env.example b/apps/backend/.env.example index 258db03..cd96463 100644 --- a/apps/backend/.env.example +++ b/apps/backend/.env.example @@ -3,4 +3,4 @@ APP_DEBUG=true APP_LOG_LEVEL=INFO APP_SECRET_KEY=change_me APP_CORS_ORIGINS=http://localhost:4200 -DATABASE_URL=postgresql+asyncpg://enervision:change_me@localhost:5432/enervision +DATABASE_URL=postgresql+asyncpg://enervision:change_me@localhost:5433/enervision diff --git a/apps/backend/README.md b/apps/backend/README.md index bd1c6fa..d70b3ca 100644 --- a/apps/backend/README.md +++ b/apps/backend/README.md @@ -22,6 +22,9 @@ uv sync --all-groups `APP_SECRET_KEY` et `DATABASE_URL` n'ont pas de valeur par defaut : l'application refuse de demarrer sans elles. +`DATABASE_URL` pointe sur `localhost:5433`, le port publie par le service `db` du +`docker-compose.yml` racine. Demarrer la base depuis la racine avec `make db-up`. + ## Commandes Depuis la racine du monorepo, via le `Makefile` : `make install`, `make dev`, `make lint`, @@ -35,8 +38,13 @@ uv run ruff check . # lint uv run ruff format . # format uv run mypy app # typage strict uv run pytest # tests + couverture +uv run pytest -m integration # tests exigeant une base joignable ``` +`pytest` ecarte par defaut les tests marques `integration`, pour que `make check` reste +jouable sans Docker. Ces tests visent la base `enervision_test`, creee par +`db/init/110-test-database.sql` au premier demarrage du conteneur. + L'application est exposee par une factory (`create_app`) et non par un objet module : aucune configuration n'est lue a l'import, ce qui rend les tests et les migrations independants de l'environnement. @@ -73,7 +81,7 @@ Le sens de dependance est unique : `endpoints` vers `services` vers `repositorie | Route | Role | |------------------------|-------------------------------------------------| | `/api/v1/health/live` | Sonde de vivacite, aucune dependance externe | -| `/api/v1/health/ready` | Sonde de disponibilite, verifie la base | +| `/api/v1/health/ready` | Sonde de disponibilite, verifie la base et TimescaleDB | | `/metrics` | Metriques au format Prometheus | | `/docs`, `/openapi.json` | Documentation, desactivee quand `APP_ENV=prod` | @@ -86,6 +94,10 @@ uv run alembic upgrade head L'URL de connexion vient de `DATABASE_URL`, pas de `alembic.ini`. +La premiere revision ne cree aucune table : elle refuse de s'appliquer si l'extension +TimescaleDB manque, ce qui arrive quand `db/init` n'a pas ete joue. Le DDL propre a +TimescaleDB qui ne depend pas du schema applicatif vit dans `db/`, pas ici. + ## Image Docker Build multi-stage, dependances resolues par uv depuis `uv.lock`, execution sous un diff --git a/apps/backend/alembic/versions/5353c0e4f094_socle_garde_extension_timescaledb.py b/apps/backend/alembic/versions/5353c0e4f094_socle_garde_extension_timescaledb.py new file mode 100644 index 0000000..de14bd3 --- /dev/null +++ b/apps/backend/alembic/versions/5353c0e4f094_socle_garde_extension_timescaledb.py @@ -0,0 +1,37 @@ +"""socle garde extension timescaledb + +Revision ID: 5353c0e4f094 +Revises: +Create Date: 2026-09-14 14:17:17.556764 + +Premiere revision du schema applicatif. Elle ne cree aucune table : elle etablit +alembic_version et refuse de s'appliquer sur une base ou l'extension TimescaleDB +manque, cas qui se produit quand db/init n'a pas ete joue. +""" + +from collections.abc import Sequence + +from alembic import op + +revision: str = "5353c0e4f094" +down_revision: str | Sequence[str] | None = None +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + +GARDE_EXTENSION = """ +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'timescaledb') THEN + RAISE EXCEPTION 'extension timescaledb absente, voir db/init et db/README.md'; + END IF; +END +$$; +""" + + +def upgrade() -> None: + op.execute(GARDE_EXTENSION) + + +def downgrade() -> None: + pass diff --git a/apps/backend/app/api/v1/endpoints/health.py b/apps/backend/app/api/v1/endpoints/health.py index 711bf89..be3abf8 100644 --- a/apps/backend/app/api/v1/endpoints/health.py +++ b/apps/backend/app/api/v1/endpoints/health.py @@ -9,6 +9,8 @@ from app.schemas.health import LivenessStatus, ReadinessStatus logger = get_logger(__name__) router = APIRouter(tags=["health"]) +TIMESCALEDB_VERSION = text("SELECT extversion FROM pg_extension WHERE extname = 'timescaledb'") + @router.get("/live", summary="Sonde de vivacite") async def liveness(settings: SettingsDep) -> LivenessStatus: @@ -23,11 +25,19 @@ async def liveness(settings: SettingsDep) -> LivenessStatus: @router.get("/ready", summary="Sonde de disponibilite") async def readiness(session: SessionDep) -> ReadinessStatus: try: - await session.execute(text("SELECT 1")) - except (SQLAlchemyError, OSError): + version: str | None = await session.scalar(TIMESCALEDB_VERSION) + except SQLAlchemyError, OSError: logger.exception("Base de donnees injoignable") raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Base de donnees injoignable", ) from None - return ReadinessStatus(status="ready", database="reachable") + + if version is None: + logger.error("Extension TimescaleDB absente de la base") + raise HTTPException( + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Extension TimescaleDB absente", + ) + + return ReadinessStatus(status="ready", database="reachable", timescaledb=version) diff --git a/apps/backend/app/schemas/health.py b/apps/backend/app/schemas/health.py index d1eb845..e4ec86e 100644 --- a/apps/backend/app/schemas/health.py +++ b/apps/backend/app/schemas/health.py @@ -13,3 +13,4 @@ class LivenessStatus(BaseModel): class ReadinessStatus(BaseModel): status: Literal["ready"] database: Literal["reachable"] + timescaledb: str diff --git a/apps/backend/pyproject.toml b/apps/backend/pyproject.toml index 87c916a..fee4044 100644 --- a/apps/backend/pyproject.toml +++ b/apps/backend/pyproject.toml @@ -79,7 +79,8 @@ disallow_untyped_defs = false [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" -addopts = "-q --strict-markers --cov=app --cov-report=term-missing" +addopts = "-q --strict-markers -m 'not integration' --cov=app --cov-report=term-missing" +markers = ["integration: requiert une base PostgreSQL joignable, hors `make test`"] [tool.coverage.run] source = ["app"] diff --git a/apps/backend/tests/api/test_health.py b/apps/backend/tests/api/test_health.py index 6a2f938..f0d647f 100644 --- a/apps/backend/tests/api/test_health.py +++ b/apps/backend/tests/api/test_health.py @@ -20,6 +20,44 @@ async def test_liveness_exposes_service_metadata(client: AsyncClient) -> None: } +async def test_readiness_reports_the_timescaledb_version(app: FastAPI, client: AsyncClient) -> None: + class ReadySession: + async def scalar(self, *_: object, **__: object) -> str: + return "2.22.1" + + async def override() -> AsyncIterator[ReadySession]: + yield ReadySession() + + app.dependency_overrides[get_session] = override + + response = await client.get("/api/v1/health/ready") + + assert response.status_code == 200 + assert response.json() == { + "status": "ready", + "database": "reachable", + "timescaledb": "2.22.1", + } + + +async def test_readiness_returns_503_when_the_extension_is_missing( + app: FastAPI, client: AsyncClient +) -> None: + class SessionWithoutExtension: + async def scalar(self, *_: object, **__: object) -> None: + return None + + async def override() -> AsyncIterator[SessionWithoutExtension]: + yield SessionWithoutExtension() + + app.dependency_overrides[get_session] = override + + response = await client.get("/api/v1/health/ready") + + assert response.status_code == 503 + assert response.json()["detail"] == "Extension TimescaleDB absente" + + @pytest.mark.parametrize( "failure", [ @@ -32,7 +70,7 @@ async def test_readiness_returns_503_when_database_is_unreachable( app: FastAPI, client: AsyncClient, failure: Exception ) -> None: class UnreachableSession: - async def execute(self, *_: object, **__: object) -> None: + async def scalar(self, *_: object, **__: object) -> None: raise failure async def override() -> AsyncIterator[UnreachableSession]: @@ -49,3 +87,14 @@ async def test_readiness_returns_503_when_database_is_unreachable( @pytest.mark.parametrize("path", ["/openapi.json", "/metrics"]) async def test_technical_endpoints_are_served(client: AsyncClient, path: str) -> None: assert (await client.get(path)).status_code == 200 + + +@pytest.mark.integration +async def test_readiness_reaches_the_real_database(client: AsyncClient) -> None: + response = await client.get("/api/v1/health/ready") + + assert response.status_code == 200, response.text + body = response.json() + assert body["status"] == "ready" + assert body["database"] == "reachable" + assert body["timescaledb"] diff --git a/apps/backend/tests/conftest.py b/apps/backend/tests/conftest.py index d0d5873..ac0e89b 100644 --- a/apps/backend/tests/conftest.py +++ b/apps/backend/tests/conftest.py @@ -6,6 +6,7 @@ from fastapi import FastAPI from httpx import ASGITransport, AsyncClient from app.core.config import get_settings +from app.db.session import get_engine, get_session_factory from app.main import create_app @@ -13,13 +14,24 @@ from app.main import create_app def environment() -> Iterator[None]: os.environ.setdefault("APP_SECRET_KEY", "secret-de-test") os.environ.setdefault( - "DATABASE_URL", "postgresql+asyncpg://enervision:enervision@localhost:5432/enervision_test" + "DATABASE_URL", "postgresql+asyncpg://enervision:change_me@localhost:5433/enervision_test" ) get_settings.cache_clear() yield get_settings.cache_clear() +# Piege : get_engine est lru_cache et pytest-asyncio ouvre une boucle par test. Sans ce +# recyclage, le 2e test touchant vraiment la base heriterait d une boucle morte. +@pytest.fixture(autouse=True) +async def engine_per_test() -> AsyncIterator[None]: + yield + if get_engine.cache_info().currsize: + await get_engine().dispose() + get_engine.cache_clear() + get_session_factory.cache_clear() + + @pytest.fixture def app() -> FastAPI: return create_app() From f4d05a8ca9c473923ecdcf3e8a6b8d471d8093a8 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Mon, 14 Sep 2026 14:19:25 +0200 Subject: [PATCH 04/18] docs: ADR du choix PostgreSQL TimescaleDB Acte le choix de l'extension plutot qu'un second SGBD, celui de l'image -ha et celui de PG17. Fixe surtout la frontiere db/init contre db/migrations contre apps/backend/alembic, qui n'est deductible d'aucun fichier. --- README.md | 26 ++++++++-- docs/adr/.gitkeep | 0 docs/adr/0001-postgresql-timescaledb.md | 66 +++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) delete mode 100644 docs/adr/.gitkeep create mode 100644 docs/adr/0001-postgresql-timescaledb.md diff --git a/README.md b/README.md index b6ab9c8..12342ac 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,14 @@ series temporelles energetiques, deployee sur une machine on-premise. |------------|-------------------------------------|---------------------|---------------| | Backend | FastAPI, Python 3.14 | `apps/backend` | Initialise | | Frontend | Angular, Node 24 LTS | `apps/frontend` | A initialiser | -| Base | PostgreSQL + TimescaleDB | `db` | A initialiser | +| Base | PostgreSQL 17 + TimescaleDB | `db` | Initialise | | ETL | Apache Airflow | `etl/airflow` | A initialiser | | Infra | Terraform | `infra/terraform` | A initialiser | | CI/CD | GitHub Actions | `.github/workflows` | A initialiser | | Monitoring | Prometheus, Grafana, Alertmanager | `monitoring` | A initialiser | -Seul le backend est initialise a ce stade. Les autres dossiers portent l'arborescence et -un README de cadrage, leur contenu fait l'objet d'un ticket dedie. +Le backend et la base sont initialises a ce stade. Les autres dossiers portent +l'arborescence et un README de cadrage, leur contenu fait l'objet d'un ticket dedie. ## Arborescence @@ -50,13 +50,33 @@ un README de cadrage, leur contenu fait l'objet d'un ticket dedie. Prerequis : uv, Docker. Le poste doit disposer de Python 3.14, que `uv` installe seul. ```bash +cp .env.example .env # variables de docker-compose +cp apps/backend/.env.example apps/backend/.env # variables du backend hors conteneur + +make db-up # PostgreSQL + TimescaleDB, publie sur le port 5433 make install # dependances du backend +make migrate # applique les migrations Alembic make dev # API sur http://localhost:8000, docs sur /docs make check # lint + typage + tests ``` `make help` liste les cibles disponibles. +Deux fichiers d'environnement, deux usages : `.env` a la racine alimente `docker-compose.yml`, +`apps/backend/.env` alimente le backend lance sur le poste. Le port 5433 est publie plutot que +5432, souvent deja pris par une autre base. + +La boucle de developpement est `make db-up` puis `make dev` : seule la base tourne en +conteneur. Le service `backend` du `docker-compose.yml` sert la stack complete et la recette, +et n'embarque pas le source, donc toute modification y demande un +`docker compose up -d --build backend`. + +Verifier que la base repond et que l'extension est chargee : + +```bash +curl -s localhost:8000/api/v1/health/ready +``` + ## Conventions - Branches : `feat/`, `fix/`, `chore/`, `docs/` suivi d'un libelle court. diff --git a/docs/adr/.gitkeep b/docs/adr/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/adr/0001-postgresql-timescaledb.md b/docs/adr/0001-postgresql-timescaledb.md new file mode 100644 index 0000000..4ed562b --- /dev/null +++ b/docs/adr/0001-postgresql-timescaledb.md @@ -0,0 +1,66 @@ +# 0001 - PostgreSQL avec l'extension TimescaleDB + +- Statut : accepte +- Date : 2026-09-14 + +## Contexte + +EnerVision collecte, stocke et restitue des series temporelles energetiques sur une machine +on-premise. La charge est dominee par des insertions horodatees en flux et par des lectures +agregees sur des fenetres de temps. Airflow produira des agregations continues, Grafana lira +les memes donnees, et l'API FastAPI les exposera. + +Un SGBD relationnel generaliste sait faire, mais degrade a mesure que la table de mesures +grossit : les index se fragmentent, les balayages de fenetre deviennent couteux, et il faut +ecrire a la main le partitionnement, la retention et les agregats pre-calcules. + +## Decision + +PostgreSQL 17 avec l'extension TimescaleDB, servie en local par l'image +`timescale/timescaledb-ha:pg17`. + +PostgreSQL reste une base relationnelle standard : un seul SGBD pour les donnees metier et +les mesures, un seul dialecte SQL, un seul pilote (`asyncpg`), et l'outillage habituel. +TimescaleDB ajoute le partitionnement automatique, les agregations continues et les +politiques de retention sans changer de moteur. + +L'image `-ha` plutot que l'image alpine : elle embarque `timescaledb_toolkit`, `postgis` et +`pgvector`. Le toolkit porte les fonctions de comblement de trous et d'analyse de series dont +l'ETL aura besoin, et changer d'image plus tard imposerait une reinitialisation du volume. + +PG17 plutot que PG18 : c'est la version la mieux couverte par Airflow et Grafana a ce jour. + +## Frontiere entre `db/` et `apps/backend/alembic/` + +C'est la regle que ce document existe surtout pour fixer. + +- `db/init/` : bootstrap joue **une seule fois**, a la premiere initialisation du conteneur. + Extensions, bases annexes. Ne rejoue jamais sur un volume existant. +- `db/migrations/` : SQL versionne qui ne decoule pas du schema applicatif, typiquement les + politiques de retention et de compression TimescaleDB. +- `apps/backend/alembic/` : le schema expose par l'API, et lui seul. C'est `Base.metadata` + qui fait foi. + +Une hypertable relevera des deux : Alembic cree la table, et le `create_hypertable()` vit +dans la meme revision Alembic, parce que separer les deux rendrait le schema irreproductible +depuis un seul `alembic upgrade head`. + +## Consequences + +- Le projet se lie a une extension, donc a un hebergement qui l'autorise. C'est acquis + puisque le deploiement est on-premise. +- `CREATE EXTENSION` demande le superutilisateur : cela reste un acte de bootstrap, pas une + migration applicative. +- Un bootstrap saute ne se voit pas au demarrage de l'API. Deux gardes couvrent ce cas : + `/api/v1/health/ready` repond 503 si l'extension est absente, et la premiere revision + Alembic refuse de s'appliquer. +- L'image `-ha` pese environ 1 Go, a telecharger une fois par poste. + +## Alternatives ecartees + +- **PostgreSQL nu, partitionnement manuel** : faisable, mais il faudrait reecrire ce que + TimescaleDB fournit, et le maintenir. +- **InfluxDB** : tres bon sur la serie temporelle, mais imposerait un second SGBD pour le + relationnel, donc deux dialectes, deux sauvegardes et des jointures applicatives. +- **ClickHouse** : taille pour un volume analytique que le projet n'atteindra pas, et moins + a l'aise sur les ecritures unitaires frequentes du flux d'ingestion. From 6bc2c3793f47a6c7f45d576432ccc1981e54779f Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Mon, 14 Sep 2026 14:28:49 +0200 Subject: [PATCH 05/18] fix(db): monte db/init fichier par fichier et coupe la telemetrie Monter le dossier ./db/init sur /docker-entrypoint-initdb.d remplacait le dossier de l'image au lieu de s'y ajouter. Les trois scripts d'init livres par timescaledb-ha disparaissaient sans aucun message : creation de l'extension dans template1, reglage par timescaledb-tune, et installation de timescaledb_toolkit. Verifie au demarrage : le dossier ne contenait que nos deux fichiers, et timescaledb_toolkit etait absent des bases. Monter chaque fichier separement retablit l'ordre attendu, verifie dans les journaux : 000, 001, 010, puis 100 et 110. TIMESCALEDB_TELEMETRY passe a off par defaut : l'image envoie sinon des statistiques d'usage a Timescale, ce qui ne va pas pour un deploiement on-premise. --- .env.example | 2 ++ db/README.md | 35 ++++++++++++++++++++--------------- docker-compose.yml | 6 +++++- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index 4d97678..a5fba5a 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,8 @@ POSTGRES_PASSWORD=change_me POSTGRES_DB=enervision # 5432 est souvent deja pris par une autre base du poste. POSTGRES_PORT=5433 +# `basic` renvoie des statistiques d'usage a Timescale. +TIMESCALEDB_TELEMETRY=off APP_ENV=local APP_DEBUG=true diff --git a/db/README.md b/db/README.md index c21a87d..fd62d6f 100644 --- a/db/README.md +++ b/db/README.md @@ -12,23 +12,28 @@ Les migrations du schema applicatif expose par l'API vivent dans ## `init` ne rejoue jamais -Le dossier est monte sur `/docker-entrypoint-initdb.d`, dont PostgreSQL ne joue le +Ces scripts sont montes sur `/docker-entrypoint-initdb.d`, dont PostgreSQL ne joue le contenu qu'a la toute premiere initialisation, quand `PGDATA` est vide. Modifier ou -ajouter un script ensuite reste sans effet sur une base existante : +ajouter un script ensuite reste sans effet sur une base existante : il faut detruire +le volume, ce que fait `make db-reset`. -```bash -docker compose down -v && docker compose up -d db -``` +L'image apporte ses propres scripts dans ce dossier, et ils comptent : -L'image joue d'abord ses propres scripts (`000_`, `001_`, `010_`), dont un -`CREATE EXTENSION IF NOT EXISTS timescaledb_toolkit CASCADE` qui installe `timescaledb` -au passage dans `postgres`, `template1` et la base applicative. Nos fichiers sont -numerotes a partir de `100` pour passer apres, quelle que soit la locale de tri. +| Script | Origine | Role | +|---|---|---| +| `000_install_timescaledb.sh` | image | Cree l'extension dans `postgres`, `template1` et la base applicative, et fixe `timescaledb.telemetry_level`. | +| `001_timescaledb_tune.sh` | image | Lance `timescaledb-tune` sur la memoire et les CPU vus par le conteneur. | +| `010_install_timescaledb_toolkit.sh` | image | Ajoute `timescaledb_toolkit`. | +| `100-extensions.sql` | ce depot | Declare explicitement les extensions attendues. | +| `110-test-database.sql` | ce depot | Cree `enervision_test`, attendue par la suite de tests du backend. | -| Script | Role | -|---|---| -| `100-extensions.sql` | Declare explicitement les extensions attendues. | -| `110-test-database.sql` | Cree `enervision_test`, attendue par la suite de tests du backend. | +D'ou deux contraintes dans `docker-compose.yml`. Nos fichiers sont **montes un par un**, +et non par leur dossier : un montage de `./db/init` sur `/docker-entrypoint-initdb.d` +remplacerait le dossier de l'image au lieu de s'y ajouter, et ferait disparaitre les trois +scripts ci-dessus sans le moindre message. Ajouter un fichier ici impose donc d'ajouter +une ligne la-bas. Et leur numerotation commence a `100` pour passer apres `010`, y compris +en locale C ou un prefixe a deux chiffres se trierait avant. -Comme un bootstrap peut toujours avoir ete saute, c'est `/api/v1/health/ready` qui fait -foi : la sonde refuse de repondre 200 si l'extension n'est pas chargee. +Comme un bootstrap peut toujours avoir ete saute, deux gardes le rattrapent : +`/api/v1/health/ready` repond 503 si l'extension n'est pas chargee, et la premiere +revision Alembic refuse de s'appliquer. diff --git a/docker-compose.yml b/docker-compose.yml index e6854d8..d8569c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,7 @@ # Piege : PGDATA de l'image timescaledb-ha vaut /home/postgres/pgdata/data, pas le chemin # habituel de l'image postgres. Monte ailleurs, le volume ne retient rien, sans erreur. +# Piege : db/init est monte fichier par fichier. Monter le dossier masquerait les scripts +# d'init de l'image, dont timescaledb-tune. Ajouter un fichier impose une ligne ici. name: enervision @@ -10,11 +12,13 @@ services: POSTGRES_USER: ${POSTGRES_USER:?} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?} POSTGRES_DB: ${POSTGRES_DB:?} + TIMESCALEDB_TELEMETRY: ${TIMESCALEDB_TELEMETRY:-off} ports: - "${POSTGRES_PORT:-5433}:5432" volumes: - pgdata:/home/postgres/pgdata/data - - ./db/init:/docker-entrypoint-initdb.d:ro + - ./db/init/100-extensions.sql:/docker-entrypoint-initdb.d/100-extensions.sql:ro + - ./db/init/110-test-database.sql:/docker-entrypoint-initdb.d/110-test-database.sql:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 10s From 34890b2b04aa77c267ff5d5e54215a239363b2a4 Mon Sep 17 00:00:00 2001 From: valentin Date: Mon, 14 Sep 2026 16:41:45 +0200 Subject: [PATCH 06/18] Outillage tests unitaires frontend : couverture Vitest, scripts npm, conventions TESTING.md. --- apps/frontend/TESTING.md | 81 +++++++++++ apps/frontend/angular.json | 19 ++- apps/frontend/package-lock.json | 201 +++++++++++++++++++++++++++ apps/frontend/package.json | 4 +- apps/frontend/test-results/junit.xml | 9 ++ 5 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 apps/frontend/TESTING.md create mode 100644 apps/frontend/test-results/junit.xml diff --git a/apps/frontend/TESTING.md b/apps/frontend/TESTING.md new file mode 100644 index 0000000..e23ed7a --- /dev/null +++ b/apps/frontend/TESTING.md @@ -0,0 +1,81 @@ +# Conventions de tests unitaires — Frontend + +## Outil +Vitest (intégré nativement à Angular CLI, pas d'installation à faire). + +## Où écrire les tests +Un fichier `*.spec.ts` à côté de chaque fichier testé (convention Angular CLI +par défaut, respectée automatiquement par `ng generate`). + +## Structure attendue (Arrange / Act / Assert) +```typescript +it('devrait faire X quand Y', () => { + // Arrange : préparer les données et les mocks + const input = { valeur: 42 }; + + // Act : exécuter le code testé + const result = service.doSomething(input); + + // Assert : vérifier le résultat + expect(result).toBe(true); +}); +``` + +## Ce qui doit être testé en priorité +- Services (`core/services/`) : logique métier, gestion des erreurs +- Guards et interceptors (`core/guards/`, `core/interceptors/`) : chaque branche de décision +- Composants avec logique (formulaires, conditions d'affichage) — pas nécessaire pour + un composant 100% template, sans logique + +## Gabarit — tester un service avec appel HTTP +```typescript +import { TestBed } from '@angular/core/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing'; +import { MonService } from './mon.service'; + +describe('MonService', () => { + let service: MonService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [MonService, provideHttpClient(), provideHttpClientTesting()], + }); + service = TestBed.inject(MonService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => httpMock.verify()); + + it('devrait récupérer les données', () => { + service.getData().subscribe(); + const req = httpMock.expectOne('/api/v1/...'); + expect(req.request.method).toBe('GET'); + req.flush({ /* réponse simulée */ }); + }); +}); +``` + +## Gabarit — tester un composant standalone +```typescript +import { TestBed } from '@angular/core/testing'; +import { MonComposant } from './mon-composant'; + +describe('MonComposant', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MonComposant], + }).compileComponents(); + }); + + it('devrait se créer', () => { + const fixture = TestBed.createComponent(MonComposant); + expect(fixture.componentInstance).toBeTruthy(); + }); +}); +``` + +## Lancer les tests +- Développement (mode watch) : `npm test` +- Rapport de couverture (CI) : `npm run test:ci -- --coverage`, puis ouvrir `coverage/index.html` diff --git a/apps/frontend/angular.json b/apps/frontend/angular.json index 3140772..ddf87a3 100644 --- a/apps/frontend/angular.json +++ b/apps/frontend/angular.json @@ -77,7 +77,24 @@ "defaultConfiguration": "development" }, "test": { - "builder": "@angular/build:unit-test" + "builder": "@angular/build:unit-test", + "options": { + "coverage": true, + "coverageReporters": [ + "text-summary", + "lcov", + "html" + ], + "reporters": [ + "default", + [ + "junit", + { + "outputFile": "test-results/junit.xml" + } + ] + ] + } } } } diff --git a/apps/frontend/package-lock.json b/apps/frontend/package-lock.json index 8f4408b..5ba6595 100644 --- a/apps/frontend/package-lock.json +++ b/apps/frontend/package-lock.json @@ -21,6 +21,7 @@ "@angular/build": "^22.1.8", "@angular/cli": "^22.1.8", "@angular/compiler-cli": "^22.1.0", + "@vitest/coverage-v8": "^4.1.11", "jsdom": "^28.0.0", "prettier": "^3.8.1", "typescript": "~6.0.2", @@ -733,6 +734,16 @@ "node": "^22.18.0 || >=24.11.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@bramus/specificity": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", @@ -3692,6 +3703,37 @@ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz", + "integrity": "sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.11", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.11", + "vitest": "4.1.11" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, "node_modules/@vitest/expect": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", @@ -3943,6 +3985,18 @@ "node": ">=12" } }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.6.tgz", + "integrity": "sha512-fvpl29helSO2w/z7utIbrkNXILdrLwDwAMH2I/zPKlGf5244+gf+B4cyS1sANcrPY2h+hWCGSgC8N61s/+AF9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.11.23", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.23.tgz", @@ -5077,6 +5131,16 @@ "dev": true, "license": "ISC" }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -5139,6 +5203,13 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/htmlparser2": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", @@ -5382,6 +5453,45 @@ "dev": true, "license": "ISC" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jose": { "version": "6.2.12", "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", @@ -5886,6 +5996,84 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", + "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, + "node_modules/magicast/node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/magicast/node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/magicast/node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/magicast/node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -7088,6 +7276,19 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 7369db4..552e346 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -6,7 +6,8 @@ "start": "ng serve", "build": "ng build", "watch": "ng build --watch --configuration development", - "test": "ng test" + "test": "ng test", + "test:ci": "ng test --watch=false" }, "private": true, "packageManager": "npm@11.19.0", @@ -24,6 +25,7 @@ "@angular/build": "^22.1.8", "@angular/cli": "^22.1.8", "@angular/compiler-cli": "^22.1.0", + "@vitest/coverage-v8": "^4.1.11", "jsdom": "^28.0.0", "prettier": "^3.8.1", "typescript": "~6.0.2", diff --git a/apps/frontend/test-results/junit.xml b/apps/frontend/test-results/junit.xml new file mode 100644 index 0000000..28e5ba4 --- /dev/null +++ b/apps/frontend/test-results/junit.xml @@ -0,0 +1,9 @@ + + + + + + + + + From 98ec01c847393da2049bd0fcaf52ff267c56b46d Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 15 Sep 2026 09:54:15 +0200 Subject: [PATCH 07/18] test(backend): rend la configuration de test independante du poste APP_ENV, APP_DEBUG, APP_LOG_LEVEL et APP_CORS_ORIGINS n'etaient poses nulle part : le .env du developpeur les decidait, alors que les tests assertent en dur l'environnement et que create_app coupe /openapi.json hors developpement. Un poste portant APP_ENV=prod faisait tomber deux tests. Fixe aussi asyncio_default_fixture_loop_scope, que pytest-asyncio 1.4 reclame. --- apps/backend/pyproject.toml | 1 + apps/backend/tests/conftest.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/backend/pyproject.toml b/apps/backend/pyproject.toml index fee4044..be64aba 100644 --- a/apps/backend/pyproject.toml +++ b/apps/backend/pyproject.toml @@ -79,6 +79,7 @@ disallow_untyped_defs = false [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" addopts = "-q --strict-markers -m 'not integration' --cov=app --cov-report=term-missing" markers = ["integration: requiert une base PostgreSQL joignable, hors `make test`"] diff --git a/apps/backend/tests/conftest.py b/apps/backend/tests/conftest.py index ac0e89b..e855f1b 100644 --- a/apps/backend/tests/conftest.py +++ b/apps/backend/tests/conftest.py @@ -10,9 +10,19 @@ from app.db.session import get_engine, get_session_factory from app.main import create_app +# Piege : les variables d'environnement priment sur apps/backend/.env. Celles qu'on ne +# pose pas ici, c'est le .env du poste qui les decide, et les assertions avec. @pytest.fixture(autouse=True, scope="session") def environment() -> Iterator[None]: - os.environ.setdefault("APP_SECRET_KEY", "secret-de-test") + os.environ.update( + { + "APP_ENV": "local", + "APP_DEBUG": "false", + "APP_LOG_LEVEL": "WARNING", + "APP_CORS_ORIGINS": "", + "APP_SECRET_KEY": "secret-de-test", + } + ) os.environ.setdefault( "DATABASE_URL", "postgresql+asyncpg://enervision:change_me@localhost:5433/enervision_test" ) From 3ca1866e93ba179bd3bf02140bd078393c0e5450 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 15 Sep 2026 09:55:38 +0200 Subject: [PATCH 08/18] test(backend): factorise les doubles de session Chaque test reecrivait sa classe de session et sa fonction d'override, soit trois fois le meme decor pour un seul endpoint. FakeSession et la fixture fake_session portent ce decor, make_settings fabrique une Settings dont les valeurs priment sur l'environnement. --- apps/backend/tests/api/test_health.py | 40 ++++++--------------------- apps/backend/tests/conftest.py | 16 +++++++++-- apps/backend/tests/factories.py | 37 +++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 33 deletions(-) create mode 100644 apps/backend/tests/factories.py diff --git a/apps/backend/tests/api/test_health.py b/apps/backend/tests/api/test_health.py index f0d647f..a7a61b6 100644 --- a/apps/backend/tests/api/test_health.py +++ b/apps/backend/tests/api/test_health.py @@ -1,12 +1,9 @@ -from collections.abc import AsyncIterator +from collections.abc import Callable import pytest -from fastapi import FastAPI from httpx import AsyncClient from sqlalchemy.exc import OperationalError -from app.db.session import get_session - async def test_liveness_exposes_service_metadata(client: AsyncClient) -> None: response = await client.get("/api/v1/health/live") @@ -20,15 +17,10 @@ async def test_liveness_exposes_service_metadata(client: AsyncClient) -> None: } -async def test_readiness_reports_the_timescaledb_version(app: FastAPI, client: AsyncClient) -> None: - class ReadySession: - async def scalar(self, *_: object, **__: object) -> str: - return "2.22.1" - - async def override() -> AsyncIterator[ReadySession]: - yield ReadySession() - - app.dependency_overrides[get_session] = override +async def test_readiness_reports_the_timescaledb_version( + fake_session: Callable[..., None], client: AsyncClient +) -> None: + fake_session(result="2.22.1") response = await client.get("/api/v1/health/ready") @@ -41,16 +33,9 @@ async def test_readiness_reports_the_timescaledb_version(app: FastAPI, client: A async def test_readiness_returns_503_when_the_extension_is_missing( - app: FastAPI, client: AsyncClient + fake_session: Callable[..., None], client: AsyncClient ) -> None: - class SessionWithoutExtension: - async def scalar(self, *_: object, **__: object) -> None: - return None - - async def override() -> AsyncIterator[SessionWithoutExtension]: - yield SessionWithoutExtension() - - app.dependency_overrides[get_session] = override + fake_session(result=None) response = await client.get("/api/v1/health/ready") @@ -67,16 +52,9 @@ async def test_readiness_returns_503_when_the_extension_is_missing( ids=["erreur_sqlalchemy", "erreur_reseau_asyncpg"], ) async def test_readiness_returns_503_when_database_is_unreachable( - app: FastAPI, client: AsyncClient, failure: Exception + fake_session: Callable[..., None], client: AsyncClient, failure: Exception ) -> None: - class UnreachableSession: - async def scalar(self, *_: object, **__: object) -> None: - raise failure - - async def override() -> AsyncIterator[UnreachableSession]: - yield UnreachableSession() - - app.dependency_overrides[get_session] = override + fake_session(failure=failure) response = await client.get("/api/v1/health/ready") diff --git a/apps/backend/tests/conftest.py b/apps/backend/tests/conftest.py index e855f1b..950fc69 100644 --- a/apps/backend/tests/conftest.py +++ b/apps/backend/tests/conftest.py @@ -1,13 +1,14 @@ import os -from collections.abc import AsyncIterator, Iterator +from collections.abc import AsyncIterator, Callable, Iterator import pytest from fastapi import FastAPI from httpx import ASGITransport, AsyncClient from app.core.config import get_settings -from app.db.session import get_engine, get_session_factory +from app.db.session import get_engine, get_session, get_session_factory from app.main import create_app +from tests.factories import FakeSession # Piege : les variables d'environnement priment sur apps/backend/.env. Celles qu'on ne @@ -52,3 +53,14 @@ async def client(app: FastAPI) -> AsyncIterator[AsyncClient]: transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as async_client: yield async_client + + +@pytest.fixture +def fake_session(app: FastAPI) -> Callable[..., None]: + def install(result: object = None, failure: Exception | None = None) -> None: + async def override() -> AsyncIterator[FakeSession]: + yield FakeSession(result=result, failure=failure) + + app.dependency_overrides[get_session] = override + + return install diff --git a/apps/backend/tests/factories.py b/apps/backend/tests/factories.py new file mode 100644 index 0000000..05ba3fc --- /dev/null +++ b/apps/backend/tests/factories.py @@ -0,0 +1,37 @@ +from typing import Any + +from app.core.config import Settings + +SETTINGS_DE_TEST: dict[str, Any] = { + "env": "local", + "debug": False, + "log_level": "WARNING", + "cors_origins": "", + "secret_key": "secret-de-test", + "database_url": "postgresql+asyncpg://enervision:change_me@localhost:5433/enervision_test", +} + + +class FakeSession: + """Session factice : renvoie `result`, ou leve `failure` si elle est fournie.""" + + def __init__(self, result: object = None, failure: Exception | None = None) -> None: + self._result = result + self._failure = failure + + async def scalar(self, *_: object, **__: object) -> object: + return self._repondre() + + async def execute(self, *_: object, **__: object) -> object: + return self._repondre() + + def _repondre(self) -> object: + if self._failure is not None: + raise self._failure + return self._result + + +# Piege : les arguments nommes priment sur l'environnement et sur .env, contrairement +# aux variables posees par la fixture `environment`, qui restent surchargeables. +def make_settings(**overrides: Any) -> Settings: + return Settings(**{**SETTINGS_DE_TEST, **overrides}) From 3db4419bdf57d86c7a34e510f5e8d9b06bff1ba2 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 15 Sep 2026 09:58:30 +0200 Subject: [PATCH 09/18] test(backend): calque l'arborescence des tests sur celle de app Le README annonce deja tests/ comme miroir de app/, mais seul tests/api existait. Les paquets core, db, services et repositories attendent le metier a venir, pour que personne n'ait a choisir ou poser son premier test. --- apps/backend/tests/core/__init__.py | 0 apps/backend/tests/db/__init__.py | 0 apps/backend/tests/repositories/__init__.py | 0 apps/backend/tests/services/__init__.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/backend/tests/core/__init__.py create mode 100644 apps/backend/tests/db/__init__.py create mode 100644 apps/backend/tests/repositories/__init__.py create mode 100644 apps/backend/tests/services/__init__.py diff --git a/apps/backend/tests/core/__init__.py b/apps/backend/tests/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/backend/tests/db/__init__.py b/apps/backend/tests/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/backend/tests/repositories/__init__.py b/apps/backend/tests/repositories/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/backend/tests/services/__init__.py b/apps/backend/tests/services/__init__.py new file mode 100644 index 0000000..e69de29 From c95f4d38515c8215074a69d01ace57347e95d55b Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 15 Sep 2026 09:58:57 +0200 Subject: [PATCH 10/18] test(backend): mesure les branches et fixe un seuil de couverture app/main.py sort du omit : la fixture app l'exerce a chaque test, et l'exclure masquait ses seules conditions, les docs coupees hors developpement et le CORS monte selon les origines declarees. Il ressort a 78 %, le lifespan n'etant pas joue par ASGITransport. Seuil pose a 85 % pour 89 % mesures. --- apps/backend/pyproject.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/backend/pyproject.toml b/apps/backend/pyproject.toml index be64aba..87948f1 100644 --- a/apps/backend/pyproject.toml +++ b/apps/backend/pyproject.toml @@ -85,4 +85,9 @@ markers = ["integration: requiert une base PostgreSQL joignable, hors `make test [tool.coverage.run] source = ["app"] -omit = ["app/main.py", "alembic/*"] +branch = true +omit = ["alembic/*"] + +[tool.coverage.report] +show_missing = true +fail_under = 85 From d14b3afc8ea2095006b03503be0ada464f29c1e3 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 15 Sep 2026 09:59:17 +0200 Subject: [PATCH 11/18] chore: ajoute une cible de rapports de tests make test-cov produit la couverture HTML et XML et les resultats au format JUnit, sans alourdir make test qui reste la boucle de developpement. Les trois artefacts sont ignores, contrairement au junit.xml versione cote frontend. --- .gitignore | 1 + Makefile | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index af1ea90..d1e16df 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ venv/ .coverage coverage.xml htmlcov/ +test-results/ dist/ build/ *.egg-info/ diff --git a/Makefile b/Makefile index aa29df8..1426c5a 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ BACKEND := apps/backend .DEFAULT_GOAL := help -.PHONY: help install dev lint format typecheck test test-integration check docker-build \ - db-up db-down db-reset db-logs db-psql migrate +.PHONY: help install dev lint format typecheck test test-cov test-integration check \ + docker-build db-up db-down db-reset db-logs db-psql migrate help: ## Liste les cibles disponibles @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}' @@ -25,6 +25,10 @@ typecheck: ## Verifie le typage du backend test: ## Execute les tests backend ne demandant pas de base cd $(BACKEND) && uv run pytest +test-cov: ## Rapports de couverture HTML et XML, plus les resultats au format JUnit + cd $(BACKEND) && uv run pytest --cov-report=html --cov-report=xml \ + --junitxml=test-results/junit.xml + test-integration: ## Execute les tests exigeant une base joignable cd $(BACKEND) && uv run pytest -m integration From d58647cad4286e9b63ef61ae32a65310f749d03d Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 15 Sep 2026 10:00:42 +0200 Subject: [PATCH 12/18] test(backend): fournit une session reelle aux tests d'integration Les repositories a venir parlent du SQL : les eprouver sur un double ne prouve rien. La fixture ouvre une vraie connexion, d'ou le marqueur integration. --- apps/backend/tests/conftest.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/backend/tests/conftest.py b/apps/backend/tests/conftest.py index 950fc69..4560b75 100644 --- a/apps/backend/tests/conftest.py +++ b/apps/backend/tests/conftest.py @@ -4,6 +4,7 @@ from collections.abc import AsyncIterator, Callable, Iterator import pytest from fastapi import FastAPI from httpx import ASGITransport, AsyncClient +from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import get_settings from app.db.session import get_engine, get_session, get_session_factory @@ -64,3 +65,10 @@ def fake_session(app: FastAPI) -> Callable[..., None]: app.dependency_overrides[get_session] = override return install + + +# Contrainte : ouvre une vraie connexion, donc reservee aux tests `integration`. +@pytest.fixture +async def session() -> AsyncIterator[AsyncSession]: + async with get_session_factory()() as async_session: + yield async_session From 50dfa72c9dc75917aaf139e9d3e6773a51b4b447 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 15 Sep 2026 10:01:09 +0200 Subject: [PATCH 13/18] test(backend): n'applique le seuil de couverture qu'aux suites completes Dans [tool.coverage.report], fail_under vaut aussi pour une execution partielle : make test-integration echouait a 71 % alors que son test passait, et un fichier joue seul aurait echoue des que le code aurait grossi. Le seuil passe donc en --cov-fail-under sur les cibles qui jouent toute la suite. --- Makefile | 6 +++--- apps/backend/pyproject.toml | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 1426c5a..81c3e6d 100644 --- a/Makefile +++ b/Makefile @@ -23,11 +23,11 @@ typecheck: ## Verifie le typage du backend cd $(BACKEND) && uv run mypy app test: ## Execute les tests backend ne demandant pas de base - cd $(BACKEND) && uv run pytest + cd $(BACKEND) && uv run pytest --cov-fail-under=85 test-cov: ## Rapports de couverture HTML et XML, plus les resultats au format JUnit - cd $(BACKEND) && uv run pytest --cov-report=html --cov-report=xml \ - --junitxml=test-results/junit.xml + cd $(BACKEND) && uv run pytest --cov-fail-under=85 --cov-report=html \ + --cov-report=xml --junitxml=test-results/junit.xml test-integration: ## Execute les tests exigeant une base joignable cd $(BACKEND) && uv run pytest -m integration diff --git a/apps/backend/pyproject.toml b/apps/backend/pyproject.toml index 87948f1..1c27c08 100644 --- a/apps/backend/pyproject.toml +++ b/apps/backend/pyproject.toml @@ -90,4 +90,3 @@ omit = ["alembic/*"] [tool.coverage.report] show_missing = true -fail_under = 85 From 08ad3bbe3486ba916dcf99ea44e268832599e9ec Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 15 Sep 2026 10:01:21 +0200 Subject: [PATCH 14/18] docs(backend): consigne les conventions de tests unitaires Pendant de apps/frontend/TESTING.md : ou ecrire un test, comment le nommer, quoi tester selon la couche, les doubles par dependency_overrides, les marqueurs, et quatre gabarits copiables. --- apps/backend/TESTING.md | 136 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 apps/backend/TESTING.md diff --git a/apps/backend/TESTING.md b/apps/backend/TESTING.md new file mode 100644 index 0000000..5cc8912 --- /dev/null +++ b/apps/backend/TESTING.md @@ -0,0 +1,136 @@ +# Conventions de tests unitaires : Backend + +## Outil + +pytest, avec pytest-asyncio en mode `auto` : un `async def test_*` est collecte sans +decorateur. Les appels HTTP passent par httpx sur `ASGITransport`, qui parle a +l'application en memoire, sans serveur ni port ouvert. + +## Ou ecrire les tests + +`tests/` est le miroir de `app/` : un test de `app/services/consumption.py` va dans +`tests/services/test_consumption.py`. Les paquets `core`, `db`, `services` et +`repositories` existent deja, vides, pour cette raison. + +## Nommage + +- Fonctions en anglais : `test___when_`. +- `ids=` de `parametrize` en francais : `ids=["erreur_sqlalchemy", "erreur_reseau"]`. +- Pas de docstring : le nom porte l'intention. + +## Structure attendue (Arrange / Act / Assert) + +Une ligne vide separe les trois temps, sans commentaire pour les annoncer. + +```python +async def test_readiness_returns_503_when_the_extension_is_missing( + fake_session: Callable[..., None], client: AsyncClient +) -> None: + fake_session(result=None) + + response = await client.get("/api/v1/health/ready") + + assert response.status_code == 503 + assert response.json()["detail"] == "Extension TimescaleDB absente" +``` + +## Ce qui doit etre teste en priorite + +Le sens de dependance du backend est `endpoints -> services -> repositories -> models`. + +| Couche | Ce qu'on teste | +|---|---| +| `services/` | La logique metier, cas nominal et cas d'erreur. C'est la priorite. | +| `repositories/` | Chaque branche de decision, sous le marqueur `integration`. | +| `endpoints/` | Le code de statut et la forme de la reponse, pas la logique metier. | +| `schemas/` | Rien, sauf si le schema porte une validation ecrite a la main. | + +## Doubles + +On remplace une dependance FastAPI par `app.dependency_overrides`, jamais par +`unittest.mock`. `tests/factories.py` fournit le necessaire. + +- `fake_session(result=...)` : la session repond `result`. +- `fake_session(failure=...)` : la session leve l'exception. +- `make_settings(**overrides)` : fabrique une `Settings`, dont les valeurs priment sur + l'environnement et sur `.env`. C'est le moyen de tester `create_app` en `prod`. + +## Gabarit : un endpoint + +```python +from collections.abc import Callable + +from httpx import AsyncClient + + +async def test_endpoint_returns_the_expected_payload( + fake_session: Callable[..., None], client: AsyncClient +) -> None: + fake_session(result=42) + + response = await client.get("/api/v1/...") + + assert response.status_code == 200 + assert response.json() == {"valeur": 42} +``` + +## Gabarit : un service avec repository factice + +Un service ne connait que son repository : on lui en passe un faux, sans base ni session. + +```python +from app.services.consumption import ConsumptionService + + +class FakeRepository: + async def total_for(self, site_id: int) -> float: + return 12.5 + + +async def test_service_converts_the_total_to_kilowatt_hours() -> None: + service = ConsumptionService(FakeRepository()) + + total = await service.total_kwh(site_id=1) + + assert total == 12.5 +``` + +## Gabarit : un repository sur la vraie base + +Un repository parle du SQL : le tester sur un double ne prouve rien. Il porte donc le +marqueur `integration`, ecarte par defaut. + +```python +import pytest +from sqlalchemy.ext.asyncio import AsyncSession + + +@pytest.mark.integration +async def test_repository_reads_back_what_it_wrote(session: AsyncSession) -> None: + ... +``` + +## Marqueurs + +`integration` designe tout test exigeant une base joignable. `pytest` les ecarte par +defaut, ce qui garde `make check` jouable sans Docker. Tout autre marqueur doit etre +declare dans `pyproject.toml` : `--strict-markers` refuse les marqueurs inconnus. + +## Couverture + +Les branches sont mesurees, pas seulement les lignes. Le seuil de 85 % ne s'applique +qu'aux cibles qui jouent toute la suite, `make test` et `make test-cov` : un fichier +joue seul affiche sa couverture sans jamais echouer dessus. Le detail se lit dans +`htmlcov/index.html` apres `make test-cov`. + +## Lancer les tests + +```bash +make test # suite unitaire, sans base +make test-cov # idem, plus les rapports HTML, XML et JUnit +make db-up && make test-integration # tests exigeant une base, demande Docker +make check # lint + typage + suite unitaire + +uv run pytest tests/api/test_health.py # un seul fichier +uv run pytest -k readiness # par motif de nom +``` From 6aeaca8ed118b03a5adc898249609fa4fd6800b5 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 15 Sep 2026 10:01:34 +0200 Subject: [PATCH 15/18] docs: renvoie vers les conventions de tests Ajoute test/ a la liste des prefixes de branches, deja utilise par la branche d'outillage frontend, et remplace le corps a trous du gabarit de repository par un exemple complet, que ruff format acceptait mal. --- README.md | 2 +- apps/backend/README.md | 3 +++ apps/backend/TESTING.md | 9 ++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 12342ac..7e2e4ae 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,6 @@ curl -s localhost:8000/api/v1/health/ready ## Conventions -- Branches : `feat/`, `fix/`, `chore/`, `docs/` suivi d'un libelle court. +- Branches : `feat/`, `fix/`, `chore/`, `docs/`, `test/` suivi d'un libelle court. - Commits : Conventional Commits, portee = dossier de premier niveau concerne. - Toute decision structurante donne lieu a un ADR dans `docs/adr`. diff --git a/apps/backend/README.md b/apps/backend/README.md index d70b3ca..5d940b8 100644 --- a/apps/backend/README.md +++ b/apps/backend/README.md @@ -41,6 +41,9 @@ uv run pytest # tests + couverture uv run pytest -m integration # tests exigeant une base joignable ``` +Les conventions de tests, les gabarits et le detail des marqueurs sont dans +[`TESTING.md`](TESTING.md). + `pytest` ecarte par defaut les tests marques `integration`, pour que `make check` reste jouable sans Docker. Ces tests visent la base `enervision_test`, creee par `db/init/110-test-database.sql` au premier demarrage du conteneur. diff --git a/apps/backend/TESTING.md b/apps/backend/TESTING.md index 5cc8912..f794421 100644 --- a/apps/backend/TESTING.md +++ b/apps/backend/TESTING.md @@ -104,10 +104,17 @@ marqueur `integration`, ecarte par defaut. import pytest from sqlalchemy.ext.asyncio import AsyncSession +from app.models.site import Site +from app.repositories.site import SiteRepository + @pytest.mark.integration async def test_repository_reads_back_what_it_wrote(session: AsyncSession) -> None: - ... + repository = SiteRepository(session) + + await repository.add(Site(name="Toulouse")) + + assert await repository.by_name("Toulouse") is not None ``` ## Marqueurs From a2727f9b5a7f8ca8b3ade68da6808437e0c9e364 Mon Sep 17 00:00:00 2001 From: Dorian PESCE Date: Tue, 15 Sep 2026 10:13:41 +0200 Subject: [PATCH 16/18] chore/k8s-single-node-k3s via Terraform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se connecte à la machine on-premise via SSH et installe k3s single-node puis instancie le module. apply reste à faire une fois la machine prête. --- .gitignore | 3 + .terraform.lock.hcl | 23 - .../docker/3.9.0/windows_386/CHANGELOG.md | 870 ------------------ .../docker/3.9.0/windows_386/LICENSE | 373 -------- .../docker/3.9.0/windows_386/README.md | 117 --- infra/README.md | 18 +- infra/terraform/environments/dev/.gitkeep | 0 infra/terraform/environments/dev/main.tf | 11 + infra/terraform/environments/dev/outputs.tf | 9 + .../environments/dev/terraform.tfvars.example | 7 + infra/terraform/environments/dev/variables.tf | 40 + infra/terraform/environments/dev/versions.tf | 14 + infra/terraform/modules/.gitkeep | 0 infra/terraform/modules/k3s/main.tf | 45 + infra/terraform/modules/k3s/outputs.tf | 9 + infra/terraform/modules/k3s/variables.tf | 39 + infra/terraform/modules/k3s/versions.tf | 10 + terraform.tf | 19 - test.txt | 1 - 19 files changed, 204 insertions(+), 1404 deletions(-) delete mode 100644 .terraform.lock.hcl delete mode 100644 .terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/CHANGELOG.md delete mode 100644 .terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/LICENSE delete mode 100644 .terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/README.md delete mode 100644 infra/terraform/environments/dev/.gitkeep create mode 100644 infra/terraform/environments/dev/main.tf create mode 100644 infra/terraform/environments/dev/outputs.tf create mode 100644 infra/terraform/environments/dev/terraform.tfvars.example create mode 100644 infra/terraform/environments/dev/variables.tf create mode 100644 infra/terraform/environments/dev/versions.tf delete mode 100644 infra/terraform/modules/.gitkeep create mode 100644 infra/terraform/modules/k3s/main.tf create mode 100644 infra/terraform/modules/k3s/outputs.tf create mode 100644 infra/terraform/modules/k3s/variables.tf create mode 100644 infra/terraform/modules/k3s/versions.tf delete mode 100644 terraform.tf delete mode 100644 test.txt diff --git a/.gitignore b/.gitignore index af1ea90..a226853 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,9 @@ override.tf override.tf.json *_override.tf *_override.tf.json +*.tfvars +!*.tfvars.example +kubeconfig # Airflow etl/airflow/logs/ diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl deleted file mode 100644 index da14477..0000000 --- a/.terraform.lock.hcl +++ /dev/null @@ -1,23 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/kreuzwerker/docker" { - version = "3.9.0" - constraints = "~> 3.0" - hashes = [ - "h1:MmhVJBgNpE2Fbksv/XObZJncwm4th4mFE7Ai+6LiIy4=", - "zh:0ead8281830e9b9496651282235d9a139ba1b1b6ff79e395eb8c78658dc446b9", - "zh:0f17d37d8d3872df3fb75c68b5272e0c981343f53b506a9675b4405191edd3ef", - "zh:11d50b37323874427c6d2a08b737d3c7707c8301fdd236c94485cf2828d0b14b", - "zh:32f6f9b847446054e2db3d72886ef2f1d1aa51a6d0dac42340b07dad18e3f28f", - "zh:5ea5c67668b5dcbda560dc6104b788a9bfc974d52f02f7886889b77cc0e5d248", - "zh:5fb19a0b07edc344cd3ddeeb9cfb3d183089deb7a6a94a7b22a583aa1712596b", - "zh:602a7ece444e2a142ec5245abb98e7a1a990a68afae2df63b6c85ec084f0c5d7", - "zh:693dce278524ad8a6d6c9dd7a01bcd63bb85189639198f8d0b044ab0e5099401", - "zh:72e9911568103576c6a78fa38841cfd45eeb88ad22a2c649eb140a377a5b3c26", - "zh:956b62b6857cbb467b50158601f01b1203daa34cbd447dcc7f044c327e878b68", - "zh:9d372bac0d4479868b34485fb4966ba7bb525938f818b6a625f4977004ea83f9", - "zh:e06658a51427f9f53dbdb06263406fc1bc56d1a4fb5e7eb660d7cdfc22f596bd", - "zh:eee38dadf672b946419af25160eae7c03fc2afbb14f39f2f1d2a7404d647e2f7", - ] -} diff --git a/.terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/CHANGELOG.md b/.terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/CHANGELOG.md deleted file mode 100644 index 35b0c05..0000000 --- a/.terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/CHANGELOG.md +++ /dev/null @@ -1,870 +0,0 @@ - - -## [v3.9.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.8.0...v3.9.0) (2025-11-09) - -### Chore - -* Add file requested by hashicorp ([#813](https://github.com/kreuzwerker/terraform-provider-docker/issues/813)) -* Prepare release v3.8.0 ([#806](https://github.com/kreuzwerker/terraform-provider-docker/issues/806)) - -### Feat - -* Implement caching of docker provider ([#808](https://github.com/kreuzwerker/terraform-provider-docker/issues/808)) - -### Fix - -* test attribute of docker_service healthcheck is not required ([#815](https://github.com/kreuzwerker/terraform-provider-docker/issues/815)) -* docker_service label can be updated without recreate ([#814](https://github.com/kreuzwerker/terraform-provider-docker/issues/814)) - - - -## [v3.8.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.7.0...v3.8.0) (2025-10-08) - -### Feat - -* Add build attribute for docker_registry_image ([#805](https://github.com/kreuzwerker/terraform-provider-docker/issues/805)) -* Add build option for additional contexts ([#798](https://github.com/kreuzwerker/terraform-provider-docker/issues/798)) -* implement mac_address for networks_advanced ([#794](https://github.com/kreuzwerker/terraform-provider-docker/issues/794)) -* Implement docker cluster volume ([#793](https://github.com/kreuzwerker/terraform-provider-docker/issues/793)) - - - -## [v3.7.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.6.2...v3.7.0) (2025-08-19) - -### Chore - -* Prepare release v3.7.0 ([#774](https://github.com/kreuzwerker/terraform-provider-docker/issues/774)) - -### Feat - -* Implement memory_reservation and network_mode enhancements ([#773](https://github.com/kreuzwerker/terraform-provider-docker/issues/773)) -* Implement cache_from and cache_to for docker_image ([#772](https://github.com/kreuzwerker/terraform-provider-docker/issues/772)) - -### Fix - -* Correctly get and set nanoCPUs for docker_container ([#771](https://github.com/kreuzwerker/terraform-provider-docker/issues/771)) - - - -## [v3.6.2](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.6.1...v3.6.2) (2025-06-13) - -### Chore - -* Prepare release v3.6.2 ([#750](https://github.com/kreuzwerker/terraform-provider-docker/issues/750)) - -### Feat - -* Allow digest in image name ([#744](https://github.com/kreuzwerker/terraform-provider-docker/issues/744)) - -### Fix - -* Remove wrong buildkit version assignment ([#747](https://github.com/kreuzwerker/terraform-provider-docker/issues/747)) -* Reading non existant volume should recreate ([#749](https://github.com/kreuzwerker/terraform-provider-docker/issues/749)) -* Typo in cgroup_parent handling ([#746](https://github.com/kreuzwerker/terraform-provider-docker/issues/746)) - - - -## [v3.6.1](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.6.0...v3.6.1) (2025-06-05) - -### Chore - -* Prepare release v3.6.1 ([#743](https://github.com/kreuzwerker/terraform-provider-docker/issues/743)) - -### Feat - -* allow to set the cgroup parent for container ([#609](https://github.com/kreuzwerker/terraform-provider-docker/issues/609)) - - - -## [v3.6.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.5.0...v3.6.0) (2025-05-25) - -### Chore - -* Prepare release v3.6.0 ([#735](https://github.com/kreuzwerker/terraform-provider-docker/issues/735)) - -### Feat - -* Implement correct cpu scheduler settings ([#732](https://github.com/kreuzwerker/terraform-provider-docker/issues/732)) -* Add implementaion of capabilities in docker servic ([#727](https://github.com/kreuzwerker/terraform-provider-docker/issues/727)) -* implement Buildx builder resource ([#724](https://github.com/kreuzwerker/terraform-provider-docker/issues/724)) - -### Fix - -* Implement buildx fixes for general buildkit support and platform handling ([#734](https://github.com/kreuzwerker/terraform-provider-docker/issues/734)) -* Make endpoint validation less strict ([#733](https://github.com/kreuzwerker/terraform-provider-docker/issues/733)) - - - -## [v3.5.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.4.0...v3.5.0) (2025-05-06) - -### Chore - -* Prepare release v3.5.0 ([#721](https://github.com/kreuzwerker/terraform-provider-docker/issues/721)) - -### Feat - -* Implement using of buildx for docker_image ([#717](https://github.com/kreuzwerker/terraform-provider-docker/issues/717)) -* Support registries that return empty auth scope [#646](https://github.com/kreuzwerker/terraform-provider-docker/issues/646) -* Implement registry_image_manifests data source ([#714](https://github.com/kreuzwerker/terraform-provider-docker/issues/714)) -* Implement healthcheck start interval ([#713](https://github.com/kreuzwerker/terraform-provider-docker/issues/713)) - - - -## [v3.4.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.3.0...v3.4.0) (2025-04-25) - -### Chore - -* Prepare release v3.4.0 ([#712](https://github.com/kreuzwerker/terraform-provider-docker/issues/712)) - -### Feat - -* Implement volume_options subpath ([#710](https://github.com/kreuzwerker/terraform-provider-docker/issues/710)) - -### Fix - -* Prevent recreation of image name is intentionally set to a fixed value ([#711](https://github.com/kreuzwerker/terraform-provider-docker/issues/711)) -* Improve container wait handling ([#709](https://github.com/kreuzwerker/terraform-provider-docker/issues/709)) -* Use auth_config block also for registry_image delete functionality ([#708](https://github.com/kreuzwerker/terraform-provider-docker/issues/708)) - - - -## [v3.3.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.2.0...v3.3.0) (2025-04-19) - -### Chore - -* Prepare release v3.3.0 ([#705](https://github.com/kreuzwerker/terraform-provider-docker/issues/705)) -* Update terraform-plugin-sdk/v2 dependency ([#699](https://github.com/kreuzwerker/terraform-provider-docker/issues/699)) -* Update docker/docker and docker/cli to newest stable ([#695](https://github.com/kreuzwerker/terraform-provider-docker/issues/695)) - -### Feat - -* Implement support for docker context ([#704](https://github.com/kreuzwerker/terraform-provider-docker/issues/704)) -* disable_docker_daemon_check for provider ([#703](https://github.com/kreuzwerker/terraform-provider-docker/issues/703)) -* Implement tag triggers for docker_tag resource ([#702](https://github.com/kreuzwerker/terraform-provider-docker/issues/702)) -* Implement auth_config for docker_registry_image ([#701](https://github.com/kreuzwerker/terraform-provider-docker/issues/701)) - -### Fix - -* Store correctly ports from server ([#698](https://github.com/kreuzwerker/terraform-provider-docker/issues/698)) - - - -## [v3.2.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.1.2...v3.2.0) (2025-04-16) - -### Chore - -* Prepare release v3.2.0 ([#694](https://github.com/kreuzwerker/terraform-provider-docker/issues/694)) -* Upgrade golangci-lint to next major version ([#686](https://github.com/kreuzwerker/terraform-provider-docker/issues/686)) - -### Docs - -* Consolidated update of docs from several PRs ([#691](https://github.com/kreuzwerker/terraform-provider-docker/issues/691)) - -### Feat - -* Implement upload permissions in docker_container resource ([#693](https://github.com/kreuzwerker/terraform-provider-docker/issues/693)) -* Implement docker_image timeouts ([#692](https://github.com/kreuzwerker/terraform-provider-docker/issues/692)) -* Add support for build-secrets ([#604](https://github.com/kreuzwerker/terraform-provider-docker/issues/604)) - -### Fix - -* Authentication to ECR public ([#690](https://github.com/kreuzwerker/terraform-provider-docker/issues/690)) - - - -## [v3.1.2](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.1.1...v3.1.2) (2025-04-15) - -### Chore - -* prepare release 3.1.2 ([#688](https://github.com/kreuzwerker/terraform-provider-docker/issues/688)) - - - -## [v3.1.1](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.1.0...v3.1.1) (2025-04-14) - -### Chore - -* Prepare release 3.1.1 ([#687](https://github.com/kreuzwerker/terraform-provider-docker/issues/687)) - - - -## [v3.1.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.0.2...v3.1.0) (2025-04-14) - -### Chore - -* Prepare release 3.1.0 ([#685](https://github.com/kreuzwerker/terraform-provider-docker/issues/685)) -* update Go version to 1.22 for consistency across workflows, jo… ([#613](https://github.com/kreuzwerker/terraform-provider-docker/issues/613)) - -### Feat - -* support setting cpu shares ([#575](https://github.com/kreuzwerker/terraform-provider-docker/issues/575)) - -### Fix - -* Use build_args everywhere and update documentation ([#681](https://github.com/kreuzwerker/terraform-provider-docker/issues/681)) -* Compress build context before sending it to Docker ([#461](https://github.com/kreuzwerker/terraform-provider-docker/issues/461)) -* Set correct default network driver and fix a test ([#677](https://github.com/kreuzwerker/terraform-provider-docker/issues/677)) - -### Typo - -* s/presend/present/ ([#606](https://github.com/kreuzwerker/terraform-provider-docker/issues/606)) - - - -## [v3.0.2](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.0.1...v3.0.2) (2023-03-17) - -### Chore - -* Prepare release v3.0.2 - -### Docs - -* correct spelling of "networks_advanced" ([#517](https://github.com/kreuzwerker/terraform-provider-docker/issues/517)) - -### Fix - -* Implement proxy support. ([#529](https://github.com/kreuzwerker/terraform-provider-docker/issues/529)) - - - -## [v3.0.1](https://github.com/kreuzwerker/terraform-provider-docker/compare/v3.0.0...v3.0.1) (2023-01-13) - -### Chore - -* Prepare release v3.0.1 - -### Fix - -* Access health of container correctly. ([#506](https://github.com/kreuzwerker/terraform-provider-docker/issues/506)) - - - -## [v3.0.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.25.0...v3.0.0) (2023-01-13) - -### Chore - -* Prepare release v3.0.0 - -### Docs - -* Update documentation. -* Add migration guide and update README ([#502](https://github.com/kreuzwerker/terraform-provider-docker/issues/502)) - -### Feat - -* Prepare v3 release ([#503](https://github.com/kreuzwerker/terraform-provider-docker/issues/503)) - - - -## [v2.25.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.24.0...v2.25.0) (2023-01-05) - -### Chore - -* Prepare release v2.25.0 - -### Docs - -* Add documentation of remote hosts. ([#498](https://github.com/kreuzwerker/terraform-provider-docker/issues/498)) - -### Feat - -* Migrate build block to `docker_image` ([#501](https://github.com/kreuzwerker/terraform-provider-docker/issues/501)) -* Add platform attribute to docker_image resource ([#500](https://github.com/kreuzwerker/terraform-provider-docker/issues/500)) -* Add sysctl implementation to container of docker_service. ([#499](https://github.com/kreuzwerker/terraform-provider-docker/issues/499)) - - - -## [v2.24.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.23.1...v2.24.0) (2022-12-23) - -### Chore - -* Prepare release v2.24.0 - -### Docs - -* Fix generated website. -* Update command typo ([#487](https://github.com/kreuzwerker/terraform-provider-docker/issues/487)) - -### Feat - -* cgroupns support ([#497](https://github.com/kreuzwerker/terraform-provider-docker/issues/497)) -* Add triggers attribute to docker_registry_image ([#496](https://github.com/kreuzwerker/terraform-provider-docker/issues/496)) -* Support registries with disabled auth ([#494](https://github.com/kreuzwerker/terraform-provider-docker/issues/494)) -* add IPAM options block for docker networks ([#491](https://github.com/kreuzwerker/terraform-provider-docker/issues/491)) - -### Fix - -* Pin data source specific tag test to older tag. - -### Tests - -* Add test for parsing auth headers. - - - -## [v2.23.1](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.23.0...v2.23.1) (2022-11-23) - -### Chore - -* Prepare release v2.23.1 - -### Fix - -* Update shasum of busybox:1.35.0 tag in test. -* Handle Auth Header Scopes ([#482](https://github.com/kreuzwerker/terraform-provider-docker/issues/482)) -* Set OS_ARCH from GOHOSTOS and GOHOSTARCH ([#477](https://github.com/kreuzwerker/terraform-provider-docker/issues/477)) - - - -## [v2.23.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.22.0...v2.23.0) (2022-11-02) - -### Chore - -* Prepare release v2.23.0 - -### Feat - -* wait container healthy state ([#467](https://github.com/kreuzwerker/terraform-provider-docker/issues/467)) -* add docker logs data source ([#471](https://github.com/kreuzwerker/terraform-provider-docker/issues/471)) - -### Fix - -* Update shasum of busybox:1.35.0 tag in test. -* Update shasum of busybox:1.35.0 tag -* Correct provider name to match the public registry ([#462](https://github.com/kreuzwerker/terraform-provider-docker/issues/462)) - - - -## [v2.22.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.21.0...v2.22.0) (2022-09-20) - -### Chore - -* Prepare release v2.22.0 - -### Feat - -* Configurable timeout for docker_container resource stateChangeConf ([#454](https://github.com/kreuzwerker/terraform-provider-docker/issues/454)) - -### Fix - -* oauth authorization support for azurecr ([#451](https://github.com/kreuzwerker/terraform-provider-docker/issues/451)) - - - -## [v2.21.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.20.3...v2.21.0) (2022-09-05) - -### Chore - -* Prepare release v2.21.0 - -### Docs - -* Fix docker config example. - -### Feat - -* Add image_id attribute to docker_image resource. ([#450](https://github.com/kreuzwerker/terraform-provider-docker/issues/450)) -* Update used goversion to 1.18. ([#449](https://github.com/kreuzwerker/terraform-provider-docker/issues/449)) - -### Fix - -* Replace deprecated .latest attribute with new image_id. ([#453](https://github.com/kreuzwerker/terraform-provider-docker/issues/453)) -* Remove reading part of docker_tag resource. ([#448](https://github.com/kreuzwerker/terraform-provider-docker/issues/448)) -* Fix repo_digest value for DockerImageDatasource test. - - - -## [v2.20.3](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.20.2...v2.20.3) (2022-08-31) - -### Chore - -* Prepare release v2.20.3 - -### Fix - -* Docker Registry Image data source use HEAD request to query image digest ([#433](https://github.com/kreuzwerker/terraform-provider-docker/issues/433)) -* Adding Support for Windows Paths in Bash ([#438](https://github.com/kreuzwerker/terraform-provider-docker/issues/438)) - - - -## [v2.20.2](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.20.1...v2.20.2) (2022-08-10) - -### Chore - -* Prepare release v2.20.2 - -### Fix - -* Check the operating system for determining the default Docker socket ([#427](https://github.com/kreuzwerker/terraform-provider-docker/issues/427)) - -### Reverts - -* fix(deps): update module github.com/golangci/golangci-lint to v1.48.0 ([#423](https://github.com/kreuzwerker/terraform-provider-docker/issues/423)) - - - -## [v2.20.1](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.20.0...v2.20.1) (2022-08-10) - -### Chore - -* Prepare release v2.20.1 -* Reduce time to setup AccTests ([#430](https://github.com/kreuzwerker/terraform-provider-docker/issues/430)) - -### Docs - -* Improve docker network usage documentation [skip-ci] - -### Feat - -* Implement triggers attribute for docker_image. ([#425](https://github.com/kreuzwerker/terraform-provider-docker/issues/425)) - -### Fix - -* Add ForceTrue to docker_image name attribute. ([#421](https://github.com/kreuzwerker/terraform-provider-docker/issues/421)) - - - -## [v2.20.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.19.0...v2.20.0) (2022-07-28) - -### Chore - -* Prepare release v2.20.0 -* Fix release targets in Makefile. - -### Feat - -* Implementation of `docker_tag` resource. ([#418](https://github.com/kreuzwerker/terraform-provider-docker/issues/418)) -* Implement support for insecure registries ([#414](https://github.com/kreuzwerker/terraform-provider-docker/issues/414)) - - - -## [v2.19.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.18.1...v2.19.0) (2022-07-15) - -### Chore - -* Prepare release v2.19.0 - -### Feat - -* Add gpu flag to docker_container resource ([#405](https://github.com/kreuzwerker/terraform-provider-docker/issues/405)) - -### Fix - -* Enable authentication to multiple registries again. ([#400](https://github.com/kreuzwerker/terraform-provider-docker/issues/400)) -* ECR authentication ([#409](https://github.com/kreuzwerker/terraform-provider-docker/issues/409)) - - - -## [v2.18.1](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.18.0...v2.18.1) (2022-07-14) - -### Chore - -* Prepare release v2.18.1 -* Automate changelog generation [skip ci] - -### Fix - -* Improve searchLocalImages error handling. ([#407](https://github.com/kreuzwerker/terraform-provider-docker/issues/407)) -* Throw errors when any part of docker config file handling goes wrong. ([#406](https://github.com/kreuzwerker/terraform-provider-docker/issues/406)) -* Enables having a Dockerfile outside the context ([#402](https://github.com/kreuzwerker/terraform-provider-docker/issues/402)) - - - -## [v2.18.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.17.0...v2.18.0) (2022-07-11) - -### Chore - -* prepare release v2.18.0 - -### Feat - -* add runtime, stop_signal and stop_timeout properties to the docker_container resource ([#364](https://github.com/kreuzwerker/terraform-provider-docker/issues/364)) - -### Fix - -* Correctly handle build files and context for docker_registry_image ([#398](https://github.com/kreuzwerker/terraform-provider-docker/issues/398)) -* Switch to proper go tools mechanism to fix website-* workflows. ([#399](https://github.com/kreuzwerker/terraform-provider-docker/issues/399)) -* compare relative paths when excluding, fixes kreuzwerker[#280](https://github.com/kreuzwerker/terraform-provider-docker/issues/280) ([#397](https://github.com/kreuzwerker/terraform-provider-docker/issues/397)) - - - -## [v2.17.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.16.0...v2.17.0) (2022-06-23) - -### Chore - -* prepare release v2.17.0 -* Exclude examples directory from renovate. -* remove the workflow to close stale issues and pull requests ([#371](https://github.com/kreuzwerker/terraform-provider-docker/issues/371)) - -### Fix - -* update go package files directly on master to fix build. -* correct authentication for ghcr.io registry([#349](https://github.com/kreuzwerker/terraform-provider-docker/issues/349)) - - - -## [v2.16.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.15.0...v2.16.0) (2022-01-24) - -### Chore - -* prepare release v2.16.0 - -### Docs - -* fix service options ([#337](https://github.com/kreuzwerker/terraform-provider-docker/issues/337)) -* update registry_image.md ([#321](https://github.com/kreuzwerker/terraform-provider-docker/issues/321)) -* fix r/registry_image truncated docs ([#304](https://github.com/kreuzwerker/terraform-provider-docker/issues/304)) - -### Feat - -* add parameter for SSH options ([#335](https://github.com/kreuzwerker/terraform-provider-docker/issues/335)) - -### Fix - -* pass container rm flag ([#322](https://github.com/kreuzwerker/terraform-provider-docker/issues/322)) -* add nil check of DriverConfig ([#315](https://github.com/kreuzwerker/terraform-provider-docker/issues/315)) -* fmt of go files for go 1.17 - - - -## [v2.15.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.14.0...v2.15.0) (2021-08-11) - -### Chore - -* prepare release v2.15.0 -* re go gets terraform-plugin-docs - -### Docs - -* corrects authentication misspell. Closes [#264](https://github.com/kreuzwerker/terraform-provider-docker/issues/264) - -### Feat - -* add container storage opts ([#258](https://github.com/kreuzwerker/terraform-provider-docker/issues/258)) - -### Fix - -* add current timestamp for file upload to container ([#259](https://github.com/kreuzwerker/terraform-provider-docker/issues/259)) - - - -## [v2.14.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.13.0...v2.14.0) (2021-07-09) - -### Chore - -* prepare release v2.14.0 - -### Docs - -* update to absolute path for registry image context ([#246](https://github.com/kreuzwerker/terraform-provider-docker/issues/246)) -* update readme with logos and subsections ([#235](https://github.com/kreuzwerker/terraform-provider-docker/issues/235)) - -### Feat - -* support terraform v1 ([#242](https://github.com/kreuzwerker/terraform-provider-docker/issues/242)) - -### Fix - -* Update the URL of the docker hub registry ([#230](https://github.com/kreuzwerker/terraform-provider-docker/issues/230)) - - - -## [v2.13.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.12.2...v2.13.0) (2021-06-22) - -### Chore - -* prepare release v2.13.0 - -### Docs - -* fix a few typos ([#216](https://github.com/kreuzwerker/terraform-provider-docker/issues/216)) -* fix typos in docker_image example usage ([#213](https://github.com/kreuzwerker/terraform-provider-docker/issues/213)) - - - -## [v2.12.2](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.12.1...v2.12.2) (2021-05-26) - -### Chore - -* prepare release v2.12.2 - - - -## [v2.12.1](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.12.0...v2.12.1) (2021-05-26) - -### Chore - -* update changelog for v2.12.1 - -### Fix - -* add service host flattener with space split ([#205](https://github.com/kreuzwerker/terraform-provider-docker/issues/205)) -* service state upgradeV2 for empty auth - - - -## [v2.12.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.11.0...v2.12.0) (2021-05-23) - -### Chore - -* update changelog for v2.12.0 -* ignore dist folder -* configure actions/stale ([#157](https://github.com/kreuzwerker/terraform-provider-docker/issues/157)) -* add the guide about Terraform Configuration in Bug Report ([#139](https://github.com/kreuzwerker/terraform-provider-docker/issues/139)) -* bump docker dependency to v20.10.5 ([#119](https://github.com/kreuzwerker/terraform-provider-docker/issues/119)) - -### Ci - -* run acceptance tests with multiple Terraform versions ([#129](https://github.com/kreuzwerker/terraform-provider-docker/issues/129)) - -### Docs - -* update for v2.12.0 -* add releasing steps -* format `Guide of Bug report` ([#159](https://github.com/kreuzwerker/terraform-provider-docker/issues/159)) -* add an example to build an image with docker_image ([#158](https://github.com/kreuzwerker/terraform-provider-docker/issues/158)) -* add a guide about writing issues to CONTRIBUTING.md ([#149](https://github.com/kreuzwerker/terraform-provider-docker/issues/149)) -* fix Github repository URL in README ([#136](https://github.com/kreuzwerker/terraform-provider-docker/issues/136)) - -### Feat - -* support darwin arm builds and golang 1.16 ([#140](https://github.com/kreuzwerker/terraform-provider-docker/issues/140)) -* migrate to terraform-sdk v2 ([#102](https://github.com/kreuzwerker/terraform-provider-docker/issues/102)) - -### Fix - -* rewriting tar header fields ([#198](https://github.com/kreuzwerker/terraform-provider-docker/issues/198)) -* test spaces for windows ([#190](https://github.com/kreuzwerker/terraform-provider-docker/issues/190)) -* replace for loops with StateChangeConf ([#182](https://github.com/kreuzwerker/terraform-provider-docker/issues/182)) -* skip sign on compile action -* assign map to rawState when it is nil to prevent panic ([#180](https://github.com/kreuzwerker/terraform-provider-docker/issues/180)) -* search local images with Docker image ID ([#151](https://github.com/kreuzwerker/terraform-provider-docker/issues/151)) -* set "ForceNew: true" to labelSchema ([#152](https://github.com/kreuzwerker/terraform-provider-docker/issues/152)) - - - -## [v2.11.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.10.0...v2.11.0) (2021-01-22) - -### Chore - -* update changelog for v2.11.0 -* updates changelog for v2.10.0 - -### Docs - -* fix legacy configuration style ([#126](https://github.com/kreuzwerker/terraform-provider-docker/issues/126)) - -### Feat - -* add properties -it (tty and stdin_opn) to docker container - - - -## [v2.10.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.9.0...v2.10.0) (2021-01-08) - -### Chore - -* updates changelog for 2.10.0 -* ignores testing folders -* adds separate bug and ft req templates - -### Ci - -* bumps to docker version 20.10.1 -* pins workflows to ubuntu:20.04 image - -### Docs - -* add labels to arguments of docker_service ([#105](https://github.com/kreuzwerker/terraform-provider-docker/issues/105)) -* cleans readme -* adds coc and contributing - -### Feat - -* supports Docker plugin ([#35](https://github.com/kreuzwerker/terraform-provider-docker/issues/35)) -* support max replicas of Docker Service Task Spec ([#112](https://github.com/kreuzwerker/terraform-provider-docker/issues/112)) -* add force_remove option to r/image ([#104](https://github.com/kreuzwerker/terraform-provider-docker/issues/104)) -* add local semantic commit validation ([#99](https://github.com/kreuzwerker/terraform-provider-docker/issues/99)) -* add ability to lint/check of links in documentation locally ([#98](https://github.com/kreuzwerker/terraform-provider-docker/issues/98)) - -### Fix - -* set "latest" to tag when tag isn't specified ([#117](https://github.com/kreuzwerker/terraform-provider-docker/issues/117)) -* image label for workflows -* remove all azure cps - -### Pull Requests - -* Merge pull request [#38](https://github.com/kreuzwerker/terraform-provider-docker/issues/38) from kreuzwerker/ci-ubuntu2004-workflow -* Merge pull request [#36](https://github.com/kreuzwerker/terraform-provider-docker/issues/36) from kreuzwerker/chore-gh-issue-tpl - - - -## [v2.9.0](https://github.com/kreuzwerker/terraform-provider-docker/compare/v2.8.0...v2.9.0) (2020-12-25) - -### Chore - -* updates changelog for 2.9.0 -* update changelog 2.8.0 release date -* introduces golangci-lint ([#32](https://github.com/kreuzwerker/terraform-provider-docker/issues/32)) -* fix changelog links - -### Ci - -* add gofmt's '-s' option -* remove unneeded make tasks -* fix test of website - -### Doc - -* devices is a block, not a boolean - -### Feat - -* adds support for OCI manifests ([#316](https://github.com/kreuzwerker/terraform-provider-docker/issues/316)) -* adds security_opts to container config. ([#308](https://github.com/kreuzwerker/terraform-provider-docker/issues/308)) -* adds support for init process injection for containers. ([#300](https://github.com/kreuzwerker/terraform-provider-docker/issues/300)) - -### Fix - -* changing mounts requires ForceNew ([#314](https://github.com/kreuzwerker/terraform-provider-docker/issues/314)) -* allow healthcheck to be computed as container can specify ([#312](https://github.com/kreuzwerker/terraform-provider-docker/issues/312)) -* treat null user as a no-op ([#318](https://github.com/kreuzwerker/terraform-provider-docker/issues/318)) -* workdir null behavior ([#320](https://github.com/kreuzwerker/terraform-provider-docker/issues/320)) - -### Style - -* format with gofumpt - -### Pull Requests - -* Merge pull request [#33](https://github.com/kreuzwerker/terraform-provider-docker/issues/33) from brandonros/patch-1 -* Merge pull request [#11](https://github.com/kreuzwerker/terraform-provider-docker/issues/11) from suzuki-shunsuke/format-with-gofumpt -* Merge pull request [#26](https://github.com/kreuzwerker/terraform-provider-docker/issues/26) from kreuzwerker/ci/fix-website-ci -* Merge pull request [#8](https://github.com/kreuzwerker/terraform-provider-docker/issues/8) from dubo-dubon-duponey/patch1 - - - -## v2.8.0 (2020-11-11) - -### Chore - -* updates changelog for 2.8.0 -* removes travis.yml -* deactivates travis -* removes vendor dir ([#298](https://github.com/kreuzwerker/terraform-provider-docker/issues/298)) -* bump go 115 ([#297](https://github.com/kreuzwerker/terraform-provider-docker/issues/297)) -* documentation updates ([#286](https://github.com/kreuzwerker/terraform-provider-docker/issues/286)) -* updates link syntax ([#287](https://github.com/kreuzwerker/terraform-provider-docker/issues/287)) -* fix typo ([#292](https://github.com/kreuzwerker/terraform-provider-docker/issues/292)) - -### Ci - -* reactivats all workflows -* fix website -* only run website workflow -* exports gopath manually -* fix absolute gopath for website -* make website check separate workflow -* fix workflow names -* adds website test to unit test -* adds acc test -* adds compile -* adds go version and goproxy env -* enables unit tests for master branch -* adds unit test workflow -* adds goreleaser and gh action -* bumps docker and ubuntu versions ([#241](https://github.com/kreuzwerker/terraform-provider-docker/issues/241)) -* removes debug option from acc tests -* skips test which is flaky only on travis - -### Deps - -* github.com/hashicorp/terraform[@sdk](https://github.com/sdk)-v0.11-with-go-modules Updated via: go get github.com/hashicorp/terraform[@sdk](https://github.com/sdk)-v0.11-with-go-modules and go mod tidy -* use go modules for dep mgmt run go mod tidy remove govendor from makefile and travis config set appropriate env vars for go modules - -### Docker - -* improve validation of runtime constraints - -### Docs - -* update container.html.markdown ([#278](https://github.com/kreuzwerker/terraform-provider-docker/issues/278)) -* update service.html.markdown ([#281](https://github.com/kreuzwerker/terraform-provider-docker/issues/281)) -* update restart_policy for service. Closes [#228](https://github.com/kreuzwerker/terraform-provider-docker/issues/228) -* adds new label structure. Closes [#214](https://github.com/kreuzwerker/terraform-provider-docker/issues/214) -* update anchors with -1 suffix ([#178](https://github.com/kreuzwerker/terraform-provider-docker/issues/178)) -* Fix misspelled words -* Fix exported attribute name in docker_registry_image -* Fix example for docker_registry_image ([#8308](https://github.com/kreuzwerker/terraform-provider-docker/issues/8308)) -* provider/docker - network settings attrs - -### Feat - -* conditionally adding port binding ([#293](https://github.com/kreuzwerker/terraform-provider-docker/issues/293)). -* adds docker Image build feature ([#283](https://github.com/kreuzwerker/terraform-provider-docker/issues/283)) -* adds complete support for Docker credential helpers ([#253](https://github.com/kreuzwerker/terraform-provider-docker/issues/253)) -* Expose IPv6 properties as attributes -* allow use of source file instead of content / content_base64 ([#240](https://github.com/kreuzwerker/terraform-provider-docker/issues/240)) -* supports to update docker_container ([#236](https://github.com/kreuzwerker/terraform-provider-docker/issues/236)) -* support to import some docker_container's attributes ([#234](https://github.com/kreuzwerker/terraform-provider-docker/issues/234)) -* adds config file content as plain string ([#232](https://github.com/kreuzwerker/terraform-provider-docker/issues/232)) -* make UID, GID, & mode for secrets and configs configurable ([#231](https://github.com/kreuzwerker/terraform-provider-docker/issues/231)) -* adds import for resources ([#196](https://github.com/kreuzwerker/terraform-provider-docker/issues/196)) -* add container ipc mode. ([#182](https://github.com/kreuzwerker/terraform-provider-docker/issues/182)) -* adds container working dir ([#181](https://github.com/kreuzwerker/terraform-provider-docker/issues/181)) - -### Fix - -* ignores 'remove_volumes' on container import -* duplicated buildImage function -* port objects with the same internal port but different protocol trigger recreation of container ([#274](https://github.com/kreuzwerker/terraform-provider-docker/issues/274)) -* panic to migrate schema of docker_container from v1 to v2 ([#271](https://github.com/kreuzwerker/terraform-provider-docker/issues/271)). Closes [#264](https://github.com/kreuzwerker/terraform-provider-docker/issues/264) -* pins docker registry for tests to v2.7.0 -* prevent force recreate of container about some attributes ([#269](https://github.com/kreuzwerker/terraform-provider-docker/issues/269)) -* service endpoint spec flattening -* corrects IPAM config read on the data provider ([#229](https://github.com/kreuzwerker/terraform-provider-docker/issues/229)) -* replica to 0 in current schema. Closes [#221](https://github.com/kreuzwerker/terraform-provider-docker/issues/221) -* label for network and volume after improt -* binary upload as base 64 content ([#194](https://github.com/kreuzwerker/terraform-provider-docker/issues/194)) -* service env truncation for multiple delimiters ([#193](https://github.com/kreuzwerker/terraform-provider-docker/issues/193)) -* destroy_grace_seconds are considered ([#179](https://github.com/kreuzwerker/terraform-provider-docker/issues/179)) - -### Make - -* Add website + website-test targets - -### Provider - -* Ensured Go 1.11 in TravisCI and README provider: Run go fix provider: Run go fmt provider: Encode go version 1.11.5 to .go-version file -* Require Go 1.11 in TravisCI and README provider: Run go fix provider: Run go fmt - -### Tests - -* Skip test if swap limit isn't available ([#136](https://github.com/kreuzwerker/terraform-provider-docker/issues/136)) -* Simplify Dockerfile(s) - -### Vendor - -* github.com/hashicorp/terraform/...[@v0](https://github.com/v0).10.0 -* Ignore github.com/hashicorp/terraform/backend - -### Website - -* Docs sweep for lists & maps -* note on docker -* docker docs - -### Pull Requests - -* Merge pull request [#134](https://github.com/kreuzwerker/terraform-provider-docker/issues/134) from terraform-providers/go-modules-2019-03-01 -* Merge pull request [#135](https://github.com/kreuzwerker/terraform-provider-docker/issues/135) from terraform-providers/t-simplify-dockerfile -* Merge pull request [#47](https://github.com/kreuzwerker/terraform-provider-docker/issues/47) from captn3m0/docker-link-warning -* Merge pull request [#60](https://github.com/kreuzwerker/terraform-provider-docker/issues/60) from terraform-providers/f-make-website -* Merge pull request [#23](https://github.com/kreuzwerker/terraform-provider-docker/issues/23) from JamesLaverack/patch-1 -* Merge pull request [#18](https://github.com/kreuzwerker/terraform-provider-docker/issues/18) from terraform-providers/vendor-tf-0.10 -* Merge pull request [#5046](https://github.com/kreuzwerker/terraform-provider-docker/issues/5046) from tpounds/use-built-in-schema-string-hash -* Merge pull request [#3761](https://github.com/kreuzwerker/terraform-provider-docker/issues/3761) from ryane/f-provider-docker-improvements -* Merge pull request [#3383](https://github.com/kreuzwerker/terraform-provider-docker/issues/3383) from apparentlymart/docker-container-command-docs -* Merge pull request [#1564](https://github.com/kreuzwerker/terraform-provider-docker/issues/1564) from nickryand/docker_links - diff --git a/.terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/LICENSE b/.terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/LICENSE deleted file mode 100644 index a612ad9..0000000 --- a/.terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/.terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/README.md b/.terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/README.md deleted file mode 100644 index 6be3b5f..0000000 --- a/.terraform/providers/registry.terraform.io/kreuzwerker/docker/3.9.0/windows_386/README.md +++ /dev/null @@ -1,117 +0,0 @@ - - Docker logo - - - Terraform logo - - - Kreuzwerker logo - - -# Terraform Provider for Docker - -[![Release](https://img.shields.io/github/v/release/kreuzwerker/terraform-provider-docker)](https://github.com/kreuzwerker/terraform-provider-docker/releases) -[![Installs](https://img.shields.io/badge/dynamic/json?logo=terraform&label=installs&query=$.data.attributes.downloads&url=https%3A%2F%2Fregistry.terraform.io%2Fv2%2Fproviders%2F713)](https://registry.terraform.io/providers/kreuzwerker/docker) -[![Registry](https://img.shields.io/badge/registry-doc%40latest-lightgrey?logo=terraform)](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs) -[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/kreuzwerker/terraform-provider-docker/blob/main/LICENSE) -[![Go Status](https://github.com/kreuzwerker/terraform-provider-docker/workflows/Acc%20Tests/badge.svg)](https://github.com/kreuzwerker/terraform-provider-docker/actions) -[![Lint Status](https://github.com/kreuzwerker/terraform-provider-docker/workflows/golangci-lint/badge.svg)](https://github.com/kreuzwerker/terraform-provider-docker/actions) -[![Go Report Card](https://goreportcard.com/badge/github.com/kreuzwerker/terraform-provider-docker)](https://goreportcard.com/report/github.com/kreuzwerker/terraform-provider-docker) - -## Documentation - -The documentation for the provider is available on the [Terraform Registry](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs). - -Do you want to migrate from `v2.x` to `v3.x`? Please read the [migration guide](docs/v2_v3_migration.md) - -## Example usage - -Take a look at the examples in the [documentation](https://registry.terraform.io/providers/kreuzwerker/docker/3.9.0/docs) of the registry -or use the following example: - - -```hcl -# Set the required provider and versions -terraform { - required_providers { - # We recommend pinning to the specific version of the Docker Provider you're using - # since new versions are released frequently - docker = { - source = "kreuzwerker/docker" - version = "3.9.0" - } - } -} - -# Configure the docker provider -provider "docker" { -} - -# Create a docker image resource -# -> docker pull nginx:latest -resource "docker_image" "nginx" { - name = "nginx:latest" - keep_locally = true -} - -# Create a docker container resource -# -> same as 'docker run --name nginx -p8080:80 -d nginx:latest' -resource "docker_container" "nginx" { - name = "nginx" - image = docker_image.nginx.image_id - - ports { - external = 8080 - internal = 80 - } -} - -# Or create a service resource -# -> same as 'docker service create -d -p 8081:80 --name nginx-service --replicas 2 nginx:latest' -resource "docker_service" "nginx_service" { - name = "nginx-service" - task_spec { - container_spec { - image = docker_image.nginx.repo_digest - } - } - - mode { - replicated { - replicas = 2 - } - } - - endpoint_spec { - ports { - published_port = 8081 - target_port = 80 - } - } -} -``` - -## Building The Provider - -[Go](https://golang.org/doc/install) 1.18.x (to build the provider plugin) - - -```sh -$ git clone git@github.com:kreuzwerker/terraform-provider-docker -$ make build -``` - -## Contributing - -The Terraform Docker Provider is the work of many of contributors. We appreciate your help! - -To contribute, please read the contribution guidelines: [Contributing to Terraform - Docker Provider](CONTRIBUTING.md) - -## License - -The Terraform Provider Docker is available to everyone under the terms of the Mozilla Public License Version 2.0. [Take a look the LICENSE file](LICENSE). - - -## Stargazers over time - -[![Stargazers over time](https://starchart.cc/kreuzwerker/terraform-provider-docker.svg)](https://starchart.cc/kreuzwerker/terraform-provider-docker) diff --git a/infra/README.md b/infra/README.md index bff8fbe..4866222 100644 --- a/infra/README.md +++ b/infra/README.md @@ -1,6 +1,22 @@ # Infrastructure -Provisionnement Terraform de la machine on-premise. Non initialise, voir le ticket dedie. +Provisionnement Terraform de la machine on-premise (serveur physique, accessible en SSH). - `terraform/modules` : modules reutilisables. + - `k3s` : installe un cluster k3s single-node sur une machine distante via SSH + (script officiel `get.k3s.io`) et rapatrie le kubeconfig en local. - `terraform/environments/` : racines Terraform, une par environnement. + - `dev` : instancie le module `k3s` sur le serveur de l'ecole. + - `prod` : non initialise, voir le ticket dedie. + +## Usage (environments/dev) + +```bash +cd infra/terraform/environments/dev +cp terraform.tfvars.example terraform.tfvars # renseigner ssh_host / ssh_private_key_path +terraform init +terraform apply +``` + +Le kubeconfig est ecrit localement au chemin defini par `kubeconfig_output_path` +(par defaut `./kubeconfig`, ignore par git). diff --git a/infra/terraform/environments/dev/.gitkeep b/infra/terraform/environments/dev/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/infra/terraform/environments/dev/main.tf b/infra/terraform/environments/dev/main.tf new file mode 100644 index 0000000..1361f4d --- /dev/null +++ b/infra/terraform/environments/dev/main.tf @@ -0,0 +1,11 @@ +module "k3s" { + source = "../../modules/k3s" + + ssh_host = var.ssh_host + ssh_port = var.ssh_port + ssh_user = var.ssh_user + ssh_private_key_path = var.ssh_private_key_path + k3s_version = var.k3s_version + k3s_disable_components = var.k3s_disable_components + kubeconfig_output_path = var.kubeconfig_output_path +} diff --git a/infra/terraform/environments/dev/outputs.tf b/infra/terraform/environments/dev/outputs.tf new file mode 100644 index 0000000..69cd419 --- /dev/null +++ b/infra/terraform/environments/dev/outputs.tf @@ -0,0 +1,9 @@ +output "kubeconfig_path" { + description = "Chemin local du kubeconfig recupere apres installation." + value = module.k3s.kubeconfig_path +} + +output "node_host" { + description = "Adresse du serveur sur lequel k3s est installe." + value = module.k3s.node_host +} diff --git a/infra/terraform/environments/dev/terraform.tfvars.example b/infra/terraform/environments/dev/terraform.tfvars.example new file mode 100644 index 0000000..ba86a56 --- /dev/null +++ b/infra/terraform/environments/dev/terraform.tfvars.example @@ -0,0 +1,7 @@ +ssh_host = "10.0.0.10" +ssh_port = 22 +ssh_user = "root" +ssh_private_key_path = "~/.ssh/id_ed25519_enervision" +k3s_version = "" +k3s_disable_components = ["traefik"] +kubeconfig_output_path = "./kubeconfig" diff --git a/infra/terraform/environments/dev/variables.tf b/infra/terraform/environments/dev/variables.tf new file mode 100644 index 0000000..b5f90db --- /dev/null +++ b/infra/terraform/environments/dev/variables.tf @@ -0,0 +1,40 @@ +variable "ssh_host" { + type = string + description = "Adresse IP ou nom d'hote du serveur on-premise de l'ecole." +} + +variable "ssh_port" { + type = number + description = "Port SSH du serveur." + default = 22 +} + +variable "ssh_user" { + type = string + description = "Utilisateur SSH utilise pour l'installation." + default = "root" +} + +variable "ssh_private_key_path" { + type = string + description = "Chemin local vers la cle privee SSH." + sensitive = true +} + +variable "k3s_version" { + type = string + description = "Version k3s a installer. Chaine vide = derniere version stable." + default = "" +} + +variable "k3s_disable_components" { + type = list(string) + description = "Composants embarques k3s a desactiver." + default = ["traefik"] +} + +variable "kubeconfig_output_path" { + type = string + description = "Chemin local ou ecrire le kubeconfig recupere apres installation." + default = "./kubeconfig" +} diff --git a/infra/terraform/environments/dev/versions.tf b/infra/terraform/environments/dev/versions.tf new file mode 100644 index 0000000..d793df3 --- /dev/null +++ b/infra/terraform/environments/dev/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.7" + + required_providers { + null = { + source = "hashicorp/null" + version = "~> 3.2" + } + } + + backend "local" { + path = "terraform.tfstate" + } +} diff --git a/infra/terraform/modules/.gitkeep b/infra/terraform/modules/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/infra/terraform/modules/k3s/main.tf b/infra/terraform/modules/k3s/main.tf new file mode 100644 index 0000000..24be659 --- /dev/null +++ b/infra/terraform/modules/k3s/main.tf @@ -0,0 +1,45 @@ +locals { + sudo_prefix = var.ssh_user == "root" ? "" : "sudo " + install_env = var.k3s_version != "" ? "INSTALL_K3S_VERSION=${var.k3s_version} " : "" + disable_flags = join(" ", [for c in var.k3s_disable_components : "--disable=${c}"]) + kubeconfig_cmd = "${local.sudo_prefix}cat /etc/rancher/k3s/k3s.yaml" +} + +resource "null_resource" "k3s_install" { + triggers = { + ssh_host = var.ssh_host + k3s_version = var.k3s_version + disable_components = join(",", var.k3s_disable_components) + } + + connection { + type = "ssh" + host = var.ssh_host + port = var.ssh_port + user = var.ssh_user + private_key = file(var.ssh_private_key_path) + } + + provisioner "remote-exec" { + inline = [ + "${local.sudo_prefix}sh -c 'curl -sfL https://get.k3s.io | ${local.install_env}sh -s - server --write-kubeconfig-mode 644 ${local.disable_flags}'", + "until ${local.sudo_prefix}test -f /etc/rancher/k3s/k3s.yaml; do sleep 2; done", + ] + } +} + +resource "null_resource" "fetch_kubeconfig" { + depends_on = [null_resource.k3s_install] + + triggers = { + install_id = null_resource.k3s_install.id + } + + provisioner "local-exec" { + interpreter = ["bash", "-c"] + command = <<-EOT + ssh -i "${var.ssh_private_key_path}" -p ${var.ssh_port} -o StrictHostKeyChecking=accept-new ${var.ssh_user}@${var.ssh_host} '${local.kubeconfig_cmd}' \ + | sed 's/127.0.0.1/${var.ssh_host}/' > "${var.kubeconfig_output_path}" + EOT + } +} diff --git a/infra/terraform/modules/k3s/outputs.tf b/infra/terraform/modules/k3s/outputs.tf new file mode 100644 index 0000000..6b92aad --- /dev/null +++ b/infra/terraform/modules/k3s/outputs.tf @@ -0,0 +1,9 @@ +output "kubeconfig_path" { + description = "Chemin local du kubeconfig recupere apres installation." + value = var.kubeconfig_output_path +} + +output "node_host" { + description = "Adresse de la machine sur laquelle k3s est installe." + value = var.ssh_host +} diff --git a/infra/terraform/modules/k3s/variables.tf b/infra/terraform/modules/k3s/variables.tf new file mode 100644 index 0000000..7f2f91d --- /dev/null +++ b/infra/terraform/modules/k3s/variables.tf @@ -0,0 +1,39 @@ +variable "ssh_host" { + type = string + description = "Adresse IP ou nom d'hote de la machine on-premise cible." +} + +variable "ssh_port" { + type = number + description = "Port SSH de la machine cible." + default = 22 +} + +variable "ssh_user" { + type = string + description = "Utilisateur SSH. Si different de root, les commandes d'installation sont prefixees par sudo." + default = "root" +} + +variable "ssh_private_key_path" { + type = string + description = "Chemin local vers la cle privee SSH utilisee pour se connecter a la machine cible." + sensitive = true +} + +variable "k3s_version" { + type = string + description = "Version k3s a installer (ex: v1.31.2+k3s1). Chaine vide = derniere version stable." + default = "" +} + +variable "k3s_disable_components" { + type = list(string) + description = "Composants embarques a desactiver a l'installation (ex: traefik, servicelb)." + default = ["traefik"] +} + +variable "kubeconfig_output_path" { + type = string + description = "Chemin local ou ecrire le kubeconfig recupere apres installation." +} diff --git a/infra/terraform/modules/k3s/versions.tf b/infra/terraform/modules/k3s/versions.tf new file mode 100644 index 0000000..90c2aa0 --- /dev/null +++ b/infra/terraform/modules/k3s/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.7" + + required_providers { + null = { + source = "hashicorp/null" + version = "~> 3.2" + } + } +} diff --git a/terraform.tf b/terraform.tf deleted file mode 100644 index 0994a4d..0000000 --- a/terraform.tf +++ /dev/null @@ -1,19 +0,0 @@ -# Provider Docker -terraform { - required_providers { - docker = { - source = "kreuzwerker/docker" - version = "~> 3.0" - } - } -} - -provider "docker" { - host = "unix:///var/run/docker.sock" -} - -# Image Docker -resource "docker_image" "python" { - name = "python:3.14.7" - keep_locally = true -} diff --git a/test.txt b/test.txt deleted file mode 100644 index 28d0af9..0000000 --- a/test.txt +++ /dev/null @@ -1 +0,0 @@ -coucou From 239efc8ee63bd89cf7a9a84dfdc562643ccc3474 Mon Sep 17 00:00:00 2001 From: Dorian PESCE Date: Tue, 15 Sep 2026 10:32:22 +0200 Subject: [PATCH 17/18] docs/update README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b6ab9c8..22b01ab 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,12 @@ series temporelles energetiques, deployee sur une machine on-premise. | Frontend | Angular, Node 24 LTS | `apps/frontend` | A initialiser | | Base | PostgreSQL + TimescaleDB | `db` | A initialiser | | ETL | Apache Airflow | `etl/airflow` | A initialiser | -| Infra | Terraform | `infra/terraform` | A initialiser | +| Infra | Terraform (k3s single-node) | `infra/terraform` | Initialise | | CI/CD | GitHub Actions | `.github/workflows` | A initialiser | | Monitoring | Prometheus, Grafana, Alertmanager | `monitoring` | A initialiser | -Seul le backend est initialise a ce stade. Les autres dossiers portent l'arborescence et -un README de cadrage, leur contenu fait l'objet d'un ticket dedie. +Le backend et l'infrastructure (Terraform/k3s) sont initialises. Les autres dossiers +portent l'arborescence et un README de cadrage, leur contenu fait l'objet d'un ticket dedie. ## Arborescence From b910e747eced50832d1bcf42245a2a95a18c4b81 Mon Sep 17 00:00:00 2001 From: Dorian PESCE Date: Tue, 15 Sep 2026 11:29:34 +0200 Subject: [PATCH 18/18] fix: terraform & module --- .gitignore | 2 +- .../environments/dev/terraform.tfvars.example | 3 ++- infra/terraform/environments/dev/variables.tf | 3 +-- infra/terraform/modules/k3s/main.tf | 14 ++++++++++++-- infra/terraform/modules/k3s/variables.tf | 8 ++++++-- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index a226853..777b2b7 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,7 @@ yarn-error.log* # Terraform .terraform/ -.terraform.lock.hcl +# .terraform.lock.hcl est versionne (pas ignore) pour figer les versions de provider entre contributeurs/CI *.tfstate *.tfstate.* *.tfplan diff --git a/infra/terraform/environments/dev/terraform.tfvars.example b/infra/terraform/environments/dev/terraform.tfvars.example index ba86a56..323145a 100644 --- a/infra/terraform/environments/dev/terraform.tfvars.example +++ b/infra/terraform/environments/dev/terraform.tfvars.example @@ -2,6 +2,7 @@ ssh_host = "10.0.0.10" ssh_port = 22 ssh_user = "root" ssh_private_key_path = "~/.ssh/id_ed25519_enervision" -k3s_version = "" +# Epingler une version reelle avant apply : https://github.com/k3s-io/k3s/releases +k3s_version = "v1.31.5+k3s1" k3s_disable_components = ["traefik"] kubeconfig_output_path = "./kubeconfig" diff --git a/infra/terraform/environments/dev/variables.tf b/infra/terraform/environments/dev/variables.tf index b5f90db..9d507e3 100644 --- a/infra/terraform/environments/dev/variables.tf +++ b/infra/terraform/environments/dev/variables.tf @@ -23,8 +23,7 @@ variable "ssh_private_key_path" { variable "k3s_version" { type = string - description = "Version k3s a installer. Chaine vide = derniere version stable." - default = "" + description = "Version k3s a epingler pour un deploiement reproductible (ex: v1.31.5+k3s1). Voir https://github.com/k3s-io/k3s/releases." } variable "k3s_disable_components" { diff --git a/infra/terraform/modules/k3s/main.tf b/infra/terraform/modules/k3s/main.tf index 24be659..a8c23de 100644 --- a/infra/terraform/modules/k3s/main.tf +++ b/infra/terraform/modules/k3s/main.tf @@ -1,6 +1,6 @@ locals { sudo_prefix = var.ssh_user == "root" ? "" : "sudo " - install_env = var.k3s_version != "" ? "INSTALL_K3S_VERSION=${var.k3s_version} " : "" + install_env = "INSTALL_K3S_VERSION=${var.k3s_version} " disable_flags = join(" ", [for c in var.k3s_disable_components : "--disable=${c}"]) kubeconfig_cmd = "${local.sudo_prefix}cat /etc/rancher/k3s/k3s.yaml" } @@ -22,10 +22,20 @@ resource "null_resource" "k3s_install" { provisioner "remote-exec" { inline = [ - "${local.sudo_prefix}sh -c 'curl -sfL https://get.k3s.io | ${local.install_env}sh -s - server --write-kubeconfig-mode 644 ${local.disable_flags}'", + "${local.sudo_prefix}sh -c 'curl -sfL https://get.k3s.io | ${local.install_env}sh -s - server ${local.disable_flags}'", "until ${local.sudo_prefix}test -f /etc/rancher/k3s/k3s.yaml; do sleep 2; done", ] } + + # Le kubeconfig est lu via sudo (fetch_kubeconfig), pas besoin de --write-kubeconfig-mode : + # il reste 600/root par defaut, ce qui evite d'exposer les droits cluster-admin a tout utilisateur local. + provisioner "remote-exec" { + when = destroy + on_failure = continue + inline = [ + "${local.sudo_prefix}sh -c 'test -x /usr/local/bin/k3s-uninstall.sh && /usr/local/bin/k3s-uninstall.sh || true'", + ] + } } resource "null_resource" "fetch_kubeconfig" { diff --git a/infra/terraform/modules/k3s/variables.tf b/infra/terraform/modules/k3s/variables.tf index 7f2f91d..08c0e1c 100644 --- a/infra/terraform/modules/k3s/variables.tf +++ b/infra/terraform/modules/k3s/variables.tf @@ -23,8 +23,12 @@ variable "ssh_private_key_path" { variable "k3s_version" { type = string - description = "Version k3s a installer (ex: v1.31.2+k3s1). Chaine vide = derniere version stable." - default = "" + description = "Version k3s a epingler pour un deploiement reproductible (ex: v1.31.5+k3s1). Voir https://github.com/k3s-io/k3s/releases." + + validation { + condition = length(trimspace(var.k3s_version)) > 0 + error_message = "k3s_version doit etre epinglee explicitement, pas de valeur vide (sinon k3s.io installerait la derniere version a chaque run, non reproductible)." + } } variable "k3s_disable_components" {