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/cli/setup/aws.py CHANGED
@@ -240,7 +240,7 @@ setup_doc = """{
240
240
  }"""
241
241
 
242
242
 
243
- def get_ongoing_doc(ecr=True, custom_bucket_prefix=None) -> str:
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 custom_bucket_prefix:
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:::{custom_bucket_prefix}-*"],
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:::{custom_bucket_prefix}-*/*"],
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(custom_bucket_prefix=custom_s3_bucket_prefix)
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
- def update_instance_profile_policy(iam, aws_account, yes):
1512
- policy_name = "CoiledInstancePolicy"
1513
- policy_doc = """{
1546
+ INSTANCE_POLICY_DOCUMENT = """{
1514
1547
  "Version": "2012-10-17",
1515
1548
  "Statement": [
1516
1549
  {
1517
- "Sid": "CoiledEC2Policy",
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(subscription, resource_group, region, account, iam_user, keep_existing_access, save_script, ship_token):
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 asyncio.iscoroutinefunction(func):
106
+ if inspect.iscoroutinefunction(func):
107
107
 
108
108
  @functools.wraps(func)
109
109
  async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
@@ -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
- with Client(cluster, name="non-user-write-gcp-adf") as client:
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(