You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by on...@apache.org on 2023/01/12 06:49:58 UTC
[airflow] branch main updated: Add a new SSM hook and use it in the System Test context builder (#28755)
This is an automated email from the ASF dual-hosted git repository.
onikolas 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 870ecd477a Add a new SSM hook and use it in the System Test context builder (#28755)
870ecd477a is described below
commit 870ecd477af3774546bd82bb71921a03914a2b64
Author: D. Ferruzzi <fe...@amazon.com>
AuthorDate: Wed Jan 11 22:49:44 2023 -0800
Add a new SSM hook and use it in the System Test context builder (#28755)
* Add a new SSM hook and use it in the System Test context builder
Includes unit tests
Co-authored-by: Andrey Anshin <An...@taragol.is>
---
airflow/providers/amazon/aws/hooks/ssm.py | 53 ++++++++++++++++
airflow/providers/amazon/provider.yaml | 7 +++
.../aws/AWS-Systems-Manager_light-bg@4x.png | Bin 0 -> 75092 bytes
tests/providers/amazon/aws/hooks/test_ssm.py | 67 +++++++++++++++++++++
.../system/providers/amazon/aws/utils/__init__.py | 7 ++-
5 files changed, 131 insertions(+), 3 deletions(-)
diff --git a/airflow/providers/amazon/aws/hooks/ssm.py b/airflow/providers/amazon/aws/hooks/ssm.py
new file mode 100644
index 0000000000..25a7f01f90
--- /dev/null
+++ b/airflow/providers/amazon/aws/hooks/ssm.py
@@ -0,0 +1,53 @@
+# 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 airflow.providers.amazon.aws.hooks.base_aws import AwsBaseHook
+from airflow.utils.types import NOTSET, ArgNotSet
+
+
+class SsmHook(AwsBaseHook):
+ """
+ Interact with Amazon Systems Manager (SSM) using the boto3 library.
+ All API calls available through the Boto API are also available here.
+ See: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#client
+
+ Additional arguments (such as ``aws_conn_id``) may be specified and
+ are passed down to the underlying AwsBaseHook.
+
+ .. seealso::
+ :class:`~airflow.providers.amazon.aws.hooks.base_aws.AwsBaseHook`
+ """
+
+ def __init__(self, *args, **kwargs) -> None:
+ kwargs["client_type"] = "ssm"
+ super().__init__(*args, **kwargs)
+
+ def get_parameter_value(self, parameter: str, default: str | ArgNotSet = NOTSET) -> str:
+ """
+ Returns the value of the provided Parameter or an optional default.
+
+ :param parameter: The SSM Parameter name to return the value for.
+ :param default: Optional default value to return if none is found.
+ """
+ try:
+ return self.conn.get_parameter(Name=parameter)["Parameter"]["Value"]
+ except self.conn.exceptions.ParameterNotFound:
+ if isinstance(default, ArgNotSet):
+ raise
+ return default
diff --git a/airflow/providers/amazon/provider.yaml b/airflow/providers/amazon/provider.yaml
index dc401b8884..b1a8aa0f89 100644
--- a/airflow/providers/amazon/provider.yaml
+++ b/airflow/providers/amazon/provider.yaml
@@ -195,6 +195,10 @@ integrations:
how-to-guide:
- /docs/apache-airflow-providers-amazon/operators/s3.rst
tags: [aws]
+ - integration-name: Amazon Systems Manager (SSM)
+ external-doc-url: https://aws.amazon.com/systems-manager/
+ logo: /integration-logos/aws/AWS-Systems-Manager_light-bg@4x.png
+ tags: [aws]
- integration-name: Amazon Web Services
external-doc-url: https://aws.amazon.com/
logo: /integration-logos/aws/AWS-Cloud-alt_light-bg@4x.png
@@ -461,6 +465,9 @@ hooks:
- integration-name: Amazon Simple Email Service (SES)
python-modules:
- airflow.providers.amazon.aws.hooks.ses
+ - integration-name: Amazon Systems Manager (SSM)
+ python-modules:
+ - airflow.providers.amazon.aws.hooks.ssm
- integration-name: Amazon SecretsManager
python-modules:
- airflow.providers.amazon.aws.hooks.secrets_manager
diff --git a/docs/integration-logos/aws/AWS-Systems-Manager_light-bg@4x.png b/docs/integration-logos/aws/AWS-Systems-Manager_light-bg@4x.png
new file mode 100644
index 0000000000..0aae71d6cb
Binary files /dev/null and b/docs/integration-logos/aws/AWS-Systems-Manager_light-bg@4x.png differ
diff --git a/tests/providers/amazon/aws/hooks/test_ssm.py b/tests/providers/amazon/aws/hooks/test_ssm.py
new file mode 100644
index 0000000000..20eb390310
--- /dev/null
+++ b/tests/providers/amazon/aws/hooks/test_ssm.py
@@ -0,0 +1,67 @@
+# 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
+
+import botocore.exceptions
+import pytest
+from moto import mock_ssm
+from pytest import param
+
+from airflow.providers.amazon.aws.hooks.ssm import SsmHook
+
+DEFAULT_CONN_ID: str = "aws_default"
+REGION: str = "us-east-1"
+
+EXISTING_PARAM_NAME = "parameter"
+BAD_PARAM_NAME = "parameter_does_not_exist"
+PARAM_VALUE = "value"
+DEFAULT_VALUE = "default"
+
+
+@mock_ssm
+class TestSsmHooks:
+ def setup(self):
+ self.hook = SsmHook(region_name=REGION)
+ self.hook.conn.put_parameter(Name=EXISTING_PARAM_NAME, Value=PARAM_VALUE, Overwrite=True)
+
+ def test_hook(self) -> None:
+ assert self.hook.conn is not None
+ assert self.hook.aws_conn_id == DEFAULT_CONN_ID
+ assert self.hook.region_name == REGION
+
+ @pytest.mark.parametrize(
+ "param_name, default_value, expected_result",
+ [
+ param(EXISTING_PARAM_NAME, None, PARAM_VALUE, id="param_exists_no_default_provided"),
+ param(EXISTING_PARAM_NAME, DEFAULT_VALUE, PARAM_VALUE, id="param_exists_with_default"),
+ param(BAD_PARAM_NAME, DEFAULT_VALUE, DEFAULT_VALUE, id="param_does_not_exist_uses_default"),
+ ],
+ )
+ def test_get_parameter_value_happy_cases(self, param_name, default_value, expected_result) -> None:
+ if default_value:
+ assert self.hook.get_parameter_value(param_name, default=default_value) == expected_result
+ else:
+ assert self.hook.get_parameter_value(param_name) == expected_result
+
+ def test_get_parameter_value_param_does_not_exist_no_default_provided(self) -> None:
+ with pytest.raises(botocore.exceptions.ClientError) as raised_exception:
+ self.hook.get_parameter_value(BAD_PARAM_NAME)
+
+ error = raised_exception.value.response["Error"]
+ assert error["Code"] == "ParameterNotFound"
+ assert BAD_PARAM_NAME in error["Message"]
diff --git a/tests/system/providers/amazon/aws/utils/__init__.py b/tests/system/providers/amazon/aws/utils/__init__.py
index eea520c085..11d88074a6 100644
--- a/tests/system/providers/amazon/aws/utils/__init__.py
+++ b/tests/system/providers/amazon/aws/utils/__init__.py
@@ -29,6 +29,7 @@ from botocore.client import BaseClient
from botocore.exceptions import ClientError, NoCredentialsError
from airflow.decorators import task
+from airflow.providers.amazon.aws.hooks.ssm import SsmHook
ENV_ID_ENVIRON_KEY: str = "SYSTEM_TESTS_ENV_ID"
ENV_ID_KEY: str = "ENV_ID"
@@ -92,15 +93,15 @@ def _fetch_from_ssm(key: str, test_name: str | None = None) -> str:
:return: The value of the provided key from SSM
"""
_test_name: str = test_name if test_name else _get_test_name()
- ssm_client: BaseClient = boto3.client("ssm")
+ hook = SsmHook(aws_conn_id=None)
value: str = ""
try:
- value = json.loads(ssm_client.get_parameter(Name=_test_name)["Parameter"]["Value"])[key]
+ value = json.loads(hook.get_parameter_value(_test_name))[key]
# Since a default value after the SSM check is allowed, these exceptions should not stop execution.
except NoCredentialsError as e:
log.info("No boto credentials found: %s", e)
- except ssm_client.exceptions.ParameterNotFound as e:
+ except hook.conn.exceptions.ParameterNotFound as e:
log.info("SSM does not contain any parameter for this test: %s", e)
except KeyError as e:
log.info("SSM contains one parameter for this test, but not the requested value: %s", e)