diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a5fba5a --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +# 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 +# `basic` renvoie des statistiques d'usage a Timescale. +TIMESCALEDB_TELEMETRY=off + +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/.gitignore b/.gitignore index af1ea90..bb3dca3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ venv/ .coverage coverage.xml htmlcov/ +test-results/ dist/ build/ *.egg-info/ @@ -23,7 +24,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 @@ -32,6 +33,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/Makefile b/Makefile index 71dea97..81c3e6d 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-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}' @@ -21,10 +22,35 @@ format: ## Formate et corrige le backend typecheck: ## Verifie le typage du backend cd $(BACKEND) && uv run mypy app -test: ## Execute les tests backend - cd $(BACKEND) && uv run pytest +test: ## Execute les tests backend ne demandant pas de base + 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-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 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/README.md b/README.md index b6ab9c8..3302c64 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 | +| 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, la base et l'infrastructure (Terraform/k3s) 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,15 +50,35 @@ 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. +- 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/.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..5d940b8 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,16 @@ 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 ``` +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. + 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 +84,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 +97,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/TESTING.md b/apps/backend/TESTING.md new file mode 100644 index 0000000..f794421 --- /dev/null +++ b/apps/backend/TESTING.md @@ -0,0 +1,143 @@ +# 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 + +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 + +`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 +``` 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 b3c8523..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")) + 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..1c27c08 100644 --- a/apps/backend/pyproject.toml +++ b/apps/backend/pyproject.toml @@ -79,8 +79,14 @@ disallow_untyped_defs = false [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" -addopts = "-q --strict-markers --cov=app --cov-report=term-missing" +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`"] [tool.coverage.run] source = ["app"] -omit = ["app/main.py", "alembic/*"] +branch = true +omit = ["alembic/*"] + +[tool.coverage.report] +show_missing = true diff --git a/apps/backend/tests/api/test_health.py b/apps/backend/tests/api/test_health.py index 6a2f938..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,6 +17,32 @@ async def test_liveness_exposes_service_metadata(client: AsyncClient) -> None: } +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") + + 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( + 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" + + @pytest.mark.parametrize( "failure", [ @@ -29,16 +52,9 @@ async def test_liveness_exposes_service_metadata(client: AsyncClient) -> None: 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 execute(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") @@ -49,3 +65,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..4560b75 100644 --- a/apps/backend/tests/conftest.py +++ b/apps/backend/tests/conftest.py @@ -1,25 +1,49 @@ 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 sqlalchemy.ext.asyncio import AsyncSession from app.core.config import get_settings +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 +# 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: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() @@ -30,3 +54,21 @@ 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 + + +# 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 diff --git a/db/init/.gitkeep b/apps/backend/tests/core/__init__.py similarity index 100% rename from db/init/.gitkeep rename to apps/backend/tests/core/__init__.py diff --git a/docs/adr/.gitkeep b/apps/backend/tests/db/__init__.py similarity index 100% rename from docs/adr/.gitkeep rename to apps/backend/tests/db/__init__.py 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}) diff --git a/infra/terraform/environments/dev/.gitkeep b/apps/backend/tests/repositories/__init__.py similarity index 100% rename from infra/terraform/environments/dev/.gitkeep rename to apps/backend/tests/repositories/__init__.py diff --git a/infra/terraform/modules/.gitkeep b/apps/backend/tests/services/__init__.py similarity index 100% rename from infra/terraform/modules/.gitkeep rename to apps/backend/tests/services/__init__.py 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 @@ + + + + + + + + + diff --git a/db/README.md b/db/README.md index e80cb3c..fd62d6f 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,31 @@ 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 + +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 : il faut detruire +le volume, ce que fait `make db-reset`. + +L'image apporte ses propres scripts dans ce dossier, et ils comptent : + +| 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. | + +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, 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/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..d8569c9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,47 @@ +# 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 + +services: + db: + image: timescale/timescaledb-ha:pg17 + environment: + 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/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 + 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: 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. 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/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..323145a --- /dev/null +++ b/infra/terraform/environments/dev/terraform.tfvars.example @@ -0,0 +1,8 @@ +ssh_host = "10.0.0.10" +ssh_port = 22 +ssh_user = "root" +ssh_private_key_path = "~/.ssh/id_ed25519_enervision" +# 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 new file mode 100644 index 0000000..9d507e3 --- /dev/null +++ b/infra/terraform/environments/dev/variables.tf @@ -0,0 +1,39 @@ +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 epingler pour un deploiement reproductible (ex: v1.31.5+k3s1). Voir https://github.com/k3s-io/k3s/releases." +} + +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/k3s/main.tf b/infra/terraform/modules/k3s/main.tf new file mode 100644 index 0000000..a8c23de --- /dev/null +++ b/infra/terraform/modules/k3s/main.tf @@ -0,0 +1,55 @@ +locals { + sudo_prefix = var.ssh_user == "root" ? "" : "sudo " + 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" +} + +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 ${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" { + 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..08c0e1c --- /dev/null +++ b/infra/terraform/modules/k3s/variables.tf @@ -0,0 +1,43 @@ +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 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" { + 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