I am trying to run a set of tests where calls to boto3.client('ssm')
are mocked using moto.
Moto is providing a set of default aws
parameter. https://github.com/spulec/moto/blob/master/moto/ssm/models.py#L59 but is preventing from adding more:
https://github.com/spulec/moto/blob/master/moto/ssm/models.py#L858 Trying to actively add any aws
prefix parameter will return an error as per the tests in https://github.com/spulec/moto/blob/master/tests/test_ssm/test_ssm_boto3.py#L397
As my lambda is relying on the following to be present my test fails: /aws/service/ecs/optimized-ami/amazon-linux-2/recommended
I was thinking of trying to monkey patch the mocked ssm client, but I have very little understanding of moto's internals.
I have been following this example but modifying it for my needs (calling SSM instead of calling SQS and S3). For ref my code looks like this as I have attempted to monkey patch the put_parameter
method without success.
app.py
import boto3
from loguru import logger
@logger.catch()
def lambda_handler(event, context):
ssm_client = boto3.client("ssm", "eu-west-1")
ami_param_name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-ebs"
ami_param_value = ssm_client.get_parameter(Name=ami_param_name)
ecs_param_name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended"
ecs_param_value = ssm_client.get_parameter(Name=ecs_param_name)
return [ami_param_value, ecs_param_value]
test.py
import os
from unittest import mock
import boto3
import pytest
from moto import mock_ssm
from src.app import lambda_handler
AWS_REGION = 'eu-west-1'
@pytest.fixture(scope="function")
def aws_credentials():
"""Mocked AWS Credentials for moto."""
os.environ["AWS_ACCESS_KEY_ID"] = "testing"
os.environ["AWS_SECRET_ACCESS_KEY"] = "testing"
os.environ["AWS_SECURITY_TOKEN"] = "testing"
os.environ["AWS_SESSION_TOKEN"] = "testing"
@pytest.fixture(scope="function")
def mock_ssm_client(aws_credentials):
with mock_ssm():
client = boto3.client("ssm", region_name=AWS_REGION)
# already present in moto
# client.put_parameter(
# Name='/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-ebs',
# Type='String',
# Value='ami-stdparam'
# )
# What the lambda requires
# client.put_parameter(
# Name='/aws/service/ecs/optimized-ami/amazon-linux-2/recommended',
# Type='String',
# Value='{"image_id": "ami-ecsparam"}'
# )
def side_effect(path):
if path == "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended":
return_value = {
"Parameter": {
"Name": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended",
"Type": "String",
"Value": "{\"ecs_agent_version\":\"1.63.1\",\"ecs_runtime_version\":\"Docker version 20.10.13\",\"image_id\":\"ami-002e2fef4b94f8fd0\",\"image_name\":\"amzn2-ami-ecs-hvm-2.0.20220921-x86_64-ebs\",\"image_version\":\"2.0.20220921\",\"os\":\"Amazon Linux 2\",\"schema_version\":1,\"source_image_name\":\"amzn2-ami-minimal-hvm-2.0.20220912.1-x86_64-ebs\"}",
"Version": 94,
"LastModifiedDate": 1664230158.399,
"ARN": "arn:aws:ssm:eu-west-1::parameter/aws/service/ecs/optimized-ami/amazon-linux-2/recommended",
"DataType": "text"
}
}
return return_value
else:
return client.get_parameter(path)
client.get_parameter = mock.patch(
'boto3.client.get_parameter',
side_effect=side_effect
)
yield client
def test_lambda_handler(mock_ssm_client):
# Arrange
# Act
results = lambda_handler('', 'test')
# Assert
assert len(results) == 2
CodePudding user response:
You could use Moto's internal API to store the parameter, as a workaround to mocking/patching Moto.
See the following code to add a custom parameter called /aws/test
:
@mock_ssm
def test_default_param():
client = boto3.client("ssm", region_name="us-east-1")
from moto.ssm.models import ssm_backends, Parameter
ssm_backends["123456789012"]["us-east-1"]._parameters["/aws/test"].append(Parameter(
account_id="123456789012",
name="/aws/test",
value="val",
parameter_type="String",
description="...",
allowed_pattern=None,
keyid=None,
last_modified_date=1664230158.399,
version=None,
tags=[],
data_type="text",
))
response = client.get_parameters(Names=["/aws/test"])
print(response)
Note that this works in the latest version of Moto (4.0.6), but as it's an internal API, it is liable to change.