apache-airflow-providers-amazon 8.27.0rc1__py3-none-any.whl → 8.28.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 (40) hide show
  1. airflow/providers/amazon/__init__.py +3 -3
  2. airflow/providers/amazon/aws/hooks/batch_client.py +3 -0
  3. airflow/providers/amazon/aws/hooks/rds.py +2 -2
  4. airflow/providers/amazon/aws/operators/batch.py +8 -0
  5. airflow/providers/amazon/aws/operators/emr.py +23 -23
  6. airflow/providers/amazon/aws/operators/redshift_cluster.py +67 -51
  7. airflow/providers/amazon/aws/operators/redshift_data.py +3 -3
  8. airflow/providers/amazon/aws/operators/sagemaker.py +4 -15
  9. airflow/providers/amazon/aws/sensors/athena.py +2 -6
  10. airflow/providers/amazon/aws/sensors/batch.py +7 -33
  11. airflow/providers/amazon/aws/sensors/bedrock.py +1 -4
  12. airflow/providers/amazon/aws/sensors/cloud_formation.py +2 -11
  13. airflow/providers/amazon/aws/sensors/comprehend.py +1 -7
  14. airflow/providers/amazon/aws/sensors/dms.py +5 -11
  15. airflow/providers/amazon/aws/sensors/ec2.py +2 -6
  16. airflow/providers/amazon/aws/sensors/ecs.py +4 -6
  17. airflow/providers/amazon/aws/sensors/eks.py +5 -7
  18. airflow/providers/amazon/aws/sensors/emr.py +9 -36
  19. airflow/providers/amazon/aws/sensors/glacier.py +2 -6
  20. airflow/providers/amazon/aws/sensors/glue.py +0 -9
  21. airflow/providers/amazon/aws/sensors/glue_catalog_partition.py +2 -6
  22. airflow/providers/amazon/aws/sensors/glue_crawler.py +2 -6
  23. airflow/providers/amazon/aws/sensors/kinesis_analytics.py +1 -4
  24. airflow/providers/amazon/aws/sensors/lambda_function.py +4 -6
  25. airflow/providers/amazon/aws/sensors/opensearch_serverless.py +1 -4
  26. airflow/providers/amazon/aws/sensors/quicksight.py +2 -5
  27. airflow/providers/amazon/aws/sensors/redshift_cluster.py +2 -6
  28. airflow/providers/amazon/aws/sensors/s3.py +3 -14
  29. airflow/providers/amazon/aws/sensors/sagemaker.py +4 -6
  30. airflow/providers/amazon/aws/sensors/sqs.py +3 -11
  31. airflow/providers/amazon/aws/sensors/step_function.py +2 -6
  32. airflow/providers/amazon/aws/transfers/dynamodb_to_s3.py +41 -10
  33. airflow/providers/amazon/aws/triggers/eks.py +2 -4
  34. airflow/providers/amazon/aws/triggers/sagemaker.py +9 -1
  35. airflow/providers/amazon/aws/utils/task_log_fetcher.py +12 -0
  36. airflow/providers/amazon/get_provider_info.py +3 -2
  37. {apache_airflow_providers_amazon-8.27.0rc1.dist-info → apache_airflow_providers_amazon-8.28.0.dist-info}/METADATA +12 -12
  38. {apache_airflow_providers_amazon-8.27.0rc1.dist-info → apache_airflow_providers_amazon-8.28.0.dist-info}/RECORD +40 -40
  39. {apache_airflow_providers_amazon-8.27.0rc1.dist-info → apache_airflow_providers_amazon-8.28.0.dist-info}/WHEEL +0 -0
  40. {apache_airflow_providers_amazon-8.27.0rc1.dist-info → apache_airflow_providers_amazon-8.28.0.dist-info}/entry_points.txt +0 -0
@@ -22,7 +22,7 @@ from abc import abstractmethod
22
22
  from functools import cached_property
23
23
  from typing import TYPE_CHECKING, Sequence
24
24
 
