yellowdog-python-examples 8.1.3__tar.gz → 8.1.4__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 (80) hide show
  1. {yellowdog_python_examples-8.1.3/yellowdog_python_examples.egg-info → yellowdog_python_examples-8.1.4}/PKG-INFO +2 -2
  2. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/README.md +4 -4
  3. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/requirements.txt +1 -1
  4. yellowdog_python_examples-8.1.4/yellowdog_cli/__init__.py +1 -0
  5. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/create.py +27 -37
  6. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/instantiate.py +5 -5
  7. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/provision.py +4 -4
  8. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/remove.py +15 -15
  9. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/entity_utils.py +173 -127
  10. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/provision_utils.py +8 -29
  11. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4/yellowdog_python_examples.egg-info}/PKG-INFO +2 -2
  12. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_python_examples.egg-info/requires.txt +1 -1
  13. yellowdog_python_examples-8.1.3/yellowdog_cli/__init__.py +0 -1
  14. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/LICENSE +0 -0
  15. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/PYPI_README.md +0 -0
  16. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/pyproject.toml +0 -0
  17. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/setup.cfg +0 -0
  18. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/tests/test_create_remove.py +0 -0
  19. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/tests/test_demos.py +0 -0
  20. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/tests/test_dryruns.py +0 -0
  21. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/tests/test_entrypoints.py +0 -0
  22. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/tests/test_gui.py +0 -0
  23. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/tests/test_list.py +0 -0
  24. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/tests/test_objects.py +0 -0
  25. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/tests/test_variable_processing.py +0 -0
  26. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/abort.py +0 -0
  27. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/admin.py +0 -0
  28. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/boost.py +0 -0
  29. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/cancel.py +0 -0
  30. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/cloudwizard.py +0 -0
  31. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/compare.py +0 -0
  32. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/delete.py +0 -0
  33. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/download.py +0 -0
  34. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/finish.py +0 -0
  35. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/follow.py +0 -0
  36. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/format_json.py +0 -0
  37. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/hold.py +0 -0
  38. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/jsonnet2json.py +0 -0
  39. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/list.py +0 -0
  40. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/resize.py +0 -0
  41. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/show.py +0 -0
  42. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/shutdown.py +0 -0
  43. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/start.py +0 -0
  44. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/submit.py +0 -0
  45. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/terminate.py +0 -0
  46. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/upload.py +0 -0
  47. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/__init__.py +0 -0
  48. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/args.py +0 -0
  49. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/check_imports.py +0 -0
  50. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/cloudwizard_aws.py +0 -0
  51. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/cloudwizard_aws_types.py +0 -0
  52. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/cloudwizard_azure.py +0 -0
  53. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/cloudwizard_common.py +0 -0
  54. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/cloudwizard_gcp.py +0 -0
  55. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/compact_json.py +0 -0
  56. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/config_types.py +0 -0
  57. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/csv_data.py +0 -0
  58. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/follow_utils.py +0 -0
  59. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/interactive.py +0 -0
  60. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/items.py +0 -0
  61. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/load_config.py +0 -0
  62. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/load_resources.py +0 -0
  63. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/misc_utils.py +0 -0
  64. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/printing.py +0 -0
  65. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/property_names.py +0 -0
  66. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/rich_console_input_fixed.py +0 -0
  67. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/settings.py +0 -0
  68. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/start_hold_common.py +0 -0
  69. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/submit_utils.py +0 -0
  70. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/type_check.py +0 -0
  71. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/upload_utils.py +0 -0
  72. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/validate_properties.py +0 -0
  73. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/variables.py +0 -0
  74. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/wrapper.py +0 -0
  75. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/utils/ydid_utils.py +0 -0
  76. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_cli/version.py +0 -0
  77. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_python_examples.egg-info/SOURCES.txt +0 -0
  78. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_python_examples.egg-info/dependency_links.txt +0 -0
  79. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_python_examples.egg-info/entry_points.txt +0 -0
  80. {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.4}/yellowdog_python_examples.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yellowdog-python-examples
3
- Version: 8.1.3
3
+ Version: 8.1.4
4
4
  Summary: Python CLI commands using the YellowDog Python SDK
5
5
  Author-email: YellowDog Limited <support@yellowdog.co>
6
6
  License-Expression: Apache-2.0
@@ -26,7 +26,7 @@ Requires-Dist: requests
26
26
  Requires-Dist: rich==13.9.4
27
27
  Requires-Dist: tabulate>=0.9.0
28
28
  Requires-Dist: toml
29
- Requires-Dist: yellowdog-sdk>=11.5.0
29
+ Requires-Dist: yellowdog-sdk>=11.6.0
30
30
  Provides-Extra: jsonnet
31
31
  Requires-Dist: jsonnet; extra == "jsonnet"
32
32
  Provides-Extra: cloudwizard
@@ -1693,7 +1693,7 @@ The following properties are available:
1693
1693
  |:------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------|:------------------------|
1694
1694
  | `idleNodeTimeout` | The timeout in minutes after which an idle node will be shut down. Set this to `0` to disable the timeout. | `5.0` |
1695
1695
  | `idlePoolTimeout` | The timeout in minutes after which an idle Worker Pool will be shut down. Set this to `0` to disable the timeout. | `30.0` |
1696
- | `imagesId` | The image ID, Image Family ID, Image Family name, or Image Group name to use when booting instances. | |
1696
+ | `imagesId` | The Image ID, Image Family ID, Image Family name, or Image Group name to use when booting instances. | |
1697
1697
  | `instanceTags` | The dictionary of instance tags to apply to the instances. Tag names must be lower case. | |
