This is an automated email from the ASF dual-hosted git repository.
o-nikolas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new dbbc8163108 Support unexposed CreateWorkflow and UpdateWorkflow inputs
in MWAA Serverless operators (#73408)
dbbc8163108 is described below
commit dbbc8163108ec7840676ecfce90a7c29c498e100
Author: Kazushi Y <[email protected]>
AuthorDate: Fri Sep 25 02:42:48 2026 +0900
Support unexposed CreateWorkflow and UpdateWorkflow inputs in MWAA
Serverless operators (#73408)
MwaaServerlessCreateWorkflowOperator reaches 6 of the 12 CreateWorkflow
request
parameters and MwaaServerlessUpdateWorkflowOperator 5 of the 9 for
UpdateWorkflow.
Neither accepts a kwargs passthrough, so NetworkConfiguration,
LoggingConfiguration, EngineVersion and TriggerMode are unreachable, along
with
EncryptionConfiguration and ClientToken on create.
Add create_workflow_kwargs and update_workflow_kwargs, following the
passthrough
convention the provider already uses for CreateCluster and
CreateApplication.
Each dict is splatted alongside the explicit parameters, so a key that
collides
with one the operator already sets raises rather than silently overriding
it.
---
.../amazon/aws/operators/mwaa_serverless.py | 41 +++++++++++--
.../amazon/aws/operators/test_mwaa_serverless.py | 70 ++++++++++++++++++++++
2 files changed, 105 insertions(+), 6 deletions(-)
diff --git
a/providers/amazon/src/airflow/providers/amazon/aws/operators/mwaa_serverless.py
b/providers/amazon/src/airflow/providers/amazon/aws/operators/mwaa_serverless.py
index 142affd82fe..d0f8883c90d 100644
---
a/providers/amazon/src/airflow/providers/amazon/aws/operators/mwaa_serverless.py
+++
b/providers/amazon/src/airflow/providers/amazon/aws/operators/mwaa_serverless.py
@@ -104,15 +104,28 @@ class
MwaaServerlessCreateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
:param role_arn: The execution role ARN. (templated)
:param description: Optional description. (templated)
:param tags: Optional tags dict.
+ :param create_workflow_kwargs: Optional parameters to pass to the
``CreateWorkflow`` API for
+ inputs this operator does not expose, such as ``NetworkConfiguration``,
+ ``LoggingConfiguration``, ``EngineVersion``, ``TriggerMode``,
``EncryptionConfiguration``
+ and ``ClientToken``. (templated)
:param if_exists: Behavior when the workflow already exists.
``"fail"`` raises an error, ``"skip"`` returns the existing ARN.
"""
aws_hook_class = AwsBaseHook
template_fields: tuple[str, ...] = aws_template_fields(
- "workflow_name", "definition_s3_location", "code", "role_arn",
"description"
+ "workflow_name",
+ "definition_s3_location",
+ "code",
+ "role_arn",
+ "description",
+ "create_workflow_kwargs",
)
- template_fields_renderers = {"definition_s3_location": "json", "code":
"json"}
+ template_fields_renderers = {
+ "definition_s3_location": "json",
+ "code": "json",
+ "create_workflow_kwargs": "json",
+ }
def __init__(
self,
@@ -123,6 +136,7 @@ class
MwaaServerlessCreateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
role_arn: str,
description: str | None = None,
tags: dict[str, str] | None = None,
+ create_workflow_kwargs: dict[str, Any] | None = None,
if_exists: Literal["fail", "skip"] = "skip",
**kwargs,
) -> None:
@@ -133,6 +147,7 @@ class
MwaaServerlessCreateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
self.role_arn = role_arn
self.description = description
self.tags = tags
+ self.create_workflow_kwargs = create_workflow_kwargs or {}
self.if_exists = if_exists
@property
@@ -152,7 +167,7 @@ class
MwaaServerlessCreateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
}
)
try:
- response = self.hook.conn.create_workflow(**kwargs)
+ response = self.hook.conn.create_workflow(**kwargs,
**self.create_workflow_kwargs)
workflow_arn = response["WorkflowArn"]
except ClientError as e:
if e.response["Error"]["Code"] == "ConflictException" and
self.if_exists == "skip":
@@ -185,13 +200,25 @@ class
MwaaServerlessUpdateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
optionally ``VersionId``. (templated)
:param role_arn: The execution role ARN. (templated)
:param description: Optional updated description. (templated)
+ :param update_workflow_kwargs: Optional parameters to pass to the
``UpdateWorkflow`` API for
+ inputs this operator does not expose, such as ``NetworkConfiguration``,
+ ``LoggingConfiguration``, ``EngineVersion`` and ``TriggerMode``.
(templated)
"""
aws_hook_class = AwsBaseHook
template_fields: tuple[str, ...] = aws_template_fields(
- "workflow_arn", "definition_s3_location", "code", "role_arn",
"description"
+ "workflow_arn",
+ "definition_s3_location",
+ "code",
+ "role_arn",
+ "description",
+ "update_workflow_kwargs",
)
- template_fields_renderers = {"definition_s3_location": "json", "code":
"json"}
+ template_fields_renderers = {
+ "definition_s3_location": "json",
+ "code": "json",
+ "update_workflow_kwargs": "json",
+ }
def __init__(
self,
@@ -201,6 +228,7 @@ class
MwaaServerlessUpdateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
code: dict[str, Any] | None = None,
role_arn: str,
description: str | None = None,
+ update_workflow_kwargs: dict[str, Any] | None = None,
**kwargs,
) -> None:
super().__init__(**kwargs)
@@ -209,6 +237,7 @@ class
MwaaServerlessUpdateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
self.code = code
self.role_arn = role_arn
self.description = description
+ self.update_workflow_kwargs = update_workflow_kwargs or {}
@property
def _hook_parameters(self) -> dict[str, Any]:
@@ -225,7 +254,7 @@ class
MwaaServerlessUpdateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
"Description": self.description,
}
)
- response = self.hook.conn.update_workflow(**kwargs)
+ response = self.hook.conn.update_workflow(**kwargs,
**self.update_workflow_kwargs)
workflow_arn = response["WorkflowArn"]
self.log.info("Workflow %s updated to version %s", workflow_arn,
response.get("WorkflowVersion"))
return workflow_arn
diff --git
a/providers/amazon/tests/unit/amazon/aws/operators/test_mwaa_serverless.py
b/providers/amazon/tests/unit/amazon/aws/operators/test_mwaa_serverless.py
index 3397120f70c..6937deded0c 100644
--- a/providers/amazon/tests/unit/amazon/aws/operators/test_mwaa_serverless.py
+++ b/providers/amazon/tests/unit/amazon/aws/operators/test_mwaa_serverless.py
@@ -90,6 +90,26 @@ WORKFLOW_ARN =
"arn:aws:mwaa-serverless:us-east-1:123456789012:workflow/test-wor
S3_LOCATION = {"Bucket": "test-bucket", "ObjectKey": "workflow.yaml"}
CODE = {"S3Location": {"Bucket": "test-bucket", "ObjectKey":
"code/my_package.zip"}}
ROLE_ARN = "arn:aws:iam::123456789012:role/test-role"
+CREATE_WORKFLOW_KWARGS = {
+ "NetworkConfiguration": {
+ "SubnetIds": ["subnet-0123456789abcdef0", "subnet-0fedcba9876543210"],
+ "SecurityGroupIds": ["sg-0123456789abcdef0"],
+ },
+ "LoggingConfiguration": {"LogGroupName":
"/aws/mwaa-serverless/test-workflow"},
+ "EncryptionConfiguration": {"Type": "AWS_MANAGED_KEY"},
+ "EngineVersion": 1,
+ "TriggerMode": "manual_only",
+ "ClientToken": "test-client-token",
+}
+UPDATE_WORKFLOW_KWARGS = {
+ "NetworkConfiguration": {
+ "SubnetIds": ["subnet-0123456789abcdef0", "subnet-0fedcba9876543210"],
+ "SecurityGroupIds": ["sg-0123456789abcdef0"],
+ },
+ "LoggingConfiguration": {"LogGroupName":
"/aws/mwaa-serverless/test-workflow"},
+ "EngineVersion": 1,
+ "TriggerMode": "manual_only",
+}
class TestMwaaServerlessCreateWorkflowOperator:
@@ -178,6 +198,29 @@ class TestMwaaServerlessCreateWorkflowOperator:
)
assert result == WORKFLOW_ARN
+ @mock.patch.object(AwsBaseHook, "conn", new_callable=mock.PropertyMock)
+ def test_execute_with_create_workflow_kwargs(self, mock_conn):
+ op = MwaaServerlessCreateWorkflowOperator(
+ task_id="create_workflow",
+ workflow_name=WORKFLOW_NAME,
+ definition_s3_location=S3_LOCATION,
+ role_arn=ROLE_ARN,
+ create_workflow_kwargs=CREATE_WORKFLOW_KWARGS,
+ )
+ mock_client = mock.MagicMock()
+ mock_client.create_workflow.return_value = {"WorkflowArn":
WORKFLOW_ARN}
+ mock_conn.return_value = mock_client
+
+ result = op.execute({})
+
+ mock_client.create_workflow.assert_called_once_with(
+ Name=WORKFLOW_NAME,
+ DefinitionS3Location=S3_LOCATION,
+ RoleArn=ROLE_ARN,
+ **CREATE_WORKFLOW_KWARGS,
+ )
+ assert result == WORKFLOW_ARN
+
def test_template_fields(self):
validate_template_fields(self.operator)
@@ -275,6 +318,33 @@ class TestMwaaServerlessUpdateWorkflowOperator:
)
assert result == WORKFLOW_ARN
+ @mock.patch.object(AwsBaseHook, "conn", new_callable=mock.PropertyMock)
+ def test_execute_with_update_workflow_kwargs(self, mock_conn):
+ op = MwaaServerlessUpdateWorkflowOperator(
+ task_id="update_workflow",
+ workflow_arn=WORKFLOW_ARN,
+ definition_s3_location=S3_LOCATION,
+ role_arn=ROLE_ARN,
+ update_workflow_kwargs=UPDATE_WORKFLOW_KWARGS,
+ )
+ mock_client = mock.MagicMock()
+ mock_client.update_workflow.return_value = {
+ "WorkflowArn": WORKFLOW_ARN,
+ "WorkflowVersion": "abc123",
+ "ModifiedAt": "2026-05-12T00:00:00Z",
+ }
+ mock_conn.return_value = mock_client
+
+ result = op.execute({})
+
+ mock_client.update_workflow.assert_called_once_with(
+ WorkflowArn=WORKFLOW_ARN,
+ DefinitionS3Location=S3_LOCATION,
+ RoleArn=ROLE_ARN,
+ **UPDATE_WORKFLOW_KWARGS,
+ )
+ assert result == WORKFLOW_ARN
+
def test_template_fields(self):
validate_template_fields(self.operator)