apache-airflow-providers-amazon 8.16.0__py3-none-any.whl → 8.17.0__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 (46) hide show
  1. airflow/providers/amazon/__init__.py +1 -1
  2. airflow/providers/amazon/aws/auth_manager/avp/entities.py +1 -0
  3. airflow/providers/amazon/aws/auth_manager/avp/facade.py +34 -19
  4. airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py +44 -1
  5. airflow/providers/amazon/aws/auth_manager/cli/__init__.py +16 -0
  6. airflow/providers/amazon/aws/auth_manager/cli/avp_commands.py +178 -0
  7. airflow/providers/amazon/aws/auth_manager/cli/definition.py +62 -0
  8. airflow/providers/amazon/aws/auth_manager/cli/schema.json +171 -0
  9. airflow/providers/amazon/aws/auth_manager/constants.py +1 -0
  10. airflow/providers/amazon/aws/executors/ecs/ecs_executor.py +77 -23
  11. airflow/providers/amazon/aws/executors/ecs/ecs_executor_config.py +17 -0
  12. airflow/providers/amazon/aws/executors/ecs/utils.py +1 -1
  13. airflow/providers/amazon/aws/executors/utils/__init__.py +16 -0
  14. airflow/providers/amazon/aws/executors/utils/exponential_backoff_retry.py +60 -0
  15. airflow/providers/amazon/aws/hooks/athena_sql.py +168 -0
  16. airflow/providers/amazon/aws/hooks/base_aws.py +14 -0
  17. airflow/providers/amazon/aws/hooks/quicksight.py +33 -18
  18. airflow/providers/amazon/aws/hooks/redshift_data.py +66 -17
  19. airflow/providers/amazon/aws/hooks/redshift_sql.py +1 -1
  20. airflow/providers/amazon/aws/hooks/s3.py +18 -4
  21. airflow/providers/amazon/aws/log/cloudwatch_task_handler.py +2 -2
  22. airflow/providers/amazon/aws/operators/batch.py +33 -15
  23. airflow/providers/amazon/aws/operators/cloud_formation.py +37 -26
  24. airflow/providers/amazon/aws/operators/datasync.py +19 -18
  25. airflow/providers/amazon/aws/operators/dms.py +57 -69
  26. airflow/providers/amazon/aws/operators/ec2.py +19 -5
  27. airflow/providers/amazon/aws/operators/emr.py +30 -10
  28. airflow/providers/amazon/aws/operators/eventbridge.py +57 -80
  29. airflow/providers/amazon/aws/operators/quicksight.py +17 -24
  30. airflow/providers/amazon/aws/operators/redshift_data.py +68 -19
  31. airflow/providers/amazon/aws/operators/s3.py +1 -1
  32. airflow/providers/amazon/aws/operators/sagemaker.py +42 -12
  33. airflow/providers/amazon/aws/sensors/cloud_formation.py +30 -25
  34. airflow/providers/amazon/aws/sensors/dms.py +31 -24
  35. airflow/providers/amazon/aws/sensors/dynamodb.py +15 -15
  36. airflow/providers/amazon/aws/sensors/quicksight.py +34 -24
  37. airflow/providers/amazon/aws/sensors/redshift_cluster.py +41 -3
  38. airflow/providers/amazon/aws/sensors/s3.py +13 -8
  39. airflow/providers/amazon/aws/triggers/redshift_cluster.py +54 -2
  40. airflow/providers/amazon/aws/triggers/redshift_data.py +113 -0
  41. airflow/providers/amazon/aws/triggers/s3.py +9 -4
  42. airflow/providers/amazon/get_provider_info.py +55 -16
  43. {apache_airflow_providers_amazon-8.16.0.dist-info → apache_airflow_providers_amazon-8.17.0.dist-info}/METADATA +15 -13
  44. {apache_airflow_providers_amazon-8.16.0.dist-info → apache_airflow_providers_amazon-8.17.0.dist-info}/RECORD +46 -38
  45. {apache_airflow_providers_amazon-8.16.0.dist-info → apache_airflow_providers_amazon-8.17.0.dist-info}/WHEEL +0 -0
  46. {apache_airflow_providers_amazon-8.16.0.dist-info → apache_airflow_providers_amazon-8.17.0.dist-info}/entry_points.txt +0 -0
@@ -17,19 +17,19 @@
17
17
  # under the License.
18
18
  from __future__ import annotations
19
19
 
20
+ import warnings
20
21
  from functools import cached_property
21
22
  from typing import TYPE_CHECKING, Sequence
22
23
 