1698
1698
  | `maintainInstanceCount` | Only used when instantiating Compute Requirements; attempt to maintain the requested number of instances. | `False` |
1699
1699
  | `maxNodes` | The maximum number of nodes to which the Worker Pool can be scaled up. | `1` |
@@ -1717,7 +1717,7 @@ The following properties are available:
1717
1717
 
1718
1718
  The `templateId` property can be directly populated with the YellowDog ID (YDID), or it can be populated with the textual name of the template, in the form `namespace/template_name`.
1719
1719
 
1720
- Similarly, the `imagesId` property can be populated with the YDID of an Image Family, Image Group, Image, or a string representing the native name of a cloud provider image (e.g., an AWS AMI). It can also be populated with an Image Family name in the form `namespace/image_family_name`, or an Image Group name in the form `namespace/image_family_name/image_group_name`.
1720
+ Similarly, the `imagesId` property can be populated with the YDID of an Image Family, Image Group, Image, or a string representing the native name of a cloud provider image (e.g., an AWS AMI). It can also be populated with an Image Family name in the form `namespace/image_family_name`, or an Image Group name in the form `namespace/image_family_name/image_group_name` or `image_family_name/image_group_name`. Optionally, a `yd/` prefix can be supplied. The CLI will aim to map the provided name into an Image Family or Group YDID.
1721
1721
 
1722
1722
  ## Automatic Properties
1723
1723
 
@@ -2171,7 +2171,7 @@ An example Compute Source resource specification is found below:
2171
2171
  }
