22import logging
33import os
44import random
5+ import re
56import shutil
67import socket
78import subprocess
@@ -78,7 +79,7 @@ def lock_acquire(self):
7879 # test if port is free
7980 s .bind (("localhost" , self .port ))
8081 # create file-lock
81- f = os .open (self .filename , os .O_CREAT | os .O_EXCL | os . O_RDWR )
82+ f = os .open (self .filename , os .O_CREAT | os .O_EXCL )
8283 self .locked = True
8384 log .debug ("Port %s used as %s was locked." , self .port , self .purpose )
8485 except FileExistsError :
@@ -92,7 +93,7 @@ def lock_acquire(self):
9293 finally :
9394 s .close ()
9495 if f :
95- f .close ()
96+ os .close (f )
9697
9798 def unlock (self ):
9899 """Delete file representing port lock."""
@@ -143,7 +144,7 @@ def port_file_locks_generator(
143144 raise IIBError (err_msg )
144145
145146
146- def get_opm_port_stacks () -> Tuple [List [List [int ]], List [str ]]:
147+ def get_opm_port_stacks (port_purposes : List [ str ] ) -> Tuple [List [List [int ]], List [str ]]:
147148 """
148149 Get stack with port numbers and list of their intended purposes.
149150
@@ -160,18 +161,20 @@ def get_opm_port_stacks() -> Tuple[List[List[int]], List[str]]:
160161 Example:
161162 ports, purposes = get_opm_port_stacks()
162163 ports contains: [[50051, 50151], [50052, 50152]]
163- purposes content: ['opm_port', 'pprof_port ']
164+ purposes content: ['opm_port', 'opm_pprof_port ']
164165
165166 In this example, ports 50051 and 50052 are port intended to use as 'opm_port' and ports 50151
166- and 50152 as 'pprof_port'.
167+ and 50152 as 'opm_pprof_port'.
168+
169+ :param list(str) port_purposes: list with port intended purposes
167170 :return: tuple with port stacks and their purposes
168171 :rtype: tuple(list(list(int)), list(str))
169172 """
170173 conf = get_worker_config ()
171- port_purposes = list (conf .iib_opm_port_ranges )
172174
173- if Version (Opm .opm_version ) < Version (conf .iib_opm_pprof_lock_required_min_version ):
174- port_purposes .remove ('pprof_port' )
175+ opm_version = Opm .get_opm_version_number ()
176+ if Version (opm_version ) < Version (conf .iib_opm_pprof_lock_required_min_version ):
177+ port_purposes .remove ('opm_pprof_port' )
175178
176179 # get port_ranges we need for the give opm_version
177180 port_ranges = [range (* conf .iib_opm_port_ranges [port_purpose ]) for port_purpose in port_purposes ]
@@ -184,60 +187,78 @@ def get_opm_port_stacks() -> Tuple[List[List[int]], List[str]]:
184187 return ports_list , port_purposes
185188
186189
187- def create_port_filelocks (func : Callable ) -> Callable :
190+ def create_port_filelocks (port_purposes : List [ str ] ) -> Callable :
188191 """
189192 Create a file-lock on random port from the configured range.
190193
191- :param function func : the function to be decorated
192- :rtype: function
193- :return: the decorated function
194+ :param List[str] port_purposes : the list of port purposes to be locked
195+ :rtype: Callable
196+ :return: the decorator function
194197 """
195198
196- @wraps (func )
197- def inner (* args , ** kwargs ):
199+ def decorator (func : Callable ) -> Callable :
200+ """
201+ Create a file-lock on random port from the configured range.
198202
199- port_stacks , port_purposes = get_opm_port_stacks ()
200- # Attempt to acquire the lock for each port in the range (shuffled order)
201- lock_success = False
203+ :param function func: the function to be decorated
204+ :rtype: function
205+ :return: the decorated function
206+ """
202207
203- # Initialize the generator
204- gen = port_file_locks_generator ( port_stacks = port_stacks , port_purposes = port_purposes )
208+ @ wraps ( func )
209+ def inner ( * args , ** kwargs ):
205210
206- # Use the function to retrieve values from the generator
207- while not lock_success :
208- new_locks = next (gen )
209- currently_active_locks = []
211+ # If we do not have any ports to lock
212+ if len (port_purposes ) == 0 :
213+ return func (* args , ** kwargs )
210214
211- try :
212- # Atomically acquire the locks for the given ports
213- for new_lock in new_locks :
214- new_lock .lock_acquire ()
215- currently_active_locks .append (new_lock )
216-
217- port_args = {
218- port_purpose : currently_active_locks [port_position ].port
219- for port_position , port_purpose in enumerate (port_purposes )
220- }
221-
222- result = func (* args , ** port_args , ** kwargs )
223- lock_success = True
224-
225- # Exception raised during execution of func()
226- except AddressAlreadyInUse :
227- lock_success = False
228- for active_lock in currently_active_locks :
229- active_lock .unlock ()
230-
231- finally :
232- # Exit loop after successful lock acquisition
233- if lock_success :
215+ port_stacks , port_purposes_updated = get_opm_port_stacks (port_purposes )
216+ # Attempt to acquire the lock for each port in the range (shuffled order)
217+ lock_success = False
218+
219+ # Initialize the generator
220+ gen = port_file_locks_generator (
221+ port_stacks = port_stacks ,
222+ port_purposes = port_purposes_updated ,
223+ )
224+
225+ # Use the function to retrieve values from the generator
226+ while not lock_success :
227+ new_locks = next (gen )
228+ currently_active_locks = []
229+
230+ try :
231+ # Atomically acquire the locks for the given ports
232+ for new_lock in new_locks :
233+ new_lock .lock_acquire ()
234+ currently_active_locks .append (new_lock )
235+
236+ port_args = {
237+ port_purpose : currently_active_locks [port_position ].port
238+ for port_position , port_purpose in enumerate (port_purposes_updated )
239+ }
240+
241+ result = func (* args , ** port_args , ** kwargs )
242+ lock_success = True
243+
244+ # Exception raised during execution of func()
245+ except AddressAlreadyInUse :
246+ lock_success = False
234247 for active_lock in currently_active_locks :
235248 active_lock .unlock ()
236- break
237249
238- return result
250+ finally :
251+ # Exit loop after successful lock acquisition
252+ if lock_success :
253+ for active_lock in currently_active_locks :
254+ active_lock .unlock ()
255+ break
256+
257+ return result
239258
240- return inner
259+ return inner
260+
261+ return decorator
241262
242263
243264def opm_serve_from_index (base_dir : str , from_index : str ) -> Tuple [int , subprocess .Popen ]:
@@ -263,11 +284,11 @@ def opm_serve_from_index(base_dir: str, from_index: str) -> Tuple[int, subproces
263284 return opm_serve (catalog_dir = catalog_dir )
264285
265286
266- @create_port_filelocks
287+ @create_port_filelocks ( port_purposes = [ "opm_port" , "opm_pprof_port" ])
267288def opm_serve (
268289 opm_port : int ,
269290 catalog_dir : str ,
270- pprof_port : Optional [int ] = None ,
291+ opm_pprof_port : Optional [int ] = None ,
271292) -> Tuple [int , subprocess .Popen ]:
272293 """
273294 Locally start OPM service, which can be communicated with using gRPC queries.
@@ -276,7 +297,7 @@ def opm_serve(
276297 binding conflicts. Resolution of port conflicts is handled in this function as well.
277298
278299 :param int opm_port: OPM port number obtained from create_port_filelock decorator
279- :param int pprof_port : Pprof opm port number obtained from create_port_filelock decorator
300+ :param int opm_pprof_port : Pprof opm port number obtained from create_port_filelock decorator
280301 :param str catalog_dir: path to file-based catalog directory that should be served.
281302 :return: tuple containing port number of the running service and the running Popen object.
282303 :rtype: (int, Popen)
@@ -288,14 +309,14 @@ def opm_serve(
288309 'serve' ,
289310 catalog_dir ,
290311 '-p' ,
291- opm_port ,
312+ str ( opm_port ) ,
292313 '-t' ,
293314 '/dev/null' ,
294315 ]
295316
296- if pprof_port :
317+ if opm_pprof_port :
297318 # by default opm uses the 127.0.0.1:6060
298- cmd .extend (["--pprof-addr" , f"127.0.0.1:{ pprof_port } " ])
319+ cmd .extend (["--pprof-addr" , f"127.0.0.1:{ str ( opm_pprof_port ) } " ])
299320
300321 cwd = os .path .abspath (os .path .join (catalog_dir , os .path .pardir ))
301322 result = (
@@ -305,11 +326,11 @@ def opm_serve(
305326 return result
306327
307328
308- @create_port_filelocks
329+ @create_port_filelocks ( port_purposes = [ "opm_port" , "opm_pprof_port" ])
309330def opm_registry_serve (
310331 opm_port : int ,
311332 db_path : str ,
312- pprof_port : Optional [int ] = None ,
333+ opm_pprof_port : Optional [int ] = None ,
313334) -> Tuple [int , subprocess .Popen ]:
314335 """
315336 Locally start OPM registry service, which can be communicated with using gRPC queries.
@@ -318,7 +339,7 @@ def opm_registry_serve(
318339 binding conflicts. Resolution of port conflicts is handled in this function as well.
319340
320341 :param int opm_port: OPM port number obtained from create_port_filelock decorator
321- :param int pprof_port : Pprof opm port number obtained from create_port_filelock decorator
342+ :param int opm_pprof_port : Pprof opm port number obtained from create_port_filelock decorator
322343 :param str db_path: path to index database containing the registry data.
323344 :return: tuple containing port number of the running service and the running Popen object.
324345 :rtype: (int, Popen)
@@ -330,15 +351,15 @@ def opm_registry_serve(
330351 'registry' ,
331352 'serve' ,
332353 '-p' ,
333- opm_port ,
354+ str ( opm_port ) ,
334355 '-d' ,
335356 db_path ,
336357 '-t' ,
337358 '/dev/null' ,
338359 ]
339- if pprof_port :
360+ if opm_pprof_port :
340361 # by default opm uses the 127.0.0.1:6060
341- cmd .extend (["--pprof-addr" , f"127.0.0.1:{ pprof_port } " ])
362+ cmd .extend (["--pprof-addr" , f"127.0.0.1:{ str ( opm_pprof_port ) } " ])
342363
343364 cwd = os .path .dirname (db_path )
344365 result = (
@@ -695,7 +716,13 @@ def insert_cache_into_dockerfile(dockerfile_path: str) -> None:
695716 verify_cache_insertion_edit_dockerfile (f .readlines ())
696717
697718
698- def generate_cache_locally (base_dir : str , fbc_dir : str , local_cache_path : str ) -> None :
719+ @create_port_filelocks (port_purposes = ["opm_pprof_port" ])
720+ def generate_cache_locally (
721+ base_dir : str ,
722+ fbc_dir : str ,
723+ local_cache_path : str ,
724+ opm_pprof_port : Optional [int ] = None ,
725+ ) -> None :
699726 """
700727 Generate the cache for the index image locally before building it.
701728
@@ -719,6 +746,10 @@ def generate_cache_locally(base_dir: str, fbc_dir: str, local_cache_path: str) -
719746 '/dev/null' ,
720747 ]
721748
749+ if opm_pprof_port :
750+ # by default opm uses the 127.0.0.1:6060
751+ cmd .extend (["--pprof-addr" , f"127.0.0.1:{ str (opm_pprof_port )} " ])
752+
722753 log .info ('Generating cache for the file-based catalog' )
723754 if os .path .exists (local_cache_path ):
724755 shutil .rmtree (local_cache_path )
@@ -1369,3 +1400,22 @@ def set_opm_version(cls, from_index: Optional[str] = None):
13691400 if index_version in opm_versions_config :
13701401 Opm .opm_version = opm_versions_config .get (index_version )
13711402 log .info ("OPM version set to %s" , Opm .opm_version )
1403+
1404+ @classmethod
1405+ def get_opm_version_number (cls ):
1406+ """
1407+ Get the opm version number to be used for the entire IIB operation.
1408+
1409+ :return: currently set-up Opm version number
1410+ :rtype: str
1411+ """
1412+ log .info ("Determining the OPM version number" )
1413+
1414+ from iib .workers .tasks .utils import run_cmd
1415+
1416+ opm_version_output = run_cmd ([Opm .opm_version , 'version' ])
1417+ match = re .search (r'OpmVersion:"v([\d.]+)"' , opm_version_output )
1418+ if match :
1419+ return match .group (1 )
1420+ else :
1421+ raise IIBError ("Opm version not found in the output of \" OPM version\" command" )
0 commit comments