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.
Files changed (35) hide show
  1. {toast_cli-4.1.2 → toast_cli-4.1.3}/PKG-INFO +5 -3
  2. {toast_cli-4.1.2 → toast_cli-4.1.3}/README.md +4 -2
  3. toast_cli-4.1.3/VERSION +1 -0
  4. {toast_cli-4.1.2 → toast_cli-4.1.3}/tests/test_storage.py +40 -0
  5. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/storage.py +41 -6
  6. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/PKG-INFO +5 -3
  7. toast_cli-4.1.2/VERSION +0 -1
  8. {toast_cli-4.1.2 → toast_cli-4.1.3}/.mergify.yml +0 -0
  9. {toast_cli-4.1.2 → toast_cli-4.1.3}/ARCHITECTURE.md +0 -0
  10. {toast_cli-4.1.2 → toast_cli-4.1.3}/LICENSE +0 -0
  11. {toast_cli-4.1.2 → toast_cli-4.1.3}/MANIFEST.in +0 -0
  12. {toast_cli-4.1.2 → toast_cli-4.1.3}/pyproject.toml +0 -0
  13. {toast_cli-4.1.2 → toast_cli-4.1.3}/setup.cfg +0 -0
  14. {toast_cli-4.1.2 → toast_cli-4.1.3}/setup.py +0 -0
  15. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/__init__.py +0 -0
  16. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/__main__.py +0 -0
  17. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/helpers.py +0 -0
  18. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/__init__.py +0 -0
  19. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/am_plugin.py +0 -0
  20. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/base_plugin.py +0 -0
  21. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/cdw_plugin.py +0 -0
  22. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/ctx_plugin.py +0 -0
  23. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/dot_plugin.py +0 -0
  24. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/env_plugin.py +0 -0
  25. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/git_plugin.py +0 -0
  26. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/prompt_plugin.py +0 -0
  27. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/region_plugin.py +0 -0
  28. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/ssm_plugin.py +0 -0
  29. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast/plugins/utils.py +0 -0
  30. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/SOURCES.txt +0 -0
  31. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/dependency_links.txt +0 -0
  32. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/entry_points.txt +0 -0
  33. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/not-zip-safe +0 -0
  34. {toast_cli-4.1.2 → toast_cli-4.1.3}/toast_cli.egg-info/requires.txt +0 -0
  35. {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.2
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
@@ -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 = pick("TOAST_ENV_STORE_REGION", "ENV_STORE_REGION", "") or None
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
- if config.region:
285
- cmd += ["--region", config.region]
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.region)
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.2
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