2172
2172
  ```
2173
2173
 
2174
- In the Compute Source Template `imageId` property, an Image Family name **namespace/family-name** or Image Group name **namespace/family-name/group-name** may be used instead of an ID. For example: `"imageId": "yellowdog/yd-agent-docker"`. The `yd-create` command will look up the Image Family name and substitute its ID. A **`yd/`** prefix may also optionally be used.
2174
+ In the Compute Source Template `imageId` property, an Image Family name **namespace/family-name** or Image Group name **namespace/family-name/group-name** may be used instead of an ID. For example: `"imageId": "yellowdog/yd-agent-docker"`. The `yd-create` command will look up the Image Family name and substitute with a well-formed name or ID. A **`yd/`** prefix may also optionally be used.
2175
2175
 
2176
2176
  ## Compute Requirement Templates
2177
2177
 
@@ -2197,7 +2197,7 @@ An example Compute Requirement resource specification is found below, for a **st
2197
2197
 
2198
2198
  Note that Compute Source Template **namespace/names** in the form `namespace/compute_source_template_name` can be used instead of their IDs: the **yd-create** command will look up the IDs and make the substitutions. The Compute Source Templates must already exist.
2199
2199
 
2200
- Also, In the `imagesId` property, an Image Family name **namespace/family-name** or an Image Group name **namespace/family-name/group-name** may be used instead of an ID. For example: `"imagesId": "yellowdog/yd-agent-docker/latest"`. The `yd-create` command will look up the Image Family name and substitute its ID. A **`yd/`** prefix may also optionally be used.
2200
+ Also, In the `imagesId` property, an Image Family name **namespace/family-name** or an Image Group name **namespace/family-name/group-name** may be used instead of an ID. For example: `"imagesId": "yellowdog/yd-agent-docker/latest"`. The `yd-create` command will look up the Image Family name and substitute with a well-formed name or ID. A **`yd/`** prefix may also optionally be used.
2201
2201
 
2202
2202
  A **dynamic** template example is:
2203
2203
 
@@ -5,4 +5,4 @@ requests
5
5
  rich == 13.9.4
6
6
  tabulate >= 0.9.0
7
7
  toml
8
- yellowdog-sdk >= 11.5.0
8
+ yellowdog-sdk >= 11.6.0
@@ -0,0 +1 @@
1
+ __version__ = "8.1.4"
@@ -50,10 +50,10 @@ from yellowdog_cli.utils.entity_utils import (
50
50
  clear_application_caches,
51
51
  clear_compute_source_template_cache,
52
52
  clear_group_caches,
53
- clear_image_family_search_cache,
53
+ clear_image_caches,
54
54
  find_compute_requirement_template_id_by_name,
55
55
  find_compute_source_template_id_by_name,
56
- find_image_family_or_group_id_by_name,
56
+ find_image_name_or_id,
57
57
  get_application_groups,
58
58
  get_application_id_by_name,
59
59
  get_group_id_by_name,
@@ -130,7 +130,9 @@ from yellowdog_cli.utils.wrapper import ARGS_PARSER, CLIENT, CONFIG_COMMON, main
130
130
  from yellowdog_cli.utils.ydid_utils import YDIDType, get_ydid_type
131
131
 
132
132
  CLEAR_CST_CACHE: bool = False # Track whether the CST cache needs to be cleared
133
- CLEAR_IMAGE_FAMILY_CACHE: bool = False # Track whether the IF cache needs to be cleared
133
+ CLEAR_IMAGE_FAMILY_CACHE: bool = (
134
+ False # Track whether the image caches need to be cleared
135
+ )
134
136
 
135
137
 
136
138
  @main_wrapper
@@ -233,7 +235,7 @@ def create_compute_source_template(resource: Dict):
233
235
  # Allow image families (etc.) to be referenced by name rather than ID
234
236
  global CLEAR_IMAGE_FAMILY_CACHE
235
237
  if CLEAR_IMAGE_FAMILY_CACHE: # Update the IF cache if required
236
- clear_image_family_search_cache()
238
+ clear_image_caches()
237
239
  CLEAR_IMAGE_FAMILY_CACHE = False
238
240
 
239
241
  # Google CSTs use property name 'image' instead of 'imageId'
@@ -244,17 +246,14 @@ def create_compute_source_template(resource: Dict):
244
246
  else PROP_IMAGE
245
247
  )
246
248
 
247
- image_id = source.get(image_property_name)
248
- if get_ydid_type(image_id) not in [
249
- YDIDType.IMAGE_FAMILY,
250
- YDIDType.IMAGE_GROUP,
251
- YDIDType.IMAGE,
252
- ]:
253
- image_family_id = find_image_family_or_group_id_by_name(
254
- client=CLIENT, image_family_name=image_id
255
- )
256
- if image_family_id is not None:
257
- source[image_property_name] = image_family_id
249
+ image_id = find_image_name_or_id(
250
+ client=CLIENT,
251
+ image_name_or_id=source.get(image_property_name),
252
+ always_return_id=False,
253
+ report_substitutions=True,
254
+ )
255
+ if image_id is not None:
256
+ source[image_property_name] = image_id
258
257
 
259
258
  if ARGS_PARSER.dry_run:
260
259
  resource[PROP_SOURCE] = source
@@ -322,31 +321,26 @@ def create_compute_requirement_template(resource: Dict):
322
321
  # Allow image families to be referenced by name rather than ID
323
322
  global CLEAR_IMAGE_FAMILY_CACHE
324
323
  if CLEAR_IMAGE_FAMILY_CACHE: # Update the IF cache if required
325
- clear_compute_source_template_cache()
324
+ clear_image_caches()
326
325
  CLEAR_IMAGE_FAMILY_CACHE = False
327
326
 
328
- def _get_images_id(image_str: str, context: Dict, key: str) -> int:
327
+ def _get_images_id(image_str: str, context: Dict, key: str):
329
328
  """
330
- Helper function to match an image family name into an ID.
329
+ Helper function to resolve an image ID.
331
330
  """
332
- if get_ydid_type(image_str) not in [
333
- YDIDType.IMAGE_FAMILY,
334
- YDIDType.IMAGE_GROUP,
335
- YDIDType.IMAGE,
336
- ]:
337
- image_family_id = find_image_family_or_group_id_by_name(
338
- client=CLIENT, image_family_name=image_str
339
- )
340
- if image_family_id is not None:
341
- context[key] = image_family_id
342
- return 1
343
- return 0
331
+ images_id_ = find_image_name_or_id(
332
+ client=CLIENT,
333
+ image_name_or_id=image_str,
334
+ always_return_id=False,
335
+ report_substitutions=True,
336
+ )
337
+ if images_id_ is not None:
338
+ context[key] = images_id_
344
339
 
345
340
  # Prepend the namespace when searching for existing templates
346
341
  name = f"{namespace}{NAMESPACE_PREFIX_SEPARATOR}{name}"
347
342
 
348
343
  source_template_substitutions = 0
349
- source_image_id_substitutions = 0
350
344
 
351
345
  # Dynamic templates don't have 'sources'; return '[]'
352
346
  for source in resource.get(PROP_SOURCES, []):
@@ -365,16 +359,12 @@ def create_compute_requirement_template(resource: Dict):
365
359
 
366
360
  source_image_id = source.get(PROP_IMAGE_ID)
367
361
  if source_image_id is not None:
368
- source_image_id_substitutions += _get_images_id(
369
- source_image_id, source, PROP_IMAGE_ID
370
- )
362
+ _get_images_id(source_image_id, source, PROP_IMAGE_ID)
371
363
 
372
364
  if source_template_substitutions > 0:
373
365
  print_log(
374
366
  f"Replaced {source_template_substitutions} Compute Source Template name(s) with ID(s)"
375
367
  )
376
- if source_image_id_substitutions > 0:
377
- print_log(f"Replaced {source_image_id_substitutions} Image name(s) with ID(s)")
378
368
 
379
369
  images_id = resource.get(PROP_IMAGES_ID)
380
370
  if images_id is not None:
@@ -549,7 +539,7 @@ def create_image_family(resource):
549
539
  # This will create the Image Family and all of its constituent
550
540
  # Image Group/Image resources
551
541
  image_family = _create_image_family(image_family, fq_name)
552
- print_log(f"Created Machine Image Family '{fq_name}' ('{image_family.id}')")
542
+ print_log(f"Created Machine Image Family '{fq_name}' ({image_family.id})")
553
543
  if ARGS_PARSER.quiet:
554
544
  print(image_family.id)
555
545
  else:
@@ -30,7 +30,7 @@ from yellowdog_cli.utils.printing import (
30
30
  print_yd_object,
31
31
  )
32
32
  from yellowdog_cli.utils.provision_utils import (
33
- get_image_family_id,
33
+ get_image_id,
34
34
  get_template_id,
35
35
  get_user_data_property,
36
36
  )
@@ -92,8 +92,8 @@ def main():
92
92
 
93
93
  # Allow use of IF name instead of ID
94
94
  if CONFIG_WP.images_id is not None:
95
- CONFIG_WP.images_id = get_image_family_id(
96
- client=CLIENT, image_family_id_or_name=CONFIG_WP.images_id
95
+ CONFIG_WP.images_id = get_image_id(
96
+ client=CLIENT, image_name_or_id=CONFIG_WP.images_id
97
97
  )
98
98
 
99
99
  if not ARGS_PARSER.report:
@@ -303,8 +303,8 @@ def _create_compute_requirement_from_json(
303
303
 
304
304
  # Allow use of IF name instead of ID
305
305
  if cr_data.get("imagesId") is not None:
306
- cr_data["imagesId"] = get_image_family_id(
307
- client=CLIENT, image_family_id_or_name=cr_data["imagesId"]
306
+ cr_data["imagesId"] = get_image_id(
307
+ client=CLIENT, image_name_or_id=cr_data["imagesId"]
308
308
  )
309
309
 
310
310
  if ARGS_PARSER.dry_run:
@@ -47,7 +47,7 @@ from yellowdog_cli.utils.property_names import (
47
47
  WORKER_TAG,
48
48
  )
49
49
  from yellowdog_cli.utils.provision_utils import (
50
- get_image_family_id,
50
+ get_image_id,
51
51
  get_template_id,
52
52
  get_user_data_property,
53
53
  )
@@ -170,7 +170,7 @@ def create_worker_pool_from_json(wp_json_file: str) -> None:
170
170
 
171
171
  # Allow Image Family name to be used instead of ID
172
172
  if reqt_template_usage.get(IMAGES_ID) is not None:
173
- reqt_template_usage[IMAGES_ID] = get_image_family_id(
173
+ reqt_template_usage[IMAGES_ID] = get_image_id(
174
174
  CLIENT, reqt_template_usage[IMAGES_ID]
175
175
  )
176
176
 
@@ -289,8 +289,8 @@ def create_worker_pool_from_toml():
289
289
 
290
290
  # Allow the Image Family name to be used instead of ID
291
291
  if CONFIG_WP.images_id is not None:
292
- CONFIG_WP.images_id = get_image_family_id(
293
- client=CLIENT, image_family_id_or_name=CONFIG_WP.images_id
292
+ CONFIG_WP.images_id = get_image_id(
293
+ client=CLIENT, image_name_or_id=CONFIG_WP.images_id
294
294
  )
295
295
 
296
296
  node_boot_timeout = (
@@ -207,17 +207,17 @@ def remove_keyring(resource: Dict):
207
207
  print_error(f"Expected property to be defined ({e})")
208
208
  return
209
209
 
210
- if not confirmed(f"Delete Keyring '{name}'?"):
210
+ if not confirmed(f"Remove Keyring '{name}'?"):
211
211
  return
212
212
 
213
213
  try:
214
214
  CLIENT.keyring_client.delete_keyring_by_name(name)
215
- print_log(f"Deleted Keyring '{name}'")
215
+ print_log(f"Removed Keyring '{name}'")
216
216
  except HTTPError as e:
217
217
  if e.response.status_code == 404:
218
- print_error(f"Keyring '{name}' not found")
218
+ print_error(f"Cannot find Keyring '{name}'")
219
219
  else:
220
- print_error(f"Unable to delete Keyring '{name}': {e}")
220
+ print_error(f"Unable to remove Keyring '{name}': {e}")
221
221
 
222
222
 
223
223
  def remove_credential(resource: Dict):
@@ -246,7 +246,7 @@ def remove_credential(resource: Dict):
246
246
  except HTTPError as e:
247
247
  if e.response.status_code == 404:
248
248
  print_error(
249
- f"Keyring '{keyring_name}' not found (possibly already deleted,"
249
+ f"Cannot find Keyring '{keyring_name}'(possibly already deleted,"
250
250
  " including its credentials?)"
251
251
  )
252
252
  else:
@@ -275,7 +275,7 @@ def remove_image_family(resource: Dict):
275
275
  )
276
276
  except HTTPError as e:
277
277
  if e.response.status_code == 404:
278
- print_error(f"Machine Image Family '{fq_name}' not found")
278
+ print_error(f"Cannot find Machine Image Family '{fq_name}'")
279
279
  return
280
280
  else:
281
281
  raise e
@@ -285,9 +285,9 @@ def remove_image_family(resource: Dict):
285
285
 
286
286
  try:
287
287
  CLIENT.images_client.delete_image_family(image_family)
288
- print_log(f"Deleted Image Family '{fq_name}' ('{image_family.id}')")
288
+ print_log(f"Removed Image Family '{fq_name}' ({image_family.id})")
289
289
  except Exception as e:
290
- print_error(f"Unable to delete Image Family '{fq_name}': {e}")
290
+ print_error(f"Unable to remove Image Family '{fq_name}': {e}")
291
291
 
292
292
 
293
293
  def remove_namespace_configuration(resource: Dict):
@@ -304,7 +304,7 @@ def remove_namespace_configuration(resource: Dict):
304
304
  CLIENT.object_store_client.get_namespace_storage_configurations()
305
305
  )
306
306
  if namespace not in [x.namespace for x in namespaces]:
307
- print_error(f"Namespace Storage Configuration '{namespace}' not found")
307
+ print_error(f"Cannot find Namespace Storage Configuration '{namespace}'")
308
308
  return
309
309
 
310
310
  if not confirmed(f"Remove Namespace Storage Configuration '{namespace}'?"):
@@ -428,7 +428,7 @@ def remove_resource_by_id(resource_id: str):
428
428
  CLIENT.keyring_client.delete_keyring_by_name(keyring.name)
429
429
  print_log(f"Removed Keyring {resource_id}")
430
430
  return
431
- raise Exception(f"Keyring {resource_id} not found")
431
+ raise Exception(f"Cannot find Keyring {resource_id}")
432
432
 
433
433
  elif get_ydid_type(resource_id) == YDIDType.WORKER_POOL:
434
434
  if confirmed(f"Shut down Worker Pool {resource_id}?"):
@@ -497,7 +497,7 @@ def remove_namespace_policy(resource: Dict):
497
497
  return
498
498
  except Exception:
499
499
  # Assume it's not found ... 404 from API
500
- print_error(f"Namespace Policy '{namespace}' not found")
500
+ print_error(f"Cannot find Namespace Policy '{namespace}'")
501
501
  return
502
502
 
503
503
  if not confirmed(f"Remove Namespace Policy '{namespace}'?"):
@@ -522,7 +522,7 @@ def remove_group(resource: Dict):
522
522
 
523
523
  group_id = get_group_id_by_name(CLIENT, group_name)
524
524
  if group_id is None:
525
- print_warning(f"Group '{group_name}' not found")
525
+ print_warning(f"Cannot find Group '{group_name}'")
526
526
  return
527
527
 
528
528
  if not confirmed(f"Remove Group '{group_name}' ({group_id})?"):
@@ -533,7 +533,7 @@ def remove_group(resource: Dict):
533
533
  print_log(f"Removed Group '{group_name}' ({group_id})")
534
534
  clear_group_caches()
535
535
  except Exception as e:
536
- print_error(f"Unable to delete Group '{group_name}' ({group_id}): {e}")
536
+ print_error(f"Unable to remove Group '{group_name}' ({group_id}): {e}")
537
537
 
538
538
 
539
539
  def remove_application(resource: Dict):
@@ -548,7 +548,7 @@ def remove_application(resource: Dict):
548
548
 
549
549
  app_id = get_application_id_by_name(CLIENT, app_name)
550
550
  if app_id is None:
551
- print_warning(f"Application '{app_name}' not found")
551
+ print_warning(f"Cannot find Application '{app_name}'")
552
552
  return
553
553
 
554
554
  if not confirmed(f"Remove Application '{app_name}' ({app_id})?"):
@@ -574,7 +574,7 @@ def remove_namespace(resource: Dict):
574
574
 
575
575
  namespace_id = get_namespace_id_by_name(CLIENT, name)
576
576
  if namespace_id is None:
577
- print_warning(f"Namespace '{name}' not found")
577
+ print_warning(f"Cannot find Namespace '{name}'")
578
578
  return
579
579
 
580
580
  if not confirmed(f"Remove Namespace '{name}'?"):
@@ -24,6 +24,7 @@ from yellowdog_client.model import (
24
24
  ExternalUser,
25
25
  GroupSearch,
26
26
  GroupSummary,
27
+ ImageAccess,
27
28
  InternalUser,
28
29
  MachineImageFamily,
29
30
  MachineImageFamilySearch,
@@ -53,7 +54,7 @@ from yellowdog_client.model import (
53
54
 
54
55
  from yellowdog_cli.utils.args import ARGS_PARSER
55
56
  from yellowdog_cli.utils.interactive import confirmed, select
56
- from yellowdog_cli.utils.printing import print_log, print_warning
57
+ from yellowdog_cli.utils.printing import print_log
57
58
  from yellowdog_cli.utils.settings import NAMESPACE_PREFIX_SEPARATOR
58
59
  from yellowdog_cli.utils.ydid_utils import YDIDType, get_ydid_type
59
60
 
@@ -94,7 +95,6 @@ def get_filtered_work_requirements(
94
95
  Get a list of Work Requirements filtered by namespace, tag
95
96
  and status. Supply either include_filter OR exclude_filter.
96
97
  """
97
-
98
98
  if include_filter is None:
99
99
  wr_search = WorkRequirementSearch(namespaces=[namespace], tag=tag)
100
100
  else:
@@ -349,146 +349,160 @@ def get_worker_pools(
349
349
 
350
350
 
351
351
  @lru_cache
352
- def find_image_family_or_group_id_by_name(
353
- client: PlatformClient, image_family_name
352
+ def find_image_name_or_id(
353
+ client: PlatformClient,
354
+ image_name_or_id: Optional[str],
355
+ always_return_id: bool = True,
356
+ report_substitutions: bool = True,
354
357
  ) -> Optional[str]:
355
358
  """
356
- Resolve image family references. Complicated logic.
357
- Fully qualified name is used for non-ambiguous PRIVATE image families.
358
- Return None if mapping can't be done, to be handled by the caller.
359
- """
359
+ Attempts to resolve to a well-formed YD image name or ID, if it can.
360
360
 
361
- if image_family_name.startswith("ami-") or image_family_name.startswith(
362
- "ocid1.image."
363
- ):
364
- # We can identify AWS AMIs and OCI OCIDs, and return immediately
365
- return image_family_name
361
+ Argument 'image_name_or_id' can take one of the following forms:
362
+ - Any image family or group YDID (returned unchanged)
363
+ - Strings prefixed or not prefixed with 'yd/' (will be added if required)
364
+ - Strings post-fixed or not post-fixed with '/latest' (will be removed)
365
+ - Any standalone image-family-name
366
+ - Any namespace/image-family-name combination
367
+ - Any image-family-name/image-group-name combination
368
+ - Any namespace/image-family-name/image-group-name combination
366
369
 
367
- original_image_family_name = image_family_name
370
+ The call will attempt to resolve the image into its fully qualified
371
+ name if 'always_return_id' is false:
372
+ - yd/namespace/image-family-name or
373
+ - yd/namespace/image-family-name/image-group-name
368
374
 
369
- # Remove leading 'yd/' prefix if necessary
370
- image_family_name = (
371
- image_family_name[3:]
372
- if image_family_name.startswith("yd/")
373
- else image_family_name
374
- )
375
+ If the resolved image is PUBLIC or 'always_return_id' is True,
376
+ the relevant YDID will always be returned; this is enforced for
377
+ PUBLIC images.
375
378
 
376
- # Handle image group names of form namespace/image-family-name/image-group-name
377
- split_name = image_family_name.split("/")
378
- if len(split_name) == 3:
379
- try:
380
- namespace, name = split_namespace_and_name(
381
- f"{split_name[0]}/{split_name[1]}"
382
- )
383
- return client.images_client.get_image_group_by_name(
384
- namespace=namespace, family_name=name, group_name=split_name[2]
385
- ).id
386
- except:
387
- return
379
+ Finally, if nothing matches, the original ID is returned. This is
380
+ likely to be a provider specific string.
381
+ """
382
+ if image_name_or_id is None:
383
+ return None
388
384
 
389
- namespace, name = split_namespace_and_name(image_family_name)
390
- if_search = MachineImageFamilySearch(
391
- familyName=name,
392
- namespaces=None if namespace in [None, ""] else [namespace],
393
- includePublic=True,
394
- )
395
- search_client: SearchClient = client.images_client.get_image_families(if_search)
385
+ # Already a matching YDID?
386
+ if get_ydid_type(image_name_or_id) in [
387
+ YDIDType.IMAGE_FAMILY,
388
+ YDIDType.IMAGE_GROUP,
389
+ YDIDType.IMAGE,
390
+ ]:
391
+ return image_name_or_id
396
392
 
397
- try:
398
- image_families: List[MachineImageFamilySummary] = search_client.list_all()
399
- except Exception as e:
400
- if "MissingPermissionException" in str(e) and "IMAGE_READ" in str(e):
401
- # IMAGE_READ permission (globally, or for this namespace) is absent;
402
- # abandon lookup and return the original image family name
403
- print_log(
404
- f"Note: failed to resolve image name '{original_image_family_name}' "
405
- "due to lack of 'IMAGE_READ' permission; using original name"
406
- )
407
- return original_image_family_name
408
- else:
409
- raise
393
+ original_image_name_or_id = image_name_or_id
410
394
 
411
- # Partial names will match, so filter for exact matches only
412
- image_families = [
413
- img_family for img_family in image_families if img_family.name == name
414
- ]
395
+ # Remove a leading 'yd/' prefix; will be reinstated later if required
396
+ image_name_or_id = (
397
+ image_name_or_id[3:] if image_name_or_id.startswith("yd/") else image_name_or_id
398
+ )
415
399
 
416
- # No matches
417
- if len(image_families) == 0:
418
- return
400
+ # Remove "/latest"; this is redundant/implied for YD image groups
401
+ image_name_or_id = (
402
+ image_name_or_id[:-7]
403
+ if image_name_or_id.endswith("/latest")
404
+ else image_name_or_id
405
+ )
419
406
 
420
- # It's possible to have both a PRIVATE and a PUBLIC match for the same
421
- # namespace/image_family_name. This is a corner case, but ...
422
- if len(image_families) == 2:
423
- image_families_public = [
424
- img_family
425
- for img_family in image_families
426
- if img_family.access.name == "PUBLIC"
407
+ def _replaced(return_val: str, is_ydid: bool = False):
408
+ """
409
+ Helper function to report the replacement.
410
+ """
411
+ if report_substitutions and return_val != original_image_name_or_id:
412
+ msg = f"{return_val}" if is_ydid else f"'{return_val}'"
413
+ print_log(f"Replaced Images ID '{original_image_name_or_id}' with {msg}")
414
+ return return_val
415
+
416
+ split_name = image_name_or_id.split("/")
417
+ image_family_summaries = get_image_family_summaries(client)
418
+
419
+ # Search for image name (only) matches
420
+ if len(split_name) == 1:
421
+ matching_image_families = [
422
+ ifs for ifs in image_family_summaries if ifs.name == split_name[0]
427
423
  ]
428
- image_families_private = [
429
- img_family
430
- for img_family in image_families
431
- if img_family.access.name == "PRIVATE"
432
- ]
433
- if len(image_families_public) == 1 and len(image_families_private) == 1:
434
- # Favour the PRIVATE image
435
- print_warning(
436
- f"Image Family '{name}' has both PUBLIC and PRIVATE "
437
- "variants; using the PRIVATE image family: "
438
- f"{image_families_private[0].namespace}/{image_families_private[0].name} "
439
- f"({image_families_private[0].id})"
424
+ if len(matching_image_families) > 1:
425
+ namespaces = [ifs.namespace for ifs in matching_image_families]
426
+ raise Exception(
427
+ f"Ambiguous Images ID '{original_image_name_or_id}': please "
428
+ f"specify a namespace from: {', '.join(namespaces)}"
440
429
  )
441
- image_families = image_families_private
442
-
443
- # Single match
444
- if len(image_families) == 1:
445
- substituted_image_family_name = (
446
- f"yd/{image_families[0].namespace}/{image_families[0].name}"
447
- )
448
-
449
- # If this is a PRIVATE image family, we can retain the fully-qualified
450
- # image family name instead of substituting the ID
451
- if image_families[0].access.name == "PRIVATE":
452
- if original_image_family_name != substituted_image_family_name:
453
- print_log(
454
- f"Substituting Image Family name '{original_image_family_name}' "
455
- f"with fully qualified name '{substituted_image_family_name}' "
456
- f"({image_families[0].id})"
430
+ elif len(matching_image_families) == 1:
431
+ if (
432
+ matching_image_families[0].access == ImageAccess.PUBLIC
433
+ or always_return_id
434
+ ):
435
+ return _replaced(matching_image_families[0].id, True)
436
+ else:
437
+ return _replaced(
438
+ f"yd/{matching_image_families[0].namespace}/"
439
+ f"{matching_image_families[0].name}"
457
440
  )
458
- return substituted_image_family_name
459
441
 
460
- # If PUBLIC, we need to replace with the YDID
461
- else:
462
- mid_msg = (
463
- ""
464
- if original_image_family_name == substituted_image_family_name
465
- else f"('{substituted_image_family_name}') "
466
- )
467
- print_log(
468
- f"Substituting Image Family name '{original_image_family_name}' {mid_msg}"
469
- f"with ID {image_families[0].id}"
442
+ # Search for namespace/family_name matches, *or* family_name/group_name matches
443
+ if len(split_name) == 2:
444
+ # namespace/family-name match
445
+ matching_image_families = [
446
+ ifs
447
+ for ifs in image_family_summaries
448
+ if ifs.namespace == split_name[0] and ifs.name == split_name[1]
449
+ ]
450
+ if len(matching_image_families) == 1:
451
+ if (
452
+ matching_image_families[0].access == ImageAccess.PUBLIC
453
+ or always_return_id
454
+ ):
455
+ return _replaced(matching_image_families[0].id, True)
456
+ return _replaced(
457
+ f"yd/{matching_image_families[0].namespace}/"
458
+ f"{matching_image_families[0].name}"
470
459
  )
471
- return image_families[0].id
472
-
473
- # Multiple matches
474
- matches = [
475
- f"{img_fam.namespace}/{img_fam.name} [{img_fam.access.name}] ({img_fam.id})"
476
- for img_fam in image_families
477
- ]
478
-
479
- raise Exception(
480
- f"Ambiguous Image Family name '{name}': "
481
- f"{matches}. "
482
- "Please specify a namespace. Note: PRIVATE image family is selected "
483
- "over PUBLIC if namespace/name are identical."
484
- )
485
460
 
461
+ # family-name/group-name match
462
+ matching_image_families = [
463
+ ifs for ifs in image_family_summaries if ifs.name == split_name[0]
464
+ ]
465
+ if_group_matches: List[Tuple[MachineImageFamilySummary, MachineImageGroup]] = []
466
+ for ifs in matching_image_families:
467
+ for if_group in get_image_family_groups(client, ifs.id):
468
+ if if_group.name == split_name[1]:
469
+ if_group_matches.append((ifs, if_group))
470
+ break
471
+ if len(if_group_matches) == 1:
472
+ if if_group_matches[0][0].access == ImageAccess.PUBLIC or always_return_id:
473
+ return _replaced(if_group_matches[0][1].id, True)
474
+ else:
475
+ return _replaced(
476
+ f"yd/{if_group_matches[0][0].namespace}/"
477
+ f"{if_group_matches[0][0].name}/"
478
+ f"{if_group_matches[0][1].name}"
479
+ )
480
+ if len(if_group_matches) > 1:
481
+ namespaces = [match[0].namespace for match in if_group_matches]
482
+ raise Exception(
483
+ f"Ambiguous image-family/image-group '{original_image_name_or_id}': "
484
+ f"please specify a namespace from: {', '.join(namespaces)}"
485
+ )
486
486
 
487
- def clear_image_family_search_cache():
488
- """
489
- Clear the cache of Image Family name searches.
490
- """
491
- find_image_family_or_group_id_by_name.cache_clear()
487
+ # Search for names of form 'namespace/image-family-name/image-group-name'
488
+ # (the platform prevents duplicates)
489
+ if len(split_name) == 3:
490
+ for ifs in image_family_summaries:
491
+ if ifs.namespace == split_name[0] and ifs.name == split_name[1]:
492
+ for ig in get_image_family_groups(client, ifs.id):
493
+ if ig.name == split_name[2]:
494
+ if ifs.access == ImageAccess.PUBLIC or always_return_id:
495
+ return _replaced(ig.id, True)
496
+ else:
497
+ return _replaced(f"yd/{image_name_or_id}")
498
+ else:
499
+ raise Exception(
500
+ "Image family found, but no matching image "
501
+ f"group for '{original_image_name_or_id}'"
502
+ )
503
+
504
+ # Finally, fall through and return the original ID string
505
+ return original_image_name_or_id
492
506
 
493
507
 
494
508
  def remove_allowances_matching_description(
@@ -631,7 +645,6 @@ def substitute_image_family_id_for_name_in_cst(
631
645
  Substitute Image Family IDs for namespace/name,
632
646
  if option is selected.
633
647
  """
634
-
635
648
  if not ARGS_PARSER.substitute_ids:
636
649
  return cst
637
650
 
@@ -664,7 +677,6 @@ def substitute_id_for_name_in_allowance(
664
677
  """
665
678
  Substitute IDs in Allowance objects.
666
679
  """
667
-
668
680
  if not ARGS_PARSER.substitute_ids:
669
681
  return allowance
670
682
 
@@ -679,7 +691,6 @@ def substitute_id_for_name_in_allowance(
679
691
  )
680
692
 
681
693
  # No processing for other allowance types
682
-
683
694
  return allowance
684
695
 
685
696
 
@@ -963,3 +974,38 @@ def get_compute_requirement_summaries(
963
974
  client.compute_client.get_compute_requirement_summaries(crs_search)
964
975
  )
965
976
  return search_client.list_all()
977
+
978
+
979
+ @lru_cache
980
+ def get_image_family_summaries(
981
+ client: PlatformClient,
982
+ ) -> List[MachineImageFamilySummary]:
983
+ """
984
+ Obtain and cache the list of image families.
985
+ """
986
+ if_search = MachineImageFamilySearch(
987
+ familyName=None,
988
+ namespaces=None,
989
+ includePublic=True,
990
+ )
991
+ search_client: SearchClient = client.images_client.get_image_families(if_search)
992
+ return search_client.list_all()
993
+
994
+
995
+ @lru_cache
996
+ def get_image_family_groups(
997
+ client: PlatformClient, image_family_id: str
998
+ ) -> List[MachineImageGroup]:
999
+ """
1000
+ Obtain and cache the list of image groups for an image family.
1001
+ """
1002
+ return client.images_client.get_image_family_by_id(image_family_id).imageGroups
1003
+
1004
+
1005
+ def clear_image_caches():
1006
+ """
1007
+ Clear the image caches.
1008
+ """
1009
+ find_image_name_or_id.cache_clear()
1010
+ get_image_family_summaries.cache_clear()
1011
+ get_image_family_groups.cache_clear()
@@ -10,7 +10,7 @@ from yellowdog_client import PlatformClient
10
10
  from yellowdog_cli.utils.config_types import ConfigWorkerPool
11
11
  from yellowdog_cli.utils.entity_utils import (
12
12
  find_compute_requirement_template_id_by_name,
13
- find_image_family_or_group_id_by_name,
13
+ find_image_name_or_id,
14
14
  split_namespace_and_name,
15
15
  )
16
16
  from yellowdog_cli.utils.load_config import CONFIG_FILE_DIR
@@ -85,16 +85,13 @@ def get_template_id(client: PlatformClient, template_id_or_name: str) -> str:
85
85
  if get_ydid_type(template_id_or_name) == YDIDType.COMPUTE_REQUIREMENT_TEMPLATE:
86
86
  return template_id_or_name
87
87
 
88
- # If this is a fully qualified CRT name, allow server-side lookup
89
- namespace, name = split_namespace_and_name(template_id_or_name)
90
- if namespace is not None:
91
- return template_id_or_name
92
-
93
88
  template_id = find_compute_requirement_template_id_by_name(
94
89
  client=client, name=template_id_or_name
95
90
  )
96
91
  if template_id is None:
97
- return template_id_or_name # Return the original input
92
+ raise Exception(
93
+ f"Compute Requirement Template '{template_id_or_name}' not found"
94
+ )
98
95
 
99
96
  print_log(
100
97
  f"Substituting Compute Requirement Template name '{template_id_or_name}'"
@@ -103,28 +100,10 @@ def get_template_id(client: PlatformClient, template_id_or_name: str) -> str:
103
100
  return template_id
104
101
 
105
102
 
106
- def get_image_family_id(client: PlatformClient, image_family_id_or_name: str) -> str:
103
+ def get_image_id(client: PlatformClient, image_name_or_id: str) -> str:
107
104
  """
108
- Check if 'image_id_or_name' looks like a valid IF ID; if not,
109
- assume it's an IF name and perform a lookup.
105
+ This function was simplified, hence the pass-through call for now.
110
106
  """
111
- if get_ydid_type(image_family_id_or_name) in [
112
- YDIDType.IMAGE_FAMILY,
113
- YDIDType.IMAGE_GROUP,
114
- YDIDType.IMAGE,
115
- ]:
116
- return image_family_id_or_name
117
-
118
- image_family_id = find_image_family_or_group_id_by_name(
119
- client=client, image_family_name=image_family_id_or_name
107
+ return find_image_name_or_id(
108
+ client=client, image_name_or_id=image_name_or_id, always_return_id=True
120
109
  )
121
-
122
- # If a specific image (e.g., an AMI) has been supplied, we'll get here
123
- # and will return the original value. This does incur the cost of
124
- # listing image families, and there's a small chance of a collision
125
- # between a supplied specific image and an Image Family name.
126
-
127
- if image_family_id is None:
128
- return image_family_id_or_name # Return the original input
129
-
130
- return image_family_id
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yellowdog-python-examples
3
- Version: 8.1.3
3
+ Version: 8.1.4
4
4
  Summary: Python CLI commands using the YellowDog Python SDK
5
5
  Author-email: YellowDog Limited <support@yellowdog.co>
6
6
  License-Expression: Apache-2.0
@@ -26,7 +26,7 @@ Requires-Dist: requests
26
26
  Requires-Dist: rich==13.9.4
27
27
  Requires-Dist: tabulate>=0.9.0
28
28
  Requires-Dist: toml
29
- Requires-Dist: yellowdog-sdk>=11.5.0
29
+ Requires-Dist: yellowdog-sdk>=11.6.0
30
30
  Provides-Extra: jsonnet
31
31
  Requires-Dist: jsonnet; extra == "jsonnet"
32
32
  Provides-Extra: cloudwizard
@@ -5,7 +5,7 @@ requests
5
5
  rich==13.9.4
6
6
  tabulate>=0.9.0
7
7
  toml
8
- yellowdog-sdk>=11.5.0
8
+ yellowdog-sdk>=11.6.0
9
9
 
10
10
  [cloudwizard]
11
11
  boto3
@@ -1 +0,0 @@
1
- __version__ = "8.1.3"