Skip to content

Commit

Permalink
Implement running in a different session via the task scheduler
Browse files Browse the repository at this point in the history
CMK-14088
  • Loading branch information
jherbel committed Aug 23, 2023
1 parent 2eaa736 commit d5dd980
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 16 deletions.
11 changes: 5 additions & 6 deletions v2/robotmk/environment.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import dataclasses
import enum
import pathlib
import subprocess
from collections.abc import Sequence


Expand Down Expand Up @@ -45,10 +44,10 @@ def wrap_for_execution(self, command: Sequence[str]) -> list[str]:
]

@staticmethod
def create_result_code(process: subprocess.CompletedProcess[str]) -> ResultCode:
if process.returncode == 0:
def create_result_code(exit_code: int) -> ResultCode:
if exit_code == 0:
return ResultCode.ALL_TESTS_PASSED
if process.returncode == 10:
if exit_code == 10:
return ResultCode.ROBOT_COMMAND_FAILED
return ResultCode.RCC_ERROR

Expand All @@ -70,7 +69,7 @@ def wrap_for_execution(self, command: Sequence[str]) -> Sequence[str]:
return command

@staticmethod
def create_result_code(process: subprocess.CompletedProcess[str]) -> ResultCode:
if process.returncode == 0:
def create_result_code(exit_code: int) -> ResultCode:
if exit_code == 0:
return ResultCode.ALL_TESTS_PASSED
return ResultCode.ROBOT_COMMAND_FAILED
24 changes: 17 additions & 7 deletions v2/robotmk/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,26 @@

from robotmk.environment import RCCEnvironment, ResultCode, RobotEnvironment
from robotmk.runner import Attempt, RetrySpec, RetryStrategy, Variant, create_attempts
from robotmk.session import CurrentSession
from robotmk.session import CurrentSession, UserSession


class _RCC(BaseModel, frozen=True):
binary: pathlib.Path
robot_yaml: pathlib.Path


class _UserSession(BaseModel, frozen=True):
user_name: str


class _SuiteConfig(BaseModel, frozen=True): # pylint: disable=too-few-public-methods
execution_interval_seconds: int
robot_target: pathlib.Path
working_directory: pathlib.Path
variants: Sequence[Variant]
retry_strategy: RetryStrategy
env: _RCC | None
session: _UserSession | None


class _SchedulerConfig(BaseModel, frozen=True):
Expand Down Expand Up @@ -65,8 +70,14 @@ def _environment(
def _session(
suite_name: str,
suite_config: _SuiteConfig,
) -> CurrentSession:
return CurrentSession(environment=_environment(suite_name, suite_config.env))
) -> CurrentSession | UserSession:
environment = _environment(suite_name, suite_config.env)
if suite_config.session:
return UserSession(
user_name=suite_config.session.user_name,
environment=environment,
)
return CurrentSession(environment=environment)


class _SuiteRetryRunner: # pylint: disable=too-few-public-methods
Expand All @@ -76,15 +87,14 @@ def __init__(self, suite_name: str, suite_config: _SuiteConfig) -> None:
self._final_outputs: list[pathlib.Path] = []

def __call__(self) -> None:
self._prepare_run()

retry_spec = RetrySpec(
id_=uuid4(),
robot_target=self._config.robot_target,
working_directory=self._config.working_directory,
variants=self._config.variants,
strategy=self._config.retry_strategy,
)
self._prepare_run(retry_spec.output_directory())

outputs = self._run_attempts_until_successful(create_attempts(retry_spec))

Expand All @@ -96,8 +106,8 @@ def __call__(self) -> None:
rebot(*outputs, output=final_output, report=None, log=None)
self._final_outputs.append(final_output)

def _prepare_run(self) -> None:
self._config.working_directory.mkdir(parents=True, exist_ok=True)
def _prepare_run(self, output_dir: pathlib.Path) -> None:
output_dir.mkdir(parents=True, exist_ok=True)
if (build_command := self._session.environment.build_command()) is not None:
_process = subprocess.run(build_command, check=True)

Expand Down
109 changes: 106 additions & 3 deletions v2/robotmk/session.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import subprocess
import time
from collections.abc import Iterable
from dataclasses import dataclass
from datetime import datetime, timedelta
from pathlib import Path

from .environment import RCCEnvironment, ResultCode, RobotEnvironment
from .runner import Attempt
from robotmk.environment import RCCEnvironment, ResultCode, RobotEnvironment
from robotmk.runner import Attempt


@dataclass(frozen=True)
Expand All @@ -15,5 +19,104 @@ def run(self, attempt: Attempt) -> ResultCode:
self.environment.wrap_for_execution(attempt.command()),
check=False,
encoding="utf-8",
)
).returncode
)


@dataclass(frozen=True)
class UserSession:
user_name: str
environment: RCCEnvironment | RobotEnvironment

def run(self, attempt: Attempt) -> ResultCode:
# NOTE: The .bat-suffix is important! Without, schtasks doesn't know how to run this.
script_path = attempt.output_directory / f"{attempt.index}_execute.bat"
exit_code_path = attempt.output_directory / f"{attempt.index}_exit_code"
script_path.write_text(
self._build_task_script(
cmd=self.environment.wrap_for_execution(attempt.command()),
exit_code_path=exit_code_path,
),
encoding="utf-8",
)
task_name = f"robotmk-{attempt.id_}-{attempt.index}"

subprocess.run(
[
"schtasks",
"/create",
"/tn",
task_name,
"/tr",
script_path,
"/sc",
"ONCE",
"/ru",
self.user_name,
"/it",
"/rl",
"LIMITED",
"/st",
(datetime.now() - timedelta(seconds=60)).strftime("%H:%M"),
"/f",
],
check=True,
)

subprocess.run(
[
"schtasks",
"/run",
"/tn",
task_name,
],
check=True,
)

while self._task_is_running(task_name):
time.sleep(10)

subprocess.run(
[
"schtasks",
"/delete",
"/tn",
task_name,
"/f",
],
check=True,
)

return self.environment.create_result_code(
int(exit_code_path.read_text(encoding="utf-8").split()[0])
)

@staticmethod
def _task_is_running(task_name: str) -> bool:
return (
"Running"
in subprocess.run(
[
"schtasks",
"/query",
"/tn",
task_name,
"/fo",
"CSV",
"/nh",
],
check=True,
capture_output=True,
encoding="utf-8",
).stdout
)

@staticmethod
def _build_task_script(*, cmd: Iterable[str], exit_code_path: Path) -> str:
return "\n".join(
[
"@echo off",
" ".join(cmd),
f"echo %errorlevel% >{exit_code_path}",
]
)

0 comments on commit d5dd980

Please sign in to comment.