Skip to content

Commit ce0f939

Browse files
authored
refactor community ext imports (localstack#7693)
1 parent 9c59e2b commit ce0f939

File tree

15 files changed

+189
-168
lines changed

15 files changed

+189
-168
lines changed

localstack/aws/app.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from localstack.aws.handlers.metric_handler import MetricHandler
33
from localstack.aws.handlers.service_plugin import ServiceLoader
44
from localstack.services.plugins import SERVICE_PLUGINS, ServiceManager, ServicePluginManager
5+
from localstack.utils.ssl import create_ssl_cert, install_predefined_cert_if_available
56

67
from .gateway import Gateway
78
from .handlers.fallback import EmptyResponseHandler
@@ -86,13 +87,8 @@ def main():
8687
setup_logging()
8788

8889
if use_ssl:
89-
from localstack.services.generic_proxy import (
90-
GenericProxy,
91-
install_predefined_cert_if_available,
92-
)
93-
9490
install_predefined_cert_if_available()
95-
_, cert_file_name, key_file_name = GenericProxy.create_ssl_cert(serial_number=port)
91+
_, cert_file_name, key_file_name = create_ssl_cert(serial_number=port)
9692
ssl_creds = (cert_file_name, key_file_name)
9793
else:
9894
ssl_creds = None

localstack/cli/localstack.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,16 @@ def cmd_config_validate(file):
256256
@publish_invocation
257257
def cmd_config_show(format):
258258
# TODO: parse values from potential docker-compose file?
259+
assert config
259260

260-
from localstack_ext import config as ext_config
261+
try:
262+
# only load the ext config if it's available
263+
from localstack_ext import config as ext_config
261264

262-
assert config
263-
assert ext_config
265+
assert ext_config
266+
except ImportError:
267+
# the ext package is not available
268+
return None
264269

265270
if format == "table":
266271
print_config_table()

localstack/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
# URL of localstack's artifacts repository on GitHub
3737
ARTIFACTS_REPO = "https://github.com/localstack/localstack-artifacts"
3838

39+
# Download URLs
40+
SSL_CERT_URL = f"{ARTIFACTS_REPO}/raw/master/local-certs/server.key"
41+
SSL_CERT_URL_FALLBACK = "{api_endpoint}/proxy/localstack.cert.key"
42+
3943
# map of default service APIs and ports to be spun up (fetch map from localstack_client)
4044
DEFAULT_SERVICE_PORTS = localstack_client.config.get_service_ports()
4145

localstack/http/hypercorn.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
from localstack.aws.handlers.proxy import ProxyHandler
1111
from localstack.aws.serving.asgi import AsgiGateway
1212
from localstack.logging.setup import setup_hypercorn_logger
13-
from localstack.services.generic_proxy import GenericProxy, install_predefined_cert_if_available
1413
from localstack.utils.collections import ensure_list
1514
from localstack.utils.serving import Server
15+
from localstack.utils.ssl import create_ssl_cert, install_predefined_cert_if_available
1616

1717

1818
class HypercornServer(Server):
@@ -98,7 +98,7 @@ def __init__(
9898

9999
if use_ssl:
100100
install_predefined_cert_if_available()
101-
_, cert_file_name, key_file_name = GenericProxy.create_ssl_cert(serial_number=port)
101+
_, cert_file_name, key_file_name = create_ssl_cert(serial_number=port)
102102
config.certfile = cert_file_name
103103
config.keyfile = key_file_name
104104

localstack/services/awslambda/lambda_api.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from urllib.parse import urlparse
2222

2323
from flask import Flask, Response, jsonify, request
24+
from flask_cors import CORS
2425

2526
from localstack import config, constants
2627
from localstack.aws.accounts import get_aws_account_id
@@ -68,8 +69,10 @@
6869
from localstack.utils.json import json_safe
6970
from localstack.utils.patch import patch
7071
from localstack.utils.run import run, run_for_max_seconds
72+
from localstack.utils.ssl import create_ssl_cert
7173
from localstack.utils.strings import long_uid, md5, short_uid, to_bytes, to_str
7274
from localstack.utils.sync import synchronized
75+
from localstack.utils.threads import start_thread
7376
from localstack.utils.time import (
7477
TIMESTAMP_FORMAT_MICROS,
7578
TIMESTAMP_READABLE_FORMAT,
@@ -2412,8 +2415,6 @@ def validate_lambda_config():
24122415

24132416
def serve(port):
24142417
try:
2415-
from localstack.services import generic_proxy # moved here to fix circular import errors
2416-
24172418
# initialize the Lambda executor
24182419
LAMBDA_EXECUTOR.startup()
24192420
# print warnings for potentially incorrect config options
@@ -2422,12 +2423,44 @@ def serve(port):
24222423
# initialize/import plugins - TODO find better place to import plugins! (to be integrated into proper plugin model)
24232424
import localstack.contrib.thundra # noqa
24242425

2425-
generic_proxy.serve_flask_app(app=app, port=port)
2426+
_serve_flask_app(app=app, port=port)
24262427
except Exception:
24272428
LOG.exception("Error while starting up lambda service")
24282429
raise
24292430

24302431

2432+
def _serve_flask_app(app, port, host=None, cors=True, asynchronous=False):
2433+
if cors:
2434+
CORS(app)
2435+
if not config.DEBUG:
2436+
logging.getLogger("werkzeug").setLevel(logging.ERROR)
2437+
if not host:
2438+
host = "0.0.0.0"
2439+
ssl_context = None
2440+
if not config.FORWARD_EDGE_INMEM and config.USE_SSL:
2441+
_, cert_file_name, key_file_name = create_ssl_cert(serial_number=port)
2442+
ssl_context = cert_file_name, key_file_name
2443+
app.config["ENV"] = "development"
2444+
2445+
def noecho(*args, **kwargs):
2446+
pass
2447+
2448+
try:
2449+
import click
2450+
2451+
click.echo = noecho
2452+
except Exception:
2453+
pass
2454+
2455+
def _run(*_):
2456+
app.run(port=int(port), threaded=True, host=host, ssl_context=ssl_context)
2457+
return app
2458+
2459+
if asynchronous:
2460+
return start_thread(_run, name="flaskapp")
2461+
return _run()
2462+
2463+
24312464
# Config listener
24322465
def on_config_change(config_key: str, config_newvalue: str) -> None:
24332466
global LAMBDA_EXECUTOR

localstack/services/edge.py

Lines changed: 19 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import subprocess
77
import sys
88
import threading
9-
from typing import Dict
9+
from typing import Dict, List, Optional
1010
from urllib.parse import urlparse
1111

1212
from requests.models import Response
@@ -44,12 +44,10 @@
4444
from localstack.utils.functions import empty_context_manager
4545
from localstack.utils.http import parse_request_data
4646
from localstack.utils.http import safe_requests as requests
47-
from localstack.utils.net import is_port_open
4847
from localstack.utils.run import is_root, run
4948
from localstack.utils.server.http2_server import HTTPErrorResponse
5049
from localstack.utils.server.proxy_server import start_tcp_proxy
5150
from localstack.utils.strings import to_bytes, truncate
52-
from localstack.utils.sync import sleep_forever
5351
from localstack.utils.threads import TMP_THREADS, start_thread
5452

5553
LOG = logging.getLogger(__name__)
@@ -356,7 +354,6 @@ def is_trace_logging_enabled(headers) -> bool:
356354

357355

358356
def do_start_edge(bind_address, port, use_ssl, asynchronous=False):
359-
start_dns_server(asynchronous=True)
360357
if config.LEGACY_EDGE_PROXY:
361358
serve = do_start_edge_proxy
362359
else:
@@ -411,44 +408,11 @@ def ensure_can_use_sudo():
411408
def start_component(component: str, port=None):
412409
if component == "edge":
413410
return start_edge(port=port)
414-
if component == "dns":
415-
return start_dns_server()
416411
if component == "proxy":
417412
return start_proxy(port=port)
418413
raise Exception("Unexpected component name '%s' received during start up" % component)
419414

420415

421-
def start_dns_server(asynchronous=False):
422-
try:
423-
# start local DNS server, if present
424-
from localstack_ext import config as config_ext
425-
from localstack_ext.services import dns_server
426-
427-
if config_ext.DNS_ADDRESS in config.FALSE_STRINGS:
428-
return
429-
430-
if is_port_open(PORT_DNS):
431-
return
432-
433-
if is_root():
434-
result = dns_server.start_servers()
435-
if not asynchronous:
436-
sleep_forever()
437-
return result
438-
439-
env_vars = {}
440-
for env_var in config.CONFIG_ENV_VARS:
441-
if env_var.startswith("DNS_"):
442-
value = os.environ.get(env_var, None)
443-
if value is not None:
444-
env_vars[env_var] = value
445-
446-
# note: running in a separate process breaks integration with Route53 (to be fixed for local dev mode!)
447-
return run_process_as_sudo("dns", PORT_DNS, asynchronous=asynchronous, env_vars=env_vars)
448-
except Exception:
449-
pass
450-
451-
452416
def start_proxy(port, asynchronous=False):
453417
"""
454418
Starts a TCP proxy to perform a low-level forwarding of incoming requests.
@@ -511,17 +475,16 @@ def stop(self, quiet=True):
511475
"EDGE_FORWARD_URL": config.get_edge_url(),
512476
"EDGE_BIND_HOST": config.EDGE_BIND_HOST,
513477
}
514-
return run_process_as_sudo("proxy", port, env_vars=env_vars, asynchronous=asynchronous)
515-
478+
proxy_module = "localstack.services.edge"
479+
proxy_args = ["proxy", str(port)]
480+
return run_module_as_sudo(
481+
module=proxy_module, arguments=proxy_args, env_vars=env_vars, asynchronous=asynchronous
482+
)
516483

517-
def run_process_as_sudo(component, port, asynchronous=False, env_vars=None):
518-
# make sure we can run sudo commands
519-
try:
520-
ensure_can_use_sudo()
521-
except Exception as e:
522-
LOG.error("cannot start service on privileged port %s: %s", port, str(e))
523-
return
524484

485+
def run_module_as_sudo(
486+
module: str, arguments: Optional[List[str]] = None, asynchronous=False, env_vars=None
487+
):
525488
# prepare environment
526489
env_vars = env_vars or {}
527490
env_vars["PYTHONPATH"] = f".:{LOCALSTACK_ROOT_FOLDER}"
@@ -530,16 +493,16 @@ def run_process_as_sudo(component, port, asynchronous=False, env_vars=None):
530493
# start the process as sudo
531494
sudo_cmd = "sudo -n"
532495
python_cmd = sys.executable
533-
cmd = [
534-
sudo_cmd,
535-
env_vars_str,
536-
python_cmd,
537-
"-m",
538-
"localstack.services.edge",
539-
component,
540-
str(port),
541-
]
542-
shell_cmd = " ".join(cmd)
496+
cmd = [sudo_cmd, env_vars_str, python_cmd, "-m", module]
497+
arguments = arguments or []
498+
shell_cmd = " ".join(cmd + arguments)
499+
500+
# make sure we can run sudo commands
501+
try:
502+
ensure_can_use_sudo()
503+
except Exception as e:
504+
LOG.error("cannot run command as root (%s): %s ", str(e), shell_cmd)
505+
return
543506

544507
def run_command(*_):
545508
run(shell_cmd, outfile=subprocess.PIPE, print_error=False, env_vars=env_vars)

0 commit comments

Comments
 (0)