This is an automated email from the ASF dual-hosted git repository.
shahar1 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 0b64e4eae4d Add Amazon ECR repository operators (#69886)
0b64e4eae4d is described below
commit 0b64e4eae4d570fec15392b74a04a5c2c7f60603
Author: Morgan <[email protected]>
AuthorDate: Sat Jul 18 14:59:46 2026 -0300
Add Amazon ECR repository operators (#69886)
---
providers/amazon/docs/operators/ecr.rst | 90 ++++++++
providers/amazon/provider.yaml | 5 +
.../airflow/providers/amazon/aws/operators/ecr.py | 231 +++++++++++++++++++++
.../airflow/providers/amazon/get_provider_info.py | 5 +
.../amazon/tests/system/amazon/aws/example_ecr.py | 99 +++++++++
.../tests/unit/amazon/aws/operators/test_ecr.py | 208 +++++++++++++++++++
6 files changed, 638 insertions(+)
diff --git a/providers/amazon/docs/operators/ecr.rst
b/providers/amazon/docs/operators/ecr.rst
new file mode 100644
index 00000000000..466e3c72d57
--- /dev/null
+++ b/providers/amazon/docs/operators/ecr.rst
@@ -0,0 +1,90 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+=======================================
+Amazon Elastic Container Registry (ECR)
+=======================================
+
+`Amazon Elastic Container Registry (ECR) <https://aws.amazon.com/ecr/>`__ is a
managed container registry
+for storing, sharing, and deploying container images and artifacts.
+
+Prerequisite Tasks
+------------------
+
+.. include:: ../_partials/prerequisite_tasks.rst
+
+Generic Parameters
+------------------
+
+.. include:: ../_partials/generic_parameters.rst
+
+Operators
+---------
+
+.. _howto/operator:EcrCreateRepositoryOperator:
+
+Create an ECR repository
+========================
+
+To create an ECR repository, use
+:class:`~airflow.providers.amazon.aws.operators.ecr.EcrCreateRepositoryOperator`.
+This operator can provision a repository before a workflow builds or pushes
container images.
+The operator returns the complete response from the Boto3
``create_repository`` API operation.
+
+.. exampleinclude:: /../../amazon/tests/system/amazon/aws/example_ecr.py
+ :language: python
+ :dedent: 4
+ :start-after: [START howto_operator_ecr_create_repository]
+ :end-before: [END howto_operator_ecr_create_repository]
+
+.. _howto/operator:EcrSetRepositoryPolicyOperator:
+
+Set an ECR repository policy
+============================
+
+To set the repository policy for an ECR repository, use
+:class:`~airflow.providers.amazon.aws.operators.ecr.EcrSetRepositoryPolicyOperator`.
+This operator can configure permissions such as cross-account access to images
stored in the repository.
+The operator returns the complete response from the Boto3
``set_repository_policy`` API operation.
+
+.. exampleinclude:: /../../amazon/tests/system/amazon/aws/example_ecr.py
+ :language: python
+ :dedent: 4
+ :start-after: [START howto_operator_ecr_set_repository_policy]
+ :end-before: [END howto_operator_ecr_set_repository_policy]
+
+.. _howto/operator:EcrDeleteRepositoryOperator:
+
+Delete an ECR repository
+========================
+
+To delete an ECR repository, use
+:class:`~airflow.providers.amazon.aws.operators.ecr.EcrDeleteRepositoryOperator`.
+This operator can clean up temporary repositories or repositories that are no
longer needed.
+Set ``force=True`` to delete a repository that contains images. The operator
returns the complete response
+from the Boto3 ``delete_repository`` API operation.
+
+.. exampleinclude:: /../../amazon/tests/system/amazon/aws/example_ecr.py
+ :language: python
+ :dedent: 4
+ :start-after: [START howto_operator_ecr_delete_repository]
+ :end-before: [END howto_operator_ecr_delete_repository]
+
+Reference
+---------
+
+* `AWS Boto3 library documentation for ECR
<https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ecr.html>`__
diff --git a/providers/amazon/provider.yaml b/providers/amazon/provider.yaml
index 4fa06f21121..afc76681482 100644
--- a/providers/amazon/provider.yaml
+++ b/providers/amazon/provider.yaml
@@ -182,6 +182,8 @@ integrations:
- integration-name: Amazon Elastic Container Registry (ECR)
external-doc-url: https://aws.amazon.com/ecr/
logo:
/docs/integration-logos/[email protected]
+ how-to-guide:
+ - /docs/apache-airflow-providers-amazon/operators/ecr.rst
tags: [aws]
- integration-name: Amazon ECS
external-doc-url: https://aws.amazon.com/ecs/
@@ -447,6 +449,9 @@ operators:
- integration-name: Amazon EC2
python-modules:
- airflow.providers.amazon.aws.operators.ec2
+ - integration-name: Amazon Elastic Container Registry (ECR)
+ python-modules:
+ - airflow.providers.amazon.aws.operators.ecr
- integration-name: Amazon ECS
python-modules:
- airflow.providers.amazon.aws.operators.ecs
diff --git a/providers/amazon/src/airflow/providers/amazon/aws/operators/ecr.py
b/providers/amazon/src/airflow/providers/amazon/aws/operators/ecr.py
new file mode 100644
index 00000000000..dec75ede9b5
--- /dev/null
+++ b/providers/amazon/src/airflow/providers/amazon/aws/operators/ecr.py
@@ -0,0 +1,231 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from collections.abc import Sequence
+from typing import TYPE_CHECKING, Any, ClassVar
+
+from airflow.providers.amazon.aws.hooks.ecr import EcrHook
+from airflow.providers.amazon.aws.operators.base_aws import AwsBaseOperator
+from airflow.providers.amazon.aws.utils.mixins import aws_template_fields
+from airflow.utils.helpers import prune_dict
+
+if TYPE_CHECKING:
+ from airflow.sdk import Context
+
+
+class EcrCreateRepositoryOperator(AwsBaseOperator[EcrHook]):
+ """
+ Create an Amazon ECR repository.
+
+ .. seealso::
+ For more information on how to use this operator, take a look at the
guide:
+ :ref:`howto/operator:EcrCreateRepositoryOperator`
+
+ :param repository_name: The name of the repository to create. (templated)
+ :param registry_id: The AWS account ID associated with the registry.
(templated)
+ :param tags: Metadata to apply to the repository. (templated)
+ :param image_tag_mutability: The tag mutability setting for the
repository. (templated)
+ :param image_tag_mutability_exclusion_filters: Filters that override the
repository's
+ image tag mutability setting. (templated)
+ :param image_scanning_configuration: The image scanning configuration for
the repository. (templated)
+ :param encryption_configuration: The encryption configuration for the
repository. (templated)
+ :param aws_conn_id: The Airflow connection used for AWS credentials.
+ If this is ``None`` or empty then the default boto3 behaviour is used.
If
+ running Airflow in a distributed manner and aws_conn_id is None or
+ empty, then default boto3 configuration would be used (and must be
+ maintained on each worker node).
+ :param region_name: AWS region_name. If not specified then the default
boto3 behaviour is used.
+ :param verify: Whether or not to verify SSL certificates. See:
+
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html
+ :param botocore_config: Configuration dictionary (key-values) for botocore
client. See:
+
https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html
+ """
+
+ aws_hook_class = EcrHook
+ template_fields: Sequence[str] = aws_template_fields(
+ "repository_name",
+ "registry_id",
+ "tags",
+ "image_tag_mutability",
+ "image_tag_mutability_exclusion_filters",
+ "image_scanning_configuration",
+ "encryption_configuration",
+ )
+ template_fields_renderers: ClassVar[dict[str, str]] = {
+ "tags": "json",
+ "image_tag_mutability_exclusion_filters": "json",
+ "image_scanning_configuration": "json",
+ "encryption_configuration": "json",
+ }
+
+ def __init__(
+ self,
+ *,
+ repository_name: str,
+ registry_id: str | None = None,
+ tags: list[dict[str, str]] | None = None,
+ image_tag_mutability: str | None = None,
+ image_tag_mutability_exclusion_filters: list[dict[str, str]] | None =
None,
+ image_scanning_configuration: dict[str, bool] | None = None,
+ encryption_configuration: dict[str, str] | None = None,
+ aws_conn_id: str | None = "aws_default",
+ **kwargs,
+ ):
+ super().__init__(aws_conn_id=aws_conn_id, **kwargs)
+ self.repository_name = repository_name
+ self.registry_id = registry_id
+ self.tags = tags
+ self.image_tag_mutability = image_tag_mutability
+ self.image_tag_mutability_exclusion_filters =
image_tag_mutability_exclusion_filters
+ self.image_scanning_configuration = image_scanning_configuration
+ self.encryption_configuration = encryption_configuration
+
+ def execute(self, context: Context) -> dict[str, Any]:
+ self.log.info("Creating Amazon ECR repository %s",
self.repository_name)
+ response = self.hook.conn.create_repository(
+ **prune_dict(
+ {
+ "registryId": self.registry_id,
+ "repositoryName": self.repository_name,
+ "tags": self.tags,
+ "imageTagMutability": self.image_tag_mutability,
+ "imageTagMutabilityExclusionFilters":
self.image_tag_mutability_exclusion_filters,
+ "imageScanningConfiguration":
self.image_scanning_configuration,
+ "encryptionConfiguration": self.encryption_configuration,
+ }
+ )
+ )
+ self.log.info("Created Amazon ECR repository %s", self.repository_name)
+ return response
+
+
+class EcrSetRepositoryPolicyOperator(AwsBaseOperator[EcrHook]):
+ """
+ Set the repository policy for an Amazon ECR repository.
+
+ .. seealso::
+ For more information on how to use this operator, take a look at the
guide:
+ :ref:`howto/operator:EcrSetRepositoryPolicyOperator`
+
+ :param repository_name: The name of the repository to receive the policy.
(templated)
+ :param policy_text: The JSON repository policy text to apply. (templated)
+ :param registry_id: The AWS account ID associated with the registry.
(templated)
+ :param force: Whether to replace an existing policy that prevents setting
a new policy. (templated)
+ :param aws_conn_id: The Airflow connection used for AWS credentials.
+ If this is ``None`` or empty then the default boto3 behaviour is used.
If
+ running Airflow in a distributed manner and aws_conn_id is None or
+ empty, then default boto3 configuration would be used (and must be
+ maintained on each worker node).
+ :param region_name: AWS region_name. If not specified then the default
boto3 behaviour is used.
+ :param verify: Whether or not to verify SSL certificates. See:
+
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html
+ :param botocore_config: Configuration dictionary (key-values) for botocore
client. See:
+
https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html
+ """
+
+ aws_hook_class = EcrHook
+ template_fields: Sequence[str] = aws_template_fields(
+ "repository_name", "policy_text", "registry_id", "force"
+ )
+ template_fields_renderers: ClassVar[dict[str, str]] = {"policy_text":
"json"}
+
+ def __init__(
+ self,
+ *,
+ repository_name: str,
+ policy_text: str,
+ registry_id: str | None = None,
+ force: bool = False,
+ aws_conn_id: str | None = "aws_default",
+ **kwargs,
+ ):
+ super().__init__(aws_conn_id=aws_conn_id, **kwargs)
+ self.repository_name = repository_name
+ self.policy_text = policy_text
+ self.registry_id = registry_id
+ self.force = force
+
+ def execute(self, context: Context) -> dict[str, Any]:
+ self.log.info("Setting repository policy for Amazon ECR repository
%s", self.repository_name)
+ response = self.hook.conn.set_repository_policy(
+ **prune_dict(
+ {
+ "registryId": self.registry_id,
+ "repositoryName": self.repository_name,
+ "policyText": self.policy_text,
+ "force": self.force,
+ }
+ )
+ )
+ self.log.info("Set repository policy for Amazon ECR repository %s",
self.repository_name)
+ return response
+
+
+class EcrDeleteRepositoryOperator(AwsBaseOperator[EcrHook]):
+ """
+ Delete an Amazon ECR repository.
+
+ .. seealso::
+ For more information on how to use this operator, take a look at the
guide:
+ :ref:`howto/operator:EcrDeleteRepositoryOperator`
+
+ :param repository_name: The name of the repository to delete. (templated)
+ :param registry_id: The AWS account ID associated with the registry.
(templated)
+ :param force: Whether to delete the repository when it contains images.
(templated)
+ :param aws_conn_id: The Airflow connection used for AWS credentials.
+ If this is ``None`` or empty then the default boto3 behaviour is used.
If
+ running Airflow in a distributed manner and aws_conn_id is None or
+ empty, then default boto3 configuration would be used (and must be
+ maintained on each worker node).
+ :param region_name: AWS region_name. If not specified then the default
boto3 behaviour is used.
+ :param verify: Whether or not to verify SSL certificates. See:
+
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html
+ :param botocore_config: Configuration dictionary (key-values) for botocore
client. See:
+
https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html
+ """
+
+ aws_hook_class = EcrHook
+ template_fields: Sequence[str] = aws_template_fields("repository_name",
"registry_id", "force")
+
+ def __init__(
+ self,
+ *,
+ repository_name: str,
+ registry_id: str | None = None,
+ force: bool = False,
+ aws_conn_id: str | None = "aws_default",
+ **kwargs,
+ ):
+ super().__init__(aws_conn_id=aws_conn_id, **kwargs)
+ self.repository_name = repository_name
+ self.registry_id = registry_id
+ self.force = force
+
+ def execute(self, context: Context) -> dict[str, Any]:
+ self.log.info("Deleting Amazon ECR repository %s",
self.repository_name)
+ response = self.hook.conn.delete_repository(
+ **prune_dict(
+ {
+ "registryId": self.registry_id,
+ "repositoryName": self.repository_name,
+ "force": self.force,
+ }
+ )
+ )
+ self.log.info("Deleted Amazon ECR repository %s", self.repository_name)
+ return response
diff --git a/providers/amazon/src/airflow/providers/amazon/get_provider_info.py
b/providers/amazon/src/airflow/providers/amazon/get_provider_info.py
index a5a5c532e7e..35fdd5756a6 100644
--- a/providers/amazon/src/airflow/providers/amazon/get_provider_info.py
+++ b/providers/amazon/src/airflow/providers/amazon/get_provider_info.py
@@ -94,6 +94,7 @@ def get_provider_info():
"integration-name": "Amazon Elastic Container Registry (ECR)",
"external-doc-url": "https://aws.amazon.com/ecr/",
"logo":
"/docs/integration-logos/[email protected]",
+ "how-to-guide":
["/docs/apache-airflow-providers-amazon/operators/ecr.rst"],
"tags": ["aws"],
},
{
@@ -424,6 +425,10 @@ def get_provider_info():
"integration-name": "Amazon EC2",
"python-modules":
["airflow.providers.amazon.aws.operators.ec2"],
},
+ {
+ "integration-name": "Amazon Elastic Container Registry (ECR)",
+ "python-modules":
["airflow.providers.amazon.aws.operators.ecr"],
+ },
{
"integration-name": "Amazon ECS",
"python-modules":
["airflow.providers.amazon.aws.operators.ecs"],
diff --git a/providers/amazon/tests/system/amazon/aws/example_ecr.py
b/providers/amazon/tests/system/amazon/aws/example_ecr.py
new file mode 100644
index 00000000000..64230657cb0
--- /dev/null
+++ b/providers/amazon/tests/system/amazon/aws/example_ecr.py
@@ -0,0 +1,99 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from datetime import datetime
+
+from airflow.providers.amazon.aws.operators.ecr import (
+ EcrCreateRepositoryOperator,
+ EcrDeleteRepositoryOperator,
+ EcrSetRepositoryPolicyOperator,
+)
+from airflow.providers.common.compat.sdk import DAG, chain
+
+from system.amazon.aws.utils import ENV_ID_KEY, SystemTestContextBuilder
+from tests_common.test_utils.version_compat import AIRFLOW_V_3_0_PLUS
+
+if AIRFLOW_V_3_0_PLUS:
+ from airflow.sdk import TriggerRule
+else:
+ from airflow.utils.trigger_rule import TriggerRule # type:
ignore[no-redef,attr-defined]
+
+DAG_ID = "example_ecr"
+
+sys_test_context_task = SystemTestContextBuilder().build()
+
+
+with DAG(
+ dag_id=DAG_ID,
+ schedule=None,
+ start_date=datetime(2024, 1, 1),
+ catchup=False,
+) as dag:
+ test_context = sys_test_context_task()
+ repository_name = f"{test_context[ENV_ID_KEY]}-test-repository"
+
+ # [START howto_operator_ecr_create_repository]
+ create_repository = EcrCreateRepositoryOperator(
+ task_id="create_repository",
+ repository_name=repository_name,
+ )
+ # [END howto_operator_ecr_create_repository]
+
+ # [START howto_operator_ecr_set_repository_policy]
+ set_repository_policy = EcrSetRepositoryPolicyOperator(
+ task_id="set_repository_policy",
+ repository_name=repository_name,
+ policy_text="""
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "AllowAccountPull",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": "arn:aws:iam::{{
task_instance.xcom_pull(task_ids='create_repository')['repository']['registryId']
}}:root"
+ },
+ "Action": [
+ "ecr:BatchGetImage",
+ "ecr:GetDownloadUrlForLayer"
+ ]
+ }
+ ]
+ }
+ """,
+ )
+ # [END howto_operator_ecr_set_repository_policy]
+
+ # [START howto_operator_ecr_delete_repository]
+ delete_repository = EcrDeleteRepositoryOperator(
+ task_id="delete_repository",
+ repository_name=repository_name,
+ force=True,
+ trigger_rule=TriggerRule.ALL_DONE,
+ )
+ # [END howto_operator_ecr_delete_repository]
+
+ chain(test_context, create_repository, set_repository_policy,
delete_repository)
+
+ from tests_common.test_utils.watcher import watcher
+
+ list(dag.tasks) >> watcher()
+
+from tests_common.test_utils.system_tests import get_test_run # noqa: E402
+
+test_run = get_test_run(dag)
diff --git a/providers/amazon/tests/unit/amazon/aws/operators/test_ecr.py
b/providers/amazon/tests/unit/amazon/aws/operators/test_ecr.py
new file mode 100644
index 00000000000..ba485ba4016
--- /dev/null
+++ b/providers/amazon/tests/unit/amazon/aws/operators/test_ecr.py
@@ -0,0 +1,208 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from unittest import mock
+
+import pytest
+
+from airflow.providers.amazon.aws.hooks.ecr import EcrHook
+from airflow.providers.amazon.aws.operators.ecr import (
+ EcrCreateRepositoryOperator,
+ EcrDeleteRepositoryOperator,
+ EcrSetRepositoryPolicyOperator,
+)
+
+from unit.amazon.aws.utils.test_template_fields import validate_template_fields
+
+REPOSITORY_NAME = "test-repository"
+REGISTRY_ID = "123456789012"
+REPOSITORY_RESPONSE = {
+ "repository": {
+ "repositoryArn":
f"arn:aws:ecr:us-east-1:{REGISTRY_ID}:repository/{REPOSITORY_NAME}",
+ "registryId": REGISTRY_ID,
+ "repositoryName": REPOSITORY_NAME,
+ "repositoryUri":
f"{REGISTRY_ID}.dkr.ecr.us-east-1.amazonaws.com/{REPOSITORY_NAME}",
+ },
+ "ResponseMetadata": {"HTTPStatusCode": 200},
+}
+POLICY_TEXT = '{"Version":"2012-10-17","Statement":[]}'
+POLICY_RESPONSE = {
+ "registryId": REGISTRY_ID,
+ "repositoryName": REPOSITORY_NAME,
+ "policyText": POLICY_TEXT,
+ "ResponseMetadata": {"HTTPStatusCode": 200},
+}
+
+
+class TestEcrCreateRepositoryOperator:
+ @pytest.mark.parametrize(
+ ("operator_parameters", "boto3_parameters"),
+ [
+ pytest.param(
+ {},
+ {
+ "repositoryName": REPOSITORY_NAME,
+ },
+ id="required-parameters",
+ ),
+ pytest.param(
+ {
+ "registry_id": REGISTRY_ID,
+ "tags": [{"Key": "environment", "Value": "test"}],
+ "image_tag_mutability": "IMMUTABLE_WITH_EXCLUSION",
+ "image_tag_mutability_exclusion_filters": [
+ {"filterType": "WILDCARD", "filter": "latest"}
+ ],
+ "image_scanning_configuration": {"scanOnPush": True},
+ "encryption_configuration": {
+ "encryptionType": "KMS",
+ "kmsKey":
"arn:aws:kms:us-east-1:123456789012:key/test-key",
+ },
+ },
+ {
+ "repositoryName": REPOSITORY_NAME,
+ "registryId": REGISTRY_ID,
+ "tags": [{"Key": "environment", "Value": "test"}],
+ "imageTagMutability": "IMMUTABLE_WITH_EXCLUSION",
+ "imageTagMutabilityExclusionFilters": [{"filterType":
"WILDCARD", "filter": "latest"}],
+ "imageScanningConfiguration": {"scanOnPush": True},
+ "encryptionConfiguration": {
+ "encryptionType": "KMS",
+ "kmsKey":
"arn:aws:kms:us-east-1:123456789012:key/test-key",
+ },
+ },
+ id="all-parameters",
+ ),
+ ],
+ )
+ @mock.patch.object(EcrHook, "conn", new_callable=mock.PropertyMock)
+ def test_execute(self, mock_conn, operator_parameters, boto3_parameters):
+ mock_client = mock.MagicMock(spec=["create_repository"])
+ mock_client.create_repository.return_value = REPOSITORY_RESPONSE
+ mock_conn.return_value = mock_client
+ operator = EcrCreateRepositoryOperator(
+ task_id="create_repository",
+ repository_name=REPOSITORY_NAME,
+ **operator_parameters,
+ )
+
+ result = operator.execute({})
+
+
mock_client.create_repository.assert_called_once_with(**boto3_parameters)
+ assert result == REPOSITORY_RESPONSE
+
+ def test_template_fields(self):
+ operator = EcrCreateRepositoryOperator(
+ task_id="create_repository",
+ repository_name=REPOSITORY_NAME,
+ )
+
+ validate_template_fields(operator)
+
+
+class TestEcrSetRepositoryPolicyOperator:
+ @pytest.mark.parametrize(
+ ("operator_parameters", "boto3_parameters"),
+ [
+ pytest.param(
+ {},
+ {
+ "repositoryName": REPOSITORY_NAME,
+ "policyText": POLICY_TEXT,
+ "force": False,
+ },
+ id="defaults",
+ ),
+ pytest.param(
+ {"registry_id": REGISTRY_ID, "force": True},
+ {
+ "registryId": REGISTRY_ID,
+ "repositoryName": REPOSITORY_NAME,
+ "policyText": POLICY_TEXT,
+ "force": True,
+ },
+ id="registry-and-force",
+ ),
+ ],
+ )
+ @mock.patch.object(EcrHook, "conn", new_callable=mock.PropertyMock)
+ def test_execute(self, mock_conn, operator_parameters, boto3_parameters):
+ mock_client = mock.MagicMock(spec=["set_repository_policy"])
+ mock_client.set_repository_policy.return_value = POLICY_RESPONSE
+ mock_conn.return_value = mock_client
+ operator = EcrSetRepositoryPolicyOperator(
+ task_id="set_repository_policy",
+ repository_name=REPOSITORY_NAME,
+ policy_text=POLICY_TEXT,
+ **operator_parameters,
+ )
+
+ result = operator.execute({})
+
+
mock_client.set_repository_policy.assert_called_once_with(**boto3_parameters)
+ assert result == POLICY_RESPONSE
+
+ def test_template_fields(self):
+ operator = EcrSetRepositoryPolicyOperator(
+ task_id="set_repository_policy",
+ repository_name=REPOSITORY_NAME,
+ policy_text=POLICY_TEXT,
+ )
+
+ validate_template_fields(operator)
+
+
+class TestEcrDeleteRepositoryOperator:
+ @pytest.mark.parametrize(
+ ("operator_parameters", "boto3_parameters"),
+ [
+ pytest.param(
+ {},
+ {"repositoryName": REPOSITORY_NAME, "force": False},
+ id="defaults",
+ ),
+ pytest.param(
+ {"registry_id": REGISTRY_ID, "force": True},
+ {"repositoryName": REPOSITORY_NAME, "registryId": REGISTRY_ID,
"force": True},
+ id="registry-and-force",
+ ),
+ ],
+ )
+ @mock.patch.object(EcrHook, "conn", new_callable=mock.PropertyMock)
+ def test_execute(self, mock_conn, operator_parameters, boto3_parameters):
+ mock_client = mock.MagicMock(spec=["delete_repository"])
+ mock_client.delete_repository.return_value = REPOSITORY_RESPONSE
+ mock_conn.return_value = mock_client
+ operator = EcrDeleteRepositoryOperator(
+ task_id="delete_repository",
+ repository_name=REPOSITORY_NAME,
+ **operator_parameters,
+ )
+
+ result = operator.execute({})
+
+
mock_client.delete_repository.assert_called_once_with(**boto3_parameters)
+ assert result == REPOSITORY_RESPONSE
+
+ def test_template_fields(self):
+ operator = EcrDeleteRepositoryOperator(
+ task_id="delete_repository",
+ repository_name=REPOSITORY_NAME,
+ )
+
+ validate_template_fields(operator)