29
29
import tempfile
30
30
import shutil
31
31
from base64 import b64encode
32
+ from dataclasses import dataclass , field
32
33
from enum import Enum
33
34
from uuid import uuid4
34
35
from collections .abc import Mapping
@@ -61,96 +62,81 @@ class BaseExecutionMode(Enum):
61
62
GENERIC_COMMANDS = 2
62
63
63
64
65
+ @dataclass
66
+ class _ArgField (dict ):
67
+ required : bool = True
68
+
69
+ def __getattr__ (self , attr ):
70
+ return self [attr ]
71
+
72
+
73
+ @dataclass
64
74
class BaseConfig :
65
75
66
- def __init__ (self ,
67
- private_data_dir : str | None = None ,
68
- host_cwd : str | None = None ,
69
- envvars : dict [str , Any ] | None = None ,
70
- passwords = None ,
71
- settings = None ,
72
- project_dir : str | None = None ,
73
- artifact_dir : str | None = None ,
74
- fact_cache_type : str = 'jsonfile' ,
75
- fact_cache = None ,
76
- process_isolation : bool = False ,
77
- process_isolation_executable : str | None = None ,
78
- container_image : str = "" ,
79
- container_volume_mounts = None ,
80
- container_options = None ,
81
- container_workdir : str | None = None ,
82
- container_auth_data = None ,
83
- ident : str | None = None ,
84
- rotate_artifacts : int = 0 ,
85
- timeout : int | None = None ,
86
- ssh_key : str | None = None ,
87
- quiet : bool = False ,
88
- json_mode : bool = False ,
89
- check_job_event_data : bool = False ,
90
- suppress_env_files : bool = False ,
91
- keepalive_seconds : int | None = None
92
- ):
76
+ private_data_dir : str | None = field (metadata = _ArgField (), default = None )
77
+ host_cwd : str | None = field (metadata = _ArgField (), default = None )
78
+ envvars : dict [str , Any ] | None = field (metadata = _ArgField (), default = None )
79
+ passwords : dict [str , str ] | None = field (metadata = _ArgField (), default = None )
80
+ settings : dict | None = field (metadata = _ArgField (), default = None )
81
+ project_dir : str | None = field (metadata = _ArgField (), default = None )
82
+ artifact_dir : str | None = field (metadata = _ArgField (), default = None )
83
+ fact_cache_type : str = field (metadata = _ArgField (), default = 'jsonfile' )
84
+ fact_cache : str | None = field (metadata = _ArgField (), default = None )
85
+ process_isolation : bool = field (metadata = _ArgField (), default = False )
86
+ process_isolation_executable : str = field (metadata = _ArgField (), default = defaults .default_process_isolation_executable )
87
+ container_image : str = field (metadata = _ArgField (), default = "" )
88
+ container_volume_mounts : list [str ] | None = field (metadata = _ArgField (), default = None )
89
+ container_options : list [str ] | None = field (metadata = _ArgField (), default = None )
90
+ container_workdir : str | None = field (metadata = _ArgField (), default = None )
91
+ container_auth_data : dict [str , str ] | None = field (metadata = _ArgField (), default = None )
92
+ ident : str | None = field (metadata = _ArgField (), default = None )
93
+ rotate_artifacts : int = field (metadata = _ArgField (), default = 0 )
94
+ timeout : int | None = field (metadata = _ArgField (), default = None )
95
+ ssh_key : str | None = field (metadata = _ArgField (), default = None )
96
+ quiet : bool = field (metadata = _ArgField (), default = False )
97
+ json_mode : bool = field (metadata = _ArgField (), default = False )
98
+ check_job_event_data : bool = field (metadata = _ArgField (), default = False )
99
+ suppress_env_files : bool = field (metadata = _ArgField (), default = False )
100
+ keepalive_seconds : int | None = field (metadata = _ArgField (), default = None )
101
+
102
+ _CONTAINER_ENGINES = ('docker' , 'podman' )
103
+
104
+ def __post_init__ (self ) -> None :
93
105
# pylint: disable=W0613
94
106
95
- # common params
96
- self .host_cwd = host_cwd
97
- self .envvars = envvars
98
- self .ssh_key_data = ssh_key
99
107
self .command : list [str ] = []
100
-
101
- # container params
102
- self .process_isolation = process_isolation
103
- self .process_isolation_executable = process_isolation_executable or defaults .default_process_isolation_executable
104
- self .container_image = container_image
105
- self .container_volume_mounts = container_volume_mounts
106
- self .container_workdir = container_workdir
107
- self .container_auth_data = container_auth_data
108
108
self .registry_auth_path : str
109
109
self .container_name : str = "" # like other properties, not accurate until prepare is called
110
- self .container_options = container_options
111
-
112
- # runner params
113
- self .rotate_artifacts = rotate_artifacts
114
- self .quiet = quiet
115
- self .json_mode = json_mode
116
- self .passwords = passwords
117
- self .settings = settings
118
- self .timeout = timeout
119
- self .check_job_event_data = check_job_event_data
120
- self .suppress_env_files = suppress_env_files
110
+
121
111
# ignore this for now since it's worker-specific and would just trip up old runners
122
112
# self.keepalive_seconds = keepalive_seconds
123
113
124
114
# setup initial environment
125
- if private_data_dir :
126
- self .private_data_dir = os .path .abspath (private_data_dir )
115
+ if self . private_data_dir :
116
+ self .private_data_dir = os .path .abspath (self . private_data_dir )
127
117
# Note that os.makedirs, exist_ok=True is dangerous. If there's a directory writable
128
118
# by someone other than the user anywhere in the path to be created, an attacker can
129
119
# attempt to compromise the directories via a race.
130
120
os .makedirs (self .private_data_dir , exist_ok = True , mode = 0o700 )
131
121
else :
132
122
self .private_data_dir = tempfile .mkdtemp (prefix = defaults .AUTO_CREATE_NAMING , dir = defaults .AUTO_CREATE_DIR )
133
123
134
- if artifact_dir is None :
135
- artifact_dir = os .path .join (self .private_data_dir , 'artifacts' )
124
+ if self . artifact_dir is None :
125
+ self . artifact_dir = os .path .join (self .private_data_dir , 'artifacts' )
136
126
else :
137
- artifact_dir = os .path .abspath (artifact_dir )
127
+ self . artifact_dir = os .path .abspath (self . artifact_dir )
138
128
139
- if ident is None :
129
+ if self . ident is None :
140
130
self .ident = str (uuid4 ())
141
131
else :
142
- self .ident = str (ident )
132
+ self .ident = str (self . ident )
143
133
144
- self .artifact_dir = os .path .join (artifact_dir , self .ident )
134
+ self .artifact_dir = os .path .join (self . artifact_dir , self .ident )
145
135
146
- if not project_dir :
136
+ if not self . project_dir :
147
137
self .project_dir = os .path .join (self .private_data_dir , 'project' )
148
- else :
149
- self .project_dir = project_dir
150
138
151
- self .rotate_artifacts = rotate_artifacts
152
- self .fact_cache_type = fact_cache_type
153
- self .fact_cache = os .path .join (self .artifact_dir , fact_cache or 'fact_cache' ) if self .fact_cache_type == 'jsonfile' else None
139
+ self .fact_cache = os .path .join (self .artifact_dir , self .fact_cache or 'fact_cache' ) if self .fact_cache_type == 'jsonfile' else None
154
140
155
141
self .loader = ArtifactLoader (self .private_data_dir )
156
142
@@ -162,12 +148,19 @@ def __init__(self,
162
148
163
149
os .makedirs (self .artifact_dir , exist_ok = True , mode = 0o700 )
164
150
165
- _CONTAINER_ENGINES = ('docker' , 'podman' )
166
-
167
151
@property
168
152
def containerized (self ):
169
153
return self .process_isolation and self .process_isolation_executable in self ._CONTAINER_ENGINES
170
154
155
+ @property
156
+ def ssh_key_data (self ):
157
+ """ Alias for backward compatibility. """
158
+ return self .ssh_key
159
+
160
+ @ssh_key_data .setter
161
+ def ssh_key_data (self , value ):
162
+ self .ssh_key = value
163
+
171
164
def prepare_env (self , runner_mode : str = 'pexpect' ) -> None :
172
165
"""
173
166
Manages reading environment metadata files under ``private_data_dir`` and merging/updating
@@ -178,7 +171,7 @@ def prepare_env(self, runner_mode: str = 'pexpect') -> None:
178
171
if self .settings and isinstance (self .settings , dict ):
179
172
self .settings .update (self .loader .load_file ('env/settings' , Mapping )) # type: ignore
180
173
else :
181
- self .settings = self .loader .load_file ('env/settings' , Mapping )
174
+ self .settings = self .loader .load_file ('env/settings' , Mapping ) # type: ignore
182
175
except ConfigurationError :
183
176
debug ("Not loading settings" )
184
177
self .settings = {}
@@ -188,11 +181,11 @@ def prepare_env(self, runner_mode: str = 'pexpect') -> None:
188
181
if self .passwords and isinstance (self .passwords , dict ):
189
182
self .passwords .update (self .loader .load_file ('env/passwords' , Mapping )) # type: ignore
190
183
else :
191
- self .passwords = self .passwords or self .loader .load_file ('env/passwords' , Mapping )
184
+ self .passwords = self .passwords or self .loader .load_file ('env/passwords' , Mapping ) # type: ignore
192
185
except ConfigurationError :
193
186
debug ('Not loading passwords' )
194
187
195
- self .expect_passwords = {}
188
+ self .expect_passwords : dict [ Any , Any ] = {}
196
189
try :
197
190
if self .passwords :
198
191
self .expect_passwords = {
@@ -268,16 +261,16 @@ def prepare_env(self, runner_mode: str = 'pexpect') -> None:
268
261
# Still need to pass default environment to pexpect
269
262
270
263
try :
271
- if self .ssh_key_data is None :
272
- self .ssh_key_data = self .loader .load_file ('env/ssh_key' , str ) # type: ignore
264
+ if self .ssh_key is None :
265
+ self .ssh_key = self .loader .load_file ('env/ssh_key' , str ) # type: ignore
273
266
except ConfigurationError :
274
267
debug ("Not loading ssh key" )
275
- self .ssh_key_data = None
268
+ self .ssh_key = None
276
269
277
270
# write the SSH key data into a fifo read by ssh-agent
278
- if self .ssh_key_data :
271
+ if self .ssh_key :
279
272
self .ssh_key_path = os .path .join (self .artifact_dir , 'ssh_key_data' )
280
- open_fifo_write (self .ssh_key_path , self .ssh_key_data )
273
+ open_fifo_write (self .ssh_key_path , self .ssh_key )
281
274
282
275
self .suppress_output_file = self .settings .get ('suppress_output_file' , False )
283
276
self .suppress_ansible_output = self .settings .get ('suppress_ansible_output' , self .quiet )
@@ -340,7 +333,7 @@ def prepare_env(self, runner_mode: str = 'pexpect') -> None:
340
333
debug (f' { k } : { v } ' )
341
334
342
335
def handle_command_wrap (self , execution_mode : BaseExecutionMode , cmdline_args : list [str ]) -> None :
343
- if self .ssh_key_data :
336
+ if self .ssh_key :
344
337
logger .debug ('ssh key data added' )
345
338
self .command = self .wrap_args_with_ssh_agent (self .command , self .ssh_key_path )
346
339
0 commit comments