Skip to content

Commit caad616

Browse files
shawnrusaw-wfwhummer
authored andcommitted
Added @synchronized decorator to provide thread-based locking support for ssl cert creation (#1821)
1 parent 41b37c2 commit caad616

File tree

3 files changed

+36
-0
lines changed

3 files changed

+36
-0
lines changed

localstack/utils/common.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import shutil
2424
import requests
2525
import dns.resolver
26+
import functools
2627
from io import BytesIO
2728
from contextlib import closing
2829
from datetime import datetime
@@ -62,6 +63,8 @@
6263
# flag to indicate whether we've received and processed the stop signal
6364
INFRA_STOPPED = False
6465

66+
SSL_CERT_LOCK = threading.RLock()
67+
6568

6669
class CustomEncoder(json.JSONEncoder):
6770
""" Helper class to convert JSON documents with datetime, decimals, or bytes. """
@@ -290,6 +293,19 @@ def stderr(self):
290293
# UTILITY METHODS
291294
# ----------------
292295

296+
def synchronized(lock=None):
297+
"""
298+
Synchronization decorator as described in
299+
http://blog.dscpl.com.au/2014/01/the-missing-synchronized-decorator.html.
300+
"""
301+
def _decorator(wrapped):
302+
@functools.wraps(wrapped)
303+
def _wrapper(*args, **kwargs):
304+
with lock:
305+
return wrapped(*args, **kwargs)
306+
return _wrapper
307+
return _decorator
308+
293309

294310
def is_string(s, include_unicode=True, exclude_binary=False):
295311
if isinstance(s, six.binary_type) and exclude_binary:
@@ -797,6 +813,7 @@ def cleanup_resources():
797813
cleanup_threads_and_processes()
798814

799815

816+
@synchronized(lock=SSL_CERT_LOCK)
800817
def generate_ssl_cert(target_file=None, overwrite=False, random=False, return_content=False, serial_number=None):
801818
# Note: Do NOT import "OpenSSL" at the root scope
802819
# (Our test Lambdas are importing this file but don't have the module installed)

tests/unit/utils/__init__.py

Whitespace-only changes.

tests/unit/utils/test_common.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import unittest
2+
import threading
3+
from unittest.mock import MagicMock
4+
from localstack.utils.common import synchronized
5+
6+
7+
class SynchronizedTest(unittest.TestCase):
8+
9+
reallock = threading.RLock()
10+
mocklock = MagicMock(wraps=reallock)
11+
12+
@synchronized(lock=mocklock)
13+
def locked(self):
14+
pass
15+
16+
def test_synchronized_uses_with_enter_exit(self):
17+
self.locked()
18+
self.mocklock.__enter__.assert_called_with()
19+
self.mocklock.__exit__.assert_called_with(None, None, None)

0 commit comments

Comments
 (0)