19
19
# DEALINGS IN THE SOFTWARE.
20
20
"""Molecule Scenario Module."""
21
21
22
+ import fcntl
22
23
import fnmatch
23
24
import os
24
25
import shutil
25
26
from pathlib import Path
27
+ from time import sleep
26
28
27
29
from molecule import logger , scenarios , util
30
+ from molecule .constants import RC_TIMEOUT
28
31
29
32
LOG = logger .get_logger (__name__ )
30
33
@@ -91,6 +94,7 @@ def __init__(self, config):
91
94
:param config: An instance of a Molecule config.
92
95
:return: None
93
96
"""
97
+ self ._lock = None
94
98
self .config = config
95
99
self ._setup ()
96
100
@@ -143,22 +147,42 @@ def directory(self):
143
147
144
148
@property
145
149
def ephemeral_directory (self ):
146
- _ephemeral_directory = os .getenv ("MOLECULE_EPHEMERAL_DIRECTORY" )
147
- if _ephemeral_directory :
148
- return _ephemeral_directory
149
-
150
- project_directory = os .path .basename (self .config .project_directory )
151
-
152
- if self .config .is_parallel :
153
- project_directory = "{}-{}" .format (project_directory , self .config ._run_uuid )
154
-
155
- project_scenario_directory = os .path .join (
156
- self .config .cache_directory , project_directory , self .name
157
- )
158
-
159
- path = ephemeral_directory (project_scenario_directory )
160
-
161
- return ephemeral_directory (path )
150
+ path = os .getenv ("MOLECULE_EPHEMERAL_DIRECTORY" , None )
151
+ if not path :
152
+
153
+ project_directory = os .path .basename (self .config .project_directory )
154
+
155
+ if self .config .is_parallel :
156
+ project_directory = "{}-{}" .format (
157
+ project_directory , self .config ._run_uuid
158
+ )
159
+
160
+ project_scenario_directory = os .path .join (
161
+ self .config .cache_directory , project_directory , self .name
162
+ )
163
+
164
+ path = ephemeral_directory (project_scenario_directory )
165
+
166
+ # TODO(ssbarnea): Tune or make the retry logic configurable once we have enough data
167
+ if not self ._lock :
168
+ self ._lock = open (os .path .join (path , ".lock" ), "w" )
169
+ for i in range (1 , 5 ):
170
+ try :
171
+ fcntl .lockf (self ._lock , fcntl .LOCK_EX | fcntl .LOCK_NB )
172
+ break
173
+ except OSError :
174
+ delay = 30 * i
175
+ LOG .warning (
176
+ "Retrying to acquire lock on %s, waiting for %s seconds" ,
177
+ path ,
178
+ delay ,
179
+ )
180
+ sleep (delay )
181
+ else :
182
+ LOG .warning ("Timedout trying to acquire lock on %s" , path )
183
+ raise SystemExit (RC_TIMEOUT )
184
+
185
+ return path
162
186
163
187
@property
164
188
def inventory_directory (self ):
@@ -246,7 +270,7 @@ def _setup(self):
246
270
os .makedirs (self .inventory_directory )
247
271
248
272
249
- def ephemeral_directory (path = None ):
273
+ def ephemeral_directory (path : str = None ) -> str :
250
274
"""
251
275
Return temporary directory to be used by molecule.
252
276
@@ -256,6 +280,8 @@ def ephemeral_directory(path=None):
256
280
d = os .getenv ("MOLECULE_EPHEMERAL_DIRECTORY" )
257
281
if not d :
258
282
d = os .getenv ("XDG_CACHE_HOME" , os .path .expanduser ("~/.cache" ))
283
+ if not d :
284
+ raise RuntimeError ("Unable to determine ephemeral directory to use." )
259
285
d = os .path .abspath (os .path .join (d , path if path else "molecule" ))
260
286
261
287
if not os .path .isdir (d ):
0 commit comments