Files
Johan LEROY e53c7e441c feat(infra): déploie Garage par environnement, secrets par le .env, fumée S3 en CI
Reprend l'amorce de la PR 164 et l'intègre à la stack : service `garage` (dxflrs/garage v2.4.1,
`--single-node --default-bucket`) dans docker-compose.yml, garage.toml versionné sans secret,
ports sur 127.0.0.1 décalés par environnement dans provision-host.sh, garde des six clés
GARAGE_* dans le Makefile, cible Prometheus avec jeton, tests de fumée déplacés dans
tests/garage et joués par le job compose d'infra.yml contre le vrai conteneur, SSE-C compris.
Variables APP_S3_* et APP_READING_RETENTION_DAYS posées sur airflow-scheduler pour le DAG
`retention`. ADR 0019.

Closes #24
2026-09-24 10:30:04 +02:00

71 lines
2.3 KiB
Python

# Pourquoi : la CI (job compose d'infra.yml) démarre le vrai conteneur Garage et joue ces tests avec
# boto3, sans venv projet. Ils prouvent que le S3 accepte des écritures, les rend intactes, supprime
# vraiment, et que SSE-C refuse une lecture sans clé (ADR 0019, 0020) - test_smoke.py
import base64
import os
import uuid
import boto3
import pytest
from botocore.exceptions import ClientError
BUCKET = os.environ.get("GARAGE_BUCKET", "enervision-archives")
ENDPOINT_URL = os.environ.get(
"GARAGE_ENDPOINT_URL", f"http://127.0.0.1:{os.environ.get('GARAGE_S3_PORT', '3900')}"
)
SSE_KEY = base64.b64decode(os.environ["GARAGE_SSE_KEY"])
SSE = {"SSECustomerAlgorithm": "AES256", "SSECustomerKey": SSE_KEY}
@pytest.fixture
def client():
return boto3.client(
"s3",
endpoint_url=ENDPOINT_URL,
aws_access_key_id=os.environ["GARAGE_ACCESS_KEY"],
aws_secret_access_key=os.environ["GARAGE_SECRET_KEY"],
region_name="garage",
)
def test_default_bucket_exists(client):
noms = [bucket["Name"] for bucket in client.list_buckets()["Buckets"]]
assert BUCKET in noms
def test_upload_and_download_roundtrip(client):
cle = f"fumee/{uuid.uuid4()}.txt"
contenu = b"contenu de test EnerVision"
client.put_object(Bucket=BUCKET, Key=cle, Body=contenu)
recupere = client.get_object(Bucket=BUCKET, Key=cle)["Body"].read()
client.delete_object(Bucket=BUCKET, Key=cle)
assert recupere == contenu
def test_deleted_object_is_really_gone(client):
cle = f"fumee/suppression-{uuid.uuid4()}.txt"
client.put_object(Bucket=BUCKET, Key=cle, Body=b"a supprimer")
client.delete_object(Bucket=BUCKET, Key=cle)
with pytest.raises(client.exceptions.NoSuchKey):
client.get_object(Bucket=BUCKET, Key=cle)
def test_sse_c_object_is_unreadable_without_the_key(client):
cle = f"fumee/chiffre-{uuid.uuid4()}.txt"
contenu = b"archive chiffree"
client.put_object(Bucket=BUCKET, Key=cle, Body=contenu, **SSE)
with pytest.raises(ClientError) as erreur:
client.get_object(Bucket=BUCKET, Key=cle)
dechiffre = client.get_object(Bucket=BUCKET, Key=cle, **SSE)["Body"].read()
client.delete_object(Bucket=BUCKET, Key=cle)
assert erreur.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
assert dechiffre == contenu