2019-10-04 01:00:07 +00:00
|
|
|
import json
|
2023-04-20 16:47:39 +00:00
|
|
|
from typing import Any, Dict, List, Optional, Tuple
|
2019-10-04 01:00:07 +00:00
|
|
|
|
2019-09-24 00:16:20 +00:00
|
|
|
from moto.core.exceptions import InvalidNextTokenException
|
2022-05-11 20:18:35 +00:00
|
|
|
from moto.core.common_models import ConfigQueryModel
|
2019-09-24 00:16:20 +00:00
|
|
|
from moto.s3 import s3_backends
|
|
|
|
|
|
|
|
|
|
|
|
class S3ConfigQuery(ConfigQueryModel):
|
|
|
|
def list_config_service_resources(
|
|
|
|
self,
|
2023-04-20 16:47:39 +00:00
|
|
|
account_id: str,
|
|
|
|
resource_ids: Optional[List[str]],
|
|
|
|
resource_name: Optional[str],
|
|
|
|
limit: int,
|
|
|
|
next_token: Optional[str],
|
|
|
|
backend_region: Optional[str] = None,
|
|
|
|
resource_region: Optional[str] = None,
|
|
|
|
aggregator: Optional[Dict[str, Any]] = None,
|
|
|
|
) -> Tuple[List[Dict[str, Any]], Optional[str]]:
|
2019-10-24 01:37:35 +00:00
|
|
|
# The resource_region only matters for aggregated queries as you can filter on bucket regions for them.
|
|
|
|
# For other resource types, you would need to iterate appropriately for the backend_region.
|
2019-09-24 00:16:20 +00:00
|
|
|
|
|
|
|
# Resource IDs are the same as S3 bucket names
|
|
|
|
# For aggregation -- did we get both a resource ID and a resource name?
|
|
|
|
if resource_ids and resource_name:
|
|
|
|
# If the values are different, then return an empty list:
|
|
|
|
if resource_name not in resource_ids:
|
|
|
|
return [], None
|
|
|
|
|
|
|
|
# If no filter was passed in for resource names/ids then return them all:
|
|
|
|
if not resource_ids and not resource_name:
|
2022-08-13 09:49:43 +00:00
|
|
|
bucket_list = list(self.backends[account_id]["global"].buckets.keys())
|
2019-09-24 00:16:20 +00:00
|
|
|
|
|
|
|
else:
|
|
|
|
# Match the resource name / ID:
|
|
|
|
bucket_list = []
|
|
|
|
filter_buckets = [resource_name] if resource_name else resource_ids
|
|
|
|
|
2022-08-13 09:49:43 +00:00
|
|
|
for bucket in self.backends[account_id]["global"].buckets.keys():
|
2023-04-20 16:47:39 +00:00
|
|
|
if bucket in filter_buckets: # type: ignore
|
2019-09-24 00:16:20 +00:00
|
|
|
bucket_list.append(bucket)
|
|
|
|
|
2019-10-24 01:37:35 +00:00
|
|
|
# Filter on the proper region if supplied:
|
|
|
|
region_filter = backend_region or resource_region
|
|
|
|
if region_filter:
|
2019-09-24 00:16:20 +00:00
|
|
|
region_buckets = []
|
|
|
|
|
|
|
|
for bucket in bucket_list:
|
2022-08-13 09:49:43 +00:00
|
|
|
if (
|
|
|
|
self.backends[account_id]["global"].buckets[bucket].region_name
|
|
|
|
== region_filter
|
|
|
|
):
|
2019-09-24 00:16:20 +00:00
|
|
|
region_buckets.append(bucket)
|
|
|
|
|
|
|
|
bucket_list = region_buckets
|
|
|
|
|
|
|
|
if not bucket_list:
|
|
|
|
return [], None
|
|
|
|
|
|
|
|
# Pagination logic:
|
|
|
|
sorted_buckets = sorted(bucket_list)
|
|
|
|
new_token = None
|
|
|
|
|
|
|
|
# Get the start:
|
|
|
|
if not next_token:
|
|
|
|
start = 0
|
|
|
|
else:
|
|
|
|
# Tokens for this moto feature is just the bucket name:
|
|
|
|
# For OTHER non-global resource types, it's the region concatenated with the resource ID.
|
|
|
|
if next_token not in sorted_buckets:
|
|
|
|
raise InvalidNextTokenException()
|
|
|
|
|
|
|
|
start = sorted_buckets.index(next_token)
|
|
|
|
|
|
|
|
# Get the list of items to collect:
|
|
|
|
bucket_list = sorted_buckets[start : (start + limit)]
|
|
|
|
|
|
|
|
if len(sorted_buckets) > (start + limit):
|
|
|
|
new_token = sorted_buckets[start + limit]
|
|
|
|
|
|
|
|
return (
|
|
|
|
[
|
2019-10-31 15:44:26 +00:00
|
|
|
{
|
2019-09-24 00:16:20 +00:00
|
|
|
"type": "AWS::S3::Bucket",
|
|
|
|
"id": bucket,
|
|
|
|
"name": bucket,
|
2022-08-13 09:49:43 +00:00
|
|
|
"region": self.backends[account_id]["global"]
|
|
|
|
.buckets[bucket]
|
|
|
|
.region_name,
|
2019-09-24 00:16:20 +00:00
|
|
|
}
|
|
|
|
for bucket in bucket_list
|
|
|
|
],
|
|
|
|
new_token,
|
2019-10-31 15:44:26 +00:00
|
|
|
)
|
|
|
|
|
2019-10-04 01:00:07 +00:00
|
|
|
def get_config_resource(
|
2022-08-13 09:49:43 +00:00
|
|
|
self,
|
2023-04-20 16:47:39 +00:00
|
|
|
account_id: str,
|
|
|
|
resource_id: str,
|
|
|
|
resource_name: Optional[str] = None,
|
|
|
|
backend_region: Optional[str] = None,
|
|
|
|
resource_region: Optional[str] = None,
|
|
|
|
) -> Optional[Dict[str, Any]]:
|
2019-10-04 01:00:07 +00:00
|
|
|
# Get the bucket:
|
2022-08-13 09:49:43 +00:00
|
|
|
bucket = self.backends[account_id]["global"].buckets.get(resource_id, {})
|
2019-10-04 01:00:07 +00:00
|
|
|
|
|
|
|
if not bucket:
|
2023-04-20 16:47:39 +00:00
|
|
|
return None
|
2019-10-04 01:00:07 +00:00
|
|
|
|
|
|
|
# Are we filtering based on region?
|
2019-10-24 01:37:35 +00:00
|
|
|
region_filter = backend_region or resource_region
|
|
|
|
if region_filter and bucket.region_name != region_filter:
|
2023-04-20 16:47:39 +00:00
|
|
|
return None
|
2019-10-04 01:00:07 +00:00
|
|
|
|
|
|
|
# Are we also filtering on bucket name?
|
|
|
|
if resource_name and bucket.name != resource_name:
|
2023-04-20 16:47:39 +00:00
|
|
|
return None
|
2019-10-04 01:00:07 +00:00
|
|
|
|
|
|
|
# Format the bucket to the AWS Config format:
|
|
|
|
config_data = bucket.to_config_dict()
|
|
|
|
|
|
|
|
# The 'configuration' field is also a JSON string:
|
|
|
|
config_data["configuration"] = json.dumps(config_data["configuration"])
|
|
|
|
|
|
|
|
# Supplementary config need all values converted to JSON strings if they are not strings already:
|
|
|
|
for field, value in config_data["supplementaryConfiguration"].items():
|
|
|
|
if not isinstance(value, str):
|
|
|
|
config_data["supplementaryConfiguration"][field] = json.dumps(value)
|
|
|
|
|
|
|
|
return config_data
|
|
|
|
|
2019-09-24 00:16:20 +00:00
|
|
|
|
|
|
|
s3_config_query = S3ConfigQuery(s3_backends)
|