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,183 @@
1
+ import json
2
+
3
+ import click
4
+ from localstack_cli.cli import console
5
+ from localstack_cli.cli.exceptions import CLIError
6
+ from localstack_cli.pro.core.bootstrap.pods_client import CloudPodsClient, StateService
7
+ from localstack_cli.pro.core.cli.cli import RequiresLicenseGroup, _assert_host_reachable
8
+ from localstack_cli.pro.core.cli.tree_view import TreeRenderer
9
+ from localstack_cli.utils.analytics.cli import publish_invocation
10
+ from localstack_cli.utils.collections import is_comma_delimited_list
11
+
12
+
13
+ @click.group(
14
+ name="state",
15
+ short_help="(Preview) Export, restore, and reset LocalStack state.",
16
+ help="""
17
+ (Preview) Manage and manipulate the localstack state.
18
+
19
+ The state command group allows you to interact with LocalStack's state backend.
20
+
21
+ Read more: https://docs.localstack.cloud/references/persistence-mechanism/#snapshot-based-persistence
22
+ """,
23
+ cls=RequiresLicenseGroup,
24
+ )
25
+ def state() -> None:
26
+ pass
27
+
28
+
29
+ @state.command(
30
+ name="reset",
31
+ short_help="Reset the state of LocalStack services",
32
+ help="""
33
+ Reset the service states of the current LocalStack runtime.
34
+
35
+ This command invokes a reset of services in the currently running LocalStack container.
36
+ By default, all services are rest. The `services` options allows to select a subset of services
37
+ which should be reset.
38
+
39
+ This command tries to automatically discover the running LocalStack instance.
40
+ If LocalStack has not been started with `localstack start` (and is not automatically discoverable),
41
+ please set `LOCALSTACK_HOST`.
42
+ """,
43
+ )
44
+ @click.option(
45
+ "-s",
46
+ "--services",
47
+ help="Comma-delimited list of services to reset. By default, the state of all running services is reset.",
48
+ )
49
+ @publish_invocation
50
+ def reset(services: str | None) -> None:
51
+ from localstack_cli.pro.core.bootstrap import pods_client
52
+
53
+ if services and not is_comma_delimited_list(services):
54
+ raise click.ClickException("Input the services as a comma-delimited list")
55
+
56
+ service_list = [x.strip() for x in services.split(",")] if services else None
57
+ pods_client.reset_state(services=service_list)
58
+ console.print("LocalStack state successfully reset")
59
+
60
+
61
+ @state.command(
62
+ name="export",
63
+ short_help="Export the state of LocalStack services",
64
+ help="""
65
+ Save the current state of the LocalStack container to a file on the local disk. This file can be restored at any
66
+ point in time using the `localstack state import` command. Please be aware that this might not be possible when
67
+ importing the state with a different version of LocalStack.
68
+
69
+ If you are looking for a managed solution to handle the state of your LocalStack container, please check out
70
+ the Cloud Pods feature: https://docs.localstack.cloud/user-guide/tools/cloud-pods/.
71
+
72
+ Use the DESTINATION argument to specify an absolute or relative path for the exported file.
73
+ If no destination is specified, a file named `ls-state-export` will be saved in the current working directory.
74
+
75
+ \b
76
+ Examples:
77
+ localstack state export my-state
78
+ localstack state export ../parent-dir/my-state
79
+ localstack state export /home/johndoe/my-state
80
+
81
+ You can also specify a subset of services to export with the `--services` option.
82
+
83
+ \b
84
+ For example:
85
+ localstack state export my-state --services s3,lambda
86
+
87
+ By default, the state of all running services is exported.
88
+ """,
89
+ )
90
+ @click.argument("destination", type=click.Path(resolve_path=True), default="ls-state-export")
91
+ @click.option(
92
+ "-s",
93
+ "--services",
94
+ help="Comma-delimited list of services to reset. By default, the state of all running services is exported.",
95
+ )
96
+ @click.option(
97
+ "-f",
98
+ "--format",
99
+ "format_",
100
+ type=click.Choice(["json"]),
101
+ help="The formatting style for the save command output.",
102
+ )
103
+ @publish_invocation
104
+ def export(destination: str, services: str | None = None, format_: str | None = None) -> None:
105
+ _assert_host_reachable()
106
+
107
+ client = StateService()
108
+ try:
109
+ services = [x.strip() for x in services.split(",") if x] if services else None
110
+ pod_info = client.export_pod(target=destination, services=services)
111
+ if format_ == "json":
112
+ console.print_json(json.dumps(pod_info))
113
+ else:
114
+ console.print(f"LocalStack state successfully exported to: {destination} ✅")
115
+ return
116
+ except Exception as e:
117
+ raise CLIError(f"Failed to export LocalStack state - {e}") from e
118
+
119
+
120
+ @state.command(
121
+ name="import",
122
+ short_help="Import the state of LocalStack services",
123
+ help="""
124
+ Load the state of LocalStack from a file into the running container.
125
+ The SOURCE argument is the absolute or relative path to the file containing the state to import.
126
+ This file must have been generated from a previous `localstack state export` command.
127
+ Please be aware that it might not be possible to import a state generated from a different version of LocalStack.
128
+
129
+ \b
130
+ Examples:
131
+ localstack state import my-state
132
+ localstack state import ../parent-dir/my-state
133
+ localstack state import /home/johndoe/my-state
134
+ """,
135
+ )
136
+ @click.argument("source", type=click.Path(exists=True, resolve_path=True))
137
+ @publish_invocation
138
+ def import_state(source: str) -> None:
139
+ _assert_host_reachable()
140
+
141
+ client = StateService()
142
+ try:
143
+ client.import_pod(source=source, show_progress=True)
144
+ console.print(f"LocalStack state successfully imported from {source} ✅")
145
+ except Exception as e:
146
+ raise CLIError(f"Failed to import LocalStack state - {e}") from e
147
+
148
+
149
+ @state.command(
150
+ name="inspect",
151
+ short_help="Inspect the state of LocalStack services",
152
+ help="""
153
+ Inspect the state of the Localstack Container.
154
+
155
+ By default, it starts a curses interface which allows an interactive inspection of the contents of the LocalStack
156
+ running instance.
157
+ """,
158
+ )
159
+ @click.option(
160
+ "-f",
161
+ "--format",
162
+ "format_",
163
+ type=click.Choice(["curses", "rich", "json"]),
164
+ default="curses",
165
+ help="The formatting style for the inspect command output.",
166
+ )
167
+ @publish_invocation
168
+ def inspect_state(format_: str) -> None:
169
+ _assert_host_reachable()
170
+
171
+ client = CloudPodsClient()
172
+ try:
173
+ result = client.get_state_data()
174
+ except Exception:
175
+ raise CLIError("Error occurred while fetching the metamodel")
176
+
177
+ # filter out too noisy/verbose services like CloudWatch
178
+ skipped_services = ["cloudwatch"]
179
+ for account, details in result.items():
180
+ result[account] = {k: v for k, v in details.items() if k not in skipped_services}
181
+
182
+ # render tree with given format
183
+ TreeRenderer.get(format_).render_tree(result, "LocalStack State")
@@ -0,0 +1,235 @@
1
+ """Utilities to display trees in the terminal (e.g., interactive using `curses`, via `rich`, or as JSON)"""
2
+
3
+ import functools
4
+ import json
5
+ import logging
6
+ import os
7
+ from abc import ABC
8
+ from typing import Any
9
+
10
+ from localstack_cli.utils.objects import SubtypesInstanceManager
11
+
12
+ # constants
13
+ ESC = 27
14
+ INDENTATION = 2
15
+
16
+
17
+ class TreeRenderer(SubtypesInstanceManager, ABC):
18
+ """Abstract base class for rendering trees (e.g., cloud pod contents) on the terminal."""
19
+
20
+ def render_tree(self, tree: dict[str, Any], tree_name: str):
21
+ raise NotImplementedError
22
+
23
+
24
+ class TreeRendererRich(TreeRenderer):
25
+ @staticmethod
26
+ def impl_name() -> str:
27
+ return "rich"
28
+
29
+ def render_tree(self, tree: dict[str, Any], tree_name: str):
30
+ from rich import print
31
+ from rich.tree import Tree
32
+
33
+ def _convert(obj, parent):
34
+ if isinstance(obj, list):
35
+ for idx, o in enumerate(obj):
36
+ subtree = Tree(str(idx))
37
+ parent.add(_convert(o, subtree))
38
+ return parent
39
+ elif isinstance(obj, dict):
40
+ for k, v in obj.items():
41
+ if isinstance(v, (dict, list)):
42
+ if not v:
43
+ return Tree(f"{k} = {v}")
44
+ subtree = Tree(str(k))
45
+ _convert(v, subtree)
46
+ parent.add(subtree)
47
+ else:
48
+ parent.add(Tree(f"{k} = {v}"))
49
+ return parent
50
+ return Tree(str(obj))
51
+
52
+ tree_obj = Tree(tree_name)
53
+ _convert(tree, tree_obj)
54
+ print(tree_obj)
55
+
56
+
57
+ class Tree:
58
+ def __init__(self, name, obj):
59
+ self.name = name
60
+ self.object = obj
61
+ self.expanded = True
62
+
63
+ def render(self, depth, width):
64
+ padding = " " * INDENTATION * depth
65
+ return self.pad(f"{padding}{self.icon()} {self.name}", width)
66
+
67
+ @functools.cache
68
+ def children(self):
69
+ def _name(key, value):
70
+ if isinstance(value, (list, dict)):
71
+ return key
72
+ return f"{value}" if isinstance(key, int) else f"{key} = {value}"
73
+
74
+ if isinstance(self.object, dict):
75
+ return [Tree(_name(k, v), v) for k, v in self.object.items()]
76
+ if isinstance(self.object, list):
77
+ return [Tree(_name(idx, v), v) for idx, v in enumerate(self.object)]
78
+ return []
79
+
80
+ def icon(self):
81
+ if self.children() and not self.expanded:
82
+ return "+"
83
+ return "-"
84
+
85
+ def expand(self):
86
+ self.expanded = True
87
+
88
+ def collapse(self):
89
+ self.expanded = False
90
+
91
+ def toggle(self):
92
+ self.expanded = not self.expanded
93
+
94
+ def traverse(self):
95
+ yield self, 0
96
+ if not self.expanded:
97
+ return
98
+ for _child in self.children():
99
+ for child, depth in _child.traverse():
100
+ yield child, depth + 1
101
+
102
+ def pad(self, data, width):
103
+ return data + " " * (width - len(data))
104
+
105
+
106
+ class TreeRendererCurses(TreeRenderer):
107
+ """
108
+ Renders an interactive tree in the terminal via the curses library.
109
+ Loosely based on https://github.com/mcchae/treesel
110
+ """
111
+
112
+ # file logger, lazily initialized
113
+ LOG = None
114
+
115
+ @staticmethod
116
+ def impl_name() -> str:
117
+ return "curses"
118
+
119
+ def render_tree(self, dict_obj: dict, tree_name: str):
120
+ from curses import wrapper # note: keep here to avoid import issues on some systems
121
+
122
+ saved_fds = (os.dup(0), os.dup(1))
123
+
124
+ def curses_main_wrapper(_tree: Tree):
125
+ def _main(win):
126
+ return self.curses_main(win, _tree)
127
+
128
+ return _main
129
+
130
+ try:
131
+ saved_fds = self.open_tty()
132
+ tree = Tree(tree_name, dict_obj)
133
+ wrapper(curses_main_wrapper(tree))
134
+ finally:
135
+ os.close(0)
136
+ os.close(1)
137
+ os.dup(saved_fds[0])
138
+ os.dup(saved_fds[1])
139
+
140
+ @staticmethod
141
+ def curses_main(win, tree: Tree):
142
+ """
143
+ Curses main loop that performs the rendering.
144
+ :param win: the `curses.window` instance to render on
145
+ :param tree: the tree structure to render
146
+ """
147
+ import curses # note: keep here to avoid import issues on some systems
148
+
149
+ # initialize window
150
+ win.clear()
151
+ win.refresh()
152
+ curses.nl()
153
+ curses.noecho()
154
+ win.timeout(0)
155
+ win.nodelay(False)
156
+ tree.expand()
157
+ selected_line = 3
158
+ pending_action = None
159
+
160
+ # set up default terminal colors
161
+ curses.use_default_colors()
162
+
163
+ # main render loop
164
+ while True:
165
+ win.clear()
166
+ curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
167
+ line = 0
168
+ offset = max(0, selected_line - curses.LINES + 3)
169
+ for data, depth in tree.traverse():
170
+ if line == selected_line:
171
+ win.attrset(curses.color_pair(1) | curses.A_BOLD)
172
+ if pending_action:
173
+ getattr(data, pending_action)()
174
+ pending_action = None
175
+ else:
176
+ win.attrset(curses.color_pair(0))
177
+ if 0 <= line - offset < curses.LINES - 1:
178
+ win.addstr(line - offset, 0, data.render(depth, curses.COLS))
179
+ line += 1
180
+ win.refresh()
181
+ ch = win.getch()
182
+ if ch == curses.KEY_UP:
183
+ selected_line -= 1
184
+ elif ch == curses.KEY_DOWN:
185
+ selected_line += 1
186
+ elif ch == curses.KEY_PPAGE:
187
+ selected_line -= curses.LINES
188
+ if selected_line < 0:
189
+ selected_line = 0
190
+ elif ch == curses.KEY_NPAGE:
191
+ selected_line += curses.LINES
192
+ if selected_line >= line:
193
+ selected_line = line - 1
194
+ elif ch == curses.KEY_RIGHT:
195
+ pending_action = "expand"
196
+ elif ch == curses.KEY_LEFT:
197
+ pending_action = "collapse"
198
+ elif ch == ord(" "):
199
+ pending_action = "toggle"
200
+ elif ch == ESC:
201
+ return
202
+
203
+ selected_line %= line
204
+
205
+ @staticmethod
206
+ def open_tty():
207
+ saved_stdin = os.dup(0)
208
+ saved_stdout = os.dup(1)
209
+ os.close(0)
210
+ os.close(1)
211
+ os.open("/dev/tty", os.O_RDONLY)
212
+ os.open("/dev/tty", os.O_RDWR)
213
+ return saved_stdin, saved_stdout
214
+
215
+ @classmethod
216
+ def log(cls, *args, **kwargs):
217
+ """Logger that can be used to log debug output from curses program"""
218
+ if cls.LOG:
219
+ LOG = logging.getLogger(__file__)
220
+ handler = logging.FileHandler("cloud_pods_viewer.log")
221
+ formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
222
+ handler.setFormatter(formatter)
223
+ LOG.addHandler(handler)
224
+ LOG.setLevel(logging.INFO)
225
+ cls.LOG.info(*args, **kwargs)
226
+
227
+
228
+ class TreeRendererJSON(TreeRenderer):
229
+ @staticmethod
230
+ def impl_name() -> str:
231
+ return "json"
232
+
233
+ def render_tree(self, dict_obj: dict, tree_name: str):
234
+ # simply print out the JSON with indentation
235
+ print(json.dumps(dict_obj, indent=4))