Skip to content

Commit 09e7a39

Browse files
authored
Maintenance of project and LocalStack configurations
2 parents 217e206 + 14f1db3 commit 09e7a39

12 files changed

Lines changed: 211 additions & 148 deletions

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
.venv
2+
.idea
23
nosetests.xml
3-
__pycache__
4+
__pycache__
5+
localstack_utils.egg-info
6+
.ruff_cache

Makefile

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
1+
VENV_BIN ?= python3 -m venv
12
VENV_DIR ?= .venv
2-
TEST_PATH ?= ./tests
3-
PIP_CMD ?= pip
4-
NOSE_LOG_LEVEL ?= WARNING
3+
PIP_CMD ?= pip3
54

65
ifeq ($(OS), Windows_NT)
7-
VENV_RUN = . $(VENV_DIR)/Scripts/activate
6+
VENV_ACTIVATE = $(VENV_DIR)/Scripts/activate
87
else
9-
VENV_RUN = . $(VENV_DIR)/bin/activate
8+
VENV_ACTIVATE = $(VENV_DIR)/bin/activate
109
endif
1110

12-
setup-venv:
13-
(test `which virtualenv` || $(PIP_CMD) install --user virtualenv) && \
14-
(test -e $(VENV_DIR) || virtualenv $(VENV_OPTS) $(VENV_DIR))
11+
VENV_RUN = . $(VENV_ACTIVATE)
1512

16-
install-venv:
17-
make setup-venv && \
18-
test ! -e requirements.txt || ($(VENV_RUN); $(PIP_CMD) -q install -r requirements.txt)
13+
venv: $(VENV_ACTIVATE)
14+
15+
format: venv ## Run ruff and black to format the whole codebase
16+
($(VENV_RUN); python -m ruff check --show-source --fix .; python -m black .)
17+
18+
lint: venv ## Run code linter to check code style and check if formatter would make changes
19+
($(VENV_RUN); python -m ruff check --show-source . && python -m black --check .)
20+
21+
install: venv
22+
$(VENV_RUN); $(PIP_CMD) install -e .
23+
24+
test: venv ## Run tests
25+
($(VENV_RUN); python -m pytest -v --cov=plux --cov-report=term-missing --cov-report=xml tests)
1926

20-
test:
21-
($(VENV_RUN); DEBUG=$(DEBUG) PYTHONPATH=`pwd` nosetests $(NOSE_ARGS) --with-timer --logging-level=$(NOSE_LOG_LEVEL) --nocapture --no-skip --exe --cover-erase --cover-tests --cover-inclusive --cover-package=localstack --with-xunit --exclude='$(VENV_DIR).*' --ignore-files='lambda_python3.py' $(TEST_PATH))

localstack_utils/__init__.py

Whitespace-only changes.

localstack_utils/container.py

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,73 @@
33
import docker
44
from time import sleep
55

6-
LOCALSTACK_NAME = "localstack/localstack";
7-
LOCALSTACK_TAG = "latest";
8-
LOCALSTACK_PORT_EDGE = "4566";
9-
LOCALSTACK_PORT_ELASTICSEARCH = "4571";
10-
11-
MAX_PORT_CONNECTION_ATTEMPTS = 10;
12-
MAX_LOG_COLLECTION_ATTEMPTS = 120;
13-
POLL_INTERVAL = 1;
14-
NUM_LOG_LINES = 10;
15-
16-
ENV_DEBUG = "DEBUG";
17-
ENV_USE_SSL = "USE_SSL";
18-
ENV_DEBUG_DEFAULT = "1";
6+
LOCALSTACK_COMMUNITY_IMAGE_NAME = "localstack/localstack"
7+
LOCALSTACK_PRO_IMAGE_NAME = "localstack/localstack-pro"
8+
LATEST_TAG = "latest"
9+
10+
MAX_PORT_CONNECTION_ATTEMPTS = 10
11+
MAX_LOG_COLLECTION_ATTEMPTS = 120
12+
POLL_INTERVAL = 1
13+
NUM_LOG_LINES = 10
14+
15+
ENV_DEBUG = "DEBUG"
16+
ENV_USE_SSL = "USE_SSL"
17+
ENV_DEBUG_DEFAULT = "1"
1918
LOCALSTACK_EXTERNAL_HOSTNAME = "HOSTNAME_EXTERNAL"
20-
DEFAULT_CONTAINER_ID = "localstack_main"
19+
DEFAULT_CONTAINER_ID = "localstack-main"
2120

2221
DOCKER_CLIENT = docker.from_env()
2322

24-
class Container:
2523

