skypilot-nightly 1.0.0.dev20250405__py3-none-any.whl → 1.0.0.dev20250407__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.
sky/__init__.py CHANGED
@@ -5,7 +5,7 @@ from typing import Optional
5
5
  import urllib.request
6
6
 
7
7
  # Replaced with the current commit when building the wheels.
8
- _SKYPILOT_COMMIT_SHA = '5025c819176d48aa8643fe9ec058158ce2bece9a'
8
+ _SKYPILOT_COMMIT_SHA = 'c5039370280815a3f347e76622dc154ede36d6c3'
9
9
 
10
10
 
11
11
  def _get_git_commit():
@@ -35,7 +35,7 @@ def _get_git_commit():
35
35
 
36
36
 
37
37
  __commit__ = _get_git_commit()
38
- __version__ = '1.0.0.dev20250405'
38
+ __version__ = '1.0.0.dev20250407'
39
39
  __root_dir__ = os.path.dirname(os.path.abspath(__file__))
40
40
 
41
41
 
@@ -43,7 +43,6 @@ class Lambda(clouds.Cloud):
43
43
  clouds.CloudImplementationFeatures.SPOT_INSTANCE: f'Spot instances are not supported in {_REPR}.',
44
44
  clouds.CloudImplementationFeatures.IMAGE_ID: f'Specifying image ID is not supported in {_REPR}.',
45
45
  clouds.CloudImplementationFeatures.CUSTOM_DISK_TIER: f'Custom disk tiers are not supported in {_REPR}.',
46
- clouds.CloudImplementationFeatures.OPEN_PORTS: f'Opening ports is currently not supported on {_REPR}.',
47
46
  clouds.CloudImplementationFeatures.HOST_CONTROLLERS: f'Host controllers are not supported in {_REPR}.',
48
47
  }
49
48
 
@@ -1,12 +1,13 @@
1
- """Lambda instance provisioning."""
1
+ """Lambda Cloud instance provisioning."""
2
2
 
3
3
  import time
4
4
  from typing import Any, Dict, List, Optional
5
5
 
6
6
  from sky import sky_logging
7
7
  from sky.provision import common
8
- import sky.provision.lambda_cloud.lambda_utils as lambda_utils
8
+ from sky.provision.lambda_cloud import lambda_utils
9
9
  from sky.utils import common_utils
10
+ from sky.utils import resources_utils
10
11
  from sky.utils import status_lib
11
12
  from sky.utils import ux_utils
12
13
 
