Skip to content

Commit

Permalink
Fix the code so that tox does not complain
Browse files Browse the repository at this point in the history
  • Loading branch information
t-persson committed Oct 16, 2024
1 parent 7988532 commit 04581a3
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 83 deletions.
3 changes: 0 additions & 3 deletions src/environment_provider/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@
from environment_provider.lib.database import ETCDPath
from environment_provider.lib.registry import ProviderRegistry
from environment_provider.lib.releaser import EnvironmentReleaser
from etos_lib.kubernetes.schemas import Environment as EnvironmentSchema
from etos_lib.kubernetes.schemas import Provider as ProviderSchema
from etos_lib.kubernetes import Kubernetes, Environment, Provider
from execution_space_provider import ExecutionSpaceProvider
from execution_space_provider.execution_space import ExecutionSpace
from iut_provider import IutProvider
Expand Down
71 changes: 46 additions & 25 deletions src/environment_provider/environment_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ class EnvironmentProviderNotConfigured(Exception):
"""Environment provider was not configured prior to request."""


class EnvironmentProviderError(Exception):
"""Environment provider got an error."""


class EnvironmentProvider: # pylint:disable=too-many-instance-attributes
"""Environment provider."""

Expand All @@ -75,12 +79,14 @@ class EnvironmentProvider: # pylint:disable=too-many-instance-attributes
execution_space_provider = None
testrun = None

def __init__(self, suite_runner_ids: Optional[list[str]]=None) -> None:
def __init__(self, suite_runner_ids: Optional[list[str]] = None) -> None:
"""Initialize ETOS, dataset, provider registry and splitter.
:param suite_runner_ids: IDs from the suite runner to correlate sub suites.
"""
self.etos = ETOS("ETOS Environment Provider", os.getenv("HOSTNAME", "Unknown"), "Environment Provider")
self.etos = ETOS(
"ETOS Environment Provider", os.getenv("HOSTNAME", "Unknown"), "Environment Provider"
)
self.environment_provider_config = Config(self.etos, suite_runner_ids)

FORMAT_CONFIG.identifier = self.environment_provider_config.requests[0].spec.identifier
Expand Down Expand Up @@ -221,12 +227,15 @@ def upload_sub_suite(self, sub_suite: dict) -> tuple[str, dict]:
with NamedTemporaryFile(mode="w", delete=False) as sub_suite_file:
json.dump(sub_suite, sub_suite_file)
log_area = LogArea(self.etos, sub_suite)
return log_area.upload(
sub_suite_file.name,
f"{sub_suite['name']}.json",
sub_suite["test_suite_started_id"],
sub_suite["sub_suite_id"],
), sub_suite
return (
log_area.upload(
sub_suite_file.name,
f"{sub_suite['name']}.json",
sub_suite["test_suite_started_id"],
sub_suite["sub_suite_id"],
),
sub_suite,
)
finally:
os.remove(sub_suite_file.name)

Expand Down Expand Up @@ -272,7 +281,9 @@ def recipes_from_tests(self, tests: list[Test]) -> list[dict]:
)
return recipes

def create_environment_resource(self, request: EnvironmentRequestSchema, sub_suite: dict) -> tuple[str, dict]:
def create_environment_resource(
self, request: EnvironmentRequestSchema, sub_suite: dict
) -> tuple[str, dict]:
"""Create an environment resource in Kubernetes.
:param sub_suite: Sub suite to add to Environment resource.
Expand Down Expand Up @@ -302,12 +313,15 @@ def create_environment_resource(self, request: EnvironmentRequestSchema, sub_sui
labels=labels,
ownerReferences=owners,
),
spec=EnvironmentSpec(**sub_suite.copy())
spec=EnvironmentSpec(**sub_suite.copy()),
)
environment_client = Environment(self.kubernetes, strict=True)
environment_client = Environment(self.kubernetes)
if not environment_client.create(environment):
raise RuntimeError("Failed to create the environment for an etos testrun")
return f"{os.getenv('ETOS_API')}/v1alpha/testrun/{environment_id}", environment.spec.model_dump()
return (
f"{os.getenv('ETOS_API')}/v1alpha/testrun/{environment_id}",
environment.spec.model_dump(),
)

def checkout_an_execution_space(self) -> ExecutionSpace:
"""Check out a single execution space.
Expand Down Expand Up @@ -344,26 +358,26 @@ def checkout_timeout(self) -> int:
)
return endtime

def checkout(self, request: EnvironmentRequestSchema) -> None:
def checkout(
self, request: EnvironmentRequestSchema
) -> None:
"""Checkout an environment for a test suite.
A request can have multiple environments due to IUT availability or the amount of
unique test runners in the request.
"""
# pylint:disable=too-many-statements
self.logger.info("Checkout environment for %r", request.spec.name, extra={"user_log": True})
self.new_dataset(request)
splitter = Splitter(self.etos, request.spec.splitter)