23
- from airflow.exceptions import AirflowException, AirflowSkipException
24
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
24
25
  from airflow.providers.amazon.aws.hooks.quicksight import QuickSightHook
25
- from airflow.providers.amazon.aws.hooks.sts import StsHook
26
- from airflow.sensors.base import BaseSensorOperator
26
+ from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
27
27
 
28
28
  if TYPE_CHECKING:
29
29
  from airflow.utils.context import Context
30
30
 
31
31
 
32
- class QuickSightSensor(BaseSensorOperator):
32
+ class QuickSightSensor(AwsBaseSensor[QuickSightHook]):
33
33
  """
34
34
  Watches for the status of an Amazon QuickSight Ingestion.
35
35
 
@@ -39,27 +39,25 @@ class QuickSightSensor(BaseSensorOperator):
39
39
 
40
40
  :param data_set_id: ID of the dataset used in the ingestion.
41
41
  :param ingestion_id: ID for the ingestion.
42
- :param aws_conn_id: The Airflow connection used for AWS credentials. (templated)
43
- If this is None or empty then the default boto3 behaviour is used. If
44
- running Airflow in a distributed manner and aws_conn_id is None or
45
- empty, then the default boto3 configuration would be used (and must be
46
- maintained on each worker node).
42
+ :param aws_conn_id: The Airflow connection used for AWS credentials.
43
+ If this is ``None`` or empty then the default boto3 behaviour is used. If
44
+ running Airflow in a distributed manner and aws_conn_id is None or
45
+ empty, then default boto3 configuration would be used (and must be
46
+ maintained on each worker node).
47
+ :param region_name: AWS region_name. If not specified then the default boto3 behaviour is used.
48
+ :param verify: Whether or not to verify SSL certificates. See:
49
+ https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html
50
+ :param botocore_config: Configuration dictionary (key-values) for botocore client. See:
51
+ https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html
47
52
  """
48
53
 
54
+ aws_hook_class = QuickSightHook
49
55
  template_fields: Sequence[str] = ("data_set_id", "ingestion_id", "aws_conn_id")
50
56
 
51
- def __init__(
52
- self,
53
- *,
54
- data_set_id: str,
55
- ingestion_id: str,
56
- aws_conn_id: str = "aws_default",
57
- **kwargs,
58
- ) -> None:
57
+ def __init__(self, *, data_set_id: str, ingestion_id: str, **kwargs):
59
58
  super().__init__(**kwargs)
60
59
  self.data_set_id = data_set_id
61
60
  self.ingestion_id = ingestion_id
62
- self.aws_conn_id = aws_conn_id
63
61
  self.success_status = "COMPLETED"
64
62
  self.errored_statuses = ("FAILED", "CANCELLED")
65
63
 
@@ -71,13 +69,10 @@ class QuickSightSensor(BaseSensorOperator):
71
69
  :return: True if it COMPLETED and False if not.
72
70
  """
73
71
  self.log.info("Poking for Amazon QuickSight Ingestion ID: %s", self.ingestion_id)
74
- aws_account_id = self.sts_hook.get_account_number()
75
- quicksight_ingestion_state = self.quicksight_hook.get_status(
76
- aws_account_id, self.data_set_id, self.ingestion_id
77
- )
72
+ quicksight_ingestion_state = self.hook.get_status(None, self.data_set_id, self.ingestion_id)
78
73
  self.log.info("QuickSight Status: %s", quicksight_ingestion_state)
79
74
  if quicksight_ingestion_state in self.errored_statuses:
80
- error = self.quicksight_hook.get_error_info(aws_account_id, self.data_set_id, self.ingestion_id)
75
+ error = self.hook.get_error_info(None, self.data_set_id, self.ingestion_id)
81
76
  message = f"The QuickSight Ingestion failed. Error info: {error}"
82
77
  if self.soft_fail:
83
78
  raise AirflowSkipException(message)
@@ -86,8 +81,23 @@ class QuickSightSensor(BaseSensorOperator):
86
81
 
87
82
  @cached_property
88
83
  def quicksight_hook(self):
89
- return QuickSightHook(aws_conn_id=self.aws_conn_id)
84
+ warnings.warn(
85
+ f"`{type(self).__name__}.quicksight_hook` property is deprecated, "
86
+ f"please use `{type(self).__name__}.hook` property instead.",
87
+ AirflowProviderDeprecationWarning,
88
+ stacklevel=2,
89
+ )
90
+ return self.hook
90
91
 
91
92
  @cached_property
92
93
  def sts_hook(self):
94
+ warnings.warn(
95
+ f"`{type(self).__name__}.sts_hook` property is deprecated and will be removed in the future. "
96
+ "This property used for obtain AWS Account ID, "
97
+ f"please consider to use `{type(self).__name__}.hook.account_id` instead",
98
+ AirflowProviderDeprecationWarning,
99
+ stacklevel=2,
100
+ )
101
+ from airflow.providers.amazon.aws.hooks.sts import StsHook
102
+
93
103
  return StsHook(aws_conn_id=self.aws_conn_id)
@@ -16,13 +16,16 @@
16
16
  # under the License.
17
17
  from __future__ import annotations
18
18
 
19
+ from datetime import timedelta
19
20
  from functools import cached_property
20
- from typing import TYPE_CHECKING, Sequence
21
+ from typing import TYPE_CHECKING, Any, Sequence
21
22
 
22
23
  from deprecated import deprecated
23
24
 
24
- from airflow.exceptions import AirflowProviderDeprecationWarning
25
+ from airflow.configuration import conf
26
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
25
27
  from airflow.providers.amazon.aws.hooks.redshift_cluster import RedshiftHook
28
+ from airflow.providers.amazon.aws.triggers.redshift_cluster import RedshiftClusterTrigger
26
29
  from airflow.sensors.base import BaseSensorOperator
27
30
 
28
31
  if TYPE_CHECKING:
@@ -39,6 +42,7 @@ class RedshiftClusterSensor(BaseSensorOperator):
39
42
 
40
43
  :param cluster_identifier: The identifier for the cluster being pinged.
41
44
  :param target_status: The cluster status desired.
45
+ :param deferrable: Run operator in the deferrable mode.
42
46
  """
