toast-cli 4.1.2__tar.gz → 4.1.3__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.
- {toast_cli-4.1.2 → toast_cli-4.1.3}/PKG-INFO +5 -3
- {toast_cli-4.1.2 → toast_cli-4.1.3}/README.md +4 -2
- toast_cli-4.1.3/VERSION +1 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/tests/test_storage.py +40 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/storage.py +41 -6
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/PKG-INFO +5 -3
- toast_cli-4.1.2/VERSION +0 -1
- {toast_cli-4.1.2 → toast_cli-4.1.3}/.mergify.yml +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/ARCHITECTURE.md +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/LICENSE +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/MANIFEST.in +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/pyproject.toml +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/setup.cfg +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/setup.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/__init__.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/__main__.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/helpers.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/__init__.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/am_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/base_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/cdw_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/ctx_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/dot_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/env_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/git_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/prompt_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/region_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/ssm_plugin.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/utils.py +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/SOURCES.txt +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/dependency_links.txt +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/entry_points.txt +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/not-zip-safe +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/requires.txt +0 -0
- {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: toast-cli
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.3
|
|
4
4
|
Summary: A Python-based CLI utility with a plugin architecture for AWS, Kubernetes, Git, and more
|
|
5
5
|
Home-page: https://github.com/opspresso/toast-cli
|
|
6
6
|
Author: nalbam
|
|
@@ -245,12 +245,14 @@ Environment variables:
|
|
|
245
245
|
TOAST_ENV_STORE_PROFILE # default: {username}-admin
|
|
246
246
|
TOAST_ENV_STORE_BUCKET # default: env-store-{account-id of the profile}
|
|
247
247
|
TOAST_ENV_STORE_KMS_KEY # default: bucket/account default KMS key
|
|
248
|
-
TOAST_ENV_STORE_REGION # default: profile's region
|
|
248
|
+
TOAST_ENV_STORE_REGION # default: profile's region (SSM reads fall back to us-east-1)
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
The profile defaults to your OS username + `-admin`, and the bucket defaults to
|
|
252
252
|
`env-store-` + the AWS account id of that profile (looked up via
|
|
253
|
-
`aws sts get-caller-identity`).
|
|
253
|
+
`aws sts get-caller-identity`). The region resolves to the configured value, else
|
|
254
|
+
the profile's region; SSM reads (which require a region) fall back to `us-east-1`
|
|
255
|
+
so a missing parameter is reported as absent rather than failing.
|
|
254
256
|
|
|
255
257
|
Config file `~/.config/toast/config` (`KEY=VALUE` format). On first run, if it
|
|
256
258
|
is missing, toast prompts for the values and saves them (interactive sessions
|
|
@@ -210,12 +210,14 @@ Environment variables:
|
|
|
210
210
|
TOAST_ENV_STORE_PROFILE # default: {username}-admin
|
|
211
211
|
TOAST_ENV_STORE_BUCKET # default: env-store-{account-id of the profile}
|
|
212
212
|
TOAST_ENV_STORE_KMS_KEY # default: bucket/account default KMS key
|
|
213
|
-
TOAST_ENV_STORE_REGION # default: profile's region
|
|
213
|
+
TOAST_ENV_STORE_REGION # default: profile's region (SSM reads fall back to us-east-1)
|
|
214
214
|
```
|
|
215
215
|
|
|
216
216
|
The profile defaults to your OS username + `-admin`, and the bucket defaults to
|
|
217
217
|
`env-store-` + the AWS account id of that profile (looked up via
|
|
218
|
-
`aws sts get-caller-identity`).
|
|
218
|
+
`aws sts get-caller-identity`). The region resolves to the configured value, else
|
|
219
|
+
the profile's region; SSM reads (which require a region) fall back to `us-east-1`
|
|
220
|
+
so a missing parameter is reported as absent rather than failing.
|
|
219
221
|
|
|
220
222
|
Config file `~/.config/toast/config` (`KEY=VALUE` format). On first run, if it
|
|
221
223
|
is missing, toast prompts for the values and saves them (interactive sessions
|
toast_cli-4.1.3/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
v4.1.3
|
|
@@ -68,6 +68,12 @@ class DefaultsTests(unittest.TestCase):
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
class ConfigTests(unittest.TestCase):
|
|
71
|
+
def setUp(self):
|
|
72
|
+
# Isolate the `aws configure get region` lookup from the environment.
|
|
73
|
+
p = mock.patch.object(storage, "_profile_region", return_value=None)
|
|
74
|
+
p.start()
|
|
75
|
+
self.addCleanup(p.stop)
|
|
76
|
+
|
|
71
77
|
def test_defaults_no_file(self):
|
|
72
78
|
with tempfile.TemporaryDirectory() as d:
|
|
73
79
|
path = os.path.join(d, "config")
|
|
@@ -80,6 +86,15 @@ class ConfigTests(unittest.TestCase):
|
|
|
80
86
|
self.assertIsNone(c.kms_key)
|
|
81
87
|
self.assertIsNone(c.region)
|
|
82
88
|
|
|
89
|
+
def test_region_from_profile(self):
|
|
90
|
+
with tempfile.TemporaryDirectory() as d:
|
|
91
|
+
path = os.path.join(d, "config")
|
|
92
|
+
with mock.patch.dict(os.environ, {}, clear=True), mock.patch.object(
|
|
93
|
+
storage, "_profile_region", return_value="ap-northeast-2"
|
|
94
|
+
), mock.patch.object(storage, "get_account_id", return_value="111122223333"):
|
|
95
|
+
c = storage.resolve_config(config_path=path, create=False)
|
|
96
|
+
self.assertEqual(c.region, "ap-northeast-2")
|
|
97
|
+
|
|
83
98
|
def test_bucket_none_on_sts_failure(self):
|
|
84
99
|
with tempfile.TemporaryDirectory() as d:
|
|
85
100
|
path = os.path.join(d, "config")
|
|
@@ -230,6 +245,11 @@ class AwsCommandTests(unittest.TestCase):
|
|
|
230
245
|
cmd = storage._aws(cfg, ["ssm", "get-parameter"])
|
|
231
246
|
self.assertEqual(cmd[cmd.index("--region") + 1], "us-west-2")
|
|
232
247
|
|
|
248
|
+
def test_region_override_takes_precedence(self):
|
|
249
|
+
cfg = storage.StoreConfig("b", "p", None, None)
|
|
250
|
+
cmd = storage._aws(cfg, ["ssm", "x"], region="ap-northeast-2")
|
|
251
|
+
self.assertEqual(cmd[cmd.index("--region") + 1], "ap-northeast-2")
|
|
252
|
+
|
|
233
253
|
def test_s3_put_uses_kms_server_side_encryption(self):
|
|
234
254
|
cfg = storage.StoreConfig("b", "p", None, None)
|
|
235
255
|
captured = {}
|
|
@@ -258,6 +278,26 @@ class AwsCommandTests(unittest.TestCase):
|
|
|
258
278
|
"/toast/local/o/p/env-local", profile="myprofile", region="eu-west-1"
|
|
259
279
|
)
|
|
260
280
|
|
|
281
|
+
def test_ssm_get_falls_back_to_default_region(self):
|
|
282
|
+
cfg = storage.StoreConfig("b", "myprofile", None, None)
|
|
283
|
+
with mock.patch.object(
|
|
284
|
+
storage, "get_ssm_parameter", return_value=("v", "t", None)
|
|
285
|
+
) as m:
|
|
286
|
+
storage.ssm_get(cfg, "/toast/local/o/p/env-local")
|
|
287
|
+
m.assert_called_once_with(
|
|
288
|
+
"/toast/local/o/p/env-local",
|
|
289
|
+
profile="myprofile",
|
|
290
|
+
region=storage.DEFAULT_REGION,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def test_profile_region_lookup(self):
|
|
294
|
+
ok = mock.Mock(returncode=0, stdout="ap-northeast-2\n", stderr="")
|
|
295
|
+
with mock.patch.object(storage.subprocess, "run", return_value=ok):
|
|
296
|
+
self.assertEqual(storage._profile_region("p"), "ap-northeast-2")
|
|
297
|
+
empty = mock.Mock(returncode=0, stdout="\n", stderr="")
|
|
298
|
+
with mock.patch.object(storage.subprocess, "run", return_value=empty):
|
|
299
|
+
self.assertIsNone(storage._profile_region("p"))
|
|
300
|
+
|
|
261
301
|
|
|
262
302
|
class StoreReadTests(unittest.TestCase):
|
|
263
303
|
def _cfg(self):
|
|
@@ -40,6 +40,11 @@ console = Console()
|
|
|
40
40
|
PROFILE_SUFFIX = "-admin"
|
|
41
41
|
BUCKET_PREFIX = "env-store-"
|
|
42
42
|
|
|
43
|
+
# SSM get-parameter requires a region. When none is configured (env/file/profile)
|
|
44
|
+
# this fallback keeps SSM reads from failing with NoRegion; a missing parameter
|
|
45
|
+
# then returns ParameterNotFound (treated as absent) instead of an error.
|
|
46
|
+
DEFAULT_REGION = "us-east-1"
|
|
47
|
+
|
|
43
48
|
|
|
44
49
|
class StoreConfig:
|
|
45
50
|
"""Resolved env-store configuration."""
|
|
@@ -154,6 +159,21 @@ def default_profile():
|
|
|
154
159
|
return f"{_current_username()}{PROFILE_SUFFIX}"
|
|
155
160
|
|
|
156
161
|
|
|
162
|
+
def _profile_region(profile):
|
|
163
|
+
"""Region configured for a profile via `aws configure get region`, or None."""
|
|
164
|
+
try:
|
|
165
|
+
result = subprocess.run(
|
|
166
|
+
["aws", "configure", "get", "region", "--profile", profile],
|
|
167
|
+
capture_output=True,
|
|
168
|
+
text=True,
|
|
169
|
+
)
|
|
170
|
+
if result.returncode != 0:
|
|
171
|
+
return None
|
|
172
|
+
return result.stdout.strip() or None
|
|
173
|
+
except Exception:
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
|
|
157
177
|
def get_account_id(profile, region=None):
|
|
158
178
|
"""Return the AWS account id for a profile via STS, or None on failure."""
|
|
159
179
|
cmd = [
|
|
@@ -205,7 +225,11 @@ def resolve_config(config_path=None, create=True):
|
|
|
205
225
|
return os.environ.get(env_key) or file_cfg.get(file_key) or default
|
|
206
226
|
|
|
207
227
|
profile = pick("TOAST_ENV_STORE_PROFILE", "ENV_STORE_PROFILE", default_profile())
|
|
208
|
-
region =
|
|
228
|
+
region = (
|
|
229
|
+
pick("TOAST_ENV_STORE_REGION", "ENV_STORE_REGION", "")
|
|
230
|
+
or _profile_region(profile)
|
|
231
|
+
or None
|
|
232
|
+
)
|
|
209
233
|
kms_key = pick("TOAST_ENV_STORE_KMS_KEY", "ENV_STORE_KMS_KEY", "") or None
|
|
210
234
|
bucket = (
|
|
211
235
|
os.environ.get("TOAST_ENV_STORE_BUCKET")
|
|
@@ -278,14 +302,24 @@ def parse_timestamp(value):
|
|
|
278
302
|
return dt.astimezone(timezone.utc)
|
|
279
303
|
|
|
280
304
|
|
|
281
|
-
def _aws(config, service_args):
|
|
282
|
-
"""Build an aws CLI command with the env-store profile (and region) applied.
|
|
305
|
+
def _aws(config, service_args, region=None):
|
|
306
|
+
"""Build an aws CLI command with the env-store profile (and region) applied.
|
|
307
|
+
|
|
308
|
+
A `region` override takes precedence over config.region (used for SSM, which
|
|
309
|
+
requires a region even when none is configured).
|
|
310
|
+
"""
|
|
283
311
|
cmd = ["aws"] + service_args + ["--profile", config.profile]
|
|
284
|
-
|
|
285
|
-
|
|
312
|
+
resolved = region or config.region
|
|
313
|
+
if resolved:
|
|
314
|
+
cmd += ["--region", resolved]
|
|
286
315
|
return cmd
|
|
287
316
|
|
|
288
317
|
|
|
318
|
+
def _ssm_region(config):
|
|
319
|
+
"""Region for SSM calls: configured region, else a safe default."""
|
|
320
|
+
return config.region or DEFAULT_REGION
|
|
321
|
+
|
|
322
|
+
|
|
289
323
|
def s3_get(config, key):
|
|
290
324
|
"""Get an object's content and LastModified.
|
|
291
325
|
|
|
@@ -417,7 +451,7 @@ def s3_list(config, prefix="local/"):
|
|
|
417
451
|
|
|
418
452
|
def ssm_get(config, path):
|
|
419
453
|
"""Get an SSM parameter value and LastModified using the env-store profile."""
|
|
420
|
-
return get_ssm_parameter(path, profile=config.profile, region=config
|
|
454
|
+
return get_ssm_parameter(path, profile=config.profile, region=_ssm_region(config))
|
|
421
455
|
|
|
422
456
|
|
|
423
457
|
def ssm_list(config, prefix="/toast/local/"):
|
|
@@ -438,6 +472,7 @@ def ssm_list(config, prefix="/toast/local/"):
|
|
|
438
472
|
"--output",
|
|
439
473
|
"json",
|
|
440
474
|
],
|
|
475
|
+
region=_ssm_region(config),
|
|
441
476
|
),
|
|
442
477
|
capture_output=True,
|
|
443
478
|
text=True,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: toast-cli
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.3
|
|
4
4
|
Summary: A Python-based CLI utility with a plugin architecture for AWS, Kubernetes, Git, and more
|
|
5
5
|
Home-page: https://github.com/opspresso/toast-cli
|
|
6
6
|
Author: nalbam
|
|
@@ -245,12 +245,14 @@ Environment variables:
|
|
|
245
245
|
TOAST_ENV_STORE_PROFILE # default: {username}-admin
|
|
246
246
|
TOAST_ENV_STORE_BUCKET # default: env-store-{account-id of the profile}
|
|
247
247
|
TOAST_ENV_STORE_KMS_KEY # default: bucket/account default KMS key
|
|
248
|
-
TOAST_ENV_STORE_REGION # default: profile's region
|
|
248
|
+
TOAST_ENV_STORE_REGION # default: profile's region (SSM reads fall back to us-east-1)
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
The profile defaults to your OS username + `-admin`, and the bucket defaults to
|
|
252
252
|
`env-store-` + the AWS account id of that profile (looked up via
|
|
253
|
-
`aws sts get-caller-identity`).
|
|
253
|
+
`aws sts get-caller-identity`). The region resolves to the configured value, else
|
|
254
|
+
the profile's region; SSM reads (which require a region) fall back to `us-east-1`
|
|
255
|
+
so a missing parameter is reported as absent rather than failing.
|
|
254
256
|
|
|
255
257
|
Config file `~/.config/toast/config` (`KEY=VALUE` format). On first run, if it
|
|
256
258
|
is missing, toast prompts for the values and saves them (interactive sessions
|
toast_cli-4.1.2/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
v4.1.2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|