手动修补的一个问题boto3.session
是,如果您使用除 之外的其他 AWS 服务secretsmanager
,那么模拟的补丁将应用于所有内容,这可能不是您想要的。
我在这里提出 2 个专门用于修补/包装 secretsmanager 功能的解决方案:
- 请参阅下面的./test_secret_manual_amend.py。拦截
botocore.client.BaseClient._make_api_call.GetSecretValue
并控制响应的方式。这是更灵活的解决方案,因为您可以自由检查访问的秘密名称、引发异常并返回您喜欢的任何内容。
- 请参阅下面的./test_secret_using_moto.py。使用
moto.mock_secretsmanager
. 这是最简洁的方法,因为它模仿了您通常与实际 AWs secretsmanager 交互的方式。但可能不如解决方案 1 灵活。
./src.py
import boto3
sendgrid_api_key_arn = "some_name"
region_name = "ap-southeast-1"
credentials = {}
def do_stuff():
session = boto3.session.Session(**credentials)
client = session.client(service_name="secretsmanager", region_name=region_name)
return client.get_secret_value(SecretId=sendgrid_api_key_arn)
./test_secret_manual_amend.py
import botocore
import json
import pytest
from src import do_stuff, sendgrid_api_key_arn
def _amend_get_secret_value(secret_name, secret_value, mocker):
orig = botocore.client.BaseClient._make_api_call
def amend_make_api_call(self, operation_name, kwargs):
# Intercept boto3 operations for <secretsmanager.get_secret_value>. Optionally, you can also
# check on the argument <SecretId> and control how you want the response would be. This is
# a very flexible solution as you have full control over the whole process of fetching a
# secret.
if operation_name == 'GetSecretValue' and kwargs["SecretId"] == secret_name:
if isinstance(secret_value, Exception):
raise secret_value
return {
'Name': secret_name,
'SecretString': secret_value,
}
return orig(self, operation_name, kwargs)
mocker.patch('botocore.client.BaseClient._make_api_call', new=amend_make_api_call)
@pytest.mark.parametrize(
'secret_value',
[
"some value",
str(1993),
json.dumps({"SecretString": "my-secret"}),
json.dumps([2, 3, 5, 7, 11, 13, 17, 19]),
KeyError("How dare you touch my secret!"),
ValueError("Oh my goodness you even have the guts to repeat it!!!"),
],
)
def test_secret_manual_amend(secret_value, mocker):
_amend_get_secret_value(sendgrid_api_key_arn, secret_value, mocker)
if isinstance(secret_value, Exception):
with pytest.raises(type(secret_value)) as error:
do_stuff()
result = error
else:
result = do_stuff()
print("Result:", result)
./test_secret_using_moto.py
import boto3
import json
from moto import mock_secretsmanager
import pytest
from src import do_stuff, sendgrid_api_key_arn
def _setup_secrets_manager(secret_name, secret_value):
secret_manager = boto3.client('secretsmanager')
secret_manager.create_secret(
Name=secret_name,
SecretString=secret_value,
)
@mock_secretsmanager
@pytest.mark.parametrize(
'secret_value',
[
"some value",
str(1993),
json.dumps({"SecretString": "my-secret"}),
json.dumps([2, 3, 5, 7, 11, 13, 17, 19]),
],
)
def test_secret_using_moto(secret_value):
_setup_secrets_manager(sendgrid_api_key_arn, secret_value)
result = do_stuff()
print("Result:", result)
输出:
$ pytest -rP
====================================================================================== test session starts ======================================================================================
test_secret_manual_amend.py ...... [ 60%]
test_secret_using_moto.py .... [100%]
============================================================================================ PASSES =============================================================================================
_____________________________________________________________________________ test_secret_manual_amend[some value] ______________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: {'Name': 'some_name', 'SecretString': 'some value'}
________________________________________________________________________________ test_secret_manual_amend[1993] _________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: {'Name': 'some_name', 'SecretString': '1993'}
____________________________________________________________________ test_secret_manual_amend[{"SecretString": "my-secret"}] ____________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: {'Name': 'some_name', 'SecretString': '{"SecretString": "my-secret"}'}
____________________________________________________________________ test_secret_manual_amend[[2, 3, 5, 7, 11, 13, 17, 19]] _____________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: {'Name': 'some_name', 'SecretString': '[2, 3, 5, 7, 11, 13, 17, 19]'}
____________________________________________________________________________ test_secret_manual_amend[secret_value4] ____________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: <ExceptionInfo KeyError('How dare you touch my secret!') tblen=4>
____________________________________________________________________________ test_secret_manual_amend[secret_value5] ____________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: <ExceptionInfo ValueError('Oh my goodness you even have the guts to repeat it!!!') tblen=4>
______________________________________________________________________________ test_secret_using_moto[some value] _______________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: {'ARN': 'arn:aws:secretsmanager:ap-southeast-1:1234567890:secret:some_name-ghtZn', 'Name': 'some_name', 'VersionId': '42a45aad-8764-49fb-b1ad-67759063f804', 'SecretString': 'some value', 'VersionStages': ['AWSCURRENT'], 'CreatedDate': datetime.datetime(2021, 5, 13, 15, 3, 43, tzinfo=tzlocal()), 'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'amazon.com'}, 'RetryAttempts': 0}}
_________________________________________________________________________________ test_secret_using_moto[1993] __________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: {'ARN': 'arn:aws:secretsmanager:ap-southeast-1:1234567890:secret:some_name-JdOXC', 'Name': 'some_name', 'VersionId': '098b2878-4368-4f25-b75d-942e82745257', 'SecretString': '1993', 'VersionStages': ['AWSCURRENT'], 'CreatedDate': datetime.datetime(2021, 5, 13, 15, 3, 44, tzinfo=tzlocal()), 'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'amazon.com'}, 'RetryAttempts': 0}}
_____________________________________________________________________ test_secret_using_moto[{"SecretString": "my-secret"}] _____________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: {'ARN': 'arn:aws:secretsmanager:ap-southeast-1:1234567890:secret:some_name-jQgDK', 'Name': 'some_name', 'VersionId': '04bd6ceb-6ec9-427b-817c-c90360abfcd9', 'SecretString': '{"SecretString": "my-secret"}', 'VersionStages': ['AWSCURRENT'], 'CreatedDate': datetime.datetime(2021, 5, 13, 15, 3, 44, tzinfo=tzlocal()), 'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'amazon.com'}, 'RetryAttempts': 0}}
_____________________________________________________________________ test_secret_using_moto[[2, 3, 5, 7, 11, 13, 17, 19]] ______________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Result: {'ARN': 'arn:aws:secretsmanager:ap-southeast-1:1234567890:secret:some_name-dQpLq', 'Name': 'some_name', 'VersionId': '94b7a169-c32b-4424-b47c-2dee2802c211', 'SecretString': '[2, 3, 5, 7, 11, 13, 17, 19]', 'VersionStages': ['AWSCURRENT'], 'CreatedDate': datetime.datetime(2021, 5, 13, 15, 3, 44, tzinfo=tzlocal()), 'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'amazon.com'}, 'RetryAttempts': 0}}
====================================================================================== 10 passed in 1.26s =======================================================================================