43
47
 
44
48
  template_fields: Sequence[str] = ("cluster_identifier", "target_status")
@@ -49,14 +53,16 @@ class RedshiftClusterSensor(BaseSensorOperator):
49
53
  cluster_identifier: str,
50
54
  target_status: str = "available",
51
55
  aws_conn_id: str = "aws_default",
56
+ deferrable: bool = conf.getboolean("operators", "default_deferrable", fallback=False),
52
57
  **kwargs,
53
58
  ):
54
59
  super().__init__(**kwargs)
55
60
  self.cluster_identifier = cluster_identifier
56
61
  self.target_status = target_status
57
62
  self.aws_conn_id = aws_conn_id
63
+ self.deferrable = deferrable
58
64
 
59
- def poke(self, context: Context):
65
+ def poke(self, context: Context) -> bool:
60
66
  current_status = self.hook.cluster_status(self.cluster_identifier)
61
67
  self.log.info(
62
68
  "Poked cluster %s for status '%s', found status '%s'",
@@ -66,6 +72,38 @@ class RedshiftClusterSensor(BaseSensorOperator):
66
72
  )
67
73
  return current_status == self.target_status
68
74
 
75
+ def execute(self, context: Context) -> None:
76
+ if not self.deferrable:
77
+ super().execute(context=context)
78
+ elif not self.poke(context):
79
+ self.defer(
80
+ timeout=timedelta(seconds=self.timeout),
81
+ trigger=RedshiftClusterTrigger(
82
+ aws_conn_id=self.aws_conn_id,
83
+ cluster_identifier=self.cluster_identifier,
84
+ target_status=self.target_status,
85
+ poke_interval=self.poke_interval,
86
+ ),
87
+ method_name="execute_complete",
88
+ )
89
+
90
+ def execute_complete(self, context: Context, event: dict[str, Any] | None = None) -> None:
91
+ if event is None:
92
+ err_msg = "Trigger error: event is None"
93
+ self.log.error(err_msg)
94
+ raise AirflowException(err_msg)
95
+
96
+ status = event["status"]
97
+ if status == "error":
98
+ # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
99
+ message = f"{event['status']}: {event['message']}"
100
+ if self.soft_fail:
101
+ raise AirflowSkipException(message)
102
+ raise AirflowException(message)
103
+ elif status == "success":
104
+ self.log.info("%s completed successfully.", self.task_id)
105
+ self.log.info("Cluster Identifier %s is in %s state", self.cluster_identifier, self.target_status)
106
+
69
107
  @deprecated(reason="use `hook` property instead.", category=AirflowProviderDeprecationWarning)
70
108
  def get_hook(self) -> RedshiftHook:
71
109
  """Create and return a RedshiftHook."""
@@ -65,7 +65,6 @@ class S3KeySensor(BaseSensorOperator):
65
65
  def check_fn(files: List) -> bool:
66
66
  return any(f.get('Size', 0) > 1048576 for f in files)
67
67
  :param aws_conn_id: a reference to the s3 connection
