localstack 4.14.0__tar.gz → 2026.3.0__tar.gz
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-4.14.0 → localstack-2026.3.0}/.github/workflows/tests-cli.yml +3 -4
- {localstack-4.14.0/localstack.egg-info → localstack-2026.3.0}/PKG-INFO +1 -1
- {localstack-4.14.0 → localstack-2026.3.0/localstack.egg-info}/PKG-INFO +1 -1
- {localstack-4.14.0 → localstack-2026.3.0}/localstack.egg-info/SOURCES.txt +1 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/localstack.py +0 -10
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/constants.py +1 -2
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/licensingv2.py +1 -1
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/replicator.py +12 -2
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/config.py +4 -13
- localstack-2026.3.0/localstack_cli/pro/core/plugins.py +169 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/bootstrap.py +2 -7
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/container_networking.py +1 -1
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/docker_utils.py +2 -2
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/version.py +3 -3
- {localstack-4.14.0 → localstack-2026.3.0}/tests/cli/test_cli.py +5 -28
- {localstack-4.14.0 → localstack-2026.3.0}/tests/cli/test_cli_pro.py +0 -24
- {localstack-4.14.0 → localstack-2026.3.0}/tests/integration/cli/test_cli.py +1 -1
- localstack-2026.3.0/tests/unit/pro/test_grace_period.py +101 -0
- localstack-4.14.0/localstack_cli/pro/core/plugins.py +0 -81
- {localstack-4.14.0 → localstack-2026.3.0}/.gitignore +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/Makefile +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/README.md +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/bin/localstack +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack.egg-info/dependency_links.txt +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack.egg-info/entry_points.txt +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack.egg-info/requires.txt +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack.egg-info/top_level.txt +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/console.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/core_plugin.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/exceptions.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/lpm.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/main.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/plugin.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/plugins.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/cli/profiles.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/config.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/logging/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/logging/format.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/logging/setup.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/packages/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/packages/api.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/packages/core.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/auth.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/dns_utils.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/entitlements.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/extensions/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/extensions/__main__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/extensions/autoinstall.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/extensions/bootstrap.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/extensions/repository.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/pods/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/pods/api_types.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/pods/constants.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/pods/remotes/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/pods/remotes/api.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/pods/remotes/configs.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/pods/remotes/params.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/bootstrap/pods_client.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/auth.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/aws.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/cli.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/click_utils.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/cloud_pods.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/diff_view.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/ephemeral.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/extensions.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/iam.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/license.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/localstack.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/state.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/cli/tree_view.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/pro/core/constants.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/runtime/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/runtime/exceptions.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/runtime/hooks.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/testing/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/testing/config.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/analytics/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/analytics/cli.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/analytics/client.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/analytics/events.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/analytics/logger.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/analytics/metadata.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/analytics/publisher.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/analytics/service_request_aggregator.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/archives.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/batching.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/checksum.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/collections.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/common.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/container_utils/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/container_utils/container_client.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/container_utils/docker_cmd_client.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/container_utils/docker_sdk_client.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/crypto.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/files.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/functions.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/http.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/json.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/net.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/no_exit_argument_parser.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/numbers.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/objects.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/patch.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/platform.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/run.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/server/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/server/tcp_proxy.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/serving.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/ssl.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/strings.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/sync.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/threads.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/time.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/urls.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/venv.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/localstack_cli/utils/xml.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/plux.ini +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/pyproject.toml +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/setup.cfg +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/bootstrap/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/bootstrap/extensions/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/bootstrap/extensions/test_extension_install.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/cli/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/cli/conftest.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/conftest.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/integration/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/integration/cli/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/integration/cli/test_lpm.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/integration/replicator/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/integration/replicator/core/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/integration/replicator/core/test_replicator_cli.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/unit/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/unit/cli/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/unit/cli/test_profiles.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/unit/pro/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/unit/pro/test_cli.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/unit/replicator/__init__.py +0 -0
- {localstack-4.14.0 → localstack-2026.3.0}/tests/unit/replicator/test_profile_loading.py +0 -0
|
@@ -64,8 +64,7 @@ jobs:
|
|
|
64
64
|
|
|
65
65
|
- name: Pull Docker images
|
|
66
66
|
run: |
|
|
67
|
-
docker pull localstack/localstack-pro
|
|
68
|
-
docker pull localstack/localstack
|
|
67
|
+
docker pull localstack/localstack-pro
|
|
69
68
|
|
|
70
69
|
- name: Install dependencies
|
|
71
70
|
run: |
|
|
@@ -82,7 +81,7 @@ jobs:
|
|
|
82
81
|
|
|
83
82
|
- name: Run CLI tests
|
|
84
83
|
env:
|
|
85
|
-
LOCALSTACK_AUTH_TOKEN: ${{ secrets.
|
|
84
|
+
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
|
|
86
85
|
run: |
|
|
87
86
|
python -m pytest tests/cli/ \
|
|
88
87
|
--log-cli-level=${{ env.PYTEST_LOGLEVEL }} \
|
|
@@ -90,7 +89,7 @@ jobs:
|
|
|
90
89
|
|
|
91
90
|
- name: Run unit tests
|
|
92
91
|
env:
|
|
93
|
-
LOCALSTACK_AUTH_TOKEN: ${{ secrets.
|
|
92
|
+
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
|
|
94
93
|
run: |
|
|
95
94
|
python -m pytest tests/unit/ \
|
|
96
95
|
--log-cli-level=${{ env.PYTEST_LOGLEVEL }} \
|
|
@@ -523,16 +523,6 @@ def cmd_start(
|
|
|
523
523
|
print_app()
|
|
524
524
|
console.line()
|
|
525
525
|
|
|
526
|
-
# check if we are about to activate Pro, if not raise a warning about the upcoming changes
|
|
527
|
-
# LOCALSTACK_ACTIVATE_PRO is set by the Pro config if the auth token is cached or explicitly set in the env
|
|
528
|
-
if not config.is_env_true("LOCALSTACK_ACTIVATE_PRO"):
|
|
529
|
-
console.log(
|
|
530
|
-
"Warning: You are starting LocalStack without an auth token. "
|
|
531
|
-
"Starting in March 2026, LocalStack will require an auth token. "
|
|
532
|
-
"Go to this page for more infos: https://localstack.cloud/2026-updates",
|
|
533
|
-
style="bold red",
|
|
534
|
-
)
|
|
535
|
-
|
|
536
526
|
from localstack_cli.utils import bootstrap
|
|
537
527
|
|
|
538
528
|
if not no_banner:
|
|
@@ -64,9 +64,7 @@ DEFAULT_VOLUME_DIR = "/var/lib/localstack"
|
|
|
64
64
|
PATH_USER_REQUEST = "_user_request_"
|
|
65
65
|
|
|
66
66
|
# name of LocalStack Docker image
|
|
67
|
-
DOCKER_IMAGE_NAME = "localstack/localstack"
|
|
68
67
|
DOCKER_IMAGE_NAME_PRO = "localstack/localstack-pro"
|
|
69
|
-
DOCKER_IMAGE_NAME_FULL = "localstack/localstack-full"
|
|
70
68
|
|
|
71
69
|
# backdoor API path used to retrieve or update config variables
|
|
72
70
|
CONFIG_UPDATE_PATH = "/?_config_"
|
|
@@ -142,6 +140,7 @@ TRACE_LOG_LEVELS = [LS_LOG_TRACE, LS_LOG_TRACE_INTERNAL]
|
|
|
142
140
|
OFFICIAL_IMAGES = [
|
|
143
141
|
"localstack/localstack",
|
|
144
142
|
"localstack/localstack-pro",
|
|
143
|
+
"localstack/snowflake"
|
|
145
144
|
]
|
|
146
145
|
|
|
147
146
|
# port for debug py
|
|
@@ -69,7 +69,7 @@ class LicensingError(Exception):
|
|
|
69
69
|
|
|
70
70
|
- Please check that your credentials are set up correctly and that you have an active license.
|
|
71
71
|
You can find your credentials in our webapp at https://app.localstack.cloud.
|
|
72
|
-
- If you want to continue using LocalStack without pro features
|
|
72
|
+
- If you want to continue using LocalStack without pro features, set `LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT=1` during the grace period.
|
|
73
73
|
"""
|
|
74
74
|
)
|
|
75
75
|
|
|
@@ -11,6 +11,7 @@ from typing import TypedDict
|
|
|
11
11
|
|
|
12
12
|
import click
|
|
13
13
|
import requests
|
|
14
|
+
|
|
14
15
|
from localstack_cli import config
|
|
15
16
|
from localstack_cli.cli import console
|
|
16
17
|
from localstack_cli.cli.exceptions import CLIError
|
|
@@ -255,10 +256,17 @@ At the moment only environment variables are recognized.
|
|
|
255
256
|
)
|
|
256
257
|
@click.option(
|
|
257
258
|
"--replication-type",
|
|
258
|
-
type=click.Choice(["
|
|
259
|
+
type=click.Choice(["SINGLE_RESOURCE", "BATCH"]),
|
|
259
260
|
default="SINGLE_RESOURCE",
|
|
260
261
|
show_default=True,
|
|
261
|
-
help="Type of replication job:
|
|
262
|
+
help="Type of replication job: SINGLE_RESOURCE, BATCH",
|
|
263
|
+
)
|
|
264
|
+
@click.option(
|
|
265
|
+
"--explore-strategy",
|
|
266
|
+
type=click.Choice(["SIMPLE", "TREE"]),
|
|
267
|
+
default="SIMPLE",
|
|
268
|
+
show_default=True,
|
|
269
|
+
help="How we explore the resource tree. SIMPLE only replicates the resource requested.",
|
|
262
270
|
)
|
|
263
271
|
@click.option(
|
|
264
272
|
"--resource-arn",
|
|
@@ -285,6 +293,7 @@ At the moment only environment variables are recognized.
|
|
|
285
293
|
@click.option("--delay", help="Delay for the MOCK replication work")
|
|
286
294
|
def start(
|
|
287
295
|
replication_type: str,
|
|
296
|
+
explore_strategy: str,
|
|
288
297
|
resource_arn: str | None = None,
|
|
289
298
|
resource_type: str | None = None,
|
|
290
299
|
resource_identifier: str | None = None,
|
|
@@ -314,6 +323,7 @@ def start(
|
|
|
314
323
|
|
|
315
324
|
payload = {
|
|
316
325
|
"replication_type": replication_type,
|
|
326
|
+
"explore_strategy": explore_strategy,
|
|
317
327
|
"replication_job_config": replication_config,
|
|
318
328
|
"source_aws_config": source_config,
|
|
319
329
|
"target_aws_config": target_config,
|
|
@@ -368,10 +368,9 @@ def is_auth_token_configured() -> bool:
|
|
|
368
368
|
return False
|
|
369
369
|
|
|
370
370
|
|
|
371
|
-
#
|
|
372
|
-
#
|
|
373
|
-
|
|
374
|
-
ACTIVATE_PRO = localstack_config.is_env_not_false("ACTIVATE_PRO")
|
|
371
|
+
# pro plugins should always run. If license activation failed this will be set to false to avoid loading
|
|
372
|
+
# pro plugins after that.
|
|
373
|
+
ACTIVATE_PRO = True
|
|
375
374
|
|
|
376
375
|
# a comma-separated list of cloud pods to be automatically loaded at startup
|
|
377
376
|
AUTO_LOAD_POD = os.environ.get("AUTO_LOAD_POD", "")
|
|
@@ -394,14 +393,6 @@ ENABLE_POD_RESOURCES = is_env_true("ENABLE_POD_RESOURCES")
|
|
|
394
393
|
# set of Cloud Pods.
|
|
395
394
|
CLI_INJECT_POD_IDENTITY = localstack_config.is_env_not_false("CLI_INJECT_POD_IDENTITY")
|
|
396
395
|
|
|
397
|
-
if is_env_true("LOCALSTACK_CLI"):
|
|
398
|
-
# when we're in the CLI, we only want to activate pro code if an API key is set. this is because we are
|
|
399
|
-
# always loading localstack/pro/core/config.py in the CLI, and we would otherwise always have ACTIVATE_PRO=1
|
|
400
|
-
# when running `localstack start`.
|
|
401
|
-
ACTIVATE_PRO = ACTIVATE_PRO and is_auth_token_configured()
|
|
402
|
-
# we also need to update the environment so `ACTIVATE_PRO` is disabled in the container
|
|
403
|
-
os.environ["LOCALSTACK_ACTIVATE_PRO"] = "1" if ACTIVATE_PRO else "0"
|
|
404
|
-
|
|
405
396
|
# backend service ports
|
|
406
397
|
DEFAULT_PORT_LOCAL_DAEMON = 4600
|
|
407
398
|
DEFAULT_PORT_LOCAL_DAEMON_ROOT = 4601
|
|
@@ -471,8 +462,8 @@ APPINSPECTOR_ENABLE = is_env_true("APPINSPECTOR_ENABLE")
|
|
|
471
462
|
|
|
472
463
|
# update variable names that need to be passed as arguments to Docker
|
|
473
464
|
localstack_config.CONFIG_ENV_VARS += [
|
|
474
|
-
"ACTIVATE_PRO",
|
|
475
465
|
"APPSYNC_JS_LIBS_VERSION",
|
|
466
|
+
"ACKNOWLEDGE_ACCOUNT_REQUIREMENT",
|
|
476
467
|
"APIGW_ENABLE_NEXT_GEN_WEBSOCKETS",
|
|
477
468
|
"APIGW_ENABLE_NEXT_GEN_WEBSOCKETS_INVOCATION",
|
|
478
469
|
"AUTO_LOAD_POD",
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pro plugin hooks for the LocalStack CLI.
|
|
3
|
+
|
|
4
|
+
These hooks are executed on the host machine when running CLI commands like `localstack start`.
|
|
5
|
+
They handle license activation, container configuration, and extension developer mode.
|
|
6
|
+
|
|
7
|
+
Note: This file was extracted from localstack-pro-core/localstack/pro/core/plugins.py
|
|
8
|
+
and rewritten to contain only CLI-relevant functionality.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
import requests
|
|
17
|
+
from localstack_cli import config as localstack_config
|
|
18
|
+
from localstack_cli.config import HostAndPort
|
|
19
|
+
from localstack_cli.constants import API_ENDPOINT
|
|
20
|
+
from localstack_cli.pro.core import config as pro_config
|
|
21
|
+
from localstack_cli.pro.core.bootstrap import licensingv2
|
|
22
|
+
from localstack_cli.runtime import hooks
|
|
23
|
+
from localstack_cli.runtime.exceptions import LocalstackExit
|
|
24
|
+
from localstack_cli.utils.bootstrap import Container
|
|
25
|
+
|
|
26
|
+
LOG = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def modify_gateway_listen_config(cfg):
|
|
30
|
+
"""
|
|
31
|
+
Modifies the localstack config to additionally listen to port 443.
|
|
32
|
+
Needs to be called before any edge URLs are resolved using the config.
|
|
33
|
+
"""
|
|
34
|
+
if os.getenv("GATEWAY_LISTEN") is None:
|
|
35
|
+
host = "0.0.0.0" if localstack_config.in_docker() else "127.0.0.1"
|
|
36
|
+
cfg.GATEWAY_LISTEN.append(HostAndPort(host=host, port=443))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@hooks.prepare_host(priority=200)
|
|
40
|
+
def patch_community_pro_detection():
|
|
41
|
+
"""This is currently needed to make localstack core aware of the `localstack auth set-token`
|
|
42
|
+
functionality, where we set the key into the ``~/.localstack/auth.json`` file that community does not
|
|
43
|
+
yet know about. ``is_api_key_configured`` is used in the LocalStack CLI to determine whether to start
|
|
44
|
+
the localstack or localstack-pro container image."""
|
|
45
|
+
from localstack_cli.utils import bootstrap
|
|
46
|
+
|
|
47
|
+
bootstrap.is_auth_token_configured = pro_config.is_auth_token_configured
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## ---------------------------------------------------------------------------
|
|
51
|
+
## Grace period helpers (temporary, to be removed once grace period expires)
|
|
52
|
+
## ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
GRACE_PERIOD_ENDPOINT_PATH = "/license/grace-period-check"
|
|
55
|
+
|
|
56
|
+
LICENSE_ERROR_MESSAGE = """\
|
|
57
|
+
===============================================
|
|
58
|
+
LocalStack requires an account to run.
|
|
59
|
+
|
|
60
|
+
==> Have an account? Learn how to set LOCALSTACK_AUTH_TOKEN: https://app.localstack.cloud/settings/auth-tokens
|
|
61
|
+
==> Need an account? Get started: https://www.localstack.cloud/pricing
|
|
62
|
+
==> Want more time? Snooze until April 6, 2026 by setting LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT=1
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
GRACE_PERIOD_EXPIRED_MESSAGE = """\
|
|
66
|
+
===============================================
|
|
67
|
+
LocalStack requires an account to run.
|
|
68
|
+
|
|
69
|
+
==> Have an account? Learn how to set LOCALSTACK_AUTH_TOKEN: https://app.localstack.cloud/settings/auth-tokens
|
|
70
|
+
==> Need an account? Get started: https://www.localstack.cloud/pricing
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _check_grace_period_active(ack: bool) -> bool:
|
|
75
|
+
"""Call the platform grace period endpoint.
|
|
76
|
+
|
|
77
|
+
Returns True if the grace period is active (200 response).
|
|
78
|
+
Returns False for any other response (including 404 when the endpoint is removed).
|
|
79
|
+
"""
|
|
80
|
+
from localstack_cli.pro.core.bootstrap.licensingv2 import get_system_information_summary
|
|
81
|
+
from localstack_cli.pro.core.constants import VERSION
|
|
82
|
+
from localstack_cli.utils.analytics.metadata import get_client_metadata
|
|
83
|
+
from localstack_cli.utils.http import get_proxies
|
|
84
|
+
|
|
85
|
+
metadata = get_client_metadata()
|
|
86
|
+
payload = {
|
|
87
|
+
"machine": {
|
|
88
|
+
"id": metadata.machine_id,
|
|
89
|
+
"ci": metadata.is_ci,
|
|
90
|
+
"system": get_system_information_summary(),
|
|
91
|
+
},
|
|
92
|
+
"version": VERSION,
|
|
93
|
+
"requesting_grace": ack,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
url = f"{API_ENDPOINT}{GRACE_PERIOD_ENDPOINT_PATH}"
|
|
97
|
+
proxies = get_proxies()
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
response = requests.post(
|
|
101
|
+
url,
|
|
102
|
+
json=payload,
|
|
103
|
+
verify=not localstack_config.is_env_true("SSL_NO_VERIFY"),
|
|
104
|
+
proxies=proxies,
|
|
105
|
+
timeout=10,
|
|
106
|
+
)
|
|
107
|
+
return response.ok
|
|
108
|
+
except requests.exceptions.RequestException:
|
|
109
|
+
LOG.debug("Failed to reach grace period endpoint at %s", url)
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
## ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@hooks.prepare_host(priority=100, should_load=pro_config.ACTIVATE_PRO)
|
|
117
|
+
def activate_pro_key_on_host():
|
|
118
|
+
"""Activate license on host (needed for DNS forward and EC2 daemon)."""
|
|
119
|
+
try:
|
|
120
|
+
licensingv2.get_licensed_environment().activate()
|
|
121
|
+
except licensingv2.LicensingError as e:
|
|
122
|
+
# license activation was unsuccessful (this can also be because no auth token was set)
|
|
123
|
+
# defensively set pro to False so that we don't load pro plugins
|
|
124
|
+
pro_config.ACTIVATE_PRO = False
|
|
125
|
+
|
|
126
|
+
# check also with LOCALSTACK_ prefix here because we don't have the handling from the docker entrypoint on the host
|
|
127
|
+
ack = localstack_config.is_env_true(
|
|
128
|
+
"LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT") or localstack_config.is_env_true(
|
|
129
|
+
"ACKNOWLEDGE_ACCOUNT_REQUIREMENT")
|
|
130
|
+
# Note: this can also fail because they've got their connection set up wrong.
|
|
131
|
+
active = _check_grace_period_active(ack)
|
|
132
|
+
if not ack and active:
|
|
133
|
+
# Grace period is active but unused. Prompt user to set up account or snooze.
|
|
134
|
+
raise LocalstackExit(reason=LICENSE_ERROR_MESSAGE, code=55)
|
|
135
|
+
if ack and active:
|
|
136
|
+
# Grace period is active, start in community mode (no pro features).
|
|
137
|
+
LOG.info("Grace period active: starting LocalStack in community mode")
|
|
138
|
+
return
|
|
139
|
+
if ack and not active:
|
|
140
|
+
# Grace period expired or endpoint unreachable
|
|
141
|
+
raise LocalstackExit(reason=GRACE_PERIOD_EXPIRED_MESSAGE, code=55)
|
|
142
|
+
raise LocalstackExit(reason=e.get_user_friendly(), code=55)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@hooks.configure_localstack_container(priority=10, should_load=pro_config.ACTIVATE_PRO)
|
|
146
|
+
def configure_pro_container(container: Container):
|
|
147
|
+
"""Configure the LocalStack container for pro features."""
|
|
148
|
+
modify_gateway_listen_config(localstack_config)
|
|
149
|
+
container.configure(licensingv2.configure_container_licensing)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@hooks.prepare_host(should_load=pro_config.ACTIVATE_PRO and pro_config.EXTENSION_DEV_MODE)
|
|
153
|
+
def configure_extensions_dev_host():
|
|
154
|
+
"""Load extension directories from ~/.localstack/extensions-dev.json."""
|
|
155
|
+
from localstack_cli.pro.core.bootstrap.extensions.bootstrap import run_on_configure_host_hook
|
|
156
|
+
|
|
157
|
+
run_on_configure_host_hook()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@hooks.configure_localstack_container(
|
|
161
|
+
should_load=pro_config.ACTIVATE_PRO and pro_config.EXTENSION_DEV_MODE
|
|
162
|
+
)
|
|
163
|
+
def configure_extensions_dev_container(container: Container):
|
|
164
|
+
"""Configure container for extension developer mode."""
|
|
165
|
+
from localstack_cli.pro.core.bootstrap.extensions.bootstrap import (
|
|
166
|
+
run_on_configure_localstack_container_hook,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
run_on_configure_localstack_container_hook(container)
|
|
@@ -428,7 +428,7 @@ def validate_localstack_config(name: str):
|
|
|
428
428
|
image_name = ls_service_details.get("image", "")
|
|
429
429
|
if image_name.split(":")[0] not in constants.OFFICIAL_IMAGES:
|
|
430
430
|
warns.append(
|
|
431
|
-
f'Using custom image "{image_name}", we recommend using an
|
|
431
|
+
f'Using custom image "{image_name}", we recommend using an officially supported image: {constants.OFFICIAL_IMAGES}'
|
|
432
432
|
)
|
|
433
433
|
|
|
434
434
|
# prepare config options
|
|
@@ -467,12 +467,7 @@ def validate_localstack_config(name: str):
|
|
|
467
467
|
|
|
468
468
|
|
|
469
469
|
def get_docker_image_to_start():
|
|
470
|
-
|
|
471
|
-
if not image_name:
|
|
472
|
-
image_name = constants.DOCKER_IMAGE_NAME
|
|
473
|
-
if is_auth_token_configured():
|
|
474
|
-
image_name = constants.DOCKER_IMAGE_NAME_PRO
|
|
475
|
-
return image_name
|
|
470
|
+
return os.environ.get("IMAGE_NAME", constants.DOCKER_IMAGE_NAME_PRO)
|
|
476
471
|
|
|
477
472
|
|
|
478
473
|
def extract_port_flags(user_flags, port_mappings: PortMappings):
|
|
@@ -78,7 +78,7 @@ def get_endpoint_for_network(network: str | None = None) -> str:
|
|
|
78
78
|
else:
|
|
79
79
|
# In a non-Linux host-mode environment, we need to determine the IP of the host by running a container
|
|
80
80
|
# (basically macOS host mode, i.e. this is a feature to improve the developer experience)
|
|
81
|
-
image_name = constants.
|
|
81
|
+
image_name = constants.DOCKER_IMAGE_NAME_PRO
|
|
82
82
|
out, _ = DOCKER_CLIENT.run_container(
|
|
83
83
|
image_name,
|
|
84
84
|
remove=True,
|
|
@@ -4,7 +4,7 @@ import platform
|
|
|
4
4
|
import random
|
|
5
5
|
|
|
6
6
|
from localstack_cli import config
|
|
7
|
-
from localstack_cli.constants import DEFAULT_VOLUME_DIR,
|
|
7
|
+
from localstack_cli.constants import DEFAULT_VOLUME_DIR, DOCKER_IMAGE_NAME_PRO
|
|
8
8
|
from localstack_cli.utils.collections import ensure_list
|
|
9
9
|
from localstack_cli.utils.container_utils.container_client import (
|
|
10
10
|
ContainerClient,
|
|
@@ -266,7 +266,7 @@ def _get_ports_check_docker_image() -> str:
|
|
|
266
266
|
return container["Config"]["Image"]
|
|
267
267
|
except Exception:
|
|
268
268
|
# fall back to using the default Docker image
|
|
269
|
-
return
|
|
269
|
+
return DOCKER_IMAGE_NAME_PRO
|
|
270
270
|
|
|
271
271
|
|
|
272
272
|
DOCKER_CLIENT: ContainerClient = create_docker_client()
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '
|
|
32
|
-
__version_tuple__ = version_tuple = (
|
|
31
|
+
__version__ = version = '2026.3.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (2026, 3, 0)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g99b4cdb24'
|
|
@@ -60,8 +60,8 @@ def backup_and_remove_image(monkeypatch, container_client: ContainerClient):
|
|
|
60
60
|
up" - i.e. tag it with another tag, and restore it afterwards.
|
|
61
61
|
"""
|
|
62
62
|
|
|
63
|
-
source_image_name = f"{constants.
|
|
64
|
-
tagged_image_name = f"{constants.
|
|
63
|
+
source_image_name = f"{constants.DOCKER_IMAGE_NAME_PRO}:latest"
|
|
64
|
+
tagged_image_name = f"{constants.DOCKER_IMAGE_NAME_PRO}:backup"
|
|
65
65
|
container_client.tag_image(source_image_name, tagged_image_name)
|
|
66
66
|
container_client.remove_image(source_image_name, force=True)
|
|
67
67
|
monkeypatch.setenv("IMAGE_NAME", source_image_name)
|
|
@@ -74,7 +74,7 @@ def backup_and_remove_image(monkeypatch, container_client: ContainerClient):
|
|
|
74
74
|
@pytest.fixture
|
|
75
75
|
def create_container(container_client: ContainerClient):
|
|
76
76
|
"""Creates (but does not start) a docker container with the name 'localstack-main'"""
|
|
77
|
-
container_client.create_container(image_name="localstack/localstack", name="localstack-main")
|
|
77
|
+
container_client.create_container(image_name="localstack/localstack-pro", name="localstack-main")
|
|
78
78
|
yield
|
|
79
79
|
container_client.remove_container("localstack-main", force=True)
|
|
80
80
|
|
|
@@ -106,7 +106,7 @@ class TestCliContainerLifecycle:
|
|
|
106
106
|
|
|
107
107
|
@pytest.mark.usefixtures("backup_and_remove_image")
|
|
108
108
|
def test_pulling_image_message(self, runner, container_client: ContainerClient):
|
|
109
|
-
image_name_and_tag = f"{constants.
|
|
109
|
+
image_name_and_tag = f"{constants.DOCKER_IMAGE_NAME_PRO}:latest"
|
|
110
110
|
with pytest.raises(NoSuchImage):
|
|
111
111
|
container_client.inspect_image(image_name_and_tag, pull=False)
|
|
112
112
|
|
|
@@ -245,34 +245,11 @@ class TestCliContainerLifecycle:
|
|
|
245
245
|
output = container_client.exec_in_container(config.MAIN_CONTAINER_NAME, ["ps", "-fu", user])
|
|
246
246
|
assert "localstack-supervisor" in to_str(output[0])
|
|
247
247
|
|
|
248
|
-
@pytest.mark.skip(reason="Test assumes localstack module name, not localstack_cli")
|
|
249
|
-
def test_start_cli_within_container(self, runner, container_client, tmp_path):
|
|
250
|
-
output = container_client.run_container(
|
|
251
|
-
# CAVEAT: Updates to the Docker image are not immediately reflected when using the latest image from
|
|
252
|
-
# DockerHub in the CI. Re-build the Docker image locally through
|
|
253
|
-
# `IMAGE_NAME="localstack/localstack" ./bin/docker-helper.sh build` for local testing.
|
|
254
|
-
"localstack/localstack",
|
|
255
|
-
remove=True,
|
|
256
|
-
entrypoint="",
|
|
257
|
-
command=["bin/localstack", "start", "-d"],
|
|
258
|
-
volumes=[
|
|
259
|
-
("/var/run/docker.sock", "/var/run/docker.sock"),
|
|
260
|
-
(MODULE_MAIN_PATH, "/opt/code/localstack/localstack"),
|
|
261
|
-
],
|
|
262
|
-
env_vars={"LOCALSTACK_VOLUME_DIR": f"{tmp_path}/ls-volume"},
|
|
263
|
-
)
|
|
264
|
-
stdout = to_str(output[0])
|
|
265
|
-
assert "starting LocalStack" in stdout
|
|
266
|
-
assert "detaching" in stdout
|
|
267
|
-
|
|
268
|
-
# assert that container is running
|
|
269
|
-
runner.invoke(cli, ["wait", "-t", "60"])
|
|
270
|
-
|
|
271
248
|
def test_install_cli_in_container(self, container_client):
|
|
272
249
|
"""Test that the standalone CLI can be installed alongside the runtime in a container."""
|
|
273
250
|
# Mount the CLI source code and install it, then run a simple command
|
|
274
251
|
output = container_client.run_container(
|
|
275
|
-
"localstack/localstack",
|
|
252
|
+
"localstack/localstack-pro",
|
|
276
253
|
remove=True,
|
|
277
254
|
entrypoint="",
|
|
278
255
|
command=[
|
|
@@ -153,30 +153,6 @@ class TestCliContainerLifecycle:
|
|
|
153
153
|
output = container_client.exec_in_container(config.MAIN_CONTAINER_NAME, ["ps", "-fu", user])
|
|
154
154
|
assert "localstack-supervisor" in to_str(output[0])
|
|
155
155
|
|
|
156
|
-
@pytest.mark.skip(reason="Test assumes localstack module name, not localstack_cli")
|
|
157
|
-
def test_start_cli_within_container(self, runner, container_client):
|
|
158
|
-
output = container_client.run_container(
|
|
159
|
-
# CAVEAT: Updates to the Docker image are not immediately reflected when using the latest image from
|
|
160
|
-
# DockerHub in the CI.
|
|
161
|
-
# Re-build the Docker image locally with `bin/docker-helper.sh build` in community for local testing.
|
|
162
|
-
"localstack/localstack",
|
|
163
|
-
remove=True,
|
|
164
|
-
entrypoint="",
|
|
165
|
-
command=["bin/localstack", "start", "-d"],
|
|
166
|
-
volumes=[
|
|
167
|
-
("/var/run/docker.sock", "/var/run/docker.sock"),
|
|
168
|
-
(MODULE_MAIN_PATH, "/opt/code/localstack/localstack"),
|
|
169
|
-
],
|
|
170
|
-
env_vars={"LOCALSTACK_VOLUME_DIR": "/tmp/ls-volume"},
|
|
171
|
-
)
|
|
172
|
-
stdout = to_str(output[0])
|
|
173
|
-
assert "starting LocalStack" in stdout
|
|
174
|
-
assert "detaching" in stdout
|
|
175
|
-
|
|
176
|
-
# assert that container is running
|
|
177
|
-
runner.invoke(cli, ["wait", "-t", "180"])
|
|
178
|
-
|
|
179
|
-
|
|
180
156
|
@pytest.mark.skip(reason="TODO: fix test setup - extensions tests need investigation")
|
|
181
157
|
class TestExtensionsCli:
|
|
182
158
|
def test_extensions_install_with_IMAGE_NAME_installs_correct_venv_version(
|
|
@@ -156,7 +156,7 @@ def test_validate_config(runner, monkeypatch, tmp_path):
|
|
|
156
156
|
services:
|
|
157
157
|
localstack:
|
|
158
158
|
container_name: "${LOCALSTACK_DOCKER_NAME-localstack-main}"
|
|
159
|
-
image: localstack/localstack
|
|
159
|
+
image: localstack/localstack-pro
|
|
160
160
|
network_mode: bridge
|
|
161
161
|
ports:
|
|
162
162
|
- "127.0.0.1:53:53"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the grace period / LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT feature.
|
|
3
|
+
|
|
4
|
+
Temporary — this entire file should be removed when the grace period expires.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from unittest.mock import MagicMock
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
from localstack_cli.pro.core import config as pro_config
|
|
11
|
+
from localstack_cli.pro.core.bootstrap.licensingv2 import LicensingError
|
|
12
|
+
from localstack_cli.pro.core.plugins import (
|
|
13
|
+
GRACE_PERIOD_EXPIRED_MESSAGE,
|
|
14
|
+
LICENSE_ERROR_MESSAGE,
|
|
15
|
+
activate_pro_key_on_host,
|
|
16
|
+
)
|
|
17
|
+
from localstack_cli.runtime.exceptions import LocalstackExit
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _patch_licensing_failure(monkeypatch):
|
|
21
|
+
"""Set up a mock licensed environment that fails activation."""
|
|
22
|
+
mock_env = MagicMock()
|
|
23
|
+
mock_env.activate.side_effect = LicensingError("no credentials")
|
|
24
|
+
|
|
25
|
+
import localstack_cli.pro.core.plugins as plugins_module
|
|
26
|
+
|
|
27
|
+
monkeypatch.setattr(plugins_module.licensingv2, "get_licensed_environment", lambda: mock_env)
|
|
28
|
+
monkeypatch.setattr(plugins_module.licensingv2, "LicensingError", LicensingError)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_no_ack_grace_active_exits_with_license_message(monkeypatch):
|
|
32
|
+
"""No LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT + grace active should exit with license error message."""
|
|
33
|
+
import localstack_cli.pro.core.plugins as plugins_module
|
|
34
|
+
|
|
35
|
+
_patch_licensing_failure(monkeypatch)
|
|
36
|
+
monkeypatch.delenv("LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT", raising=False)
|
|
37
|
+
monkeypatch.delenv("ACKNOWLEDGE_ACCOUNT_REQUIREMENT", raising=False)
|
|
38
|
+
monkeypatch.setattr(plugins_module, "_check_grace_period_active", lambda ack: True)
|
|
39
|
+
|
|
40
|
+
with pytest.raises(LocalstackExit) as exc_info:
|
|
41
|
+
activate_pro_key_on_host()
|
|
42
|
+
|
|
43
|
+
assert "LocalStack requires an account to run" in str(exc_info.value)
|
|
44
|
+
assert "LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT=1" in str(exc_info.value)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_no_ack_grace_inactive_exits_with_licensing_error(monkeypatch):
|
|
48
|
+
"""No LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT + grace inactive should exit with LicensingError message."""
|
|
49
|
+
import localstack_cli.pro.core.plugins as plugins_module
|
|
50
|
+
|
|
51
|
+
_patch_licensing_failure(monkeypatch)
|
|
52
|
+
monkeypatch.delenv("LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT", raising=False)
|
|
53
|
+
monkeypatch.delenv("ACKNOWLEDGE_ACCOUNT_REQUIREMENT", raising=False)
|
|
54
|
+
monkeypatch.setattr(plugins_module, "_check_grace_period_active", lambda ack: False)
|
|
55
|
+
|
|
56
|
+
with pytest.raises(LocalstackExit) as exc_info:
|
|
57
|
+
activate_pro_key_on_host()
|
|
58
|
+
|
|
59
|
+
assert "no credentials" in str(exc_info.value)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
ACK_ENV_VARS = [
|
|
63
|
+
"LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT",
|
|
64
|
+
"ACKNOWLEDGE_ACCOUNT_REQUIREMENT",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.mark.parametrize("ack_env_var", ACK_ENV_VARS)
|
|
69
|
+
def test_ack_with_grace_active_starts_community(monkeypatch, ack_env_var):
|
|
70
|
+
"""ACKNOWLEDGE_ACCOUNT_REQUIREMENT=1 + grace period active should run in community mode."""
|
|
71
|
+
import localstack_cli.pro.core.plugins as plugins_module
|
|
72
|
+
|
|
73
|
+
_patch_licensing_failure(monkeypatch)
|
|
74
|
+
for var in ACK_ENV_VARS:
|
|
75
|
+
monkeypatch.delenv(var, raising=False)
|
|
76
|
+
monkeypatch.setenv(ack_env_var, "1")
|
|
77
|
+
monkeypatch.setattr(plugins_module, "_check_grace_period_active", lambda ack: True)
|
|
78
|
+
monkeypatch.setattr(pro_config, "ACTIVATE_PRO", True)
|
|
79
|
+
|
|
80
|
+
activate_pro_key_on_host()
|
|
81
|
+
|
|
82
|
+
assert pro_config.ACTIVATE_PRO is False
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@pytest.mark.parametrize("ack_env_var", ACK_ENV_VARS)
|
|
86
|
+
def test_ack_with_grace_expired_exits(monkeypatch, ack_env_var):
|
|
87
|
+
"""ACKNOWLEDGE_ACCOUNT_REQUIREMENT=1 + grace period expired/404 should exit with licensing error."""
|
|
88
|
+
import localstack_cli.pro.core.plugins as plugins_module
|
|
89
|
+
|
|
90
|
+
_patch_licensing_failure(monkeypatch)
|
|
91
|
+
for var in ACK_ENV_VARS:
|
|
92
|
+
monkeypatch.delenv(var, raising=False)
|
|
93
|
+
monkeypatch.setenv(ack_env_var, "1")
|
|
94
|
+
monkeypatch.setattr(plugins_module, "_check_grace_period_active", lambda ack: False)
|
|
95
|
+
monkeypatch.setattr(pro_config, "ACTIVATE_PRO", True)
|
|
96
|
+
|
|
97
|
+
with pytest.raises(LocalstackExit) as exc_info:
|
|
98
|
+
activate_pro_key_on_host()
|
|
99
|
+
|
|
100
|
+
assert GRACE_PERIOD_EXPIRED_MESSAGE in str(exc_info.value)
|
|
101
|
+
assert pro_config.ACTIVATE_PRO is False
|