TechDebt: MyPy Config (#5633)
This commit is contained in:
parent
79aaf2bc03
commit
2500affb1d
@ -1,10 +1,11 @@
|
|||||||
from moto.core.exceptions import JsonRESTError
|
from moto.core.exceptions import JsonRESTError
|
||||||
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
|
|
||||||
class NameTooLongException(JsonRESTError):
|
class NameTooLongException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, name, location, max_limit=256):
|
def __init__(self, name: str, location: str, max_limit: int = 256):
|
||||||
message = (
|
message = (
|
||||||
f"1 validation error detected: Value '{name}' at '{location}' "
|
f"1 validation error detected: Value '{name}' at '{location}' "
|
||||||
f"failed to satisfy constraint: Member must have length less "
|
f"failed to satisfy constraint: Member must have length less "
|
||||||
@ -16,7 +17,7 @@ class NameTooLongException(JsonRESTError):
|
|||||||
class InvalidConfigurationRecorderNameException(JsonRESTError):
|
class InvalidConfigurationRecorderNameException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name: Optional[str]):
|
||||||
message = (
|
message = (
|
||||||
f"The configuration recorder name '{name}' is not valid, blank string."
|
f"The configuration recorder name '{name}' is not valid, blank string."
|
||||||
)
|
)
|
||||||
@ -26,7 +27,7 @@ class InvalidConfigurationRecorderNameException(JsonRESTError):
|
|||||||
class MaxNumberOfConfigurationRecordersExceededException(JsonRESTError):
|
class MaxNumberOfConfigurationRecordersExceededException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name: str):
|
||||||
message = (
|
message = (
|
||||||
f"Failed to put configuration recorder '{name}' because the maximum number of "
|
f"Failed to put configuration recorder '{name}' because the maximum number of "
|
||||||
"configuration recorders: 1 is reached."
|
"configuration recorders: 1 is reached."
|
||||||
@ -37,7 +38,7 @@ class MaxNumberOfConfigurationRecordersExceededException(JsonRESTError):
|
|||||||
class InvalidRecordingGroupException(JsonRESTError):
|
class InvalidRecordingGroupException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
message = "The recording group provided is not valid"
|
message = "The recording group provided is not valid"
|
||||||
super().__init__("InvalidRecordingGroupException", message)
|
super().__init__("InvalidRecordingGroupException", message)
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ class InvalidRecordingGroupException(JsonRESTError):
|
|||||||
class InvalidResourceTypeException(JsonRESTError):
|
class InvalidResourceTypeException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, bad_list, good_list):
|
def __init__(self, bad_list: List[str], good_list: Any):
|
||||||
message = (
|
message = (
|
||||||
f"{len(bad_list)} validation error detected: Value '{bad_list}' at "
|
f"{len(bad_list)} validation error detected: Value '{bad_list}' at "
|
||||||
"'configurationRecorder.recordingGroup.resourceTypes' failed to satisfy constraint: "
|
"'configurationRecorder.recordingGroup.resourceTypes' failed to satisfy constraint: "
|
||||||
@ -58,7 +59,7 @@ class InvalidResourceTypeException(JsonRESTError):
|
|||||||
class NoSuchConfigurationAggregatorException(JsonRESTError):
|
class NoSuchConfigurationAggregatorException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, number=1):
|
def __init__(self, number: int = 1):
|
||||||
if number == 1:
|
if number == 1:
|
||||||
message = "The configuration aggregator does not exist. Check the configuration aggregator name and try again."
|
message = "The configuration aggregator does not exist. Check the configuration aggregator name and try again."
|
||||||
else:
|
else:
|
||||||
@ -72,7 +73,7 @@ class NoSuchConfigurationAggregatorException(JsonRESTError):
|
|||||||
class NoSuchConfigurationRecorderException(JsonRESTError):
|
class NoSuchConfigurationRecorderException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name: str):
|
||||||
message = (
|
message = (
|
||||||
f"Cannot find configuration recorder with the specified name '{name}'."
|
f"Cannot find configuration recorder with the specified name '{name}'."
|
||||||
)
|
)
|
||||||
@ -82,7 +83,7 @@ class NoSuchConfigurationRecorderException(JsonRESTError):
|
|||||||
class InvalidDeliveryChannelNameException(JsonRESTError):
|
class InvalidDeliveryChannelNameException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name: Optional[str]):
|
||||||
message = f"The delivery channel name '{name}' is not valid, blank string."
|
message = f"The delivery channel name '{name}' is not valid, blank string."
|
||||||
super().__init__("InvalidDeliveryChannelNameException", message)
|
super().__init__("InvalidDeliveryChannelNameException", message)
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ class NoSuchBucketException(JsonRESTError):
|
|||||||
|
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
message = "Cannot find a S3 bucket with an empty bucket name."
|
message = "Cannot find a S3 bucket with an empty bucket name."
|
||||||
super().__init__("NoSuchBucketException", message)
|
super().__init__("NoSuchBucketException", message)
|
||||||
|
|
||||||
@ -100,7 +101,7 @@ class NoSuchBucketException(JsonRESTError):
|
|||||||
class InvalidNextTokenException(JsonRESTError):
|
class InvalidNextTokenException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
message = "The nextToken provided is invalid"
|
message = "The nextToken provided is invalid"
|
||||||
super().__init__("InvalidNextTokenException", message)
|
super().__init__("InvalidNextTokenException", message)
|
||||||
|
|
||||||
@ -108,7 +109,7 @@ class InvalidNextTokenException(JsonRESTError):
|
|||||||
class InvalidS3KeyPrefixException(JsonRESTError):
|
class InvalidS3KeyPrefixException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
message = "The s3 key prefix '' is not valid, empty s3 key prefix."
|
message = "The s3 key prefix '' is not valid, empty s3 key prefix."
|
||||||
super().__init__("InvalidS3KeyPrefixException", message)
|
super().__init__("InvalidS3KeyPrefixException", message)
|
||||||
|
|
||||||
@ -118,7 +119,7 @@ class InvalidSNSTopicARNException(JsonRESTError):
|
|||||||
|
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
message = "The sns topic arn '' is not valid."
|
message = "The sns topic arn '' is not valid."
|
||||||
super().__init__("InvalidSNSTopicARNException", message)
|
super().__init__("InvalidSNSTopicARNException", message)
|
||||||
|
|
||||||
@ -126,7 +127,7 @@ class InvalidSNSTopicARNException(JsonRESTError):
|
|||||||
class InvalidDeliveryFrequency(JsonRESTError):
|
class InvalidDeliveryFrequency(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, value, good_list):
|
def __init__(self, value: str, good_list: Any):
|
||||||
message = (
|
message = (
|
||||||
f"1 validation error detected: Value '{value}' at "
|
f"1 validation error detected: Value '{value}' at "
|
||||||
"'deliveryChannel.configSnapshotDeliveryProperties.deliveryFrequency' failed to satisfy "
|
"'deliveryChannel.configSnapshotDeliveryProperties.deliveryFrequency' failed to satisfy "
|
||||||
@ -138,7 +139,7 @@ class InvalidDeliveryFrequency(JsonRESTError):
|
|||||||
class MaxNumberOfDeliveryChannelsExceededException(JsonRESTError):
|
class MaxNumberOfDeliveryChannelsExceededException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name: str):
|
||||||
message = f"Failed to put delivery channel '{name}' because the maximum number of delivery channels: 1 is reached."
|
message = f"Failed to put delivery channel '{name}' because the maximum number of delivery channels: 1 is reached."
|
||||||
super().__init__("MaxNumberOfDeliveryChannelsExceededException", message)
|
super().__init__("MaxNumberOfDeliveryChannelsExceededException", message)
|
||||||
|
|
||||||
@ -146,7 +147,7 @@ class MaxNumberOfDeliveryChannelsExceededException(JsonRESTError):
|
|||||||
class NoSuchDeliveryChannelException(JsonRESTError):
|
class NoSuchDeliveryChannelException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name: str):
|
||||||
message = f"Cannot find delivery channel with specified name '{name}'."
|
message = f"Cannot find delivery channel with specified name '{name}'."
|
||||||
super().__init__("NoSuchDeliveryChannelException", message)
|
super().__init__("NoSuchDeliveryChannelException", message)
|
||||||
|
|
||||||
@ -154,7 +155,7 @@ class NoSuchDeliveryChannelException(JsonRESTError):
|
|||||||
class NoAvailableConfigurationRecorderException(JsonRESTError):
|
class NoAvailableConfigurationRecorderException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
message = "Configuration recorder is not available to put delivery channel."
|
message = "Configuration recorder is not available to put delivery channel."
|
||||||
super().__init__("NoAvailableConfigurationRecorderException", message)
|
super().__init__("NoAvailableConfigurationRecorderException", message)
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ class NoAvailableConfigurationRecorderException(JsonRESTError):
|
|||||||
class NoAvailableDeliveryChannelException(JsonRESTError):
|
class NoAvailableDeliveryChannelException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
message = "Delivery channel is not available to start configuration recorder."
|
message = "Delivery channel is not available to start configuration recorder."
|
||||||
super().__init__("NoAvailableDeliveryChannelException", message)
|
super().__init__("NoAvailableDeliveryChannelException", message)
|
||||||
|
|
||||||
@ -170,7 +171,7 @@ class NoAvailableDeliveryChannelException(JsonRESTError):
|
|||||||
class LastDeliveryChannelDeleteFailedException(JsonRESTError):
|
class LastDeliveryChannelDeleteFailedException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name: str):
|
||||||
message = (
|
message = (
|
||||||
f"Failed to delete last specified delivery channel with name '{name}', because there, "
|
f"Failed to delete last specified delivery channel with name '{name}', because there, "
|
||||||
"because there is a running configuration recorder."
|
"because there is a running configuration recorder."
|
||||||
@ -181,7 +182,7 @@ class LastDeliveryChannelDeleteFailedException(JsonRESTError):
|
|||||||
class TooManyAccountSources(JsonRESTError):
|
class TooManyAccountSources(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, length):
|
def __init__(self, length: int):
|
||||||
locations = ["com.amazonaws.xyz"] * length
|
locations = ["com.amazonaws.xyz"] * length
|
||||||
|
|
||||||
message = (
|
message = (
|
||||||
@ -196,7 +197,7 @@ class TooManyAccountSources(JsonRESTError):
|
|||||||
class DuplicateTags(JsonRESTError):
|
class DuplicateTags(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"InvalidInput",
|
"InvalidInput",
|
||||||
"Duplicate tag keys found. Please note that Tag keys are case insensitive.",
|
"Duplicate tag keys found. Please note that Tag keys are case insensitive.",
|
||||||
@ -206,7 +207,7 @@ class DuplicateTags(JsonRESTError):
|
|||||||
class TagKeyTooBig(JsonRESTError):
|
class TagKeyTooBig(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, tag, param="tags.X.member.key"):
|
def __init__(self, tag: str, param: str = "tags.X.member.key"):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"ValidationException",
|
"ValidationException",
|
||||||
f"1 validation error detected: Value '{tag}' at '{param}' failed to satisfy "
|
f"1 validation error detected: Value '{tag}' at '{param}' failed to satisfy "
|
||||||
@ -217,7 +218,7 @@ class TagKeyTooBig(JsonRESTError):
|
|||||||
class TagValueTooBig(JsonRESTError):
|
class TagValueTooBig(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, tag, param="tags.X.member.value"):
|
def __init__(self, tag: str, param: str = "tags.X.member.value"):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"ValidationException",
|
"ValidationException",
|
||||||
f"1 validation error detected: Value '{tag}' at '{param}' failed to satisfy "
|
f"1 validation error detected: Value '{tag}' at '{param}' failed to satisfy "
|
||||||
@ -228,14 +229,14 @@ class TagValueTooBig(JsonRESTError):
|
|||||||
class InvalidParameterValueException(JsonRESTError):
|
class InvalidParameterValueException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, message):
|
def __init__(self, message: str):
|
||||||
super().__init__("InvalidParameterValueException", message)
|
super().__init__("InvalidParameterValueException", message)
|
||||||
|
|
||||||
|
|
||||||
class InvalidTagCharacters(JsonRESTError):
|
class InvalidTagCharacters(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, tag, param="tags.X.member.key"):
|
def __init__(self, tag: str, param: str = "tags.X.member.key"):
|
||||||
message = f"1 validation error detected: Value '{tag}' at '{param}' failed to satisfy "
|
message = f"1 validation error detected: Value '{tag}' at '{param}' failed to satisfy "
|
||||||
message += "constraint: Member must satisfy regular expression pattern: [\\\\p{L}\\\\p{Z}\\\\p{N}_.:/=+\\\\-@]+"
|
message += "constraint: Member must satisfy regular expression pattern: [\\\\p{L}\\\\p{Z}\\\\p{N}_.:/=+\\\\-@]+"
|
||||||
|
|
||||||
@ -245,7 +246,7 @@ class InvalidTagCharacters(JsonRESTError):
|
|||||||
class TooManyTags(JsonRESTError):
|
class TooManyTags(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, tags, param="tags"):
|
def __init__(self, tags: Any, param: str = "tags"):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"ValidationException",
|
"ValidationException",
|
||||||
f"1 validation error detected: Value '{tags}' at '{param}' failed to satisfy "
|
f"1 validation error detected: Value '{tags}' at '{param}' failed to satisfy "
|
||||||
@ -256,7 +257,7 @@ class TooManyTags(JsonRESTError):
|
|||||||
class InvalidResourceParameters(JsonRESTError):
|
class InvalidResourceParameters(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"ValidationException",
|
"ValidationException",
|
||||||
"Both Resource ID and Resource Name " "cannot be specified in the request",
|
"Both Resource ID and Resource Name " "cannot be specified in the request",
|
||||||
@ -266,7 +267,7 @@ class InvalidResourceParameters(JsonRESTError):
|
|||||||
class InvalidLimitException(JsonRESTError):
|
class InvalidLimitException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, value):
|
def __init__(self, value: int):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"InvalidLimitException",
|
"InvalidLimitException",
|
||||||
f"Value '{value}' at 'limit' failed to satisfy constraint: Member"
|
f"Value '{value}' at 'limit' failed to satisfy constraint: Member"
|
||||||
@ -277,7 +278,7 @@ class InvalidLimitException(JsonRESTError):
|
|||||||
class TooManyResourceIds(JsonRESTError):
|
class TooManyResourceIds(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"ValidationException",
|
"ValidationException",
|
||||||
"The specified list had more than 20 resource ID's. "
|
"The specified list had more than 20 resource ID's. "
|
||||||
@ -288,7 +289,7 @@ class TooManyResourceIds(JsonRESTError):
|
|||||||
class ResourceNotDiscoveredException(JsonRESTError):
|
class ResourceNotDiscoveredException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, resource_type, resource):
|
def __init__(self, resource_type: str, resource: str):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"ResourceNotDiscoveredException",
|
"ResourceNotDiscoveredException",
|
||||||
f"Resource {resource} of resourceType:{resource_type} is unknown or has not been discovered",
|
f"Resource {resource} of resourceType:{resource_type} is unknown or has not been discovered",
|
||||||
@ -298,7 +299,7 @@ class ResourceNotDiscoveredException(JsonRESTError):
|
|||||||
class ResourceNotFoundException(JsonRESTError):
|
class ResourceNotFoundException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, resource_arn):
|
def __init__(self, resource_arn: str):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"ResourceNotFoundException", f"ResourceArn '{resource_arn}' does not exist"
|
"ResourceNotFoundException", f"ResourceArn '{resource_arn}' does not exist"
|
||||||
)
|
)
|
||||||
@ -307,7 +308,7 @@ class ResourceNotFoundException(JsonRESTError):
|
|||||||
class TooManyResourceKeys(JsonRESTError):
|
class TooManyResourceKeys(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, bad_list):
|
def __init__(self, bad_list: List[str]):
|
||||||
message = (
|
message = (
|
||||||
f"1 validation error detected: Value '{bad_list}' at "
|
f"1 validation error detected: Value '{bad_list}' at "
|
||||||
"'resourceKeys' failed to satisfy constraint: "
|
"'resourceKeys' failed to satisfy constraint: "
|
||||||
@ -319,7 +320,7 @@ class TooManyResourceKeys(JsonRESTError):
|
|||||||
class InvalidResultTokenException(JsonRESTError):
|
class InvalidResultTokenException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
message = "The resultToken provided is invalid"
|
message = "The resultToken provided is invalid"
|
||||||
super().__init__("InvalidResultTokenException", message)
|
super().__init__("InvalidResultTokenException", message)
|
||||||
|
|
||||||
@ -327,21 +328,21 @@ class InvalidResultTokenException(JsonRESTError):
|
|||||||
class ValidationException(JsonRESTError):
|
class ValidationException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, message):
|
def __init__(self, message: str):
|
||||||
super().__init__("ValidationException", message)
|
super().__init__("ValidationException", message)
|
||||||
|
|
||||||
|
|
||||||
class NoSuchOrganizationConformancePackException(JsonRESTError):
|
class NoSuchOrganizationConformancePackException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, message):
|
def __init__(self, message: str):
|
||||||
super().__init__("NoSuchOrganizationConformancePackException", message)
|
super().__init__("NoSuchOrganizationConformancePackException", message)
|
||||||
|
|
||||||
|
|
||||||
class MaxNumberOfConfigRulesExceededException(JsonRESTError):
|
class MaxNumberOfConfigRulesExceededException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, name, max_limit):
|
def __init__(self, name: str, max_limit: int):
|
||||||
message = f"Failed to put config rule '{name}' because the maximum number of config rules: {max_limit} is reached."
|
message = f"Failed to put config rule '{name}' because the maximum number of config rules: {max_limit} is reached."
|
||||||
super().__init__("MaxNumberOfConfigRulesExceededException", message)
|
super().__init__("MaxNumberOfConfigRulesExceededException", message)
|
||||||
|
|
||||||
@ -349,21 +350,21 @@ class MaxNumberOfConfigRulesExceededException(JsonRESTError):
|
|||||||
class ResourceInUseException(JsonRESTError):
|
class ResourceInUseException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, message):
|
def __init__(self, message: str):
|
||||||
super().__init__("ResourceInUseException", message)
|
super().__init__("ResourceInUseException", message)
|
||||||
|
|
||||||
|
|
||||||
class InsufficientPermissionsException(JsonRESTError):
|
class InsufficientPermissionsException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, message):
|
def __init__(self, message: str):
|
||||||
super().__init__("InsufficientPermissionsException", message)
|
super().__init__("InsufficientPermissionsException", message)
|
||||||
|
|
||||||
|
|
||||||
class NoSuchConfigRuleException(JsonRESTError):
|
class NoSuchConfigRuleException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, rule_name):
|
def __init__(self, rule_name: str):
|
||||||
message = f"The ConfigRule '{rule_name}' provided in the request is invalid. Please check the configRule name"
|
message = f"The ConfigRule '{rule_name}' provided in the request is invalid. Please check the configRule name"
|
||||||
super().__init__("NoSuchConfigRuleException", message)
|
super().__init__("NoSuchConfigRuleException", message)
|
||||||
|
|
||||||
@ -371,5 +372,5 @@ class NoSuchConfigRuleException(JsonRESTError):
|
|||||||
class MissingRequiredConfigRuleParameterException(JsonRESTError):
|
class MissingRequiredConfigRuleParameterException(JsonRESTError):
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
def __init__(self, message):
|
def __init__(self, message: str):
|
||||||
super().__init__("ParamValidationError", message)
|
super().__init__("ParamValidationError", message)
|
||||||
|
@ -4,6 +4,7 @@ import re
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
from moto.config.exceptions import (
|
from moto.config.exceptions import (
|
||||||
InvalidResourceTypeException,
|
InvalidResourceTypeException,
|
||||||
@ -48,6 +49,7 @@ from moto.config.exceptions import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from moto.core import BaseBackend, BaseModel
|
from moto.core import BaseBackend, BaseModel
|
||||||
|
from moto.core.common_models import ConfigQueryModel
|
||||||
from moto.core.responses import AWSServiceSpec
|
from moto.core.responses import AWSServiceSpec
|
||||||
from moto.core.utils import BackendDict
|
from moto.core.utils import BackendDict
|
||||||
from moto.iam.config import role_config_query, policy_config_query
|
from moto.iam.config import role_config_query, policy_config_query
|
||||||
@ -69,7 +71,7 @@ DEFAULT_PAGE_SIZE = 100
|
|||||||
CONFIG_RULE_PAGE_SIZE = 25
|
CONFIG_RULE_PAGE_SIZE = 25
|
||||||
|
|
||||||
# Map the Config resource type to a backend:
|
# Map the Config resource type to a backend:
|
||||||
RESOURCE_MAP = {
|
RESOURCE_MAP: Dict[str, ConfigQueryModel] = {
|
||||||
"AWS::S3::Bucket": s3_config_query,
|
"AWS::S3::Bucket": s3_config_query,
|
||||||
"AWS::S3::AccountPublicAccessBlock": s3_account_public_access_block_query,
|
"AWS::S3::AccountPublicAccessBlock": s3_account_public_access_block_query,
|
||||||
"AWS::IAM::Role": role_config_query,
|
"AWS::IAM::Role": role_config_query,
|
||||||
@ -81,14 +83,14 @@ CAMEL_TO_SNAKE_REGEX = re.compile(r"(?<!^)(?=[A-Z])")
|
|||||||
MAX_TAGS_IN_ARG = 50
|
MAX_TAGS_IN_ARG = 50
|
||||||
|
|
||||||
MANAGED_RULES = load_resource(__name__, "resources/aws_managed_rules.json")
|
MANAGED_RULES = load_resource(__name__, "resources/aws_managed_rules.json")
|
||||||
MANAGED_RULES_CONSTRAINTS = MANAGED_RULES["ManagedRules"]
|
MANAGED_RULES_CONSTRAINTS = MANAGED_RULES["ManagedRules"] # type: ignore[index]
|
||||||
|
|
||||||
|
|
||||||
def datetime2int(date):
|
def datetime2int(date: datetime) -> int:
|
||||||
return int(time.mktime(date.timetuple()))
|
return int(time.mktime(date.timetuple()))
|
||||||
|
|
||||||
|
|
||||||
def snake_to_camels(original, cap_start, cap_arn):
|
def snake_to_camels(original: str, cap_start: bool, cap_arn: bool) -> str:
|
||||||
parts = original.split("_")
|
parts = original.split("_")
|
||||||
|
|
||||||
camel_cased = parts[0].lower() + "".join(p.title() for p in parts[1:])
|
camel_cased = parts[0].lower() + "".join(p.title() for p in parts[1:])
|
||||||
@ -104,12 +106,12 @@ def snake_to_camels(original, cap_start, cap_arn):
|
|||||||
return camel_cased
|
return camel_cased
|
||||||
|
|
||||||
|
|
||||||
def random_string():
|
def random_string() -> str:
|
||||||
"""Returns a random set of 8 lowercase letters for the Config Aggregator ARN"""
|
"""Returns a random set of 8 lowercase letters for the Config Aggregator ARN"""
|
||||||
return random.get_random_string(length=8, include_digits=False, lower_case=True)
|
return random.get_random_string(length=8, include_digits=False, lower_case=True)
|
||||||
|
|
||||||
|
|
||||||
def validate_tag_key(tag_key, exception_param="tags.X.member.key"):
|
def validate_tag_key(tag_key: str, exception_param: str = "tags.X.member.key") -> None:
|
||||||
"""Validates the tag key.
|
"""Validates the tag key.
|
||||||
|
|
||||||
:param tag_key: The tag key to check against.
|
:param tag_key: The tag key to check against.
|
||||||
@ -131,7 +133,7 @@ def validate_tag_key(tag_key, exception_param="tags.X.member.key"):
|
|||||||
raise InvalidTagCharacters(tag_key, param=exception_param)
|
raise InvalidTagCharacters(tag_key, param=exception_param)
|
||||||
|
|
||||||
|
|
||||||
def check_tag_duplicate(all_tags, tag_key):
|
def check_tag_duplicate(all_tags: Dict[str, str], tag_key: str) -> None:
|
||||||
"""Validates that a tag key is not a duplicate
|
"""Validates that a tag key is not a duplicate
|
||||||
|
|
||||||
:param all_tags: Dict to check if there is a duplicate tag.
|
:param all_tags: Dict to check if there is a duplicate tag.
|
||||||
@ -142,8 +144,8 @@ def check_tag_duplicate(all_tags, tag_key):
|
|||||||
raise DuplicateTags()
|
raise DuplicateTags()
|
||||||
|
|
||||||
|
|
||||||
def validate_tags(tags):
|
def validate_tags(tags: List[Dict[str, str]]) -> Dict[str, str]:
|
||||||
proper_tags = {}
|
proper_tags: Dict[str, str] = {}
|
||||||
|
|
||||||
if len(tags) > MAX_TAGS_IN_ARG:
|
if len(tags) > MAX_TAGS_IN_ARG:
|
||||||
raise TooManyTags(tags)
|
raise TooManyTags(tags)
|
||||||
@ -162,7 +164,7 @@ def validate_tags(tags):
|
|||||||
return proper_tags
|
return proper_tags
|
||||||
|
|
||||||
|
|
||||||
def convert_to_class_args(dict_arg):
|
def convert_to_class_args(dict_arg: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Return dict that can be used to instantiate it's representative class.
|
"""Return dict that can be used to instantiate it's representative class.
|
||||||
|
|
||||||
Given a dictionary in the incoming API request, convert the keys to
|
Given a dictionary in the incoming API request, convert the keys to
|
||||||
@ -184,7 +186,7 @@ class ConfigEmptyDictable(BaseModel):
|
|||||||
This assumes that the sub-class will NOT return 'None's in the JSON.
|
This assumes that the sub-class will NOT return 'None's in the JSON.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, capitalize_start=False, capitalize_arn=True):
|
def __init__(self, capitalize_start: bool = False, capitalize_arn: bool = True):
|
||||||
"""Assists with the serialization of the config object
|
"""Assists with the serialization of the config object
|
||||||
:param capitalize_start: For some Config services, the first letter
|
:param capitalize_start: For some Config services, the first letter
|
||||||
is lowercase -- for others it's capital
|
is lowercase -- for others it's capital
|
||||||
@ -194,8 +196,8 @@ class ConfigEmptyDictable(BaseModel):
|
|||||||
self.capitalize_start = capitalize_start
|
self.capitalize_start = capitalize_start
|
||||||
self.capitalize_arn = capitalize_arn
|
self.capitalize_arn = capitalize_arn
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
data = {}
|
data: Dict[str, Any] = {}
|
||||||
for item, value in self.__dict__.items():
|
for item, value in self.__dict__.items():
|
||||||
# ignore private attributes
|
# ignore private attributes
|
||||||
if not item.startswith("_") and value is not None:
|
if not item.startswith("_") and value is not None:
|
||||||
@ -220,32 +222,32 @@ class ConfigEmptyDictable(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ConfigRecorderStatus(ConfigEmptyDictable):
|
class ConfigRecorderStatus(ConfigEmptyDictable):
|
||||||
def __init__(self, name):
|
def __init__(self, name: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.recording = False
|
self.recording = False
|
||||||
self.last_start_time = None
|
self.last_start_time: Optional[int] = None
|
||||||
self.last_stop_time = None
|
self.last_stop_time: Optional[int] = None
|
||||||
self.last_status = None
|
self.last_status: Optional[str] = None
|
||||||
self.last_error_code = None
|
self.last_error_code: Optional[str] = None
|
||||||
self.last_error_message = None
|
self.last_error_message: Optional[str] = None
|
||||||
self.last_status_change_time = None
|
self.last_status_change_time: Optional[int] = None
|
||||||
|
|
||||||
def start(self):
|
def start(self) -> None:
|
||||||
self.recording = True
|
self.recording = True
|
||||||
self.last_status = "PENDING"
|
self.last_status = "PENDING"
|
||||||
self.last_start_time = datetime2int(datetime.utcnow())
|
self.last_start_time = datetime2int(datetime.utcnow())
|
||||||
self.last_status_change_time = datetime2int(datetime.utcnow())
|
self.last_status_change_time = datetime2int(datetime.utcnow())
|
||||||
|
|
||||||
def stop(self):
|
def stop(self) -> None:
|
||||||
self.recording = False
|
self.recording = False
|
||||||
self.last_stop_time = datetime2int(datetime.utcnow())
|
self.last_stop_time = datetime2int(datetime.utcnow())
|
||||||
self.last_status_change_time = datetime2int(datetime.utcnow())
|
self.last_status_change_time = datetime2int(datetime.utcnow())
|
||||||
|
|
||||||
|
|
||||||
class ConfigDeliverySnapshotProperties(ConfigEmptyDictable):
|
class ConfigDeliverySnapshotProperties(ConfigEmptyDictable):
|
||||||
def __init__(self, delivery_frequency):
|
def __init__(self, delivery_frequency: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.delivery_frequency = delivery_frequency
|
self.delivery_frequency = delivery_frequency
|
||||||
@ -253,7 +255,12 @@ class ConfigDeliverySnapshotProperties(ConfigEmptyDictable):
|
|||||||
|
|
||||||
class ConfigDeliveryChannel(ConfigEmptyDictable):
|
class ConfigDeliveryChannel(ConfigEmptyDictable):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, name, s3_bucket_name, prefix=None, sns_arn=None, snapshot_properties=None
|
self,
|
||||||
|
name: str,
|
||||||
|
s3_bucket_name: str,
|
||||||
|
prefix: Optional[str] = None,
|
||||||
|
sns_arn: Optional[str] = None,
|
||||||
|
snapshot_properties: Optional[ConfigDeliverySnapshotProperties] = None,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
@ -267,9 +274,9 @@ class ConfigDeliveryChannel(ConfigEmptyDictable):
|
|||||||
class RecordingGroup(ConfigEmptyDictable):
|
class RecordingGroup(ConfigEmptyDictable):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
all_supported=True,
|
all_supported: bool = True,
|
||||||
include_global_resource_types=False,
|
include_global_resource_types: bool = False,
|
||||||
resource_types=None,
|
resource_types: Optional[List[str]] = None,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
@ -279,7 +286,13 @@ class RecordingGroup(ConfigEmptyDictable):
|
|||||||
|
|
||||||
|
|
||||||
class ConfigRecorder(ConfigEmptyDictable):
|
class ConfigRecorder(ConfigEmptyDictable):
|
||||||
def __init__(self, role_arn, recording_group, name="default", status=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
role_arn: str,
|
||||||
|
recording_group: RecordingGroup,
|
||||||
|
name: str = "default",
|
||||||
|
status: Optional[ConfigRecorderStatus] = None,
|
||||||
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -293,7 +306,12 @@ class ConfigRecorder(ConfigEmptyDictable):
|
|||||||
|
|
||||||
|
|
||||||
class AccountAggregatorSource(ConfigEmptyDictable):
|
class AccountAggregatorSource(ConfigEmptyDictable):
|
||||||
def __init__(self, account_ids, aws_regions=None, all_aws_regions=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
account_ids: List[str],
|
||||||
|
aws_regions: Optional[List[str]] = None,
|
||||||
|
all_aws_regions: Optional[bool] = None,
|
||||||
|
):
|
||||||
super().__init__(capitalize_start=True)
|
super().__init__(capitalize_start=True)
|
||||||
|
|
||||||
# Can't have both the regions and all_regions flag present -- also
|
# Can't have both the regions and all_regions flag present -- also
|
||||||
@ -321,7 +339,12 @@ class AccountAggregatorSource(ConfigEmptyDictable):
|
|||||||
|
|
||||||
|
|
||||||
class OrganizationAggregationSource(ConfigEmptyDictable):
|
class OrganizationAggregationSource(ConfigEmptyDictable):
|
||||||
def __init__(self, role_arn, aws_regions=None, all_aws_regions=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
role_arn: str,
|
||||||
|
aws_regions: Optional[List[str]] = None,
|
||||||
|
all_aws_regions: Optional[bool] = None,
|
||||||
|
):
|
||||||
super().__init__(capitalize_start=True, capitalize_arn=False)
|
super().__init__(capitalize_start=True, capitalize_arn=False)
|
||||||
|
|
||||||
# Can't have both the regions and all_regions flag present -- also
|
# Can't have both the regions and all_regions flag present -- also
|
||||||
@ -349,7 +372,13 @@ class OrganizationAggregationSource(ConfigEmptyDictable):
|
|||||||
|
|
||||||
class ConfigAggregator(ConfigEmptyDictable):
|
class ConfigAggregator(ConfigEmptyDictable):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, name, account_id, region, account_sources=None, org_source=None, tags=None
|
self,
|
||||||
|
name: str,
|
||||||
|
account_id: str,
|
||||||
|
region: str,
|
||||||
|
account_sources: Optional[List[AccountAggregatorSource]] = None,
|
||||||
|
org_source: Optional[OrganizationAggregationSource] = None,
|
||||||
|
tags: Optional[Dict[str, str]] = None,
|
||||||
):
|
):
|
||||||
super().__init__(capitalize_start=True, capitalize_arn=False)
|
super().__init__(capitalize_start=True, capitalize_arn=False)
|
||||||
|
|
||||||
@ -364,7 +393,7 @@ class ConfigAggregator(ConfigEmptyDictable):
|
|||||||
self.tags = tags or {}
|
self.tags = tags or {}
|
||||||
|
|
||||||
# Override the to_dict so that we can format the tags properly...
|
# Override the to_dict so that we can format the tags properly...
|
||||||
def to_dict(self):
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
result = super().to_dict()
|
result = super().to_dict()
|
||||||
|
|
||||||
# Override the account aggregation sources if present:
|
# Override the account aggregation sources if present:
|
||||||
@ -384,11 +413,11 @@ class ConfigAggregator(ConfigEmptyDictable):
|
|||||||
class ConfigAggregationAuthorization(ConfigEmptyDictable):
|
class ConfigAggregationAuthorization(ConfigEmptyDictable):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
account_id,
|
account_id: str,
|
||||||
current_region,
|
current_region: str,
|
||||||
authorized_account_id,
|
authorized_account_id: str,
|
||||||
authorized_aws_region,
|
authorized_aws_region: str,
|
||||||
tags=None,
|
tags: Dict[str, str],
|
||||||
):
|
):
|
||||||
super().__init__(capitalize_start=True, capitalize_arn=False)
|
super().__init__(capitalize_start=True, capitalize_arn=False)
|
||||||
|
|
||||||
@ -412,13 +441,13 @@ class ConfigAggregationAuthorization(ConfigEmptyDictable):
|
|||||||
class OrganizationConformancePack(ConfigEmptyDictable):
|
class OrganizationConformancePack(ConfigEmptyDictable):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
account_id,
|
account_id: str,
|
||||||
region,
|
region: str,
|
||||||
name,
|
name: str,
|
||||||
delivery_s3_bucket,
|
delivery_s3_bucket: str,
|
||||||
delivery_s3_key_prefix=None,
|
delivery_s3_key_prefix: Optional[str] = None,
|
||||||
input_parameters=None,
|
input_parameters: Optional[List[Dict[str, Any]]] = None,
|
||||||
excluded_accounts=None,
|
excluded_accounts: Optional[List[str]] = None,
|
||||||
):
|
):
|
||||||
super().__init__(capitalize_start=True, capitalize_arn=False)
|
super().__init__(capitalize_start=True, capitalize_arn=False)
|
||||||
|
|
||||||
@ -435,11 +464,11 @@ class OrganizationConformancePack(ConfigEmptyDictable):
|
|||||||
|
|
||||||
def update(
|
def update(
|
||||||
self,
|
self,
|
||||||
delivery_s3_bucket,
|
delivery_s3_bucket: str,
|
||||||
delivery_s3_key_prefix,
|
delivery_s3_key_prefix: str,
|
||||||
input_parameters,
|
input_parameters: List[Dict[str, Any]],
|
||||||
excluded_accounts,
|
excluded_accounts: List[str],
|
||||||
):
|
) -> None:
|
||||||
self._status = "UPDATE_SUCCESSFUL"
|
self._status = "UPDATE_SUCCESSFUL"
|
||||||
|
|
||||||
self.conformance_pack_input_parameters = input_parameters
|
self.conformance_pack_input_parameters = input_parameters
|
||||||
@ -464,10 +493,10 @@ class Scope(ConfigEmptyDictable):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
compliance_resource_types=None,
|
compliance_resource_types: Optional[List[str]] = None,
|
||||||
tag_key=None,
|
tag_key: Optional[str] = None,
|
||||||
tag_value=None,
|
tag_value: Optional[str] = None,
|
||||||
compliance_resource_id=None,
|
compliance_resource_id: Optional[str] = None,
|
||||||
):
|
):
|
||||||
super().__init__(capitalize_start=True, capitalize_arn=False)
|
super().__init__(capitalize_start=True, capitalize_arn=False)
|
||||||
self.tags = None
|
self.tags = None
|
||||||
@ -489,7 +518,7 @@ class Scope(ConfigEmptyDictable):
|
|||||||
"Scope cannot be applied to both resource and tag"
|
"Scope cannot be applied to both resource and tag"
|
||||||
)
|
)
|
||||||
|
|
||||||
if compliance_resource_id and len(compliance_resource_types) != 1:
|
if compliance_resource_id and len(compliance_resource_types) != 1: # type: ignore[arg-type]
|
||||||
raise InvalidParameterValueException(
|
raise InvalidParameterValueException(
|
||||||
"A single resourceType should be provided when resourceId "
|
"A single resourceType should be provided when resourceId "
|
||||||
"is provided in scope"
|
"is provided in scope"
|
||||||
@ -522,7 +551,10 @@ class SourceDetail(ConfigEmptyDictable):
|
|||||||
EVENT_SOURCES = ["aws.config"]
|
EVENT_SOURCES = ["aws.config"]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, event_source=None, message_type=None, maximum_execution_frequency=None
|
self,
|
||||||
|
event_source: Optional[str] = None,
|
||||||
|
message_type: Optional[str] = None,
|
||||||
|
maximum_execution_frequency: Optional[str] = None,
|
||||||
):
|
):
|
||||||
super().__init__(capitalize_start=True, capitalize_arn=False)
|
super().__init__(capitalize_start=True, capitalize_arn=False)
|
||||||
|
|
||||||
@ -599,7 +631,12 @@ class Source(ConfigEmptyDictable):
|
|||||||
OWNERS = {"AWS", "CUSTOM_LAMBDA"}
|
OWNERS = {"AWS", "CUSTOM_LAMBDA"}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, account_id, region, owner, source_identifier, source_details=None
|
self,
|
||||||
|
account_id: str,
|
||||||
|
region: str,
|
||||||
|
owner: str,
|
||||||
|
source_identifier: str,
|
||||||
|
source_details: Optional[SourceDetail] = None,
|
||||||
):
|
):
|
||||||
super().__init__(capitalize_start=True, capitalize_arn=False)
|
super().__init__(capitalize_start=True, capitalize_arn=False)
|
||||||
if owner not in Source.OWNERS:
|
if owner not in Source.OWNERS:
|
||||||
@ -651,7 +688,7 @@ class Source(ConfigEmptyDictable):
|
|||||||
)
|
)
|
||||||
|
|
||||||
details = []
|
details = []
|
||||||
for detail in source_details:
|
for detail in source_details: # type: ignore[attr-defined]
|
||||||
detail_dict = convert_to_class_args(detail)
|
detail_dict = convert_to_class_args(detail)
|
||||||
details.append(SourceDetail(**detail_dict))
|
details.append(SourceDetail(**detail_dict))
|
||||||
|
|
||||||
@ -659,7 +696,7 @@ class Source(ConfigEmptyDictable):
|
|||||||
self.owner = owner
|
self.owner = owner
|
||||||
self.source_identifier = source_identifier
|
self.source_identifier = source_identifier
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
"""Format the SourceDetails properly."""
|
"""Format the SourceDetails properly."""
|
||||||
result = super().to_dict()
|
result = super().to_dict()
|
||||||
if self.source_details:
|
if self.source_details:
|
||||||
@ -678,7 +715,13 @@ class ConfigRule(ConfigEmptyDictable):
|
|||||||
MAX_RULES = 150
|
MAX_RULES = 150
|
||||||
RULE_STATES = {"ACTIVE", "DELETING", "DELETING_RESULTS", "EVALUATING"}
|
RULE_STATES = {"ACTIVE", "DELETING", "DELETING_RESULTS", "EVALUATING"}
|
||||||
|
|
||||||
def __init__(self, account_id, region, config_rule, tags):
|
def __init__(
|
||||||
|
self,
|
||||||
|
account_id: str,
|
||||||
|
region: str,
|
||||||
|
config_rule: Dict[str, Any],
|
||||||
|
tags: Dict[str, str],
|
||||||
|
):
|
||||||
super().__init__(capitalize_start=True, capitalize_arn=False)
|
super().__init__(capitalize_start=True, capitalize_arn=False)
|
||||||
self.account_id = account_id
|
self.account_id = account_id
|
||||||
self.config_rule_name = config_rule.get("ConfigRuleName")
|
self.config_rule_name = config_rule.get("ConfigRuleName")
|
||||||
@ -697,7 +740,9 @@ class ConfigRule(ConfigEmptyDictable):
|
|||||||
f"arn:aws:config:{region}:{account_id}:config-rule/{self.config_rule_id}"
|
f"arn:aws:config:{region}:{account_id}:config-rule/{self.config_rule_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def modify_fields(self, region, config_rule, tags):
|
def modify_fields(
|
||||||
|
self, region: str, config_rule: Dict[str, Any], tags: Dict[str, str]
|
||||||
|
) -> None:
|
||||||
"""Initialize or update ConfigRule fields."""
|
"""Initialize or update ConfigRule fields."""
|
||||||
self.config_rule_state = config_rule.get("ConfigRuleState", "ACTIVE")
|
self.config_rule_state = config_rule.get("ConfigRuleState", "ACTIVE")
|
||||||
if self.config_rule_state not in ConfigRule.RULE_STATES:
|
if self.config_rule_state not in ConfigRule.RULE_STATES:
|
||||||
@ -787,23 +832,22 @@ class ConfigRule(ConfigEmptyDictable):
|
|||||||
self.last_updated_time = datetime2int(datetime.utcnow())
|
self.last_updated_time = datetime2int(datetime.utcnow())
|
||||||
self.tags = tags
|
self.tags = tags
|
||||||
|
|
||||||
def validate_managed_rule(self):
|
def validate_managed_rule(self) -> None:
|
||||||
"""Validate parameters specific to managed rules."""
|
"""Validate parameters specific to managed rules."""
|
||||||
rule_info = MANAGED_RULES_CONSTRAINTS[self.source.source_identifier]
|
rule_info = MANAGED_RULES_CONSTRAINTS[self.source.source_identifier] # type: ignore[index]
|
||||||
param_names = self.input_parameters_dict.keys()
|
param_names = self.input_parameters_dict.keys()
|
||||||
|
|
||||||
# Verify input parameter names are actual parameters for the rule ID.
|
# Verify input parameter names are actual parameters for the rule ID.
|
||||||
if param_names:
|
if param_names:
|
||||||
allowed_names = {x["Name"] for x in rule_info["Parameters"]}
|
allowed_names = {x["Name"] for x in rule_info["Parameters"]} # type: ignore[index]
|
||||||
if not set(param_names).issubset(allowed_names):
|
if not set(param_names).issubset(allowed_names):
|
||||||
raise InvalidParameterValueException(
|
raise InvalidParameterValueException(
|
||||||
"Unknown parameters provided in the inputParameters: "
|
f"Unknown parameters provided in the inputParameters: {self.input_parameters}"
|
||||||
+ self.input_parameters
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify all the required parameters are specified.
|
# Verify all the required parameters are specified.
|
||||||
required_names = {
|
required_names = {
|
||||||
x["Name"] for x in rule_info["Parameters"] if not x["Optional"]
|
x["Name"] for x in rule_info["Parameters"] if not x["Optional"] # type: ignore[index]
|
||||||
}
|
}
|
||||||
diffs = required_names.difference(set(param_names))
|
diffs = required_names.difference(set(param_names))
|
||||||
if diffs:
|
if diffs:
|
||||||
@ -849,24 +893,24 @@ class ConfigRule(ConfigEmptyDictable):
|
|||||||
|
|
||||||
|
|
||||||
class ConfigBackend(BaseBackend):
|
class ConfigBackend(BaseBackend):
|
||||||
def __init__(self, region_name, account_id):
|
def __init__(self, region_name: str, account_id: str):
|
||||||
super().__init__(region_name, account_id)
|
super().__init__(region_name, account_id)
|
||||||
self.recorders = {}
|
self.recorders: Dict[str, ConfigRecorder] = {}
|
||||||
self.delivery_channels = {}
|
self.delivery_channels: Dict[str, ConfigDeliveryChannel] = {}
|
||||||
self.config_aggregators = {}
|
self.config_aggregators: Dict[str, ConfigAggregator] = {}
|
||||||
self.aggregation_authorizations = {}
|
self.aggregation_authorizations: Dict[str, ConfigAggregationAuthorization] = {}
|
||||||
self.organization_conformance_packs = {}
|
self.organization_conformance_packs: Dict[str, OrganizationConformancePack] = {}
|
||||||
self.config_rules = {}
|
self.config_rules: Dict[str, ConfigRule] = {}
|
||||||
self.config_schema = None
|
self.config_schema: Optional[AWSServiceSpec] = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default_vpc_endpoint_service(service_region, zones):
|
def default_vpc_endpoint_service(service_region: str, zones: List[str]) -> List[Dict[str, Any]]: # type: ignore[misc]
|
||||||
"""List of dicts representing default VPC endpoints for this service."""
|
"""List of dicts representing default VPC endpoints for this service."""
|
||||||
return BaseBackend.default_vpc_endpoint_service_factory(
|
return BaseBackend.default_vpc_endpoint_service_factory(
|
||||||
service_region, zones, "config"
|
service_region, zones, "config"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _validate_resource_types(self, resource_list):
|
def _validate_resource_types(self, resource_list: List[str]) -> None:
|
||||||
if not self.config_schema:
|
if not self.config_schema:
|
||||||
self.config_schema = AWSServiceSpec(
|
self.config_schema = AWSServiceSpec(
|
||||||
path="data/config/2014-11-12/service-2.json"
|
path="data/config/2014-11-12/service-2.json"
|
||||||
@ -875,15 +919,17 @@ class ConfigBackend(BaseBackend):
|
|||||||
# Verify that each entry exists in the supported list:
|
# Verify that each entry exists in the supported list:
|
||||||
bad_list = []
|
bad_list = []
|
||||||
for resource in resource_list:
|
for resource in resource_list:
|
||||||
if resource not in self.config_schema.shapes["ResourceType"]["enum"]:
|
if resource not in self.config_schema.shapes["ResourceType"]["enum"]: # type: ignore[index]
|
||||||
bad_list.append(resource)
|
bad_list.append(resource)
|
||||||
|
|
||||||
if bad_list:
|
if bad_list:
|
||||||
raise InvalidResourceTypeException(
|
raise InvalidResourceTypeException(
|
||||||
bad_list, self.config_schema.shapes["ResourceType"]["enum"]
|
bad_list, self.config_schema.shapes["ResourceType"]["enum"] # type: ignore[index]
|
||||||
)
|
)
|
||||||
|
|
||||||
def _validate_delivery_snapshot_properties(self, properties):
|
def _validate_delivery_snapshot_properties(
|
||||||
|
self, properties: Dict[str, Any]
|
||||||
|
) -> None:
|
||||||
if not self.config_schema:
|
if not self.config_schema:
|
||||||
self.config_schema = AWSServiceSpec(
|
self.config_schema = AWSServiceSpec(
|
||||||
path="data/config/2014-11-12/service-2.json"
|
path="data/config/2014-11-12/service-2.json"
|
||||||
@ -892,23 +938,23 @@ class ConfigBackend(BaseBackend):
|
|||||||
# Verify that the deliveryFrequency is set to an acceptable value:
|
# Verify that the deliveryFrequency is set to an acceptable value:
|
||||||
if (
|
if (
|
||||||
properties.get("deliveryFrequency", None)
|
properties.get("deliveryFrequency", None)
|
||||||
not in self.config_schema.shapes["MaximumExecutionFrequency"]["enum"]
|
not in self.config_schema.shapes["MaximumExecutionFrequency"]["enum"] # type: ignore[index]
|
||||||
):
|
):
|
||||||
raise InvalidDeliveryFrequency(
|
raise InvalidDeliveryFrequency(
|
||||||
properties.get("deliveryFrequency", None),
|
properties.get("deliveryFrequency", None),
|
||||||
self.config_schema.shapes["MaximumExecutionFrequency"]["enum"],
|
self.config_schema.shapes["MaximumExecutionFrequency"]["enum"], # type: ignore[index]
|
||||||
)
|
)
|
||||||
|
|
||||||
def put_configuration_aggregator(self, config_aggregator):
|
def put_configuration_aggregator(
|
||||||
|
self, config_aggregator: Dict[str, Any]
|
||||||
|
) -> Dict[str, Any]:
|
||||||
# Validate the name:
|
# Validate the name:
|
||||||
if len(config_aggregator["ConfigurationAggregatorName"]) > 256:
|
config_aggr_name = config_aggregator["ConfigurationAggregatorName"]
|
||||||
raise NameTooLongException(
|
if len(config_aggr_name) > 256:
|
||||||
config_aggregator["ConfigurationAggregatorName"],
|
raise NameTooLongException(config_aggr_name, "configurationAggregatorName")
|
||||||
"configurationAggregatorName",
|
|
||||||
)
|
|
||||||
|
|
||||||
account_sources = None
|
account_sources: Optional[List[AccountAggregatorSource]] = None
|
||||||
org_source = None
|
org_source: Optional[OrganizationAggregationSource] = None
|
||||||
|
|
||||||
# Tag validation:
|
# Tag validation:
|
||||||
tags = validate_tags(config_aggregator.get("Tags", []))
|
tags = validate_tags(config_aggregator.get("Tags", []))
|
||||||
@ -965,25 +1011,19 @@ class ConfigBackend(BaseBackend):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Grab the existing one if it exists and update it:
|
# Grab the existing one if it exists and update it:
|
||||||
if not self.config_aggregators.get(
|
if not self.config_aggregators.get(config_aggr_name):
|
||||||
config_aggregator["ConfigurationAggregatorName"]
|
|
||||||
):
|
|
||||||
aggregator = ConfigAggregator(
|
aggregator = ConfigAggregator(
|
||||||
config_aggregator["ConfigurationAggregatorName"],
|
config_aggr_name,
|
||||||
account_id=self.account_id,
|
account_id=self.account_id,
|
||||||
region=self.region_name,
|
region=self.region_name,
|
||||||
account_sources=account_sources,
|
account_sources=account_sources,
|
||||||
org_source=org_source,
|
org_source=org_source,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
)
|
)
|
||||||
self.config_aggregators[
|
self.config_aggregators[config_aggr_name] = aggregator
|
||||||
config_aggregator["ConfigurationAggregatorName"]
|
|
||||||
] = aggregator
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
aggregator = self.config_aggregators[
|
aggregator = self.config_aggregators[config_aggr_name]
|
||||||
config_aggregator["ConfigurationAggregatorName"]
|
|
||||||
]
|
|
||||||
aggregator.tags = tags
|
aggregator.tags = tags
|
||||||
aggregator.account_aggregation_sources = account_sources
|
aggregator.account_aggregation_sources = account_sources
|
||||||
aggregator.organization_aggregation_source = org_source
|
aggregator.organization_aggregation_source = org_source
|
||||||
@ -991,10 +1031,12 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return aggregator.to_dict()
|
return aggregator.to_dict()
|
||||||
|
|
||||||
def describe_configuration_aggregators(self, names, token, limit):
|
def describe_configuration_aggregators(
|
||||||
|
self, names: List[str], token: str, limit: Optional[int]
|
||||||
|
) -> Dict[str, Any]:
|
||||||
limit = DEFAULT_PAGE_SIZE if not limit or limit < 0 else limit
|
limit = DEFAULT_PAGE_SIZE if not limit or limit < 0 else limit
|
||||||
agg_list = []
|
agg_list = []
|
||||||
result = {"ConfigurationAggregators": []}
|
result: Dict[str, Any] = {"ConfigurationAggregators": []}
|
||||||
|
|
||||||
if names:
|
if names:
|
||||||
for name in names:
|
for name in names:
|
||||||
@ -1034,17 +1076,20 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def delete_configuration_aggregator(self, config_aggregator):
|
def delete_configuration_aggregator(self, config_aggregator: str) -> None:
|
||||||
if not self.config_aggregators.get(config_aggregator):
|
if not self.config_aggregators.get(config_aggregator):
|
||||||
raise NoSuchConfigurationAggregatorException()
|
raise NoSuchConfigurationAggregatorException()
|
||||||
|
|
||||||
del self.config_aggregators[config_aggregator]
|
del self.config_aggregators[config_aggregator]
|
||||||
|
|
||||||
def put_aggregation_authorization(
|
def put_aggregation_authorization(
|
||||||
self, authorized_account, authorized_region, tags
|
self,
|
||||||
):
|
authorized_account: str,
|
||||||
|
authorized_region: str,
|
||||||
|
tags: List[Dict[str, str]],
|
||||||
|
) -> Dict[str, Any]:
|
||||||
# Tag validation:
|
# Tag validation:
|
||||||
tags = validate_tags(tags or [])
|
tag_dict = validate_tags(tags or [])
|
||||||
|
|
||||||
# Does this already exist?
|
# Does this already exist?
|
||||||
key = "{}/{}".format(authorized_account, authorized_region)
|
key = "{}/{}".format(authorized_account, authorized_region)
|
||||||
@ -1055,20 +1100,22 @@ class ConfigBackend(BaseBackend):
|
|||||||
self.region_name,
|
self.region_name,
|
||||||
authorized_account,
|
authorized_account,
|
||||||
authorized_region,
|
authorized_region,
|
||||||
tags=tags,
|
tags=tag_dict,
|
||||||
)
|
)
|
||||||
self.aggregation_authorizations[
|
self.aggregation_authorizations[
|
||||||
"{}/{}".format(authorized_account, authorized_region)
|
"{}/{}".format(authorized_account, authorized_region)
|
||||||
] = agg_auth
|
] = agg_auth
|
||||||
else:
|
else:
|
||||||
# Only update the tags:
|
# Only update the tags:
|
||||||
agg_auth.tags = tags
|
agg_auth.tags = tag_dict
|
||||||
|
|
||||||
return agg_auth.to_dict()
|
return agg_auth.to_dict()
|
||||||
|
|
||||||
def describe_aggregation_authorizations(self, token, limit):
|
def describe_aggregation_authorizations(
|
||||||
|
self, token: Optional[str], limit: Optional[int]
|
||||||
|
) -> Dict[str, Any]:
|
||||||
limit = DEFAULT_PAGE_SIZE if not limit or limit < 0 else limit
|
limit = DEFAULT_PAGE_SIZE if not limit or limit < 0 else limit
|
||||||
result = {"AggregationAuthorizations": []}
|
result: Dict[str, Any] = {"AggregationAuthorizations": []}
|
||||||
|
|
||||||
if not self.aggregation_authorizations:
|
if not self.aggregation_authorizations:
|
||||||
return result
|
return result
|
||||||
@ -1097,19 +1144,21 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def delete_aggregation_authorization(self, authorized_account, authorized_region):
|
def delete_aggregation_authorization(
|
||||||
|
self, authorized_account: str, authorized_region: str
|
||||||
|
) -> None:
|
||||||
# This will always return a 200 -- regardless if there is or isn't an existing
|
# This will always return a 200 -- regardless if there is or isn't an existing
|
||||||
# aggregation authorization.
|
# aggregation authorization.
|
||||||
key = "{}/{}".format(authorized_account, authorized_region)
|
key = "{}/{}".format(authorized_account, authorized_region)
|
||||||
self.aggregation_authorizations.pop(key, None)
|
self.aggregation_authorizations.pop(key, None)
|
||||||
|
|
||||||
def put_configuration_recorder(self, config_recorder):
|
def put_configuration_recorder(self, config_recorder: Dict[str, Any]) -> None:
|
||||||
# Validate the name:
|
# Validate the name:
|
||||||
if not config_recorder.get("name"):
|
if not config_recorder.get("name"):
|
||||||
raise InvalidConfigurationRecorderNameException(config_recorder.get("name"))
|
raise InvalidConfigurationRecorderNameException(config_recorder.get("name"))
|
||||||
if len(config_recorder.get("name")) > 256:
|
if len(config_recorder["name"]) > 256:
|
||||||
raise NameTooLongException(
|
raise NameTooLongException(
|
||||||
config_recorder.get("name"), "configurationRecorder.name"
|
config_recorder["name"], "configurationRecorder.name"
|
||||||
)
|
)
|
||||||
|
|
||||||
# We're going to assume that the passed in Role ARN is correct.
|
# We're going to assume that the passed in Role ARN is correct.
|
||||||
@ -1164,8 +1213,10 @@ class ConfigBackend(BaseBackend):
|
|||||||
status=recorder_status,
|
status=recorder_status,
|
||||||
)
|
)
|
||||||
|
|
||||||
def describe_configuration_recorders(self, recorder_names):
|
def describe_configuration_recorders(
|
||||||
recorders = []
|
self, recorder_names: Optional[List[str]]
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
recorders: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
if recorder_names:
|
if recorder_names:
|
||||||
for rname in recorder_names:
|
for rname in recorder_names:
|
||||||
@ -1181,8 +1232,10 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return recorders
|
return recorders
|
||||||
|
|
||||||
def describe_configuration_recorder_status(self, recorder_names):
|
def describe_configuration_recorder_status(
|
||||||
recorders = []
|
self, recorder_names: List[str]
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
recorders: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
if recorder_names:
|
if recorder_names:
|
||||||
for rname in recorder_names:
|
for rname in recorder_names:
|
||||||
@ -1198,7 +1251,7 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return recorders
|
return recorders
|
||||||
|
|
||||||
def put_delivery_channel(self, delivery_channel):
|
def put_delivery_channel(self, delivery_channel: Dict[str, Any]) -> None:
|
||||||
# Must have a configuration recorder:
|
# Must have a configuration recorder:
|
||||||
if not self.recorders:
|
if not self.recorders:
|
||||||
raise NoAvailableConfigurationRecorderException()
|
raise NoAvailableConfigurationRecorderException()
|
||||||
@ -1206,10 +1259,8 @@ class ConfigBackend(BaseBackend):
|
|||||||
# Validate the name:
|
# Validate the name:
|
||||||
if not delivery_channel.get("name"):
|
if not delivery_channel.get("name"):
|
||||||
raise InvalidDeliveryChannelNameException(delivery_channel.get("name"))
|
raise InvalidDeliveryChannelNameException(delivery_channel.get("name"))
|
||||||
if len(delivery_channel.get("name")) > 256:
|
if len(delivery_channel["name"]) > 256:
|
||||||
raise NameTooLongException(
|
raise NameTooLongException(delivery_channel["name"], "deliveryChannel.name")
|
||||||
delivery_channel.get("name"), "deliveryChannel.name"
|
|
||||||
)
|
|
||||||
|
|
||||||
# We are going to assume that the bucket exists -- but will verify if
|
# We are going to assume that the bucket exists -- but will verify if
|
||||||
# the bucket provided is blank:
|
# the bucket provided is blank:
|
||||||
@ -1256,8 +1307,10 @@ class ConfigBackend(BaseBackend):
|
|||||||
snapshot_properties=dprop,
|
snapshot_properties=dprop,
|
||||||
)
|
)
|
||||||
|
|
||||||
def describe_delivery_channels(self, channel_names):
|
def describe_delivery_channels(
|
||||||
channels = []
|
self, channel_names: List[str]
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
channels: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
if channel_names:
|
if channel_names:
|
||||||
for cname in channel_names:
|
for cname in channel_names:
|
||||||
@ -1273,7 +1326,7 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return channels
|
return channels
|
||||||
|
|
||||||
def start_configuration_recorder(self, recorder_name):
|
def start_configuration_recorder(self, recorder_name: str) -> None:
|
||||||
if not self.recorders.get(recorder_name):
|
if not self.recorders.get(recorder_name):
|
||||||
raise NoSuchConfigurationRecorderException(recorder_name)
|
raise NoSuchConfigurationRecorderException(recorder_name)
|
||||||
|
|
||||||
@ -1284,20 +1337,20 @@ class ConfigBackend(BaseBackend):
|
|||||||
# Start recording:
|
# Start recording:
|
||||||
self.recorders[recorder_name].status.start()
|
self.recorders[recorder_name].status.start()
|
||||||
|
|
||||||
def stop_configuration_recorder(self, recorder_name):
|
def stop_configuration_recorder(self, recorder_name: str) -> None:
|
||||||
if not self.recorders.get(recorder_name):
|
if not self.recorders.get(recorder_name):
|
||||||
raise NoSuchConfigurationRecorderException(recorder_name)
|
raise NoSuchConfigurationRecorderException(recorder_name)
|
||||||
|
|
||||||
# Stop recording:
|
# Stop recording:
|
||||||
self.recorders[recorder_name].status.stop()
|
self.recorders[recorder_name].status.stop()
|
||||||
|
|
||||||
def delete_configuration_recorder(self, recorder_name):
|
def delete_configuration_recorder(self, recorder_name: str) -> None:
|
||||||
if not self.recorders.get(recorder_name):
|
if not self.recorders.get(recorder_name):
|
||||||
raise NoSuchConfigurationRecorderException(recorder_name)
|
raise NoSuchConfigurationRecorderException(recorder_name)
|
||||||
|
|
||||||
del self.recorders[recorder_name]
|
del self.recorders[recorder_name]
|
||||||
|
|
||||||
def delete_delivery_channel(self, channel_name):
|
def delete_delivery_channel(self, channel_name: str) -> None:
|
||||||
if not self.delivery_channels.get(channel_name):
|
if not self.delivery_channels.get(channel_name):
|
||||||
raise NoSuchDeliveryChannelException(channel_name)
|
raise NoSuchDeliveryChannelException(channel_name)
|
||||||
|
|
||||||
@ -1310,13 +1363,13 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
def list_discovered_resources(
|
def list_discovered_resources(
|
||||||
self,
|
self,
|
||||||
resource_type,
|
resource_type: str,
|
||||||
backend_region,
|
backend_region: str,
|
||||||
resource_ids,
|
resource_ids: List[str],
|
||||||
resource_name,
|
resource_name: str,
|
||||||
limit,
|
limit: int,
|
||||||
next_token,
|
next_token: str,
|
||||||
):
|
) -> Dict[str, Any]:
|
||||||
"""Queries against AWS Config (non-aggregated) listing function.
|
"""Queries against AWS Config (non-aggregated) listing function.
|
||||||
|
|
||||||
The listing function must exist for the resource backend.
|
The listing function must exist for the resource backend.
|
||||||
@ -1392,8 +1445,13 @@ class ConfigBackend(BaseBackend):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def list_aggregate_discovered_resources(
|
def list_aggregate_discovered_resources(
|
||||||
self, aggregator_name, resource_type, filters, limit, next_token
|
self,
|
||||||
):
|
aggregator_name: str,
|
||||||
|
resource_type: str,
|
||||||
|
filters: Dict[str, str],
|
||||||
|
limit: Optional[int],
|
||||||
|
next_token: Optional[str],
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""Queries AWS Config listing function that must exist for resource backend.
|
"""Queries AWS Config listing function that must exist for resource backend.
|
||||||
|
|
||||||
As far a moto goes -- the only real difference between this function
|
As far a moto goes -- the only real difference between this function
|
||||||
@ -1453,14 +1511,16 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
resource_identifiers.append(item)
|
resource_identifiers.append(item)
|
||||||
|
|
||||||
result = {"ResourceIdentifiers": resource_identifiers}
|
result: Dict[str, Any] = {"ResourceIdentifiers": resource_identifiers}
|
||||||
|
|
||||||
if new_token:
|
if new_token:
|
||||||
result["NextToken"] = new_token
|
result["NextToken"] = new_token
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_resource_config_history(self, resource_type, resource_id, backend_region):
|
def get_resource_config_history(
|
||||||
|
self, resource_type: str, resource_id: str, backend_region: str
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""Returns configuration of resource for the current regional backend.
|
"""Returns configuration of resource for the current regional backend.
|
||||||
|
|
||||||
Item returned in AWS Config format.
|
Item returned in AWS Config format.
|
||||||
@ -1502,7 +1562,9 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return {"configurationItems": [item]}
|
return {"configurationItems": [item]}
|
||||||
|
|
||||||
def batch_get_resource_config(self, resource_keys, backend_region):
|
def batch_get_resource_config(
|
||||||
|
self, resource_keys: List[Dict[str, str]], backend_region: str
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""Returns configuration of resource for the current regional backend.
|
"""Returns configuration of resource for the current regional backend.
|
||||||
|
|
||||||
Item is returned in AWS Config format.
|
Item is returned in AWS Config format.
|
||||||
@ -1562,8 +1624,8 @@ class ConfigBackend(BaseBackend):
|
|||||||
} # At this time, moto is not adding unprocessed items.
|
} # At this time, moto is not adding unprocessed items.
|
||||||
|
|
||||||
def batch_get_aggregate_resource_config(
|
def batch_get_aggregate_resource_config(
|
||||||
self, aggregator_name, resource_identifiers
|
self, aggregator_name: str, resource_identifiers: List[Dict[str, str]]
|
||||||
):
|
) -> Dict[str, Any]:
|
||||||
"""Returns configuration of resource for current regional backend.
|
"""Returns configuration of resource for current regional backend.
|
||||||
|
|
||||||
Item is returned in AWS Config format.
|
Item is returned in AWS Config format.
|
||||||
@ -1621,7 +1683,12 @@ class ConfigBackend(BaseBackend):
|
|||||||
"UnprocessedResourceIdentifiers": not_found,
|
"UnprocessedResourceIdentifiers": not_found,
|
||||||
}
|
}
|
||||||
|
|
||||||
def put_evaluations(self, evaluations=None, result_token=None, test_mode=False):
|
def put_evaluations(
|
||||||
|
self,
|
||||||
|
evaluations: Optional[List[Dict[str, Any]]] = None,
|
||||||
|
result_token: Optional[str] = None,
|
||||||
|
test_mode: Optional[bool] = False,
|
||||||
|
) -> Dict[str, List[Any]]:
|
||||||
if not evaluations:
|
if not evaluations:
|
||||||
raise InvalidParameterValueException(
|
raise InvalidParameterValueException(
|
||||||
"The Evaluations object in your request cannot be null."
|
"The Evaluations object in your request cannot be null."
|
||||||
@ -1644,14 +1711,14 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
def put_organization_conformance_pack(
|
def put_organization_conformance_pack(
|
||||||
self,
|
self,
|
||||||
name,
|
name: str,
|
||||||
template_s3_uri,
|
template_s3_uri: str,
|
||||||
template_body,
|
template_body: str,
|
||||||
delivery_s3_bucket,
|
delivery_s3_bucket: str,
|
||||||
delivery_s3_key_prefix,
|
delivery_s3_key_prefix: str,
|
||||||
input_parameters,
|
input_parameters: List[Dict[str, str]],
|
||||||
excluded_accounts,
|
excluded_accounts: List[str],
|
||||||
):
|
) -> Dict[str, Any]:
|
||||||
# a real validation of the content of the template is missing at the moment
|
# a real validation of the content of the template is missing at the moment
|
||||||
if not template_s3_uri and not template_body:
|
if not template_s3_uri and not template_body:
|
||||||
raise ValidationException("Template body is invalid")
|
raise ValidationException("Template body is invalid")
|
||||||
@ -1690,7 +1757,9 @@ class ConfigBackend(BaseBackend):
|
|||||||
"OrganizationConformancePackArn": pack.organization_conformance_pack_arn
|
"OrganizationConformancePackArn": pack.organization_conformance_pack_arn
|
||||||
}
|
}
|
||||||
|
|
||||||
def describe_organization_conformance_packs(self, names):
|
def describe_organization_conformance_packs(
|
||||||
|
self, names: List[str]
|
||||||
|
) -> Dict[str, Any]:
|
||||||
packs = []
|
packs = []
|
||||||
|
|
||||||
for name in names:
|
for name in names:
|
||||||
@ -1707,7 +1776,9 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return {"OrganizationConformancePacks": packs}
|
return {"OrganizationConformancePacks": packs}
|
||||||
|
|
||||||
def describe_organization_conformance_pack_statuses(self, names):
|
def describe_organization_conformance_pack_statuses(
|
||||||
|
self, names: List[str]
|
||||||
|
) -> Dict[str, Any]:
|
||||||
packs = []
|
packs = []
|
||||||
statuses = []
|
statuses = []
|
||||||
|
|
||||||
@ -1737,7 +1808,9 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return {"OrganizationConformancePackStatuses": statuses}
|
return {"OrganizationConformancePackStatuses": statuses}
|
||||||
|
|
||||||
def get_organization_conformance_pack_detailed_status(self, name):
|
def get_organization_conformance_pack_detailed_status(
|
||||||
|
self, name: str
|
||||||
|
) -> Dict[str, Any]:
|
||||||
pack = self.organization_conformance_packs.get(name)
|
pack = self.organization_conformance_packs.get(name)
|
||||||
|
|
||||||
if not pack:
|
if not pack:
|
||||||
@ -1760,7 +1833,7 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
return {"OrganizationConformancePackDetailedStatuses": statuses}
|
return {"OrganizationConformancePackDetailedStatuses": statuses}
|
||||||
|
|
||||||
def delete_organization_conformance_pack(self, name):
|
def delete_organization_conformance_pack(self, name: str) -> None:
|
||||||
pack = self.organization_conformance_packs.get(name)
|
pack = self.organization_conformance_packs.get(name)
|
||||||
|
|
||||||
if not pack:
|
if not pack:
|
||||||
@ -1771,28 +1844,24 @@ class ConfigBackend(BaseBackend):
|
|||||||
|
|
||||||
self.organization_conformance_packs.pop(name)
|
self.organization_conformance_packs.pop(name)
|
||||||
|
|
||||||
def _match_arn(self, resource_arn):
|
def _match_arn(
|
||||||
|
self, resource_arn: str
|
||||||
|
) -> Union[ConfigRule, ConfigAggregator, ConfigAggregationAuthorization]:
|
||||||
"""Return config instance that has a matching ARN."""
|
"""Return config instance that has a matching ARN."""
|
||||||
# The allowed resources are ConfigRule, ConfigurationAggregator,
|
# The allowed resources are ConfigRule, ConfigurationAggregator,
|
||||||
# and AggregatorAuthorization.
|
# and AggregatorAuthorization.
|
||||||
allowed_resources = [
|
allowed_resources = {
|
||||||
{
|
"configuration_aggregator_arn": self.config_aggregators,
|
||||||
"configs": self.config_aggregators,
|
"aggregation_authorization_arn": self.aggregation_authorizations,
|
||||||
"arn_attribute": "configuration_aggregator_arn",
|
"config_rule_arn": self.config_rules,
|
||||||
},
|
}
|
||||||
{
|
|
||||||
"configs": self.aggregation_authorizations,
|
|
||||||
"arn_attribute": "aggregation_authorization_arn",
|
|
||||||
},
|
|
||||||
{"configs": self.config_rules, "arn_attribute": "config_rule_arn"},
|
|
||||||
]
|
|
||||||
|
|
||||||
# Find matching config for given resource_arn among all the
|
# Find matching config for given resource_arn among all the
|
||||||
# allowed config resources.
|
# allowed config resources.
|
||||||
matched_config = None
|
matched_config = None
|
||||||
for resource in allowed_resources:
|
for arn_attribute, configs in allowed_resources.items():
|
||||||
for config in resource["configs"].values():
|
for config in configs.values(): # type: ignore[attr-defined]
|
||||||
if resource_arn == getattr(config, resource["arn_attribute"]):
|
if resource_arn == getattr(config, arn_attribute):
|
||||||
matched_config = config
|
matched_config = config
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -1800,18 +1869,18 @@ class ConfigBackend(BaseBackend):
|
|||||||
raise ResourceNotFoundException(resource_arn)
|
raise ResourceNotFoundException(resource_arn)
|
||||||
return matched_config
|
return matched_config
|
||||||
|
|
||||||
def tag_resource(self, resource_arn, tags):
|
def tag_resource(self, resource_arn: str, tags: List[Dict[str, str]]) -> None:
|
||||||
"""Add tags in config with a matching ARN."""
|
"""Add tags in config with a matching ARN."""
|
||||||
# Tag validation:
|
# Tag validation:
|
||||||
tags = validate_tags(tags)
|
tag_dict = validate_tags(tags)
|
||||||
|
|
||||||
# Find config with a matching ARN.
|
# Find config with a matching ARN.
|
||||||
matched_config = self._match_arn(resource_arn)
|
matched_config = self._match_arn(resource_arn)
|
||||||
|
|
||||||
# Merge the new tags with the existing tags.
|
# Merge the new tags with the existing tags.
|
||||||
matched_config.tags.update(tags)
|
matched_config.tags.update(tag_dict)
|
||||||
|
|
||||||
def untag_resource(self, resource_arn, tag_keys):
|
def untag_resource(self, resource_arn: str, tag_keys: List[str]) -> None:
|
||||||
"""Remove tags in config with a matching ARN.
|
"""Remove tags in config with a matching ARN.
|
||||||
|
|
||||||
If the tags in the tag_keys don't match any keys for that
|
If the tags in the tag_keys don't match any keys for that
|
||||||
@ -1827,8 +1896,8 @@ class ConfigBackend(BaseBackend):
|
|||||||
matched_config.tags.pop(tag_key, None)
|
matched_config.tags.pop(tag_key, None)
|
||||||
|
|
||||||
def list_tags_for_resource(
|
def list_tags_for_resource(
|
||||||
self, resource_arn, limit, next_token
|
self, resource_arn: str, limit: int
|
||||||
): # pylint: disable=unused-argument
|
) -> Dict[str, List[Dict[str, str]]]:
|
||||||
"""Return list of tags for AWS Config resource."""
|
"""Return list of tags for AWS Config resource."""
|
||||||
# The limit argument is essentially ignored as a config instance
|
# The limit argument is essentially ignored as a config instance
|
||||||
# can only have 50 tags, but we'll check the argument anyway.
|
# can only have 50 tags, but we'll check the argument anyway.
|
||||||
@ -1845,7 +1914,9 @@ class ConfigBackend(BaseBackend):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
def put_config_rule(self, config_rule, tags=None):
|
def put_config_rule(
|
||||||
|
self, config_rule: Dict[str, Any], tags: Optional[List[Dict[str, str]]] = None
|
||||||
|
) -> str:
|
||||||
"""Add/Update config rule for evaluating resource compliance.
|
"""Add/Update config rule for evaluating resource compliance.
|
||||||
|
|
||||||
TBD - Only the "accounting" of config rules are handled at the
|
TBD - Only the "accounting" of config rules are handled at the
|
||||||
@ -1880,7 +1951,7 @@ class ConfigBackend(BaseBackend):
|
|||||||
"Name or Id or Arn"
|
"Name or Id or Arn"
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = validate_tags(tags or [])
|
tag_dict = validate_tags(tags or [])
|
||||||
|
|
||||||
# With the rule_name, determine whether it's for an existing rule
|
# With the rule_name, determine whether it's for an existing rule
|
||||||
# or whether a new rule should be created.
|
# or whether a new rule should be created.
|
||||||
@ -1896,20 +1967,22 @@ class ConfigBackend(BaseBackend):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Update the current rule.
|
# Update the current rule.
|
||||||
rule.modify_fields(self.region_name, config_rule, tags)
|
rule.modify_fields(self.region_name, config_rule, tag_dict)
|
||||||
else:
|
else:
|
||||||
# Create a new ConfigRule if the limit hasn't been reached.
|
# Create a new ConfigRule if the limit hasn't been reached.
|
||||||
if len(self.config_rules) == ConfigRule.MAX_RULES:
|
if len(self.config_rules) == ConfigRule.MAX_RULES:
|
||||||
raise MaxNumberOfConfigRulesExceededException(
|
raise MaxNumberOfConfigRulesExceededException(
|
||||||
rule_name, ConfigRule.MAX_RULES
|
rule_name, ConfigRule.MAX_RULES
|
||||||
)
|
)
|
||||||
rule = ConfigRule(self.account_id, self.region_name, config_rule, tags)
|
rule = ConfigRule(self.account_id, self.region_name, config_rule, tag_dict)
|
||||||
self.config_rules[rule_name] = rule
|
self.config_rules[rule_name] = rule
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def describe_config_rules(self, config_rule_names, next_token):
|
def describe_config_rules(
|
||||||
|
self, config_rule_names: Optional[List[str]], next_token: Optional[str]
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""Return details for the given ConfigRule names or for all rules."""
|
"""Return details for the given ConfigRule names or for all rules."""
|
||||||
result = {"ConfigRules": []}
|
result: Dict[str, Any] = {"ConfigRules": []}
|
||||||
if not self.config_rules:
|
if not self.config_rules:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -1937,7 +2010,7 @@ class ConfigBackend(BaseBackend):
|
|||||||
result["NextToken"] = sorted_rules[start + CONFIG_RULE_PAGE_SIZE]
|
result["NextToken"] = sorted_rules[start + CONFIG_RULE_PAGE_SIZE]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def delete_config_rule(self, rule_name):
|
def delete_config_rule(self, rule_name: str) -> None:
|
||||||
"""Delete config rule used for evaluating resource compliance."""
|
"""Delete config rule used for evaluating resource compliance."""
|
||||||
rule = self.config_rules.get(rule_name)
|
rule = self.config_rules.get(rule_name)
|
||||||
if not rule:
|
if not rule:
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
import json
|
import json
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
from .models import config_backends
|
from .models import config_backends, ConfigBackend
|
||||||
|
|
||||||
|
|
||||||
class ConfigResponse(BaseResponse):
|
class ConfigResponse(BaseResponse):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__(service_name="config")
|
super().__init__(service_name="config")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def config_backend(self):
|
def config_backend(self) -> ConfigBackend:
|
||||||
return config_backends[self.current_account][self.region]
|
return config_backends[self.current_account][self.region]
|
||||||
|
|
||||||
def put_configuration_recorder(self):
|
def put_configuration_recorder(self) -> str:
|
||||||
self.config_backend.put_configuration_recorder(
|
self.config_backend.put_configuration_recorder(
|
||||||
self._get_param("ConfigurationRecorder")
|
self._get_param("ConfigurationRecorder")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def put_configuration_aggregator(self):
|
def put_configuration_aggregator(self) -> str:
|
||||||
aggregator = self.config_backend.put_configuration_aggregator(
|
aggregator = self.config_backend.put_configuration_aggregator(
|
||||||
json.loads(self.body)
|
json.loads(self.body)
|
||||||
)
|
)
|
||||||
schema = {"ConfigurationAggregator": aggregator}
|
schema = {"ConfigurationAggregator": aggregator}
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def describe_configuration_aggregators(self):
|
def describe_configuration_aggregators(self) -> str:
|
||||||
aggregators = self.config_backend.describe_configuration_aggregators(
|
aggregators = self.config_backend.describe_configuration_aggregators(
|
||||||
self._get_param("ConfigurationAggregatorNames"),
|
self._get_param("ConfigurationAggregatorNames"),
|
||||||
self._get_param("NextToken"),
|
self._get_param("NextToken"),
|
||||||
@ -32,13 +32,13 @@ class ConfigResponse(BaseResponse):
|
|||||||
)
|
)
|
||||||
return json.dumps(aggregators)
|
return json.dumps(aggregators)
|
||||||
|
|
||||||
def delete_configuration_aggregator(self):
|
def delete_configuration_aggregator(self) -> str:
|
||||||
self.config_backend.delete_configuration_aggregator(
|
self.config_backend.delete_configuration_aggregator(
|
||||||
self._get_param("ConfigurationAggregatorName")
|
self._get_param("ConfigurationAggregatorName")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def put_aggregation_authorization(self):
|
def put_aggregation_authorization(self) -> str:
|
||||||
agg_auth = self.config_backend.put_aggregation_authorization(
|
agg_auth = self.config_backend.put_aggregation_authorization(
|
||||||
self._get_param("AuthorizedAccountId"),
|
self._get_param("AuthorizedAccountId"),
|
||||||
self._get_param("AuthorizedAwsRegion"),
|
self._get_param("AuthorizedAwsRegion"),
|
||||||
@ -47,14 +47,14 @@ class ConfigResponse(BaseResponse):
|
|||||||
schema = {"AggregationAuthorization": agg_auth}
|
schema = {"AggregationAuthorization": agg_auth}
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def describe_aggregation_authorizations(self):
|
def describe_aggregation_authorizations(self) -> str:
|
||||||
authorizations = self.config_backend.describe_aggregation_authorizations(
|
authorizations = self.config_backend.describe_aggregation_authorizations(
|
||||||
self._get_param("NextToken"), self._get_param("Limit")
|
self._get_param("NextToken"), self._get_param("Limit")
|
||||||
)
|
)
|
||||||
|
|
||||||
return json.dumps(authorizations)
|
return json.dumps(authorizations)
|
||||||
|
|
||||||
def delete_aggregation_authorization(self):
|
def delete_aggregation_authorization(self) -> str:
|
||||||
self.config_backend.delete_aggregation_authorization(
|
self.config_backend.delete_aggregation_authorization(
|
||||||
self._get_param("AuthorizedAccountId"),
|
self._get_param("AuthorizedAccountId"),
|
||||||
self._get_param("AuthorizedAwsRegion"),
|
self._get_param("AuthorizedAwsRegion"),
|
||||||
@ -62,59 +62,59 @@ class ConfigResponse(BaseResponse):
|
|||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def describe_configuration_recorders(self):
|
def describe_configuration_recorders(self) -> str:
|
||||||
recorders = self.config_backend.describe_configuration_recorders(
|
recorders = self.config_backend.describe_configuration_recorders(
|
||||||
self._get_param("ConfigurationRecorderNames")
|
self._get_param("ConfigurationRecorderNames")
|
||||||
)
|
)
|
||||||
schema = {"ConfigurationRecorders": recorders}
|
schema = {"ConfigurationRecorders": recorders}
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def describe_configuration_recorder_status(self):
|
def describe_configuration_recorder_status(self) -> str:
|
||||||
recorder_statuses = self.config_backend.describe_configuration_recorder_status(
|
recorder_statuses = self.config_backend.describe_configuration_recorder_status(
|
||||||
self._get_param("ConfigurationRecorderNames")
|
self._get_param("ConfigurationRecorderNames")
|
||||||
)
|
)
|
||||||
schema = {"ConfigurationRecordersStatus": recorder_statuses}
|
schema = {"ConfigurationRecordersStatus": recorder_statuses}
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def put_delivery_channel(self):
|
def put_delivery_channel(self) -> str:
|
||||||
self.config_backend.put_delivery_channel(self._get_param("DeliveryChannel"))
|
self.config_backend.put_delivery_channel(self._get_param("DeliveryChannel"))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def describe_delivery_channels(self):
|
def describe_delivery_channels(self) -> str:
|
||||||
delivery_channels = self.config_backend.describe_delivery_channels(
|
delivery_channels = self.config_backend.describe_delivery_channels(
|
||||||
self._get_param("DeliveryChannelNames")
|
self._get_param("DeliveryChannelNames")
|
||||||
)
|
)
|
||||||
schema = {"DeliveryChannels": delivery_channels}
|
schema = {"DeliveryChannels": delivery_channels}
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def describe_delivery_channel_status(self):
|
def describe_delivery_channel_status(self) -> str:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def delete_delivery_channel(self):
|
def delete_delivery_channel(self) -> str:
|
||||||
self.config_backend.delete_delivery_channel(
|
self.config_backend.delete_delivery_channel(
|
||||||
self._get_param("DeliveryChannelName")
|
self._get_param("DeliveryChannelName")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def delete_configuration_recorder(self):
|
def delete_configuration_recorder(self) -> str:
|
||||||
self.config_backend.delete_configuration_recorder(
|
self.config_backend.delete_configuration_recorder(
|
||||||
self._get_param("ConfigurationRecorderName")
|
self._get_param("ConfigurationRecorderName")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def start_configuration_recorder(self):
|
def start_configuration_recorder(self) -> str:
|
||||||
self.config_backend.start_configuration_recorder(
|
self.config_backend.start_configuration_recorder(
|
||||||
self._get_param("ConfigurationRecorderName")
|
self._get_param("ConfigurationRecorderName")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def stop_configuration_recorder(self):
|
def stop_configuration_recorder(self) -> str:
|
||||||
self.config_backend.stop_configuration_recorder(
|
self.config_backend.stop_configuration_recorder(
|
||||||
self._get_param("ConfigurationRecorderName")
|
self._get_param("ConfigurationRecorderName")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def list_discovered_resources(self):
|
def list_discovered_resources(self) -> str:
|
||||||
schema = self.config_backend.list_discovered_resources(
|
schema = self.config_backend.list_discovered_resources(
|
||||||
self._get_param("resourceType"),
|
self._get_param("resourceType"),
|
||||||
self.region,
|
self.region,
|
||||||
@ -125,7 +125,7 @@ class ConfigResponse(BaseResponse):
|
|||||||
)
|
)
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def list_aggregate_discovered_resources(self):
|
def list_aggregate_discovered_resources(self) -> str:
|
||||||
schema = self.config_backend.list_aggregate_discovered_resources(
|
schema = self.config_backend.list_aggregate_discovered_resources(
|
||||||
self._get_param("ConfigurationAggregatorName"),
|
self._get_param("ConfigurationAggregatorName"),
|
||||||
self._get_param("ResourceType"),
|
self._get_param("ResourceType"),
|
||||||
@ -135,34 +135,32 @@ class ConfigResponse(BaseResponse):
|
|||||||
)
|
)
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def list_tags_for_resource(self):
|
def list_tags_for_resource(self) -> str:
|
||||||
schema = self.config_backend.list_tags_for_resource(
|
schema = self.config_backend.list_tags_for_resource(
|
||||||
self._get_param("ResourceArn"),
|
self._get_param("ResourceArn"), self._get_param("Limit")
|
||||||
self._get_param("Limit"),
|
|
||||||
self._get_param("NextToken"),
|
|
||||||
)
|
)
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def get_resource_config_history(self):
|
def get_resource_config_history(self) -> str:
|
||||||
schema = self.config_backend.get_resource_config_history(
|
schema = self.config_backend.get_resource_config_history(
|
||||||
self._get_param("resourceType"), self._get_param("resourceId"), self.region
|
self._get_param("resourceType"), self._get_param("resourceId"), self.region
|
||||||
)
|
)
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def batch_get_resource_config(self):
|
def batch_get_resource_config(self) -> str:
|
||||||
schema = self.config_backend.batch_get_resource_config(
|
schema = self.config_backend.batch_get_resource_config(
|
||||||
self._get_param("resourceKeys"), self.region
|
self._get_param("resourceKeys"), self.region
|
||||||
)
|
)
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def batch_get_aggregate_resource_config(self):
|
def batch_get_aggregate_resource_config(self) -> str:
|
||||||
schema = self.config_backend.batch_get_aggregate_resource_config(
|
schema = self.config_backend.batch_get_aggregate_resource_config(
|
||||||
self._get_param("ConfigurationAggregatorName"),
|
self._get_param("ConfigurationAggregatorName"),
|
||||||
self._get_param("ResourceIdentifiers"),
|
self._get_param("ResourceIdentifiers"),
|
||||||
)
|
)
|
||||||
return json.dumps(schema)
|
return json.dumps(schema)
|
||||||
|
|
||||||
def put_evaluations(self):
|
def put_evaluations(self) -> str:
|
||||||
evaluations = self.config_backend.put_evaluations(
|
evaluations = self.config_backend.put_evaluations(
|
||||||
self._get_param("Evaluations"),
|
self._get_param("Evaluations"),
|
||||||
self._get_param("ResultToken"),
|
self._get_param("ResultToken"),
|
||||||
@ -170,7 +168,7 @@ class ConfigResponse(BaseResponse):
|
|||||||
)
|
)
|
||||||
return json.dumps(evaluations)
|
return json.dumps(evaluations)
|
||||||
|
|
||||||
def put_organization_conformance_pack(self):
|
def put_organization_conformance_pack(self) -> str:
|
||||||
conformance_pack = self.config_backend.put_organization_conformance_pack(
|
conformance_pack = self.config_backend.put_organization_conformance_pack(
|
||||||
name=self._get_param("OrganizationConformancePackName"),
|
name=self._get_param("OrganizationConformancePackName"),
|
||||||
template_s3_uri=self._get_param("TemplateS3Uri"),
|
template_s3_uri=self._get_param("TemplateS3Uri"),
|
||||||
@ -183,19 +181,19 @@ class ConfigResponse(BaseResponse):
|
|||||||
|
|
||||||
return json.dumps(conformance_pack)
|
return json.dumps(conformance_pack)
|
||||||
|
|
||||||
def describe_organization_conformance_packs(self):
|
def describe_organization_conformance_packs(self) -> str:
|
||||||
conformance_packs = self.config_backend.describe_organization_conformance_packs(
|
conformance_packs = self.config_backend.describe_organization_conformance_packs(
|
||||||
self._get_param("OrganizationConformancePackNames")
|
self._get_param("OrganizationConformancePackNames")
|
||||||
)
|
)
|
||||||
return json.dumps(conformance_packs)
|
return json.dumps(conformance_packs)
|
||||||
|
|
||||||
def describe_organization_conformance_pack_statuses(self):
|
def describe_organization_conformance_pack_statuses(self) -> str:
|
||||||
statuses = self.config_backend.describe_organization_conformance_pack_statuses(
|
statuses = self.config_backend.describe_organization_conformance_pack_statuses(
|
||||||
self._get_param("OrganizationConformancePackNames")
|
self._get_param("OrganizationConformancePackNames")
|
||||||
)
|
)
|
||||||
return json.dumps(statuses)
|
return json.dumps(statuses)
|
||||||
|
|
||||||
def get_organization_conformance_pack_detailed_status(self):
|
def get_organization_conformance_pack_detailed_status(self) -> str:
|
||||||
# 'Filters' parameter is not implemented yet
|
# 'Filters' parameter is not implemented yet
|
||||||
statuses = (
|
statuses = (
|
||||||
self.config_backend.get_organization_conformance_pack_detailed_status(
|
self.config_backend.get_organization_conformance_pack_detailed_status(
|
||||||
@ -204,36 +202,36 @@ class ConfigResponse(BaseResponse):
|
|||||||
)
|
)
|
||||||
return json.dumps(statuses)
|
return json.dumps(statuses)
|
||||||
|
|
||||||
def delete_organization_conformance_pack(self):
|
def delete_organization_conformance_pack(self) -> str:
|
||||||
self.config_backend.delete_organization_conformance_pack(
|
self.config_backend.delete_organization_conformance_pack(
|
||||||
self._get_param("OrganizationConformancePackName")
|
self._get_param("OrganizationConformancePackName")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def tag_resource(self):
|
def tag_resource(self) -> str:
|
||||||
self.config_backend.tag_resource(
|
self.config_backend.tag_resource(
|
||||||
self._get_param("ResourceArn"), self._get_param("Tags")
|
self._get_param("ResourceArn"), self._get_param("Tags")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def untag_resource(self):
|
def untag_resource(self) -> str:
|
||||||
self.config_backend.untag_resource(
|
self.config_backend.untag_resource(
|
||||||
self._get_param("ResourceArn"), self._get_param("TagKeys")
|
self._get_param("ResourceArn"), self._get_param("TagKeys")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def put_config_rule(self):
|
def put_config_rule(self) -> str:
|
||||||
self.config_backend.put_config_rule(
|
self.config_backend.put_config_rule(
|
||||||
self._get_param("ConfigRule"), self._get_param("Tags")
|
self._get_param("ConfigRule"), self._get_param("Tags")
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def describe_config_rules(self):
|
def describe_config_rules(self) -> str:
|
||||||
rules = self.config_backend.describe_config_rules(
|
rules = self.config_backend.describe_config_rules(
|
||||||
self._get_param("ConfigRuleNames"), self._get_param("NextToken")
|
self._get_param("ConfigRuleNames"), self._get_param("NextToken")
|
||||||
)
|
)
|
||||||
return json.dumps(rules)
|
return json.dumps(rules)
|
||||||
|
|
||||||
def delete_config_rule(self):
|
def delete_config_rule(self) -> str:
|
||||||
self.config_backend.delete_config_rule(self._get_param("ConfigRuleName"))
|
self.config_backend.delete_config_rule(self._get_param("ConfigRuleName"))
|
||||||
return ""
|
return ""
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict, List, Optional
|
||||||
from .base_backend import InstanceTrackerMeta
|
from .base_backend import InstanceTrackerMeta
|
||||||
|
|
||||||
|
|
||||||
@ -98,14 +98,14 @@ class ConfigQueryModel:
|
|||||||
|
|
||||||
def list_config_service_resources(
|
def list_config_service_resources(
|
||||||
self,
|
self,
|
||||||
account_id,
|
account_id: str,
|
||||||
resource_ids,
|
resource_ids: Optional[List[str]],
|
||||||
resource_name,
|
resource_name: Optional[str],
|
||||||
limit,
|
limit: int,
|
||||||
next_token,
|
next_token: Optional[str],
|
||||||
backend_region=None,
|
backend_region: Optional[str] = None,
|
||||||
resource_region=None,
|
resource_region: Optional[str] = None,
|
||||||
aggregator=None,
|
aggregator: Optional[Dict[str, Any]] = None,
|
||||||
):
|
):
|
||||||
"""For AWS Config. This will list all of the resources of the given type and optional resource name and region.
|
"""For AWS Config. This will list all of the resources of the given type and optional resource name and region.
|
||||||
|
|
||||||
@ -158,12 +158,12 @@ class ConfigQueryModel:
|
|||||||
|
|
||||||
def get_config_resource(
|
def get_config_resource(
|
||||||
self,
|
self,
|
||||||
account_id,
|
account_id: str,
|
||||||
resource_id,
|
resource_id: str,
|
||||||
resource_name=None,
|
resource_name: Optional[str] = None,
|
||||||
backend_region=None,
|
backend_region: Optional[str] = None,
|
||||||
resource_region=None,
|
resource_region: Optional[str] = None,
|
||||||
):
|
) -> Dict[str, Any]:
|
||||||
"""For AWS Config. This will query the backend for the specific resource type configuration.
|
"""For AWS Config. This will query the backend for the specific resource type configuration.
|
||||||
|
|
||||||
This supports both aggregated, and non-aggregated fetching -- for batched fetching -- the Config batching requests
|
This supports both aggregated, and non-aggregated fetching -- for batched fetching -- the Config batching requests
|
||||||
|
@ -870,7 +870,7 @@ class AWSServiceSpec(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path: str):
|
||||||
spec = load_resource("botocore", path)
|
spec = load_resource("botocore", path)
|
||||||
|
|
||||||
self.metadata = spec["metadata"]
|
self.metadata = spec["metadata"]
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import pathlib
|
import pathlib
|
||||||
|
from typing import Any, Dict
|
||||||
from os import listdir
|
from os import listdir
|
||||||
from ..utils import generic_filter
|
from ..utils import generic_filter
|
||||||
|
|
||||||
from moto.utilities.utils import load_resource
|
from moto.utilities.utils import load_resource
|
||||||
from ..exceptions import InvalidFilter, InvalidInstanceTypeError
|
from ..exceptions import InvalidFilter, InvalidInstanceTypeError
|
||||||
|
|
||||||
INSTANCE_TYPES = load_resource(__name__, "../resources/instance_types.json")
|
INSTANCE_TYPES: Dict[str, Any] = load_resource(
|
||||||
|
__name__, "../resources/instance_types.json"
|
||||||
|
)
|
||||||
INSTANCE_FAMILIES = list(set([i.split(".")[0] for i in INSTANCE_TYPES.keys()]))
|
INSTANCE_FAMILIES = list(set([i.split(".")[0] for i in INSTANCE_TYPES.keys()]))
|
||||||
|
|
||||||
root = pathlib.Path(__file__).parent
|
root = pathlib.Path(__file__).parent
|
||||||
|
@ -3,7 +3,7 @@ import hashlib
|
|||||||
import pkgutil
|
import pkgutil
|
||||||
|
|
||||||
from collections.abc import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict, Union
|
||||||
|
|
||||||
|
|
||||||
def str2bool(v):
|
def str2bool(v):
|
||||||
@ -13,7 +13,9 @@ def str2bool(v):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def load_resource(package, resource, as_json=True):
|
def load_resource(
|
||||||
|
package: str, resource: str, as_json: bool = True
|
||||||
|
) -> Union[Dict[str, Any], str]:
|
||||||
"""
|
"""
|
||||||
Open a file, and return the contents as JSON.
|
Open a file, and return the contents as JSON.
|
||||||
Usage:
|
Usage:
|
||||||
|
@ -18,7 +18,7 @@ disable = W,C,R,E
|
|||||||
enable = anomalous-backslash-in-string, arguments-renamed, dangerous-default-value, deprecated-module, function-redefined, import-self, redefined-builtin, redefined-outer-name, reimported, pointless-statement, super-with-arguments, unused-argument, unused-import, unused-variable, useless-else-on-loop, wildcard-import
|
enable = anomalous-backslash-in-string, arguments-renamed, dangerous-default-value, deprecated-module, function-redefined, import-self, redefined-builtin, redefined-outer-name, reimported, pointless-statement, super-with-arguments, unused-argument, unused-import, unused-variable, useless-else-on-loop, wildcard-import
|
||||||
|
|
||||||
[mypy]
|
[mypy]
|
||||||
files= moto/a*,moto/b*,moto/ce,moto/cloudformation,moto/cloudfront,moto/cloudtrail,moto/codebuild,moto/cloudwatch,moto/codepipeline,moto/codecommit,moto/cognito*,moto/comprehend
|
files= moto/a*,moto/b*,moto/ce,moto/cloudformation,moto/cloudfront,moto/cloudtrail,moto/codebuild,moto/cloudwatch,moto/codepipeline,moto/codecommit,moto/cognito*,moto/comprehend,moto/config
|
||||||
show_column_numbers=True
|
show_column_numbers=True
|
||||||
show_error_codes = True
|
show_error_codes = True
|
||||||
disable_error_code=abstract
|
disable_error_code=abstract
|
||||||
|
Loading…
Reference in New Issue
Block a user