# TODO: This is a hack to make it work without too many changes to the code.
# TODO: This is a hack to make the controller environment work without too many changes
# to the original code, since we want to run them at the same time.
test_runners = {}
for test in request.spec.splitter.tests:
test_runners.setdefault(
test.execution.testRunner,
{
"docker": test.execution.testRunner,
"priority": 1,
"unsplit_recipes": []
}
{"docker": test.execution.testRunner, "priority": 1, "unsplit_recipes": []},
)
test_runners[test.execution.testRunner]["unsplit_recipes"].append(test)

Expand Down Expand Up @@ -409,7 +423,8 @@ def checkout(self, request: EnvironmentRequestSchema) -> None:
# Check out and assign IUTs to test runners.
iuts = self.iut_provider.wait_for_and_checkout_iuts(
minimum_amount=request.spec.minimumAmount,
# maximum_amount=request.spec.maximumAmount, # TODO: Total test count changes, must check
# maximum_amount=request.spec.maximumAmount,
# TODO: Total test count changes, must check
maximum_amount=self.dataset.get(
"maximum_amount",
os.getenv(
Expand All @@ -421,7 +436,7 @@ def checkout(self, request: EnvironmentRequestSchema) -> None:
splitter.assign_iuts(test_runners, iuts)
span.set_attribute(SemConvAttributes.IUT_DESCRIPTION, str(iuts))

for test_runner in test_runners.keys():
for test_runner in test_runners: # pylint:disable=consider-using-dict-items
self.dataset.add("test_runner", test_runner)

# No IUTs assigned to test runner
Expand Down Expand Up @@ -456,7 +471,9 @@ def checkout(self, request: EnvironmentRequestSchema) -> None:
request, test_runner, iut, suite, test_runners[test_runner]["priority"]
)
if self.environment_provider_config.etos_controller:
self.send_environment_events(*self.create_environment_resource(request, sub_suite))
self.send_environment_events(
*self.create_environment_resource(request, sub_suite)
)
else:
self.send_environment_events(*self.upload_sub_suite(sub_suite))

Expand Down Expand Up @@ -584,7 +601,8 @@ def configure_environment_provider(self, request: EnvironmentRequestSchema):
self._configure_iut(iut) # type: ignore
log_area = provider_client.get(request.spec.providers.logArea.id).to_dict() # type: ignore
self._configure_log_area(log_area) # type: ignore
execution_space = provider_client.get(request.spec.providers.executionSpace.id).to_dict() # type: ignore
provider_id = request.spec.providers.executionSpace.id # type: ignore
execution_space = provider_client.get(provider_id).to_dict() # type: ignore
self._configure_execution_space(execution_space) # type: ignore

def run(self) -> dict:
Expand Down Expand Up @@ -625,8 +643,11 @@ def get_environment():
try:
status = EnvironmentProvider().run()
if status.get("error") is not None:
raise Exception(status.get("error"))
result = {"conclusion": "Successful", "description": "Successfully provisioned an environment"}
raise EnvironmentProviderError(status.get("error"))
result = {
"conclusion": "Successful",
"description": "Successfully provisioned an environment",
}
with open("/dev/termination-log", "w", encoding="utf-8") as termination_log:
json.dump(result, termination_log)
except:
Expand Down
72 changes: 43 additions & 29 deletions src/environment_provider/lib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
)
from etos_lib.kubernetes import Kubernetes, EnvironmentRequest
from etos_lib.kubernetes.schemas.common import Metadata
from environment_provider.lib.registry import ProviderRegistry
from jsontas.jsontas import JsonTas
from environment_provider.lib.registry import ProviderRegistry

from .graphql import request_activity_triggered, request_artifact_created

Expand All @@ -43,7 +43,7 @@ class Config: # pylint:disable=too-many-instance-attributes
__request = None
__activity_triggered = None

def __init__(self, etos: ETOS, ids: Optional[list[str]]=None) -> None:
def __init__(self, etos: ETOS, ids: Optional[list[str]] = None) -> None:
"""Initialize with ETOS library and automatically load the config.
:param etos: ETOS library instance.
Expand Down Expand Up @@ -82,7 +82,10 @@ def load_config(self) -> None:

def __wait_for_activity(self) -> Optional[dict]:
"""Wait for activity triggered event."""
self.logger.info("Waiting for an activity triggered event %ds", self.etos.config.get("EVENT_DATA_TIMEOUT"))
self.logger.info(
"Waiting for an activity triggered event %ds",
self.etos.config.get("EVENT_DATA_TIMEOUT"),
)
timeout = time.time() + self.etos.config.get("EVENT_DATA_TIMEOUT") # type: ignore
while time.time() <= timeout:
time.sleep(1)
Expand All @@ -105,7 +108,11 @@ def __wait_for_activity(self) -> Optional[dict]:
self.logger.info("No activity triggered found yet, retrying")
continue
return edges[0]["node"]
self.logger.info("Activity triggered event not found after %ds", self.etos.config.get("EVENT_DATA_TIMEOUT"))
self.logger.info(
"Activity triggered event not found after %ds",
self.etos.config.get("EVENT_DATA_TIMEOUT"),
)
return None

# TODO: The requests method shall not return a list in the future, this is just to
# keep the changes backwards compatible.
Expand All @@ -114,14 +121,17 @@ def requests(self) -> list[EnvironmentRequestSchema]:
"""Request returns the environment request, either from Eiffel TERCC or environment."""
if self.__request is None:
if self.etos_controller:
request_client = EnvironmentRequest(self.kubernetes, strict=True)
request_client = EnvironmentRequest(self.kubernetes)
request_name = os.getenv("REQUEST")
assert request_name is not None, "Environment variable REQUEST must be set!"
self.__request = [EnvironmentRequestSchema.model_validate(
request_client.get(request_name).to_dict() # type: ignore
)]
self.__request = [
EnvironmentRequestSchema.model_validate(
request_client.get(request_name).to_dict() # type: ignore
)
]
else:
# Whenever the environment provider is run as a part of the suite runner, this variable is set.
# Whenever the environment provider is run as a part of the suite runner,
# this variable is set.
tercc = json.loads(os.getenv("TERCC", "{}"))
self.__request = self.__request_from_tercc(tercc)
return self.__request
Expand All @@ -134,14 +144,18 @@ def context(self) -> str:
"""
if self.__activity_triggered is None:
self.__activity_triggered = self.__wait_for_activity()
assert self.__activity_triggered is not None, "ActivityTriggered must exist for the environment provider"
assert (
self.__activity_triggered is not None
), "ActivityTriggered must exist for the environment provider"
try:
return self.__activity_triggered["meta"]["id"]
except KeyError:
return ""

def __request_from_tercc(self, tercc: dict) -> list[EnvironmentRequestSchema]:
assert self.ids is not None, "Suite runner IDs must be provided when running outside of controller"
assert (
self.ids is not None
), "Suite runner IDs must be provided when running outside of controller"
requests = []
response = request_artifact_created(self.etos, tercc["links"][0]["target"])
assert response is not None, "ArtifactCreated must exist for the environment provider"
Expand All @@ -160,25 +174,25 @@ def __request_from_tercc(self, tercc: dict) -> list[EnvironmentRequestSchema]:
datasets = [datasets] * len(test_suites)

for suite in test_suites:
requests.append(EnvironmentRequestSchema(
metadata=Metadata(),
spec=EnvironmentRequestSpec(
id=self.ids.pop(0),
name=suite.get("name"),
identifier=tercc["meta"]["id"],
artifact=artifact["meta"]["id"],
identity=artifact["data"]["identity"],
minimumAmount=1,
maximumAmount=10, # TODO: Ignored in environment_provider.py
image="N/A",
imagePullPolicy="N/A",
splitter=Splitter(
tests=Suite.tests_from_recipes(suite.get("recipes", []))
requests.append(
EnvironmentRequestSchema(
metadata=Metadata(),
spec=EnvironmentRequestSpec(
id=self.ids.pop(0),
name=suite.get("name"),
identifier=tercc["meta"]["id"],
artifact=artifact["meta"]["id"],
identity=artifact["data"]["identity"],
minimumAmount=1,
maximumAmount=10, # TODO: Ignored in environment_provider.py
image="N/A",
imagePullPolicy="N/A",
splitter=Splitter(tests=Suite.tests_from_recipes(suite.get("recipes", []))),
dataset=datasets.pop(0),
providers=EnvironmentProviders(),
),
dataset=datasets.pop(0),
providers=EnvironmentProviders()
)
))
)
return requests

def __test_suite(self, tercc: dict) -> list[dict]:
Expand All @@ -193,7 +207,7 @@ def __test_suite(self, tercc: dict) -> list[dict]:
raise ValueError("Only one of 'batches' or 'batchesUri' shall be set")
if batch is not None:
return batch
elif batch_uri is not None:
if batch_uri is not None:
response = self.etos.http.get(
batch_uri,
timeout=self.etos.config.get("TEST_SUITE_TIMEOUT"),
Expand Down
3 changes: 2 additions & 1 deletion src/environment_provider/lib/log_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
from requests.exceptions import ConnectionError as RequestsConnectionError
from requests.exceptions import HTTPError

# pylint:disable=too-many-arguments,too-many-positional-arguments
# pylint:disable=too-many-arguments
# pylint:disable=too-many-positional-arguments


class LogArea: # pylint:disable=too-few-public-methods
Expand Down
Loading

0 comments on commit 04581a3

Please sign in to comment.