Skip to content

Commit

Permalink
fix experiment synchronizer, run container as root, add Makefile
Browse files Browse the repository at this point in the history
gmega committed Dec 6, 2024

Verified

This commit was signed with the committer’s verified signature.
gmega Giuliano Mega
1 parent 7fe9daf commit 8605cce
Showing 7 changed files with 97 additions and 31 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.pyc
.idea
/volume/deluge*
/volume/deluge*
*.timestamp
62 changes: 62 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
SHELL := bash

.SHELLFLAGS := -eu -o pipefail -c

.PHONY: test-unit-local \
start-local-integration-harness \
stop-local-integration-harness \
test-integration-local \
test-all-local \
test-unit-docker \
test-integration-docker \
clean

# Runs the unit tests locally.
unit:
poetry run pytest -m "not integration"

# Starts the local integration harness. This is required for running pytest with the "integration" marker.
harness-start:
rm -rf ${PWD}/volume/deluge-{1,2,3}
docker compose -f docker-compose.local.yaml up

# Stops the local integration harness.
harness-stop:
docker compose -f docker-compose.local.yaml down --volumes --remove-orphans

# Runs the integration tests locally. This requires the integration harness to be running.
integration:
echo "NOTE: Make sure to have started the integration harness or this will not work"
poetry run pytest -m "integration"

tests: unit integration

docker/.lastbuilt-test.timestamp: docker/bittorrent-benchmarks.Dockerfile
docker build -t bittorrent-benchmarks:test -f ./docker/bittorrent-benchmarks.Dockerfile .
touch docker/.lastbuilt-test.timestamp

docker/.lastbuilt-release.timestamp: docker/bittorrent-benchmarks.Dockerfile
docker build -t bittorrent-benchmarks:test --build-arg BUILD_TYPE="release" \
-f ./docker/bittorrent-benchmarks.Dockerfile .
touch docker/.lastbuilt-release.timestamp

# Builds the test image required for local dockerized integration tests.
image-test: docker/.lastbuilt-test.timestamp
image-release: docker/.lastbuilt-release.timestamp

# Runs the unit tests in a docker container.
unit-docker: image-test
docker run --entrypoint poetry --rm bittorrent-benchmarks:test run pytest -m "not integration"

# Runs the integration tests in a docker container.
integration-docker: image-test
docker compose -f docker-compose.local.yaml -f docker-compose.ci.yaml down --volumes --remove-orphans
docker compose -f docker-compose.local.yaml -f docker-compose.ci.yaml up \
--abort-on-container-exit --exit-code-from test-runner

tests-docker: unit-docker integration-docker

clean:
rm -rf docker/.lastbuilt*
rm -rf volume/deluge-{1,2,3}
docker compose -f docker-compose.local.yaml -f docker-compose.ci.yaml down --volumes --rmi all --remove-orphans
32 changes: 23 additions & 9 deletions benchmarks/core/experiments/experiments.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@
import logging
from abc import ABC, abstractmethod
from collections.abc import Iterable
from typing import Optional
from time import time, sleep
from typing import Optional, List

from typing_extensions import Generic, TypeVar

@@ -44,17 +45,30 @@ def __init__(self, components: Iterable[ExperimentComponent], polling_interval:

def await_ready(self, timeout: float = 0) -> bool:
"""Awaits for all components to be ready, or until a timeout is reached."""
# TODO we should probably have per-component timeouts, or at least provide feedback
# as to what was the completion state of each component.
if not await_predicate(
lambda: all(component.is_ready() for component in self.components),
timeout=timeout,
polling_interval=self.polling_interval,
):
return False

start_time = time()
not_ready = [component for component in self.components]

logging.info(f'Awaiting for components to be ready: {self._component_names(not_ready)}')
while len(not_ready) != 0:
for component in not_ready:
if component.is_ready():
logger.info(f'Component {str(component)} is ready.')
not_ready.remove(component)

sleep(self.polling_interval)

if (timeout != 0) and (time() - start_time > timeout):
print("timeout")
logger.info(f'Some components timed out: {self._component_names(not_ready)}')
return False

return True

@staticmethod
def _component_names(components: List[ExperimentComponent]) -> str:
return ', '.join(str(component) for component in components)

def run(self, experiment: Experiment):
"""Runs the :class:`Experiment` within this :class:`ExperimentEnvironment`."""
if not self.await_ready():
8 changes: 5 additions & 3 deletions benchmarks/core/experiments/tests/test_experiments.py
Original file line number Diff line number Diff line change
@@ -44,10 +44,12 @@ def test_should_timeout_if_component_takes_too_long():
]

environment = ExperimentEnvironment(components, polling_interval=0)
assert not environment.await_ready(0.1)
assert not environment.await_ready(0.09)

assert components[0].iteration == 5
assert components[1].iteration < 3
# Because ExperimentEnvironment sweeps through the components, it will
# iterate exactly once before timing out.
assert components[0].iteration == 1
assert components[1].iteration == 1


class ExperimentThatReliesOnComponents(Experiment):
7 changes: 0 additions & 7 deletions docker-compose-up.sh

This file was deleted.

1 change: 0 additions & 1 deletion docker/bin/run-tests.sh
Original file line number Diff line number Diff line change
@@ -14,4 +14,3 @@ touch /opt/bittorrent-benchmarks/volume/.initialized
echo "Launching tests."
cd /opt/bittorrent-benchmarks
poetry run pytest --exitfirst

15 changes: 5 additions & 10 deletions docker/bittorrent-benchmarks.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
FROM python:3.12-slim

ARG UID=1000
ARG GID=1000
ARG BUILD_TYPE="test"

RUN groupadd -g ${GID} runner \
&& useradd -u ${UID} -g ${GID} -s /bin/bash -m runner
RUN mkdir /opt/bittorrent-benchmarks && chown -R runner:runner /opt/bittorrent-benchmarks
RUN pip install poetry

USER runner
RUN mkdir /opt/bittorrent-benchmarks
WORKDIR /opt/bittorrent-benchmarks

COPY --chown=runner:runner pyproject.toml poetry.lock ./
RUN if [ "$BUILD_TYPE" = "production" ]; then \
echo "Image is a production build"; \
COPY pyproject.toml poetry.lock ./
RUN if [ "$BUILD_TYPE" = "release" ]; then \
echo "Image is a release build"; \
poetry install --only main --no-root; \
else \
echo "Image is a test build"; \
poetry install --no-root; \
fi

COPY --chown=runner:runner . .
COPY . .
RUN poetry install --only main

ENTRYPOINT ["poetry", "run", "bittorrent-benchmarks", "/opt/bittorrent-benchmarks/experiments.yaml"]

0 comments on commit 8605cce

Please sign in to comment.