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.
Files changed (112) hide show
  1. localstack_cli/__init__.py +0 -0
  2. localstack_cli/cli/__init__.py +10 -0
  3. localstack_cli/cli/console.py +11 -0
  4. localstack_cli/cli/core_plugin.py +12 -0
  5. localstack_cli/cli/exceptions.py +19 -0
  6. localstack_cli/cli/localstack.py +951 -0
  7. localstack_cli/cli/lpm.py +138 -0
  8. localstack_cli/cli/main.py +22 -0
  9. localstack_cli/cli/plugin.py +39 -0
  10. localstack_cli/cli/plugins.py +134 -0
  11. localstack_cli/cli/profiles.py +65 -0
  12. localstack_cli/config.py +1689 -0
  13. localstack_cli/constants.py +165 -0
  14. localstack_cli/logging/__init__.py +0 -0
  15. localstack_cli/logging/format.py +194 -0
  16. localstack_cli/logging/setup.py +142 -0
  17. localstack_cli/packages/__init__.py +25 -0
  18. localstack_cli/packages/api.py +418 -0
  19. localstack_cli/packages/core.py +416 -0
  20. localstack_cli/pro/__init__.py +0 -0
  21. localstack_cli/pro/core/__init__.py +0 -0
  22. localstack_cli/pro/core/bootstrap/__init__.py +1 -0
  23. localstack_cli/pro/core/bootstrap/auth.py +213 -0
  24. localstack_cli/pro/core/bootstrap/dns_utils.py +55 -0
  25. localstack_cli/pro/core/bootstrap/entitlements.py +117 -0
  26. localstack_cli/pro/core/bootstrap/extensions/__init__.py +3 -0
  27. localstack_cli/pro/core/bootstrap/extensions/__main__.py +106 -0
  28. localstack_cli/pro/core/bootstrap/extensions/autoinstall.py +63 -0
  29. localstack_cli/pro/core/bootstrap/extensions/bootstrap.py +97 -0
  30. localstack_cli/pro/core/bootstrap/extensions/repository.py +374 -0
  31. localstack_cli/pro/core/bootstrap/licensingv2.py +1259 -0
  32. localstack_cli/pro/core/bootstrap/pods/__init__.py +0 -0
  33. localstack_cli/pro/core/bootstrap/pods/api_types.py +17 -0
  34. localstack_cli/pro/core/bootstrap/pods/constants.py +26 -0
  35. localstack_cli/pro/core/bootstrap/pods/remotes/__init__.py +0 -0
  36. localstack_cli/pro/core/bootstrap/pods/remotes/api.py +75 -0
  37. localstack_cli/pro/core/bootstrap/pods/remotes/configs.py +69 -0
  38. localstack_cli/pro/core/bootstrap/pods/remotes/params.py +86 -0
  39. localstack_cli/pro/core/bootstrap/pods_client.py +834 -0
  40. localstack_cli/pro/core/cli/__init__.py +0 -0
  41. localstack_cli/pro/core/cli/auth.py +226 -0
  42. localstack_cli/pro/core/cli/aws.py +16 -0
  43. localstack_cli/pro/core/cli/cli.py +99 -0
  44. localstack_cli/pro/core/cli/click_utils.py +21 -0
  45. localstack_cli/pro/core/cli/cloud_pods.py +465 -0
  46. localstack_cli/pro/core/cli/diff_view.py +41 -0
  47. localstack_cli/pro/core/cli/ephemeral.py +199 -0
  48. localstack_cli/pro/core/cli/extensions.py +492 -0
  49. localstack_cli/pro/core/cli/iam.py +180 -0
  50. localstack_cli/pro/core/cli/license.py +90 -0
  51. localstack_cli/pro/core/cli/localstack.py +118 -0
  52. localstack_cli/pro/core/cli/replicator.py +378 -0
  53. localstack_cli/pro/core/cli/state.py +183 -0
  54. localstack_cli/pro/core/cli/tree_view.py +235 -0
  55. localstack_cli/pro/core/config.py +556 -0
  56. localstack_cli/pro/core/constants.py +54 -0
  57. localstack_cli/pro/core/plugins.py +169 -0
  58. localstack_cli/runtime/__init__.py +6 -0
  59. localstack_cli/runtime/exceptions.py +7 -0
  60. localstack_cli/runtime/hooks.py +73 -0
  61. localstack_cli/testing/__init__.py +1 -0
  62. localstack_cli/testing/config.py +4 -0
  63. localstack_cli/utils/__init__.py +0 -0
  64. localstack_cli/utils/analytics/__init__.py +12 -0
  65. localstack_cli/utils/analytics/cli.py +67 -0
  66. localstack_cli/utils/analytics/client.py +111 -0
  67. localstack_cli/utils/analytics/events.py +30 -0
  68. localstack_cli/utils/analytics/logger.py +48 -0
  69. localstack_cli/utils/analytics/metadata.py +250 -0
  70. localstack_cli/utils/analytics/publisher.py +160 -0
  71. localstack_cli/utils/analytics/service_request_aggregator.py +133 -0
  72. localstack_cli/utils/archives.py +271 -0
  73. localstack_cli/utils/batching.py +258 -0
  74. localstack_cli/utils/bootstrap.py +1418 -0
  75. localstack_cli/utils/checksum.py +313 -0
  76. localstack_cli/utils/collections.py +554 -0
  77. localstack_cli/utils/common.py +229 -0
  78. localstack_cli/utils/container_networking.py +142 -0
  79. localstack_cli/utils/container_utils/__init__.py +0 -0
  80. localstack_cli/utils/container_utils/container_client.py +1585 -0
  81. localstack_cli/utils/container_utils/docker_cmd_client.py +987 -0
  82. localstack_cli/utils/container_utils/docker_sdk_client.py +1018 -0
  83. localstack_cli/utils/crypto.py +294 -0
  84. localstack_cli/utils/docker_utils.py +272 -0
  85. localstack_cli/utils/files.py +327 -0
  86. localstack_cli/utils/functions.py +92 -0
  87. localstack_cli/utils/http.py +326 -0
  88. localstack_cli/utils/json.py +219 -0
  89. localstack_cli/utils/net.py +516 -0
  90. localstack_cli/utils/no_exit_argument_parser.py +19 -0
  91. localstack_cli/utils/numbers.py +49 -0
  92. localstack_cli/utils/objects.py +235 -0
  93. localstack_cli/utils/patch.py +260 -0
  94. localstack_cli/utils/platform.py +77 -0
  95. localstack_cli/utils/run.py +514 -0
  96. localstack_cli/utils/server/__init__.py +0 -0
  97. localstack_cli/utils/server/tcp_proxy.py +108 -0
  98. localstack_cli/utils/serving.py +187 -0
  99. localstack_cli/utils/ssl.py +71 -0
  100. localstack_cli/utils/strings.py +245 -0
  101. localstack_cli/utils/sync.py +267 -0
  102. localstack_cli/utils/threads.py +163 -0
  103. localstack_cli/utils/time.py +81 -0
  104. localstack_cli/utils/urls.py +21 -0
  105. localstack_cli/utils/venv.py +100 -0
  106. localstack_cli/utils/xml.py +41 -0
  107. localstack_cli/version.py +34 -0
  108. playground_ls_cli-4.14.1.dev8.dist-info/METADATA +95 -0
  109. playground_ls_cli-4.14.1.dev8.dist-info/RECORD +112 -0
  110. playground_ls_cli-4.14.1.dev8.dist-info/WHEEL +5 -0
  111. playground_ls_cli-4.14.1.dev8.dist-info/entry_points.txt +17 -0
  112. 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,3 @@
1
+ """Internal tools to operate extensions."""
2
+
3
+ name = "extensions"
@@ -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))