coiled 1.118.4.dev6__py3-none-any.whl → 1.129.3.dev10__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.
- coiled/batch.py +17 -1
- coiled/capture_environment.py +45 -77
- coiled/cli/batch/run.py +141 -10
- coiled/cli/batch/util.py +28 -0
- coiled/cli/batch/wait.py +57 -47
- coiled/cli/core.py +4 -0
- coiled/cli/curl.py +7 -2
- coiled/cli/file.py +116 -0
- coiled/cli/hello/hello.py +6 -5
- coiled/cli/mpi.py +252 -0
- coiled/cli/notebook/notebook.py +10 -0
- coiled/cli/run.py +53 -10
- coiled/cli/setup/aws.py +48 -12
- coiled/cli/setup/azure.py +50 -1
- coiled/context.py +2 -2
- coiled/credentials/google.py +1 -20
- coiled/filestore.py +458 -0
- coiled/plugins.py +3 -0
- coiled/pypi_conda_map.py +14 -0
- coiled/software_utils.py +140 -5
- coiled/spans.py +2 -0
- coiled/types.py +18 -1
- coiled/utils.py +65 -1
- coiled/v2/cluster.py +25 -3
- coiled/v2/cluster_comms.py +72 -0
- coiled/v2/core.py +7 -0
- {coiled-1.118.4.dev6.dist-info → coiled-1.129.3.dev10.dist-info}/METADATA +1 -1
- {coiled-1.118.4.dev6.dist-info → coiled-1.129.3.dev10.dist-info}/RECORD +31 -26
- {coiled-1.118.4.dev6.dist-info → coiled-1.129.3.dev10.dist-info}/WHEEL +1 -1
- {coiled-1.118.4.dev6.dist-info → coiled-1.129.3.dev10.dist-info}/entry_points.txt +0 -0
- {coiled-1.118.4.dev6.dist-info → coiled-1.129.3.dev10.dist-info}/licenses/LICENSE +0 -0
coiled/cli/setup/aws.py
CHANGED
|
@@ -240,7 +240,7 @@ setup_doc = """{
|
|
|
240
240
|
}"""
|
|
241
241
|
|
|
242
242
|
|
|
243
|
-
def get_ongoing_doc(ecr=True,
|
|
243
|
+
def get_ongoing_doc(ecr=True, package_sync_bucket_prefix=None) -> str:
|
|
244
244
|
optional_permissions = []
|
|
245
245
|
|
|
246
246
|
if ecr:
|
|
@@ -266,7 +266,7 @@ def get_ongoing_doc(ecr=True, custom_bucket_prefix=None) -> str:
|
|
|
266
266
|
],
|
|
267
267
|
})
|
|
268
268
|
|
|
269
|
-
if
|
|
269
|
+
if package_sync_bucket_prefix:
|
|
270
270
|
optional_permissions.extend([
|
|
271
271
|
{
|
|
272
272
|
"Sid": "OngoingPackageSyncBucketCreate",
|
|
@@ -277,7 +277,7 @@ def get_ongoing_doc(ecr=True, custom_bucket_prefix=None) -> str:
|
|
|
277
277
|
"s3:PutBucketOwnershipControls",
|
|
278
278
|
"s3:PutBucketPolicy",
|
|
279
279
|
],
|
|
280
|
-
"Resource": [f"arn:*:s3:::{
|
|
280
|
+
"Resource": [f"arn:*:s3:::{package_sync_bucket_prefix}-*"],
|
|
281
281
|
},
|
|
282
282
|
{
|
|
283
283
|
"Sid": "OngoingPackageSyncEnvUploadDownload",
|
|
@@ -286,10 +286,34 @@ def get_ongoing_doc(ecr=True, custom_bucket_prefix=None) -> str:
|
|
|
286
286
|
"s3:PutObject",
|
|
287
287
|
"s3:GetObject",
|
|
288
288
|
],
|
|
289
|
-
"Resource": [f"arn:*:s3:::{
|
|
289
|
+
"Resource": [f"arn:*:s3:::{package_sync_bucket_prefix}-*/*"],
|
|
290
290
|
},
|
|
291
291
|
])
|
|
292
292
|
|
|
293
|
+
# TODO should this be configurable?
|
|
294
|
+
optional_permissions.extend([
|
|
295
|
+
{
|
|
296
|
+
"Sid": "OngoingPersistentDataBuckets",
|
|
297
|
+
"Effect": "Allow",
|
|
298
|
+
"Action": [
|
|
299
|
+
"s3:CreateBucket",
|
|
300
|
+
"s3:ListBucket",
|
|
301
|
+
"s3:PutBucketOwnershipControls",
|
|
302
|
+
"s3:PutBucketPolicy",
|
|
303
|
+
],
|
|
304
|
+
"Resource": ["arn:*:s3:::coiled-data-*"],
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"Sid": "OngoingPersistentDataObjects",
|
|
308
|
+
"Effect": "Allow",
|
|
309
|
+
"Action": [
|
|
310
|
+
"s3:PutObject",
|
|
311
|
+
"s3:GetObject",
|
|
312
|
+
],
|
|
313
|
+
"Resource": ["arn:*:s3:::coiled-data-*/*"],
|
|
314
|
+
},
|
|
315
|
+
])
|
|
316
|
+
|
|
293
317
|
ongoing_policy_statement = {
|
|
294
318
|
"Statement": [
|
|
295
319
|
{
|
|
@@ -297,6 +321,7 @@ def get_ongoing_doc(ecr=True, custom_bucket_prefix=None) -> str:
|
|
|
297
321
|
"Effect": "Allow",
|
|
298
322
|
"Resource": "*",
|
|
299
323
|
"Action": [
|
|
324
|
+
"ec2:AuthorizeSecurityGroupEgress",
|
|
300
325
|
"ec2:AuthorizeSecurityGroupIngress",
|
|
301
326
|
"ec2:CreateFleet",
|
|
302
327
|
"ec2:CreateLaunchTemplate",
|
|
@@ -361,6 +386,16 @@ def get_ongoing_doc(ecr=True, custom_bucket_prefix=None) -> str:
|
|
|
361
386
|
],
|
|
362
387
|
"Condition": {"StringEquals": {"ec2:ResourceTag/owner": "coiled"}},
|
|
363
388
|
},
|
|
389
|
+
{
|
|
390
|
+
"Sid": "OngoingPlacementGroupPolicy",
|
|
391
|
+
"Effect": "Allow",
|
|
392
|
+
"Resource": "arn:*:ec2:*:*:placement-group/coiled-*",
|
|
393
|
+
"Action": [
|
|
394
|
+
"ec2:CreatePlacementGroup",
|
|
395
|
+
"ec2:DescribePlacementGroups",
|
|
396
|
+
"ec2:DeletePlacementGroup",
|
|
397
|
+
],
|
|
398
|
+
},
|
|
364
399
|
{
|
|
365
400
|
"Sid": "OptionalLogPull",
|
|
366
401
|
"Effect": "Allow",
|
|
@@ -1393,7 +1428,7 @@ def do_setup(
|
|
|
1393
1428
|
user_name = slug
|
|
1394
1429
|
setup_name = setup_name or f"{slug}-setup"
|
|
1395
1430
|
ongoing_name = ongoing_name or f"{slug}-ongoing"
|
|
1396
|
-
ongoing_doc = get_ongoing_doc(
|
|
1431
|
+
ongoing_doc = get_ongoing_doc(package_sync_bucket_prefix=custom_s3_bucket_prefix)
|
|
1397
1432
|
|
|
1398
1433
|
try:
|
|
1399
1434
|
aws_account = do_intro(sts, iam, region=region, coiled_account=coiled_account)
|
|
@@ -1508,26 +1543,27 @@ def check_local_aws_creds():
|
|
|
1508
1543
|
return False
|
|
1509
1544
|
|
|
1510
1545
|
|
|
1511
|
-
|
|
1512
|
-
policy_name = "CoiledInstancePolicy"
|
|
1513
|
-
policy_doc = """{
|
|
1546
|
+
INSTANCE_POLICY_DOCUMENT = """{
|
|
1514
1547
|
"Version": "2012-10-17",
|
|
1515
1548
|
"Statement": [
|
|
1516
1549
|
{
|
|
1517
|
-
"Sid": "
|
|
1550
|
+
"Sid": "CoiledEC2LogPolicy",
|
|
1518
1551
|
"Effect": "Allow",
|
|
1519
1552
|
"Action": [
|
|
1520
1553
|
"logs:CreateLogGroup",
|
|
1521
1554
|
"logs:CreateLogStream",
|
|
1522
|
-
"logs:PutLogEvents"
|
|
1523
|
-
"cloudwatch:PutMetricData",
|
|
1524
|
-
"aps:RemoteWrite"
|
|
1555
|
+
"logs:PutLogEvents"
|
|
1525
1556
|
],
|
|
1526
1557
|
"Resource": "*"
|
|
1527
1558
|
}
|
|
1528
1559
|
]
|
|
1529
1560
|
}"""
|
|
1530
1561
|
|
|
1562
|
+
|
|
1563
|
+
def update_instance_profile_policy(iam, aws_account, yes):
|
|
1564
|
+
policy_name = "CoiledInstancePolicy"
|
|
1565
|
+
policy_doc = INSTANCE_POLICY_DOCUMENT
|
|
1566
|
+
|
|
1531
1567
|
# Check for existing policy
|
|
1532
1568
|
policy_arn, policy_diff = get_policy_diff(iam, aws_account, policy_name, policy_doc)
|
|
1533
1569
|
|
coiled/cli/setup/azure.py
CHANGED
|
@@ -175,8 +175,21 @@ coiled curl -X POST "${SETUP_ENDPOINT}" --json --data "{\\"credentials\\": {\\"t
|
|
|
175
175
|
"service principal for Coiled to use."
|
|
176
176
|
),
|
|
177
177
|
)
|
|
178
|
+
@click.option(
|
|
179
|
+
"--refresh-for-app-id", default=None, help="Refresh the secret key used by Coiled for specified Application ID."
|
|
180
|
+
)
|
|
178
181
|
@click.command(context_settings=CONTEXT_SETTINGS)
|
|
179
|
-
def azure_setup(
|
|
182
|
+
def azure_setup(
|
|
183
|
+
subscription,
|
|
184
|
+
resource_group,
|
|
185
|
+
region,
|
|
186
|
+
account,
|
|
187
|
+
iam_user,
|
|
188
|
+
keep_existing_access,
|
|
189
|
+
save_script,
|
|
190
|
+
ship_token,
|
|
191
|
+
refresh_for_app_id,
|
|
192
|
+
):
|
|
180
193
|
print(
|
|
181
194
|
"Coiled on Azure is currently in [bold]public beta[/bold], "
|
|
182
195
|
"please contact [link]support@coiled.io[/link] if you have any questions or problems."
|
|
@@ -264,6 +277,17 @@ def azure_setup(subscription, resource_group, region, account, iam_user, keep_ex
|
|
|
264
277
|
f"with [green]{region}[/green] as the default region\n"
|
|
265
278
|
)
|
|
266
279
|
|
|
280
|
+
if refresh_for_app_id:
|
|
281
|
+
refresh_app_creds(
|
|
282
|
+
app_id=refresh_for_app_id,
|
|
283
|
+
coiled_account=coiled_account,
|
|
284
|
+
sub_id=sub_id,
|
|
285
|
+
rg_name=rg_name,
|
|
286
|
+
region=region,
|
|
287
|
+
keep_existing_keys=True,
|
|
288
|
+
)
|
|
289
|
+
return
|
|
290
|
+
|
|
267
291
|
if ship_token:
|
|
268
292
|
enable_providers(creds, sub_id)
|
|
269
293
|
ship_token_creds(
|
|
@@ -378,6 +402,31 @@ def setup_with_service_principal(
|
|
|
378
402
|
return True
|
|
379
403
|
|
|
380
404
|
|
|
405
|
+
def refresh_app_creds(app_id, keep_existing_keys, coiled_account, sub_id, rg_name, region):
|
|
406
|
+
print(f" [bright_black]Resetting/retrieving credentials for {app_id}...")
|
|
407
|
+
cred_reset_opts = "--append" if keep_existing_keys else ""
|
|
408
|
+
app_creds_json = az_cli_wrapper(f"az ad app credential reset --id {app_id} {cred_reset_opts}")
|
|
409
|
+
app_creds = json.loads(app_creds_json)
|
|
410
|
+
|
|
411
|
+
creds_to_submit = {
|
|
412
|
+
"tenant_id": app_creds["tenant"],
|
|
413
|
+
"client_id": app_creds["appId"],
|
|
414
|
+
"client_secret": app_creds["password"],
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
print("Sending Azure credentials to Coiled... ", end="")
|
|
418
|
+
submit_azure_credentials(
|
|
419
|
+
coiled_account=coiled_account,
|
|
420
|
+
sub_id=sub_id,
|
|
421
|
+
rg_name=rg_name,
|
|
422
|
+
region=region,
|
|
423
|
+
creds_to_submit=creds_to_submit,
|
|
424
|
+
)
|
|
425
|
+
print(f"Azure credentials have been updated for {coiled_account} using Azure app {app_id} and {rg_name}!")
|
|
426
|
+
|
|
427
|
+
coiled.add_interaction(action="RefreshCloudCredentials", success=True)
|
|
428
|
+
|
|
429
|
+
|
|
381
430
|
def submit_azure_credentials(coiled_account, sub_id, rg_name, region, creds_to_submit, check_after: bool = False):
|
|
382
431
|
with coiled.Cloud(account=coiled_account) as cloud:
|
|
383
432
|
setup_endpoint = f"/api/v2/cloud-credentials/{coiled_account}/azure"
|
coiled/context.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import functools
|
|
4
|
+
import inspect
|
|
5
5
|
import random
|
|
6
6
|
import string
|
|
7
7
|
from contextlib import contextmanager, nullcontext
|
|
@@ -103,7 +103,7 @@ def get_trace_context(func: Union[SyncFuncType, AsyncFuncType]):
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
def track_context(func: F) -> F:
|
|
106
|
-
if
|
|
106
|
+
if inspect.iscoroutinefunction(func):
|
|
107
107
|
|
|
108
108
|
@functools.wraps(func)
|
|
109
109
|
async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
|
coiled/credentials/google.py
CHANGED
|
@@ -4,8 +4,6 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
from typing import Tuple, cast
|
|
6
6
|
|
|
7
|
-
from distributed import Client
|
|
8
|
-
|
|
9
7
|
from coiled.utils import supress_logs
|
|
10
8
|
|
|
11
9
|
|
|
@@ -128,25 +126,8 @@ def get_long_lived_adc_to_forward(scopes=None):
|
|
|
128
126
|
def send_application_default_credentials(cluster, scopes=None, return_creds: bool = False):
|
|
129
127
|
to_forward = get_long_lived_adc_to_forward(scopes)
|
|
130
128
|
|
|
131
|
-
def write_adf_files():
|
|
132
|
-
for path, content in to_forward["files"].items():
|
|
133
|
-
abs_path = os.path.expanduser(path)
|
|
134
|
-
os.makedirs(os.path.dirname(abs_path), exist_ok=True)
|
|
135
|
-
with open(abs_path, "w") as f:
|
|
136
|
-
f.write(content)
|
|
137
|
-
|
|
138
|
-
os.makedirs(os.path.expanduser("~/.config"), exist_ok=True)
|
|
139
|
-
container_adc_dir = os.path.expanduser("~/.config/gcloud")
|
|
140
|
-
|
|
141
|
-
# files are written to /gcloud-config, which is then symlinked to ~/.config/gcloud
|
|
142
|
-
if not os.path.exists(container_adc_dir):
|
|
143
|
-
os.symlink("/gcloud-config", container_adc_dir, target_is_directory=True)
|
|
144
|
-
# otherwise, maybe we should copy the files to both places?
|
|
145
|
-
|
|
146
129
|
cluster.send_private_envs(to_forward["env"])
|
|
147
|
-
|
|
148
|
-
client.run_on_scheduler(write_adf_files)
|
|
149
|
-
client.run(write_adf_files)
|
|
130
|
+
cluster.write_files_for_dask(files=to_forward["files"], symlink_dirs={"/gcloud-config": "~/.config/gcloud"})
|
|
150
131
|
|
|
151
132
|
cluster._queue_cluster_event("credentials", "Google Application Default Credentials forwarded")
|
|
152
133
|
print(
|