25
- from airflow.exceptions import AirflowException, AirflowSkipException
25
+ from airflow.exceptions import AirflowException
26
26
  from airflow.providers.amazon.aws.hooks.eks import (
27
27
  ClusterStates,
28
28
  EksHook,
@@ -106,12 +106,10 @@ class EksBaseSensor(BaseSensorOperator):
106
106
  state = self.get_state()
107
107
  self.log.info("Current state: %s", state)
108
108
  if state in (self.get_terminal_states() - {self.target_state}):
109
- # If we reach a terminal state which is not the target state:
110
- # TODO: remove this if check when min_airflow_version is set to higher than 2.7.1
111
- message = f"Terminal state reached. Current state: {state}, Expected state: {self.target_state}"
112
- if self.soft_fail:
113
- raise AirflowSkipException(message)
114
- raise AirflowException(message)
109
+ # If we reach a terminal state which is not the target state
110
+ raise AirflowException(
111
+ f"Terminal state reached. Current state: {state}, Expected state: {self.target_state}"
112
+ )
115
113
  return state == self.target_state
116
114
 
117
115
  @abstractmethod
@@ -27,7 +27,6 @@ from airflow.configuration import conf
27
27
  from airflow.exceptions import (
28
28
  AirflowException,
29
29
  AirflowProviderDeprecationWarning,
30
- AirflowSkipException,
31
30
  )
32
31
  from airflow.providers.amazon.aws.hooks.emr import EmrContainerHook, EmrHook, EmrServerlessHook
33
32
  from airflow.providers.amazon.aws.links.emr import EmrClusterLink, EmrLogsLink, get_log_uri
@@ -91,11 +90,7 @@ class EmrBaseSensor(BaseSensorOperator):
91
90
  return True
92
91
 
93
92
  if state in self.failed_states:
94
- # TODO: remove this if check when min_airflow_version is set to higher than 2.7.1
95
- message = f"EMR job failed: {self.failure_message_from_response(response)}"
96
- if self.soft_fail:
97
- raise AirflowSkipException(message)
98
- raise AirflowException(message)
93
+ raise AirflowException(f"EMR job failed: {self.failure_message_from_response(response)}")
99
94
 
100
95
  return False
101
96
 
@@ -172,11 +167,9 @@ class EmrServerlessJobSensor(BaseSensorOperator):
172
167
  state = response["jobRun"]["state"]
173
168
 
174
169
  if state in EmrServerlessHook.JOB_FAILURE_STATES:
175
- failure_message = f"EMR Serverless job failed: {self.failure_message_from_response(response)}"
176
- # TODO: remove this if check when min_airflow_version is set to higher than 2.7.1
177
- if self.soft_fail:
178
- raise AirflowSkipException(failure_message)
179
- raise AirflowException(failure_message)
170
+ raise AirflowException(
171
+ f"EMR Serverless job failed: {self.failure_message_from_response(response)}"
172
+ )
180
173
 
181
174
  return state in self.target_states
182
175
 
@@ -234,13 +227,9 @@ class EmrServerlessApplicationSensor(BaseSensorOperator):
234
227
  state = response["application"]["state"]
235
228
 
236
229
  if state in EmrServerlessHook.APPLICATION_FAILURE_STATES:
237
- # TODO: remove this if check when min_airflow_version is set to higher than 2.7.1
238
- failure_message = (
230
+ raise AirflowException(
239
231
  f"EMR Serverless application failed: {self.failure_message_from_response(response)}"
240
232
  )
241
- if self.soft_fail:
242
- raise AirflowSkipException(failure_message)
243
- raise AirflowException(failure_message)
244
233
 
245
234
  return state in self.target_states
246
235
 
@@ -328,11 +317,7 @@ class EmrContainerSensor(BaseSensorOperator):
328
317
  )
329
318
 
330
319
  if state in self.FAILURE_STATES:
331
- # TODO: remove this if check when min_airflow_version is set to higher than 2.7.1
332
- message = "EMR Containers sensor failed"
333
- if self.soft_fail:
334
- raise AirflowSkipException(message)
335
- raise AirflowException(message)
320
+ raise AirflowException("EMR Containers sensor failed")
336
321
 
337
322
  if state in self.INTERMEDIATE_STATES:
338
323
  return False
@@ -370,11 +355,7 @@ class EmrContainerSensor(BaseSensorOperator):
370
355
  event = validate_execute_complete_event(event)
371
356
 
372
357
  if event["status"] != "success":
373
- # TODO: remove this if check when min_airflow_version is set to higher than 2.7.1
374
- message = f"Error while running job: {event}"
375
- if self.soft_fail:
376
- raise AirflowSkipException(message)
377
- raise AirflowException(message)
358
+ raise AirflowException(f"Error while running job: {event}")
378
359
 
379
360
  self.log.info("Job completed.")
380
361
 
@@ -563,11 +544,7 @@ class EmrJobFlowSensor(EmrBaseSensor):
563
544
  event = validate_execute_complete_event(event)
564
545
 
565
546
  if event["status"] != "success":
566
- # TODO: remove this if check when min_airflow_version is set to higher than 2.7.1
567
- message = f"Error while running job: {event}"
568
- if self.soft_fail:
569
- raise AirflowSkipException(message)
570
- raise AirflowException(message)
547
+ raise AirflowException(f"Error while running job: {event}")
571
548
  self.log.info("Job completed.")
572
549
 
573
550
 
@@ -696,10 +673,6 @@ class EmrStepSensor(EmrBaseSensor):
696
673
  event = validate_execute_complete_event(event)
697
674
 
698
675
  if event["status"] != "success":
699
- # TODO: remove this if check when min_airflow_version is set to higher than 2.7.1
700
- message = f"Error while running job: {event}"
701
- if self.soft_fail:
702
- raise AirflowSkipException(message)
703
- raise AirflowException(message)
676
+ raise AirflowException(f"Error while running job: {event}")
704
677
 
705
678
  self.log.info("Job %s completed.", self.job_flow_id)
@@ -20,7 +20,7 @@ from __future__ import annotations
20
20
  from enum import Enum
21
21
  from typing import TYPE_CHECKING, Any, Sequence
22
22
 
23
- from airflow.exceptions import AirflowException, AirflowSkipException
23
+ from airflow.exceptions import AirflowException
24
24
  from airflow.providers.amazon.aws.hooks.glacier import GlacierHook
25
25
  from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
26
26
  from airflow.providers.amazon.aws.utils.mixins import aws_template_fields
@@ -93,10 +93,6 @@ class GlacierJobOperationSensor(AwsBaseSensor[GlacierHook]):
93
93
  self.log.warning("Code status: %s", response["StatusCode"])
94
94
  return False
95
95
  else:
96
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
97
- message = (
96
+ raise AirflowException(
98
97
  f'Sensor failed. Job status: {response["Action"]}, code status: {response["StatusCode"]}'
99
98
  )
100
- if self.soft_fail:
101
- raise AirflowSkipException(message)
102
- raise AirflowException(message)
@@ -86,9 +86,6 @@ class GlueJobSensor(BaseSensorOperator):
86
86
  elif job_state in self.errored_states:
87
87
  job_error_message = "Exiting Job %s Run State: %s", self.run_id, job_state
88
88
  self.log.info(job_error_message)
89
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
90
- if self.soft_fail:
91
- raise AirflowSkipException(job_error_message)
92
89
  raise AirflowException(job_error_message)
93
90
  else:
94
91
  return False
@@ -223,9 +220,6 @@ class GlueDataQualityRuleSetEvaluationRunSensor(AwsBaseSensor[GlueDataQualityHoo
223
220
  f": {response.get('ErrorString')}"
224
221
  )
225
222
  self.log.info(job_error_message)
226
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
227
- if self.soft_fail:
228
- raise AirflowSkipException(job_error_message)
229
223
  raise AirflowException(job_error_message)
230
224
  else:
231
225
  return False
@@ -343,9 +337,6 @@ class GlueDataQualityRuleRecommendationRunSensor(AwsBaseSensor[GlueDataQualityHo
343
337
  f": {response.get('ErrorString')}"
344
338
  )
345
339
  self.log.info(job_error_message)
346
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
347
- if self.soft_fail:
348
- raise AirflowSkipException(job_error_message)
349
340
  raise AirflowException(job_error_message)
350
341
  else:
351
342
  return False
@@ -23,7 +23,7 @@ from typing import TYPE_CHECKING, Any, Sequence
23
23
  from deprecated import deprecated
24
24
 
25
25
  from airflow.configuration import conf
26
- from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
26
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
27
27
  from airflow.providers.amazon.aws.hooks.glue_catalog import GlueCatalogHook
28
28
  from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
29
29
  from airflow.providers.amazon.aws.triggers.glue import GlueCatalogPartitionTrigger
@@ -127,11 +127,7 @@ class GlueCatalogPartitionSensor(AwsBaseSensor[GlueCatalogHook]):
127
127
  event = validate_execute_complete_event(event)
128
128
 
129
129
  if event["status"] != "success":
130
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
131
- message = f"Trigger error: event is {event}"
132
- if self.soft_fail:
133
- raise AirflowSkipException(message)
134
- raise AirflowException(message)
130
+ raise AirflowException(f"Trigger error: event is {event}")
135
131
  self.log.info("Partition exists in the Glue Catalog")
136
132
 
137
133
  @deprecated(reason="use `hook` property instead.", category=AirflowProviderDeprecationWarning)
@@ -21,7 +21,7 @@ from typing import TYPE_CHECKING, Sequence
21
21
 
22
22
  from deprecated import deprecated
23
23
 
24
- from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
24
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
25
25
  from airflow.providers.amazon.aws.hooks.glue_crawler import GlueCrawlerHook
26
26
  from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
27
27
  from airflow.providers.amazon.aws.utils.mixins import aws_template_fields
@@ -75,11 +75,7 @@ class GlueCrawlerSensor(AwsBaseSensor[GlueCrawlerHook]):
75
75
  self.log.info("Status: %s", crawler_status)
76
76
  return True
77
77
  else:
78
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
79
- message = f"Status: {crawler_status}"
80
- if self.soft_fail:
81
- raise AirflowSkipException(message)
82
- raise AirflowException(message)
78
+ raise AirflowException(f"Status: {crawler_status}")
83
79
  else:
84
80
  return False
85
81
 
@@ -19,7 +19,7 @@ from __future__ import annotations
19
19
  from typing import TYPE_CHECKING, Any, Sequence
20
20
 
21
21
  from airflow.configuration import conf
22
- from airflow.exceptions import AirflowException, AirflowSkipException
22
+ from airflow.exceptions import AirflowException
23
23
  from airflow.providers.amazon.aws.hooks.kinesis_analytics import KinesisAnalyticsV2Hook
24
24
  from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
25
25
  from airflow.providers.amazon.aws.triggers.kinesis_analytics import (
@@ -80,9 +80,6 @@ class KinesisAnalyticsV2BaseSensor(AwsBaseSensor[KinesisAnalyticsV2Hook]):
80
80
  )
81
81
 
82
82
  if status in self.FAILURE_STATES:
83
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
84
- if self.soft_fail:
85
- raise AirflowSkipException(self.FAILURE_MESSAGE)
86
83
  raise AirflowException(self.FAILURE_MESSAGE)
87
84
 
88
85
  if status in self.SUCCESS_STATES:
@@ -19,7 +19,7 @@ from __future__ import annotations
19
19
 
20
20
  from typing import TYPE_CHECKING, Any, Sequence
21
21
 
22
- from airflow.exceptions import AirflowException, AirflowSkipException
22
+ from airflow.exceptions import AirflowException
23
23
  from airflow.providers.amazon.aws.hooks.lambda_function import LambdaHook
24
24
  from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
25
25
  from airflow.providers.amazon.aws.utils import trim_none_values
@@ -78,10 +78,8 @@ class LambdaFunctionStateSensor(AwsBaseSensor[LambdaHook]):
78
78
  state = self.hook.conn.get_function(**trim_none_values(get_function_args))["Configuration"]["State"]
79
79
 
80
80
  if state in self.FAILURE_STATES:
81
- message = "Lambda function state sensor failed because the Lambda is in a failed state"
82
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
83
- if self.soft_fail:
84
- raise AirflowSkipException(message)
85
- raise AirflowException(message)
81
+ raise AirflowException(
82
+ "Lambda function state sensor failed because the Lambda is in a failed state"
83
+ )
86
84
 
87
85
  return state in self.target_states
@@ -19,7 +19,7 @@ from __future__ import annotations
19
19
  from typing import TYPE_CHECKING, Any, Sequence
20
20
 
21
21
  from airflow.configuration import conf
22
- from airflow.exceptions import AirflowException, AirflowSkipException
22
+ from airflow.exceptions import AirflowException
23
23
  from airflow.providers.amazon.aws.hooks.opensearch_serverless import OpenSearchServerlessHook
24
24
  from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
25
25
  from airflow.providers.amazon.aws.triggers.opensearch_serverless import (
@@ -104,9 +104,6 @@ class OpenSearchServerlessCollectionActiveSensor(AwsBaseSensor[OpenSearchServerl
104
104
  state = self.hook.conn.batch_get_collection(**call_args)["collectionDetails"][0]["status"]
105
105
 
106
106
  if state in self.FAILURE_STATES:
107
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
108
- if self.soft_fail:
109
- raise AirflowSkipException(self.FAILURE_MESSAGE)
110
107
  raise AirflowException(self.FAILURE_MESSAGE)
111
108
 
112
109
  if state in self.INTERMEDIATE_STATES:
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Sequence
22
22
 
23
23
  from deprecated import deprecated
24
24
 
25
- from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
25
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
26
26
  from airflow.providers.amazon.aws.hooks.quicksight import QuickSightHook
27
27
  from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
28
28
 
@@ -74,10 +74,7 @@ class QuickSightSensor(AwsBaseSensor[QuickSightHook]):
74
74
  self.log.info("QuickSight Status: %s", quicksight_ingestion_state)
75
75
  if quicksight_ingestion_state in self.errored_statuses:
76
76
  error = self.hook.get_error_info(None, self.data_set_id, self.ingestion_id)
77
- message = f"The QuickSight Ingestion failed. Error info: {error}"
78
- if self.soft_fail:
79
- raise AirflowSkipException(message)
80
- raise AirflowException(message)
77
+ raise AirflowException(f"The QuickSight Ingestion failed. Error info: {error}")
81
78
  return quicksight_ingestion_state == self.success_status
82
79
 
83
80
  @cached_property
@@ -23,7 +23,7 @@ from typing import TYPE_CHECKING, Any, Sequence
23
23
  from deprecated import deprecated
24
24
 
25
25
  from airflow.configuration import conf
26
- from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
26
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
27
27
  from airflow.providers.amazon.aws.hooks.redshift_cluster import RedshiftHook
28
28
  from airflow.providers.amazon.aws.triggers.redshift_cluster import RedshiftClusterTrigger
29
29
  from airflow.providers.amazon.aws.utils import validate_execute_complete_event
@@ -93,11 +93,7 @@ class RedshiftClusterSensor(BaseSensorOperator):
93
93
 
94
94
  status = event["status"]
95
95
  if status == "error":
96
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
97
- message = f"{event['status']}: {event['message']}"
98
- if self.soft_fail:
99
- raise AirflowSkipException(message)
100
- raise AirflowException(message)
96
+ raise AirflowException(f"{event['status']}: {event['message']}")
101
97
  elif status == "success":
102
98
  self.log.info("%s completed successfully.", self.task_id)
103
99
  self.log.info("Cluster Identifier %s is in %s state", self.cluster_identifier, self.target_status)
@@ -33,7 +33,7 @@ from airflow.providers.amazon.aws.utils import validate_execute_complete_event
33
33
  if TYPE_CHECKING:
34
34
  from airflow.utils.context import Context
35
35
 
36
- from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
36
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
37
37
  from airflow.providers.amazon.aws.hooks.s3 import S3Hook
38
38
  from airflow.providers.amazon.aws.triggers.s3 import S3KeysUnchangedTrigger, S3KeyTrigger
39
39
  from airflow.sensors.base import BaseSensorOperator, poke_mode_only
@@ -219,9 +219,6 @@ class S3KeySensor(BaseSensorOperator):
219
219
  if not found_keys:
220
220
  self._defer()
221
221
  elif event["status"] == "error":
222
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
223
- if self.soft_fail:
224
- raise AirflowSkipException(event["message"])
225
222
  raise AirflowException(event["message"])
226
223
 
227
224
  @deprecated(reason="use `hook` property instead.", category=AirflowProviderDeprecationWarning)
@@ -342,14 +339,9 @@ class S3KeysUnchangedSensor(BaseSensorOperator):
342
339
  )
343
340
  return False
344
341
 
345
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
346
- message = (
347
- f"Illegal behavior: objects were deleted in"
348
- f" {os.path.join(self.bucket_name, self.prefix)} between pokes."
342
+ raise AirflowException(
343
+ f"Illegal behavior: objects were deleted in {os.path.join(self.bucket_name, self.prefix)} between pokes."
349
344
  )
350
- if self.soft_fail:
351
- raise AirflowSkipException(message)
352
- raise AirflowException(message)
353
345
 
354
346
  if self.last_activity_time:
355
347
  self.inactivity_seconds = int((datetime.now() - self.last_activity_time).total_seconds())
@@ -411,8 +403,5 @@ class S3KeysUnchangedSensor(BaseSensorOperator):
411
403
  event = validate_execute_complete_event(event)
412
404
 
413
405
  if event and event["status"] == "error":
414
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
415
- if self.soft_fail:
416
- raise AirflowSkipException(event["message"])
417
406
  raise AirflowException(event["message"])
418
407
  return None
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Sequence
22
22
 
23
23
  from deprecated import deprecated
24
24
 
25
- from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
25
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
26
26
  from airflow.providers.amazon.aws.hooks.sagemaker import LogState, SageMakerHook
27
27
  from airflow.sensors.base import BaseSensorOperator
28
28
 
@@ -65,11 +65,9 @@ class SageMakerBaseSensor(BaseSensorOperator):
65
65
  return False
66
66
  if state in self.failed_states():
67
67
  failed_reason = self.get_failed_reason_from_response(response)
68
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
69
- message = f"Sagemaker {self.resource_type} failed for the following reason: {failed_reason}"
70
- if self.soft_fail:
71
- raise AirflowSkipException(message)
72
- raise AirflowException(message)
68
+ raise AirflowException(
69
+ f"Sagemaker {self.resource_type} failed for the following reason: {failed_reason}"
70
+ )
73
71
  return True
74
72
 
75
73
  def non_terminal_states(self) -> set[str]:
@@ -25,7 +25,7 @@ from typing import TYPE_CHECKING, Any, Collection, Sequence
25
25
  from deprecated import deprecated
26
26
 
27
27
  from airflow.configuration import conf
28
- from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
28
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
29
29
  from airflow.providers.amazon.aws.hooks.sqs import SqsHook
30
30
  from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
31
31
  from airflow.providers.amazon.aws.triggers.sqs import SqsSensorTrigger
@@ -160,11 +160,7 @@ class SqsSensor(AwsBaseSensor[SqsHook]):
160
160
  event = validate_execute_complete_event(event)
161
161
 
162
162
  if event["status"] != "success":
163
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
164
- message = f"Trigger error: event is {event}"
165
- if self.soft_fail:
166
- raise AirflowSkipException(message)
167
- raise AirflowException(message)
163
+ raise AirflowException(f"Trigger error: event is {event}")
168
164
  context["ti"].xcom_push(key="messages", value=event["message_batch"])
169
165
 
170
166
  def poll_sqs(self, sqs_conn: BaseAwsConnection) -> Collection:
@@ -221,11 +217,7 @@ class SqsSensor(AwsBaseSensor[SqsHook]):
221
217
  response = self.hook.conn.delete_message_batch(QueueUrl=self.sqs_queue, Entries=entries)
222
218
 
223
219
  if "Successful" not in response:
224
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
225
- error_message = f"Delete SQS Messages failed {response} for messages {messages}"
226
- if self.soft_fail:
227
- raise AirflowSkipException(error_message)
228
- raise AirflowException(error_message)
220
+ raise AirflowException(f"Delete SQS Messages failed {response} for messages {messages}")
229
221
  if message_batch:
230
222
  context["ti"].xcom_push(key="messages", value=message_batch)
231
223
  return True
@@ -21,7 +21,7 @@ from typing import TYPE_CHECKING, Sequence
21
21
 
22
22
  from deprecated import deprecated
23
23
 
24
- from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning, AirflowSkipException
24
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
25
25
  from airflow.providers.amazon.aws.hooks.step_function import StepFunctionHook
26
26
  from airflow.providers.amazon.aws.sensors.base_aws import AwsBaseSensor
27
27
  from airflow.providers.amazon.aws.utils.mixins import aws_template_fields
@@ -76,11 +76,7 @@ class StepFunctionExecutionSensor(AwsBaseSensor[StepFunctionHook]):
76
76
  output = json.loads(execution_status["output"]) if "output" in execution_status else None
77
77
 
78
78
  if state in self.FAILURE_STATES:
79
- # TODO: remove this if block when min_airflow_version is set to higher than 2.7.1
80
- message = f"Step Function sensor failed. State Machine Output: {output}"
81
- if self.soft_fail:
82
- raise AirflowSkipException(message)
83
- raise AirflowException(message)
79
+ raise AirflowException(f"Step Function sensor failed. State Machine Output: {output}")
84
80
 
85
81
  if state in self.INTERMEDIATE_STATES:
86
82
  return False
@@ -32,6 +32,7 @@ from airflow.providers.amazon.aws.hooks.base_aws import AwsBaseHook
32
32
  from airflow.providers.amazon.aws.hooks.dynamodb import DynamoDBHook
33
33
  from airflow.providers.amazon.aws.hooks.s3 import S3Hook
34
34
  from airflow.providers.amazon.aws.transfers.base import AwsToAwsBaseOperator
35
+ from airflow.utils.helpers import prune_dict
35
36
 
36
37
  if TYPE_CHECKING:
37
38
  from airflow.utils.context import Context
@@ -89,10 +90,13 @@ class DynamoDBToS3Operator(AwsToAwsBaseOperator):
89
90
  <https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.scan>
90
91
  :param s3_key_prefix: Prefix of s3 object key
91
92
  :param process_func: How we transform a dynamodb item to bytes. By default, we dump the json
93
+ :param point_in_time_export: Boolean value indicating the operator to use 'scan' or 'point in time export'
92
94
  :param export_time: Time in the past from which to export table data, counted in seconds from the start of
93
95
  the Unix epoch. The table export will be a snapshot of the table's state at this point in time.
94
96
  :param export_format: The format for the exported data. Valid values for ExportFormat are DYNAMODB_JSON
95
97
  or ION.
98
+ :param export_table_to_point_in_time_kwargs: extra parameters for the boto3
99
+ `export_table_to_point_in_time` function all. e.g. `ExportType`, `IncrementalExportSpecification`
96
100
  :param check_interval: The amount of time in seconds to wait between attempts. Only if ``export_time`` is
97
101
  provided.
98
102
  :param max_attempts: The maximum number of attempts to be made. Only if ``export_time`` is provided.
@@ -107,12 +111,14 @@ class DynamoDBToS3Operator(AwsToAwsBaseOperator):
107
111
  "s3_key_prefix",
108
112
  "export_time",
109
113
  "export_format",
114
+ "export_table_to_point_in_time_kwargs",
110
115
  "check_interval",
111
116
  "max_attempts",
112
117
  )
113
118
 
114
119
  template_fields_renderers = {
115
120
  "dynamodb_scan_kwargs": "json",
121
+ "export_table_to_point_in_time_kwargs": "json",
116
122
  }
117
123
 
118
124
  def __init__(
@@ -120,12 +126,14 @@ class DynamoDBToS3Operator(AwsToAwsBaseOperator):
120
126
  *,
121
127
  dynamodb_table_name: str,
122
128
  s3_bucket_name: str,
123
- file_size: int,
129
+ file_size: int = 1000,
124
130
  dynamodb_scan_kwargs: dict[str, Any] | None = None,
125
131
  s3_key_prefix: str = "",
126
132
  process_func: Callable[[dict[str, Any]], bytes] = _convert_item_to_json_bytes,
133
+ point_in_time_export: bool = False,
127
134
  export_time: datetime | None = None,
128
135
  export_format: str = "DYNAMODB_JSON",
136
+ export_table_to_point_in_time_kwargs: dict | None = None,
129
137
  check_interval: int = 30,
130
138
  max_attempts: int = 60,
131
139
  **kwargs,
@@ -137,8 +145,10 @@ class DynamoDBToS3Operator(AwsToAwsBaseOperator):
137
145
  self.dynamodb_scan_kwargs = dynamodb_scan_kwargs
138
146
  self.s3_bucket_name = s3_bucket_name
139
147
  self.s3_key_prefix = s3_key_prefix
148
+ self.point_in_time_export = point_in_time_export
140
149
  self.export_time = export_time
141
150
  self.export_format = export_format
151
+ self.export_table_to_point_in_time_kwargs = export_table_to_point_in_time_kwargs or {}
142
152
  self.check_interval = check_interval
143
153
  self.max_attempts = max_attempts
144
154
 
@@ -148,29 +158,50 @@ class DynamoDBToS3Operator(AwsToAwsBaseOperator):
148
158
  return DynamoDBHook(aws_conn_id=self.source_aws_conn_id)
149
159
 
150
160
  def execute(self, context: Context) -> None:
151
- if self.export_time:
161
+ # There are 2 separate export to point in time configuration:
162
+ # 1. Full export, which takes the export_time arg.
163
+ # 2. Incremental export, which takes the incremental_export_... args
164
+ # Hence export time could not be used as the proper indicator for the `_export_table_to_point_in_time`
165
+ # function. This change introduces a new boolean, as the indicator for whether the operator scans
166
+ # and export entire data or using the point in time functionality.
167
+ if self.point_in_time_export or self.export_time:
152
168
  self._export_table_to_point_in_time()
153
169
  else:
154
170
  self._export_entire_data()
155
171
 
156
172
  def _export_table_to_point_in_time(self):
157
173
  """
158
- Export data from start of epoc till `export_time`.
174
+ Export data to point in time.
159
175
 
176
+ Full export exports data from start of epoc till `export_time`.
160
177
  Table export will be a snapshot of the table's state at this point in time.
178
+
179
+ Incremental export exports the data from a specific datetime to a specific datetime
180
+
181
+
182
+ Note: S3BucketOwner is a required parameter when exporting to a S3 bucket in another account.
161
183
  """
162
184
  if self.export_time and self.export_time > datetime.now(self.export_time.tzinfo):
163
185
  raise ValueError("The export_time parameter cannot be a future time.")
164
186
 
165
187
  client = self.hook.conn.meta.client
166
188
  table_description = client.describe_table(TableName=self.dynamodb_table_name)
167
- response = client.export_table_to_point_in_time(
168
- TableArn=table_description.get("Table", {}).get("TableArn"),
169
- ExportTime=self.export_time,
170
- S3Bucket=self.s3_bucket_name,
171
- S3Prefix=self.s3_key_prefix,
172
- ExportFormat=self.export_format,
173
- )
189
+
190
+ export_table_to_point_in_time_base_args = {
191
+ "TableArn": table_description.get("Table", {}).get("TableArn"),
192
+ "ExportTime": self.export_time,
193
+ "S3Bucket": self.s3_bucket_name,
194
+ "S3Prefix": self.s3_key_prefix,
195
+ "ExportFormat": self.export_format,
196
+ }
197
+ export_table_to_point_in_time_args = {
198
+ **export_table_to_point_in_time_base_args,
199
+ **self.export_table_to_point_in_time_kwargs,
200
+ }
201
+
202
+ args_filtered = prune_dict(export_table_to_point_in_time_args)
203
+
204
+ response = client.export_table_to_point_in_time(**args_filtered)
174
205
  waiter = self.hook.get_waiter("export_table")
175
206
  export_arn = response.get("ExportDescription", {}).get("ExportArn")
176
207
  waiter.wait(
@@ -318,13 +318,12 @@ class EksCreateNodegroupTrigger(AwsBaseWaiterTrigger):
318
318
  The trigger will asynchronously poll the boto3 API and wait for the
319
319
  nodegroup to be in the state specified by the waiter.
320
320
 
321
- :param waiter_name: Name of the waiter to use, for instance 'nodegroup_active' or 'nodegroup_deleted'
322
321
  :param cluster_name: The name of the EKS cluster associated with the node group.
323
322
  :param nodegroup_name: The name of the nodegroup to check.
324
323
  :param waiter_delay: The amount of time in seconds to wait between attempts.
325
324
  :param waiter_max_attempts: The maximum number of attempts to be made.
326
325
  :param aws_conn_id: The Airflow connection used for AWS credentials.
327
- :param region: Which AWS region the connection should use. (templated)
326
+ :param region_name: Which AWS region the connection should use. (templated)
328
327
  If this is None or empty then the default boto3 behaviour is used.
329
328
  """
330
329
 
@@ -366,13 +365,12 @@ class EksDeleteNodegroupTrigger(AwsBaseWaiterTrigger):
366
365
  The trigger will asynchronously poll the boto3 API and wait for the
367
366
  nodegroup to be in the state specified by the waiter.
368
367
 
369
- :param waiter_name: Name of the waiter to use, for instance 'nodegroup_active' or 'nodegroup_deleted'
370
368
  :param cluster_name: The name of the EKS cluster associated with the node group.
371
369
  :param nodegroup_name: The name of the nodegroup to check.
372
370
  :param waiter_delay: The amount of time in seconds to wait between attempts.
373
371
  :param waiter_max_attempts: The maximum number of attempts to be made.
374
372
  :param aws_conn_id: The Airflow connection used for AWS credentials.
375
- :param region: Which AWS region the connection should use. (templated)
373
+ :param region_name: Which AWS region the connection should use. (templated)
376
374
  If this is None or empty then the default boto3 behaviour is used.
377
375
  """
378
376
 
@@ -25,8 +25,9 @@ from functools import cached_property
25
25
  from typing import Any, AsyncIterator
26
26
 
27
27
  from botocore.exceptions import WaiterError
28
+ from deprecated import deprecated
28
29
 
29
- from airflow.exceptions import AirflowException
30
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
30
31
  from airflow.providers.amazon.aws.hooks.sagemaker import LogState, SageMakerHook
31
32
  from airflow.providers.amazon.aws.utils.waiter_with_logging import async_wait
32
33
  from airflow.triggers.base import BaseTrigger, TriggerEvent
@@ -199,6 +200,13 @@ class SageMakerPipelineTrigger(BaseTrigger):
199
200
  raise AirflowException("Waiter error: max attempts reached")
200
201
 
201
202
 
203
+ @deprecated(
204
+ reason=(
205
+ "`airflow.providers.amazon.aws.triggers.sagemaker.SageMakerTrainingPrintLogTrigger` "
206
+ "has been deprecated and will be removed in future. Please use ``SageMakerTrigger`` instead."
207
+ ),
208
+ category=AirflowProviderDeprecationWarning,
209
+ )
202
210
  class SageMakerTrainingPrintLogTrigger(BaseTrigger):
203
211
  """
204
212
  SageMakerTrainingPrintLogTrigger is fired as deferred class with params to run the task in triggerer.