24+
class Container:
2625
@staticmethod
27-
def create_localstack_container(external_hostname, pull_new_image, randomize_ports, image_name, image_tag, port_edge, port_elasticsearch, environment_variables, port_mappings, bind_mounts, platform):
26+
def create_localstack_container(
27+
pull_new_image: bool,
28+
image_name: str = None,
29+
image_tag: str = LATEST_TAG,
30+
gateway_listen: str = "0.0.0.0:4566",
31+
environment_variables: dict = None,
32+
bind_ports: dict = None,
33+
pro: bool = False,
34+
):
35+
environment_variables = environment_variables or {}
36+
environment_variables["GATEWAY_LISTEN"] = gateway_listen
37+
38+
image_name_or_default = image_name or (
39+
LOCALSTACK_PRO_IMAGE_NAME if pro else LOCALSTACK_COMMUNITY_IMAGE_NAME
40+
)
41+
image_exists = (
42+
True
43+
if len(DOCKER_CLIENT.images.list(name=image_name_or_default))
44+
else False
45+
)
2846

29-
environment_variables = {} if environment_variables == None else environment_variables
30-
bind_mounts = {} if bind_mounts == None else bind_mounts
31-
port_mappings = {} if port_mappings == None else port_mappings
32-
image_name_or_default = LOCALSTACK_NAME if image_name == None else image_name
33-
image_exists = True if len(DOCKER_CLIENT.images.list(name=image_name_or_default)) else False
34-
35-
fullPortEdge = {(LOCALSTACK_PORT_EDGE if port_edge == None else port_edge) : (LOCALSTACK_PORT_EDGE)}
47+
bind_ports = bind_ports or {}
48+
gateway_port = gateway_listen.split(":")[1]
49+
bind_ports.update({gateway_port: gateway_port})
3650

3751
if pull_new_image or not image_exists:
3852
logging.info("Pulling latest image")
3953
DOCKER_CLIENT.images.pull(image_name_or_default, image_tag)
40-
41-
return DOCKER_CLIENT.containers.run(image_name_or_default, ports=fullPortEdge, environment=environment_variables, detach=True)
42-
54+
55+
return DOCKER_CLIENT.containers.run(
56+
image_name_or_default,
57+
ports=bind_ports,
58+
environment=environment_variables,
59+
detach=True,
60+
)
61+
4362
@staticmethod
44-
def waitForReady(container, pattern):
45-
attemps = 0
46-
63+
def wait_for_ready(container, pattern):
64+
attempts = 0
65+
4766
while True:
48-
logs = container.logs(tail=NUM_LOG_LINES).decode('utf-8')
67+
logs = container.logs(tail=NUM_LOG_LINES).decode("utf-8")
4968
if re.search(pattern, logs):
50-
return;
51-
69+
return
70+
5271
sleep(POLL_INTERVAL)
53-
attemps += 1
72+
attempts += 1
5473

55-
if attemps >= MAX_LOG_COLLECTION_ATTEMPTS:
56-
raise "Could not find token: " +pattern.toString()+ "in logs"
74+
if attempts >= MAX_LOG_COLLECTION_ATTEMPTS:
75+
raise "Could not find token: " + pattern.toString() + "in logs"
Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import docker
44
import logging
55
from localstack_utils.container import Container
6-
from localstack_utils.localstack_docker_configuration import LocalstackDockerConfiguration
6+
from localstack_utils.localstack_docker_configuration import (
7+
LocalstackDockerConfiguration,
8+
)
79
from localstack_utils.localstack_logger import LocalstackLogger
810

911
ENV_CONFIG_USE_SSL = "USE_SSL"
@@ -12,14 +14,16 @@
1214
TMP_PATH = "/tmp/localstack"
1315
READY_TOKEN = re.compile("Ready\\.")
1416
DEFAULT_EDGE_PORT = 4566
15-
PORT_CONFIG_FILENAME = "/opt/code/localstack/.venv/lib/python3.8/site-packages/localstack_client/config.py"
17+
PORT_CONFIG_FILENAME = (
18+
"/opt/code/localstack/.venv/lib/python3.8/site-packages/localstack_client/config.py"
19+
)
1620
# DEFAULT_PORT_PATTERN = re.compile("'(\\w+)'\\Q: '{proto}://{host}:\\E(\\d+)'")
1721

1822
localstack_instance = None
19-
logging.basicConfig(level=logging.INFO, format='%(message)s')
23+
logging.basicConfig(level=logging.INFO, format="%(message)s")
24+
2025

2126
class Localstack:
22-
2327
localstack_container = None
2428
service_to_map = {}
2529
locked = False
@@ -28,62 +32,78 @@ class Localstack:
2832
def INSTANCE():
2933
return Localstack()
3034

31-
external_hostName = ''
35+
external_hostName = ""
3236

33-
def startup(self, docker_configuration) :
37+
def startup(self, docker_configuration):
3438
if self.locked:
3539
raise Exception("A docker instance is starting or already started.")
3640
self.locked = True
37-
self.external_hostname = docker_configuration.external_hostname
3841

3942
try:
4043
self.localstack_container = Container.create_localstack_container(
41-
docker_configuration.external_hostname,
4244
docker_configuration.pull_new_image,
43-
docker_configuration.randomize_ports,
4445
docker_configuration.image_name,
4546
docker_configuration.image_tag,
46-
docker_configuration.port_edge,
47-
docker_configuration.port_elastic_search,
47+
docker_configuration.gateway_listen,
4848
docker_configuration.environment_variables,
4949
docker_configuration.port_mappings,
50-
docker_configuration.bind_mounts,
51-
docker_configuration.platform
50+
docker_configuration.pro,
5251
)
5352

