11import json
22from typing import Optional , Tuple
33
4+ import google .api_core .exceptions
45import google .auth
6+ import google .cloud .compute_v1 as compute_v1
57from google .auth .credentials import Credentials
68from google .auth .exceptions import DefaultCredentialsError
7- from google .cloud import storage
89from google .oauth2 import service_account
910
1011from dstack ._internal .core .errors import BackendAuthError
1617from dstack ._internal .core .models .common import is_core_model_instance
1718
1819
19- def authenticate (creds : AnyGCPCreds ) -> Tuple [Credentials , Optional [str ]]:
20- """
21- :raises BackendAuthError:
22- :return: GCP credentials and project_id
23- """
24- credentials , project_id = get_credentials (creds )
25- validate_credentials (credentials )
20+ def authenticate (creds : AnyGCPCreds , project_id : Optional [str ] = None ) -> Tuple [Credentials , str ]:
21+ credentials , credentials_project_id = get_credentials (creds )
22+ if project_id is None :
23+ # If project_id is not specified explicitly, try using credentials' project_id.
24+ # Explicit project_id takes precedence bacause credentials' project_id may be irrelevant.
25+ # For example, with Workload Identity Federation for GKE, it's cluster project_id.
26+ project_id = credentials_project_id
27+ if project_id is None :
28+ raise BackendAuthError ("Credentials require project_id to be specified" )
29+ validate_credentials (credentials , project_id )
2630 return credentials , project_id
2731
2832
@@ -40,17 +44,19 @@ def get_credentials(creds: AnyGCPCreds) -> Tuple[Credentials, Optional[str]]:
4044 try :
4145 default_credentials , project_id = google .auth .default ()
4246 except DefaultCredentialsError :
43- raise BackendAuthError ()
47+ raise BackendAuthError ("Failed to find default credentials" )
4448
4549 return default_credentials , project_id
4650
4751
48- def validate_credentials (credentials : Credentials ):
52+ def validate_credentials (credentials : Credentials , project_id : str ):
4953 try :
50- storage_client = storage .Client (credentials = credentials )
51- storage_client .list_buckets (max_results = 1 )
54+ regions_client = compute_v1 .RegionsClient (credentials = credentials )
55+ regions_client .list (project = project_id )
56+ except google .api_core .exceptions .NotFound :
57+ raise BackendAuthError (f"project_id { project_id } not found" )
5258 except Exception :
53- raise BackendAuthError ()
59+ raise BackendAuthError ("Insufficient permissions" )
5460
5561
5662def default_creds_available () -> bool :
0 commit comments