Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FluidStack] Add FluidStack Integration (Provisioner Interface) #3086

Merged
merged 8 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sky/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def get_git_commit():
OCI = clouds.OCI
RunPod = clouds.RunPod
Vsphere = clouds.Vsphere
Fluidstack = clouds.Fluidstack
optimize = Optimizer.optimize

__all__ = [
Expand All @@ -99,6 +100,7 @@ def get_git_commit():
'RunPod',
'SCP',
'Vsphere',
'Fluidstack',
'Optimizer',
'OptimizeTarget',
'backends',
Expand Down
29 changes: 29 additions & 0 deletions sky/adaptors/fluidstack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""fluidstack cloud adaptor."""

import functools

_fluidstack_sdk = None


def import_package(func):

@functools.wraps(func)
def wrapper(*args, **kwargs):
global _fluidstack_sdk
if _fluidstack_sdk is None:
try:
import fluidstack as _fluidstack # pylint: disable=import-outside-toplevel
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update the setup.py to have an extra for fluidstack that installs the dependency.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, actually, it seems we are not using fluidstack package anywhere. Should we leave this file out?

_fluidstack_sdk = _fluidstack
except ImportError:
raise ImportError(
'Fail to import dependencies for fluidstack.'
'Try pip install "skypilot[fluidstack]"') from None
return func(*args, **kwargs)

return wrapper


@import_package
def fluidstack():
"""Return the fluidstack package."""
return _fluidstack_sdk
15 changes: 15 additions & 0 deletions sky/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from sky.adaptors import ibm
from sky.adaptors import runpod
from sky.clouds.utils import lambda_utils
from sky.skylet.providers.fluidstack import fluidstack_utils
from sky.provision.kubernetes import utils as kubernetes_utils
from sky.utils import common_utils
from sky.utils import kubernetes_enums
Expand Down Expand Up @@ -464,3 +465,17 @@ def setup_runpod_authentication(config: Dict[str, Any]) -> Dict[str, Any]:
runpod.runpod().cli.groups.ssh.functions.add_ssh_key(public_key)

return configure_ssh_info(config)


def setup_fluidstack_authentication(config: Dict[str, Any]) -> Dict[str, Any]:

get_or_generate_keys()

client = fluidstack_utils.FluidstackClient()
public_key_path = os.path.expanduser(PUBLIC_SSH_KEY_PATH)
public_key = None
with open(public_key_path, 'r') as f:
public_key = f.read()
client.get_or_add_ssh_key(public_key)
config['auth']['ssh_public_key'] = PUBLIC_SSH_KEY_PATH
return configure_ssh_info(config)
9 changes: 9 additions & 0 deletions sky/backends/backend_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,10 @@ def write_cluster_config(
if isinstance(cloud, clouds.GCP):
gcp_project_id = cloud.get_project_id(dryrun=dryrun)

fluidstack_username = 'ubuntu'
if isinstance(cloud, clouds.Fluidstack):
fluidstack_username = cloud.default_username(to_provision.region)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this into the cloud.make_deploy_variables called in L781, instead of having it in this function.

specific_reservations = set(
skypilot_config.get_nested(('gcp', 'specific_reservations'), set()))

Expand Down Expand Up @@ -898,6 +902,9 @@ def write_cluster_config(
'specific_reservations': filtered_specific_reservations,
'tpu_node_name': tpu_node_name,

# Fluidstack only:
'fluidstack_username': fluidstack_username,

# Conda setup
'conda_installation_commands':
constants.CONDA_INSTALLATION_COMMANDS,
Expand Down Expand Up @@ -1001,6 +1008,8 @@ def _add_auth_to_cluster_config(cloud: clouds.Cloud, cluster_config_file: str):
config = auth.setup_ibm_authentication(config)
elif isinstance(cloud, clouds.RunPod):
config = auth.setup_runpod_authentication(config)
elif isinstance(cloud, clouds.Fluidstack):
config = auth.setup_fluidstack_authentication(config)
else:
assert isinstance(cloud, clouds.Local), cloud
# Local cluster case, authentication is already filled by the user
Expand Down
33 changes: 33 additions & 0 deletions sky/backends/cloud_vm_ray_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def _get_cluster_config_template(cloud):
clouds.RunPod: 'runpod-ray.yml.j2',
clouds.Kubernetes: 'kubernetes-ray.yml.j2',
clouds.Vsphere: 'vsphere-ray.yml.j2',
clouds.Fluidstack: 'fluidstack-ray.yml.j2'
}
return cloud_to_template[type(cloud)]

Expand Down Expand Up @@ -929,6 +930,38 @@ def _oci_handler(blocked_resources: Set['resources_lib.Resources'],
blocked_resources,
launchable_resources.copy(zone=zone.name))

@staticmethod
def _fluidstack_handler(blocked_resources: Set['resources_lib.Resources'],
launchable_resources: 'resources_lib.Resources',
region: 'clouds.Region',
zones: Optional[List['clouds.Zone']], stdout: str,
stderr: str) -> None:
del zones # Unused.
style = colorama.Style
stdout_splits = stdout.split('\n')
stderr_splits = stderr.split('\n')
errors = [
s.strip()
for s in stdout_splits + stderr_splits
if 'FluidstackAPIError:' in s.strip()
]
if not errors:
logger.info('====== stdout ======')
for s in stdout_splits:
print(s)
logger.info('====== stderr ======')
for s in stderr_splits:
print(s)
with ux_utils.print_exception_no_traceback():
raise RuntimeError('Errors occurred during provision; '
'check logs above.')

logger.warning(f'Got error(s) in {region.name}:')
messages = '\n\t'.join(errors)
logger.warning(f'{style.DIM}\t{messages}{style.RESET_ALL}')
_add_to_blocked_resources(blocked_resources,
launchable_resources.copy(zone=None))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed now, as we will use the FailoverCloudErrorHandlerV2

Suggested change
@staticmethod
def _fluidstack_handler(blocked_resources: Set['resources_lib.Resources'],
launchable_resources: 'resources_lib.Resources',
region: 'clouds.Region',
zones: Optional[List['clouds.Zone']], stdout: str,
stderr: str) -> None:
del zones # Unused.
style = colorama.Style
stdout_splits = stdout.split('\n')
stderr_splits = stderr.split('\n')
errors = [
s.strip()
for s in stdout_splits + stderr_splits
if 'FluidstackAPIError:' in s.strip()
]
if not errors:
logger.info('====== stdout ======')
for s in stdout_splits:
print(s)
logger.info('====== stderr ======')
for s in stderr_splits:
print(s)
with ux_utils.print_exception_no_traceback():
raise RuntimeError('Errors occurred during provision; '
'check logs above.')
logger.warning(f'Got error(s) in {region.name}:')
messages = '\n\t'.join(errors)
logger.warning(f'{style.DIM}\t{messages}{style.RESET_ALL}')
_add_to_blocked_resources(blocked_resources,
launchable_resources.copy(zone=None))


@staticmethod
def update_blocklist_on_error(
blocked_resources: Set['resources_lib.Resources'],
Expand Down
23 changes: 5 additions & 18 deletions sky/clouds/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# isort: split
from sky.clouds.aws import AWS
from sky.clouds.azure import Azure
from sky.clouds.fluidstack import Fluidstack
from sky.clouds.gcp import GCP
from sky.clouds.ibm import IBM
from sky.clouds.kubernetes import Kubernetes
Expand All @@ -22,22 +23,8 @@
from sky.clouds.vsphere import Vsphere

__all__ = [
'IBM',
'AWS',
'Azure',
'Cloud',
'GCP',
'Lambda',
'Local',
'SCP',
'RunPod',
'OCI',
'Vsphere',
'Kubernetes',
'CloudImplementationFeatures',
'Region',
'Zone',
'CLOUD_REGISTRY',
'ProvisionerVersion',
'StatusVersion',
'IBM', 'AWS', 'Azure', 'Cloud', 'GCP', 'Lambda', 'Local', 'SCP', 'RunPod',
'OCI', 'Vsphere', 'Kubernetes', 'CloudImplementationFeatures', 'Region',
'Zone', 'CLOUD_REGISTRY', 'ProvisionerVersion', 'StatusVersion',
'Fluidstack'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: just for readability

Suggested change
'Fluidstack'
'Fluidstack',

]
Loading
Loading