3030import warnings
3131from typing import Sequence
3232
33- from google .api_core .exceptions import AlreadyExists , NotFound
33+ from google .api_core .exceptions import NotFound
3434from google .api_core .gapic_v1 .method import DEFAULT , _MethodDefault
3535from google .api_core .retry import Retry
3636
3737# not sure why but mypy complains on missing `container_v1` but it is clearly there and is importable
3838from google .cloud import container_v1 , exceptions # type: ignore[attr-defined]
39- from google .cloud .container_v1 import ClusterManagerClient
39+ from google .cloud .container_v1 import ClusterManagerAsyncClient , ClusterManagerClient
4040from google .cloud .container_v1 .types import Cluster , Operation
4141
4242from airflow import version
4343from airflow .exceptions import AirflowException
4444from airflow .providers .google .common .consts import CLIENT_INFO
45- from airflow .providers .google .common .hooks .base_google import PROVIDE_PROJECT_ID , GoogleBaseHook
45+ from airflow .providers .google .common .hooks .base_google import (
46+ PROVIDE_PROJECT_ID ,
47+ GoogleBaseAsyncHook ,
48+ GoogleBaseHook ,
49+ )
4650
4751OPERATIONAL_POLL_INTERVAL = 15
4852
@@ -156,9 +160,10 @@ def delete_cluster(
156160 self ,
157161 name : str ,
158162 project_id : str = PROVIDE_PROJECT_ID ,
163+ wait_to_complete : bool = True ,
159164 retry : Retry | _MethodDefault = DEFAULT ,
160165 timeout : float | None = None ,
161- ) -> str | None :
166+ ) -> Operation | None :
162167 """
163168 Deletes the cluster, including the Kubernetes endpoint and all
164169 worker nodes. Firewalls and routes that were configured during
@@ -169,6 +174,8 @@ def delete_cluster(
169174
170175 :param name: The name of the cluster to delete
171176 :param project_id: Google Cloud project ID
177+ :param wait_to_complete: A boolean value which makes method to sleep while
178+ operation of deletion is not finished.
172179 :param retry: Retry object used to determine when/if to retry requests.
173180 If None is specified, requests will not be retried.
174181 :param timeout: The amount of time, in seconds, to wait for the request to
@@ -179,26 +186,28 @@ def delete_cluster(
179186 self .log .info ("Deleting (project_id=%s, location=%s, cluster_id=%s)" , project_id , self .location , name )
180187
181188 try :
182- resource = self .get_cluster_manager_client ().delete_cluster (
189+ operation = self .get_cluster_manager_client ().delete_cluster (
183190 name = f"projects/{ project_id } /locations/{ self .location } /clusters/{ name } " ,
184191 retry = retry ,
185192 timeout = timeout ,
186193 )
187- resource = self .wait_for_operation (resource , project_id )
194+ if wait_to_complete :
195+ operation = self .wait_for_operation (operation , project_id )
188196 # Returns server-defined url for the resource
189- return resource . self_link
197+ return operation
190198 except NotFound as error :
191199 self .log .info ("Assuming Success: %s" , error .message )
192200 return None
193201
194202 @GoogleBaseHook .fallback_to_default_project_id
195203 def create_cluster (
196204 self ,
197- cluster : dict | Cluster | None ,
205+ cluster : dict | Cluster ,
198206 project_id : str = PROVIDE_PROJECT_ID ,
207+ wait_to_complete : bool = True ,
199208 retry : Retry | _MethodDefault = DEFAULT ,
200209 timeout : float | None = None ,
201- ) -> str :
210+ ) -> Operation | Cluster :
202211 """
203212 Creates a cluster, consisting of the specified number and type of Google Compute
204213 Engine instances.
@@ -207,6 +216,8 @@ def create_cluster(
207216 be of the same form as the protobuf message
208217 :class:`google.cloud.container_v1.types.Cluster`
209218 :param project_id: Google Cloud project ID
219+ :param wait_to_complete: A boolean value which makes method to sleep while
220+ operation of creation is not finished.
210221 :param retry: A retry object (``google.api_core.retry.Retry``) used to
211222 retry requests.
212223 If None is specified, requests will not be retried.
@@ -231,19 +242,17 @@ def create_cluster(
231242 self .location ,
232243 cluster .name , # type: ignore
233244 )
234- try :
235- resource = self .get_cluster_manager_client ().create_cluster (
236- parent = f"projects/{ project_id } /locations/{ self .location } " ,
237- cluster = cluster , # type: ignore
238- retry = retry ,
239- timeout = timeout ,
240- )
241- resource = self .wait_for_operation (resource , project_id )
245+ operation = self .get_cluster_manager_client ().create_cluster (
246+ parent = f"projects/{ project_id } /locations/{ self .location } " ,
247+ cluster = cluster , # type: ignore
248+ retry = retry ,
249+ timeout = timeout ,
250+ )
242251
243- return resource . target_link
244- except AlreadyExists as error :
245- self . log . info ( "Assuming Success: %s" , error . message )
246- return self . get_cluster ( name = cluster . name , project_id = project_id ) # type: ignore
252+ if wait_to_complete :
253+ operation = self . wait_for_operation ( operation , project_id )
254+
255+ return operation
247256
248257 @GoogleBaseHook .fallback_to_default_project_id
249258 def get_cluster (
@@ -272,12 +281,58 @@ def get_cluster(
272281 name ,
273282 )
274283
275- return (
276- self .get_cluster_manager_client ()
277- .get_cluster (
278- name = f"projects/{ project_id } /locations/{ self .location } /clusters/{ name } " ,
279- retry = retry ,
280- timeout = timeout ,
284+ return self .get_cluster_manager_client ().get_cluster (
285+ name = f"projects/{ project_id } /locations/{ self .location } /clusters/{ name } " ,
286+ retry = retry ,
287+ timeout = timeout ,
288+ )
289+
290+
291+ class AsyncGKEHook (GoogleBaseAsyncHook ):
292+ """Hook implemented with usage of asynchronous client of GKE."""
293+
294+ sync_hook_class = GKEHook
295+
296+ def __init__ (
297+ self ,
298+ gcp_conn_id : str = "google_cloud_default" ,
299+ delegate_to : str | None = None ,
300+ location : str | None = None ,
301+ impersonation_chain : str | Sequence [str ] | None = None ,
302+ ) -> None :
303+ super ().__init__ (
304+ gcp_conn_id = gcp_conn_id ,
305+ delegate_to = delegate_to ,
306+ impersonation_chain = impersonation_chain ,
307+ )
308+ self ._client : ClusterManagerAsyncClient | None = None
309+ self .location = location
310+
311+ async def _get_client (self ) -> ClusterManagerAsyncClient :
312+ if self ._client is None :
313+ self ._client = ClusterManagerAsyncClient (
314+ credentials = (await self .get_sync_hook ()).get_credentials (),
315+ client_info = CLIENT_INFO ,
281316 )
282- .self_link
317+ return self ._client
318+
319+ @GoogleBaseHook .fallback_to_default_project_id
320+ async def get_operation (
321+ self ,
322+ operation_name : str ,
323+ project_id : str = PROVIDE_PROJECT_ID ,
324+ ) -> Operation :
325+ """
326+ Fetches the operation from Google Cloud.
327+
328+ :param operation_name: Name of operation to fetch.
329+ :param project_id: Google Cloud project ID.
330+ :return: The new, updated operation from Google Cloud.
331+ """
332+ project_id = project_id or (await self .get_sync_hook ()).project_id
333+
334+ operation_path = f"projects/{ project_id } /locations/{ self .location } /operations/{ operation_name } "
335+ client = await self ._get_client ()
336+ return await client .get_operation (
337+ name = operation_path ,
283338 )
0 commit comments