-
Notifications
You must be signed in to change notification settings - Fork 470
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
Sky init and cloud credentials for storage #102
Changes from 42 commits
3627ba1
6bf331d
5722950
0055be5
9c0d9de
716893f
d32c8e8
cbb1ac7
0feca9c
b0c98e1
f2c28e3
20bd57f
12e7069
0d8c5f5
d40de79
e8035a3
8fe4f82
7e839b7
6d25abd
b43dfd5
d190562
7386753
f13fc34
a4630b1
122625b
6c85f87
81c9c4b
d468056
90744db
0f14428
b10dcbe
ccbad66
f124d3e
096d0c8
80546a9
a0e78f6
b7afe19
737c6f4
7f8de10
5828176
6df4934
c690de1
15dcc9b
9bbfd30
9319dd2
a687e51
5013f5b
285e59b
e46027f
88f92df
33fe806
9a1d8ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,6 @@ tabulate | |
docker | ||
|
||
awscli==1.22.17 | ||
azure-cli | ||
azure-cli==2.22.0 | ||
google-api-python-client | ||
google-cloud-storage |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ | |
import sky | ||
from sky import backends | ||
from sky import global_user_state | ||
from sky import init as sky_init | ||
from sky import task as task_lib | ||
from sky.backends import backend as backend_lib | ||
from sky.backends import backend_utils | ||
|
@@ -208,6 +209,7 @@ def _create_and_ssh_into_node( | |
dag = sky.optimize(dag) | ||
task = dag.tasks[0] | ||
backend.register_info(dag=dag) | ||
task.update_file_mounts(sky_init.get_cloud_credential_file_mounts()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe move this to task.py so it will be done independent of Sky being ran via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both yaml or python will go to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we just have it once in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Our There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand we need |
||
|
||
handle = global_user_state.get_handle_from_cluster_name(cluster_name) | ||
if handle is None or handle.head_ip is None: | ||
|
@@ -793,6 +795,17 @@ def tpunode(cluster: str, port_forward: Optional[List[int]], | |
) | ||
|
||
|
||
@cli.command() | ||
def init(): | ||
"""Determines a set of clouds that Sky will use. | ||
|
||
It checks access credentials for AWS, Azure and GCP. Sky tasks will only | ||
run in clouds that you have access to. After configuring access for a | ||
cloud, rerun `sky init` to reflect the changes. | ||
""" | ||
sky_init.init() | ||
franklsf95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def main(): | ||
return cli() | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,23 @@ | ||
"""Azure.""" | ||
import copy | ||
import json | ||
import os | ||
import subprocess | ||
from typing import Dict, Iterator, List, Optional, Tuple | ||
|
||
from sky import clouds | ||
from sky.clouds.service_catalog import azure_catalog | ||
|
||
|
||
def _run_output(cmd): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, move to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see above There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any clues on how to fix this circular import? @franklsf95 How did Ray solve this type of issue? |
||
proc = subprocess.run(cmd, | ||
shell=True, | ||
check=True, | ||
stderr=subprocess.PIPE, | ||
stdout=subprocess.PIPE) | ||
return proc.stdout.decode('ascii') | ||
|
||
|
||
class Azure(clouds.Cloud): | ||
"""Azure.""" | ||
|
||
|
@@ -141,3 +152,27 @@ def _make(instance_type): | |
if instance_type is None: | ||
return [] | ||
return _make(instance_type) | ||
|
||
def check_credentials(self) -> Tuple[bool, Optional[str]]: | ||
"""Checks if the user has access credentials to this cloud.""" | ||
# This file is required because it will be synced to remote VMs for | ||
# `az` to access private storage buckets. | ||
# `az account show` does not guarantee this file exists. | ||
if not os.path.isfile(os.path.expanduser('~/.azure/accessTokens.json')): | ||
return ( | ||
False, | ||
'~/.azure/accessTokens.json does not exist. Run `az login`.') | ||
try: | ||
output = _run_output('az account show --output=json') | ||
except subprocess.CalledProcessError: | ||
return False, 'Azure CLI returned error.' | ||
# If Azure is properly logged in, this will return something like: | ||
# {"id": ..., "user": ...} | ||
# and if not, it will return: | ||
# Please run 'az login' to setup account. | ||
if output.startswith('{'): | ||
return True, None | ||
return False, 'Azure credentials not set. Run `az login`.' | ||
franklsf95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def get_credential_file_mounts(self) -> Dict[str, str]: | ||
return {'~/.azure': '~/.azure'} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
"""Google Cloud Platform.""" | ||
import copy | ||
import json | ||
import os | ||
from typing import Dict, Iterator, List, Optional, Tuple | ||
|
||
from google import auth | ||
|
||
from sky import clouds | ||
|
||
|
||
|
@@ -285,3 +288,32 @@ def get_accelerators_from_instance_type( | |
instance_type: str, | ||
) -> Optional[Dict[str, int]]: | ||
return None | ||
|
||
def check_credentials(self) -> Tuple[bool, Optional[str]]: | ||
"""Checks if the user has access credentials to this cloud.""" | ||
try: | ||
# These files are required because they will be synced to remote | ||
# VMs for `gsutil` to access private storage buckets. | ||
# `auth.default()` does not guarantee these files exist. | ||
for file in [ | ||
'~/.config/gcloud/access_tokens.db', | ||
'~/.config/gcloud/credentials.db' | ||
]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we check the |
||
assert os.path.isfile(os.path.expanduser(file)) | ||
# Calling `auth.default()` ensures the GCP client library works, | ||
# which is used by Ray Autoscaler to launch VMs. | ||
auth.default() | ||
except (AssertionError, auth.exceptions.DefaultCredentialsError): | ||
return False, ( | ||
'GCP credentials not set. Run the following commands:\n ' | ||
# This authenticates the CLI to make `gsutil` work: | ||
'$ gcloud auth login\n ' | ||
'$ gcloud config set project <proj>\n ' | ||
# These two commands setup the client library to make | ||
# Ray Autoscaler work: | ||
'$ gcloud auth application-default login\n ' | ||
'$ gcloud auth application-default set-quota-project <proj>') | ||
return True, None | ||
|
||
def get_credential_file_mounts(self) -> Dict[str, str]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure it will sync to the remote's home directory? Had a small bug last time where Ray autoscaler made a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. About circular import: the solution would be to create another file e.g. About syncing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok sgtm, lets just leave the circular import for now and fix in a future PR. Maybe try There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me try $HOME. |
||
return {'~/.config/gcloud': '~/.config/gcloud'} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
|
||
import sky | ||
from sky import backends | ||
from sky import init | ||
from sky import logging | ||
from sky import optimizer | ||
|
||
|
@@ -86,6 +87,8 @@ def execute(dag: sky.Dag, | |
backend = backend if backend is not None else backends.CloudVmRayBackend() | ||
backend.register_info(dag=dag, optimize_target=optimize_target) | ||
|
||
task.update_file_mounts(init.get_cloud_credential_file_mounts()) | ||
michaelzhiluo marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the filemount enough? Or, should we add them to the environment as well? |
||
|
||
if task.storage_mounts is not None: | ||
# Optimizer should eventually choose where to store bucket | ||
task.add_storage_mounts() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the meaning of this sentence?