idmtools-platform-comps 0.0.0.dev0__py3-none-any.whl → 0.0.3__py3-none-any.whl

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 (64) hide show
  1. idmtools_platform_comps/__init__.py +25 -8
  2. idmtools_platform_comps/cli/__init__.py +4 -0
  3. idmtools_platform_comps/cli/cli_functions.py +50 -0
  4. idmtools_platform_comps/cli/comps.py +492 -0
  5. idmtools_platform_comps/comps_cli.py +48 -0
  6. idmtools_platform_comps/comps_operations/__init__.py +6 -0
  7. idmtools_platform_comps/comps_operations/asset_collection_operations.py +263 -0
  8. idmtools_platform_comps/comps_operations/experiment_operations.py +569 -0
  9. idmtools_platform_comps/comps_operations/simulation_operations.py +678 -0
  10. idmtools_platform_comps/comps_operations/suite_operations.py +228 -0
  11. idmtools_platform_comps/comps_operations/workflow_item_operations.py +269 -0
  12. idmtools_platform_comps/comps_platform.py +309 -0
  13. idmtools_platform_comps/plugin_info.py +168 -0
  14. idmtools_platform_comps/ssmt_operations/__init__.py +6 -0
  15. idmtools_platform_comps/ssmt_operations/simulation_operations.py +77 -0
  16. idmtools_platform_comps/ssmt_operations/workflow_item_operations.py +73 -0
  17. idmtools_platform_comps/ssmt_platform.py +44 -0
  18. idmtools_platform_comps/ssmt_work_items/__init__.py +4 -0
  19. idmtools_platform_comps/ssmt_work_items/comps_work_order_task.py +29 -0
  20. idmtools_platform_comps/ssmt_work_items/comps_workitems.py +113 -0
  21. idmtools_platform_comps/ssmt_work_items/icomps_workflowitem.py +71 -0
  22. idmtools_platform_comps/ssmt_work_items/work_order.py +54 -0
  23. idmtools_platform_comps/utils/__init__.py +4 -0
  24. idmtools_platform_comps/utils/assetize_output/__init__.py +4 -0
  25. idmtools_platform_comps/utils/assetize_output/assetize_output.py +125 -0
  26. idmtools_platform_comps/utils/assetize_output/assetize_ssmt_script.py +144 -0
  27. idmtools_platform_comps/utils/base_singularity_work_order.json +6 -0
  28. idmtools_platform_comps/utils/download/__init__.py +4 -0
  29. idmtools_platform_comps/utils/download/download.py +178 -0
  30. idmtools_platform_comps/utils/download/download_ssmt.py +81 -0
  31. idmtools_platform_comps/utils/download_experiment.py +116 -0
  32. idmtools_platform_comps/utils/file_filter_workitem.py +519 -0
  33. idmtools_platform_comps/utils/general.py +358 -0
  34. idmtools_platform_comps/utils/linux_mounts.py +73 -0
  35. idmtools_platform_comps/utils/lookups.py +123 -0
  36. idmtools_platform_comps/utils/package_version.py +489 -0
  37. idmtools_platform_comps/utils/python_requirements_ac/__init__.py +4 -0
  38. idmtools_platform_comps/utils/python_requirements_ac/create_asset_collection.py +155 -0
  39. idmtools_platform_comps/utils/python_requirements_ac/install_requirements.py +109 -0
  40. idmtools_platform_comps/utils/python_requirements_ac/requirements_to_asset_collection.py +374 -0
  41. idmtools_platform_comps/utils/python_version.py +40 -0
  42. idmtools_platform_comps/utils/scheduling.py +154 -0
  43. idmtools_platform_comps/utils/singularity_build.py +491 -0
  44. idmtools_platform_comps/utils/spatial_output.py +76 -0
  45. idmtools_platform_comps/utils/ssmt_utils/__init__.py +6 -0
  46. idmtools_platform_comps/utils/ssmt_utils/common.py +70 -0
  47. idmtools_platform_comps/utils/ssmt_utils/file_filter.py +568 -0
  48. idmtools_platform_comps/utils/sweeping.py +162 -0
  49. idmtools_platform_comps-0.0.3.dist-info/METADATA +100 -0
  50. idmtools_platform_comps-0.0.3.dist-info/RECORD +62 -0
  51. idmtools_platform_comps-0.0.3.dist-info/entry_points.txt +9 -0
  52. idmtools_platform_comps-0.0.3.dist-info/licenses/LICENSE.TXT +3 -0
  53. {idmtools_platform_comps-0.0.0.dev0.dist-info → idmtools_platform_comps-0.0.3.dist-info}/top_level.txt +1 -0
  54. ssmt_image/Dockerfile +52 -0
  55. ssmt_image/Makefile +21 -0
  56. ssmt_image/__init__.py +6 -0
  57. ssmt_image/bootstrap.sh +30 -0
  58. ssmt_image/build_docker_image.py +161 -0
  59. ssmt_image/pip.conf +3 -0
  60. ssmt_image/push_docker_image.py +49 -0
  61. ssmt_image/requirements.txt +9 -0
  62. idmtools_platform_comps-0.0.0.dev0.dist-info/METADATA +0 -41
  63. idmtools_platform_comps-0.0.0.dev0.dist-info/RECORD +0 -5
  64. {idmtools_platform_comps-0.0.0.dev0.dist-info → idmtools_platform_comps-0.0.3.dist-info}/WHEEL +0 -0
