playground-ls-cli 4.14.1.dev8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- localstack_cli/__init__.py +0 -0
- localstack_cli/cli/__init__.py +10 -0
- localstack_cli/cli/console.py +11 -0
- localstack_cli/cli/core_plugin.py +12 -0
- localstack_cli/cli/exceptions.py +19 -0
- localstack_cli/cli/localstack.py +951 -0
- localstack_cli/cli/lpm.py +138 -0
- localstack_cli/cli/main.py +22 -0
- localstack_cli/cli/plugin.py +39 -0
- localstack_cli/cli/plugins.py +134 -0
- localstack_cli/cli/profiles.py +65 -0
- localstack_cli/config.py +1689 -0
- localstack_cli/constants.py +165 -0
- localstack_cli/logging/__init__.py +0 -0
- localstack_cli/logging/format.py +194 -0
- localstack_cli/logging/setup.py +142 -0
- localstack_cli/packages/__init__.py +25 -0
- localstack_cli/packages/api.py +418 -0
- localstack_cli/packages/core.py +416 -0
- localstack_cli/pro/__init__.py +0 -0
- localstack_cli/pro/core/__init__.py +0 -0
- localstack_cli/pro/core/bootstrap/__init__.py +1 -0
- localstack_cli/pro/core/bootstrap/auth.py +213 -0
- localstack_cli/pro/core/bootstrap/dns_utils.py +55 -0
- localstack_cli/pro/core/bootstrap/entitlements.py +117 -0
- localstack_cli/pro/core/bootstrap/extensions/__init__.py +3 -0
- localstack_cli/pro/core/bootstrap/extensions/__main__.py +106 -0
- localstack_cli/pro/core/bootstrap/extensions/autoinstall.py +63 -0
- localstack_cli/pro/core/bootstrap/extensions/bootstrap.py +97 -0
- localstack_cli/pro/core/bootstrap/extensions/repository.py +374 -0
- localstack_cli/pro/core/bootstrap/licensingv2.py +1259 -0
- localstack_cli/pro/core/bootstrap/pods/__init__.py +0 -0
- localstack_cli/pro/core/bootstrap/pods/api_types.py +17 -0
- localstack_cli/pro/core/bootstrap/pods/constants.py +26 -0
- localstack_cli/pro/core/bootstrap/pods/remotes/__init__.py +0 -0
- localstack_cli/pro/core/bootstrap/pods/remotes/api.py +75 -0
- localstack_cli/pro/core/bootstrap/pods/remotes/configs.py +69 -0
- localstack_cli/pro/core/bootstrap/pods/remotes/params.py +86 -0
- localstack_cli/pro/core/bootstrap/pods_client.py +834 -0
- localstack_cli/pro/core/cli/__init__.py +0 -0
- localstack_cli/pro/core/cli/auth.py +226 -0
- localstack_cli/pro/core/cli/aws.py +16 -0
- localstack_cli/pro/core/cli/cli.py +99 -0
- localstack_cli/pro/core/cli/click_utils.py +21 -0
- localstack_cli/pro/core/cli/cloud_pods.py +465 -0
- localstack_cli/pro/core/cli/diff_view.py +41 -0
- localstack_cli/pro/core/cli/ephemeral.py +199 -0
- localstack_cli/pro/core/cli/extensions.py +492 -0
- localstack_cli/pro/core/cli/iam.py +180 -0
- localstack_cli/pro/core/cli/license.py +90 -0
- localstack_cli/pro/core/cli/localstack.py +118 -0
- localstack_cli/pro/core/cli/replicator.py +378 -0
- localstack_cli/pro/core/cli/state.py +183 -0
- localstack_cli/pro/core/cli/tree_view.py +235 -0
- localstack_cli/pro/core/config.py +556 -0
- localstack_cli/pro/core/constants.py +54 -0
- localstack_cli/pro/core/plugins.py +169 -0
- localstack_cli/runtime/__init__.py +6 -0
- localstack_cli/runtime/exceptions.py +7 -0
- localstack_cli/runtime/hooks.py +73 -0
- localstack_cli/testing/__init__.py +1 -0
- localstack_cli/testing/config.py +4 -0
- localstack_cli/utils/__init__.py +0 -0
- localstack_cli/utils/analytics/__init__.py +12 -0
- localstack_cli/utils/analytics/cli.py +67 -0
- localstack_cli/utils/analytics/client.py +111 -0
- localstack_cli/utils/analytics/events.py +30 -0
- localstack_cli/utils/analytics/logger.py +48 -0
- localstack_cli/utils/analytics/metadata.py +250 -0
- localstack_cli/utils/analytics/publisher.py +160 -0
- localstack_cli/utils/analytics/service_request_aggregator.py +133 -0
- localstack_cli/utils/archives.py +271 -0
- localstack_cli/utils/batching.py +258 -0
- localstack_cli/utils/bootstrap.py +1418 -0
- localstack_cli/utils/checksum.py +313 -0
- localstack_cli/utils/collections.py +554 -0
- localstack_cli/utils/common.py +229 -0
- localstack_cli/utils/container_networking.py +142 -0
- localstack_cli/utils/container_utils/__init__.py +0 -0
- localstack_cli/utils/container_utils/container_client.py +1585 -0
- localstack_cli/utils/container_utils/docker_cmd_client.py +987 -0
- localstack_cli/utils/container_utils/docker_sdk_client.py +1018 -0
- localstack_cli/utils/crypto.py +294 -0
- localstack_cli/utils/docker_utils.py +272 -0
- localstack_cli/utils/files.py +327 -0
- localstack_cli/utils/functions.py +92 -0
- localstack_cli/utils/http.py +326 -0
- localstack_cli/utils/json.py +219 -0
- localstack_cli/utils/net.py +516 -0
- localstack_cli/utils/no_exit_argument_parser.py +19 -0
- localstack_cli/utils/numbers.py +49 -0
- localstack_cli/utils/objects.py +235 -0
- localstack_cli/utils/patch.py +260 -0
- localstack_cli/utils/platform.py +77 -0
- localstack_cli/utils/run.py +514 -0
- localstack_cli/utils/server/__init__.py +0 -0
- localstack_cli/utils/server/tcp_proxy.py +108 -0
- localstack_cli/utils/serving.py +187 -0
- localstack_cli/utils/ssl.py +71 -0
- localstack_cli/utils/strings.py +245 -0
- localstack_cli/utils/sync.py +267 -0
- localstack_cli/utils/threads.py +163 -0
- localstack_cli/utils/time.py +81 -0
- localstack_cli/utils/urls.py +21 -0
- localstack_cli/utils/venv.py +100 -0
- localstack_cli/utils/xml.py +41 -0
- localstack_cli/version.py +34 -0
- playground_ls_cli-4.14.1.dev8.dist-info/METADATA +95 -0
- playground_ls_cli-4.14.1.dev8.dist-info/RECORD +112 -0
- playground_ls_cli-4.14.1.dev8.dist-info/WHEEL +5 -0
- playground_ls_cli-4.14.1.dev8.dist-info/entry_points.txt +17 -0
- playground_ls_cli-4.14.1.dev8.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fnmatch
|
|
2
|
+
from collections.abc import Iterable, Iterator
|
|
3
|
+
from typing import Any, TypedDict
|
|
4
|
+
|
|
5
|
+
from localstack_cli.utils.numbers import to_number
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ProductInfo(TypedDict):
|
|
9
|
+
"""
|
|
10
|
+
A description of the product being licensed. Does not necessarily map to the stripe products.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
name: str
|
|
14
|
+
version: str
|
|
15
|
+
metadata: dict[str, Any] | None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ProductEntitlements:
|
|
19
|
+
"""
|
|
20
|
+
Helper for feature gating with the product entitlement information embedded in a license document.
|
|
21
|
+
|
|
22
|
+
Currently, two types of features/entitlements are supported:
|
|
23
|
+
* Boolean features (`has_entitlement`)
|
|
24
|
+
* Numerical limits (`get_entitlement_limit`)
|
|
25
|
+
|
|
26
|
+
To be extended in the future to support other types of features/entitlements.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
products: Iterable[ProductInfo],
|
|
32
|
+
allow_all: bool = False,
|
|
33
|
+
):
|
|
34
|
+
self._products = list(products)
|
|
35
|
+
self._allow_all = allow_all
|
|
36
|
+
|
|
37
|
+
def __contains__(self, entitlement: str) -> bool:
|
|
38
|
+
return self.has_entitlement(entitlement)
|
|
39
|
+
|
|
40
|
+
def __iter__(self) -> Iterator[ProductInfo]:
|
|
41
|
+
return iter(self._products)
|
|
42
|
+
|
|
43
|
+
def has_entitlement(self, entitlement: str) -> bool:
|
|
44
|
+
"""
|
|
45
|
+
Check if an entitlement is part of the currently active license.
|
|
46
|
+
|
|
47
|
+
The comparison uses fnmatch to compare, so the license could contain a ProductInfo record
|
|
48
|
+
``localstack.extensions/*`` which would give access to all restricted extensions.
|
|
49
|
+
|
|
50
|
+
:param entitlement: The entitlement to check, for example ``localstack.extensions/foo``.
|
|
51
|
+
:return: True if the entitlement is part of the active license.
|
|
52
|
+
"""
|
|
53
|
+
if self._allow_all:
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
return self._get_product_info(entitlement, pattern_matching=True) is not None
|
|
57
|
+
|
|
58
|
+
def get_entitlement_limit(
|
|
59
|
+
self,
|
|
60
|
+
entitlement: str,
|
|
61
|
+
default: int | float | None = None,
|
|
62
|
+
) -> int | float | None:
|
|
63
|
+
"""
|
|
64
|
+
Returns the numerical limit configured for the given feature.
|
|
65
|
+
|
|
66
|
+
:param entitlement: The entitlement to check, for example ``localstack.extensions/foo``.
|
|
67
|
+
:param default: The default value to return if the entitlement is not part of the active license or the limit
|
|
68
|
+
cannot be determined.
|
|
69
|
+
:return: The limit configured for the given entitlement, or the default value if the entitlement is not part of
|
|
70
|
+
the active license or the limit cannot be determined.
|
|
71
|
+
"""
|
|
72
|
+
product = self._get_product_info(entitlement)
|
|
73
|
+
if not product:
|
|
74
|
+
return default
|
|
75
|
+
|
|
76
|
+
metadata = self._get_metadata(product)
|
|
77
|
+
if metadata is None:
|
|
78
|
+
return default
|
|
79
|
+
|
|
80
|
+
raw_limit = metadata.get("limit")
|
|
81
|
+
if raw_limit is None:
|
|
82
|
+
return default
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
limit = to_number(raw_limit)
|
|
86
|
+
except Exception:
|
|
87
|
+
return default
|
|
88
|
+
|
|
89
|
+
if limit is None or isinstance(limit, bool):
|
|
90
|
+
return default
|
|
91
|
+
|
|
92
|
+
return limit
|
|
93
|
+
|
|
94
|
+
def _get_product_info(
|
|
95
|
+
self, entitlement: str, pattern_matching: bool = False
|
|
96
|
+
) -> ProductInfo | None:
|
|
97
|
+
for product in self._products:
|
|
98
|
+
pattern = product.get("name")
|
|
99
|
+
if not pattern:
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
pattern = str(pattern)
|
|
103
|
+
|
|
104
|
+
if "*" in pattern and pattern_matching:
|
|
105
|
+
if fnmatch.fnmatch(entitlement, pattern):
|
|
106
|
+
return product
|
|
107
|
+
elif pattern == entitlement:
|
|
108
|
+
return product
|
|
109
|
+
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
def _get_metadata(self, product: ProductInfo) -> dict[str, Any] | None:
|
|
113
|
+
metadata = product.get("metadata")
|
|
114
|
+
if metadata is None or not isinstance(metadata, dict):
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
return metadata
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Simple internal CLI script used to communicate between the container and the host CLI in
|
|
2
|
+
``localstack.pro.core.cli.extensions."""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import traceback
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from localstack_cli import config
|
|
10
|
+
|
|
11
|
+
from . import repository
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group("extensions")
|
|
15
|
+
def cli():
|
|
16
|
+
from localstack_cli.pro.core.bootstrap import licensingv2
|
|
17
|
+
from localstack_cli.utils.bootstrap import setup_logging
|
|
18
|
+
|
|
19
|
+
setup_logging()
|
|
20
|
+
|
|
21
|
+
# we activate the license here to make sure that extensions that import encrypted code on
|
|
22
|
+
# module level can be resolved and don't lead to errors
|
|
23
|
+
licensingv2.get_licensed_environment().activate()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@cli.command("debug")
|
|
27
|
+
def debug():
|
|
28
|
+
"""
|
|
29
|
+
Print debug information about localstack directories and run ``pip list`` in the extensions venv.
|
|
30
|
+
"""
|
|
31
|
+
click.echo("Directories")
|
|
32
|
+
click.echo("===========")
|
|
33
|
+
for k, v in config.dirs.__dict__.items():
|
|
34
|
+
click.echo(f"{k:13} {v}")
|
|
35
|
+
|
|
36
|
+
click.echo()
|
|
37
|
+
click.echo("Extensions venv")
|
|
38
|
+
click.echo("===============")
|
|
39
|
+
venv = repository.LOCALSTACK_VENV
|
|
40
|
+
click.echo(f"localstack venv: {venv.venv_dir}")
|
|
41
|
+
click.echo(f" site-packages: {venv.site_dir if venv.exists else 'not initialized'}")
|
|
42
|
+
venv = repository.get_extensions_venv()
|
|
43
|
+
click.echo(f"extensions venv: {venv.venv_dir}")
|
|
44
|
+
click.echo(f" site-packages: {venv.site_dir if venv.exists else 'not initialized'}")
|
|
45
|
+
|
|
46
|
+
click.echo()
|
|
47
|
+
click.echo("pip list")
|
|
48
|
+
click.echo("========")
|
|
49
|
+
os.system(f"bash -c '. {venv.venv_dir}/bin/activate && pip list'")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@cli.command("init")
|
|
53
|
+
def init():
|
|
54
|
+
"""
|
|
55
|
+
Initialize the extensions repository.
|
|
56
|
+
"""
|
|
57
|
+
repository.init_extension_venv()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@cli.command("list")
|
|
61
|
+
def list_():
|
|
62
|
+
"""Print the extension metadata as NDJSON to stdout."""
|
|
63
|
+
for extension in repository.list_extension_metadata():
|
|
64
|
+
click.echo(json.dumps(extension))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@cli.command("install")
|
|
68
|
+
@click.argument("name", required=True)
|
|
69
|
+
def install(name: str):
|
|
70
|
+
try:
|
|
71
|
+
repo = repository.ExtensionsRepository()
|
|
72
|
+
for event in repo.run_install(name):
|
|
73
|
+
click.echo(json.dumps(event))
|
|
74
|
+
except Exception as e:
|
|
75
|
+
click.echo(
|
|
76
|
+
json.dumps(
|
|
77
|
+
{
|
|
78
|
+
"event": "exception",
|
|
79
|
+
"message": f"Error while installing extension: {e}",
|
|
80
|
+
"extra": {"traceback": traceback.format_exc()},
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@cli.command("uninstall")
|
|
87
|
+
@click.argument("name", required=True)
|
|
88
|
+
def uninstall(name: str):
|
|
89
|
+
try:
|
|
90
|
+
repo = repository.ExtensionsRepository()
|
|
91
|
+
for event in repo.run_uninstall(name):
|
|
92
|
+
click.echo(json.dumps(event))
|
|
93
|
+
except Exception as e:
|
|
94
|
+
click.echo(
|
|
95
|
+
json.dumps(
|
|
96
|
+
{
|
|
97
|
+
"event": "exception",
|
|
98
|
+
"message": f"Error while uninstalling extension: {e}",
|
|
99
|
+
"extra": {"traceback": traceback.format_exc()},
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
cli()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Used from the docker-entrypoint.sh to auto-install extensions configured in ``EXTENSION_AUTO_INSTALL`` or
|
|
3
|
+
located in ``/etc/localstack/conf.d/extensions.txt``.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os.path
|
|
7
|
+
import traceback
|
|
8
|
+
|
|
9
|
+
from localstack_cli import config
|
|
10
|
+
from localstack_cli.pro.core import config as pro_config
|
|
11
|
+
|
|
12
|
+
from .repository import SubprocessLineStream, get_extension_repository
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def log(msg: str):
|
|
16
|
+
print(f"Localstack extensions installer: {msg}")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
if not pro_config.ACTIVATE_PRO:
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
extensions_txt = os.path.join(config.dirs.config, "extensions.txt")
|
|
24
|
+
auto_installable = pro_config.EXTENSION_AUTO_INSTALL
|
|
25
|
+
|
|
26
|
+
if not os.path.exists(extensions_txt) and not auto_installable:
|
|
27
|
+
# no extensions to install
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
# this will create an extensions venv directory if it doesn't exist
|
|
31
|
+
repo = get_extension_repository()
|
|
32
|
+
|
|
33
|
+
# install extensions.txt first
|
|
34
|
+
if os.path.exists(extensions_txt):
|
|
35
|
+
log(f"installing extensions defined in {extensions_txt}")
|
|
36
|
+
cmd = [
|
|
37
|
+
repo.pip,
|
|
38
|
+
"install",
|
|
39
|
+
"--no-input",
|
|
40
|
+
"--no-color",
|
|
41
|
+
"--disable-pip-version-check",
|
|
42
|
+
"-r",
|
|
43
|
+
extensions_txt,
|
|
44
|
+
]
|
|
45
|
+
with SubprocessLineStream.open(cmd) as stream:
|
|
46
|
+
for line in stream:
|
|
47
|
+
log(line)
|
|
48
|
+
|
|
49
|
+
# install from variable next
|
|
50
|
+
log("installing extensions defined EXTENSIONS_AUTO_INSTALL")
|
|
51
|
+
for name_or_url in auto_installable:
|
|
52
|
+
log(f"auto installing extension {name_or_url}")
|
|
53
|
+
# TODO: proper output formatting
|
|
54
|
+
try:
|
|
55
|
+
for event in repo.run_install(name_or_url=name_or_url):
|
|
56
|
+
log(event.get("message"))
|
|
57
|
+
except Exception as e:
|
|
58
|
+
log(f"{e}")
|
|
59
|
+
log(traceback.format_exc())
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
main()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Hooks for extension developer mode on the host CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from tempfile import gettempdir
|
|
9
|
+
|
|
10
|
+
from localstack_cli import config
|
|
11
|
+
from localstack_cli.utils.bootstrap import Container
|
|
12
|
+
from localstack_cli.utils.container_utils.container_client import BindMount
|
|
13
|
+
from localstack_cli.utils.json import FileMappedDocument
|
|
14
|
+
from localstack_cli.utils.strings import md5
|
|
15
|
+
|
|
16
|
+
LOG = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
_ENTRYPOINT_SCRIPT = """#!/bin/bash
|
|
19
|
+
echo "=================================================="
|
|
20
|
+
echo "LocalStack extension developer mode enabled"
|
|
21
|
+
shopt -s nullglob
|
|
22
|
+
|
|
23
|
+
pkgs=$(echo /opt/code/localstack/.venv/lib/python3*/site-packages)
|
|
24
|
+
|
|
25
|
+
echo "import localstack_extensions_resolve;localstack_extensions_resolve.resolve()" > ${pkgs}/localstack-extensions-resolve.pth
|
|
26
|
+
|
|
27
|
+
cat << EOF >> ${pkgs}/localstack_extensions_resolve.py
|
|
28
|
+
import os
|
|
29
|
+
import sys
|
|
30
|
+
import glob
|
|
31
|
+
|
|
32
|
+
base_dirs_visited = set()
|
|
33
|
+
|
|
34
|
+
def append_pth_recursively(base_dir):
|
|
35
|
+
if base_dir in base_dirs_visited:
|
|
36
|
+
return
|
|
37
|
+
base_dirs_visited.add(base_dir)
|
|
38
|
+
|
|
39
|
+
for f in glob.glob(f"{base_dir}/*.pth", recursive=True):
|
|
40
|
+
with open(f, "r") as fd:
|
|
41
|
+
abs_path = os.path.abspath(os.path.dirname(f))
|
|
42
|
+
lines = fd.readlines()
|
|
43
|
+
for line in lines:
|
|
44
|
+
if line := line.strip():
|
|
45
|
+
if "import" in line:
|
|
46
|
+
continue
|
|
47
|
+
module_dir = os.path.abspath(os.path.join(abs_path, line)) if not os.path.isabs(line) else line
|
|
48
|
+
if os.path.exists(module_dir) and os.path.isdir(module_dir):
|
|
49
|
+
if module_dir not in sys.path:
|
|
50
|
+
sys.path.append(module_dir)
|
|
51
|
+
append_pth_recursively(module_dir)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def resolve():
|
|
55
|
+
append_pth_recursively(os.path.dirname(__file__))
|
|
56
|
+
EOF
|
|
57
|
+
|
|
58
|
+
for d in /opt/code/extensions/* ;
|
|
59
|
+
do
|
|
60
|
+
echo "- mounting extension ${d}"
|
|
61
|
+
find ${d} -type d -name "site-packages" >> ${pkgs}/localstack-extensions-venv.pth
|
|
62
|
+
echo ${d} >> ${pkgs}/localstack-extensions-venv.pth
|
|
63
|
+
done
|
|
64
|
+
echo "Resuming normal execution, ..."
|
|
65
|
+
echo "=================================================="
|
|
66
|
+
exec /usr/local/bin/docker-entrypoint.sh
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
_host_extension_dirs: list[str] = []
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def run_on_configure_host_hook():
|
|
73
|
+
"""Load extension directories from ~/.localstack/extensions-dev.json."""
|
|
74
|
+
doc = FileMappedDocument(os.path.join(config.CONFIG_DIR, "extensions-dev.json"))
|
|
75
|
+
|
|
76
|
+
for extensions_spec in doc.get("extensions", []):
|
|
77
|
+
path = extensions_spec.get("host_path")
|
|
78
|
+
if path and os.path.exists(path):
|
|
79
|
+
_host_extension_dirs.append(path)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def run_on_configure_localstack_container_hook(container: Container):
|
|
83
|
+
"""Configure container for extension dev mode."""
|
|
84
|
+
# Create and mount custom entrypoint script
|
|
85
|
+
h = md5(_ENTRYPOINT_SCRIPT)
|
|
86
|
+
file = Path(gettempdir(), f"docker-entrypoint-{h}.sh")
|
|
87
|
+
if not file.exists():
|
|
88
|
+
file.write_text(_ENTRYPOINT_SCRIPT, newline="\n", encoding="utf-8")
|
|
89
|
+
file.chmod(0o777)
|
|
90
|
+
|
|
91
|
+
container.config.volumes.add(BindMount(str(file), f"/tmp/{file.name}"))
|
|
92
|
+
container.config.entrypoint = f"/tmp/{file.name}"
|
|
93
|
+
|
|
94
|
+
# Mount extension directories
|
|
95
|
+
for ext_dir in _host_extension_dirs:
|
|
96
|
+
target = os.path.join("/opt/code/extensions", os.path.basename(ext_dir))
|
|
97
|
+
container.config.volumes.add(BindMount(ext_dir, target, read_only=True))
|