Skip to content

Commit

Permalink
ADCM-6208 Run all tests on ADCM with SSL ON (#69)
Browse files Browse the repository at this point in the history
Co-authored-by: Araslanov Egor <[email protected]>
  • Loading branch information
Sealwing and Araslanov Egor authored Jan 15, 2025
1 parent a7ed7ff commit fda0eee
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 9 deletions.
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pytest-asyncio = "^0.24.0"
testcontainers = "^4.8.2"
pyyaml = "^6.0.2"
pytest-timeout = "^2.3.1"
docker = "^7.1.0"

[build-system]
requires = ["poetry-core"]
Expand Down
93 changes: 87 additions & 6 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
from collections.abc import AsyncGenerator, Generator
from io import BytesIO
from pathlib import Path
from time import sleep
from urllib.parse import urljoin
import os
import random
import string
import tarfile

from httpx import AsyncClient
from testcontainers.core.network import Network
import httpx
import pytest
import pytest_asyncio

Expand All @@ -19,7 +24,6 @@
ADCMContainer,
ADCMPostgresContainer,
DatabaseInfo,
adcm_image_name,
postgres_image_name,
)

Expand All @@ -31,6 +35,35 @@
################


def load_certs_to_running_adcm(adcm: ADCMContainer, certs_dir: Path) -> None:
file = BytesIO()
with tarfile.open(mode="w:gz", fileobj=file) as tar:
tar.add(certs_dir, "")
file.seek(0)

container = adcm.get_wrapped_container()
container.put_archive("/adcm/data/conf/ssl", file.read())
ec, out = adcm.exec(["nginx", "-s", "reload"])
if ec != 0:
raise RuntimeError(f"Failed to reload nginx after attaching certs: {out.decode('utf-8')}")

verify_cert_path = str(certs_dir / "cert.pem")

attempts = 15
last_err = None

for _ in range(attempts):
try:
httpx.get(adcm.ssl_url, verify=verify_cert_path)
except httpx.ConnectError as e:
last_err = e
sleep(0.1)
else:
return

raise RuntimeError(f"Failed to connect to HTTPS port with {attempts} attempts") from last_err


@pytest.fixture(scope="session")
def network() -> Generator[Network, None, None]:
with Network() as network:
Expand All @@ -43,13 +76,53 @@ def postgres(network: Network) -> Generator[ADCMPostgresContainer, None, None]:
yield container


@pytest.fixture(scope="session")
def ssl_certs_dir(tmp_path_factory: pytest.TempdirFactory) -> Path:
cert_dir = Path(tmp_path_factory.mktemp("cert"))

exit_code = os.system( # noqa: S605
f"openssl req -x509 -newkey rsa:4096 -keyout {cert_dir}/key.pem -out {cert_dir}/cert.pem"
' -days 365 -subj "/C=RU/ST=Moscow/L=Moscow/O=Arenadata Software LLC/OU=Release/CN=ADCM"'
f' -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" -nodes',
)
if exit_code != 0:
message = "Certificate generation failed, see logs for more details"
raise RuntimeError(message)

return cert_dir


@pytest.fixture(scope="session")
def adcm_image(network: Network, postgres: ADCMPostgresContainer, ssl_certs_dir: Path) -> str:
suffix = "".join(random.sample(string.ascii_letters, k=6)).lower()
base_repo = "hub.adsw.io/adcm/adcm"
base_tag = "develop"
new_repo = "local/adcm"
new_tag = f"{base_tag}-ssl-{suffix}"

db = DatabaseInfo(name=f"adcm_{suffix}_migration", host=postgres.name)
postgres.execute_statement(f"CREATE DATABASE {db.name} OWNER {DB_USER}")

with ADCMContainer(image=f"{base_repo}:{base_tag}", network=network, db=db, migration_mode=True) as adcm:
file = BytesIO()
with tarfile.open(mode="w:gz", fileobj=file) as tar:
tar.add(ssl_certs_dir, "")
file.seek(0)

