apache-airflow-providers-google 10.17.0rc1__py3-none-any.whl → 10.18.0rc1__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.
Files changed (89) hide show
  1. airflow/providers/google/__init__.py +3 -3
  2. airflow/providers/google/cloud/hooks/automl.py +1 -1
  3. airflow/providers/google/cloud/hooks/bigquery.py +64 -33
  4. airflow/providers/google/cloud/hooks/cloud_composer.py +250 -2
  5. airflow/providers/google/cloud/hooks/cloud_sql.py +154 -7
  6. airflow/providers/google/cloud/hooks/cloud_storage_transfer_service.py +7 -2
  7. airflow/providers/google/cloud/hooks/compute_ssh.py +2 -1
  8. airflow/providers/google/cloud/hooks/dataflow.py +246 -32
  9. airflow/providers/google/cloud/hooks/dataplex.py +6 -2
  10. airflow/providers/google/cloud/hooks/dlp.py +14 -14
  11. airflow/providers/google/cloud/hooks/gcs.py +6 -2
  12. airflow/providers/google/cloud/hooks/gdm.py +2 -2
  13. airflow/providers/google/cloud/hooks/kubernetes_engine.py +2 -2
  14. airflow/providers/google/cloud/hooks/mlengine.py +8 -4
  15. airflow/providers/google/cloud/hooks/pubsub.py +1 -1
  16. airflow/providers/google/cloud/hooks/secret_manager.py +252 -4
  17. airflow/providers/google/cloud/hooks/vertex_ai/custom_job.py +1431 -74
  18. airflow/providers/google/cloud/links/vertex_ai.py +2 -1
  19. airflow/providers/google/cloud/log/gcs_task_handler.py +2 -1
  20. airflow/providers/google/cloud/operators/automl.py +13 -12
  21. airflow/providers/google/cloud/operators/bigquery.py +36 -22
  22. airflow/providers/google/cloud/operators/bigquery_dts.py +4 -3
  23. airflow/providers/google/cloud/operators/bigtable.py +7 -6
  24. airflow/providers/google/cloud/operators/cloud_build.py +12 -11
  25. airflow/providers/google/cloud/operators/cloud_composer.py +147 -2
  26. airflow/providers/google/cloud/operators/cloud_memorystore.py +17 -16
  27. airflow/providers/google/cloud/operators/cloud_sql.py +60 -17
  28. airflow/providers/google/cloud/operators/cloud_storage_transfer_service.py +35 -16
  29. airflow/providers/google/cloud/operators/compute.py +12 -11
  30. airflow/providers/google/cloud/operators/datacatalog.py +21 -20
  31. airflow/providers/google/cloud/operators/dataflow.py +59 -42
  32. airflow/providers/google/cloud/operators/datafusion.py +11 -10
  33. airflow/providers/google/cloud/operators/datapipeline.py +3 -2
  34. airflow/providers/google/cloud/operators/dataprep.py +5 -4
  35. airflow/providers/google/cloud/operators/dataproc.py +19 -16
  36. airflow/providers/google/cloud/operators/datastore.py +8 -7
  37. airflow/providers/google/cloud/operators/dlp.py +31 -30
  38. airflow/providers/google/cloud/operators/functions.py +4 -3
  39. airflow/providers/google/cloud/operators/gcs.py +66 -41
  40. airflow/providers/google/cloud/operators/kubernetes_engine.py +232 -12
  41. airflow/providers/google/cloud/operators/life_sciences.py +2 -1
  42. airflow/providers/google/cloud/operators/mlengine.py +11 -10
  43. airflow/providers/google/cloud/operators/pubsub.py +6 -5
  44. airflow/providers/google/cloud/operators/spanner.py +7 -6
  45. airflow/providers/google/cloud/operators/speech_to_text.py +2 -1
  46. airflow/providers/google/cloud/operators/stackdriver.py +11 -10
  47. airflow/providers/google/cloud/operators/tasks.py +14 -13
  48. airflow/providers/google/cloud/operators/text_to_speech.py +2 -1
  49. airflow/providers/google/cloud/operators/translate_speech.py +2 -1
  50. airflow/providers/google/cloud/operators/vertex_ai/custom_job.py +333 -26
  51. airflow/providers/google/cloud/operators/vertex_ai/generative_model.py +20 -12
  52. airflow/providers/google/cloud/operators/vertex_ai/pipeline_job.py +0 -1
  53. airflow/providers/google/cloud/operators/vision.py +13 -12
  54. airflow/providers/google/cloud/operators/workflows.py +10 -9
  55. airflow/providers/google/cloud/secrets/secret_manager.py +2 -1
  56. airflow/providers/google/cloud/sensors/bigquery_dts.py +2 -1
  57. airflow/providers/google/cloud/sensors/bigtable.py +2 -1
  58. airflow/providers/google/cloud/sensors/cloud_storage_transfer_service.py +2 -1
  59. airflow/providers/google/cloud/sensors/dataflow.py +239 -52
  60. airflow/providers/google/cloud/sensors/datafusion.py +2 -1
  61. airflow/providers/google/cloud/sensors/dataproc.py +3 -2
  62. airflow/providers/google/cloud/sensors/gcs.py +14 -12
  63. airflow/providers/google/cloud/sensors/tasks.py +2 -1
  64. airflow/providers/google/cloud/sensors/workflows.py +2 -1
  65. airflow/providers/google/cloud/transfers/adls_to_gcs.py +8 -2
  66. airflow/providers/google/cloud/transfers/azure_blob_to_gcs.py +7 -1
  67. airflow/providers/google/cloud/transfers/azure_fileshare_to_gcs.py +7 -1
  68. airflow/providers/google/cloud/transfers/bigquery_to_gcs.py +2 -1
  69. airflow/providers/google/cloud/transfers/bigquery_to_mssql.py +1 -1
  70. airflow/providers/google/cloud/transfers/bigquery_to_sql.py +1 -0
  71. airflow/providers/google/cloud/transfers/gcs_to_bigquery.py +5 -6
  72. airflow/providers/google/cloud/transfers/gcs_to_gcs.py +22 -12
  73. airflow/providers/google/cloud/triggers/bigquery.py +14 -3
  74. airflow/providers/google/cloud/triggers/cloud_composer.py +68 -0
  75. airflow/providers/google/cloud/triggers/cloud_sql.py +2 -1
  76. airflow/providers/google/cloud/triggers/cloud_storage_transfer_service.py +2 -1
  77. airflow/providers/google/cloud/triggers/dataflow.py +504 -4
  78. airflow/providers/google/cloud/triggers/dataproc.py +110 -26
  79. airflow/providers/google/cloud/triggers/mlengine.py +2 -1
  80. airflow/providers/google/cloud/triggers/vertex_ai.py +94 -0
  81. airflow/providers/google/common/hooks/base_google.py +45 -7
  82. airflow/providers/google/firebase/hooks/firestore.py +2 -2
  83. airflow/providers/google/firebase/operators/firestore.py +2 -1
  84. airflow/providers/google/get_provider_info.py +3 -2
  85. {apache_airflow_providers_google-10.17.0rc1.dist-info → apache_airflow_providers_google-10.18.0rc1.dist-info}/METADATA +8 -8
  86. {apache_airflow_providers_google-10.17.0rc1.dist-info → apache_airflow_providers_google-10.18.0rc1.dist-info}/RECORD +88 -89
  87. airflow/providers/google/cloud/example_dags/example_cloud_sql_query.py +0 -289
  88. {apache_airflow_providers_google-10.17.0rc1.dist-info → apache_airflow_providers_google-10.18.0rc1.dist-info}/WHEEL +0 -0
  89. {apache_airflow_providers_google-10.17.0rc1.dist-info → apache_airflow_providers_google-10.18.0rc1.dist-info}/entry_points.txt +0 -0
