Skip to content

Commit 6b3260b

Browse files
committed
restructure project to use pyproject.toml
1 parent abcbaca commit 6b3260b

File tree

16 files changed

+504
-447
lines changed

16 files changed

+504
-447
lines changed

aws-proxy/Makefile

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,33 @@ VENV_RUN = . $(VENV_ACTIVATE)
55
TEST_PATH ?= tests
66
PIP_CMD ?= pip
77

8-
usage: ## Show this help
8+
usage: ## Show this help
99
@grep -Fh "##" $(MAKEFILE_LIST) | grep -Fv fgrep | sed -e 's/:.*##\s*/##/g' | awk -F'##' '{ printf "%-25s %s\n", $$1, $$2 }'
1010

11-
venv: $(VENV_ACTIVATE)
12-
13-
$(VENV_ACTIVATE): setup.py setup.cfg
11+
install: ## Install dependencies
1412
test -d .venv || $(VENV_BIN) .venv
15-
$(VENV_RUN); pip install --upgrade pip setuptools plux wheel
16-
$(VENV_RUN); pip install --upgrade black isort pyproject-flake8 flake8-black flake8-isort
17-
$(VENV_RUN); pip install -e .
13+
$(VENV_RUN); pip install -e .[test]
1814
touch $(VENV_DIR)/bin/activate
1915

20-
clean:
16+
clean: ## Clean up
2117
rm -rf .venv/
2218
rm -rf build/
2319
rm -rf .eggs/
2420
rm -rf *.egg-info/
2521

26-
lint:
27-
$(VENV_RUN); python -m pflake8 --show-source
28-
29-
format:
30-
$(VENV_RUN); python -m isort .; python -m black .
22+
format: ## Run ruff to format the whole codebase
23+
($(VENV_RUN); python -m ruff format .; python -m ruff check --output-format=full --fix .)
3124

32-
install: venv
33-
$(VENV_RUN); $(PIP_CMD) install -e ".[test]"
25+
lint: ## Run code linter to check code style
26+
($(VENV_RUN); python -m ruff check --output-format=full . && python -m ruff format --check .)
3427

35-
test: venv
28+
test: venv ## Run tests
3629
$(VENV_RUN); python -m pytest $(PYTEST_ARGS) $(TEST_PATH)
3730

38-
dist: venv
31+
dist: venv ## Create distribution package
3932
$(VENV_RUN); python setup.py sdist bdist_wheel
4033

41-
build: ## Build the extension
34+
build: ## Build the extension
4235
mkdir -p build
4336
cp -r setup.py setup.cfg README.md aws_proxy build/
4437
(cd build && python setup.py sdist)