@@ -1,8 +1,25 @@
1
- """
2
- idmtools-platform-comps - Placeholder Package
3
-
4
- This is a placeholder package to reserve the name on PyPI.
5
- The actual package will be published later.
6
- """
7
-
8
- __version__ = "0.0.0.dev0"
1
+ """idmtools comps platform.
2
+
3
+ We try to load the CLI here but if idmtools-cli is not installed, we fail gracefully.
4
+
5
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
6
+ """
7
+ # flake8: noqa F821
8
+ from idmtools_platform_comps.plugin_info import COMPSPlatformSpecification
9
+ try: # since cli is not required but we always try to load file, wrap in try except
10
+ from idmtools_platform_comps.comps_cli import CompsCLI
11
+ except ImportError:
12
+ pass
13
+
14
+ try:
15
+ from importlib.metadata import version, PackageNotFoundError
16
+ except ImportError:
17
+ # Python < 3.8
18
+ from importlib_metadata import version, PackageNotFoundError
19
+
20
+ try:
21
+ __version__ = version("idmtools-platform-comps") # Use your actual package name
22
+ except PackageNotFoundError:
23
+ # Package not installed, use fallback
24
+ __version__ = "0.0.0+unknown"
25
+
@@ -0,0 +1,4 @@
1
+ """idmtools comps cli module.
2
+
3
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
4
+ """
@@ -0,0 +1,50 @@
1
+ """idmtools cli utils.
2
+
3
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
4
+ """
5
+ from dataclasses import Field
6
+ from typing import Dict, Tuple
7
+
8
+
9
+ def validate_range(value: float, min: float, max: float) -> Tuple[bool, str]:
10
+ """
11
+ Function used to validate an integer value between min and max.
12
+
13
+ Args:
14
+ value: The value set by the user
15
+ min: Minimum value
16
+ max: Maximum value
17
+
18
+ Returns: tuple with validation result and error message if needed
19
+ """
20
+ if min <= value <= max:
21
+ return True, ''
22
+ return False, f"The value needs to be between {min} and {max}"
23
+
24
+
25
+ def environment_list(previous_settings: Dict, current_field: Field) -> Dict:
26
+ """
27
+ Allows the CLI to provide a list of available environments.
28
+
29
+ Uses the previous_settings to get the endpoint to query for environments
30
+
31
+ Args:
32
+ previous_settings: previous settings set by the user in the CLI.
33
+ current_field: Current field specs
34
+
35
+ Returns: updates to the choices and default
36
+ """
37
+ from COMPS import Client
38
+ Client.login(previous_settings["endpoint"])
39
+ client = Client.get("environments")
40
+ environment_choices = []
41
+ for environment_info in client.json()["Environments"]:
42
+ environment_choices.append(environment_info["EnvironmentName"])
43
+
44
+ # Set a valid default
45
+ if current_field.default not in environment_choices:
46
+ default_env = environment_choices[0]
47
+ else:
48
+ default_env = current_field.default
49
+
50
+ return {"choices": environment_choices, "default": default_env}
@@ -0,0 +1,492 @@
1
+ """idmtools comps cli comands.
2
+
3
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
4
+ """
5
+ # flake8: D301
6
+ import glob
7
+ import json as json_parser
8
+ import os
9
+ import sys
10
+ from typing import Optional, List
11
+ import tabulate
12
+ from getpass import getpass
13
+ from logging import getLogger
14
+ from COMPS.CredentialPrompt import CredentialPrompt
15
+ import json as je
16
+ from idmtools.assets import AssetCollection
17
+ from idmtools_platform_comps.utils.singularity_build import SingularityBuildWorkItem
18
+
19
+ logger = getLogger(__name__)
20
+ user_logger = getLogger('user')
21
+
22
+
23
+ def add_item(assets: AssetCollection, file: str):
24
+ """
25
+ Add Item.
26
+
27
+ Args:
28
+ assets: Assets
29
+ file: File or Directory
30
+
31
+ Returns:
32
+ None
33
+
34
+ Raises:
35
+ FileNotFoundError if file cannot be found.
36
+ """
37
+ if os.path.isdir(file):
38
+ assets.add_directory(file)
39
+ elif os.path.isfile(file):
40
+ assets.add_asset(file)
41
+ else:
42
+ user_logger.error(f"Cannot find file {file}")
43
+ raise FileNotFoundError(f"Cannot find file {file}")
44
+
45
+
46
+ try:
47
+
48
+ class StaticCredentialPrompt(CredentialPrompt):
49
+ """Provides a class to allow login to comps from a username password that is static or provided."""
50
+ def __init__(self, comps_url, username, password):
51
+ """Constructor."""
52
+ if (comps_url is None) or (username is None) or (password is None):
53
+ raise RuntimeError('Missing comps_url, or username or password')
54
+ self._times_prompted = 0
55
+ self.comps_url = comps_url
56
+ self.username = username
57
+ self.password = password
58
+
59
+ def prompt(self):
60
+ """Return our stores username and password."""
61
+ self._times_prompted = self._times_prompted + 1
62
+ if self._times_prompted > 3:
63
+ raise PermissionError('Failure authenticating')
64
+ return {'Username': self.username, 'Password': self.password}
65
+
66
+ os.environ['IDMTOOLS_NO_CONFIG_WARNING'] = '1'
67
+ from idmtools.core.platform_factory import Platform
68
+ import click
69
+ from idmtools_platform_comps.utils.assetize_output.assetize_output import AssetizeOutput
70
+ from idmtools_platform_comps.utils.file_filter_workitem import DEFAULT_EXCLUDES
71
+ from idmtools_platform_comps.comps_platform import COMPSPlatform
72
+
73
+ @click.group(short_help="COMPS platform related commands.")
74
+ @click.argument('config-block')
75
+ @click.pass_context
76
+ def comps(ctx: click.Context, config_block):
77
+ """
78
+ Commands related to managing the COMPS platform.
79
+
80
+ CONFIG_BLOCK - Name of configuration section or alias to load COMPS connection information from
81
+ """
82
+ ctx.obj = dict(config_block=config_block)
83
+
84
+ @comps.command(help="Login to COMPS")
85
+ @click.option('--username', required=True, help="Username")
86
+ @click.option('--password', help="Password")
87
+ @click.pass_context
88
+ def login(ctx: click.Context, username, password): # noqa D103
89
+ from COMPS import Client
90
+ from idmtools.core.logging import SUCCESS
91
+ os.environ['IDMTOOLS_LOGGING_USER_OUTPUT'] = '0'
92
+ if password:
93
+ user_logger.warning("Password the password via the command line is considered insecure")
94
+ else:
95
+ password = getpass("Password:")
96
+ # make platform object to load info from alias or config but don't login
97
+ platform = Platform(ctx.obj['config_block'], _skip_login=True)
98
+
99
+ try:
100
+ Client.login(platform.endpoint,
101
+ StaticCredentialPrompt(comps_url=platform.endpoint, username=username, password=password))
102
+ user_logger.log(SUCCESS, "Login succeeded")
103
+ except PermissionError:
104
+ user_logger.error(f"Could not loging to {platform.endpoint}")
105
+ sys.exit(-1)
106
+
107
+ @comps.command(help="Allows Downloading outputs from the command line")
108
+ @click.option('--pattern', default=[], multiple=True, help="File patterns")
109
+ @click.option('--exclude-pattern', default=DEFAULT_EXCLUDES, multiple=True, help="File patterns")
110
+ @click.option('--experiment', default=[], multiple=True, help="Experiment ids to filter for files to download")
111
+ @click.option('--simulation', default=[], multiple=True, help="Simulation ids to filter for files to download")
112
+ @click.option('--work-item', default=[], multiple=True, help="WorkItems ids to filter for files to download")
113
+ @click.option('--asset-collection', default=[], multiple=True,
114
+ help="Asset Collection ids to filter for files to download")
115
+ @click.option('--dry-run/--no-dry-run', default=False,
116
+ help="Gather a list of files that would be downloaded instead of actually downloading")
117
+ @click.option('--wait/--no-wait', default=True, help="Wait on item to finish")
118
+ @click.option('--include-assets/--no-include-assets', default=False,
119
+ help="Scan common assets of WorkItems and Experiments when filtering")
120
+ @click.option('--verbose/--no-verbose', default=True, help="Enable verbose output in worker")
121
+ @click.option('--json/--no-json', default=False, help="Outputs File list as JSON when used with dry run")
122
+ @click.option('--simulation-prefix-format-str', default=None,
123
+ help="Simulation Prefix Format str. Defaults to '{simulation.id}'. For no prefix, pass a empty string")
124
+ @click.option('--work-item-prefix-format-str', default=None, help="WorkItem Prefix Format str. Defaults to ''")
125
+ @click.option('--name', default=None, help="Name of Download Workitem. If not provided, one will be generated")
126
+ @click.option('--output-path', default=os.getcwd(), help="Output path to save zip")
127
+ @click.option('--delete-after-download/--no-delete-after-download', default=True,
128
+ help="Delete the workitem used to gather files after download")
129
+ @click.option('--extract-after-download/--no-extract-after-download', default=True,
130
+ help="Extract zip after download")
131
+ @click.option('--zip-name', default="output.zip", help="Name of zipfile")
132
+ @click.pass_context
133
+ def download( # noqa D103
134
+ ctx: click.Context, pattern, exclude_pattern, experiment, simulation, work_item, asset_collection, dry_run,
135
+ wait,
136
+ include_assets, verbose, json, simulation_prefix_format_str, work_item_prefix_format_str, name, output_path,
137
+ delete_after_download,
138
+ extract_after_download, zip_name
139
+ ):
140
+ from idmtools_platform_comps.utils.download.download import DownloadWorkItem
141
+
142
+ if json and not dry_run:
143
+ user_logger.error("You cannot return JSON without enabling dry-run mode")
144
+ sys.exit(-1)
145
+
146
+ if dry_run and delete_after_download:
147
+ user_logger.warning(
148
+ "You are using dry-run with delete after download. This will most result in an empty file list since "
149
+ "the item will be deleted before the output can be fetched.")
150
+
151
+ # ensure no output is enabled when using --json
152
+ if json:
153
+ os.environ['IDMTOOLS_LOGGING_USER_OUTPUT'] = '0'
154
+ os.environ['IDMTOOLS_DISABLE_PROGRESS_BAR'] = '1'
155
+
156
+ p: COMPSPlatform = Platform(ctx.obj['config_block'])
157
+
158
+ dl_wi = DownloadWorkItem(
159
+ output_path=output_path,
160
+ delete_after_download=delete_after_download,
161
+ extract_after_download=extract_after_download,
162
+ zip_name=zip_name
163
+ )
164
+
165
+ if name:
166
+ dl_wi.name = name
167
+ if pattern:
168
+ dl_wi.file_patterns = list(pattern)
169
+ if exclude_pattern:
170
+ dl_wi.exclude_patterns = exclude_pattern if isinstance(exclude_pattern, list) else list(exclude_pattern)
171
+
172
+ dl_wi.related_experiments = list(experiment)
173
+ dl_wi.related_simulations = list(simulation)
174
+ dl_wi.related_work_items = list(work_item)
175
+ dl_wi.related_asset_collections = list(asset_collection)
176
+ dl_wi.include_assets = include_assets
177
+ dl_wi.dry_run = dry_run
178
+ dl_wi.verbose = verbose
179
+ if simulation_prefix_format_str is not None:
180
+ if simulation_prefix_format_str.strip() == "":
181
+ dl_wi.no_simulation_prefix = True
182
+ else:
183
+ dl_wi.simulation_prefix_format_str = simulation_prefix_format_str
184
+ if work_item_prefix_format_str is not None:
185
+ dl_wi.work_item_prefix_format_str = work_item_prefix_format_str
186
+
187
+ if dl_wi.total_items_watched() == 0:
188
+ user_logger.error("You must specify at least one item to download")
189
+
190
+ dl_wi.run(wait_until_done=False, platform=p)
191
+ if not json and not delete_after_download:
192
+ user_logger.info(f"Item can be viewed at {p.get_workitem_link(dl_wi)}")
193
+ if wait:
194
+ dl_wi.wait(wait_on_done_progress=wait)
195
+ if dl_wi.succeeded:
196
+ if dl_wi.dry_run and not delete_after_download:
197
+ file = p.get_files(dl_wi, ['file_list.json'])
198
+ file = file['file_list.json'].decode('utf-8')
199
+ if json:
200
+ user_logger.info(file)
201
+ else:
202
+ file = json_parser.loads(file)
203
+ user_logger.info(tabulate.tabulate([x.values() for x in file], file[0].keys()))
204
+ else:
205
+ pass
206
+ elif dl_wi.failed:
207
+ user_logger.error("Download failed. Check logs in COMPS")
208
+ if dl_wi.failed:
209
+ dl_wi.fetch_error()
210
+ sys.exit(-1)
211
+
212
+ @comps.command(help="Allows assetizing outputs from the command line")
213
+ @click.option('--pattern', default=[], multiple=True, help="File patterns")
214
+ @click.option('--exclude-pattern', default=DEFAULT_EXCLUDES, multiple=True, help="File patterns")
215
+ @click.option('--experiment', default=[], multiple=True, help="Experiment ids to assetize")
216
+ @click.option('--simulation', default=[], multiple=True, help="Simulation ids to assetize")
217
+ @click.option('--work-item', default=[], multiple=True, help="WorkItems ids to assetize")
218
+ @click.option('--asset-collection', default=[], multiple=True, help="Asset Collection ids to assetize")
219
+ @click.option('--dry-run/--no-dry-run', default=False,
220
+ help="Gather a list of files that would be assetized instead of actually assetizing")
221
+ @click.option('--wait/--no-wait', default=True, help="Wait on item to finish")
222
+ @click.option('--include-assets/--no-include-assets', default=False,
223
+ help="Scan common assets of WorkItems and Experiments when filtering")
224
+ @click.option('--verbose/--no-verbose', default=True, help="Enable verbose output in worker")
225
+ @click.option('--json/--no-json', default=False, help="Outputs File list as JSON when used with dry run")
226
+ @click.option('--simulation-prefix-format-str', default=None,
227
+ help="Simulation Prefix Format str. Defaults to '{simulation.id}'. For no prefix, pass a empty string")
228
+ @click.option('--work-item-prefix-format-str', default=None, help="WorkItem Prefix Format str. Defaults to ''")
229
+ @click.option('--tag', default=[], type=(str, str), multiple=True,
230
+ help="Tags to add to the created asset collection as pairs.")
231
+ @click.option('--name', default=None, help="Name of AssetizeWorkitem. If not provided, one will be generated")
232
+ @click.option('--id-file/--no-id-file', default=False, help="Enable or disable writing out an id file")
233
+ @click.option('--id-filename', default=None,
234
+ help="Name of ID file to save build as. Required when id file is enabled")
235
+ @click.pass_context
236
+ def assetize_outputs( # noqa D103
237
+ ctx: click.Context, pattern, exclude_pattern, experiment, simulation, work_item, asset_collection, dry_run,
238
+ wait,
239
+ include_assets, verbose, json, simulation_prefix_format_str, work_item_prefix_format_str, tag, name,
240
+ id_file, id_filename
241
+ ):
242
+
243
+ if id_file and id_filename is None:
244
+ user_logger.error("--id-filename is required when filename is not provided")
245
+ sys.exit(-1)
246
+ if json:
247
+ os.environ['IDMTOOLS_LOGGING_USER_OUTPUT'] = '0'
248
+ os.environ['IDMTOOLS_DISABLE_PROGRESS_BAR'] = '1'
249
+
250
+ p: COMPSPlatform = Platform(ctx.obj['config_block'])
251
+ ao = AssetizeOutput()
252
+ if name:
253
+ ao.name = name
254
+ if pattern:
255
+ ao.file_patterns = list(pattern)
256
+ if exclude_pattern:
257
+ ao.exclude_patterns = exclude_pattern if isinstance(exclude_pattern, list) else list(exclude_pattern)
258
+ ao.related_experiments = list(experiment)
259
+ ao.related_simulations = list(simulation)
260
+ ao.related_work_items = list(work_item)
261
+ ao.related_asset_collections = list(asset_collection)
262
+ ao.include_assets = include_assets
263
+ ao.dry_run = dry_run
264
+ ao.verbose = verbose
265
+ if simulation_prefix_format_str is not None:
266
+ if simulation_prefix_format_str.strip() == "":
267
+ ao.no_simulation_prefix = True
268
+ else:
269
+ ao.simulation_prefix_format_str = simulation_prefix_format_str
270
+ if work_item_prefix_format_str is not None:
271
+ ao.work_item_prefix_format_str = work_item_prefix_format_str
272
+ if tag:
273
+ for name, value in tag:
274
+ ao.asset_tags[name] = value
275
+ if ao.total_items_watched() == 0:
276
+ user_logger.error("You must specify at least one item to assetize")
277
+ ao.run(wait_until_done=False, platform=p)
278
+ if not json:
279
+ user_logger.info(f"Item can be viewed at {p.get_workitem_link(ao)}")
280
+ if wait:
281
+ ao.wait(wait_on_done_progress=wait)
282
+ if ao.succeeded:
283
+ if ao.dry_run:
284
+ file = p.get_files(ao, ['file_list.json'])
285
+ file = file['file_list.json'].decode('utf-8')
286
+ if json:
287
+ user_logger.info(file)
288
+ else:
289
+ file = json_parser.loads(file)
290
+ user_logger.info(tabulate.tabulate([x.values() for x in file], file[0].keys()))
291
+ else:
292
+ if id_file:
293
+ ao.asset_collection.to_id_file(id_filename)
294
+ if json:
295
+ result = [i.short_remote_path() for i in ao.asset_collection.assets]
296
+ user_logger.info(je.dumps(result))
297
+ else:
298
+ user_logger.info(f"Created {ao.asset_collection.id}")
299
+ user_logger.info(f"It can be viewed at {p.get_asset_collection_link(ao.asset_collection)}")
300
+ user_logger.info("Items in Asset Collection")
301
+ user_logger.info("-------------------------")
302
+ for asset in ao.asset_collection:
303
+ user_logger.info(asset.short_remote_path())
304
+ elif ao.failed:
305
+ user_logger.error("Assetized failed. Check logs in COMPS")
306
+ if ao.failed:
307
+ ao.fetch_error()
308
+ sys.exit(-1)
309
+
310
+ @comps.command(help="""
311
+ \b
312
+ Create ac from requirement file
313
+ Args:
314
+ asset_tag: tag to be added to ac
315
+ pkg: package name (along with version)
316
+ wheel: package wheel file
317
+ """)
318
+ @click.argument('requirement', type=click.Path(exists=True), required=False)
319
+ @click.option('--asset_tag', multiple=True, help="Tag to be added to AC. Format: 'key:value'")
320
+ @click.option('--pkg', multiple=True, help="Package for override. Format: 'key==value'")
321
+ @click.option('--wheel', multiple=True, help="Local wheel file")
322
+ @click.pass_context
323
+ def req2ac(ctx: click.Context, requirement: str = None, asset_tag: Optional[List[str]] = None, # noqa D103
324
+ pkg: Optional[List[str]] = None,
325
+ wheel: Optional[List[str]] = None):
326
+ from idmtools_platform_comps.utils.python_requirements_ac.requirements_to_asset_collection import RequirementsToAssetCollection
327
+
328
+ pkg_list = list(pkg)
329
+ wheel_list = [os.path.abspath(w) for w in wheel]
330
+ tags = dict()
331
+ for t in asset_tag:
332
+ parts = t.split(':')
333
+ tags[parts[0]] = parts[1]
334
+
335
+ p: COMPSPlatform = Platform(ctx.obj['config_block'])
336
+ pl = RequirementsToAssetCollection(p, requirements_path=requirement, pkg_list=pkg_list,
337
+ local_wheels=wheel_list, asset_tags=tags)
338
+ ac_id = pl.run()
339
+ print(ac_id)
340
+
341
+ @comps.command(help="""
342
+ \b
343
+ Check ac existing based on requirement file
344
+ Args:
345
+ pkg: package name (along with version)
346
+ wheel: package wheel file
347
+ """)
348
+ @click.argument('requirement', type=click.Path(exists=True), required=False)
349
+ @click.option('--pkg', multiple=True, help="Package used for override. Format: say, 'key==value'")
350
+ @click.option('--wheel', multiple=True, help="Local wheel file")
351
+ @click.pass_context
352
+ def ac_exist(ctx: click.Context, requirement: str = None, pkg: Optional[List[str]] = None, wheel: Optional[List[str]] = None): # noqa D103
353
+ # TODO rename this and move to a subcommand for all the requirements functions
354
+ from idmtools_platform_comps.utils.python_requirements_ac.requirements_to_asset_collection import RequirementsToAssetCollection
355
+
356
+ pkg_list = list(pkg)
357
+ wheel_list = [os.path.abspath(w) for w in wheel]
358
+ p: COMPSPlatform = Platform(ctx.obj['config_block'])
359
+ pl = RequirementsToAssetCollection(p, requirements_path=requirement, pkg_list=pkg_list, local_wheels=wheel_list)
360
+ # Check if ac with md5 exists
361
+ ac = pl.retrieve_ac_by_tag()
362
+ if ac:
363
+ print("AC exist: ", ac.id)
364
+ else:
365
+ print("AC doesn't exist")
366
+
367
+ @comps.group(help="Singularity commands")
368
+ def singularity(): # noqa D103
369
+ pass
370
+
371
+ @singularity.command(help="Build Singularity Image")
372
+ @click.option('--common-input', default=[], multiple=True, help="Files")
373
+ @click.option('--common-input-glob', default=[], multiple=True, help="File patterns")
374
+ @click.option('--transient-input', default=[], multiple=True, help="Transient Files (Paths)")
375
+ @click.option('--transient-input-glob', default=[], multiple=True, help="Transient Files Glob Patterns")
376
+ @click.argument('definition_file')
377
+ @click.option('--wait/--no-wait', default=True, help="Wait on item to finish")
378
+ @click.option('--tag', default=[], type=(str, str), multiple=True,
379
+ help="Extra Tags as Value Pairs for the Resulting AC")
380
+ @click.option('--workitem-tag', default=[], type=(str, str), multiple=True,
381
+ help="Extra Tags as Value Pairs for the WorkItem")
382
+ @click.option('--name', default=None, help="Name of WorkItem. If not provided, one will be generated")
383
+ @click.option('--force/--no-force', default=False, help="Force build, ignoring build context")
384
+ @click.option('--image-name', default=None, help="Name of resulting image")
385
+ @click.option('--id-file/--no-id-file', default=True,
386
+ help="Enable or disable writing out an ID file that points to the created asset collection")
387
+ @click.option('--id-filename', default=None,
388
+ help="Name of ID file to save build as. If not specified, and id-file is enabled, a name is calculated")
389
+ @click.option('--id-workitem/--no-id-workitem', default=True,
390
+ help="Enable or disable writing out an id file for the workitem")
391
+ @click.option('--id-workitem-failed/--no-id-workitem-failed', default=False,
392
+ help="Write id of the workitem even if it failed. You need to enable --id-workitem for this is be active")
393
+ @click.option('--id-workitem-filename', default=None,
394
+ help="Name of ID file to save workitem to. You need to enable --id-workitem for this is be active")
395
+ @click.pass_context
396
+ def build(ctx: click.Context, common_input, common_input_glob, transient_input, transient_input_glob, # noqa D103
397
+ definition_file, wait, tag, workitem_tag, name, force, image_name: str,
398
+ id_file: str, id_filename: str, id_workitem: bool, id_workitem_failed: bool, id_workitem_filename: str):
399
+ p: COMPSPlatform = Platform(ctx.obj['config_block'])
400
+ sb = SingularityBuildWorkItem(definition_file=definition_file, name=name, force=force, image_name=image_name)
401
+
402
+ if tag:
403
+ for name, value in tag:
404
+ sb.image_tags[name] = value
405
+
406
+ if workitem_tag:
407
+ for name, value in tag:
408
+ sb.tags[name] = value
409
+
410
+ # Add inputs from files
411
+ for assets, inputs in [(sb.assets, common_input), (sb.transient_assets, transient_input)]:
412
+ for file in inputs:
413
+ add_item(assets, file)
414
+
415
+ # And then from glob patterns
416
+ for assets, patterns in [(sb.assets, common_input_glob), (sb.transient_assets, transient_input_glob)]:
417
+ for pattern in patterns:
418
+ for file in glob.glob(pattern):
419
+ add_item(assets, file)
420
+
421
+ sb.run(wait_until_done=wait, platform=p)
422
+ if sb.succeeded and id_file:
423
+ if id_filename is None:
424
+ id_filename = sb.get_id_filename()
425
+ user_logger.info(f"Saving the Asset collection ID that contains the image to {id_filename}")
426
+ sb.asset_collection.to_id_file(id_filename, save_platform=True)
427
+ if id_workitem:
428
+ # TODO when we should use platform id but that need to be updated through the code base
429
+ if sb.succeeded and sb._uid is None:
430
+ user_logger.warning(
431
+ "Cannot save workitem id because an existing container was found with the same inputs. You can force run using --force, but it is recommended to use the container used.")
432
+ elif id_workitem_failed or sb.succeeded:
433
+ if id_workitem_filename is None:
434
+ id_workitem_filename = sb.get_id_filename(prefix="builder.")
435
+ user_logger.info(f"Saving the Builder Workitem ID that contains the image to {id_workitem_filename}")
436
+ sb.to_id_file(id_workitem_filename, save_platform=True)
437
+ sys.exit(0 if sb.succeeded else -1)
438
+
439
+ @singularity.command(help="Pull Singularity Image")
440
+ @click.argument('image_url')
441
+ @click.option('--wait/--no-wait', default=True, help="Wait on item to finish")
442
+ @click.option('--tag', default=[], type=(str, str), multiple=True,
443
+ help="Extra Tags as Value Pairs for the Resulting AC")
444
+ @click.option('--workitem-tag', default=[], type=(str, str), multiple=True,
445
+ help="Extra Tags as Value Pairs for the WorkItem")
446
+ @click.option('--name', default=None, help="Name of WorkItem. If not provided, one will be generated")
447
+ @click.option('--force/--no-force', default=False, help="Force build, ignoring build context")
448
+ @click.option('--image-name', default=None, help="Name of resulting image")
449
+ @click.option('--id-file/--no-id-file', default=True, help="Enable or disable writing out an id file")
450
+ @click.option('--id-filename', default=None,
451
+ help="Name of ID file to save build as. If not specified, and id-file is enabled, a name is calculated")
452
+ @click.option('--id-workitem/--no-id-workitem', default=True,
453
+ help="Enable or disable writing out an id file for the workitem")
454
+ @click.option('--id-workitem-failed/--no-id-workitem-failed', default=False,
455
+ help="Write id of the workitem even if it failed. You need to enable --id-workitem for this is be active")
456
+ @click.option('--id-workitem-filename', default=None,
457
+ help="Name of ID file to save workitem to. You need to enable --id-workitem for this is be active")
458
+ @click.pass_context
459
+ def pull(ctx: click.Context, image_url, wait, tag, workitem_tag, name, force, image_name: str, id_file: str, # noqa D103
460
+ id_filename: str,
461
+ id_workitem: bool, id_workitem_failed: bool, id_workitem_filename: str):
462
+ p: COMPSPlatform = Platform(ctx.obj['config_block'])
463
+ sb = SingularityBuildWorkItem(image_url=image_url, force=force, image_name=image_name)
464
+ sb.name = f"Pulling {image_url}" if name is None else name
465
+
466
+ if tag:
467
+ for name, value in tag:
468
+ sb.image_tags[name] = value
469
+
470
+ if workitem_tag:
471
+ for name, value in tag:
472
+ sb.tags[name] = value
473
+
474
+ sb.run(wait_until_done=wait, platform=p)
475
+ if sb.succeeded and id_file:
476
+ if id_filename is None:
477
+ id_filename = sb.get_id_filename()
478
+ user_logger.info(f"Saving ID to {id_filename}")
479
+ sb.asset_collection.to_id_file(id_filename, save_platform=True)
480
+
481
+ if id_workitem and sb.succeeded and sb._uid is None:
482
+ user_logger.warning(
483
+ "Cannot save workitem id because an existing container was found with the same inputs. You can force run using --force, but it is recommended to use the container used.")
484
+ elif id_workitem_failed or sb.succeeded:
485
+ if id_workitem_filename is None:
486
+ id_workitem_filename = sb.get_id_filename(prefix="builder.")
487
+ user_logger.info(f"Saving the Builder Workitem ID that contains the image to {id_workitem_filename}")
488
+ sb.to_id_file(id_workitem_filename, save_platform=True)
489
+ sys.exit(0 if sb.succeeded else -1)
490
+
491
+ except ImportError as e:
492
+ logger.warning(f"COMPS CLI not enabled because a dependency is missing. Most likely it is either click or idmtools cli {e.args}")
@@ -0,0 +1,48 @@
1
+ """Define the comps cli spec.
2
+
3
+ Notes:
4
+ - We eventually need to deprecate this
5
+
6
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
7
+ """
8
+ try: # since cli is not required but we always try to load file, wrap in try except
9
+ from typing import NoReturn
10
+
11
+ from idmtools.registry.plugin_specification import get_description_impl
12
+ from idmtools_cli.iplatform_cli import IPlatformCLI, PlatformCLISpecification, get_platform_cli_impl, \
13
+ get_additional_commands_impl
14
+
15
+ class CompsCLI(IPlatformCLI):
16
+ """Defines our CLI interface for COMPS using IPlatformCLI."""
17
+
18
+ def get_experiment_status(self, *args, **kwargs) -> NoReturn:
19
+ """Experiment status command."""
20
+ pass
21
+
22
+ def get_simulation_status(self, *args, **kwargs) -> NoReturn:
23
+ """Simulation status command."""
24
+ pass
25
+
26
+ def get_platform_information(self) -> dict:
27
+ """Platofrm info."""
28
+ pass
29
+
30
+ class COMPSCLISpecification(PlatformCLISpecification):
31
+ """Provides plugin definition for CompsCLI."""
32
+
33
+ @get_platform_cli_impl
34
+ def get(self, configuration: dict) -> CompsCLI:
35
+ """Get our CLI plugin with config."""
36
+ return CompsCLI(**configuration)
37
+
38
+ @get_additional_commands_impl
39
+ def get_additional_commands(self) -> NoReturn:
40
+ """Get our CLI commands."""
41
+ import idmtools_platform_comps.cli.comps # noqa: F401
42
+
43
+ @get_description_impl
44
+ def get_description(self) -> str:
45
+ """Get COMPS CLI plugin description."""
46
+ return "Provides CLI commands for the COMPS Platform"
47
+ except ImportError:
48
+ pass
@@ -0,0 +1,6 @@
1
+ """idmtools comps operations module.
2
+
3
+ The operations define how to interact with specific item types.
4
+
5
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
6
+ """