@@ -29,6 +29,8 @@ from typing import TYPE_CHECKING, Sequence
29
29
 
30
30
  import pendulum
31
31
 
32
+ from airflow.providers.google.common.hooks.base_google import PROVIDE_PROJECT_ID
33
+
32
34
  if TYPE_CHECKING:
33
35
  from airflow.utils.context import Context
34
36
 
@@ -119,7 +121,7 @@ class GCSCreateBucketOperator(GoogleCloudBaseOperator):
119
121
  resource: dict | None = None,
120
122
  storage_class: str = "MULTI_REGIONAL",
121
123
  location: str = "US",
122
- project_id: str | None = None,
124
+ project_id: str = PROVIDE_PROJECT_ID,
123
125
  labels: dict | None = None,
124
126
  gcp_conn_id: str = "google_cloud_default",
125
127
  impersonation_chain: str | Sequence[str] | None = None,
@@ -297,7 +299,7 @@ class GCSDeleteObjectsOperator(GoogleCloudBaseOperator):
297
299
  *,
298
300
  bucket_name: str,
299
301
  objects: list[str] | None = None,
300
- prefix: str | None = None,
302
+ prefix: str | list[str] | None = None,
301
303
  gcp_conn_id: str = "google_cloud_default",
302
304
  impersonation_chain: str | Sequence[str] | None = None,
303
305
  **kwargs,
@@ -309,12 +311,14 @@ class GCSDeleteObjectsOperator(GoogleCloudBaseOperator):
309
311
  self.impersonation_chain = impersonation_chain
310
312
 
311
313
  if objects is None and prefix is None:
312
- err_message = "(Task {task_id}) Either object or prefix should be set. Both are None.".format(
314
+ err_message = "(Task {task_id}) Either objects or prefix should be set. Both are None.".format(
313
315
  **kwargs
314
316
  )
315
317
  raise ValueError(err_message)
318
+ if objects is not None and prefix is not None:
319
+ err_message = "(Task {task_id}) Objects or prefix should be set. Both provided.".format(**kwargs)
320
+ raise ValueError(err_message)
316
321
 
317
- self._objects: list[str] = []
318
322
  super().__init__(**kwargs)
319
323
 
320
324
  def execute(self, context: Context) -> None:
@@ -324,15 +328,14 @@ class GCSDeleteObjectsOperator(GoogleCloudBaseOperator):
324
328
  )
325
329
 
326
330
  if self.objects is not None:
327
- self._objects = self.objects
331
+ objects = self.objects
328
332
  else:
329
- self._objects = hook.list(bucket_name=self.bucket_name, prefix=self.prefix)
330
- self.log.info("Deleting %s objects from %s", len(self._objects), self.bucket_name)
331
- for object_name in self._objects:
333
+ objects = hook.list(bucket_name=self.bucket_name, prefix=self.prefix)
334
+ self.log.info("Deleting %s objects from %s", len(objects), self.bucket_name)
335
+ for object_name in objects:
332
336
  hook.delete(bucket_name=self.bucket_name, object_name=object_name)
333
337
 
334
- def get_openlineage_facets_on_complete(self, task_instance):
335
- """Implement on_complete as execute() resolves object names."""
338
+ def get_openlineage_facets_on_start(self):
336
339
  from openlineage.client.facet import (
337
340
  LifecycleStateChange,
338
341
  LifecycleStateChangeDatasetFacet,
@@ -342,8 +345,17 @@ class GCSDeleteObjectsOperator(GoogleCloudBaseOperator):
342
345
 
343
346
  from airflow.providers.openlineage.extractors import OperatorLineage
344
347
 
345
- if not self._objects:
346
- return OperatorLineage()
348
+ objects = []
349
+ if self.objects is not None:
350
+ objects = self.objects
351
+ elif self.prefix is not None:
352
+ prefixes = [self.prefix] if isinstance(self.prefix, str) else self.prefix
353
+ for pref in prefixes:
354
+ # Use parent if not a file (dot not in name) and not a dir (ends with slash)
355
+ if "." not in pref.split("/")[-1] and not pref.endswith("/"):
356
+ pref = Path(pref).parent.as_posix()
357
+ pref = "/" if pref in (".", "", "/") else pref.rstrip("/")
358
+ objects.append(pref)
347
359
 
348
360
  bucket_url = f"gs://{self.bucket_name}"
349
361
  input_datasets = [
@@ -360,7 +372,7 @@ class GCSDeleteObjectsOperator(GoogleCloudBaseOperator):
360
372
  )
361
373
  },
362
374
  )
363
- for object_name in self._objects
375
+ for object_name in objects
364
376
  ]
365
377
 
366
378
  return OperatorLineage(inputs=input_datasets)
@@ -774,8 +786,8 @@ class GCSTimeSpanFileTransformOperator(GoogleCloudBaseOperator):
774
786
  self.upload_continue_on_fail = upload_continue_on_fail
775
787
  self.upload_num_attempts = upload_num_attempts
776
788
 
777
- self._source_object_names: list[str] = []
778
- self._destination_object_names: list[str] = []
789
+ self._source_prefix_interp: str | None = None
790
+ self._destination_prefix_interp: str | None = None
779
791
 
780
792
  def execute(self, context: Context) -> list[str]:
781
793
  # Define intervals and prefixes.
@@ -803,11 +815,11 @@ class GCSTimeSpanFileTransformOperator(GoogleCloudBaseOperator):
803
815
  timespan_start = timespan_start.in_timezone(timezone.utc)
804
816
  timespan_end = timespan_end.in_timezone(timezone.utc)
805
817
 