aws-proxy/aws_proxy/client/auth_proxy.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
from localstack import config as localstack_config
1616
from localstack.aws.spec import load_service
1717
from localstack.config import external_service_url
18-
from localstack.constants import AWS_REGION_US_EAST_1, DOCKER_IMAGE_NAME_PRO, LOCALHOST_HOSTNAME
18+
from localstack.constants import (
19+
AWS_REGION_US_EAST_1,
20+
DOCKER_IMAGE_NAME_PRO,
21+
LOCALHOST_HOSTNAME,
22+
)
1923
from localstack.http import Request
2024
from localstack.pro.core.bootstrap.licensingv2 import (
2125
ENV_LOCALSTACK_API_KEY,
@@ -25,7 +29,10 @@
2529
from localstack.utils.bootstrap import setup_logging
2630
from localstack.utils.collections import select_attributes
2731
from localstack.utils.container_utils.container_client import PortMappings
28-
from localstack.utils.docker_utils import DOCKER_CLIENT, reserve_available_container_port
32+
from localstack.utils.docker_utils import (
33+
DOCKER_CLIENT,
34+
reserve_available_container_port,
35+
)
2936
from localstack.utils.files import new_tmp_file, save_file
3037
from localstack.utils.functions import run_safe
3138
from localstack.utils.net import get_docker_host_from_container, get_free_tcp_port
@@ -68,7 +75,9 @@ def __init__(self, config: ProxyConfig, port: int = None):
6875
def do_run(self):
6976
self.register_in_instance()
7077
bind_host = self.config.get("bind_host") or DEFAULT_BIND_HOST
71-
proxy = run_server(port=self.port, bind_addresses=[bind_host], handler=self.proxy_request)
78+
proxy = run_server(
79+
port=self.port, bind_addresses=[bind_host], handler=self.proxy_request
80+
)
7281
proxy.join()
7382

7483
def proxy_request(self, request: Request, data: bytes) -> Response:
@@ -109,7 +118,9 @@ def proxy_request(self, request: Request, data: bytes) -> Response:
109118
# adjust request dict and fix certain edge cases in the request
110119
self._adjust_request_dict(service_name, request_dict)
111120

112-
headers_truncated = {k: truncate(to_str(v)) for k, v in dict(aws_request.headers).items()}
121+
headers_truncated = {
122+
k: truncate(to_str(v)) for k, v in dict(aws_request.headers).items()
123+
}
113124
LOG.debug(
114125
"Sending request for service %s to AWS: %s %s - %s - %s",
115126
service_name,
@@ -138,7 +149,9 @@ def proxy_request(self, request: Request, data: bytes) -> Response:
138149
return response
139150
except Exception as e:
140151
if LOG.isEnabledFor(logging.DEBUG):
141-
LOG.exception("Error when making request to AWS service %s: %s", service_name, e)
152+
LOG.exception(
153+
"Error when making request to AWS service %s: %s", service_name, e
154+
)
142155
return requests_response("", status_code=400)
143156

144157
def register_in_instance(self):
@@ -224,7 +237,10 @@ def _adjust_request_dict(self, service_name: str, request_dict: Dict):
224237
body_str = run_safe(lambda: to_str(req_body)) or ""
225238

226239
# TODO: this custom fix should not be required - investigate and remove!
227-
if "<CreateBucketConfiguration" in body_str and "LocationConstraint" not in body_str:
240+
if (
241+
"<CreateBucketConfiguration" in body_str
242+
and "LocationConstraint" not in body_str
243+
):
228244
region = request_dict["context"]["client_region"]
229245
if region == AWS_REGION_US_EAST_1:
230246
request_dict["body"] = ""
@@ -238,15 +254,19 @@ def _adjust_request_dict(self, service_name: str, request_dict: Dict):
238254
account_id = self._query_account_id_from_aws()
239255
if "QueueUrl" in req_body:
240256
queue_name = req_body["QueueUrl"].split("/")[-1]
241-
req_body["QueueUrl"] = f"https://queue.amazonaws.com/{account_id}/{queue_name}"
257+
req_body["QueueUrl"] = (
258+
f"https://queue.amazonaws.com/{account_id}/{queue_name}"
259+
)
242260
if "QueueOwnerAWSAccountId" in req_body:
243261
req_body["QueueOwnerAWSAccountId"] = account_id
244262
if service_name == "sqs" and request_dict.get("url"):
245263
req_json = run_safe(lambda: json.loads(body_str)) or {}
246264
account_id = self._query_account_id_from_aws()
247265
queue_name = req_json.get("QueueName")
248266
if account_id and queue_name:
249-
request_dict["url"] = f"https://queue.amazonaws.com/{account_id}/{queue_name}"
267+
request_dict["url"] = (
268+
f"https://queue.amazonaws.com/{account_id}/{queue_name}"
269+
)
250270
req_json["QueueOwnerAWSAccountId"] = account_id
251271
request_dict["body"] = to_bytes(json.dumps(req_json))
252272

@@ -256,7 +276,9 @@ def _fix_headers(self, request: Request, service_name: str):
256276
host = request.headers.get("Host") or ""
257277
regex = r"^(https?://)?([0-9.]+|localhost)(:[0-9]+)?"
258278
if re.match(regex, host):
259-
request.headers["Host"] = re.sub(regex, rf"\1s3.{LOCALHOST_HOSTNAME}", host)
279+
request.headers["Host"] = re.sub(
280+
regex, rf"\1s3.{LOCALHOST_HOSTNAME}", host
281+
)
260282
request.headers.pop("Content-Length", None)
261283
request.headers.pop("x-localstack-request-url", None)
262284
request.headers.pop("X-Forwarded-For", None)
@@ -311,7 +333,9 @@ def start_aws_auth_proxy_in_container(
311333
# should consider building pre-baked images for the extension in the future. Also,
312334
# the new packaged CLI binary can help us gain more stability over time...
313335

314-
logging.getLogger("localstack.utils.container_utils.docker_cmd_client").setLevel(logging.INFO)
336+
logging.getLogger("localstack.utils.container_utils.docker_cmd_client").setLevel(
337+
logging.INFO
338+
)
315339
logging.getLogger("localstack.utils.docker_utils").setLevel(logging.INFO)
316340
logging.getLogger("localstack.utils.run").setLevel(logging.INFO)
317341

@@ -328,13 +352,18 @@ def start_aws_auth_proxy_in_container(
328352
image_name = DOCKER_IMAGE_NAME_PRO
329353
# add host mapping for localstack.cloud to localhost to prevent the health check from failing
330354
additional_flags = (
331-
repl_config.PROXY_DOCKER_FLAGS + " --add-host=localhost.localstack.cloud:host-gateway"
355+
repl_config.PROXY_DOCKER_FLAGS
356+
+ " --add-host=localhost.localstack.cloud:host-gateway"
332357
)
333358
DOCKER_CLIENT.create_container(
334359
image_name,
335360
name=container_name,
336361
entrypoint="",
337-
command=["bash", "-c", f"touch {CONTAINER_LOG_FILE}; tail -f {CONTAINER_LOG_FILE}"],
362+
command=[
363+
"bash",
364+
"-c",
365+
f"touch {CONTAINER_LOG_FILE}; tail -f {CONTAINER_LOG_FILE}",
366+
],
338367
ports=ports,
339368
additional_flags=additional_flags,
340369
)
@@ -388,7 +417,10 @@ def start_aws_auth_proxy_in_container(
388417
command = f"{venv_activate}; localstack aws proxy -c {CONTAINER_CONFIG_FILE} -p {port} --host 0.0.0.0 > {CONTAINER_LOG_FILE} 2>&1"
389418
if use_docker_sdk_command:
390419
DOCKER_CLIENT.exec_in_container(
391-
container_name, command=["bash", "-c", command], env_vars=env_vars, interactive=True
420+
container_name,
421+
command=["bash", "-c", command],
422+
env_vars=env_vars,
423+
interactive=True,
392424
)
393425
else:
394426
env_vars_list = []

aws-proxy/aws_proxy/client/http2_server.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ def _do_create():
106106
def _encode_headers(headers):
107107
if RETURN_CASE_SENSITIVE_HEADERS:
108108
return [(key.encode(), value.encode()) for key, value in headers.items()]
109-
return [(key.lower().encode(), value.encode()) for key, value in headers.items()]
109+
return [
110+
(key.lower().encode(), value.encode()) for key, value in headers.items()
111+
]
110112

111113
quart_asgi._encode_headers = quart_asgi.encode_headers = _encode_headers
112114
quart_app.encode_headers = quart_utils.encode_headers = _encode_headers
@@ -116,7 +118,9 @@ def build_and_validate_headers(headers):
116118
for name, value in headers:
117119
if name[0] == b":"[0]:
118120
raise ValueError("Pseudo headers are not valid")
119-
header_name = bytes(name) if RETURN_CASE_SENSITIVE_HEADERS else bytes(name).lower()
121+
header_name = (
122+
bytes(name) if RETURN_CASE_SENSITIVE_HEADERS else bytes(name).lower()
123+
)
120124
validated_headers.append((header_name.strip(), bytes(value).strip()))
121125
return validated_headers
122126

@@ -212,7 +216,9 @@ async def index(path=None):
212216
response.headers.pop("Content-Length", None)
213217
result.headers.pop("Server", None)
214218
result.headers.pop("Date", None)
215-
headers = {k: str(v).replace("\n", r"\n") for k, v in result.headers.items()}
219+
headers = {
220+
k: str(v).replace("\n", r"\n") for k, v in result.headers.items()
221+
}
216222
response.headers.update(headers)
217223
# set multi-value headers
218224
multi_value_headers = getattr(result, "multi_value_headers", {})

aws-proxy/aws_proxy/server/aws_request_forwarder.py

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ class AwsProxyHandler(Handler):
3232
# maps port numbers to proxy instances
3333
PROXY_INSTANCES: Dict[int, ProxyInstance] = {}
3434

35-
def __call__(self, chain: HandlerChain, context: RequestContext, response: Response):
35+
def __call__(
36+
self, chain: HandlerChain, context: RequestContext, response: Response
37+
):
3638
proxy = self.select_proxy(context)
3739
if not proxy:
3840
return
@@ -63,7 +65,9 @@ def select_proxy(self, context: RequestContext) -> Optional[ProxyInstance]:
6365
proxy = self.PROXY_INSTANCES[port]
6466
proxy_config = proxy.get("config") or {}
6567
services = proxy_config.get("services") or {}
66-
service_name = self._get_canonical_service_name(context.service.service_name)
68+
service_name = self._get_canonical_service_name(
69+
context.service.service_name
70+
)
6771
service_config = services.get(service_name)
6872
if not service_config:
6973
continue
@@ -100,7 +104,9 @@ def _request_matches_resource(
100104
self, context: RequestContext, resource_name_pattern: str
101105
) -> bool:
102106
try:
103-
service_name = self._get_canonical_service_name(context.service.service_name)
107+
service_name = self._get_canonical_service_name(
108+
context.service.service_name
109+
)
104110
if service_name == "s3":
105111
bucket_name = context.service_request.get("Bucket") or ""
106112
s3_bucket_arn = arns.s3_bucket_arn(bucket_name)
@@ -113,7 +119,9 @@ def _request_matches_resource(
113119
queue_name,
114120
queue_url,
115121
sqs_queue_arn(
116-
queue_name, account_id=context.account_id, region_name=context.region
122+
queue_name,
123+
account_id=context.account_id,
124+
region_name=context.region,
117125
),
118126
)
119127
for candidate in candidates:
@@ -133,12 +141,16 @@ def _request_matches_resource(
133141
) from e
134142
return True
135143

136-
def forward_request(self, context: RequestContext, proxy: ProxyInstance) -> requests.Response:
144+
def forward_request(
145+
self, context: RequestContext, proxy: ProxyInstance
146+
) -> requests.Response:
137147
"""Forward the given request to the proxy instance, and return the response."""
138148
port = proxy["port"]
139149
request = context.request
140150
target_host = get_addressable_container_host(default_local_hostname=LOCALHOST)
141-
url = f"http://{target_host}:{port}{request.path}?{to_str(request.query_string)}"
151+
url = (
152+
f"http://{target_host}:{port}{request.path}?{to_str(request.query_string)}"
153+
)
142154

143155
# inject Auth header, to ensure we're passing the right region to the proxy (e.g., for Cognito InitiateAuth)
144156
self._extract_region_from_domain(context)
@@ -156,10 +168,20 @@ def forward_request(self, context: RequestContext, proxy: ProxyInstance) -> requ
156168
data = request.form
157169
elif request.data:
158170
data = request.data
159-
LOG.debug("Forward request: %s %s - %s - %s", request.method, url, dict(headers), data)
171+
LOG.debug(
172+
"Forward request: %s %s - %s - %s",
173+
request.method,
174+
url,
175+
dict(headers),
176+
data,
177+
)
160178
# construct response
161179
result = requests.request(
162-
method=request.method, url=url, data=data, headers=dict(headers), stream=True
180+
method=request.method,
181+
url=url,
182+
data=data,
183+
headers=dict(headers),
184+
stream=True,
163185
)
164186
# TODO: ugly hack for now, simply attaching an additional attribute for raw response content
165187
result.raw_content = result.raw.read()
@@ -173,7 +195,10 @@ def forward_request(self, context: RequestContext, proxy: ProxyInstance) -> requ
173195
)
174196
except requests.exceptions.ConnectionError:
175197
# remove unreachable proxy
176-
LOG.info("Removing unreachable AWS forward proxy due to connection issue: %s", url)
198+
LOG.info(
199+
"Removing unreachable AWS forward proxy due to connection issue: %s",
200+
url,
201+
)
177202
self.PROXY_INSTANCES.pop(port, None)
178203
return result
179204

@@ -186,7 +211,10 @@ def _is_read_request(self, context: RequestContext) -> bool:
186211
if operation_name.lower().startswith(("describe", "get", "list", "query")):
187212
return True
188213
# service-specific rules
189-
if context.service.service_name == "cognito-idp" and operation_name == "InitiateAuth":
214+
if (
215+
context.service.service_name == "cognito-idp"
216+
and operation_name == "InitiateAuth"
217+
):
190218
return True
191219
if context.service.service_name == "dynamodb" and operation_name in {
192220
"Scan",

aws-proxy/aws_proxy/server/request_handler.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
LOCALHOST_HOSTNAME,
1414
)
1515
from localstack.http import Request, Response, route
16-
from localstack.utils.docker_utils import DOCKER_CLIENT, reserve_available_container_port
16+
from localstack.utils.docker_utils import (
17+
DOCKER_CLIENT,
18+
reserve_available_container_port,
19+
)
1720
from localstack.utils.files import load_file, new_tmp_file, rm_rf
1821
from localstack.utils.json import parse_json_or_yaml
1922
from localstack.utils.strings import to_str
@@ -37,7 +40,6 @@
3740

3841

3942
class RequestHandler:
40-
4143
@route(HANDLER_PATH_PROXIES, methods=["POST"])
4244
def add_proxy(self, request: Request, **kwargs):
4345
payload = _get_json(request)
@@ -54,11 +56,15 @@ def get_status(self, request: Request, **kwargs):
5456
tmp_file = new_tmp_file()
5557
container_name = containers[0]["name"]
5658
try:
57-
DOCKER_CLIENT.copy_from_container(container_name, tmp_file, CONTAINER_CONFIG_FILE)
59+
DOCKER_CLIENT.copy_from_container(
60+
container_name, tmp_file, CONTAINER_CONFIG_FILE
61+
)
5862
config = load_file(tmp_file)
5963
config = to_str(yaml.dump(json.loads(config)))
6064
except Exception as e:
61-
LOG.debug("Unable to get config from container %s: %s", container_name, e)
65+
LOG.debug(
66+
"Unable to get config from container %s: %s", container_name, e
67+
)
6268
rm_rf(tmp_file)
6369
return {"status": status, "config": config}
6470

@@ -109,7 +115,9 @@ def handle_proxies_request(request: AddProxyRequest):
109115
request["config"] = config = parse_json_or_yaml(config) or {}
110116

111117
def _start(*_):
112-
start_aws_auth_proxy_in_container(config, env_vars=env_vars, port=port, quiet=True)
118+
start_aws_auth_proxy_in_container(
119+
config, env_vars=env_vars, port=port, quiet=True
120+
)
113121

114122
start_worker_thread(_start)
115123

0 commit comments

Comments
 (0)