Skip to content

Commit 15fe5e5

Browse files
committed
Have Transmitter get values from RunnerConfig
1 parent 1121854 commit 15fe5e5

File tree

7 files changed

+117
-67
lines changed

7 files changed

+117
-67
lines changed

src/ansible_runner/config/_base.py

+20-14
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class BaseExecutionMode(Enum):
6464

6565
# Metadata string values
6666
class MetaValues(Enum):
67-
STREAMABLE = 'streamable'
67+
TRANSMIT = 'transmit'
6868

6969

7070
@dataclass
@@ -82,38 +82,38 @@ class BaseConfig:
8282
# No other config objects make use of positional parameters, so this should be fine.
8383
#
8484
# Example use case: RunnerConfig("/tmp/demo", playbook="main.yml", ...)
85-
private_data_dir: str | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
85+
private_data_dir: str | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
8686

87-
artifact_dir: str | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
87+
artifact_dir: str | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
8888
check_job_event_data: bool = False
8989
container_auth_data: dict[str, str] | None = None
90-
container_image: str = ""
90+
container_image: str | None = None
9191
container_options: list[str] | None = None
9292
container_volume_mounts: list[str] | None = None
9393
container_workdir: str | None = None
9494
envvars: dict[str, Any] | None = None
95-
fact_cache: str | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
95+
fact_cache: str | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
9696
fact_cache_type: str = 'jsonfile'
9797
host_cwd: str | None = None
98-
ident: str | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
98+
ident: str | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
9999
json_mode: bool = False
100-
keepalive_seconds: int | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
100+
keepalive_seconds: int | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
101101
passwords: dict[str, str] | None = None
102102
process_isolation: bool = False
103103
process_isolation_executable: str = defaults.default_process_isolation_executable
104-
project_dir: str | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
104+
project_dir: str | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
105105
quiet: bool = False
106106
rotate_artifacts: int = 0
107107
settings: dict | None = None
108108
ssh_key: str | None = None
109109
suppress_env_files: bool = False
110110
timeout: int | None = None
111111

112-
event_handler: Callable[[dict], None] | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
113-
status_handler: Callable[[dict, BaseConfig], bool] | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
114-
artifacts_handler: Callable[[str], None] | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
115-
cancel_callback: Callable[[], bool] | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
116-
finished_callback: Callable[[BaseConfig], None] | None = field(metadata={MetaValues.STREAMABLE: False}, default=None)
112+
event_handler: Callable[[dict], None] | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
113+
status_handler: Callable[[dict, BaseConfig], bool] | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
114+
artifacts_handler: Callable[[str], None] | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
115+
cancel_callback: Callable[[], bool] | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
116+
finished_callback: Callable[[BaseConfig], None] | None = field(metadata={MetaValues.TRANSMIT: False}, default=None)
117117

118118
_CONTAINER_ENGINES = ('docker', 'podman')
119119

@@ -123,6 +123,8 @@ def __post_init__(self) -> None:
123123
self.command: list[str] = []
124124
self.registry_auth_path: str
125125
self.container_name: str = "" # like other properties, not accurate until prepare is called
126+
if self.container_image is None:
127+
self.container_image = ''
126128

127129
# ignore this for now since it's worker-specific and would just trip up old runners
128130
# self.keepalive_seconds = keepalive_seconds
@@ -147,7 +149,9 @@ def __post_init__(self) -> None:
147149

148150
if self.ident is None:
149151
self.ident = str(uuid4())
152+
self.ident_set_by_user = False
150153
else:
154+
self.ident_set_by_user = True
151155
self.ident = str(self.ident)
152156