68
- :param deferrable: Run operator in the deferrable mode
69
68
  :param verify: Whether to verify SSL certificates for S3 connection.
70
69
  By default, SSL certificates are verified.
71
70
  You can provide the following values:
@@ -76,6 +75,8 @@ class S3KeySensor(BaseSensorOperator):
76
75
  - ``path/to/cert/bundle.pem``: A filename of the CA cert bundle to uses.
77
76
  You can specify this argument if you want to use a different
78
77
  CA cert bundle than the one used by botocore.
78
+ :param deferrable: Run operator in the deferrable mode
79
+ :param use_regex: whether to use regex to check bucket
79
80
  """
80
81
 
81
82
  template_fields: Sequence[str] = ("bucket_key", "bucket_name")
@@ -90,6 +91,7 @@ class S3KeySensor(BaseSensorOperator):
90
91
  aws_conn_id: str = "aws_default",
91
92
  verify: str | bool | None = None,
92
93
  deferrable: bool = conf.getboolean("operators", "default_deferrable", fallback=False),
94
+ use_regex: bool = False,
93
95
  **kwargs,
94
96
  ):
95
97
  super().__init__(**kwargs)
@@ -100,6 +102,7 @@ class S3KeySensor(BaseSensorOperator):
100
102
  self.aws_conn_id = aws_conn_id
101
103
  self.verify = verify
102
104
  self.deferrable = deferrable
105
+ self.use_regex = use_regex
103
106
 
104
107
  def _check_key(self, key):
105
108
  bucket_name, key = S3Hook.get_s3_bucket_key(self.bucket_name, key, "bucket_name", "bucket_key")
@@ -121,6 +124,11 @@ class S3KeySensor(BaseSensorOperator):
121
124
 
122
125
  # Reduce the set of metadata to size only
123
126
  files = [{"Size": f["Size"]} for f in key_matches]
127
+ elif self.use_regex:
128
+ keys = self.hook.get_file_metadata("", bucket_name)
129
+ key_matches = [k for k in keys if re.match(pattern=key, string=k["Key"])]
130
+ if not key_matches:
131
+ return False
124
132
  else:
125
133
  obj = self.hook.head_object(key, bucket_name)
126
134
  if obj is None:
@@ -158,11 +166,12 @@ class S3KeySensor(BaseSensorOperator):
158
166
  verify=self.verify,
159
167
  poke_interval=self.poke_interval,
160
168
  should_check_fn=bool(self.check_fn),
169
+ use_regex=self.use_regex,
161
170
  ),
162
171
  method_name="execute_complete",
163
172
  )
164
173
 
165
- def execute_complete(self, context: Context, event: dict[str, Any]) -> bool | None:
174
+ def execute_complete(self, context: Context, event: dict[str, Any]) -> None:
166
175
  """
167
176
  Execute when the trigger fires - returns immediately.
168
177
 