806
- source_prefix_interp = GCSTimeSpanFileTransformOperator.interpolate_prefix(
818
+ self._source_prefix_interp = GCSTimeSpanFileTransformOperator.interpolate_prefix(
807
819
  self.source_prefix,
808
820
  timespan_start,
809
821
  )
810
- destination_prefix_interp = GCSTimeSpanFileTransformOperator.interpolate_prefix(
822
+ self._destination_prefix_interp = GCSTimeSpanFileTransformOperator.interpolate_prefix(
811
823
  self.destination_prefix,
812
824
  timespan_start,
813
825
  )
@@ -828,9 +840,9 @@ class GCSTimeSpanFileTransformOperator(GoogleCloudBaseOperator):
828
840
  )
829
841
 
830
842
  # Fetch list of files.
831
- self._source_object_names = source_hook.list_by_timespan(
843
+ blobs_to_transform = source_hook.list_by_timespan(
832
844
  bucket_name=self.source_bucket,
833
- prefix=source_prefix_interp,
845
+ prefix=self._source_prefix_interp,
834
846
  timespan_start=timespan_start,
835
847
  timespan_end=timespan_end,
836
848
  )
@@ -840,7 +852,7 @@ class GCSTimeSpanFileTransformOperator(GoogleCloudBaseOperator):
840
852
  temp_output_dir_path = Path(temp_output_dir)
841
853
 
842
854
  # TODO: download in parallel.
843
- for blob_to_transform in self._source_object_names:
855
+ for blob_to_transform in blobs_to_transform:
844
856
  destination_file = temp_input_dir_path / blob_to_transform
845
857
  destination_file.parent.mkdir(parents=True, exist_ok=True)
846
858
  try:
@@ -877,6 +889,8 @@ class GCSTimeSpanFileTransformOperator(GoogleCloudBaseOperator):
877
889
 
878
890
  self.log.info("Transformation succeeded. Output temporarily located at %s", temp_output_dir_path)
879
891
 
892
+ files_uploaded = []
893
+
880
894
  # TODO: upload in parallel.
881
895
  for upload_file in temp_output_dir_path.glob("**/*"):
882
896
  if upload_file.is_dir():
@@ -884,8 +898,8 @@ class GCSTimeSpanFileTransformOperator(GoogleCloudBaseOperator):
884
898
 
885
899
  upload_file_name = str(upload_file.relative_to(temp_output_dir_path))
886
900
 
887
- if self.destination_prefix is not None:
888
- upload_file_name = f"{destination_prefix_interp}/{upload_file_name}"
901
+ if self._destination_prefix_interp is not None:
902
+ upload_file_name = f"{self._destination_prefix_interp.rstrip('/')}/{upload_file_name}"
889
903
 
890
904
  self.log.info("Uploading file %s to %s", upload_file, upload_file_name)
891
905
 
@@ -897,35 +911,46 @@ class GCSTimeSpanFileTransformOperator(GoogleCloudBaseOperator):
897
911
  chunk_size=self.chunk_size,
898
912
  num_max_attempts=self.upload_num_attempts,
899
913
  )
900
- self._destination_object_names.append(str(upload_file_name))
914
+ files_uploaded.append(str(upload_file_name))
901
915
  except GoogleCloudError:
902
916
  if not self.upload_continue_on_fail:
903
917
  raise
904
918
 
905
- return self._destination_object_names
919
+ return files_uploaded
906
920
 
907
921
  def get_openlineage_facets_on_complete(self, task_instance):
908
- """Implement on_complete as execute() resolves object names."""
922
+ """Implement on_complete as execute() resolves object prefixes."""
909
923
  from openlineage.client.run import Dataset
910
924
 
911
925
  from airflow.providers.openlineage.extractors import OperatorLineage
912
926
 
913
- input_datasets = [
914
- Dataset(
915
- namespace=f"gs://{self.source_bucket}",
916
- name=object_name,
917
- )
918
- for object_name in self._source_object_names
919
- ]
920
- output_datasets = [
921
- Dataset(
922
- namespace=f"gs://{self.destination_bucket}",
923
- name=object_name,
924
- )
925
- for object_name in self._destination_object_names
926
- ]
927
-
928
- return OperatorLineage(inputs=input_datasets, outputs=output_datasets)
927
+ def _parse_prefix(pref):
928
+ # Use parent if not a file (dot not in name) and not a dir (ends with slash)
929
+ if "." not in pref.split("/")[-1] and not pref.endswith("/"):
930
+ pref = Path(pref).parent.as_posix()
931
+ return "/" if pref in (".", "/", "") else pref.rstrip("/")
932
+
933
+ input_prefix, output_prefix = "/", "/"
934
+ if self._source_prefix_interp is not None:
935
+ input_prefix = _parse_prefix(self._source_prefix_interp)
936
+
937
+ if self._destination_prefix_interp is not None:
938
+ output_prefix = _parse_prefix(self._destination_prefix_interp)
939
+
940
+ return OperatorLineage(
941
+ inputs=[
942
+ Dataset(
943
+ namespace=f"gs://{self.source_bucket}",
944
+ name=input_prefix,
945
+ )
946
+ ],
947
+ outputs=[
948
+ Dataset(
949
+ namespace=f"gs://{self.destination_bucket}",
950
+ name=output_prefix,
951
+ )
952
+ ],
953
+ )
929
954
 
930
955
 
931
956
  class GCSDeleteBucketOperator(GoogleCloudBaseOperator):
@@ -29,13 +29,13 @@ import yaml
29
29
  from deprecated import deprecated
30
30
  from google.api_core.exceptions import AlreadyExists
31
31
  from google.cloud.container_v1.types import Cluster
32
- from kubernetes.client import V1JobList
32
+ from kubernetes.client import V1JobList, models as k8s
33
33
  from kubernetes.utils.create_from_yaml import FailToCreateError
34
34
  from packaging.version import parse as parse_version
35
35
 
36
36
  from airflow.configuration import conf
37
37
  from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
38
- from airflow.providers.cncf.kubernetes.operators.job import KubernetesDeleteJobOperator, KubernetesJobOperator
38
+ from airflow.providers.cncf.kubernetes.operators.job import KubernetesJobOperator
39
39
  from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
40
40
  from airflow.providers.cncf.kubernetes.operators.resource import (
41
41
  KubernetesCreateResourceOperator,
@@ -47,6 +47,7 @@ from airflow.providers.google.cloud.hooks.kubernetes_engine import (
47
47
  GKEDeploymentHook,
48
48
  GKEHook,
49
49
  GKEJobHook,
50
+ GKEKubernetesHook,
50
51
  GKEPodHook,
51
52
  )
52
53
  from airflow.providers.google.cloud.links.kubernetes_engine import (
@@ -61,9 +62,20 @@ from airflow.providers.google.cloud.triggers.kubernetes_engine import (
61
62
  GKEOperationTrigger,
62
63
  GKEStartPodTrigger,
63
64
  )
65
+ from airflow.providers.google.common.hooks.base_google import PROVIDE_PROJECT_ID
64
66
  from airflow.providers_manager import ProvidersManager
65
67
  from airflow.utils.timezone import utcnow
66
68
 
69
+ try:
70
+ from airflow.providers.cncf.kubernetes.operators.job import KubernetesDeleteJobOperator
71
+ except ImportError:
72
+ from airflow.exceptions import AirflowOptionalProviderFeatureException
73
+
74
+ raise AirflowOptionalProviderFeatureException(
75
+ "Failed to import KubernetesDeleteJobOperator. This operator is only available in cncf-kubernetes "
76
+ "provider version >=8.1.0"
77
+ )
78
+
67
79
  if TYPE_CHECKING:
68
80
  from kubernetes.client.models import V1Job, V1Pod
69
81
 
@@ -166,7 +178,7 @@ class GKEDeleteClusterOperator(GoogleCloudBaseOperator):
166
178
  *,
167
179
  name: str,
168
180
  location: str,
169
- project_id: str | None = None,
181
+ project_id: str = PROVIDE_PROJECT_ID,
170
182
  gcp_conn_id: str = "google_cloud_default",
171
183
  api_version: str = "v2",
172
184
  impersonation_chain: str | Sequence[str] | None = None,
@@ -310,7 +322,7 @@ class GKECreateClusterOperator(GoogleCloudBaseOperator):
310
322
  *,
311
323
  location: str,
312
324
  body: dict | Cluster,
313
- project_id: str | None = None,
325
+ project_id: str = PROVIDE_PROJECT_ID,
314
326
  gcp_conn_id: str = "google_cloud_default",
315
327
  api_version: str = "v2",
316
328
  impersonation_chain: str | Sequence[str] | None = None,
@@ -496,7 +508,7 @@ class GKEStartKueueInsideClusterOperator(GoogleCloudBaseOperator):
496
508
  cluster_name: str,
497
509
  kueue_version: str,
498
510
  use_internal_ip: bool = False,
499
- project_id: str | None = None,
511
+ project_id: str = PROVIDE_PROJECT_ID,
500
512
  gcp_conn_id: str = "google_cloud_default",
501
513
  impersonation_chain: str | Sequence[str] | None = None,
502
514
  **kwargs,
@@ -658,7 +670,7 @@ class GKEStartPodOperator(KubernetesPodOperator):
658
670
  location: str,
659
671
  cluster_name: str,
660
672
  use_internal_ip: bool = False,
661
- project_id: str | None = None,
673
+ project_id: str = PROVIDE_PROJECT_ID,
662
674
  gcp_conn_id: str = "google_cloud_default",
663
675
  impersonation_chain: str | Sequence[str] | None = None,
664
676
  regional: bool | None = None,
@@ -856,7 +868,7 @@ class GKEStartJobOperator(KubernetesJobOperator):
856
868
  location: str,
857
869
  cluster_name: str,
858
870
  use_internal_ip: bool = False,
859
- project_id: str | None = None,
871
+ project_id: str = PROVIDE_PROJECT_ID,
860
872
  gcp_conn_id: str = "google_cloud_default",
861
873
  impersonation_chain: str | Sequence[str] | None = None,
862
874
  deferrable: bool = conf.getboolean("operators", "default_deferrable", fallback=False),
@@ -993,7 +1005,7 @@ class GKEDescribeJobOperator(GoogleCloudBaseOperator):
993
1005
  location: str,
994
1006
  namespace: str,
995
1007
  cluster_name: str,
996
- project_id: str | None = None,
1008
+ project_id: str = PROVIDE_PROJECT_ID,
997
1009
  use_internal_ip: bool = False,
998
1010
  gcp_conn_id: str = "google_cloud_default",
999
1011
  impersonation_chain: str | Sequence[str] | None = None,
@@ -1094,7 +1106,7 @@ class GKEListJobsOperator(GoogleCloudBaseOperator):
1094
1106
  location: str,
1095
1107
  cluster_name: str,
1096
1108
  namespace: str | None = None,
1097
- project_id: str | None = None,
1109
+ project_id: str = PROVIDE_PROJECT_ID,
1098
1110
  use_internal_ip: bool = False,
1099
1111
  do_xcom_push: bool = True,
1100
1112
  gcp_conn_id: str = "google_cloud_default",
@@ -1194,7 +1206,7 @@ class GKECreateCustomResourceOperator(KubernetesCreateResourceOperator):
1194
1206
  location: str,
1195
1207
  cluster_name: str,
1196
1208
  use_internal_ip: bool = False,
1197
- project_id: str | None = None,
1209
+ project_id: str = PROVIDE_PROJECT_ID,
1198
1210
  gcp_conn_id: str = "google_cloud_default",
1199
1211
  impersonation_chain: str | Sequence[str] | None = None,
1200
1212
  **kwargs,
@@ -1296,7 +1308,7 @@ class GKEDeleteCustomResourceOperator(KubernetesDeleteResourceOperator):
1296
1308
  location: str,
1297
1309
  cluster_name: str,
1298
1310
  use_internal_ip: bool = False,
1299
- project_id: str | None = None,
1311
+ project_id: str = PROVIDE_PROJECT_ID,
1300
1312
  gcp_conn_id: str = "google_cloud_default",
1301
1313
  impersonation_chain: str | Sequence[str] | None = None,
1302
1314
  **kwargs,
@@ -1435,7 +1447,7 @@ class GKEDeleteJobOperator(KubernetesDeleteJobOperator):
1435
1447
  location: str,
1436
1448
  cluster_name: str,
1437
1449
  use_internal_ip: bool = False,
1438
- project_id: str | None = None,
1450
+ project_id: str = PROVIDE_PROJECT_ID,
1439
1451
  gcp_conn_id: str = "google_cloud_default",
1440
1452
  impersonation_chain: str | Sequence[str] | None = None,
1441
1453
  **kwargs,
@@ -1494,3 +1506,211 @@ class GKEDeleteJobOperator(KubernetesDeleteJobOperator):
1494
1506
  ).fetch_cluster_info()
1495
1507
 
1496
1508
  return super().execute(context)
1509
+
1510
+
1511
+ class GKESuspendJobOperator(GoogleCloudBaseOperator):
1512
+ """
1513
+ Suspend Job by given name.
1514
+
1515
+ .. seealso::
1516
+ For more information on how to use this operator, take a look at the guide:
1517
+ :ref:`howto/operator:GKESuspendJobOperator`
1518
+
1519
+ :param name: The name of the Job to suspend
1520
+ :param project_id: The Google Developers Console project id.
1521
+ :param location: The name of the Google Kubernetes Engine zone or region in which the cluster
1522
+ resides.
1523
+ :param cluster_name: The name of the Google Kubernetes Engine cluster.
1524
+ :param namespace: The name of the Google Kubernetes Engine namespace.
1525
+ :param use_internal_ip: Use the internal IP address as the endpoint.
1526
+ :param gcp_conn_id: The connection ID to use connecting to Google Cloud.
1527
+ :param impersonation_chain: Optional service account to impersonate using short-term
1528
+ credentials, or chained list of accounts required to get the access_token
1529
+ of the last account in the list, which will be impersonated in the request.
1530
+ If set as a string, the account must grant the originating account
1531
+ the Service Account Token Creator IAM role.
1532
+ If set as a sequence, the identities from the list must grant
1533
+ Service Account Token Creator IAM role to the directly preceding identity, with first
1534
+ account from the list granting this role to the originating account (templated).
1535
+ """
1536
+
1537
+ template_fields: Sequence[str] = (
1538
+ "project_id",
1539
+ "gcp_conn_id",
1540
+ "name",
1541
+ "namespace",
1542
+ "cluster_name",
1543
+ "location",
1544
+ "impersonation_chain",
1545
+ )
1546
+ operator_extra_links = (KubernetesEngineJobLink(),)
1547
+
1548
+ def __init__(
1549
+ self,
1550
+ *,
1551
+ name: str,
1552
+ location: str,
1553
+ namespace: str,
1554
+ cluster_name: str,
1555
+ project_id: str = PROVIDE_PROJECT_ID,
1556
+ use_internal_ip: bool = False,
1557
+ gcp_conn_id: str = "google_cloud_default",
1558
+ impersonation_chain: str | Sequence[str] | None = None,
1559
+ **kwargs,
1560
+ ) -> None:
1561
+ super().__init__(**kwargs)
1562
+
1563
+ self.project_id = project_id
1564
+ self.gcp_conn_id = gcp_conn_id
1565
+ self.location = location
1566
+ self.name = name
1567
+ self.namespace = namespace
1568
+ self.cluster_name = cluster_name
1569
+ self.use_internal_ip = use_internal_ip
1570
+ self.impersonation_chain = impersonation_chain
1571
+
1572
+ self.job: V1Job | None = None
1573
+ self._ssl_ca_cert: str
1574
+ self._cluster_url: str
1575
+
1576
+ @cached_property
1577
+ def cluster_hook(self) -> GKEHook:
1578
+ return GKEHook(
1579
+ gcp_conn_id=self.gcp_conn_id,
1580
+ location=self.location,
1581
+ impersonation_chain=self.impersonation_chain,
1582
+ )
1583
+
1584
+ @cached_property
1585
+ def hook(self) -> GKEKubernetesHook:
1586
+ self._cluster_url, self._ssl_ca_cert = GKEClusterAuthDetails(
1587
+ cluster_name=self.cluster_name,
1588
+ project_id=self.project_id,
1589
+ use_internal_ip=self.use_internal_ip,
1590
+ cluster_hook=self.cluster_hook,
1591
+ ).fetch_cluster_info()
1592
+
1593
+ return GKEKubernetesHook(
1594
+ gcp_conn_id=self.gcp_conn_id,
1595
+ cluster_url=self._cluster_url,
1596
+ ssl_ca_cert=self._ssl_ca_cert,
1597
+ )
1598
+
1599
+ def execute(self, context: Context) -> None:
1600
+ self.job = self.hook.patch_namespaced_job(
1601
+ job_name=self.name,
1602
+ namespace=self.namespace,
1603
+ body={"spec": {"suspend": True}},
1604
+ )
1605
+ self.log.info(
1606
+ "Job %s from cluster %s was suspended.",
1607
+ self.name,
1608
+ self.cluster_name,
1609
+ )
1610
+ KubernetesEngineJobLink.persist(context=context, task_instance=self)
1611
+
1612
+ return k8s.V1Job.to_dict(self.job)
1613
+
1614
+
1615
+ class GKEResumeJobOperator(GoogleCloudBaseOperator):
1616
+ """
1617
+ Resume Job by given name.
1618
+
1619
+ .. seealso::
1620
+ For more information on how to use this operator, take a look at the guide:
1621
+ :ref:`howto/operator:GKEResumeJobOperator`
1622
+
1623
+ :param name: The name of the Job to resume
1624
+ :param project_id: The Google Developers Console project id.
1625
+ :param location: The name of the Google Kubernetes Engine zone or region in which the cluster
1626
+ resides.
1627
+ :param cluster_name: The name of the Google Kubernetes Engine cluster.
1628
+ :param namespace: The name of the Google Kubernetes Engine namespace.
1629
+ :param use_internal_ip: Use the internal IP address as the endpoint.
1630
+ :param gcp_conn_id: The connection ID to use connecting to Google Cloud.
1631
+ :param impersonation_chain: Optional service account to impersonate using short-term
1632
+ credentials, or chained list of accounts required to get the access_token
1633
+ of the last account in the list, which will be impersonated in the request.
1634
+ If set as a string, the account must grant the originating account
1635
+ the Service Account Token Creator IAM role.
1636
+ If set as a sequence, the identities from the list must grant
1637
+ Service Account Token Creator IAM role to the directly preceding identity, with first
1638
+ account from the list granting this role to the originating account (templated).
1639
+ """
1640
+
1641
+ template_fields: Sequence[str] = (
1642
+ "project_id",
1643
+ "gcp_conn_id",
1644
+ "name",
1645
+ "namespace",
1646
+ "cluster_name",
1647
+ "location",
1648
+ "impersonation_chain",
1649
+ )
1650
+ operator_extra_links = (KubernetesEngineJobLink(),)
1651
+
1652
+ def __init__(
1653
+ self,
1654
+ *,
1655
+ name: str,
1656
+ location: str,
1657
+ namespace: str,
1658
+ cluster_name: str,
1659
+ project_id: str = PROVIDE_PROJECT_ID,
1660
+ use_internal_ip: bool = False,
1661
+ gcp_conn_id: str = "google_cloud_default",
1662
+ impersonation_chain: str | Sequence[str] | None = None,
1663
+ **kwargs,
1664
+ ) -> None:
1665
+ super().__init__(**kwargs)
1666
+
1667
+ self.project_id = project_id
1668
+ self.gcp_conn_id = gcp_conn_id
1669
+ self.location = location
1670
+ self.name = name
1671
+ self.namespace = namespace
1672
+ self.cluster_name = cluster_name
1673
+ self.use_internal_ip = use_internal_ip
1674
+ self.impersonation_chain = impersonation_chain
1675
+
1676
+ self.job: V1Job | None = None
1677
+ self._ssl_ca_cert: str
1678
+ self._cluster_url: str
1679
+
1680
+ @cached_property
1681
+ def cluster_hook(self) -> GKEHook:
1682
+ return GKEHook(
1683
+ gcp_conn_id=self.gcp_conn_id,
1684
+ location=self.location,
1685
+ impersonation_chain=self.impersonation_chain,
1686
+ )
1687
+
1688
+ @cached_property
1689
+ def hook(self) -> GKEKubernetesHook:
1690
+ self._cluster_url, self._ssl_ca_cert = GKEClusterAuthDetails(
1691
+ cluster_name=self.cluster_name,
1692
+ project_id=self.project_id,
1693
+ use_internal_ip=self.use_internal_ip,
1694
+ cluster_hook=self.cluster_hook,
1695
+ ).fetch_cluster_info()
1696
+
1697
+ return GKEKubernetesHook(
1698
+ gcp_conn_id=self.gcp_conn_id,
1699
+ cluster_url=self._cluster_url,
1700
+ ssl_ca_cert=self._ssl_ca_cert,
1701
+ )
1702
+
1703
+ def execute(self, context: Context) -> None:
1704
+ self.job = self.hook.patch_namespaced_job(
1705
+ job_name=self.name,
1706
+ namespace=self.namespace,
1707
+ body={"spec": {"suspend": False}},
1708
+ )
1709
+ self.log.info(
1710
+ "Job %s from cluster %s was resumed.",
1711
+ self.name,
1712
+ self.cluster_name,
1713
+ )
1714
+ KubernetesEngineJobLink.persist(context=context, task_instance=self)
1715
+
1716
+ return k8s.V1Job.to_dict(self.job)
@@ -27,6 +27,7 @@ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarni
27
27
  from airflow.providers.google.cloud.hooks.life_sciences import LifeSciencesHook
28
28
  from airflow.providers.google.cloud.links.life_sciences import LifeSciencesLink
29
29
  from airflow.providers.google.cloud.operators.cloud_base import GoogleCloudBaseOperator
30
+ from airflow.providers.google.common.hooks.base_google import PROVIDE_PROJECT_ID
30
31
 
31
32
  if TYPE_CHECKING:
32
33
  from airflow.utils.context import Context
@@ -82,7 +83,7 @@ class LifeSciencesRunPipelineOperator(GoogleCloudBaseOperator):
82
83
  *,
83
84
  body: dict,
84
85
  location: str,
85
- project_id: str | None = None,
86
+ project_id: str = PROVIDE_PROJECT_ID,
86
87
  gcp_conn_id: str = "google_cloud_default",
87
88
  api_version: str = "v2beta",
88
89
  impersonation_chain: str | Sequence[str] | None = None,
@@ -39,6 +39,7 @@ from airflow.providers.google.cloud.links.mlengine import (
39
39
  )
40
40
  from airflow.providers.google.cloud.operators.cloud_base import GoogleCloudBaseOperator
41
41
  from airflow.providers.google.cloud.triggers.mlengine import MLEngineStartTrainingJobTrigger
42
+ from airflow.providers.google.common.hooks.base_google import PROVIDE_PROJECT_ID
42
43
 
43
44
  if TYPE_CHECKING:
44
45
  from airflow.utils.context import Context
@@ -345,7 +346,7 @@ class MLEngineManageModelOperator(GoogleCloudBaseOperator):
345
346
  *,
346
347
  model: dict,
347
348
  operation: str = "create",
348
- project_id: str | None = None,
349
+ project_id: str = PROVIDE_PROJECT_ID,
349
350
  gcp_conn_id: str = "google_cloud_default",
350
351
  impersonation_chain: str | Sequence[str] | None = None,
351
352
  **kwargs,
@@ -417,7 +418,7 @@ class MLEngineCreateModelOperator(GoogleCloudBaseOperator):
417
418
  self,
418
419
  *,
419
420
  model: dict,
420
- project_id: str | None = None,
421
+ project_id: str = PROVIDE_PROJECT_ID,
421
422
  gcp_conn_id: str = "google_cloud_default",
422
423
  impersonation_chain: str | Sequence[str] | None = None,
423
424
  **kwargs,
@@ -493,7 +494,7 @@ class MLEngineGetModelOperator(GoogleCloudBaseOperator):
493
494
  self,
494
495
  *,
495
496
  model_name: str,
496
- project_id: str | None = None,
497
+ project_id: str = PROVIDE_PROJECT_ID,
497
498
  gcp_conn_id: str = "google_cloud_default",
498
499
  impersonation_chain: str | Sequence[str] | None = None,
499
500
  **kwargs,
@@ -573,7 +574,7 @@ class MLEngineDeleteModelOperator(GoogleCloudBaseOperator):
573
574
  *,
574
575
  model_name: str,
575
576
  delete_contents: bool = False,
576
- project_id: str | None = None,
577
+ project_id: str = PROVIDE_PROJECT_ID,
577
578
  gcp_conn_id: str = "google_cloud_default",
578
579
  impersonation_chain: str | Sequence[str] | None = None,
579
580
  **kwargs,
@@ -680,7 +681,7 @@ class MLEngineManageVersionOperator(GoogleCloudBaseOperator):
680
681
  version_name: str | None = None,
681
682
  version: dict | None = None,
682
683
  operation: str = "create",
683
- project_id: str | None = None,
684
+ project_id: str = PROVIDE_PROJECT_ID,
684
685
  gcp_conn_id: str = "google_cloud_default",
685
686
  impersonation_chain: str | Sequence[str] | None = None,
686
687
  **kwargs,
@@ -773,7 +774,7 @@ class MLEngineCreateVersionOperator(GoogleCloudBaseOperator):
773
774
  *,
774
775
  model_name: str,
775
776
  version: dict,
776
- project_id: str | None = None,
777
+ project_id: str = PROVIDE_PROJECT_ID,
777
778
  gcp_conn_id: str = "google_cloud_default",
778
779
  impersonation_chain: str | Sequence[str] | None = None,
779
780
  **kwargs,
@@ -866,7 +867,7 @@ class MLEngineSetDefaultVersionOperator(GoogleCloudBaseOperator):
866
867
  *,
867
868
  model_name: str,
868
869
  version_name: str,
869
- project_id: str | None = None,
870
+ project_id: str = PROVIDE_PROJECT_ID,
870
871
  gcp_conn_id: str = "google_cloud_default",
871
872
  impersonation_chain: str | Sequence[str] | None = None,
872
873
  **kwargs,
@@ -956,7 +957,7 @@ class MLEngineListVersionsOperator(GoogleCloudBaseOperator):
956
957
  self,
957
958
  *,
958
959
  model_name: str,
959
- project_id: str | None = None,
960
+ project_id: str = PROVIDE_PROJECT_ID,
960
961
  gcp_conn_id: str = "google_cloud_default",
961
962
  impersonation_chain: str | Sequence[str] | None = None,
962
963
  **kwargs,
@@ -1045,7 +1046,7 @@ class MLEngineDeleteVersionOperator(GoogleCloudBaseOperator):
1045
1046
  *,
1046
1047
  model_name: str,
1047
1048
  version_name: str,
1048
- project_id: str | None = None,
1049
+ project_id: str = PROVIDE_PROJECT_ID,
1049
1050
  gcp_conn_id: str = "google_cloud_default",
1050
1051
  impersonation_chain: str | Sequence[str] | None = None,
1051
1052
  **kwargs,
@@ -1467,7 +1468,7 @@ class MLEngineTrainingCancelJobOperator(GoogleCloudBaseOperator):
1467
1468
  self,
1468
1469
  *,
1469
1470
  job_id: str,
1470
- project_id: str | None = None,
1471
+ project_id: str = PROVIDE_PROJECT_ID,
1471
1472
  gcp_conn_id: str = "google_cloud_default",
1472
1473
  impersonation_chain: str | Sequence[str] | None = None,
1473
1474
  **kwargs,