apache-airflow-providers-google 14.0.0__py3-none-any.whl → 19.1.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.
- airflow/providers/google/3rd-party-licenses/LICENSES.txt +14 -0
- airflow/providers/google/3rd-party-licenses/NOTICE +5 -0
- airflow/providers/google/__init__.py +3 -3
- airflow/providers/google/_vendor/__init__.py +0 -0
- airflow/providers/google/_vendor/json_merge_patch.py +91 -0
- airflow/providers/google/ads/hooks/ads.py +52 -43
- airflow/providers/google/ads/operators/ads.py +2 -2
- airflow/providers/google/ads/transfers/ads_to_gcs.py +3 -19
- airflow/providers/google/assets/gcs.py +1 -11
- airflow/providers/google/cloud/_internal_client/secret_manager_client.py +3 -2
- airflow/providers/google/cloud/bundles/gcs.py +161 -0
- airflow/providers/google/cloud/hooks/alloy_db.py +2 -3
- airflow/providers/google/cloud/hooks/bigquery.py +195 -318
- airflow/providers/google/cloud/hooks/bigquery_dts.py +8 -8
- airflow/providers/google/cloud/hooks/bigtable.py +3 -2
- airflow/providers/google/cloud/hooks/cloud_batch.py +8 -9
- airflow/providers/google/cloud/hooks/cloud_build.py +6 -65
- airflow/providers/google/cloud/hooks/cloud_composer.py +292 -24
- airflow/providers/google/cloud/hooks/cloud_logging.py +109 -0
- airflow/providers/google/cloud/hooks/cloud_memorystore.py +4 -3
- airflow/providers/google/cloud/hooks/cloud_run.py +20 -11
- airflow/providers/google/cloud/hooks/cloud_sql.py +136 -64
- airflow/providers/google/cloud/hooks/cloud_storage_transfer_service.py +35 -15
- airflow/providers/google/cloud/hooks/compute.py +7 -6
- airflow/providers/google/cloud/hooks/compute_ssh.py +7 -4
- airflow/providers/google/cloud/hooks/datacatalog.py +12 -3
- airflow/providers/google/cloud/hooks/dataflow.py +87 -242
- airflow/providers/google/cloud/hooks/dataform.py +9 -14
- airflow/providers/google/cloud/hooks/datafusion.py +7 -9
- airflow/providers/google/cloud/hooks/dataplex.py +13 -12
- airflow/providers/google/cloud/hooks/dataprep.py +2 -2
- airflow/providers/google/cloud/hooks/dataproc.py +76 -74
- airflow/providers/google/cloud/hooks/dataproc_metastore.py +4 -3
- airflow/providers/google/cloud/hooks/dlp.py +5 -4
- airflow/providers/google/cloud/hooks/gcs.py +144 -33
- airflow/providers/google/cloud/hooks/gen_ai.py +196 -0
- airflow/providers/google/cloud/hooks/kms.py +3 -2
- airflow/providers/google/cloud/hooks/kubernetes_engine.py +22 -17
- airflow/providers/google/cloud/hooks/looker.py +6 -1
- airflow/providers/google/cloud/hooks/managed_kafka.py +227 -3
- airflow/providers/google/cloud/hooks/mlengine.py +7 -8
- airflow/providers/google/cloud/hooks/natural_language.py +3 -2
- airflow/providers/google/cloud/hooks/os_login.py +3 -2
- airflow/providers/google/cloud/hooks/pubsub.py +6 -6
- airflow/providers/google/cloud/hooks/secret_manager.py +105 -12
- airflow/providers/google/cloud/hooks/spanner.py +75 -10
- airflow/providers/google/cloud/hooks/speech_to_text.py +3 -2
- airflow/providers/google/cloud/hooks/stackdriver.py +18 -18
- airflow/providers/google/cloud/hooks/tasks.py +4 -3
- airflow/providers/google/cloud/hooks/text_to_speech.py +3 -2
- airflow/providers/google/cloud/hooks/translate.py +8 -17
- airflow/providers/google/cloud/hooks/vertex_ai/auto_ml.py +8 -222
- airflow/providers/google/cloud/hooks/vertex_ai/batch_prediction_job.py +9 -15
- airflow/providers/google/cloud/hooks/vertex_ai/custom_job.py +33 -283
- airflow/providers/google/cloud/hooks/vertex_ai/dataset.py +5 -12
- airflow/providers/google/cloud/hooks/vertex_ai/endpoint_service.py +6 -12
- airflow/providers/google/cloud/hooks/vertex_ai/experiment_service.py +202 -0
- airflow/providers/google/cloud/hooks/vertex_ai/feature_store.py +311 -10
- airflow/providers/google/cloud/hooks/vertex_ai/generative_model.py +79 -75
- airflow/providers/google/cloud/hooks/vertex_ai/hyperparameter_tuning_job.py +7 -13
- airflow/providers/google/cloud/hooks/vertex_ai/model_service.py +8 -12
- airflow/providers/google/cloud/hooks/vertex_ai/pipeline_job.py +6 -12
- airflow/providers/google/cloud/hooks/vertex_ai/prediction_service.py +3 -2
- airflow/providers/google/cloud/hooks/vertex_ai/ray.py +223 -0
- airflow/providers/google/cloud/hooks/video_intelligence.py +3 -2
- airflow/providers/google/cloud/hooks/vision.py +7 -7
- airflow/providers/google/cloud/hooks/workflows.py +4 -3
- airflow/providers/google/cloud/links/alloy_db.py +0 -46
- airflow/providers/google/cloud/links/base.py +77 -7
- airflow/providers/google/cloud/links/bigquery.py +0 -47
- airflow/providers/google/cloud/links/bigquery_dts.py +0 -20
- airflow/providers/google/cloud/links/bigtable.py +0 -48
- airflow/providers/google/cloud/links/cloud_build.py +0 -73
- airflow/providers/google/cloud/links/cloud_functions.py +0 -33
- airflow/providers/google/cloud/links/cloud_memorystore.py +0 -58
- airflow/providers/google/cloud/links/{life_sciences.py → cloud_run.py} +5 -27
- airflow/providers/google/cloud/links/cloud_sql.py +0 -33
- airflow/providers/google/cloud/links/cloud_storage_transfer.py +17 -46
- airflow/providers/google/cloud/links/cloud_tasks.py +7 -26
- airflow/providers/google/cloud/links/compute.py +0 -58
- airflow/providers/google/cloud/links/data_loss_prevention.py +0 -169
- airflow/providers/google/cloud/links/datacatalog.py +23 -54
- airflow/providers/google/cloud/links/dataflow.py +0 -34
- airflow/providers/google/cloud/links/dataform.py +0 -64
- airflow/providers/google/cloud/links/datafusion.py +1 -90
- airflow/providers/google/cloud/links/dataplex.py +0 -154
- airflow/providers/google/cloud/links/dataprep.py +0 -24
- airflow/providers/google/cloud/links/dataproc.py +11 -89
- airflow/providers/google/cloud/links/datastore.py +0 -31
- airflow/providers/google/cloud/links/kubernetes_engine.py +11 -61
- airflow/providers/google/cloud/links/managed_kafka.py +11 -51
- airflow/providers/google/cloud/links/mlengine.py +0 -70
- airflow/providers/google/cloud/links/pubsub.py +0 -32
- airflow/providers/google/cloud/links/spanner.py +0 -33
- airflow/providers/google/cloud/links/stackdriver.py +0 -30
- airflow/providers/google/cloud/links/translate.py +17 -187
- airflow/providers/google/cloud/links/vertex_ai.py +28 -195
- airflow/providers/google/cloud/links/workflows.py +0 -52
- airflow/providers/google/cloud/log/gcs_task_handler.py +166 -118
- airflow/providers/google/cloud/log/stackdriver_task_handler.py +14 -9
- airflow/providers/google/cloud/openlineage/CloudStorageTransferJobFacet.json +68 -0
- airflow/providers/google/cloud/openlineage/CloudStorageTransferRunFacet.json +60 -0
- airflow/providers/google/cloud/openlineage/DataFusionRunFacet.json +32 -0
- airflow/providers/google/cloud/openlineage/facets.py +141 -40
- airflow/providers/google/cloud/openlineage/mixins.py +14 -13
- airflow/providers/google/cloud/openlineage/utils.py +19 -3
- airflow/providers/google/cloud/operators/alloy_db.py +76 -61
- airflow/providers/google/cloud/operators/bigquery.py +104 -667
- airflow/providers/google/cloud/operators/bigquery_dts.py +12 -12
- airflow/providers/google/cloud/operators/bigtable.py +38 -7
- airflow/providers/google/cloud/operators/cloud_base.py +22 -1
- airflow/providers/google/cloud/operators/cloud_batch.py +18 -18
- airflow/providers/google/cloud/operators/cloud_build.py +80 -36
- airflow/providers/google/cloud/operators/cloud_composer.py +157 -71
- airflow/providers/google/cloud/operators/cloud_logging_sink.py +341 -0
- airflow/providers/google/cloud/operators/cloud_memorystore.py +74 -46
- airflow/providers/google/cloud/operators/cloud_run.py +39 -20
- airflow/providers/google/cloud/operators/cloud_sql.py +46 -61
- airflow/providers/google/cloud/operators/cloud_storage_transfer_service.py +92 -14
- airflow/providers/google/cloud/operators/compute.py +18 -50
- airflow/providers/google/cloud/operators/datacatalog.py +167 -29
- airflow/providers/google/cloud/operators/dataflow.py +38 -15
- airflow/providers/google/cloud/operators/dataform.py +19 -7
- airflow/providers/google/cloud/operators/datafusion.py +43 -43
- airflow/providers/google/cloud/operators/dataplex.py +212 -126
- airflow/providers/google/cloud/operators/dataprep.py +1 -5
- airflow/providers/google/cloud/operators/dataproc.py +134 -207
- airflow/providers/google/cloud/operators/dataproc_metastore.py +102 -84
- airflow/providers/google/cloud/operators/datastore.py +22 -6
- airflow/providers/google/cloud/operators/dlp.py +24 -45
- airflow/providers/google/cloud/operators/functions.py +21 -14
- airflow/providers/google/cloud/operators/gcs.py +15 -12
- airflow/providers/google/cloud/operators/gen_ai.py +389 -0
- airflow/providers/google/cloud/operators/kubernetes_engine.py +115 -106
- airflow/providers/google/cloud/operators/looker.py +1 -1
- airflow/providers/google/cloud/operators/managed_kafka.py +362 -40
- airflow/providers/google/cloud/operators/natural_language.py +5 -3
- airflow/providers/google/cloud/operators/pubsub.py +69 -21
- airflow/providers/google/cloud/operators/spanner.py +53 -45
- airflow/providers/google/cloud/operators/speech_to_text.py +5 -4
- airflow/providers/google/cloud/operators/stackdriver.py +5 -11
- airflow/providers/google/cloud/operators/tasks.py +6 -15
- airflow/providers/google/cloud/operators/text_to_speech.py +4 -3
- airflow/providers/google/cloud/operators/translate.py +46 -20
- airflow/providers/google/cloud/operators/translate_speech.py +4 -3
- airflow/providers/google/cloud/operators/vertex_ai/auto_ml.py +44 -34
- airflow/providers/google/cloud/operators/vertex_ai/batch_prediction_job.py +34 -12
- airflow/providers/google/cloud/operators/vertex_ai/custom_job.py +62 -53
- airflow/providers/google/cloud/operators/vertex_ai/dataset.py +75 -11
- airflow/providers/google/cloud/operators/vertex_ai/endpoint_service.py +48 -12
- airflow/providers/google/cloud/operators/vertex_ai/experiment_service.py +435 -0
- airflow/providers/google/cloud/operators/vertex_ai/feature_store.py +532 -1
- airflow/providers/google/cloud/operators/vertex_ai/generative_model.py +135 -116
- airflow/providers/google/cloud/operators/vertex_ai/hyperparameter_tuning_job.py +16 -12
- airflow/providers/google/cloud/operators/vertex_ai/model_service.py +62 -14
- airflow/providers/google/cloud/operators/vertex_ai/pipeline_job.py +35 -10
- airflow/providers/google/cloud/operators/vertex_ai/ray.py +393 -0
- airflow/providers/google/cloud/operators/video_intelligence.py +5 -3
- airflow/providers/google/cloud/operators/vision.py +7 -5
- airflow/providers/google/cloud/operators/workflows.py +24 -19
- airflow/providers/google/cloud/secrets/secret_manager.py +2 -1
- airflow/providers/google/cloud/sensors/bigquery.py +2 -2
- airflow/providers/google/cloud/sensors/bigquery_dts.py +6 -4
- airflow/providers/google/cloud/sensors/bigtable.py +14 -6
- airflow/providers/google/cloud/sensors/cloud_composer.py +535 -33
- airflow/providers/google/cloud/sensors/cloud_storage_transfer_service.py +6 -5
- airflow/providers/google/cloud/sensors/dataflow.py +27 -10
- airflow/providers/google/cloud/sensors/dataform.py +2 -2
- airflow/providers/google/cloud/sensors/datafusion.py +4 -4
- airflow/providers/google/cloud/sensors/dataplex.py +7 -5
- airflow/providers/google/cloud/sensors/dataprep.py +2 -2
- airflow/providers/google/cloud/sensors/dataproc.py +10 -9
- airflow/providers/google/cloud/sensors/dataproc_metastore.py +4 -3
- airflow/providers/google/cloud/sensors/gcs.py +22 -21
- airflow/providers/google/cloud/sensors/looker.py +5 -5
- airflow/providers/google/cloud/sensors/pubsub.py +20 -20
- airflow/providers/google/cloud/sensors/tasks.py +2 -2
- airflow/providers/google/cloud/sensors/vertex_ai/feature_store.py +2 -2
- airflow/providers/google/cloud/sensors/workflows.py +6 -4
- airflow/providers/google/cloud/transfers/adls_to_gcs.py +1 -1
- airflow/providers/google/cloud/transfers/azure_blob_to_gcs.py +2 -2
- airflow/providers/google/cloud/transfers/azure_fileshare_to_gcs.py +2 -2
- airflow/providers/google/cloud/transfers/bigquery_to_bigquery.py +11 -8
- airflow/providers/google/cloud/transfers/bigquery_to_gcs.py +14 -13
- airflow/providers/google/cloud/transfers/bigquery_to_mssql.py +7 -3
- airflow/providers/google/cloud/transfers/bigquery_to_mysql.py +12 -1
- airflow/providers/google/cloud/transfers/bigquery_to_postgres.py +24 -10
- airflow/providers/google/cloud/transfers/bigquery_to_sql.py +104 -5
- airflow/providers/google/cloud/transfers/calendar_to_gcs.py +1 -1
- airflow/providers/google/cloud/transfers/cassandra_to_gcs.py +18 -22
- airflow/providers/google/cloud/transfers/facebook_ads_to_gcs.py +4 -5
- airflow/providers/google/cloud/transfers/gcs_to_bigquery.py +45 -38
- airflow/providers/google/cloud/transfers/gcs_to_gcs.py +2 -2
- airflow/providers/google/cloud/transfers/gcs_to_local.py +5 -3
- airflow/providers/google/cloud/transfers/gcs_to_sftp.py +10 -4
- airflow/providers/google/cloud/transfers/gdrive_to_gcs.py +6 -2
- airflow/providers/google/cloud/transfers/gdrive_to_local.py +2 -2
- airflow/providers/google/cloud/transfers/http_to_gcs.py +193 -0
- airflow/providers/google/cloud/transfers/local_to_gcs.py +2 -2
- airflow/providers/google/cloud/transfers/mssql_to_gcs.py +1 -1
- airflow/providers/google/cloud/transfers/oracle_to_gcs.py +36 -11
- airflow/providers/google/cloud/transfers/postgres_to_gcs.py +44 -12
- airflow/providers/google/cloud/transfers/s3_to_gcs.py +12 -6
- airflow/providers/google/cloud/transfers/salesforce_to_gcs.py +2 -2
- airflow/providers/google/cloud/transfers/sftp_to_gcs.py +36 -14
- airflow/providers/google/cloud/transfers/sheets_to_gcs.py +3 -3
- airflow/providers/google/cloud/transfers/sql_to_gcs.py +10 -10
- airflow/providers/google/cloud/triggers/bigquery.py +75 -34
- airflow/providers/google/cloud/triggers/bigquery_dts.py +2 -1
- airflow/providers/google/cloud/triggers/cloud_batch.py +2 -1
- airflow/providers/google/cloud/triggers/cloud_build.py +3 -2
- airflow/providers/google/cloud/triggers/cloud_composer.py +303 -47
- airflow/providers/google/cloud/triggers/cloud_run.py +2 -2
- airflow/providers/google/cloud/triggers/cloud_storage_transfer_service.py +96 -5
- airflow/providers/google/cloud/triggers/dataflow.py +125 -2
- airflow/providers/google/cloud/triggers/datafusion.py +1 -1
- airflow/providers/google/cloud/triggers/dataplex.py +16 -3
- airflow/providers/google/cloud/triggers/dataproc.py +124 -53
- airflow/providers/google/cloud/triggers/kubernetes_engine.py +46 -28
- airflow/providers/google/cloud/triggers/mlengine.py +1 -1
- airflow/providers/google/cloud/triggers/pubsub.py +17 -20
- airflow/providers/google/cloud/triggers/vertex_ai.py +8 -7
- airflow/providers/google/cloud/utils/bigquery.py +5 -7
- airflow/providers/google/cloud/utils/bigquery_get_data.py +1 -1
- airflow/providers/google/cloud/utils/credentials_provider.py +4 -3
- airflow/providers/google/cloud/utils/dataform.py +1 -1
- airflow/providers/google/cloud/utils/external_token_supplier.py +0 -1
- airflow/providers/google/cloud/utils/field_validator.py +1 -2
- airflow/providers/google/cloud/utils/validators.py +43 -0
- airflow/providers/google/common/auth_backend/google_openid.py +26 -9
- airflow/providers/google/common/consts.py +2 -1
- airflow/providers/google/common/deprecated.py +2 -1
- airflow/providers/google/common/hooks/base_google.py +40 -43
- airflow/providers/google/common/hooks/operation_helpers.py +78 -0
- airflow/providers/google/common/links/storage.py +0 -22
- airflow/providers/google/common/utils/get_secret.py +31 -0
- airflow/providers/google/common/utils/id_token_credentials.py +4 -5
- airflow/providers/google/firebase/operators/firestore.py +2 -2
- airflow/providers/google/get_provider_info.py +61 -216
- airflow/providers/google/go_module_utils.py +35 -3
- airflow/providers/google/leveldb/hooks/leveldb.py +30 -6
- airflow/providers/google/leveldb/operators/leveldb.py +2 -2
- airflow/providers/google/marketing_platform/hooks/analytics_admin.py +3 -2
- airflow/providers/google/marketing_platform/hooks/display_video.py +3 -109
- airflow/providers/google/marketing_platform/hooks/search_ads.py +1 -1
- airflow/providers/google/marketing_platform/links/analytics_admin.py +4 -5
- airflow/providers/google/marketing_platform/operators/analytics_admin.py +7 -6
- airflow/providers/google/marketing_platform/operators/campaign_manager.py +5 -5
- airflow/providers/google/marketing_platform/operators/display_video.py +28 -489
- airflow/providers/google/marketing_platform/operators/search_ads.py +2 -2
- airflow/providers/google/marketing_platform/sensors/campaign_manager.py +2 -2
- airflow/providers/google/marketing_platform/sensors/display_video.py +4 -64
- airflow/providers/google/suite/hooks/calendar.py +1 -1
- airflow/providers/google/suite/hooks/drive.py +2 -2
- airflow/providers/google/suite/hooks/sheets.py +15 -1
- airflow/providers/google/suite/operators/sheets.py +8 -3
- airflow/providers/google/suite/sensors/drive.py +2 -2
- airflow/providers/google/suite/transfers/gcs_to_gdrive.py +2 -2
- airflow/providers/google/suite/transfers/gcs_to_sheets.py +1 -1
- airflow/providers/google/suite/transfers/local_to_drive.py +3 -3
- airflow/providers/google/suite/transfers/sql_to_sheets.py +5 -4
- airflow/providers/google/version_compat.py +15 -1
- {apache_airflow_providers_google-14.0.0.dist-info → apache_airflow_providers_google-19.1.0rc1.dist-info}/METADATA +117 -72
- apache_airflow_providers_google-19.1.0rc1.dist-info/RECORD +331 -0
- {apache_airflow_providers_google-14.0.0.dist-info → apache_airflow_providers_google-19.1.0rc1.dist-info}/WHEEL +1 -1
- apache_airflow_providers_google-19.1.0rc1.dist-info/licenses/NOTICE +5 -0
- airflow/providers/google/cloud/example_dags/example_cloud_task.py +0 -54
- airflow/providers/google/cloud/hooks/automl.py +0 -679
- airflow/providers/google/cloud/hooks/life_sciences.py +0 -159
- airflow/providers/google/cloud/links/automl.py +0 -193
- airflow/providers/google/cloud/operators/automl.py +0 -1360
- airflow/providers/google/cloud/operators/life_sciences.py +0 -119
- airflow/providers/google/cloud/operators/mlengine.py +0 -1515
- airflow/providers/google/cloud/utils/mlengine_operator_utils.py +0 -273
- apache_airflow_providers_google-14.0.0.dist-info/RECORD +0 -318
- /airflow/providers/google/cloud/{example_dags → bundles}/__init__.py +0 -0
- {apache_airflow_providers_google-14.0.0.dist-info → apache_airflow_providers_google-19.1.0rc1.dist-info}/entry_points.txt +0 -0
- {airflow/providers/google → apache_airflow_providers_google-19.1.0rc1.dist-info/licenses}/LICENSE +0 -0
|
@@ -20,16 +20,17 @@ from __future__ import annotations
|
|
|
20
20
|
|
|
21
21
|
import asyncio
|
|
22
22
|
import json
|
|
23
|
-
from collections.abc import Sequence
|
|
23
|
+
from collections.abc import Collection, Iterable, Sequence
|
|
24
24
|
from datetime import datetime
|
|
25
25
|
from typing import Any
|
|
26
26
|
|
|
27
27
|
from dateutil import parser
|
|
28
|
+
from google.api_core.exceptions import NotFound
|
|
29
|
+
from google.cloud.orchestration.airflow.service_v1.types import ExecuteAirflowCommandResponse
|
|
28
30
|
|
|
29
31
|
from airflow.exceptions import AirflowException
|
|
30
32
|
from airflow.providers.google.cloud.hooks.cloud_composer import CloudComposerAsyncHook
|
|
31
33
|
from airflow.triggers.base import BaseTrigger, TriggerEvent
|
|
32
|
-
from google.cloud.orchestration.airflow.service_v1.types import ExecuteAirflowCommandResponse
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
class CloudComposerExecutionTrigger(BaseTrigger):
|
|
@@ -52,11 +53,6 @@ class CloudComposerExecutionTrigger(BaseTrigger):
|
|
|
52
53
|
self.impersonation_chain = impersonation_chain
|
|
53
54
|
self.pooling_period_seconds = pooling_period_seconds
|
|
54
55
|
|
|
55
|
-
self.gcp_hook = CloudComposerAsyncHook(
|
|
56
|
-
gcp_conn_id=self.gcp_conn_id,
|
|
57
|
-
impersonation_chain=self.impersonation_chain,
|
|
58
|
-
)
|
|
59
|
-
|
|
60
56
|
def serialize(self) -> tuple[str, dict[str, Any]]:
|
|
61
57
|
return (
|
|
62
58
|
"airflow.providers.google.cloud.triggers.cloud_composer.CloudComposerExecutionTrigger",
|
|
@@ -70,7 +66,14 @@ class CloudComposerExecutionTrigger(BaseTrigger):
|
|
|
70
66
|
},
|
|
71
67
|
)
|
|
72
68
|
|
|
69
|
+
def _get_async_hook(self) -> CloudComposerAsyncHook:
|
|
70
|
+
return CloudComposerAsyncHook(
|
|
71
|
+
gcp_conn_id=self.gcp_conn_id,
|
|
72
|
+
impersonation_chain=self.impersonation_chain,
|
|
73
|
+
)
|
|
74
|
+
|
|
73
75
|
async def run(self):
|
|
76
|
+
self.gcp_hook = self._get_async_hook()
|
|
74
77
|
while True:
|
|
75
78
|
operation = await self.gcp_hook.get_operation(operation_name=self.operation_name)
|
|
76
79
|
if operation.done:
|
|
@@ -108,11 +111,6 @@ class CloudComposerAirflowCLICommandTrigger(BaseTrigger):
|
|
|
108
111
|
self.impersonation_chain = impersonation_chain
|
|
109
112
|
self.poll_interval = poll_interval
|
|
110
113
|
|
|
111
|
-
self.gcp_hook = CloudComposerAsyncHook(
|
|
112
|
-
gcp_conn_id=self.gcp_conn_id,
|
|
113
|
-
impersonation_chain=self.impersonation_chain,
|
|
114
|
-
)
|
|
115
|
-
|
|
116
114
|
def serialize(self) -> tuple[str, dict[str, Any]]:
|
|
117
115
|
return (
|
|
118
116
|
"airflow.providers.google.cloud.triggers.cloud_composer.CloudComposerAirflowCLICommandTrigger",
|
|
@@ -127,7 +125,14 @@ class CloudComposerAirflowCLICommandTrigger(BaseTrigger):
|
|
|
127
125
|
},
|
|
128
126
|
)
|
|
129
127
|
|
|
128
|
+
def _get_async_hook(self) -> CloudComposerAsyncHook:
|
|
129
|
+
return CloudComposerAsyncHook(
|
|
130
|
+
gcp_conn_id=self.gcp_conn_id,
|
|
131
|
+
impersonation_chain=self.impersonation_chain,
|
|
132
|
+
)
|
|
133
|
+
|
|
130
134
|
async def run(self):
|
|
135
|
+
self.gcp_hook = self._get_async_hook()
|
|
131
136
|
try:
|
|
132
137
|
result = await self.gcp_hook.wait_command_execution_result(
|
|
133
138
|
project_id=self.project_id,
|
|
@@ -145,10 +150,23 @@ class CloudComposerAirflowCLICommandTrigger(BaseTrigger):
|
|
|
145
150
|
)
|
|
146
151
|
return
|
|
147
152
|
|
|
153
|
+
exit_code = result.get("exit_info", {}).get("exit_code")
|
|
154
|
+
|
|
155
|
+
if exit_code == 0:
|
|
156
|
+
yield TriggerEvent(
|
|
157
|
+
{
|
|
158
|
+
"status": "success",
|
|
159
|
+
"result": result,
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
error_output = "".join(line["content"] for line in result.get("error", []))
|
|
165
|
+
message = f"Airflow CLI command failed with exit code {exit_code}.\nError output:\n{error_output}"
|
|
148
166
|
yield TriggerEvent(
|
|
149
167
|
{
|
|
150
|
-
"status": "
|
|
151
|
-
"
|
|
168
|
+
"status": "error",
|
|
169
|
+
"message": message,
|
|
152
170
|
}
|
|
153
171
|
)
|
|
154
172
|
return
|
|
@@ -166,10 +184,12 @@ class CloudComposerDAGRunTrigger(BaseTrigger):
|
|
|
166
184
|
start_date: datetime,
|
|
167
185
|
end_date: datetime,
|
|
168
186
|
allowed_states: list[str],
|
|
187
|
+
composer_dag_run_id: str | None = None,
|
|
169
188
|
gcp_conn_id: str = "google_cloud_default",
|
|
170
189
|
impersonation_chain: str | Sequence[str] | None = None,
|
|
171
190
|
poll_interval: int = 10,
|
|
172
191
|
composer_airflow_version: int = 2,
|
|
192
|
+
use_rest_api: bool = False,
|
|
173
193
|
):
|
|
174
194
|
super().__init__()
|
|
175
195
|
self.project_id = project_id
|
|
@@ -179,15 +199,12 @@ class CloudComposerDAGRunTrigger(BaseTrigger):
|
|
|
179
199
|
self.start_date = start_date
|
|
180
200
|
self.end_date = end_date
|
|
181
201
|
self.allowed_states = allowed_states
|
|
202
|
+
self.composer_dag_run_id = composer_dag_run_id
|
|
182
203
|
self.gcp_conn_id = gcp_conn_id
|
|
183
204
|
self.impersonation_chain = impersonation_chain
|
|
184
205
|
self.poll_interval = poll_interval
|
|
185
206
|
self.composer_airflow_version = composer_airflow_version
|
|
186
|
-
|
|
187
|
-
self.gcp_hook = CloudComposerAsyncHook(
|
|
188
|
-
gcp_conn_id=self.gcp_conn_id,
|
|
189
|
-
impersonation_chain=self.impersonation_chain,
|
|
190
|
-
)
|
|
207
|
+
self.use_rest_api = use_rest_api
|
|
191
208
|
|
|
192
209
|
def serialize(self) -> tuple[str, dict[str, Any]]:
|
|
193
210
|
return (
|
|
@@ -200,35 +217,60 @@ class CloudComposerDAGRunTrigger(BaseTrigger):
|
|
|
200
217
|
"start_date": self.start_date,
|
|
201
218
|
"end_date": self.end_date,
|
|
202
219
|
"allowed_states": self.allowed_states,
|
|
220
|
+
"composer_dag_run_id": self.composer_dag_run_id,
|
|
203
221
|
"gcp_conn_id": self.gcp_conn_id,
|
|
204
222
|
"impersonation_chain": self.impersonation_chain,
|
|
205
223
|
"poll_interval": self.poll_interval,
|
|
206
224
|
"composer_airflow_version": self.composer_airflow_version,
|
|
225
|
+
"use_rest_api": self.use_rest_api,
|
|
207
226
|
},
|
|
208
227
|
)
|
|
209
228
|
|
|
210
229
|
async def _pull_dag_runs(self) -> list[dict]:
|
|
211
230
|
"""Pull the list of dag runs."""
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
if self.use_rest_api:
|
|
232
|
+
try:
|
|
233
|
+
environment = await self.gcp_hook.get_environment(
|
|
234
|
+
project_id=self.project_id,
|
|
235
|
+
region=self.region,
|
|
236
|
+
environment_id=self.environment_id,
|
|
237
|
+
)
|
|
238
|
+
except NotFound as not_found_err:
|
|
239
|
+
self.log.info("The Composer environment %s does not exist.", self.environment_id)
|
|
240
|
+
raise AirflowException(not_found_err)
|
|
241
|
+
composer_airflow_uri = environment.config.airflow_uri
|
|
242
|
+
|
|
243
|
+
self.log.info(
|
|
244
|
+
"Pulling the DAG %s runs from the %s environment...",
|
|
245
|
+
self.composer_dag_id,
|
|
246
|
+
self.environment_id,
|
|
247
|
+
)
|
|
248
|
+
dag_runs_response = await self.gcp_hook.get_dag_runs(
|
|
249
|
+
composer_airflow_uri=composer_airflow_uri,
|
|
250
|
+
composer_dag_id=self.composer_dag_id,
|
|
251
|
+
)
|
|
252
|
+
dag_runs = dag_runs_response["dag_runs"]
|
|
253
|
+
else:
|
|
254
|
+
cmd_parameters = (
|
|
255
|
+
["-d", self.composer_dag_id, "-o", "json"]
|
|
256
|
+
if self.composer_airflow_version < 3
|
|
257
|
+
else [self.composer_dag_id, "-o", "json"]
|
|
258
|
+
)
|
|
259
|
+
dag_runs_cmd = await self.gcp_hook.execute_airflow_command(
|
|
260
|
+
project_id=self.project_id,
|
|
261
|
+
region=self.region,
|
|
262
|
+
environment_id=self.environment_id,
|
|
263
|
+
command="dags",
|
|
264
|
+
subcommand="list-runs",
|
|
265
|
+
parameters=cmd_parameters,
|
|
266
|
+
)
|
|
267
|
+
cmd_result = await self.gcp_hook.wait_command_execution_result(
|
|
268
|
+
project_id=self.project_id,
|
|
269
|
+
region=self.region,
|
|
270
|
+
environment_id=self.environment_id,
|
|
271
|
+
execution_cmd_info=ExecuteAirflowCommandResponse.to_dict(dag_runs_cmd),
|
|
272
|
+
)
|
|
273
|
+
dag_runs = json.loads(cmd_result["output"][0]["content"])
|
|
232
274
|
return dag_runs
|
|
233
275
|
|
|
234
276
|
def _check_dag_runs_states(
|
|
@@ -248,20 +290,234 @@ class CloudComposerDAGRunTrigger(BaseTrigger):
|
|
|
248
290
|
return False
|
|
249
291
|
return True
|
|
250
292
|
|
|
293
|
+
def _get_async_hook(self) -> CloudComposerAsyncHook:
|
|
294
|
+
return CloudComposerAsyncHook(
|
|
295
|
+
gcp_conn_id=self.gcp_conn_id,
|
|
296
|
+
impersonation_chain=self.impersonation_chain,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
def _check_composer_dag_run_id_states(self, dag_runs: list[dict]) -> bool:
|
|
300
|
+
for dag_run in dag_runs:
|
|
301
|
+
if (
|
|
302
|
+
dag_run["dag_run_id" if self.use_rest_api else "run_id"] == self.composer_dag_run_id
|
|
303
|
+
and dag_run["state"] in self.allowed_states
|
|
304
|
+
):
|
|
305
|
+
return True
|
|
306
|
+
return False
|
|
307
|
+
|
|
251
308
|
async def run(self):
|
|
309
|
+
self.gcp_hook: CloudComposerAsyncHook = self._get_async_hook()
|
|
252
310
|
try:
|
|
253
311
|
while True:
|
|
254
312
|
if datetime.now(self.end_date.tzinfo).timestamp() > self.end_date.timestamp():
|
|
255
313
|
dag_runs = await self._pull_dag_runs()
|
|
256
314
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
315
|
+
if len(dag_runs) == 0:
|
|
316
|
+
self.log.info("Dag runs are empty. Sensor waits for dag runs...")
|
|
317
|
+
self.log.info("Sleeping for %s seconds.", self.poll_interval)
|
|
318
|
+
await asyncio.sleep(self.poll_interval)
|
|
319
|
+
continue
|
|
320
|
+
|
|
321
|
+
if self.composer_dag_run_id:
|
|
322
|
+
self.log.info(
|
|
323
|
+
"Sensor waits for allowed states %s for specified RunID: %s",
|
|
324
|
+
self.allowed_states,
|
|
325
|
+
self.composer_dag_run_id,
|
|
326
|
+
)
|
|
327
|
+
if self._check_composer_dag_run_id_states(dag_runs=dag_runs):
|
|
328
|
+
yield TriggerEvent({"status": "success"})
|
|
329
|
+
return
|
|
330
|
+
else:
|
|
331
|
+
self.log.info("Sensor waits for allowed states: %s", self.allowed_states)
|
|
332
|
+
if self._check_dag_runs_states(
|
|
333
|
+
dag_runs=dag_runs,
|
|
334
|
+
start_date=self.start_date,
|
|
335
|
+
end_date=self.end_date,
|
|
336
|
+
):
|
|
337
|
+
yield TriggerEvent({"status": "success"})
|
|
338
|
+
return
|
|
339
|
+
self.log.info("Sleeping for %s seconds.", self.poll_interval)
|
|
340
|
+
await asyncio.sleep(self.poll_interval)
|
|
341
|
+
except AirflowException as ex:
|
|
342
|
+
yield TriggerEvent(
|
|
343
|
+
{
|
|
344
|
+
"status": "error",
|
|
345
|
+
"message": str(ex),
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class CloudComposerExternalTaskTrigger(BaseTrigger):
|
|
352
|
+
"""The trigger wait for the external task completion."""
|
|
353
|
+
|
|
354
|
+
def __init__(
|
|
355
|
+
self,
|
|
356
|
+
project_id: str,
|
|
357
|
+
region: str,
|
|
358
|
+
environment_id: str,
|
|
359
|
+
start_date: datetime,
|
|
360
|
+
end_date: datetime,
|
|
361
|
+
allowed_states: list[str],
|
|
362
|
+
skipped_states: list[str],
|
|
363
|
+
failed_states: list[str],
|
|
364
|
+
composer_external_dag_id: str,
|
|
365
|
+
composer_external_task_ids: Collection[str] | None = None,
|
|
366
|
+
composer_external_task_group_id: str | None = None,
|
|
367
|
+
gcp_conn_id: str = "google_cloud_default",
|
|
368
|
+
impersonation_chain: str | Sequence[str] | None = None,
|
|
369
|
+
poll_interval: int = 10,
|
|
370
|
+
composer_airflow_version: int = 2,
|
|
371
|
+
):
|
|
372
|
+
super().__init__()
|
|
373
|
+
self.project_id = project_id
|
|
374
|
+
self.region = region
|
|
375
|
+
self.environment_id = environment_id
|
|
376
|
+
self.start_date = start_date
|
|
377
|
+
self.end_date = end_date
|
|
378
|
+
self.allowed_states = allowed_states
|
|
379
|
+
self.skipped_states = skipped_states
|
|
380
|
+
self.failed_states = failed_states
|
|
381
|
+
self.composer_external_dag_id = composer_external_dag_id
|
|
382
|
+
self.composer_external_task_ids = composer_external_task_ids
|
|
383
|
+
self.composer_external_task_group_id = composer_external_task_group_id
|
|
384
|
+
self.gcp_conn_id = gcp_conn_id
|
|
385
|
+
self.impersonation_chain = impersonation_chain
|
|
386
|
+
self.poll_interval = poll_interval
|
|
387
|
+
self.composer_airflow_version = composer_airflow_version
|
|
388
|
+
|
|
389
|
+
def serialize(self) -> tuple[str, dict[str, Any]]:
|
|
390
|
+
return (
|
|
391
|
+
"airflow.providers.google.cloud.triggers.cloud_composer.CloudComposerExternalTaskTrigger",
|
|
392
|
+
{
|
|
393
|
+
"project_id": self.project_id,
|
|
394
|
+
"region": self.region,
|
|
395
|
+
"environment_id": self.environment_id,
|
|
396
|
+
"start_date": self.start_date,
|
|
397
|
+
"end_date": self.end_date,
|
|
398
|
+
"allowed_states": self.allowed_states,
|
|
399
|
+
"skipped_states": self.skipped_states,
|
|
400
|
+
"failed_states": self.failed_states,
|
|
401
|
+
"composer_external_dag_id": self.composer_external_dag_id,
|
|
402
|
+
"composer_external_task_ids": self.composer_external_task_ids,
|
|
403
|
+
"composer_external_task_group_id": self.composer_external_task_group_id,
|
|
404
|
+
"gcp_conn_id": self.gcp_conn_id,
|
|
405
|
+
"impersonation_chain": self.impersonation_chain,
|
|
406
|
+
"poll_interval": self.poll_interval,
|
|
407
|
+
"composer_airflow_version": self.composer_airflow_version,
|
|
408
|
+
},
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
async def _get_task_instances(self, start_date: str, end_date: str) -> list[dict]:
|
|
412
|
+
"""Get the list of task instances."""
|
|
413
|
+
try:
|
|
414
|
+
environment = await self.gcp_hook.get_environment(
|
|
415
|
+
project_id=self.project_id,
|
|
416
|
+
region=self.region,
|
|
417
|
+
environment_id=self.environment_id,
|
|
418
|
+
)
|
|
419
|
+
except NotFound as not_found_err:
|
|
420
|
+
self.log.info("The Composer environment %s does not exist.", self.environment_id)
|
|
421
|
+
raise AirflowException(not_found_err)
|
|
422
|
+
composer_airflow_uri = environment.config.airflow_uri
|
|
423
|
+
|
|
424
|
+
self.log.info(
|
|
425
|
+
"Pulling the DAG '%s' task instances from the '%s' environment...",
|
|
426
|
+
self.composer_external_dag_id,
|
|
427
|
+
self.environment_id,
|
|
428
|
+
)
|
|
429
|
+
task_instances_response = await self.gcp_hook.get_task_instances(
|
|
430
|
+
composer_airflow_uri=composer_airflow_uri,
|
|
431
|
+
composer_dag_id=self.composer_external_dag_id,
|
|
432
|
+
query_parameters={
|
|
433
|
+
"execution_date_gte" if self.composer_airflow_version < 3 else "logical_date_gte": start_date,
|
|
434
|
+
"execution_date_lte" if self.composer_airflow_version < 3 else "logical_date_lte": end_date,
|
|
435
|
+
},
|
|
436
|
+
)
|
|
437
|
+
task_instances = task_instances_response["task_instances"]
|
|
438
|
+
|
|
439
|
+
if self.composer_external_task_ids:
|
|
440
|
+
task_instances = [
|
|
441
|
+
task_instance
|
|
442
|
+
for task_instance in task_instances
|
|
443
|
+
if task_instance["task_id"] in self.composer_external_task_ids
|
|
444
|
+
]
|
|
445
|
+
elif self.composer_external_task_group_id:
|
|
446
|
+
task_instances = [
|
|
447
|
+
task_instance
|
|
448
|
+
for task_instance in task_instances
|
|
449
|
+
if self.composer_external_task_group_id in task_instance["task_id"].split(".")
|
|
450
|
+
]
|
|
451
|
+
|
|
452
|
+
return task_instances
|
|
453
|
+
|
|
454
|
+
def _check_task_instances_states(
|
|
455
|
+
self,
|
|
456
|
+
task_instances: list[dict],
|
|
457
|
+
start_date: datetime,
|
|
458
|
+
end_date: datetime,
|
|
459
|
+
states: Iterable[str],
|
|
460
|
+
) -> bool:
|
|
461
|
+
for task_instance in task_instances:
|
|
462
|
+
if (
|
|
463
|
+
start_date.timestamp()
|
|
464
|
+
< parser.parse(
|
|
465
|
+
task_instance["execution_date" if self.composer_airflow_version < 3 else "logical_date"]
|
|
466
|
+
).timestamp()
|
|
467
|
+
< end_date.timestamp()
|
|
468
|
+
) and task_instance["state"] not in states:
|
|
469
|
+
return False
|
|
470
|
+
return True
|
|
471
|
+
|
|
472
|
+
def _get_async_hook(self) -> CloudComposerAsyncHook:
|
|
473
|
+
return CloudComposerAsyncHook(
|
|
474
|
+
gcp_conn_id=self.gcp_conn_id,
|
|
475
|
+
impersonation_chain=self.impersonation_chain,
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
async def run(self):
|
|
479
|
+
self.gcp_hook: CloudComposerAsyncHook = self._get_async_hook()
|
|
480
|
+
try:
|
|
481
|
+
while True:
|
|
482
|
+
task_instances = await self._get_task_instances(
|
|
483
|
+
start_date=self.start_date.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
484
|
+
end_date=self.end_date.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
if len(task_instances) == 0:
|
|
488
|
+
self.log.info("Task Instances are empty. Sensor waits for task instances...")
|
|
489
|
+
self.log.info("Sleeping for %s seconds.", self.poll_interval)
|
|
490
|
+
await asyncio.sleep(self.poll_interval)
|
|
491
|
+
continue
|
|
492
|
+
|
|
493
|
+
if self.failed_states and self._check_task_instances_states(
|
|
494
|
+
task_instances=task_instances,
|
|
495
|
+
start_date=self.start_date,
|
|
496
|
+
end_date=self.end_date,
|
|
497
|
+
states=self.failed_states,
|
|
498
|
+
):
|
|
499
|
+
yield TriggerEvent({"status": "failed"})
|
|
500
|
+
return
|
|
501
|
+
|
|
502
|
+
if self.skipped_states and self._check_task_instances_states(
|
|
503
|
+
task_instances=task_instances,
|
|
504
|
+
start_date=self.start_date,
|
|
505
|
+
end_date=self.end_date,
|
|
506
|
+
states=self.skipped_states,
|
|
507
|
+
):
|
|
508
|
+
yield TriggerEvent({"status": "skipped"})
|
|
509
|
+
return
|
|
510
|
+
|
|
511
|
+
self.log.info("Sensor waits for allowed states: %s", self.allowed_states)
|
|
512
|
+
if self._check_task_instances_states(
|
|
513
|
+
task_instances=task_instances,
|
|
514
|
+
start_date=self.start_date,
|
|
515
|
+
end_date=self.end_date,
|
|
516
|
+
states=self.allowed_states,
|
|
517
|
+
):
|
|
518
|
+
yield TriggerEvent({"status": "success"})
|
|
519
|
+
return
|
|
520
|
+
|
|
265
521
|
self.log.info("Sleeping for %s seconds.", self.poll_interval)
|
|
266
522
|
await asyncio.sleep(self.poll_interval)
|
|
267
523
|
except AirflowException as ex:
|
|
@@ -26,7 +26,7 @@ from airflow.providers.google.cloud.hooks.cloud_run import CloudRunAsyncHook
|
|
|
26
26
|
from airflow.triggers.base import BaseTrigger, TriggerEvent
|
|
27
27
|
|
|
28
28
|
if TYPE_CHECKING:
|
|
29
|
-
from google.longrunning import operations_pb2
|
|
29
|
+
from google.longrunning import operations_pb2
|
|
30
30
|
|
|
31
31
|
DEFAULT_BATCH_LOCATION = "us-central1"
|
|
32
32
|
|
|
@@ -134,7 +134,7 @@ class CloudRunJobFinishedTrigger(BaseTrigger):
|
|
|
134
134
|
|
|
135
135
|
yield TriggerEvent(
|
|
136
136
|
{
|
|
137
|
-
"status": RunJobStatus.TIMEOUT,
|
|
137
|
+
"status": RunJobStatus.TIMEOUT.value,
|
|
138
138
|
"job_name": self.job_name,
|
|
139
139
|
}
|
|
140
140
|
)
|
|
@@ -21,6 +21,10 @@ import asyncio
|
|
|
21
21
|
from collections.abc import AsyncIterator, Iterable, Sequence
|
|
22
22
|
from typing import Any
|
|
23
23
|
|
|
24
|
+
from google.api_core.exceptions import GoogleAPIError
|
|
25
|
+
from google.cloud.storage_transfer_v1.types import TransferOperation
|
|
26
|
+
from google.protobuf.json_format import MessageToDict
|
|
27
|
+
|
|
24
28
|
from airflow.exceptions import AirflowException
|
|
25
29
|
from airflow.providers.google.cloud.hooks.cloud_storage_transfer_service import (
|
|
26
30
|
CloudDataTransferServiceAsyncHook,
|
|
@@ -28,8 +32,6 @@ from airflow.providers.google.cloud.hooks.cloud_storage_transfer_service import
|
|
|
28
32
|
)
|
|
29
33
|
from airflow.providers.google.common.hooks.base_google import PROVIDE_PROJECT_ID
|
|
30
34
|
from airflow.triggers.base import BaseTrigger, TriggerEvent
|
|
31
|
-
from google.api_core.exceptions import GoogleAPIError
|
|
32
|
-
from google.cloud.storage_transfer_v1.types import TransferOperation
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
class CloudStorageTransferServiceCreateJobsTrigger(BaseTrigger):
|
|
@@ -58,7 +60,7 @@ class CloudStorageTransferServiceCreateJobsTrigger(BaseTrigger):
|
|
|
58
60
|
def serialize(self) -> tuple[str, dict[str, Any]]:
|
|
59
61
|
"""Serialize StorageTransferJobsTrigger arguments and classpath."""
|
|
60
62
|
return (
|
|
61
|
-
f"{self.__class__.__module__
|
|
63
|
+
f"{self.__class__.__module__}.{self.__class__.__qualname__}",
|
|
62
64
|
{
|
|
63
65
|
"project_id": self.project_id,
|
|
64
66
|
"job_names": self.job_names,
|
|
@@ -67,7 +69,7 @@ class CloudStorageTransferServiceCreateJobsTrigger(BaseTrigger):
|
|
|
67
69
|
},
|
|
68
70
|
)
|
|
69
71
|
|
|
70
|
-
async def run(self) -> AsyncIterator[TriggerEvent]:
|
|
72
|
+
async def run(self) -> AsyncIterator[TriggerEvent]:
|
|
71
73
|
"""Get current data storage transfer jobs and yields a TriggerEvent."""
|
|
72
74
|
async_hook: CloudDataTransferServiceAsyncHook = self.get_async_hook()
|
|
73
75
|
|
|
@@ -175,7 +177,7 @@ class CloudStorageTransferServiceCheckJobStatusTrigger(BaseTrigger):
|
|
|
175
177
|
def serialize(self) -> tuple[str, dict[str, Any]]:
|
|
176
178
|
"""Serialize CloudStorageTransferServiceCheckJobStatusTrigger arguments and classpath."""
|
|
177
179
|
return (
|
|
178
|
-
f"{self.__class__.__module__
|
|
180
|
+
f"{self.__class__.__module__}.{self.__class__.__qualname__}",
|
|
179
181
|
{
|
|
180
182
|
"job_name": self.job_name,
|
|
181
183
|
"expected_statuses": self.expected_statuses,
|
|
@@ -230,3 +232,92 @@ class CloudStorageTransferServiceCheckJobStatusTrigger(BaseTrigger):
|
|
|
230
232
|
except Exception as e:
|
|
231
233
|
self.log.exception("Exception occurred while checking for query completion")
|
|
232
234
|
yield TriggerEvent({"status": "error", "message": str(e)})
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class CloudDataTransferServiceRunJobTrigger(BaseTrigger):
|
|
238
|
+
"""
|
|
239
|
+
CloudDataTransferServiceRunJobTrigger run on the trigger worker to run Cloud Storage Transfer job.
|
|
240
|
+
|
|
241
|
+
:param job_name: The name of the transfer job
|
|
242
|
+
:param project_id: The ID of the project that owns the Transfer Job.
|
|
243
|
+
:param poke_interval: Polling period in seconds to check for the status
|
|
244
|
+
:param gcp_conn_id: The connection ID used to connect to Google Cloud.
|
|
245
|
+
:param impersonation_chain: Optional service account to impersonate using short-term
|
|
246
|
+
credentials, or chained list of accounts required to get the access_token
|
|
247
|
+
of the last account in the list, which will be impersonated in the request.
|
|
248
|
+
If set as a string, the account must grant the originating account
|
|
249
|
+
the Service Account Token Creator IAM role.
|
|
250
|
+
If set as a sequence, the identities from the list must grant
|
|
251
|
+
Service Account Token Creator IAM role to the directly preceding identity, with first
|
|
252
|
+
account from the list granting this role to the originating account (templated).
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
def __init__(
|
|
256
|
+
self,
|
|
257
|
+
job_name: str,
|
|
258
|
+
project_id: str = PROVIDE_PROJECT_ID,
|
|
259
|
+
poke_interval: float = 10.0,
|
|
260
|
+
gcp_conn_id: str = "google_cloud_default",
|
|
261
|
+
impersonation_chain: str | Sequence[str] | None = None,
|
|
262
|
+
):
|
|
263
|
+
super().__init__()
|
|
264
|
+
self.job_name = job_name
|
|
265
|
+
self.project_id = project_id
|
|
266
|
+
self.poke_interval = poke_interval
|
|
267
|
+
self.gcp_conn_id = gcp_conn_id
|
|
268
|
+
self.impersonation_chain = impersonation_chain
|
|
269
|
+
|
|
270
|
+
def serialize(self) -> tuple[str, dict[str, Any]]:
|
|
271
|
+
"""Serialize CloudDataTransferServiceRunJobTrigger arguments and classpath."""
|
|
272
|
+
return (
|
|
273
|
+
f"{self.__class__.__module__}.{self.__class__.__qualname__}",
|
|
274
|
+
{
|
|
275
|
+
"job_name": self.job_name,
|
|
276
|
+
"project_id": self.project_id,
|
|
277
|
+
"poke_interval": self.poke_interval,
|
|
278
|
+
"gcp_conn_id": self.gcp_conn_id,
|
|
279
|
+
"impersonation_chain": self.impersonation_chain,
|
|
280
|
+
},
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
def _get_async_hook(self) -> CloudDataTransferServiceAsyncHook:
|
|
284
|
+
return CloudDataTransferServiceAsyncHook(
|
|
285
|
+
project_id=self.project_id,
|
|
286
|
+
gcp_conn_id=self.gcp_conn_id,
|
|
287
|
+
impersonation_chain=self.impersonation_chain,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
async def run(self) -> AsyncIterator[TriggerEvent]:
|
|
291
|
+
"""Run the transfer job and yield a TriggerEvent."""
|
|
292
|
+
hook = self._get_async_hook()
|
|
293
|
+
|
|
294
|
+
try:
|
|
295
|
+
job_operation = await hook.run_transfer_job(self.job_name)
|
|
296
|
+
while True:
|
|
297
|
+
job_completed = await job_operation.done()
|
|
298
|
+
if job_completed:
|
|
299
|
+
yield TriggerEvent(
|
|
300
|
+
{
|
|
301
|
+
"status": "success",
|
|
302
|
+
"message": "Transfer operation run completed successfully",
|
|
303
|
+
"job_result": {
|
|
304
|
+
"name": job_operation.operation.name,
|
|
305
|
+
"metadata": MessageToDict(
|
|
306
|
+
job_operation.operation.metadata, preserving_proto_field_name=True
|
|
307
|
+
),
|
|
308
|
+
"response": MessageToDict(
|
|
309
|
+
job_operation.operation.response, preserving_proto_field_name=True
|
|
310
|
+
),
|
|
311
|
+
},
|
|
312
|
+
}
|
|
313
|
+
)
|
|
314
|
+
return
|
|
315
|
+
|
|
316
|
+
self.log.info(
|
|
317
|
+
"Sleeping for %s seconds.",
|
|
318
|
+
self.poke_interval,
|
|
319
|
+
)
|
|
320
|
+
await asyncio.sleep(self.poke_interval)
|
|
321
|
+
except Exception as e:
|
|
322
|
+
self.log.exception("Exception occurred while running transfer job")
|
|
323
|
+
yield TriggerEvent({"status": "error", "message": str(e)})
|