@@ -170,17 +179,13 @@ class S3KeySensor(BaseSensorOperator):
170
179
  """
171
180
  if event["status"] == "running":
172
181
  found_keys = self.check_fn(event["files"]) # type: ignore[misc]
173
- if found_keys:
174
- return None
175
- else:
182
+ if not found_keys:
176
183
  self._defer()
177
-
178
- if event["status"] == "error":
184
+ elif event["status"] == "error":
179
185
  # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
180
186
  if self.soft_fail:
181
187
  raise AirflowSkipException(event["message"])
182
188
  raise AirflowException(event["message"])
183
- return None
184
189
 
185
190
  @deprecated(reason="use `hook` property instead.", category=AirflowProviderDeprecationWarning)
186
191
  def get_hook(self) -> S3Hook:
@@ -16,12 +16,14 @@
16
16
  # under the License.
17
17
  from __future__ import annotations
18
18
 
19
+ import asyncio
19
20
  import warnings
20
- from typing import TYPE_CHECKING
21
+ from typing import TYPE_CHECKING, Any, AsyncIterator
21
22
 
22
23
  from airflow.exceptions import AirflowProviderDeprecationWarning
23
- from airflow.providers.amazon.aws.hooks.redshift_cluster import RedshiftHook
24
+ from airflow.providers.amazon.aws.hooks.redshift_cluster import RedshiftAsyncHook, RedshiftHook
24
25
  from airflow.providers.amazon.aws.triggers.base import AwsBaseWaiterTrigger
26
+ from airflow.triggers.base import BaseTrigger, TriggerEvent
25
27
 
26
28
  if TYPE_CHECKING:
27
29
  from airflow.providers.amazon.aws.hooks.base_aws import AwsGenericHook
@@ -262,3 +264,53 @@ class RedshiftDeleteClusterTrigger(AwsBaseWaiterTrigger):
262
264
 
263
265
  def hook(self) -> AwsGenericHook:
264
266
  return RedshiftHook(aws_conn_id=self.aws_conn_id)
267
+
268
+
269
+ class RedshiftClusterTrigger(BaseTrigger):
270
+ """
271
+ RedshiftClusterTrigger is fired as deferred class with params to run the task in trigger worker.
272
+
273
+ :param aws_conn_id: Reference to AWS connection id for redshift
274
+ :param cluster_identifier: unique identifier of a cluster
275
+ :param target_status: Reference to the status which needs to be checked
276
+ :param poke_interval: polling period in seconds to check for the status
277
+ """
278
+
279
+ def __init__(
280
+ self,
281
+ aws_conn_id: str,
282
+ cluster_identifier: str,
283
+ target_status: str,
284
+ poke_interval: float,
285
+ ):
286
+ super().__init__()
287
+ self.aws_conn_id = aws_conn_id
288
+ self.cluster_identifier = cluster_identifier
289
+ self.target_status = target_status
290
+ self.poke_interval = poke_interval
291
+
292
+ def serialize(self) -> tuple[str, dict[str, Any]]:
293
+ """Serializes RedshiftClusterTrigger arguments and classpath."""
294
+ return (
295
+ "airflow.providers.amazon.aws.triggers.redshift_cluster.RedshiftClusterTrigger",
296
+ {
297
+ "aws_conn_id": self.aws_conn_id,
298
+ "cluster_identifier": self.cluster_identifier,
299
+ "target_status": self.target_status,
300
+ "poke_interval": self.poke_interval,
301
+ },
302
+ )
303
+
304
+ async def run(self) -> AsyncIterator[TriggerEvent]:
305
+ """Simple async function run until the cluster status match the target status."""
306
+ try:
307
+ hook = RedshiftAsyncHook(aws_conn_id=self.aws_conn_id)
308
+ while True:
309
+ res = await hook.cluster_status(self.cluster_identifier)
310
+ if (res["status"] == "success" and res["cluster_state"] == self.target_status) or res[
311
+ "status"
312
+ ] == "error":
313
+ yield TriggerEvent(res)
314
+ await asyncio.sleep(self.poke_interval)
315
+ except Exception as e:
316
+ yield TriggerEvent({"status": "error", "message": str(e)})
@@ -0,0 +1,113 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ from __future__ import annotations
19
+
20
+ import asyncio
21
+ from functools import cached_property
22
+ from typing import Any, AsyncIterator
23
+
24
+ from airflow.providers.amazon.aws.hooks.redshift_data import (
25
+ ABORTED_STATE,
26
+ FAILED_STATE,
27
+ RedshiftDataHook,
28
+ RedshiftDataQueryAbortedError,
29
+ RedshiftDataQueryFailedError,
30
+ )
31
+ from airflow.triggers.base import BaseTrigger, TriggerEvent
32
+
33
+
34
+ class RedshiftDataTrigger(BaseTrigger):
35
+ """
36
+ RedshiftDataTrigger is fired as deferred class with params to run the task in triggerer.
37
+
38
+ :param statement_id: the UUID of the statement
39
+ :param task_id: task ID of the Dag
40
+ :param poll_interval: polling period in seconds to check for the status
41
+ :param aws_conn_id: AWS connection ID for redshift
42
+ :param region_name: aws region to use
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ statement_id: str,
48
+ task_id: str,
49
+ poll_interval: int,
50
+ aws_conn_id: str | None = "aws_default",
51
+ region_name: str | None = None,
52
+ verify: bool | str | None = None,
53
+ botocore_config: dict | None = None,
54
+ ):
55
+ super().__init__()
56
+ self.statement_id = statement_id
57
+ self.task_id = task_id
58
+ self.poll_interval = poll_interval
59
+
60
+ self.aws_conn_id = aws_conn_id
61
+ self.region_name = region_name
62
+ self.verify = verify
63
+ self.botocore_config = botocore_config
64
+
65
+ def serialize(self) -> tuple[str, dict[str, Any]]:
66
+ """Serializes RedshiftDataTrigger arguments and classpath."""
67
+ return (
68
+ "airflow.providers.amazon.aws.triggers.redshift_data.RedshiftDataTrigger",
69
+ {
70
+ "statement_id": self.statement_id,
71
+ "task_id": self.task_id,
72
+ "aws_conn_id": self.aws_conn_id,
73
+ "poll_interval": self.poll_interval,
74
+ "region_name": self.region_name,
75
+ "verify": self.verify,
76
+ "botocore_config": self.botocore_config,
77
+ },
78
+ )
79
+
80
+ @cached_property
81
+ def hook(self) -> RedshiftDataHook:
82
+ return RedshiftDataHook(
83
+ aws_conn_id=self.aws_conn_id,
84
+ region_name=self.region_name,
85
+ verify=self.verify,
86
+ config=self.botocore_config,
87
+ )
88
+
89
+ async def run(self) -> AsyncIterator[TriggerEvent]:
90
+ try:
91
+ while await self.hook.is_still_running(self.statement_id):
92
+ await asyncio.sleep(self.poll_interval)
93
+
94
+ is_finished = await self.hook.check_query_is_finished_async(self.statement_id)
95
+ if is_finished:
96
+ response = {"status": "success", "statement_id": self.statement_id}
97
+ else:
98
+ response = {
99
+ "status": "error",
100
+ "statement_id": self.statement_id,
101
+ "message": f"{self.task_id} failed",
102
+ }
103
+ yield TriggerEvent(response)
104
+ except (RedshiftDataQueryFailedError, RedshiftDataQueryAbortedError) as error:
105
+ response = {
106
+ "status": "error",
107
+ "statement_id": self.statement_id,
108
+ "message": str(error),
109
+ "type": FAILED_STATE if isinstance(error, RedshiftDataQueryFailedError) else ABORTED_STATE,
110
+ }
111
+ yield TriggerEvent(response)
112
+ except Exception as error:
113
+ yield TriggerEvent({"status": "error", "statement_id": self.statement_id, "message": str(error)})
@@ -39,6 +39,7 @@ class S3KeyTrigger(BaseTrigger):
39
39
  :param wildcard_match: whether the bucket_key should be interpreted as a