5453
self.setup_logger()
5554

56-
Container.waitForReady(self.localstack_container, READY_TOKEN)
55+
Container.wait_for_ready(self.localstack_container, READY_TOKEN)
5756

58-
except docker.errors.APIError as error:
57+
except docker.errors.APIError:
5958
if not docker_configuration.ignore_docker_runerrors:
6059
raise "Unable to start docker"
6160

62-
except:
61+
except Exception:
6362
raise sys.exc_info()
6463

65-
6664
def stop(self):
6765
self.localstack_container.stop()
6866

69-
7067
def setup_logger(self):
7168
localstack_logger = LocalstackLogger(self.localstack_container)
7269
localstack_logger.start()
7370

74-
def startup_localstack(port=4566, services=[], ignore_docker_errors = False):
71+
72+
def startup_localstack(
73+
image_name="",
74+
tag="",
75+
pro=False,
76+
ports=None,
77+
env_variables=None,
78+
gateway_listen="",
79+
ignore_docker_errors=False,
80+
):
7581
global localstack_instance
7682
localstack_instance = Localstack.INSTANCE()
7783
config = LocalstackDockerConfiguration()
7884

79-
if len(services):
80-
config.envirement_variables.update({'SERVICES': ','.join(services)})
81-
if port != 4566:
82-
config.port_edge = str(port)
8385
config.ignore_docker_runerrors = ignore_docker_errors
86+
if image_name:
87+
config.image_name = image_name
88+
89+
if tag:
90+
config.image_tag = tag
91+
92+
if ports:
93+
config.port_mappings = ports
94+
95+
if pro:
96+
config.pro = pro
97+
98+
if env_variables:
99+
config.environment_variables = env_variables
100+
101+
if gateway_listen:
102+
config.gateway_listen = gateway_listen
84103

85104
localstack_instance.startup(config)
86105

106+
87107
def stop_localstack():
88108
if localstack_instance:
89109
localstack_instance.stop()

localstack_utils/localstack_docker_configuration.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ class LocalstackDockerConfiguration:
55
image_tag = None
66
platform = None
77

8-
port_edge = '4566'
9-
port_elastic_search = '4571'
10-
external_hostname = 'localhost'
8+
gateway_listen = "0.0.0.0:4566"
119
environment_variables = {}
1210
port_mappings = {}
1311
bind_mounts = {}
1412

1513
initialization_token = None
1614
use_dingle_docker_container = False
1715
ignore_docker_runerrors = False
16+
pro = False
Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
import threading
22
import logging
33

4-
class LocalstackLogger():
4+
5+
class LocalstackLogger:
56
stream = None
67
log_thread = None
8+
79
def __init__(self, localsack_container) -> None:
8-
self.stream = localsack_container.logs(stream=True, follow=True)
9-
10+
self.stream = localsack_container.logs(stream=True, follow=True)
11+
1012
def start(self):
11-
self.log_thread = threading.Thread(target=log_stream,args=[self.stream]).start()
12-
13+
self.log_thread = threading.Thread(
14+
target=log_stream, args=[self.stream]
15+
).start()
16+
17+
1318
def log_stream(stream):
1419
try:
1520
while True:
16-
line = next(stream).decode("utf-8").replace("\n",'')
21+
line = next(stream).decode("utf-8").replace("\n", "")
1722
logging.info(line)
1823
except StopIteration:
1924
pass

requirements.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

setug.cfg

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
[metadata]
2+
name = localstack-utils
3+
version = attr: localstack_utils.__version__
4+
author = Cristopher Pinzon
5+
author_email = cristopher.pinzon@localstack.cloud
6+
summary = LocalStack Utils for testing
7+
description = Utils for testing with LocalStack
8+
long_description = file: README.md
9+
long_description_content_type = text/markdown; charset=UTF-8
10+
license = Apache License 2.0
11+
classifiers =
12+
Development Status :: 5 - Production/Stable
13+
License :: OSI Approved :: Apache Software License
14+
Operating System :: OS Independent
15+
Programming Language :: Python :: 3
16+
Programming Language :: Python :: 3.8
17+
Programming Language :: Python :: 3.9
18+
Programming Language :: Python :: 3.10
19+
Programming Language :: Python :: 3.11
20+
Topic :: Software Development :: Libraries
21+
Topic :: Utilities
22+
23+
[options]
24+
zip_safe = False
25+
packages = find:
26+
install_requires =
27+
docker>=6.1.1
28+
nose>=1.3.7
29+
nose-timer>=0.7.5
30+
test_requires =
31+
pytest>=7.4.0
32+
boto3>=1.26.121
33+
34+
[options.extras_require]
35+
dev =
36+
boto3>=1.26.121
37+
pytest>=7.4.0
38+
black==23.10.0
39+
ruff==0.1.0
40+
41+
[options.packages.find]
42+
exclude =
43+
tests*
44+
45+
[options.package_data]
46+
* = *.md
47+
exclude =
48+
tests
49+
tests.*

0 commit comments

Comments
 (0)