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 0c8a2269b61 Support code packages in MWAA Serverless workflow 
operators (#72622)
0c8a2269b61 is described below

commit 0c8a2269b616bf3714bc00950f30dda39e16bcad
Author: Kazushi Y <[email protected]>
AuthorDate: Tue Sep 15 15:39:50 2026 +0900

    Support code packages in MWAA Serverless workflow operators (#72622)
    
    MWAA Serverless added PythonOperator and BashOperator support, which 
requires
    shipping the Python modules and shell scripts those tasks run separately 
from
    the YAML workflow definition, through the Code request parameter.
    
    Neither MwaaServerlessCreateWorkflowOperator nor
    MwaaServerlessUpdateWorkflowOperator exposed Code, and neither accepts a 
kwargs
    passthrough, so the parameter was unreachable. Workflows using those 
operators
    could not be created, and their code could not be updated, from Airflow.
    
    Add a `code` parameter to both operators, named after the API field and 
passed
    through untouched so a future member of the Code union needs no operator
    change. prune_dict omits it when unset, so existing DAGs are unaffected.
---
 .../amazon/aws/operators/mwaa_serverless.py        | 20 +++++++--
 .../amazon/aws/operators/test_mwaa_serverless.py   | 49 ++++++++++++++++++++++
 2 files changed, 65 insertions(+), 4 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 b8a31f7f1a5..142affd82fe 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
@@ -98,6 +98,9 @@ class 
MwaaServerlessCreateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
 
     :param workflow_name: The name of the workflow. (templated)
     :param definition_s3_location: Dict with ``Bucket`` and ``ObjectKey`` for 
the YAML definition. (templated)
+    :param code: Optional code location for ``PythonOperator`` and 
``BashOperator`` tasks,
+        as a dict with an ``S3Location`` key containing ``Bucket``, 
``ObjectKey``, and
+        optionally ``VersionId``. (templated)
     :param role_arn: The execution role ARN. (templated)
     :param description: Optional description. (templated)
     :param tags: Optional tags dict.
@@ -107,15 +110,16 @@ class 
MwaaServerlessCreateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
 
     aws_hook_class = AwsBaseHook
     template_fields: tuple[str, ...] = aws_template_fields(
-        "workflow_name", "definition_s3_location", "role_arn", "description"
+        "workflow_name", "definition_s3_location", "code", "role_arn", 
"description"
     )
-    template_fields_renderers = {"definition_s3_location": "json"}
+    template_fields_renderers = {"definition_s3_location": "json", "code": 
"json"}
 
     def __init__(
         self,
         *,
         workflow_name: str,
         definition_s3_location: dict[str, str],
+        code: dict[str, Any] | None = None,
         role_arn: str,
         description: str | None = None,
         tags: dict[str, str] | None = None,
@@ -125,6 +129,7 @@ class 
MwaaServerlessCreateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
         super().__init__(**kwargs)
         self.workflow_name = workflow_name
         self.definition_s3_location = definition_s3_location
+        self.code = code
         self.role_arn = role_arn
         self.description = description
         self.tags = tags
@@ -140,6 +145,7 @@ class 
MwaaServerlessCreateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
             {
                 "Name": self.workflow_name,
                 "DefinitionS3Location": self.definition_s3_location,
+                "Code": self.code,
                 "RoleArn": self.role_arn,
                 "Description": self.description,
                 "Tags": self.tags,
@@ -174,21 +180,25 @@ class 
MwaaServerlessUpdateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
     :param workflow_arn: The ARN of the workflow to update. (templated)
     :param definition_s3_location: Dict with ``Bucket``, ``ObjectKey``, and 
optionally
         ``VersionId`` for the updated YAML definition. (templated)
+    :param code: Optional code location for ``PythonOperator`` and 
``BashOperator`` tasks,
+        as a dict with an ``S3Location`` key containing ``Bucket``, 
``ObjectKey``, and
+        optionally ``VersionId``. (templated)
     :param role_arn: The execution role ARN. (templated)
     :param description: Optional updated description. (templated)
     """
 
     aws_hook_class = AwsBaseHook
     template_fields: tuple[str, ...] = aws_template_fields(
-        "workflow_arn", "definition_s3_location", "role_arn", "description"
+        "workflow_arn", "definition_s3_location", "code", "role_arn", 
"description"
     )
-    template_fields_renderers = {"definition_s3_location": "json"}
+    template_fields_renderers = {"definition_s3_location": "json", "code": 
"json"}
 
     def __init__(
         self,
         *,
         workflow_arn: str,
         definition_s3_location: dict[str, str],
+        code: dict[str, Any] | None = None,
         role_arn: str,
         description: str | None = None,
         **kwargs,
@@ -196,6 +206,7 @@ class 
MwaaServerlessUpdateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
         super().__init__(**kwargs)
         self.workflow_arn = workflow_arn
         self.definition_s3_location = definition_s3_location
+        self.code = code
         self.role_arn = role_arn
         self.description = description
 
@@ -209,6 +220,7 @@ class 
MwaaServerlessUpdateWorkflowOperator(AwsBaseOperator[AwsBaseHook]):
             {
                 "WorkflowArn": self.workflow_arn,
                 "DefinitionS3Location": self.definition_s3_location,
+                "Code": self.code,
                 "RoleArn": self.role_arn,
                 "Description": self.description,
             }
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 9b2be23881b..3397120f70c 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
@@ -88,6 +88,7 @@ class TestMwaaServerlessStartWorkflowRunOperator:
 WORKFLOW_NAME = "test-workflow"
 WORKFLOW_ARN = 
"arn:aws:mwaa-serverless:us-east-1:123456789012:workflow/test-workflow"
 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"
 
 
@@ -156,6 +157,27 @@ class TestMwaaServerlessCreateWorkflowOperator:
         with pytest.raises(ClientError, match="ConflictException"):
             op.execute({})
 
+    @mock.patch.object(AwsBaseHook, "conn", new_callable=mock.PropertyMock)
+    def test_execute_with_code_version_id(self, mock_conn):
+        code = {"S3Location": {**CODE["S3Location"], "VersionId": "abc123"}}
+        op = MwaaServerlessCreateWorkflowOperator(
+            task_id="create_workflow",
+            workflow_name=WORKFLOW_NAME,
+            definition_s3_location=S3_LOCATION,
+            code=code,
+            role_arn=ROLE_ARN,
+        )
+        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, Code=code, 
RoleArn=ROLE_ARN
+        )
+        assert result == WORKFLOW_ARN
+
     def test_template_fields(self):
         validate_template_fields(self.operator)
 
@@ -226,6 +248,33 @@ class TestMwaaServerlessUpdateWorkflowOperator:
         with pytest.raises(ClientError):
             self.operator.execute({})
 
+    @mock.patch.object(AwsBaseHook, "conn", new_callable=mock.PropertyMock)
+    def test_execute_with_code(self, mock_conn):
+        op = MwaaServerlessUpdateWorkflowOperator(
+            task_id="update_workflow",
+            workflow_arn=WORKFLOW_ARN,
+            definition_s3_location=S3_LOCATION,
+            code=CODE,
+            role_arn=ROLE_ARN,
+        )
+        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,
+            Code=CODE,
+            RoleArn=ROLE_ARN,
+        )
+        assert result == WORKFLOW_ARN
+
     def test_template_fields(self):
         validate_template_fields(self.operator)
 

Reply via email to