40
40
  Unix wildcard pattern
41
41
  :param aws_conn_id: reference to the s3 connection
42
+ :param use_regex: whether to use regex to check bucket
42
43
  :param hook_params: params for hook its optional
43
44
  """
44
45
 
@@ -50,6 +51,7 @@ class S3KeyTrigger(BaseTrigger):
50
51
  aws_conn_id: str = "aws_default",
51
52
  poke_interval: float = 5.0,
52
53
  should_check_fn: bool = False,
54
+ use_regex: bool = False,
53
55
  **hook_params: Any,
54
56
  ):
55
57
  super().__init__()
@@ -60,6 +62,7 @@ class S3KeyTrigger(BaseTrigger):
60
62
  self.hook_params = hook_params
61
63
  self.poke_interval = poke_interval
62
64
  self.should_check_fn = should_check_fn
65
+ self.use_regex = use_regex
63
66
 
64
67
  def serialize(self) -> tuple[str, dict[str, Any]]:
65
68
  """Serialize S3KeyTrigger arguments and classpath."""
@@ -73,6 +76,7 @@ class S3KeyTrigger(BaseTrigger):
73
76
  "hook_params": self.hook_params,
74
77
  "poke_interval": self.poke_interval,
75
78
  "should_check_fn": self.should_check_fn,
79
+ "use_regex": self.use_regex,
76
80
  },
77
81
  )
78
82
 
@@ -86,7 +90,7 @@ class S3KeyTrigger(BaseTrigger):
86
90
  async with self.hook.async_conn as client:
87
91
  while True:
88
92
  if await self.hook.check_key_async(
89
- client, self.bucket_name, self.bucket_key, self.wildcard_match
93
+ client, self.bucket_name, self.bucket_key, self.wildcard_match, self.use_regex
90
94
  ):
91
95
  if self.should_check_fn:
92
96
  s3_objects = await self.hook.get_files_async(
@@ -94,10 +98,11 @@ class S3KeyTrigger(BaseTrigger):
94
98
  )
95
99
  await asyncio.sleep(self.poke_interval)
96
100
  yield TriggerEvent({"status": "running", "files": s3_objects})
97
- else:
98
- yield TriggerEvent({"status": "success"})
99
- await asyncio.sleep(self.poke_interval)
100
101
 
102
+ yield TriggerEvent({"status": "success"})
103
+
104
+ self.log.info("Sleeping for %s seconds", self.poke_interval)
105
+ await asyncio.sleep(self.poke_interval)
101
106
  except Exception as e:
102
107
  yield TriggerEvent({"status": "error", "message": str(e)})
103
108
 
@@ -28,8 +28,9 @@ def get_provider_info():
28
28
  "name": "Amazon",
29
29
  "description": "Amazon integration (including `Amazon Web Services (AWS) <https://aws.amazon.com/>`__).\n",
30
30
  "state": "ready",
31
- "source-date-epoch": 1704610260,
31
+ "source-date-epoch": 1705911803,
32
32
  "versions": [
33
+ "8.17.0",
33
34
  "8.16.0",
34
35
  "8.15.0",
35
36
  "8.14.0",
@@ -86,21 +87,42 @@ def get_provider_info():
86
87
  "apache-airflow>=2.6.0",
87
88
  "apache-airflow-providers-common-sql>=1.3.1",
88
89
  "apache-airflow-providers-http",
89
- "boto3>=1.28.0",
90
- "botocore>=1.31.0",
90
+ "boto3>=1.33.0",
91
+ "botocore>=1.33.0",
91
92
  "inflection>=0.5.1",
92
93
  "watchtower>=2.0.1,<4",
93
94
  "jsonpath_ng>=1.5.3",
94
- "redshift_connector>=2.0.888",
95
+ "redshift_connector>=2.0.918",
95
96
  "sqlalchemy_redshift>=0.8.6",
96
97
  "asgiref",
98
+ "PyAthena>=3.0.10",
99
+ ],
100
+ "additional-extras": [
101
+ {"name": "pandas", "dependencies": ["pandas>=1.2.5"]},
102
+ {"name": "aiobotocore", "dependencies": ["aiobotocore[boto3]>=2.5.3"]},
103
+ {"name": "cncf.kubernetes", "dependencies": ["apache-airflow-providers-cncf-kubernetes>=7.2.0"]},
104
+ {"name": "s3fs", "dependencies": ["s3fs>=2023.10.0"]},
105
+ {"name": "python3-saml", "dependencies": ["python3-saml>=1.16.0"]},
106
+ ],
107
+ "devel-dependencies": [
108
+ "aiobotocore>=2.7.0",
109
+ "aws_xray_sdk>=2.12.0",
110
+ "moto[cloudformation,glue]>=4.2.12",
111
+ "mypy-boto3-appflow>=1.33.0",
112
+ "mypy-boto3-rds>=1.33.0",
113
+ "mypy-boto3-redshift-data>=1.33.0",
114
+ "mypy-boto3-s3>=1.33.0",
115
+ "s3fs>=2023.10.0",
97
116
  ],
98
117
  "integrations": [
99
118
  {
100
119
  "integration-name": "Amazon Athena",
101
120
  "external-doc-url": "https://aws.amazon.com/athena/",
102
121
  "logo": "/integration-logos/aws/Amazon-Athena_light-bg@4x.png",
103
- "how-to-guide": ["/docs/apache-airflow-providers-amazon/operators/athena.rst"],
122
+ "how-to-guide": [
123
+ "/docs/apache-airflow-providers-amazon/operators/athena/athena_boto.rst",
124
+ "/docs/apache-airflow-providers-amazon/operators/athena/athena_sql.rst",
125
+ ],
104
126
  "tags": ["aws"],
105
127
  },
106
128
  {
@@ -564,7 +586,10 @@ def get_provider_info():
564
586
  "hooks": [
565
587
  {
566
588
  "integration-name": "Amazon Athena",
567
- "python-modules": ["airflow.providers.amazon.aws.hooks.athena"],
589
+ "python-modules": [
590
+ "airflow.providers.amazon.aws.hooks.athena",
591
+ "airflow.providers.amazon.aws.hooks.athena_sql",
592
+ ],
568
593
  },
569
594
  {
570
595
  "integration-name": "Amazon Chime",
@@ -729,7 +754,10 @@ def get_provider_info():
729
754
  },
730
755
  {
731
756
  "integration-name": "Amazon Redshift",
732
- "python-modules": ["airflow.providers.amazon.aws.triggers.redshift_cluster"],
757
+ "python-modules": [
758
+ "airflow.providers.amazon.aws.triggers.redshift_cluster",
759
+ "airflow.providers.amazon.aws.triggers.redshift_data",
760
+ ],
733
761
  },
734
762
  {
735
763
  "integration-name": "Amazon SageMaker",
@@ -927,6 +955,10 @@ def get_provider_info():
927
955
  "hook-class-name": "airflow.providers.amazon.aws.hooks.redshift_sql.RedshiftSQLHook",
928
956
  "connection-type": "redshift",
929
957
  },
958
+ {
959
+ "hook-class-name": "airflow.providers.amazon.aws.hooks.athena_sql.AthenaSQLHook",
960
+ "connection-type": "athena",
961
+ },
930
962
  ],
931
963
  "notifications": [
932
964
  "airflow.providers.amazon.aws.notifications.chime.ChimeNotifier",
@@ -941,13 +973,6 @@ def get_provider_info():
941
973
  "airflow.providers.amazon.aws.log.s3_task_handler.S3TaskHandler",
942
974
  "airflow.providers.amazon.aws.log.cloudwatch_task_handler.CloudwatchTaskHandler",
943
975
  ],
944
- "additional-extras": [
945
- {"name": "pandas", "dependencies": ["pandas>=0.17.1"]},
946
- {"name": "aiobotocore", "dependencies": ["aiobotocore[boto3]>=2.5.3"]},
947
- {"name": "cncf.kubernetes", "dependencies": ["apache-airflow-providers-cncf-kubernetes>=7.2.0"]},
948
- {"name": "s3fs", "dependencies": ["s3fs>=2023.10.0"]},
949
- {"name": "python3-saml", "dependencies": ["python3-saml>=1.16.0"]},
950
- ],
951
976
  "config": {
952
977
  "aws": {
953
978
  "description": "This section contains settings for Amazon Web Services (AWS) integration.",
@@ -999,6 +1024,13 @@ def get_provider_info():
999
1024
  "example": "ecs_executor_cluster",
1000
1025
  "default": None,
1001
1026
  },
1027
+ "capacity_provider_strategy": {
1028
+ "description": "The capacity provider strategy to use for the task.\n\nIf a Capacity Provider Strategy is specified, the Launch Type parameter must be omitted. If\nno Capacity Provider Strategy or Launch Type is specified, the Default CapacityProvider Strategy\nfor the cluster is used, if present.\n\nWhen you use cluster auto scaling, you must specify Capacity Provider Strategy and not Launch Type.\n",
1029
+ "version_added": "8.17",
1030
+ "type": "string",
1031
+ "example": "[{'capacityProvider': 'cp1', 'weight': 5}, {'capacityProvider': 'cp2', 'weight': 1}]",
1032
+ "default": None,
1033
+ },
1002
1034
  "container_name": {
1003
1035
  "description": "Name of the container that will be used to execute Airflow tasks via the ECS executor.\nThe container should be specified in the ECS Task Definition and will receive an airflow\nCLI command as an additional parameter to its entrypoint. For more info see url to Boto3\ndocs above. Required.\n",
1004
1036
  "version_added": "8.10",
@@ -1007,11 +1039,11 @@ def get_provider_info():
1007
1039
  "default": None,
1008
1040
  },
1009
1041
  "launch_type": {
1010
- "description": "Launch type can either be 'FARGATE' OR 'EC2'. For more info see url to\nBoto3 docs above.\n\nIf the launch type is EC2, the executor will attempt to place tasks on\nempty EC2 instances. If there are no EC2 instances available, no task\nis placed and this function will be called again in the next heart-beat.\n\nIf the launch type is FARGATE, this will run the tasks on new AWS Fargate\ninstances.\n",
1042
+ "description": "Launch type can either be 'FARGATE' OR 'EC2'. For more info see url to\nBoto3 docs above.\n\nIf a Launch Type is specified, the Capacity Provider Strategy parameter must be omitted. If\nno Capacity Provider Strategy or Launch Type is specified, the Default Capacity Provider Strategy\nfor the cluster is used, if present.\n\nIf the launch type is EC2, the executor will attempt to place tasks on\nempty EC2 instances. If there are no EC2 instances available, no task\nis placed and this function will be called again in the next heart-beat.\n\nIf the launch type is FARGATE, this will run the tasks on new AWS Fargate\ninstances.\n",
1011
1043
  "version_added": "8.10",
1012
1044
  "type": "string",
1013
1045
  "example": "FARGATE",
1014
- "default": "FARGATE",
1046
+ "default": None,
1015
1047
  },
1016
1048
  "platform_version": {
1017
1049
  "description": "The platform version the task uses. A platform version is only specified\nfor tasks hosted on Fargate. If one isn't specified, the LATEST platform\nversion is used.\n",
@@ -1081,6 +1113,13 @@ def get_provider_info():
1081
1113
  "example": "aws_default",
1082
1114
  "default": "aws_default",
1083
1115
  },
1116
+ "region_name": {
1117
+ "description": "The name of the AWS Region where Amazon Verified Permissions is configured. Required.\n",
1118
+ "version_added": "8.10",
1119
+ "type": "string",
1120
+ "example": "us-east-1",
1121
+ "default": None,
1122
+ },
1084
1123
  "saml_metadata_url": {
1085
1124
  "description": "SAML metadata XML file provided by AWS Identity Center.\nThis URL can be found in the AWS Identity Center console. Required.\n",
1086
1125
  "version_added": "8.12.0",