yellowdog-python-examples 8.1.3__tar.gz → 8.1.5__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.
- {yellowdog_python_examples-8.1.3/yellowdog_python_examples.egg-info → yellowdog_python_examples-8.1.5}/PKG-INFO +2 -2
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/README.md +4 -4
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/requirements.txt +1 -1
- yellowdog_python_examples-8.1.5/yellowdog_cli/__init__.py +1 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/create.py +27 -37
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/instantiate.py +5 -5
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/provision.py +4 -4
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/remove.py +15 -15
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/entity_utils.py +206 -125
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/printing.py +9 -7
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/provision_utils.py +8 -29
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5/yellowdog_python_examples.egg-info}/PKG-INFO +2 -2
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_python_examples.egg-info/requires.txt +1 -1
- yellowdog_python_examples-8.1.3/yellowdog_cli/__init__.py +0 -1
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/LICENSE +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/PYPI_README.md +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/pyproject.toml +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/setup.cfg +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_create_remove.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_demos.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_dryruns.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_entrypoints.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_gui.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_list.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_objects.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_variable_processing.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/abort.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/admin.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/boost.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/cancel.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/cloudwizard.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/compare.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/delete.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/download.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/finish.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/follow.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/format_json.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/hold.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/jsonnet2json.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/list.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/resize.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/show.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/shutdown.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/start.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/submit.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/terminate.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/upload.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/__init__.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/args.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/check_imports.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/cloudwizard_aws.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/cloudwizard_aws_types.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/cloudwizard_azure.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/cloudwizard_common.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/cloudwizard_gcp.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/compact_json.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/config_types.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/csv_data.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/follow_utils.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/interactive.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/items.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/load_config.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/load_resources.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/misc_utils.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/property_names.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/rich_console_input_fixed.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/settings.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/start_hold_common.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/submit_utils.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/type_check.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/upload_utils.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/validate_properties.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/variables.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/wrapper.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/ydid_utils.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/version.py +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_python_examples.egg-info/SOURCES.txt +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_python_examples.egg-info/dependency_links.txt +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_python_examples.egg-info/entry_points.txt +0 -0
- {yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/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
|
+
Version: 8.1.5
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "8.1.5"
|
|
@@ -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
|
-
|
|
53
|
+
clear_image_caches,
|
|
54
54
|
find_compute_requirement_template_id_by_name,
|
|
55
55
|
find_compute_source_template_id_by_name,
|
|
56
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
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)
|
|
327
|
+
def _get_images_id(image_str: str, context: Dict, key: str):
|
|
329
328
|
"""
|
|
330
|
-
Helper function to
|
|
329
|
+
Helper function to resolve an image ID.
|
|
331
330
|
"""
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
|
|
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}' (
|
|
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:
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/instantiate.py
RENAMED
|
@@ -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
|
-
|
|
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 =
|
|
96
|
-
client=CLIENT,
|
|
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"] =
|
|
307
|
-
client=CLIENT,
|
|
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:
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/provision.py
RENAMED
|
@@ -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
|
-
|
|
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] =
|
|
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 =
|
|
293
|
-
client=CLIENT,
|
|
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"
|
|
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"
|
|
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}'
|
|
218
|
+
print_error(f"Cannot find Keyring '{name}'")
|
|
219
219
|
else:
|
|
220
|
-
print_error(f"Unable to
|
|
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}'
|
|
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}'
|
|
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"
|
|
288
|
+
print_log(f"Removed Image Family '{fq_name}' ({image_family.id})")
|
|
289
289
|
except Exception as e:
|
|
290
|
-
print_error(f"Unable to
|
|
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}'
|
|
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}
|
|
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}'
|
|
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}'
|
|
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
|
|
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}'
|
|
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}'
|
|
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
|
|
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:
|
|
@@ -231,6 +231,7 @@ def _find_id_by_name(
|
|
|
231
231
|
)
|
|
232
232
|
|
|
233
233
|
|
|
234
|
+
@lru_cache
|
|
234
235
|
def find_compute_source_template_id_by_name(
|
|
235
236
|
client: PlatformClient, name: str
|
|
236
237
|
) -> Optional[str]:
|
|
@@ -238,7 +239,12 @@ def find_compute_source_template_id_by_name(
|
|
|
238
239
|
Find a Compute Source Template id by name.
|
|
239
240
|
Compute Source Template names are unique within a namespace.
|
|
240
241
|
"""
|
|
241
|
-
|
|
242
|
+
template_id = _find_id_by_name(name, client, get_compute_source_templates)
|
|
243
|
+
if template_id is not None:
|
|
244
|
+
print_log(
|
|
245
|
+
f"Replaced Compute Source Template name '{name}' with ID {template_id}"
|
|
246
|
+
)
|
|
247
|
+
return template_id
|
|
242
248
|
|
|
243
249
|
|
|
244
250
|
@lru_cache
|
|
@@ -277,8 +283,10 @@ def get_work_requirement_summaries(
|
|
|
277
283
|
def clear_compute_source_template_cache():
|
|
278
284
|
"""
|
|
279
285
|
Clear the cache of Compute Source Templates.
|
|
286
|
+
Clear name -> CST lookups.
|
|
280
287
|
"""
|
|
281
288
|
get_compute_source_templates.cache_clear()
|
|
289
|
+
find_compute_source_template_id_by_name.cache_clear()
|
|
282
290
|
|
|
283
291
|
|
|
284
292
|
def find_compute_requirement_template_id_by_name(
|
|
@@ -349,146 +357,172 @@ def get_worker_pools(
|
|
|
349
357
|
|
|
350
358
|
|
|
351
359
|
@lru_cache
|
|
352
|
-
def
|
|
353
|
-
client: PlatformClient,
|
|
360
|
+
def find_image_name_or_id(
|
|
361
|
+
client: PlatformClient,
|
|
362
|
+
image_name_or_id: Optional[str],
|
|
363
|
+
always_return_id: bool = True,
|
|
364
|
+
report_substitutions: bool = True,
|
|
354
365
|
) -> Optional[str]:
|
|
355
366
|
"""
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
367
|
+
Attempts to resolve to a well-formed YD image name or ID, if it can.
|
|
368
|
+
|
|
369
|
+
Argument 'image_name_or_id' can take one of the following forms:
|
|
370
|
+
- Any image family or group YDID (returned unchanged)
|
|
371
|
+
- Strings prefixed or not prefixed with 'yd/' (will be added if required)
|
|
372
|
+
- Strings post-fixed or not post-fixed with '/latest' (will be removed)
|
|
373
|
+
- Any standalone image-family-name
|
|
374
|
+
- Any namespace/image-family-name combination
|
|
375
|
+
- Any image-family-name/image-group-name combination
|
|
376
|
+
- Any namespace/image-family-name/image-group-name combination
|
|
377
|
+
|
|
378
|
+
The call will attempt to resolve the image into its fully qualified
|
|
379
|
+
name if 'always_return_id' is false:
|
|
380
|
+
- yd/namespace/image-family-name or
|
|
381
|
+
- yd/namespace/image-family-name/image-group-name
|
|
382
|
+
|
|
383
|
+
If the resolved image is PUBLIC or 'always_return_id' is True,
|
|
384
|
+
the relevant YDID will always be returned; this is enforced for
|
|
385
|
+
PUBLIC images.
|
|
386
|
+
|
|
387
|
+
Finally, if nothing matches, the original ID is returned. This is
|
|
388
|
+
likely to be a provider specific string.
|
|
359
389
|
"""
|
|
390
|
+
if image_name_or_id is None:
|
|
391
|
+
return None
|
|
360
392
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
393
|
+
# Already a matching YDID?
|
|
394
|
+
if get_ydid_type(image_name_or_id) in [
|
|
395
|
+
YDIDType.IMAGE_FAMILY,
|
|
396
|
+
YDIDType.IMAGE_GROUP,
|
|
397
|
+
YDIDType.IMAGE,
|
|
398
|
+
]:
|
|
399
|
+
return image_name_or_id
|
|
366
400
|
|
|
367
|
-
|
|
401
|
+
original_image_name_or_id = image_name_or_id
|
|
368
402
|
|
|
369
|
-
# Remove leading 'yd/' prefix if
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if image_family_name.startswith("yd/")
|
|
373
|
-
else image_family_name
|
|
403
|
+
# Remove a leading 'yd/' prefix; will be reinstated later if required
|
|
404
|
+
image_name_or_id = (
|
|
405
|
+
image_name_or_id[3:] if image_name_or_id.startswith("yd/") else image_name_or_id
|
|
374
406
|
)
|
|
375
407
|
|
|
376
|
-
#
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
|
388
|
-
|
|
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,
|
|
408
|
+
# Remove "/latest"; this is redundant/implied for YD image groups
|
|
409
|
+
image_name_or_id = (
|
|
410
|
+
image_name_or_id[:-7]
|
|
411
|
+
if image_name_or_id.endswith("/latest")
|
|
412
|
+
else image_name_or_id
|
|
394
413
|
)
|
|
395
|
-
search_client: SearchClient = client.images_client.get_image_families(if_search)
|
|
396
414
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
print_log(
|
|
404
|
-
|
|
405
|
-
|
|
415
|
+
def _replaced(return_val: str, is_ydid: bool = False):
|
|
416
|
+
"""
|
|
417
|
+
Helper function to report the replacement.
|
|
418
|
+
"""
|
|
419
|
+
if report_substitutions and return_val != original_image_name_or_id:
|
|
420
|
+
msg = f"{return_val}" if is_ydid else f"'{return_val}'"
|
|
421
|
+
print_log(f"Replaced Images ID '{original_image_name_or_id}' with {msg}")
|
|
422
|
+
return return_val
|
|
423
|
+
|
|
424
|
+
split_name = image_name_or_id.split("/")
|
|
425
|
+
image_family_summaries = get_image_family_summaries(client) # All namespaces
|
|
426
|
+
|
|
427
|
+
# Search for image name (only) matches
|
|
428
|
+
if len(split_name) == 1:
|
|
429
|
+
matching_image_families = [
|
|
430
|
+
ifs for ifs in image_family_summaries if ifs.name == split_name[0]
|
|
431
|
+
]
|
|
432
|
+
if len(matching_image_families) > 1:
|
|
433
|
+
namespaces = [ifs.namespace for ifs in matching_image_families]
|
|
434
|
+
raise Exception(
|
|
435
|
+
f"Ambiguous Images ID '{original_image_name_or_id}': please "
|
|
436
|
+
f"specify a namespace from: {', '.join(namespaces)}"
|
|
406
437
|
)
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
438
|
+
elif len(matching_image_families) == 1:
|
|
439
|
+
if (
|
|
440
|
+
matching_image_families[0].access == ImageAccess.PUBLIC
|
|
441
|
+
or always_return_id
|
|
442
|
+
):
|
|
443
|
+
return _replaced(matching_image_families[0].id, True)
|
|
444
|
+
else:
|
|
445
|
+
return _replaced(
|
|
446
|
+
f"yd/{matching_image_families[0].namespace}/"
|
|
447
|
+
f"{matching_image_families[0].name}"
|
|
448
|
+
)
|
|
410
449
|
|
|
411
|
-
#
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
]
|
|
450
|
+
# Search for namespace/family_name matches, *or* family_name/group_name matches
|
|
451
|
+
if len(split_name) == 2:
|
|
452
|
+
# namespace/family-name match
|
|
415
453
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
454
|
+
# This will be tidied up when the Application can
|
|
455
|
+
# query its properties
|
|
456
|
+
if len(image_family_summaries) == 0: # Global search didn't work
|
|
457
|
+
image_family_summaries = get_image_family_summaries(client, split_name[0])
|
|
419
458
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
img_family
|
|
425
|
-
for img_family in image_families
|
|
426
|
-
if img_family.access.name == "PUBLIC"
|
|
459
|
+
matching_image_families = [
|
|
460
|
+
ifs
|
|
461
|
+
for ifs in image_family_summaries
|
|
462
|
+
if ifs.namespace == split_name[0] and ifs.name == split_name[1]
|
|
427
463
|
]
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
f"
|
|
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})"
|
|
464
|
+
if len(matching_image_families) == 1:
|
|
465
|
+
if (
|
|
466
|
+
matching_image_families[0].access == ImageAccess.PUBLIC
|
|
467
|
+
or always_return_id
|
|
468
|
+
):
|
|
469
|
+
return _replaced(matching_image_families[0].id, True)
|
|
470
|
+
return _replaced(
|
|
471
|
+
f"yd/{matching_image_families[0].namespace}/"
|
|
472
|
+
f"{matching_image_families[0].name}"
|
|
440
473
|
)
|
|
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
474
|
|
|
449
|
-
#
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
475
|
+
# family-name/group-name match
|
|
476
|
+
matching_image_families = [
|
|
477
|
+
ifs for ifs in image_family_summaries if ifs.name == split_name[0]
|
|
478
|
+
]
|
|
479
|
+
if_group_matches: List[Tuple[MachineImageFamilySummary, MachineImageGroup]] = []
|
|
480
|
+
for ifs in matching_image_families:
|
|
481
|
+
for if_group in get_image_family_groups(client, ifs.id):
|
|
482
|
+
if if_group.name == split_name[1]:
|
|
483
|
+
if_group_matches.append((ifs, if_group))
|
|
484
|
+
break
|
|
485
|
+
if len(if_group_matches) == 1:
|
|
486
|
+
if if_group_matches[0][0].access == ImageAccess.PUBLIC or always_return_id:
|
|
487
|
+
return _replaced(if_group_matches[0][1].id, True)
|
|
488
|
+
else:
|
|
489
|
+
return _replaced(
|
|
490
|
+
f"yd/{if_group_matches[0][0].namespace}/"
|
|
491
|
+
f"{if_group_matches[0][0].name}/"
|
|
492
|
+
f"{if_group_matches[0][1].name}"
|
|
457
493
|
)
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
""
|
|
464
|
-
if original_image_family_name == substituted_image_family_name
|
|
465
|
-
else f"('{substituted_image_family_name}') "
|
|
494
|
+
if len(if_group_matches) > 1:
|
|
495
|
+
namespaces = [match[0].namespace for match in if_group_matches]
|
|
496
|
+
raise Exception(
|
|
497
|
+
f"Ambiguous image-family/image-group '{original_image_name_or_id}': "
|
|
498
|
+
f"please specify a namespace from: {', '.join(namespaces)}"
|
|
466
499
|
)
|
|
467
|
-
print_log(
|
|
468
|
-
f"Substituting Image Family name '{original_image_family_name}' {mid_msg}"
|
|
469
|
-
f"with ID {image_families[0].id}"
|
|
470
|
-
)
|
|
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
500
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
"Please specify a namespace. Note: PRIVATE image family is selected "
|
|
483
|
-
"over PUBLIC if namespace/name are identical."
|
|
484
|
-
)
|
|
501
|
+
# Search for names of form 'namespace/image-family-name/image-group-name'
|
|
502
|
+
# (the platform prevents duplicates)
|
|
503
|
+
if len(split_name) == 3:
|
|
485
504
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
505
|
+
# This will be tidied up when the Application can
|
|
506
|
+
# query its properties
|
|
507
|
+
if len(image_family_summaries) == 0: # Global search didn't work
|
|
508
|
+
image_family_summaries = get_image_family_summaries(client, split_name[0])
|
|
509
|
+
|
|
510
|
+
for ifs in image_family_summaries:
|
|
511
|
+
if ifs.namespace == split_name[0] and ifs.name == split_name[1]:
|
|
512
|
+
for ig in get_image_family_groups(client, ifs.id):
|
|
513
|
+
if ig.name == split_name[2]:
|
|
514
|
+
if ifs.access == ImageAccess.PUBLIC or always_return_id:
|
|
515
|
+
return _replaced(ig.id, True)
|
|
516
|
+
else:
|
|
517
|
+
return _replaced(f"yd/{image_name_or_id}")
|
|
518
|
+
else:
|
|
519
|
+
raise Exception(
|
|
520
|
+
"Image family found, but no matching image "
|
|
521
|
+
f"group for '{original_image_name_or_id}'"
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# Finally, fall through and return the unchanged, original ID string
|
|
525
|
+
return original_image_name_or_id
|
|
492
526
|
|
|
493
527
|
|
|
494
528
|
def remove_allowances_matching_description(
|
|
@@ -631,7 +665,6 @@ def substitute_image_family_id_for_name_in_cst(
|
|
|
631
665
|
Substitute Image Family IDs for namespace/name,
|
|
632
666
|
if option is selected.
|
|
633
667
|
"""
|
|
634
|
-
|
|
635
668
|
if not ARGS_PARSER.substitute_ids:
|
|
636
669
|
return cst
|
|
637
670
|
|
|
@@ -664,7 +697,6 @@ def substitute_id_for_name_in_allowance(
|
|
|
664
697
|
"""
|
|
665
698
|
Substitute IDs in Allowance objects.
|
|
666
699
|
"""
|
|
667
|
-
|
|
668
700
|
if not ARGS_PARSER.substitute_ids:
|
|
669
701
|
return allowance
|
|
670
702
|
|
|
@@ -679,7 +711,6 @@ def substitute_id_for_name_in_allowance(
|
|
|
679
711
|
)
|
|
680
712
|
|
|
681
713
|
# No processing for other allowance types
|
|
682
|
-
|
|
683
714
|
return allowance
|
|
684
715
|
|
|
685
716
|
|
|
@@ -963,3 +994,53 @@ def get_compute_requirement_summaries(
|
|
|
963
994
|
client.compute_client.get_compute_requirement_summaries(crs_search)
|
|
964
995
|
)
|
|
965
996
|
return search_client.list_all()
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
@lru_cache
|
|
1000
|
+
def get_image_family_summaries(
|
|
1001
|
+
client: PlatformClient,
|
|
1002
|
+
namespace: Optional[str] = None,
|
|
1003
|
+
) -> List[MachineImageFamilySummary]:
|
|
1004
|
+
"""
|
|
1005
|
+
Obtain and cache the list of image families.
|
|
1006
|
+
"""
|
|
1007
|
+
# Temporarily suppress most permission errors: will be improved
|
|
1008
|
+
# once the application can be queried for its admissible
|
|
1009
|
+
# IMAGE_READ namespaces
|
|
1010
|
+
try:
|
|
1011
|
+
if_search = MachineImageFamilySearch(
|
|
1012
|
+
familyName=None,
|
|
1013
|
+
namespaces=None if namespace is None else [namespace],
|
|
1014
|
+
includePublic=True,
|
|
1015
|
+
)
|
|
1016
|
+
search_client: SearchClient = client.images_client.get_image_families(if_search)
|
|
1017
|
+
return search_client.list_all()
|
|
1018
|
+
except Exception as e:
|
|
1019
|
+
if namespace is not None and "MissingPermissionException" in str(e):
|
|
1020
|
+
# Caching will prevent this warning appearing multiple times
|
|
1021
|
+
print_log(
|
|
1022
|
+
"Warning: Possible 'IMAGE_READ' permission missing if "
|
|
1023
|
+
f"'{namespace}' is meant as an Image namespace?"
|
|
1024
|
+
)
|
|
1025
|
+
pass
|
|
1026
|
+
|
|
1027
|
+
return []
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
@lru_cache
|
|
1031
|
+
def get_image_family_groups(
|
|
1032
|
+
client: PlatformClient, image_family_id: str
|
|
1033
|
+
) -> List[MachineImageGroup]:
|
|
1034
|
+
"""
|
|
1035
|
+
Obtain and cache the list of image groups for an image family.
|
|
1036
|
+
"""
|
|
1037
|
+
return client.images_client.get_image_family_by_id(image_family_id).imageGroups
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
def clear_image_caches():
|
|
1041
|
+
"""
|
|
1042
|
+
Clear the image caches.
|
|
1043
|
+
"""
|
|
1044
|
+
find_image_name_or_id.cache_clear()
|
|
1045
|
+
get_image_family_summaries.cache_clear()
|
|
1046
|
+
get_image_family_groups.cache_clear()
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/printing.py
RENAMED
|
@@ -16,6 +16,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
|
|
16
16
|
|
|
17
17
|
from rich.console import Console, Theme
|
|
18
18
|
from rich.highlighter import JSONHighlighter, RegexHighlighter
|
|
19
|
+
from rich.markup import escape
|
|
19
20
|
from tabulate import tabulate
|
|
20
21
|
from yellowdog_client import PlatformClient
|
|
21
22
|
from yellowdog_client.common.json import Json
|
|
@@ -192,7 +193,7 @@ def print_simple(
|
|
|
192
193
|
if ARGS_PARSER.quiet and override_quiet is False:
|
|
193
194
|
return
|
|
194
195
|
|
|
195
|
-
CONSOLE.print(log_message)
|
|
196
|
+
CONSOLE.print(escape(log_message))
|
|
196
197
|
|
|
197
198
|
|
|
198
199
|
def print_log(
|
|
@@ -211,7 +212,7 @@ def print_log(
|
|
|
211
212
|
print(print_string(log_message, no_fill=no_fill), flush=True)
|
|
212
213
|
return
|
|
213
214
|
|
|
214
|
-
CONSOLE.print(print_string(log_message, no_fill=no_fill))
|
|
215
|
+
CONSOLE.print(escape(print_string(log_message, no_fill=no_fill)))
|
|
215
216
|
|
|
216
217
|
|
|
217
218
|
def print_error(error_obj: Union[Exception, str]):
|
|
@@ -222,7 +223,7 @@ def print_error(error_obj: Union[Exception, str]):
|
|
|
222
223
|
print(print_string(f"Error: {error_obj}"), flush=True)
|
|
223
224
|
return
|
|
224
225
|
|
|
225
|
-
CONSOLE_ERR.print(print_string(f"Error: {error_obj}"), style=ERROR_STYLE)
|
|
226
|
+
CONSOLE_ERR.print(escape(print_string(f"Error: {error_obj}")), style=ERROR_STYLE)
|
|
226
227
|
|
|
227
228
|
|
|
228
229
|
def print_warning(
|
|
@@ -241,7 +242,8 @@ def print_warning(
|
|
|
241
242
|
return
|
|
242
243
|
|
|
243
244
|
CONSOLE.print(
|
|
244
|
-
print_string(f"Warning: {warning}", no_fill=no_fill),
|
|
245
|
+
escape(print_string(f"Warning: {warning}", no_fill=no_fill)),
|
|
246
|
+
style=WARNING_STYLE,
|
|
245
247
|
)
|
|
246
248
|
|
|
247
249
|
|
|
@@ -280,7 +282,7 @@ def print_table_core(table: str):
|
|
|
280
282
|
if ARGS_PARSER.no_format or table.count("\n") > MAX_LINES_COLOURED_FORMATTING:
|
|
281
283
|
print(table, flush=True)
|
|
282
284
|
else:
|
|
283
|
-
CONSOLE_TABLE.print(table)
|
|
285
|
+
CONSOLE_TABLE.print(escape(table))
|
|
284
286
|
|
|
285
287
|
|
|
286
288
|
def get_type_name(obj: Item) -> str:
|
|
@@ -1098,9 +1100,9 @@ def print_json(
|
|
|
1098
1100
|
|
|
1099
1101
|
else:
|
|
1100
1102
|
if with_final_comma:
|
|
1101
|
-
CONSOLE_JSON.print(json_string, end=",\n", soft_wrap=True)
|
|
1103
|
+
CONSOLE_JSON.print(escape(json_string), end=",\n", soft_wrap=True)
|
|
1102
1104
|
else:
|
|
1103
|
-
CONSOLE_JSON.print(json_string, soft_wrap=True)
|
|
1105
|
+
CONSOLE_JSON.print(escape(json_string), soft_wrap=True)
|
|
1104
1106
|
|
|
1105
1107
|
if ARGS_PARSER.output_file is not None: # Also output to a nominated file
|
|
1106
1108
|
print_to_file(
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
103
|
+
def get_image_id(client: PlatformClient, image_name_or_id: str) -> str:
|
|
107
104
|
"""
|
|
108
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
+
Version: 8.1.5
|
|
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.
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "8.1.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_create_remove.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/tests/test_entrypoints.py
RENAMED
|
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
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/cloudwizard.py
RENAMED
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/compare.py
RENAMED
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/download.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/format_json.py
RENAMED
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/jsonnet2json.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/shutdown.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/terminate.py
RENAMED
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/__init__.py
RENAMED
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/args.py
RENAMED
|
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
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/csv_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/items.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/settings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/variables.py
RENAMED
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/utils/wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
{yellowdog_python_examples-8.1.3 → yellowdog_python_examples-8.1.5}/yellowdog_cli/version.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|