@@ -248,18 +249,102 @@ def query_instances(
248
249
  return statuses
249
250
 
250
251
 
251
- def open_ports(
252
- cluster_name_on_cloud: str,
253
- ports: List[str],
254
- provider_config: Optional[Dict[str, Any]] = None,
255
- ) -> None:
256
- raise NotImplementedError('open_ports is not supported for Lambda Cloud')
257
-
258
-
259
- def cleanup_ports(
260
- cluster_name_on_cloud: str,
261
- ports: List[str],
262
- provider_config: Optional[Dict[str, Any]] = None,
263
- ) -> None:
264
- """See sky/provision/__init__.py"""
252
+ def open_ports(cluster_name_on_cloud: str,
253
+ ports: List[str],
254
+ provider_config: Optional[Dict[str, Any]] = None) -> None:
255
+ """Open firewall ports for Lambda Cloud.
256
+
257
+ Args:
258
+ cluster_name_on_cloud: Cluster name on Lambda Cloud.
259
+ ports: List of ports to open.
260
+ provider_config: Lambda Cloud provider config. Contains the API key.
261
+ """
262
+ if not ports:
263
+ return
264
+
265
+ # Skip port opening for us-south-1 region where it's not supported
266
+ region = None
267
+ if provider_config is not None:
268
+ region = provider_config.get('region')
269
+
270
+ # Skip port opening for us-south-1, as it's not supported by Lambda Cloud
271
+ # https://cloud.lambda.ai/api/v1/docs#get-/api/v1/firewall-rules
272
+ if region == 'us-south-1':
273
+ logger.warning(
274
+ f'Skipping port opening for cluster {cluster_name_on_cloud} in '
275
+ f'us-south-1 region, as firewall rules are not supported there.')
276
+ return
277
+
278
+ del provider_config, cluster_name_on_cloud # No longer needed
279
+
280
+ lambda_client = lambda_utils.LambdaCloudClient()
281
+
282
+ # Get existing rules to avoid duplicates
283
+ existing_rules = lambda_client.list_firewall_rules()
284
+ existing_ports = set()
285
+ for rule in existing_rules:
286
+ port_range = rule.get('port_range')
287
+ if rule.get('protocol') == 'tcp' and port_range is not None:
288
+ if len(port_range) == 1:
289
+ # For single ports
290
+ existing_ports.add(port_range[0])
291
+ elif len(port_range) == 2:
292
+ # For port ranges, add all ports in the range
293
+ existing_ports.update(range(port_range[0], port_range[1] + 1))
294
+
295
+ # Convert port strings to a set of individual ports
296
+ ports_to_open = resources_utils.port_ranges_to_set(ports)
297
+
298
+ # Remove ports that are already open
299
+ ports_to_open = ports_to_open - existing_ports
300
+
301
+ # If no ports need to be opened, return early
302
+ if not ports_to_open:
303
+ return
304
+
305
+ # Convert individual ports to consolidated ranges
306
+ port_ranges = resources_utils.port_set_to_ranges(ports_to_open)
307
+
308
+ # Open port ranges
309
+ for port_range in port_ranges:
310
+ if '-' in port_range:
311
+ # Handle range (e.g., "1000-1010")
312
+ start, end = map(int, port_range.split('-'))
313
+ logger.debug(f'Opening port range {port_range}/tcp')
314
+ try:
315
+ lambda_client.create_firewall_rule(port_range=[start, end],
316
+ protocol='tcp')
317
+ except lambda_utils.LambdaCloudError as e:
318
+ logger.warning(f'Failed to open port range {port_range}: {e}')
319
+ else:
320
+ # Handle single port
321
+ port = int(port_range)
322
+ logger.debug(f'Opening port {port}/tcp')
323
+ try:
324
+ lambda_client.create_firewall_rule(port_range=[port, port],
325
+ protocol='tcp')
326
+ except lambda_utils.LambdaCloudError as e:
327
+ logger.warning(f'Failed to open port {port}: {e}')
328
+
329
+
330
+ def cleanup_ports(cluster_name_on_cloud: str,
331
+ ports: List[str],
332
+ provider_config: Optional[Dict[str, Any]] = None) -> None:
333
+ """Skip cleanup of firewall rules.
334
+
335
+ Lambda Cloud firewall rules are global to the account, not cluster-specific.
336
+ We skip cleanup because rules may be used by other clusters.
337
+
338
+ TODO(zhwu): the firewall rules may accumulate over time, and we may need
339
+ to add a way to automatically clean them up.
340
+
341
+ Args:
342
+ cluster_name_on_cloud: Unused.
343
+ ports: Unused.
344
+ provider_config: Unused.
345
+ """
265
346
  del cluster_name_on_cloud, ports, provider_config # Unused.
347
+
348
+ # Break the long line by splitting it
349
+ logger.info('Skipping cleanup of Lambda Cloud firewall rules '
350
+ 'as they are account-wide.')
@@ -112,6 +112,8 @@ def _try_request_with_backoff(method: str,
112
112
  response = requests.get(url, headers=headers)
113
113
  elif method == 'post':
114
114
  response = requests.post(url, headers=headers, data=data)
115
+ elif method == 'put':
116
+ response = requests.put(url, headers=headers, data=data)
115
117
  else:
116
118
  raise ValueError(f'Unsupported requests method: {method}')
117
119
  # If rate limited, wait and try again
@@ -249,4 +251,86 @@ class LambdaCloudClient:
249
251
  response = _try_request_with_backoff('get',
250
252
  f'{API_ENDPOINT}/instance-types',
251
253
  headers=self.headers)
254
+ return response.json().get('data', {})
255
+
256
+ def list_firewall_rules(self) -> List[Dict[str, Any]]:
257
+ """List firewall rules."""
258
+ response = _try_request_with_backoff('get',
259
+ f'{API_ENDPOINT}/firewall-rules',
260
+ headers=self.headers)
252
261
  return response.json().get('data', [])
262
+
263
+ def create_firewall_rule(self,
264
+ port_range: List[int],
265
+ protocol: str = 'tcp',
266
+ description: str = '') -> Dict[str, Any]:
267
+ """Create a firewall rule.
268
+
269
+ Args:
270
+ port_range: Port range as [min_port, max_port]. For a single port,
271
+ use [port, port].
272
+ protocol: Protocol ('tcp', 'udp', 'icmp', or 'all').
273
+ description: Description for the rule.
274
+
275
+ Returns:
276
+ The created firewall rule.
277
+ """
278
+ # First, get all existing rules
279
+ existing_rules = self.list_firewall_rules()
280
+
281
+ # Convert existing rules to the format expected by the API
282
+ rule_list = []
283
+ for rule in existing_rules:
284
+ if rule.get('protocol') and rule.get('source_network'):
285
+ api_rule = {
286
+ 'protocol': rule.get('protocol'),
287
+ 'source_network': rule.get('source_network'),
288
+ 'description': rule.get('description', '')
289
+ }
290
+
291
+ # Add port_range for non-icmp protocols
292
+ if rule.get('protocol') != 'icmp' and rule.get('port_range'):
293
+ api_rule['port_range'] = rule.get('port_range')
294
+
295
+ rule_list.append(api_rule)
296
+
297
+ # Add our new rule
298
+ new_rule: Dict[str, Any] = {
299
+ 'protocol': protocol,
300
+ 'source_network': '0.0.0.0/0', # Allow from any IP address
301
+ 'description': description or
302
+ ('SkyPilot auto-generated rule for port '
303
+ f'{port_range[0]}-{port_range[1]}/{protocol}')
304
+ }
305
+
306
+ # Add port_range for non-icmp protocols
307
+ if protocol != 'icmp':
308
+ new_rule['port_range'] = port_range
309
+
310
+ # Check if this rule already exists to avoid duplicates
311
+ rule_exists = False
312
+ for rule in rule_list:
313
+ if (rule.get('protocol') == protocol and
314
+ rule.get('source_network') == '0.0.0.0/0'):
315
+ if protocol != 'icmp':
316
+ if rule.get('port_range') == port_range:
317
+ rule_exists = True
318
+ break
319
+ else:
320
+ rule_exists = True
321
+ break
322
+
323
+ # Only add the rule if it doesn't already exist
324
+ if not rule_exists:
325
+ rule_list.append(new_rule)
326
+
327
+ # Create a data structure that matches the API schema
328
+ data = json.dumps({'data': rule_list})
329
+
330
+ response = _try_request_with_backoff(
331
+ 'put', # Using PUT instead of POST as per API documentation
332
+ f'{API_ENDPOINT}/firewall-rules',
333
+ data=data,
334
+ headers=self.headers,
335
+ )
336
+ return response.json().get('data', {})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skypilot-nightly
3
- Version: 1.0.0.dev20250405
3
+ Version: 1.0.0.dev20250407
4
4
  Summary: SkyPilot: An intercloud broker for the clouds
5
5
  Author: SkyPilot Team
6
6
  License: Apache 2.0
@@ -1,4 +1,4 @@
1
- sky/__init__.py,sha256=ZkLN1hea20dguJClNeFn8F6wG99qD8HssxJoWkHxbR8,6428
1
+ sky/__init__.py,sha256=-f-rcPq-1NRczFhvTmgyY0eGeL4xNdnjClhgY-sPx5I,6428
2
2
  sky/admin_policy.py,sha256=hPo02f_A32gCqhUueF0QYy1fMSSKqRwYEg_9FxScN_s,3248
3
3
  sky/authentication.py,sha256=ND011K_-Ud1dVZF37A9KrwYir_ihJXcHc7iDWmuBc8Q,22872
4
4
  sky/check.py,sha256=PPNQnaaZBA9_aogJpN4gnG4XWnTqkd74c-rBYDkDRDY,16101
@@ -56,7 +56,7 @@ sky/clouds/fluidstack.py,sha256=jIqW1MLe55MVME1PATZm8e6_FsiTnJawW7OdytPW0aM,1266
56
56
  sky/clouds/gcp.py,sha256=sUJ9LXUnMxYm6OYZ5P-z1dJHxgVILuC3OW3eFSTNCv8,56919
57
57
  sky/clouds/ibm.py,sha256=XtuPN8QgrwJdb1qb_b-7KwAE2tf_N9wh9eEfi2tcg-s,22013
58
58
  sky/clouds/kubernetes.py,sha256=SNRilcl_JYvaMDoYw0jnMtfaSi7FKQycCiJVmX6-4f4,36216
59
- sky/clouds/lambda_cloud.py,sha256=rR2YrZ6flEbKKpQAm60eKNjiMDYvH2hqzaCo3Hx4Ffw,12916
59
+ sky/clouds/lambda_cloud.py,sha256=H32vd30OfjXriH9SibVATrJZfZcCBTiWAXHfliGeMZk,12804
60
60
  sky/clouds/nebius.py,sha256=4D7C2NQYI-BNhXWNOyAXNAZj7-5nN33VQW1sxfSGt9w,14662
61
61
  sky/clouds/oci.py,sha256=YO4kjSsHBmAVH4z1TuVP72zfmC0BXte4E0xIyZir9N4,27622
62
62
  sky/clouds/paperspace.py,sha256=s_sZcGqmABEqzu0MTus8__NuHo5hIUvq2FqVZCRkTzE,11070
@@ -171,8 +171,8 @@ sky/provision/kubernetes/manifests/smarter-device-manager-configmap.yaml,sha256=
171
171
  sky/provision/kubernetes/manifests/smarter-device-manager-daemonset.yaml,sha256=RtTq4F1QUmR2Uunb6zuuRaPhV7hpesz4saHjn3Ncsb4,2010
172
172
  sky/provision/lambda_cloud/__init__.py,sha256=6EEvSgtUeEiup9ivIFevHmgv0GqleroO2X0K7TRa2nE,612
173
173
  sky/provision/lambda_cloud/config.py,sha256=jq1iLzp4Up61r4JGxvtpVbJlgXnea3LHYQhCQyyl7ik,272
174
- sky/provision/lambda_cloud/instance.py,sha256=fkIiUMt8TlEBJnyDuM02YYmTQmFDGOahiSDphY0PNw0,9279
175
- sky/provision/lambda_cloud/lambda_utils.py,sha256=hL7xLLKCNKumEYDhBZgUeAiYQzL-Oy8NUovakrETFL8,10014
174
+ sky/provision/lambda_cloud/instance.py,sha256=zenKFaAS9bAJxXM5_-Vnz7Fm7sSf3KSIlXhKUL4n6l8,12726
175
+ sky/provision/lambda_cloud/lambda_utils.py,sha256=G1dciGF8U3OGYk0-1pc3IDsT8AyA8AceujOkQy4fqho,13284
176
176
  sky/provision/nebius/__init__.py,sha256=30I3181mu0W5g9fNvaWMPoBJZoGZ9RibuTpBH9P2pDg,558
177
177
  sky/provision/nebius/config.py,sha256=LK9kTDp2w6zZrn3vNdcSGgsgS-dL_j63Nh4_u3pqNiA,321
178
178
  sky/provision/nebius/instance.py,sha256=kfpZqwh56XV7h6XsX5dGJYilsVdRhC1EUgk0flaCo_E,11889
@@ -350,9 +350,9 @@ sky/utils/kubernetes/k8s_gpu_labeler_setup.yaml,sha256=VLKT2KKimZu1GDg_4AIlIt488
350
350
  sky/utils/kubernetes/kubernetes_deploy_utils.py,sha256=HPVgNt-wbCVPd9dpDFiA7t2mzQLpjXHJ61eiwRbEr-c,10378
351
351
  sky/utils/kubernetes/rsync_helper.sh,sha256=h4YwrPFf9727CACnMJvF3EyK_0OeOYKKt4su_daKekw,1256
352
352
  sky/utils/kubernetes/ssh_jump_lifecycle_manager.py,sha256=Kq1MDygF2IxFmu9FXpCxqucXLmeUrvs6OtRij6XTQbo,6554
353
- skypilot_nightly-1.0.0.dev20250405.dist-info/licenses/LICENSE,sha256=emRJAvE7ngL6x0RhQvlns5wJzGI3NEQ_WMjNmd9TZc4,12170
354
- skypilot_nightly-1.0.0.dev20250405.dist-info/METADATA,sha256=wdtznc_1O5ewseMG9VlObIYMZmvjQO4-Cd7woHSdQV8,18552
355
- skypilot_nightly-1.0.0.dev20250405.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
356
- skypilot_nightly-1.0.0.dev20250405.dist-info/entry_points.txt,sha256=StA6HYpuHj-Y61L2Ze-hK2IcLWgLZcML5gJu8cs6nU4,36
357
- skypilot_nightly-1.0.0.dev20250405.dist-info/top_level.txt,sha256=qA8QuiNNb6Y1OF-pCUtPEr6sLEwy2xJX06Bd_CrtrHY,4
358
- skypilot_nightly-1.0.0.dev20250405.dist-info/RECORD,,
353
+ skypilot_nightly-1.0.0.dev20250407.dist-info/licenses/LICENSE,sha256=emRJAvE7ngL6x0RhQvlns5wJzGI3NEQ_WMjNmd9TZc4,12170
354
+ skypilot_nightly-1.0.0.dev20250407.dist-info/METADATA,sha256=hqvdfiv3pR-AR3iUrwYaDHD9U1Qra2EFlv8mwLdtAmk,18552
355
+ skypilot_nightly-1.0.0.dev20250407.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
356
+ skypilot_nightly-1.0.0.dev20250407.dist-info/entry_points.txt,sha256=StA6HYpuHj-Y61L2Ze-hK2IcLWgLZcML5gJu8cs6nU4,36
357
+ skypilot_nightly-1.0.0.dev20250407.dist-info/top_level.txt,sha256=qA8QuiNNb6Y1OF-pCUtPEr6sLEwy2xJX06Bd_CrtrHY,4
358
+ skypilot_nightly-1.0.0.dev20250407.dist-info/RECORD,,