153157
self.artifact_dir = os.path.join(self.artifact_dir, self.ident)
@@ -185,7 +189,6 @@ def prepare_env(self, runner_mode: str = 'pexpect') -> None:
185189
Manages reading environment metadata files under ``private_data_dir`` and merging/updating
186190
with existing values so the :py:class:`ansible_runner.runner.Runner` object can read and use them easily
187191
"""
188-
189192
if self.ident is None:
190193
raise ConfigurationError("ident value cannot be None")
191194
if self.artifact_dir is None:
@@ -520,6 +523,9 @@ def wrap_args_for_containerization(self,
520523
if self.private_data_dir is None:
521524
raise ConfigurationError("private_data_dir value cannot be None")
522525

526+
if self.container_image is None:
527+
raise ConfigurationError("container_image value cannot be None")
528+
523529
new_args = [self.process_isolation_executable]
524530
new_args.extend(['run', '--rm'])
525531

src/ansible_runner/config/runner.py

+28-4
Original file line numberDiff line numberDiff line change
@@ -152,18 +152,42 @@ def extra_vars(self, value):
152152
def streamable_attributes(self) -> dict[str, Any]:
153153
"""Get the set of streamable attributes that have a value that is different from the default.
154154
155-
The field metadata indicates if the attribute is streamable. By default, an attribute
155+
The field metadata indicates if the attribute is streamable from Transmit. By default, an attribute
156156
is considered streamable (must be explicitly disabled).
157157
158158
:return: A dict of attribute names and their values.
159159
"""
160160
retval = {}
161161
for field_obj in fields(self):
162-
if field_obj.metadata and not field_obj.metadata.get(MetaValues.STREAMABLE, True):
162+
if field_obj.metadata and not field_obj.metadata.get(MetaValues.TRANSMIT, True):
163163
continue
164164
current_value = getattr(self, field_obj.name)
165-
if not field_obj.default == current_value:
166-
retval[field_obj.name] = current_value
165+
166+
if field_obj.default == current_value:
167+
continue
168+
169+
# Treat an empty current value (e.g., {} or "") as the same as a default of None to prevent
170+
# streaming unnecessary empty values.
171+
if field_obj.default is None and current_value in ({}, "", []):
172+
continue
173+
174+
retval[field_obj.name] = current_value
175+
176+
return retval
177+
178+
def all_non_default_attributes(self) -> dict[str, Any]:
179+
"""Get all values that have been set differently from their default values.
180+
181+
:return: A dict of attribute names and their values.
182+
"""
183+
retval = {}
184+
for field_obj in fields(self):
185+
current_value = getattr(self, field_obj.name)
186+
if field_obj.default == current_value:
187+
continue
188+
if field_obj.default is None and current_value in ({}, "", []):
189+
continue
190+
retval[field_obj.name] = current_value
167191
return retval
168192

169193
def prepare(self):

src/ansible_runner/interface.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import sys
2626
import threading
2727
import logging
28-
from dataclasses import asdict
2928

3029
from ansible_runner import output
3130
from ansible_runner._internal._dump_artifacts import dump_artifacts
@@ -90,18 +89,16 @@ def init_runner(
9089
config.cancel_callback = signal_handler()
9190

9291
if streamer == 'transmit':
93-
kwargs = asdict(config)
94-
stream_transmitter = Transmitter(only_transmit_kwargs, _output=_output, **kwargs)
92+
stream_transmitter = Transmitter(config, only_transmit_kwargs, _output=_output)
9593
return stream_transmitter
9694

9795
if streamer == 'worker':
98-
kwargs = asdict(config)
96+
kwargs = config.all_non_default_attributes()
9997
stream_worker = Worker(_input=_input, _output=_output, **kwargs)
10098
return stream_worker
10199

102100
if streamer == 'process':
103-
kwargs = asdict(config)
104-
stream_processor = Processor(_input=_input, **kwargs)
101+
stream_processor = Processor(config, _input=_input)
105102
return stream_processor
106103

107104
if config.process_isolation:

src/ansible_runner/streaming.py

+19-27
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from typing import BinaryIO
1616

1717
import ansible_runner
18+
from ansible_runner.config.runner import RunnerConfig
1819
from ansible_runner.exceptions import ConfigurationError
1920
from ansible_runner.loader import ArtifactLoader
2021
import ansible_runner.plugins
@@ -38,16 +39,14 @@ def __init__(self, settings):
3839

3940

4041
class Transmitter:
41-
def __init__(self, only_transmit_kwargs: bool, _output: BinaryIO | None, **kwargs):
42+
def __init__(self, config: RunnerConfig, only_transmit_kwargs: bool = False, _output: BinaryIO | None = None):
4243
if _output is None:
4344
_output = sys.stdout.buffer
4445
self._output = _output
45-
self.private_data_dir = os.path.abspath(kwargs['private_data_dir'])
46+
self.private_data_dir = os.path.abspath(config.private_data_dir) if config.private_data_dir else ""
4647
self.only_transmit_kwargs = only_transmit_kwargs
47-
if 'keepalive_seconds' in kwargs:
48-
kwargs.pop('keepalive_seconds') # don't confuse older runners with this Worker-only arg
4948

50-
self.kwargs = kwargs
49+
self.kwargs = config.streamable_attributes()
5150

5251
self.status = "unstarted"
5352
self.rc = None
@@ -251,43 +250,36 @@ def finished_callback(self, runner_obj):
251250

252251

253252
class Processor:
254-
def __init__(self, _input=None, status_handler=None, event_handler=None,
255-
artifacts_handler=None, cancel_callback=None, finished_callback=None, **kwargs):
253+
def __init__(self, config: RunnerConfig, _input: BinaryIO | None = None):
256254
if _input is None:
257255
_input = sys.stdin.buffer
258256
self._input = _input
259257

260-
self.quiet = kwargs.get('quiet')
258+
self.quiet = config.quiet
261259

262-
private_data_dir = kwargs.get('private_data_dir')
263-
if private_data_dir is None:
264-
private_data_dir = tempfile.mkdtemp()
265-
self.private_data_dir = private_data_dir
260+
self.private_data_dir: str = config.private_data_dir or ''
266261
self._loader = ArtifactLoader(self.private_data_dir)
267262

268-
settings = kwargs.get('settings')
263+
settings = config.settings
269264
if settings is None:
270265
try:
271-
settings = self._loader.load_file('env/settings', Mapping)
266+
settings = self._loader.load_file('env/settings', Mapping) # type: ignore
272267
except ConfigurationError:
273268
settings = {}
274269
self.config = MockConfig(settings)
275270

276-
if kwargs.get('artifact_dir'):
277-
self.artifact_dir = os.path.abspath(kwargs.get('artifact_dir'))
278-
else:
279-
project_artifacts = os.path.abspath(os.path.join(self.private_data_dir, 'artifacts'))
280-
if ident := kwargs.get('ident'):
281-
self.artifact_dir = os.path.join(project_artifacts, str(ident))
282-
else:
283-
self.artifact_dir = project_artifacts
271+
self.artifact_dir = config.artifact_dir
272+
if self.artifact_dir and not config.ident_set_by_user:
273+
# If an ident value was not explicitly supplied, for some reason, we don't bother with
274+
# using a subdir named with the ident value.
275+
self.artifact_dir, _ = os.path.split(self.artifact_dir)
284276

285-
self.status_handler = status_handler
286-
self.event_handler = event_handler
287-
self.artifacts_handler = artifacts_handler
277+
self.status_handler = config.status_handler
278+
self.event_handler = config.event_handler
279+
self.artifacts_handler = config.artifacts_handler
288280

289-
self.cancel_callback = cancel_callback # FIXME: unused
290-
self.finished_callback = finished_callback
281+
self.cancel_callback = config.cancel_callback # FIXME: unused
282+
self.finished_callback = config.finished_callback
291283

292284
self.status = "unstarted"
293285
self.rc = None

0 commit comments

Comments
 (0)