container = adcm.get_wrapped_container()
container.put_archive("/adcm/data/conf/ssl", file.read())
container.commit(repository=new_repo, tag=new_tag)

return f"{new_repo}:{new_tag}"


@pytest.fixture(scope="function")
def adcm(network: Network, postgres: ADCMPostgresContainer) -> Generator[ADCMContainer, None, None]:
def adcm(network: Network, postgres: ADCMPostgresContainer, adcm_image: str) -> Generator[ADCMContainer, None, None]:
suffix = "".join(random.sample(string.ascii_letters, k=6)).lower()
db = DatabaseInfo(name=f"adcm_{suffix}", host=postgres.name)
postgres.execute_statement(f"CREATE DATABASE {db.name} OWNER {DB_USER}")

with ADCMContainer(image=adcm_image_name, network=network, db=db) as container:
with ADCMContainer(image=adcm_image, network=network, db=db) as container:
yield container

postgres.execute_statement(f"DROP DATABASE {db.name}")
Expand All @@ -61,11 +134,19 @@ def adcm(network: Network, postgres: ADCMPostgresContainer) -> Generator[ADCMCon


@pytest_asyncio.fixture(scope="function")
async def adcm_client(request: pytest.FixtureRequest, adcm: ADCMContainer) -> AsyncGenerator[ADCMClient, None]:
async def adcm_client(
request: pytest.FixtureRequest, adcm: ADCMContainer, ssl_certs_dir: Path
) -> AsyncGenerator[ADCMClient, None]:
credentials = Credentials(username="admin", password="admin") # noqa: S106
url = adcm.url
url = adcm.ssl_url
extra_kwargs = getattr(request, "param", {})
kwargs: dict = {"timeout": 10, "retry_interval": 1, "retry_attempts": 1} | extra_kwargs

kwargs: dict = {
"verify": str(ssl_certs_dir / "cert.pem"),
"timeout": 10,
"retry_interval": 1,
"retry_attempts": 1,
} | extra_kwargs
async with ADCMSession(url=url, credentials=credentials, **kwargs) as client:
yield client

Expand Down
11 changes: 9 additions & 2 deletions tests/integration/setup_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,16 @@ def start(self: Self) -> DbContainer:

class ADCMContainer(DockerContainer):
url: str
ssl_url: str

def __init__(self: Self, image: str, network: Network, db: DatabaseInfo) -> None:
def __init__(self: Self, image: str, network: Network, db: DatabaseInfo, *, migration_mode: bool = False) -> None:
super().__init__(image)
self._db = db
self._migration_mode = migration_mode

self.with_network(network)

self.with_env("MIGRATION_MODE", "1" if self._migration_mode else "0")
self.with_env("STATISTICS_ENABLED", "0")
self.with_env("DB_USER", DB_USER)
self.with_env("DB_PASS", DB_PASSWORD)
Expand All @@ -79,17 +82,21 @@ def __init__(self: Self, image: str, network: Network, db: DatabaseInfo) -> None
def start(self: Self) -> Self:
adcm_port = find_free_port(start=8000, end=8080)
self.with_bind_ports(8000, adcm_port)
ssl_port = find_free_port(start=8400, end=8480)
self.with_bind_ports(8443, ssl_port)

self.with_name(f"{adcm_container_name}_{adcm_port}")

super().start()

wait_container_is_ready(self)
wait_for_logs(self, "Run Nginx ...")
ready_logs = "Run Nginx ..." if not self._migration_mode else "Run main wsgi application ..."
wait_for_logs(self, ready_logs)

ip = self.get_container_host_ip()
port = self.get_exposed_port(8000)
self.url = f"http://{ip}:{port}"
self.ssl_url = f"https://{ip}:{ssl_port}"

return self

Expand Down

0 comments on commit fda0eee

Please sign in to comment.