terralab-cli 3.2.2__tar.gz → 3.2.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.
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/PKG-INFO +3 -3
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/pyproject.toml +3 -3
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/cli.py +4 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/commands/pipeline_runs_commands.py +41 -1
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/log.py +1 -1
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/logic/pipeline_runs_logic.py +19 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/utils.py +17 -1
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/LICENSE +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/README.md +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/.terralab-cli-config +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/__init__.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/auth_helper.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/client.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/commands/__init__.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/commands/account_commands.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/commands/auth_commands.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/commands/pipelines_commands.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/commands/quotas_commands.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/config.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/constants.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/logic/__init__.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/logic/account_logic.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/logic/auth_logic.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/logic/pipelines_logic.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/logic/quotas_logic.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/sam_helper.py +0 -0
- {terralab_cli-3.2.2 → terralab_cli-3.2.4}/terralab/version_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: terralab-cli
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.4
|
|
4
4
|
Summary: Command line interface for interacting with the Terra Scientific Pipelines Service, or Teaspoons.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Terra Scientific Services
|
|
@@ -15,12 +15,12 @@ Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
|
15
15
|
Requires-Dist: colorlog (>=6.9.0,<7.0.0)
|
|
16
16
|
Requires-Dist: cryptography (>=44.0.1,<48.0.0)
|
|
17
17
|
Requires-Dist: oauth2-cli-auth (>=1.5,<3.0)
|
|
18
|
-
Requires-Dist: poetry (==2.
|
|
18
|
+
Requires-Dist: poetry (==2.4.0)
|
|
19
19
|
Requires-Dist: prompt-toolkit (>=3.0.51,<4.0.0)
|
|
20
20
|
Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
|
|
21
21
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
22
22
|
Requires-Dist: tabulate (>=0.9,<0.11)
|
|
23
|
-
Requires-Dist: terra-scientific-pipelines-service-api-client (==2.4.
|
|
23
|
+
Requires-Dist: terra-scientific-pipelines-service-api-client (==2.4.5)
|
|
24
24
|
Requires-Dist: tqdm (>=4.67.0,<5.0.0)
|
|
25
25
|
Requires-Dist: tzlocal (>=5.2,<6.0)
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "terralab-cli"
|
|
3
3
|
# version is auto-incremented. do not edit.
|
|
4
|
-
version = "3.2.
|
|
4
|
+
version = "3.2.4"
|
|
5
5
|
description = "Command line interface for interacting with the Terra Scientific Pipelines Service, or Teaspoons."
|
|
6
6
|
authors = ["Terra Scientific Services <teaspoons-developers@broadinstitute.org>"]
|
|
7
7
|
readme = "README.md"
|
|
@@ -14,7 +14,7 @@ terralab = "terralab.cli:cli"
|
|
|
14
14
|
|
|
15
15
|
[tool.poetry.dependencies]
|
|
16
16
|
python = "^3.12"
|
|
17
|
-
terra-scientific-pipelines-service-api-client = "2.4.
|
|
17
|
+
terra-scientific-pipelines-service-api-client = "2.4.5"
|
|
18
18
|
python-dotenv = "^1.0.1"
|
|
19
19
|
click = "^8.1.7"
|
|
20
20
|
colorlog = "^6.9.0"
|
|
@@ -24,7 +24,7 @@ requests = "^2.32.3"
|
|
|
24
24
|
tqdm = "^4.67.0"
|
|
25
25
|
tzlocal = "^5.2"
|
|
26
26
|
tabulate = ">=0.9,<0.11"
|
|
27
|
-
poetry = "2.
|
|
27
|
+
poetry = "2.4.0"
|
|
28
28
|
cryptography = ">=44.0.1,<48.0.0"
|
|
29
29
|
prompt-toolkit = "^3.0.51"
|
|
30
30
|
|
|
@@ -13,6 +13,7 @@ from terralab.commands.auth_commands import logout, login_with_oauth, login
|
|
|
13
13
|
from terralab.commands.pipeline_runs_commands import (
|
|
14
14
|
submit,
|
|
15
15
|
download,
|
|
16
|
+
deliver,
|
|
16
17
|
jobs,
|
|
17
18
|
details as details_jobs,
|
|
18
19
|
list_command as list_jobs,
|
|
@@ -74,6 +75,9 @@ def cli(debug: bool) -> None:
|
|
|
74
75
|
cli.add_command(submit)
|
|
75
76
|
cli.add_command(download)
|
|
76
77
|
|
|
78
|
+
# deliver
|
|
79
|
+
cli.add_command(deliver)
|
|
80
|
+
|
|
77
81
|
# jobs
|
|
78
82
|
cli.add_command(jobs)
|
|
79
83
|
cli.add_command(list_jobs, name=" jobs list")
|
|
@@ -5,7 +5,7 @@ from typing import Any
|
|
|
5
5
|
import uuid
|
|
6
6
|
|
|
7
7
|
import click
|
|
8
|
-
from teaspoons_client import AsyncPipelineRunResponseV2, PipelineRun # type: ignore[attr-defined]
|
|
8
|
+
from teaspoons_client import AsyncPipelineRunResponseV2, DataDeliveryReport, PipelineRun # type: ignore[attr-defined]
|
|
9
9
|
|
|
10
10
|
from terralab.constants import FAILED_KEY, SUPPORT_EMAIL_TEXT, SUCCEEDED_KEY
|
|
11
11
|
from terralab.log import (
|
|
@@ -19,6 +19,7 @@ from terralab.utils import (
|
|
|
19
19
|
convert_file_size_to_human_readable,
|
|
20
20
|
handle_api_exceptions,
|
|
21
21
|
process_inputs_to_dict,
|
|
22
|
+
validate_gcs_path,
|
|
22
23
|
validate_job_id,
|
|
23
24
|
format_timestamp,
|
|
24
25
|
)
|
|
@@ -151,6 +152,8 @@ def details(job_id: str) -> None:
|
|
|
151
152
|
)
|
|
152
153
|
)
|
|
153
154
|
|
|
155
|
+
display_data_delivery(response.pipeline_run_report.data_delivery_report)
|
|
156
|
+
|
|
154
157
|
|
|
155
158
|
def display_outputs(outputs: dict[str, dict[str, Any]] | None) -> None:
|
|
156
159
|
if not outputs:
|
|
@@ -178,6 +181,14 @@ def display_outputs(outputs: dict[str, dict[str, Any]] | None) -> None:
|
|
|
178
181
|
)
|
|
179
182
|
|
|
180
183
|
|
|
184
|
+
def display_data_delivery(data_delivery_report: DataDeliveryReport | None) -> None:
|
|
185
|
+
if not data_delivery_report:
|
|
186
|
+
return
|
|
187
|
+
LOGGER.info(add_blankline_before("Data Delivery:"))
|
|
188
|
+
LOGGER.info(indented(f"Status: {format_status(data_delivery_report.status)}"))
|
|
189
|
+
LOGGER.info(indented(f"Destination: {data_delivery_report.destination}"))
|
|
190
|
+
|
|
191
|
+
|
|
181
192
|
@jobs.command(name="list", short_help="List your jobs")
|
|
182
193
|
@click.option(
|
|
183
194
|
"--num_results",
|
|
@@ -213,3 +224,32 @@ def list_command(num_results: int) -> None:
|
|
|
213
224
|
)
|
|
214
225
|
|
|
215
226
|
LOGGER.info(format_table_with_status(row_list))
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# DELIVER group
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@click.command(short_help="Deliver output files from a job to a cloud destination")
|
|
233
|
+
@click.argument("job_id", type=str)
|
|
234
|
+
@click.argument("destination", type=str)
|
|
235
|
+
@handle_api_exceptions
|
|
236
|
+
def deliver(job_id: str, destination: str) -> None:
|
|
237
|
+
"""Deliver output files from a job with JOB_ID identifier to a DESTINATION GCS path.
|
|
238
|
+
|
|
239
|
+
Note: delivering your data to a cloud destination will disable downloading locally.
|
|
240
|
+
"""
|
|
241
|
+
job_id_uuid: uuid.UUID = validate_job_id(job_id)
|
|
242
|
+
validated_destination: str = validate_gcs_path(destination)
|
|
243
|
+
|
|
244
|
+
pipeline_runs_logic.deliver_pipeline_run_to_cloud(
|
|
245
|
+
job_id_uuid, validated_destination
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
LOGGER.info(
|
|
249
|
+
f"Successfully initiated data delivery for job {job_id} to {validated_destination}. Delivery may take a few minutes to complete."
|
|
250
|
+
)
|
|
251
|
+
LOGGER.info(
|
|
252
|
+
add_blankline_before(
|
|
253
|
+
f"You can check the status of the delivery with `terralab jobs details {job_id}`."
|
|
254
|
+
)
|
|
255
|
+
)
|
|
@@ -124,7 +124,7 @@ def format_status_in_table_row(
|
|
|
124
124
|
|
|
125
125
|
|
|
126
126
|
def format_status(status_str: str) -> str:
|
|
127
|
-
return COLORFUL_STATUS
|
|
127
|
+
return COLORFUL_STATUS.get(status_str, status_str)
|
|
128
128
|
|
|
129
129
|
|
|
130
130
|
# This filter makes the retry messages from urllib3 more user-friendly.
|
|
@@ -7,11 +7,13 @@ from typing import Any
|
|
|
7
7
|
from teaspoons_client import ( # type: ignore[attr-defined]
|
|
8
8
|
AsyncPipelineRunResponseV2,
|
|
9
9
|
JobControl,
|
|
10
|
+
JobReport,
|
|
10
11
|
PipelineRun,
|
|
11
12
|
PipelineRunOutputSignedUrlsResponse,
|
|
12
13
|
PipelineRunsApi,
|
|
13
14
|
PreparePipelineRunRequestBody,
|
|
14
15
|
PreparePipelineRunResponseV2,
|
|
16
|
+
StartDataDeliveryRequestBody,
|
|
15
17
|
StartPipelineRunRequestBody,
|
|
16
18
|
)
|
|
17
19
|
|
|
@@ -167,6 +169,23 @@ def prepare_upload_start_pipeline_run(
|
|
|
167
169
|
return start_pipeline_run(job_id)
|
|
168
170
|
|
|
169
171
|
|
|
172
|
+
## deliver action
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def deliver_pipeline_run_to_cloud(
|
|
176
|
+
job_id: uuid.UUID, destination_gcs_path: str
|
|
177
|
+
) -> JobReport:
|
|
178
|
+
"""Call the deliverPipelineRunOutputFilesToCloud Teaspoons endpoint and return the JobReport."""
|
|
179
|
+
start_data_delivery_request_body = StartDataDeliveryRequestBody(
|
|
180
|
+
destinationGcsPath=destination_gcs_path
|
|
181
|
+
)
|
|
182
|
+
with ClientWrapper() as api_client:
|
|
183
|
+
pipeline_runs_client = PipelineRunsApi(api_client=api_client)
|
|
184
|
+
return pipeline_runs_client.deliver_pipeline_run_output_files_to_cloud(
|
|
185
|
+
str(job_id), start_data_delivery_request_body
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
170
189
|
## download action
|
|
171
190
|
|
|
172
191
|
|
|
@@ -17,7 +17,11 @@ from tqdm import tqdm
|
|
|
17
17
|
from tqdm.contrib.logging import logging_redirect_tqdm
|
|
18
18
|
from urllib3.exceptions import MaxRetryError
|
|
19
19
|
|
|
20
|
-
from terralab.constants import
|
|
20
|
+
from terralab.constants import (
|
|
21
|
+
MAX_FILE_UPLOAD_SIZE_BYTES,
|
|
22
|
+
SUPPORT_EMAIL_TEXT,
|
|
23
|
+
GCS_PREFIX,
|
|
24
|
+
)
|
|
21
25
|
from terralab.log import add_blankline_before
|
|
22
26
|
|
|
23
27
|
LOGGER = logging.getLogger(__name__)
|
|
@@ -284,6 +288,18 @@ def validate_job_id(job_id: str) -> uuid.UUID:
|
|
|
284
288
|
exit(1)
|
|
285
289
|
|
|
286
290
|
|
|
291
|
+
def validate_gcs_path(gcs_path: str) -> str:
|
|
292
|
+
"""Validates that the provided string is a valid GCS path (gs://bucket/...).
|
|
293
|
+
|
|
294
|
+
Returns the path if valid, otherwise logs an error and exits."""
|
|
295
|
+
if not gcs_path.startswith(GCS_PREFIX) or len(gcs_path) <= len(GCS_PREFIX):
|
|
296
|
+
LOGGER.error(
|
|
297
|
+
f"Error: '{gcs_path}' is not a valid GCS path. GCS paths must start with '{GCS_PREFIX}' followed by a bucket name."
|
|
298
|
+
)
|
|
299
|
+
exit(1)
|
|
300
|
+
return gcs_path
|
|
301
|
+
|
|
302
|
+
|
|
287
303
|
def format_timestamp(
|
|
288
304
|
timestamp_string: str | None, timestamp_format: str = "%Y-%m-%d %H:%M"
|
|
289
305
|
) -> str:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|