splight-runner 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- splight_runner/__init__.py +3 -0
- splight_runner/api/__init__.py +0 -0
- splight_runner/api/component_reporter.py +55 -0
- splight_runner/api/logging_interceptor.py +85 -0
- splight_runner/api/settings.py +63 -0
- splight_runner/bootstrap/__init__.py +0 -0
- splight_runner/bootstrap/sitecustomize.py +39 -0
- splight_runner/commands/__init__.py +0 -0
- splight_runner/commands/admin.py +136 -0
- splight_runner/commands/execute_agent.py +43 -0
- splight_runner/commands/execute_component.py +84 -0
- splight_runner/config.py +72 -0
- splight_runner/hooks/__init__.py +150 -0
- splight_runner/hooks/constants.py +1 -0
- splight_runner/hooks/finder.py +46 -0
- splight_runner/hooks/loader.py +88 -0
- splight_runner/hooks/meta_paths.py +16 -0
- splight_runner/hooks/registry.py +39 -0
- splight_runner/hooks/utils.py +5 -0
- splight_runner/log_streamer/__init__.py +0 -0
- splight_runner/log_streamer/log_buffer.py +55 -0
- splight_runner/log_streamer/log_client.py +30 -0
- splight_runner/log_streamer/log_streamer.py +91 -0
- splight_runner/logging.py +13 -0
- splight_runner/runner.py +30 -0
- splight_runner/version.py +3 -0
- splight_runner/wrapper/__init__.py +0 -0
- splight_runner/wrapper/healthcheck.py +24 -0
- splight_runner/wrapper/hooks.py +16 -0
- splight_runner/wrapper/logging.py +27 -0
- splight_runner-1.0.0.dist-info/METADATA +127 -0
- splight_runner-1.0.0.dist-info/RECORD +34 -0
- splight_runner-1.0.0.dist-info/WHEEL +4 -0
- splight_runner-1.0.0.dist-info/entry_points.txt +3 -0
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
from splight_runner.api.settings import settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class StatusReporter:
|
|
7
|
+
"""Class responsible for updating the component status each time the
|
|
8
|
+
healthcheck method is called.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
_SPL_PREFIX = "Splight"
|
|
12
|
+
_BASE_PATH = "v2/engine/component/components"
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self, api_host: str, access_id: str, secret_key: str, component_id: str
|
|
16
|
+
):
|
|
17
|
+
self._api_host = api_host.rstrip("/")
|
|
18
|
+
self._component_id = component_id
|
|
19
|
+
self._auth_header = {
|
|
20
|
+
"Authorization": f"{self._SPL_PREFIX} {access_id} {secret_key}"
|
|
21
|
+
}
|
|
22
|
+
self._prev_status: str = "Unknown"
|
|
23
|
+
|
|
24
|
+
def report_status(self, status: str) -> None:
|
|
25
|
+
"""Updates the status of the component in the Splight Platform making
|
|
26
|
+
POST request to the API.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
status : str
|
|
31
|
+
The status of the component.
|
|
32
|
+
It can be "Running", "Stopped" or "Succeeded".
|
|
33
|
+
"""
|
|
34
|
+
if self._prev_status == status:
|
|
35
|
+
return None
|
|
36
|
+
url = f"{self._api_host}/{self._BASE_PATH}"
|
|
37
|
+
url = f"{url}/{self._component_id}/update-status/"
|
|
38
|
+
response = requests.post(
|
|
39
|
+
url, headers=self._auth_header, data={"deployment_status": status}
|
|
40
|
+
)
|
|
41
|
+
try:
|
|
42
|
+
response.raise_for_status()
|
|
43
|
+
except requests.exceptions.HTTPError as exc:
|
|
44
|
+
print("Unable to update component status")
|
|
45
|
+
print(exc)
|
|
46
|
+
self._prev_status = status
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
reporter = StatusReporter(
|
|
51
|
+
api_host=settings.splight_platform_api_host,
|
|
52
|
+
access_id=settings.access_id,
|
|
53
|
+
secret_key=settings.secret_key,
|
|
54
|
+
component_id=settings.process_id,
|
|
55
|
+
)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
from logging import LogRecord
|
|
3
|
+
|
|
4
|
+
from splight_runner.api.settings import settings
|
|
5
|
+
from splight_runner.log_streamer.log_streamer import ComponentLogsStreamer
|
|
6
|
+
from splight_runner.logging import log
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ApplicationLogInterceptor:
|
|
10
|
+
"""Class responsible for intercept logs records from the logging module
|
|
11
|
+
and append them into a queue for further processing. In particular for
|
|
12
|
+
sending the logs events to Splight GRPC API.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
host: str,
|
|
18
|
+
access_id: str,
|
|
19
|
+
secret_key: str,
|
|
20
|
+
process_id: str,
|
|
21
|
+
sender_type: str,
|
|
22
|
+
):
|
|
23
|
+
self._streamer = ComponentLogsStreamer(
|
|
24
|
+
host=host,
|
|
25
|
+
access_id=access_id,
|
|
26
|
+
secret_key=secret_key,
|
|
27
|
+
process_id=process_id,
|
|
28
|
+
)
|
|
29
|
+
self._sender_type = sender_type
|
|
30
|
+
self._streamer.start()
|
|
31
|
+
|
|
32
|
+
def save_record(self, record: LogRecord) -> None:
|
|
33
|
+
"""Saves the logging record to be sent.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
record: LogRecord
|
|
38
|
+
The log record.
|
|
39
|
+
"""
|
|
40
|
+
exc_info = None
|
|
41
|
+
try:
|
|
42
|
+
message = str(record.msg) % record.args
|
|
43
|
+
except Exception as exc:
|
|
44
|
+
log(exc)
|
|
45
|
+
message = str(record.msg)
|
|
46
|
+
|
|
47
|
+
if record.exc_info:
|
|
48
|
+
exc_info = "".join(
|
|
49
|
+
traceback.format_exception(*record.exc_info)
|
|
50
|
+
).replace('"', "'")
|
|
51
|
+
message = str(record.msg)
|
|
52
|
+
else:
|
|
53
|
+
exc_info = ""
|
|
54
|
+
|
|
55
|
+
# tags attribute is not default in logger
|
|
56
|
+
tags = None
|
|
57
|
+
if hasattr(record, "tags"):
|
|
58
|
+
tags = getattr(record, "tags")
|
|
59
|
+
event = {
|
|
60
|
+
"sender_type": self._sender_type,
|
|
61
|
+
"name": record.name,
|
|
62
|
+
"message": message,
|
|
63
|
+
"loglevel": record.levelname,
|
|
64
|
+
"filename": record.filename,
|
|
65
|
+
"traceback": exc_info,
|
|
66
|
+
"tags": tags,
|
|
67
|
+
}
|
|
68
|
+
self._streamer.insert_message(event)
|
|
69
|
+
|
|
70
|
+
def flush(self) -> None:
|
|
71
|
+
"""Flushes the log records."""
|
|
72
|
+
self._streamer.flush()
|
|
73
|
+
|
|
74
|
+
def stop(self) -> None:
|
|
75
|
+
"""Stops the thread."""
|
|
76
|
+
self._streamer.stop()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
interceptor = ApplicationLogInterceptor(
|
|
80
|
+
host=settings.splight_platform_api_host,
|
|
81
|
+
access_id=settings.access_id,
|
|
82
|
+
secret_key=settings.secret_key,
|
|
83
|
+
process_id=settings.process_id,
|
|
84
|
+
sender_type=settings.process_type,
|
|
85
|
+
)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from functools import partial
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
SECRET_DIR = "/etc/config"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MissingEnvVariable(Exception):
|
|
10
|
+
...
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_variable(var_name: str) -> str:
|
|
14
|
+
secret_file = os.path.join(SECRET_DIR, var_name)
|
|
15
|
+
if os.path.exists(secret_file):
|
|
16
|
+
with open(secret_file) as f:
|
|
17
|
+
variable = f.read().strip()
|
|
18
|
+
elif os.getenv(var_name):
|
|
19
|
+
variable = os.getenv(var_name)
|
|
20
|
+
else:
|
|
21
|
+
raise MissingEnvVariable(f"{var_name} is missing")
|
|
22
|
+
return variable
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def load_multiple_variable(var_names: List[str]) -> str:
|
|
26
|
+
value = None
|
|
27
|
+
for var_name in var_names:
|
|
28
|
+
try:
|
|
29
|
+
value = load_variable(var_name)
|
|
30
|
+
break
|
|
31
|
+
except MissingEnvVariable:
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
if value is None:
|
|
35
|
+
raise MissingEnvVariable(f"On of {var_names} is missing")
|
|
36
|
+
return value
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class SplightSettings:
|
|
41
|
+
"""Class for holding Splight Runner settings."""
|
|
42
|
+
|
|
43
|
+
access_id: str = field(
|
|
44
|
+
default_factory=partial(load_variable, "SPLIGHT_ACCESS_ID")
|
|
45
|
+
)
|
|
46
|
+
secret_key: str = field(
|
|
47
|
+
default_factory=partial(load_variable, "SPLIGHT_SECRET_KEY")
|
|
48
|
+
)
|
|
49
|
+
splight_platform_api_host: str = field(
|
|
50
|
+
default_factory=partial(load_variable, "SPLIGHT_PLATFORM_API_HOST")
|
|
51
|
+
)
|
|
52
|
+
process_id: str = field(
|
|
53
|
+
default_factory=partial(
|
|
54
|
+
load_multiple_variable,
|
|
55
|
+
["COMPONENT_ID", "COMPUTE_NODE_ID", "AGENT_ID"],
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
process_type: str = field(
|
|
59
|
+
default_factory=partial(load_variable, "PROCESS_TYPE")
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
settings = SplightSettings()
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# This file is used for configuring splight runner in runtime. In particular
|
|
2
|
+
# loads some env vars and activate hooks for sending logs.
|
|
3
|
+
# All hooks or configurations for the Splight Runner should be loaded in
|
|
4
|
+
# this file.
|
|
5
|
+
|
|
6
|
+
import ast
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
runner_active = ast.literal_eval(os.getenv("SPLIGHT_RUNNER_ACTIVE", "False"))
|
|
11
|
+
|
|
12
|
+
boot_directory = os.path.dirname(__file__)
|
|
13
|
+
root_directory = os.path.dirname(os.path.dirname(boot_directory))
|
|
14
|
+
|
|
15
|
+
path = list(sys.path)
|
|
16
|
+
|
|
17
|
+
if boot_directory in path:
|
|
18
|
+
del path[path.index(boot_directory)]
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
from importlib.machinery import PathFinder
|
|
22
|
+
|
|
23
|
+
module_spec = PathFinder.find_spec("sitecustomize", path=path)
|
|
24
|
+
except ImportError as exc:
|
|
25
|
+
sys.stdout.write("Unable to import Splight Runner sitecustomie", exc)
|
|
26
|
+
sys.stdout.flush()
|
|
27
|
+
else:
|
|
28
|
+
if module_spec is not None:
|
|
29
|
+
module_spec.loader.load_module("sitecustomize")
|
|
30
|
+
|
|
31
|
+
if runner_active:
|
|
32
|
+
do_insert = root_directory not in sys.path
|
|
33
|
+
if do_insert:
|
|
34
|
+
sys.path.insert(0, root_directory)
|
|
35
|
+
|
|
36
|
+
import splight_runner.config
|
|
37
|
+
|
|
38
|
+
if do_insert:
|
|
39
|
+
del sys.path[sys.path.index(root_directory)]
|
|
File without changes
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MissingCommandError(Exception):
|
|
7
|
+
"""Exception"""
|
|
8
|
+
|
|
9
|
+
...
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MissingArgument(Exception):
|
|
13
|
+
"""Exception raises when there is a missing argument for a command"""
|
|
14
|
+
|
|
15
|
+
...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CommandAdmin:
|
|
19
|
+
"""Class respobsible for taking registry of all the commands and callbaks"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, name: str, version: str):
|
|
22
|
+
"""Constructor
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
name : str
|
|
27
|
+
Name of the application
|
|
28
|
+
version : str
|
|
29
|
+
The application version
|
|
30
|
+
"""
|
|
31
|
+
self._name = name
|
|
32
|
+
self._version = version
|
|
33
|
+
self._commands: Dict[str, Callable] = {}
|
|
34
|
+
self._callback: Dict[str, Callable] = {}
|
|
35
|
+
|
|
36
|
+
def command(self, name: Optional[str] = None) -> Callable:
|
|
37
|
+
"""Decorator to register a command
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
name: Optional[str]
|
|
42
|
+
The name for the command
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
Callable
|
|
47
|
+
The decorated function
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def decorator(func: Callable) -> Callable:
|
|
51
|
+
func_name = name or func.__name__
|
|
52
|
+
self._commands.update({func_name: func})
|
|
53
|
+
return func
|
|
54
|
+
|
|
55
|
+
return decorator
|
|
56
|
+
|
|
57
|
+
def callback(self, name: Optional[str] = None) -> Callable:
|
|
58
|
+
"""Decorator to register a callback
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
name: Optional[str]
|
|
63
|
+
The name for the command
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
Callable
|
|
68
|
+
The decorated function
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def decorator(func: Callable) -> Callable:
|
|
72
|
+
func_name = name or func.__name__
|
|
73
|
+
self._callback.update({func_name: func})
|
|
74
|
+
return func
|
|
75
|
+
|
|
76
|
+
return decorator
|
|
77
|
+
|
|
78
|
+
def print_commands(self) -> None:
|
|
79
|
+
"""Prints the commands and callbacks"""
|
|
80
|
+
title = f"{self._name} - {self._version}\n"
|
|
81
|
+
sys.stdout.write(title)
|
|
82
|
+
sys.stdout.write("{:=<{}}\n".format("", len(title)))
|
|
83
|
+
sys.stdout.write("Commands:\n")
|
|
84
|
+
for command in self._commands:
|
|
85
|
+
sys.stdout.write(f"{command: >20}\n")
|
|
86
|
+
sys.stdout.write("Callbacks:\n")
|
|
87
|
+
for callback in self._callback:
|
|
88
|
+
name = f"--{callback}"
|
|
89
|
+
sys.stdout.write(f"{name: >20}\n")
|
|
90
|
+
|
|
91
|
+
def __call__(self, *args: Any, **kwargs: Any):
|
|
92
|
+
"""Dunder method to run a given command."""
|
|
93
|
+
if len(sys.argv) == 1:
|
|
94
|
+
raise MissingCommandError("No command provided")
|
|
95
|
+
|
|
96
|
+
command_name = sys.argv[1]
|
|
97
|
+
raw_args = sys.argv[2:]
|
|
98
|
+
if command_name.startswith("--"):
|
|
99
|
+
command = self._retrieve_callback(command_name.lstrip("--"))
|
|
100
|
+
else:
|
|
101
|
+
command = self._retrieve_command(command_name)
|
|
102
|
+
|
|
103
|
+
signature = inspect.signature(command)
|
|
104
|
+
command_args = self._parse_arguments(raw_args, signature=signature)
|
|
105
|
+
command(**command_args)
|
|
106
|
+
|
|
107
|
+
def _parse_arguments(
|
|
108
|
+
self, raw_args: List, signature: inspect.Signature
|
|
109
|
+
) -> Dict:
|
|
110
|
+
arguments = {}
|
|
111
|
+
for idx, (name, parameter) in enumerate(signature.parameters.items()):
|
|
112
|
+
try:
|
|
113
|
+
raw_value = raw_args[idx]
|
|
114
|
+
except IndexError as exc:
|
|
115
|
+
raise MissingArgument(f"Missing argument {name}") from exc
|
|
116
|
+
|
|
117
|
+
argument = (
|
|
118
|
+
raw_value.split("=")[-1] if "=" in raw_value else raw_value
|
|
119
|
+
)
|
|
120
|
+
arg_value = parameter.annotation(argument)
|
|
121
|
+
arguments.update({name: arg_value})
|
|
122
|
+
return arguments
|
|
123
|
+
|
|
124
|
+
def _retrieve_command(self, command_name: str) -> Callable:
|
|
125
|
+
try:
|
|
126
|
+
command = self._commands[command_name]
|
|
127
|
+
except KeyError as exc:
|
|
128
|
+
sys.stderr.write(f"Command {command_name} not found: {exc}")
|
|
129
|
+
return command
|
|
130
|
+
|
|
131
|
+
def _retrieve_callback(self, command_name: str) -> Callable:
|
|
132
|
+
try:
|
|
133
|
+
command = self._callback[command_name]
|
|
134
|
+
except KeyError as exc:
|
|
135
|
+
sys.stderr.write(f"Callback {command_name} not found: {exc}")
|
|
136
|
+
return command
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from splight_runner import __file__ as root_file
|
|
4
|
+
from splight_runner.api.settings import settings
|
|
5
|
+
from splight_runner.logging import log
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def execute_agent() -> None:
|
|
9
|
+
"""Executes an splight agent after configuring splight runner hooks."""
|
|
10
|
+
|
|
11
|
+
root_dir = os.path.dirname(root_file)
|
|
12
|
+
boot_dir = os.path.join(root_dir, "bootstrap")
|
|
13
|
+
|
|
14
|
+
python_path = boot_dir
|
|
15
|
+
if "PYTHONPATH" in os.environ:
|
|
16
|
+
all_paths = os.getenv("PYTHONPATH").split(os.path.pathsep)
|
|
17
|
+
if boot_dir not in all_paths:
|
|
18
|
+
all_paths.insert(0, boot_dir)
|
|
19
|
+
python_path = os.path.pathsep.join(all_paths)
|
|
20
|
+
|
|
21
|
+
log("Configuring PYTHONPATH env var")
|
|
22
|
+
os.environ["PYTHONPATH"] = python_path
|
|
23
|
+
# The following variable is used for activate hooks
|
|
24
|
+
os.environ["SPLIGHT_RUNNER_ACTIVE"] = "True"
|
|
25
|
+
|
|
26
|
+
# Configure env variables for running Splight Agent
|
|
27
|
+
if "SPLIGHT_ACCESS_ID" not in os.environ:
|
|
28
|
+
os.environ["SPLIGHT_ACCESS_ID"] = settings.access_id
|
|
29
|
+
if "SPLIGHT_SECRET_KEY" not in os.environ:
|
|
30
|
+
os.environ["SPLIGHT_SECRET_KEY"] = settings.secret_key
|
|
31
|
+
if "SPLIGHT_PLATFORM_API_HOST" not in os.environ:
|
|
32
|
+
os.environ[
|
|
33
|
+
"SPLIGHT_PLATFORM_API_HOST"
|
|
34
|
+
] = settings.splight_platform_api_host
|
|
35
|
+
if "COMPUTE_NODE_ID" not in os.environ:
|
|
36
|
+
os.environ["COMPUTE_NODE_ID"] = settings.process_id
|
|
37
|
+
|
|
38
|
+
log("Executing Splight Agent")
|
|
39
|
+
|
|
40
|
+
command = ["/bin/bash", "-c", "splight-agent"]
|
|
41
|
+
exec_path = "/bin/bash"
|
|
42
|
+
|
|
43
|
+
os.execl(exec_path, *command)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from splight_runner import __file__ as root_file
|
|
6
|
+
from splight_runner.api.settings import settings
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class InvalidComponentLanguage(Exception):
|
|
10
|
+
...
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ComponentDoesNotExists(Exception):
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def execute_component(component_file: Path, component_id: str) -> None:
|
|
18
|
+
"""Executes a component after configuring splight runner hooks.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
component_file: Path
|
|
23
|
+
The path to the main file for the component.
|
|
24
|
+
component_id: str
|
|
25
|
+
The ID for the component to be executed.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
None
|
|
30
|
+
|
|
31
|
+
Raises
|
|
32
|
+
------
|
|
33
|
+
ComponentDoesNotExists
|
|
34
|
+
If the component file does not exists.
|
|
35
|
+
InvalidComponentLanguage
|
|
36
|
+
If the component is written in a non-supported language.
|
|
37
|
+
"""
|
|
38
|
+
# TODO: Add some logs messages for debugging
|
|
39
|
+
main_file = component_file.name
|
|
40
|
+
component_path = component_file.parent.resolve()
|
|
41
|
+
if not component_file.exists():
|
|
42
|
+
raise ComponentDoesNotExists("Component does not exists")
|
|
43
|
+
if main_file.endswith(".py"):
|
|
44
|
+
base_cmd = "python3"
|
|
45
|
+
else:
|
|
46
|
+
raise InvalidComponentLanguage(
|
|
47
|
+
"Component is not written in a supported language"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
root_dir = os.path.dirname(root_file)
|
|
51
|
+
boot_dir = os.path.join(root_dir, "bootstrap")
|
|
52
|
+
|
|
53
|
+
python_path = boot_dir
|
|
54
|
+
if "PYTHONPATH" in os.environ:
|
|
55
|
+
all_paths = os.getenv("PYTHONPATH").split(os.path.pathsep)
|
|
56
|
+
if boot_dir not in all_paths:
|
|
57
|
+
all_paths.insert(0, boot_dir)
|
|
58
|
+
python_path = os.path.pathsep.join(all_paths)
|
|
59
|
+
|
|
60
|
+
os.environ["PYTHONPATH"] = python_path
|
|
61
|
+
# The following variable is used for activate hooks
|
|
62
|
+
os.environ["SPLIGHT_RUNNER_ACTIVE"] = "True"
|
|
63
|
+
|
|
64
|
+
# TODO: improve how env variables are configured
|
|
65
|
+
if "SPLIGHT_ACCESS_ID" not in os.environ:
|
|
66
|
+
os.environ["SPLIGHT_ACCESS_ID"] = settings.access_id
|
|
67
|
+
if "SPLIGHT_SECRET_KEY" not in os.environ:
|
|
68
|
+
os.environ["SPLIGHT_SECRET_KEY"] = settings.secret_key
|
|
69
|
+
if "SPLIGHT_PLATFORM_API_HOST" not in os.environ:
|
|
70
|
+
os.environ[
|
|
71
|
+
"SPLIGHT_PLATFORM_API_HOST"
|
|
72
|
+
] = settings.splight_platform_api_host
|
|
73
|
+
if "COMPONENT_ID" not in os.environ:
|
|
74
|
+
os.environ["COMPONENT_ID"] = settings.process_id
|
|
75
|
+
|
|
76
|
+
os.chdir(component_path)
|
|
77
|
+
exec_path = sys.executable
|
|
78
|
+
command = [
|
|
79
|
+
base_cmd,
|
|
80
|
+
main_file,
|
|
81
|
+
"--component-id",
|
|
82
|
+
component_id,
|
|
83
|
+
]
|
|
84
|
+
os.execl(exec_path, *command)
|
splight_runner/config.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from types import ModuleType
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
import splight_lib
|
|
6
|
+
|
|
7
|
+
SPL_LIB_INSTALLED = True
|
|
8
|
+
except Exception as exc:
|
|
9
|
+
print("Error importing splight_lib", exc)
|
|
10
|
+
SPL_LIB_INSTALLED = False
|
|
11
|
+
|
|
12
|
+
from splight_runner.hooks import copy_module, on_import, reload_module
|
|
13
|
+
from splight_runner.logging import log
|
|
14
|
+
from splight_runner.wrapper.healthcheck import healthcheck_wrapper
|
|
15
|
+
from splight_runner.wrapper.hooks import finish_execution
|
|
16
|
+
from splight_runner.wrapper.logging import call_handlers_wrapper
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@on_import("logging")
|
|
20
|
+
def on_logging_import(module: ModuleType) -> ModuleType:
|
|
21
|
+
"""Hook for modifying default behavior for the logging module.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
module: ModuleType
|
|
26
|
+
The built-in logging module
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
ModuleType: The logging module updated
|
|
31
|
+
"""
|
|
32
|
+
original = getattr(module.Logger, "callHandlers", None)
|
|
33
|
+
if original:
|
|
34
|
+
wrapped = call_handlers_wrapper(original)
|
|
35
|
+
new_module = copy_module(logging)
|
|
36
|
+
setattr(new_module.Logger, "callHandlers", wrapped)
|
|
37
|
+
return new_module
|
|
38
|
+
return module
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@on_import("splight_lib")
|
|
42
|
+
def on_splight_lib_import(module: ModuleType) -> ModuleType:
|
|
43
|
+
if hasattr(module.execution, "ExecutionClient"):
|
|
44
|
+
original = getattr(
|
|
45
|
+
module.execution.ExecutionClient, "healthcheck", None
|
|
46
|
+
)
|
|
47
|
+
engine_name = "ExecutionClient"
|
|
48
|
+
elif hasattr(module.execution, "ExecutionEngine"):
|
|
49
|
+
original = getattr(
|
|
50
|
+
module.execution.ExecutionEngine, "healthcheck", None
|
|
51
|
+
)
|
|
52
|
+
engine_name = "ExecutionEngine"
|
|
53
|
+
else:
|
|
54
|
+
log("Unable to find Execution Engine in splight_lib")
|
|
55
|
+
return module
|
|
56
|
+
if original and SPL_LIB_INSTALLED:
|
|
57
|
+
wrapped = healthcheck_wrapper(original)
|
|
58
|
+
new_module = copy_module(splight_lib)
|
|
59
|
+
engine = getattr(module.execution, engine_name)
|
|
60
|
+
setattr(engine, "healthcheck", wrapped)
|
|
61
|
+
return new_module
|
|
62
|
+
return module
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Reload logging module to update everywhere
|
|
66
|
+
reload_module(logging)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if SPL_LIB_INSTALLED:
|
|
70
|
+
reload_module(splight_lib)
|
|
71
|
+
|
|
72
|
+
finish_execution()
|