Repository: getmoto/moto
Pull request #7610 (2024-04-20 20:34:42)

Cloudfront: fix cachebehavior response
Resolves https://github.com/getmoto/moto/issues/7586


Patch:
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 116ce7282..ee6d58a46 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -6162,7 +6162,7 @@
 - [ ] describe_project_versions
 - [ ] describe_projects
 - [ ] describe_stream_processor
-- [ ] detect_custom_labels
+- [X] detect_custom_labels
 - [ ] detect_faces
 - [X] detect_labels
 - [ ] detect_moderation_labels
diff --git a/moto/athena/models.py b/moto/athena/models.py
index b78a0bf06..b7e8d66dc 100644
--- a/moto/athena/models.py
+++ b/moto/athena/models.py
@@ -96,7 +96,7 @@ class Execution(BaseModel):
         self.start_time = time.time()
         self.status = "SUCCEEDED"
 
-        if "OutputLocation" in self.config:
+        if self.config is not None and "OutputLocation" in self.config:
             if not self.config["OutputLocation"].endswith("/"):
                 self.config["OutputLocation"] += "/"
             self.config["OutputLocation"] += f"{self.id}.csv"
diff --git a/moto/cloudfront/models.py b/moto/cloudfront/models.py
index 588030408..3c4de5227 100644
--- a/moto/cloudfront/models.py
+++ b/moto/cloudfront/models.py
@@ -51,6 +51,7 @@ class ForwardedValues:
             self.whitelisted_names = [self.whitelisted_names]
         self.headers: List[Any] = []
         self.query_string_cache_keys: List[Any] = []
+        self.cookies: List[Dict[str, Any]] = config.get("Cookies") or []
 
 
 class DefaultCacheBehaviour:
@@ -80,6 +81,19 @@ class DefaultCacheBehaviour:
         self.realtime_log_config_arn = config.get("RealtimeLogConfigArn") or ""
 
 
+class CacheBehaviour(DefaultCacheBehaviour):
+    def __init__(self, config: Dict[str, Any]):
+        super().__init__(config)
+        self.path_pattern: str = config.get("PathPattern", "")
+        methods = config.get("AllowedMethods", {})
+        self.cached_methods: List[str] = (
+            methods.get("CachedMethods", {}).get("Items", {}).get("Method", [])
+        )
+        self.allowed_methods: List[str] = methods.get("Items", {}).get("Method", [])
+        self.cache_policy_id = config.get("CachePolicyId", "")
+        self.origin_request_policy_id = config.get("OriginRequestPolicyId", "")
+
+
 class Logging:
     def __init__(self, config: Dict[str, Any]) -> None:
         self.enabled = config.get("Enabled") or False
@@ -157,6 +171,9 @@ class DistributionConfig:
             config["DefaultCacheBehavior"]
         )
         self.cache_behaviors: List[Any] = []
+        if config.get("CacheBehaviors", {}).get("Items"):
+            for _, v in config.get("CacheBehaviors", {}).get("Items").items():
+                self.cache_behaviors.append(CacheBehaviour(v))
         self.custom_error_responses: List[Any] = []
         self.logging = Logging(config.get("Logging") or {})
         self.enabled = config.get("Enabled") or False
diff --git a/moto/cloudfront/responses.py b/moto/cloudfront/responses.py
index af54383db..cd0d19dec 100644
--- a/moto/cloudfront/responses.py
+++ b/moto/cloudfront/responses.py
@@ -397,98 +397,100 @@ DIST_CONFIG_TEMPLATE = """
         {% if distribution.distribution_config.cache_behaviors %}
         <Items>
           {% for behaviour in distribution.distribution_config.cache_behaviors %}
-            <PathPattern>{{ behaviour.path_pattern }}</PathPattern>
-            <TargetOriginId>{{ behaviour.target_origin_id }}</TargetOriginId>
-            <TrustedSigners>
-              <Enabled>{{ behaviour.trusted_signers.enabled }}</Enabled>
-              <Quantity>{{ behaviour.trusted_signers.quantity }}</Quantity>
-              <Items>
-                {% for account_nr  in behaviour.trusted_signers %}
-                  <AwsAccountNumber>{{ account_nr }}</AwsAccountNumber>
-                {% endfor %}
-              </Items>
-            </TrustedSigners>
-            <TrustedKeyGroups>
-              <Enabled>{{ behaviour.trusted_key_groups.enabled }}</Enabled>
-              <Quantity>{{ behaviour.trusted_key_groups.quantity }}</Quantity>
-              <Items>
-                {% for trusted_key_group_id_list  in behaviour.trusted_key_groups.TrustedKeyGroupIdList %}
-                  <KeyGroup>{{ trusted_key_group_id_list.key_group }}</KeyGroup>
-                {% endfor %}
-              </Items>
-            </TrustedKeyGroups>
-            <ViewerProtocolPolicy>{{ ViewerProtocolPolicy }}</ViewerProtocolPolicy>
-            <AllowedMethods>
-              <Quantity>{{ behaviour.allowed_methods.quantity }}</Quantity>
-              <Items>
-                {% for methods_list in behaviour.allowed_methods.MethodsList %}{{ Method }}{% endfor %}
-              </Items>
-              <CachedMethods>
-                <Quantity>{{ behaviour.allowed_methods.cached_methods.quantity }}</Quantity>
-                <Items>
-                  {% for methods_list in behaviour.allowed_methods.cached_methods.MethodsList %}{{ Method }}{% endfor %}
-                </Items>
-              </CachedMethods>
-            </AllowedMethods>
-            <SmoothStreaming>{{ behaviour.smooth_streaming }}</SmoothStreaming>
-            <Compress>{{ behaviour.compress }}</Compress>
-            <LambdaFunctionAssociations>
-              <Quantity>{{ behaviour.lambda_function_associations.quantity }}</Quantity>
-              <Items>
-                {% for lambda_function_association_list in behaviour.lambda_function_associations.LambdaFunctionAssociationList %}
-                  <LambdaFunctionARN>{{ LambdaFunctionARN }}</LambdaFunctionARN>
-                  <EventType>{{ EventType }}</EventType>
-                  <IncludeBody>{{ lambda_function_association_list.include_body }}</IncludeBody>
-                {% endfor %}
-              </Items>
-            </LambdaFunctionAssociations>
-            <FunctionAssociations>
-              <Quantity>{{ behaviour.function_associations.quantity }}</Quantity>
-              <Items>
-                {% for function_association_list  in behaviour.function_associations.FunctionAssociationList %}
-                  <FunctionARN>{{ FunctionARN }}</FunctionARN>
-                  <EventType>{{ EventType }}</EventType>
-                {% endfor %}
-              </Items>
-            </FunctionAssociations>
-            <FieldLevelEncryptionId>{{ behaviour.field_level_encryption_id }}</FieldLevelEncryptionId>
-            <RealtimeLogConfigArn>{{ behaviour.realtime_log_config_arn }}</RealtimeLogConfigArn>
-            <CachePolicyId>{{ behaviour.cache_policy_id }}</CachePolicyId>
-            <OriginRequestPolicyId>{{ behaviour.origin_request_policy_id }}</OriginRequestPolicyId>
-            <ResponseHeadersPolicyId>{{ behaviour.response_headers_policy_id }}</ResponseHeadersPolicyId>
-            <ForwardedValues>
-              <QueryString>{{ behaviour.forwarded_values.query_string }}</QueryString>
-              <Cookies>
-                <Forward>{{ ItemSelection }}</Forward>
-                <WhitelistedNames>
-                  <Quantity>{{ behaviour.forwarded_values.cookies.whitelisted_names.quantity }}</Quantity>
+            <CacheBehavior>
+                <PathPattern>{{ behaviour.path_pattern }}</PathPattern>
+                <TargetOriginId>{{ behaviour.target_origin_id }}</TargetOriginId>
+                <TrustedSigners>
+                  <Enabled>{{ behaviour.trusted_signers.enabled }}</Enabled>
+                  <Quantity>{{ behaviour.trusted_signers | length }}</Quantity>
                   <Items>
-                    {% for cookie_name_list  in behaviour.forwarded_values.cookies.whitelisted_names.CookieNameList %}
-                      <Name>{{ cookie_name_list.name }}</Name>
+                    {% for account_nr  in behaviour.trusted_signers %}
+                      <AwsAccountNumber>{{ account_nr }}</AwsAccountNumber>
                     {% endfor %}
                   </Items>
-                </WhitelistedNames>
-              </Cookies>
-              <Headers>
-                <Quantity>{{ behaviour.forwarded_values.headers.quantity }}</Quantity>
-                <Items>
-                  {% for header_list in behaviour.forwarded_values.headers.HeaderList %}
-                    <Name>{{ header_list.name }}</Name>
-                  {% endfor %}
-                </Items>
-              </Headers>
-              <QueryStringCacheKeys>
-                <Quantity>{{ behaviour.forwarded_values.query_string_cache_keys.quantity }}</Quantity>
-                <Items>
-                  {% for query_string_cache_keys_list in behaviour.forwarded_values.query_string_cache_keys.QueryStringCacheKeysList %}
-                    <Name>{{ query_string_cache_keys_list.name }}</Name>
-                  {% endfor %}
-                </Items>
-              </QueryStringCacheKeys>
-            </ForwardedValues>
-            <MinTTL>{{ behaviour.min_ttl }}</MinTTL>
-            <DefaultTTL>{{ behaviour.default_ttl }}</DefaultTTL>
-            <MaxTTL>{{ behaviour.max_ttl }}</MaxTTL>
+                </TrustedSigners>
+                <TrustedKeyGroups>
+                  <Enabled>{{ behaviour.trusted_key_groups.enabled }}</Enabled>
+                  <Quantity>{{ behaviour.trusted_key_groups | length }}</Quantity>
+                  <Items>
+                    {% for trusted_key_group_id_list  in behaviour.trusted_key_groups.TrustedKeyGroupIdList %}
+                      <KeyGroup>{{ trusted_key_group_id_list.key_group }}</KeyGroup>
+                    {% endfor %}
+                  </Items>
+                </TrustedKeyGroups>
+                <ViewerProtocolPolicy>{{ behaviour.viewer_protocol_policy }}</ViewerProtocolPolicy>
+                <AllowedMethods>
+                  <Quantity>{{ behaviour.allowed_methods | length }}</Quantity>
+                  <Items>
+                    {% for method in behaviour.allowed_methods %}<Method>{{ method }}</Method>{% endfor %}
+                  </Items>
+                  <CachedMethods>
+                    <Quantity>{{ behaviour.cached_methods|length }}</Quantity>
+                    <Items>
+                      {% for method in behaviour.cached_methods %}<Method>{{ method }}</Method>{% endfor %}
+                    </Items>
+                  </CachedMethods>
+                </AllowedMethods>
+                <SmoothStreaming>{{ behaviour.smooth_streaming }}</SmoothStreaming>
+                <Compress>{{ behaviour.compress }}</Compress>
+                <LambdaFunctionAssociations>
+                  <Quantity>{{ behaviour.lambda_function_associations | length }}</Quantity>
+                  <Items>
+                    {% for lambda_function_association_list in behaviour.lambda_function_associations.LambdaFunctionAssociationList %}
+                      <LambdaFunctionARN>{{ LambdaFunctionARN }}</LambdaFunctionARN>
+                      <EventType>{{ EventType }}</EventType>
+                      <IncludeBody>{{ lambda_function_association_list.include_body }}</IncludeBody>
+                    {% endfor %}
+                  </Items>
+                </LambdaFunctionAssociations>
+                <FunctionAssociations>
+                  <Quantity>{{ behaviour.function_associations | length }}</Quantity>
+                  <Items>
+                    {% for function_association_list  in behaviour.function_associations.FunctionAssociationList %}
+                      <FunctionARN>{{ FunctionARN }}</FunctionARN>
+                      <EventType>{{ EventType }}</EventType>
+                    {% endfor %}
+                  </Items>
+                </FunctionAssociations>
+                <FieldLevelEncryptionId>{{ behaviour.field_level_encryption_id }}</FieldLevelEncryptionId>
+                <RealtimeLogConfigArn>{{ behaviour.realtime_log_config_arn }}</RealtimeLogConfigArn>
+                <CachePolicyId>{{ behaviour.cache_policy_id }}</CachePolicyId>
+                <OriginRequestPolicyId>{{ behaviour.origin_request_policy_id }}</OriginRequestPolicyId>
+                <ResponseHeadersPolicyId>{{ behaviour.response_headers_policy_id }}</ResponseHeadersPolicyId>
+                <ForwardedValues>
+                  <QueryString>{{ behaviour.forwarded_values.query_string }}</QueryString>
+                  <Cookies>
+                    <Forward>{{ ItemSelection }}</Forward>
+                    <WhitelistedNames>
+                      <Quantity>{{ behaviour.forwarded_values.cookies.whitelisted_names| length }}</Quantity>
+                      <Items>
+                        {% for wl_name in behaviour.forwarded_values.cookies.whitelisted_names %}
+                          <Name>{{ wl_name }}</Name>
+                        {% endfor %}
+                      </Items>
+                    </WhitelistedNames>
+                  </Cookies>
+                  <Headers>
+                    <Quantity>{{ behaviour.forwarded_values.headers | length }}</Quantity>
+                    <Items>
+                      {% for header_list in behaviour.forwarded_values.headers.HeaderList %}
+                        <Name>{{ header_list.name }}</Name>
+                      {% endfor %}
+                    </Items>
+                  </Headers>
+                  <QueryStringCacheKeys>
+                    <Quantity>{{ behaviour.forwarded_values.query_string_cache_keys | length }}</Quantity>
+                    <Items>
+                      {% for query_string_cache_keys_list in behaviour.forwarded_values.query_string_cache_keys.QueryStringCacheKeysList %}
+                        <Name>{{ query_string_cache_keys_list.name }}</Name>
+                      {% endfor %}
+                    </Items>
+                  </QueryStringCacheKeys>
+                </ForwardedValues>
+                <MinTTL>{{ behaviour.min_ttl }}</MinTTL>
+                <DefaultTTL>{{ behaviour.default_ttl }}</DefaultTTL>
+                <MaxTTL>{{ behaviour.max_ttl }}</MaxTTL>
+            </CacheBehavior>
           {% endfor %}
         </Items>
         {% endif %}
diff --git a/moto/ec2/models/elastic_ip_addresses.py b/moto/ec2/models/elastic_ip_addresses.py
index e435e3c8e..fdc14d2d0 100644
--- a/moto/ec2/models/elastic_ip_addresses.py
+++ b/moto/ec2/models/elastic_ip_addresses.py
@@ -239,6 +239,11 @@ class ElasticAddressBackend:
 
         return matches
 
+    def describe_addresses_attribute(
+        self, allocation_ids: Optional[List[str]] = None
+    ) -> List[ElasticAddress]:
+        return self.describe_addresses(allocation_ids)
+
     def disassociate_address(
         self, address: Optional[str] = None, association_id: Optional[str] = None
     ) -> None:
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-central-2.json b/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-central-2.json
index 5197f6e73..d5c9e659b 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-central-2.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-central-2.json
@@ -627,6 +627,42 @@
   "InstanceType": "r6g.xlarge",
   "Location": "euc2-az1"
  },
+ {
+  "InstanceType": "r6gd.12xlarge",
+  "Location": "euc2-az1"
+ },
+ {
+  "InstanceType": "r6gd.16xlarge",
+  "Location": "euc2-az1"
+ },
+ {
+  "InstanceType": "r6gd.2xlarge",
+  "Location": "euc2-az1"
+ },
+ {
+  "InstanceType": "r6gd.4xlarge",
+  "Location": "euc2-az1"
+ },
+ {
+  "InstanceType": "r6gd.8xlarge",
+  "Location": "euc2-az1"
+ },
+ {
+  "InstanceType": "r6gd.large",
+  "Location": "euc2-az1"
+ },
+ {
+  "InstanceType": "r6gd.medium",
+  "Location": "euc2-az1"
+ },
+ {
+  "InstanceType": "r6gd.metal",
+  "Location": "euc2-az1"
+ },
+ {
+  "InstanceType": "r6gd.xlarge",
+  "Location": "euc2-az1"
+ },
  {
   "InstanceType": "r6i.12xlarge",
   "Location": "euc2-az1"
@@ -1375,6 +1411,42 @@
   "InstanceType": "r6g.xlarge",
   "Location": "euc2-az2"
  },
+ {
+  "InstanceType": "r6gd.12xlarge",
+  "Location": "euc2-az2"
+ },
+ {
+  "InstanceType": "r6gd.16xlarge",
+  "Location": "euc2-az2"
+ },
+ {
+  "InstanceType": "r6gd.2xlarge",
+  "Location": "euc2-az2"
+ },
+ {
+  "InstanceType": "r6gd.4xlarge",
+  "Location": "euc2-az2"
+ },
+ {
+  "InstanceType": "r6gd.8xlarge",
+  "Location": "euc2-az2"
+ },
+ {
+  "InstanceType": "r6gd.large",
+  "Location": "euc2-az2"
+ },
+ {
+  "InstanceType": "r6gd.medium",
+  "Location": "euc2-az2"
+ },
+ {
+  "InstanceType": "r6gd.metal",
+  "Location": "euc2-az2"
+ },
+ {
+  "InstanceType": "r6gd.xlarge",
+  "Location": "euc2-az2"
+ },
  {
   "InstanceType": "r6i.12xlarge",
   "Location": "euc2-az2"
@@ -2071,6 +2143,42 @@
   "InstanceType": "r6g.xlarge",
   "Location": "euc2-az3"
  },
+ {
+  "InstanceType": "r6gd.12xlarge",
+  "Location": "euc2-az3"
+ },
+ {
+  "InstanceType": "r6gd.16xlarge",
+  "Location": "euc2-az3"
+ },
+ {
+  "InstanceType": "r6gd.2xlarge",
+  "Location": "euc2-az3"
+ },
+ {
+  "InstanceType": "r6gd.4xlarge",
+  "Location": "euc2-az3"
+ },
+ {
+  "InstanceType": "r6gd.8xlarge",
+  "Location": "euc2-az3"
+ },
+ {
+  "InstanceType": "r6gd.large",
+  "Location": "euc2-az3"
+ },
+ {
+  "InstanceType": "r6gd.medium",
+  "Location": "euc2-az3"
+ },
+ {
+  "InstanceType": "r6gd.metal",
+  "Location": "euc2-az3"
+ },
+ {
+  "InstanceType": "r6gd.xlarge",
+  "Location": "euc2-az3"
+ },
  {
   "InstanceType": "r6i.12xlarge",
   "Location": "euc2-az3"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone/eu-central-2.json b/moto/ec2/resources/instance_type_offerings/availability-zone/eu-central-2.json
index 59a1fdde5..9ce7dab36 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone/eu-central-2.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone/eu-central-2.json
@@ -627,6 +627,42 @@
   "InstanceType": "r6g.xlarge",
   "Location": "eu-central-2a"
  },
+ {
+  "InstanceType": "r6gd.12xlarge",
+  "Location": "eu-central-2a"
+ },
+ {
+  "InstanceType": "r6gd.16xlarge",
+  "Location": "eu-central-2a"
+ },
+ {
+  "InstanceType": "r6gd.2xlarge",
+  "Location": "eu-central-2a"
+ },
+ {
+  "InstanceType": "r6gd.4xlarge",
+  "Location": "eu-central-2a"
+ },
+ {
+  "InstanceType": "r6gd.8xlarge",
+  "Location": "eu-central-2a"
+ },
+ {
+  "InstanceType": "r6gd.large",
+  "Location": "eu-central-2a"
+ },
+ {
+  "InstanceType": "r6gd.medium",
+  "Location": "eu-central-2a"
+ },
+ {
+  "InstanceType": "r6gd.metal",
+  "Location": "eu-central-2a"
+ },
+ {
+  "InstanceType": "r6gd.xlarge",
+  "Location": "eu-central-2a"
+ },
  {
   "InstanceType": "r6i.12xlarge",
   "Location": "eu-central-2a"
@@ -1375,6 +1411,42 @@
   "InstanceType": "r6g.xlarge",
   "Location": "eu-central-2b"
  },
+ {
+  "InstanceType": "r6gd.12xlarge",
+  "Location": "eu-central-2b"
+ },
+ {
+  "InstanceType": "r6gd.16xlarge",
+  "Location": "eu-central-2b"
+ },
+ {
+  "InstanceType": "r6gd.2xlarge",
+  "Location": "eu-central-2b"
+ },
+ {
+  "InstanceType": "r6gd.4xlarge",
+  "Location": "eu-central-2b"
+ },
+ {
+  "InstanceType": "r6gd.8xlarge",
+  "Location": "eu-central-2b"
+ },
+ {
+  "InstanceType": "r6gd.large",
+  "Location": "eu-central-2b"
+ },
+ {
+  "InstanceType": "r6gd.medium",
+  "Location": "eu-central-2b"
+ },
+ {
+  "InstanceType": "r6gd.metal",
+  "Location": "eu-central-2b"
+ },
+ {
+  "InstanceType": "r6gd.xlarge",
+  "Location": "eu-central-2b"
+ },
  {
   "InstanceType": "r6i.12xlarge",
   "Location": "eu-central-2b"
@@ -2071,6 +2143,42 @@
   "InstanceType": "r6g.xlarge",
   "Location": "eu-central-2c"
  },
+ {
+  "InstanceType": "r6gd.12xlarge",
+  "Location": "eu-central-2c"
+ },
+ {
+  "InstanceType": "r6gd.16xlarge",
+  "Location": "eu-central-2c"
+ },
+ {
+  "InstanceType": "r6gd.2xlarge",
+  "Location": "eu-central-2c"
+ },
+ {
+  "InstanceType": "r6gd.4xlarge",
+  "Location": "eu-central-2c"
+ },
+ {
+  "InstanceType": "r6gd.8xlarge",
+  "Location": "eu-central-2c"
+ },
+ {
+  "InstanceType": "r6gd.large",
+  "Location": "eu-central-2c"
+ },
+ {
+  "InstanceType": "r6gd.medium",
+  "Location": "eu-central-2c"
+ },
+ {
+  "InstanceType": "r6gd.metal",
+  "Location": "eu-central-2c"
+ },
+ {
+  "InstanceType": "r6gd.xlarge",
+  "Location": "eu-central-2c"
+ },
  {
   "InstanceType": "r6i.12xlarge",
   "Location": "eu-central-2c"
diff --git a/moto/ec2/resources/instance_type_offerings/region/eu-central-2.json b/moto/ec2/resources/instance_type_offerings/region/eu-central-2.json
index e2acd40f1..a1a041b21 100644
--- a/moto/ec2/resources/instance_type_offerings/region/eu-central-2.json
+++ b/moto/ec2/resources/instance_type_offerings/region/eu-central-2.json
@@ -627,6 +627,42 @@
   "InstanceType": "r6g.xlarge",
   "Location": "eu-central-2"
  },
+ {
+  "InstanceType": "r6gd.12xlarge",
+  "Location": "eu-central-2"
+ },
+ {
+  "InstanceType": "r6gd.16xlarge",
+  "Location": "eu-central-2"
+ },
+ {
+  "InstanceType": "r6gd.2xlarge",
+  "Location": "eu-central-2"
+ },
+ {
+  "InstanceType": "r6gd.4xlarge",
+  "Location": "eu-central-2"
+ },
+ {
+  "InstanceType": "r6gd.8xlarge",
+  "Location": "eu-central-2"
+ },
+ {
+  "InstanceType": "r6gd.large",
+  "Location": "eu-central-2"
+ },
+ {
+  "InstanceType": "r6gd.medium",
+  "Location": "eu-central-2"
+ },
+ {
+  "InstanceType": "r6gd.metal",
+  "Location": "eu-central-2"
+ },
+ {
+  "InstanceType": "r6gd.xlarge",
+  "Location": "eu-central-2"
+ },
  {
   "InstanceType": "r6i.12xlarge",
   "Location": "eu-central-2"
diff --git a/moto/ec2/responses/elastic_ip_addresses.py b/moto/ec2/responses/elastic_ip_addresses.py
index c421fc8c9..ac9d1729b 100644
--- a/moto/ec2/responses/elastic_ip_addresses.py
+++ b/moto/ec2/responses/elastic_ip_addresses.py
@@ -82,6 +82,13 @@ class ElasticIPAddresses(EC2BaseResponse):
         template = self.response_template(DESCRIBE_ADDRESS_RESPONSE)
         return template.render(addresses=addresses)
 
+    def describe_addresses_attribute(self) -> str:
+        self.error_on_dryrun()
+        allocation_ids = self._get_multi_param("AllocationId")
+        addresses = self.ec2_backend.describe_addresses_attribute(allocation_ids)
+        template = self.response_template(DESCRIBE_ADDRESS_ATTRIBUTE_RESPONSE)
+        return template.render(addresses=addresses)
+
     def disassociate_address(self) -> str:
         if (
             "PublicIp" not in self.querystring
@@ -180,6 +187,26 @@ DESCRIBE_ADDRESS_RESPONSE = """<DescribeAddressesResponse xmlns="http://ec2.amaz
   </addressesSet>
 </DescribeAddressesResponse>"""
 
+DESCRIBE_ADDRESS_ATTRIBUTE_RESPONSE = """<DescribeAddressesAttributeResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
+  <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
+  <addressSet>
+    {% for address in addresses %}
+        <item>
+          <publicIp>{{ address.public_ip }}</publicIp>
+          {% if address.allocation_id %}
+            <allocationId>{{ address.allocation_id }}</allocationId>
+          {% endif %}
+          {% if address.ptrRecord %}
+            <ptrRecord>{{ address.ptrRecord }}</ptrRecord>
+          {% endif %}
+          {% if address.ptrRecordUpdate %}
+            <ptrRecordUpdate>{{ address.ptrRecordUpdate }}</ptrRecordUpdate>
+          {% endif %}
+        </item>
+    {% endfor %}
+  </addressSet>
+</DescribeAddressesAttributeResponse>"""
+
 DISASSOCIATE_ADDRESS_RESPONSE = """<DisassociateAddressResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
   <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
   <return>true</return>
diff --git a/moto/rekognition/models.py b/moto/rekognition/models.py
index d7f05d7ba..9445832ce 100644
--- a/moto/rekognition/models.py
+++ b/moto/rekognition/models.py
@@ -76,6 +76,9 @@ class RekognitionBackend(BaseBackend):
             "3.0",
         )
 
+    def detect_custom_labels(self) -> Tuple[List[Dict[str, Any]]]:
+        return (self._detect_custom_labels_detections(),)
+
     # private
 
     def _job_id(self) -> str:
@@ -755,5 +758,21 @@ class RekognitionBackend(BaseBackend):
             },
         ]
 
+    def _detect_custom_labels_detections(self) -> List[Dict[str, Any]]:
+        return [
+            {
+                "Name": "MyLogo",
+                "Confidence": 77.7729721069336,
+                "Geometry": {
+                    "BoundingBox": {
+                        "Width": 0.198987677693367,
+                        "Height": 0.31296101212501526,
+                        "Left": 0.07924537360668182,
+                        "Top": 0.4037395715713501,
+                    }
+                },
+            }
+        ]
+
 
 rekognition_backends = BackendDict(RekognitionBackend, "rekognition")
diff --git a/moto/rekognition/responses.py b/moto/rekognition/responses.py
index a18e912c7..abf56694b 100644
--- a/moto/rekognition/responses.py
+++ b/moto/rekognition/responses.py
@@ -103,6 +103,14 @@ class RekognitionResponse(BaseResponse):
             )
         )
 
+    def detect_custom_labels(self) -> str:
+        (custom_labels,) = self.rekognition_backend.detect_custom_labels()
+        return json.dumps(
+            dict(
+                CustomLabels=custom_labels,
+            )
+        )
+
     def start_face_search(self) -> TYPE_RESPONSE:
         headers = {"Content-Type": "application/x-amz-json-1.1"}
         job_id = self.rekognition_backend.start_face_search()
diff --git a/moto/sagemaker/models.py b/moto/sagemaker/models.py
index a86a9ad66..a544f3ce6 100644
--- a/moto/sagemaker/models.py
+++ b/moto/sagemaker/models.py
@@ -2176,13 +2176,31 @@ class SageMakerModelBackend(BaseBackend):
         ]
 
     def create_trial_component(
-        self, trial_component_name: str, trial_name: str
+        self,
+        trial_component_name: str,
+        trial_name: str,
+        status: Dict[str, str],
+        start_time: Optional[datetime],
+        end_time: Optional[datetime],
+        display_name: Optional[str],
+        parameters: Optional[Dict[str, Dict[str, Union[str, float]]]],
+        input_artifacts: Optional[Dict[str, Dict[str, str]]],
+        output_artifacts: Optional[Dict[str, Dict[str, str]]],
+        metadata_properties: Optional[Dict[str, str]],
     ) -> Dict[str, Any]:
         trial_component = FakeTrialComponent(
             account_id=self.account_id,
             region_name=self.region_name,
+            display_name=display_name,
+            start_time=start_time,
+            end_time=end_time,
+            parameters=parameters,
+            input_artifacts=input_artifacts,
+            output_artifacts=output_artifacts,
+            metadata_properties=metadata_properties,
             trial_component_name=trial_component_name,
             trial_name=trial_name,
+            status=status,
             tags=[],
         )
         self.trial_components[trial_component_name] = trial_component
@@ -2264,6 +2282,62 @@ class SageMakerModelBackend(BaseBackend):
             "TrialArn": f"arn:aws:sagemaker:{self.region_name}:{self.account_id}:experiment-trial/{trial_name}",
         }
 
+    def update_trial_component(
+        self,
+        trial_component_name: str,
+        status: Optional[Dict[str, str]],
+        display_name: Optional[str],
+        start_time: Optional[datetime],
+        end_time: Optional[datetime],
+        parameters: Optional[Dict[str, Dict[str, Union[str, float]]]],
+        parameters_to_remove: Optional[List[str]],
+        input_artifacts: Optional[Dict[str, Dict[str, str]]],
+        input_artifacts_to_remove: Optional[List[str]],
+        output_artifacts: Optional[Dict[str, Dict[str, str]]],
+        output_artifacts_to_remove: Optional[List[str]],
+    ) -> Dict[str, str]:
+        try:
+            trial_component = self.trial_components[trial_component_name]
+        except KeyError:
+            arn = FakeTrialComponent.arn_formatter(
+                trial_component_name, self.account_id, self.region_name
+            )
+            raise ValidationError(message=f"Could not find trial component '{arn}'")
+
+        if status:
+            trial_component.status = status
+        if display_name:
+            trial_component.display_name = display_name
+        if start_time:
+            trial_component.start_time = start_time
+        if end_time:
+            trial_component.end_time = end_time
+        if parameters:
+            trial_component.parameters = parameters
+        if input_artifacts:
+            trial_component.input_artifacts = input_artifacts
+        if output_artifacts:
+            trial_component.output_artifacts = output_artifacts
+
+        trial_component.last_modified_time = datetime.now().strftime(
+            "%Y-%m-%d %H:%M:%S"
+        )
+
+        for parameter_to_remove in parameters_to_remove or []:
+            trial_component.parameters.pop(parameter_to_remove)
+
+        for input_artifact_to_remove in input_artifacts_to_remove or []:
+            trial_component.input_artifacts.pop(input_artifact_to_remove)
+
+        for output_artifact_to_remove in output_artifacts_to_remove or []:
+            trial_component.output_artifacts.pop(output_artifact_to_remove)
+
+        return {
+            "TrialComponentArn": FakeTrialComponent.arn_formatter(
+                trial_component_name, self.account_id, self.region_name
+            )
+        }
+
     def create_notebook_instance(
         self,
         notebook_instance_name: str,
@@ -3650,17 +3724,39 @@ class FakeTrialComponent(BaseObject):
         account_id: str,
         region_name: str,
         trial_component_name: str,
+        display_name: Optional[str],
+        start_time: Optional[datetime],
+        end_time: Optional[datetime],
+        parameters: Optional[Dict[str, Dict[str, Union[str, float]]]],
+        input_artifacts: Optional[Dict[str, Dict[str, str]]],
+        output_artifacts: Optional[Dict[str, Dict[str, str]]],
+        metadata_properties: Optional[Dict[str, str]],
+        status: Optional[Dict[str, str]],
         trial_name: Optional[str],
         tags: List[Dict[str, str]],
     ):
         self.trial_component_name = trial_component_name
+        self.display_name = (
+            display_name if display_name is not None else trial_component_name
+        )
         self.trial_component_arn = FakeTrialComponent.arn_formatter(
             trial_component_name, account_id, region_name
         )
+        self.status = status
         self.tags = tags
         self.trial_name = trial_name
+        self.start_time = start_time
+        self.end_time = end_time
         now_string = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
         self.creation_time = self.last_modified_time = now_string
+        self.created_by: Dict[str, Union[Dict[str, str], str]] = {}
+        self.last_modified_by: Dict[str, Union[Dict[str, str], str]] = {}
+        self.parameters = parameters if parameters is not None else {}
+        self.input_artifacts = input_artifacts if input_artifacts is not None else {}
+        self.output_artifacts = output_artifacts if output_artifacts is not None else {}
+        self.metadata_properties = metadata_properties
+        self.metrics: List[Dict[str, Union[float, datetime, str]]] = []
+        self.sources: List[Dict[str, str]] = []
 
     @property
     def response_object(self) -> Dict[str, Any]:  # type: ignore[misc]
diff --git a/moto/sagemaker/responses.py b/moto/sagemaker/responses.py
index 8569a1651..979033a1f 100644
--- a/moto/sagemaker/responses.py
+++ b/moto/sagemaker/responses.py
@@ -407,6 +407,14 @@ class SageMakerResponse(BaseResponse):
     def create_trial_component(self) -> TYPE_RESPONSE:
         response = self.sagemaker_backend.create_trial_component(
             trial_component_name=self._get_param("TrialComponentName"),
+            start_time=self._get_param("StartTime"),
+            end_time=self._get_param("EndTime"),
+            display_name=self._get_param("DisplayName"),
+            parameters=self._get_param("Parameters"),
+            input_artifacts=self._get_param("InputArtifacts"),
+            output_artifacts=self._get_param("OutputArtifacts"),
+            metadata_properties=self._get_param("MetadataProperties"),
+            status=self._get_param("Status"),
             trial_name=self._get_param("TrialName"),
         )
         return 200, {}, json.dumps(response)
@@ -447,6 +455,22 @@ class SageMakerResponse(BaseResponse):
         )
         return 200, {}, json.dumps(response)
 
+    def update_trial_component(self) -> TYPE_RESPONSE:
+        response = self.sagemaker_backend.update_trial_component(
+            trial_component_name=self._get_param("TrialComponentName"),
+            status=self._get_param("Status"),
+            display_name=self._get_param("DisplayName"),
+            start_time=self._get_param("StartTime"),
+            end_time=self._get_param("EndTime"),
+            parameters=self._get_param("Parameters"),
+            parameters_to_remove=self._get_param("ParametersToRemove"),
+            input_artifacts=self._get_param("InputArtifacts"),
+            input_artifacts_to_remove=self._get_param("InputArtifactsToRemove"),
+            output_artifacts=self._get_param("OutputArtifacts"),
+            output_artifacts_to_remove=self._get_param("OutputArtifactsToRemove"),
+        )
+        return 200, {}, json.dumps(response)
+
     def describe_pipeline(self) -> TYPE_RESPONSE:
         response = self.sagemaker_backend.describe_pipeline(
             self._get_param("PipelineName")
diff --git a/moto/ssm/resources/regions.json b/moto/ssm/resources/regions.json
index 87ccd9ca8..95db69e13 100644
--- a/moto/ssm/resources/regions.json
+++ b/moto/ssm/resources/regions.json
@@ -941,6 +941,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.af-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -2628,6 +2631,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ap-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -4872,6 +4878,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ap-northeast-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -7446,6 +7455,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ap-northeast-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -9381,6 +9393,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ap-northeast-3.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -11415,6 +11430,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ap-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -13153,6 +13171,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -15154,6 +15175,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ap-southeast-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -17848,6 +17872,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ap-southeast-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -19913,6 +19940,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ap-southeast-3.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -23077,6 +23107,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.ca-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -24954,6 +24987,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "storagegateway": {
+                "Value": "storagegateway",
+                "endpoint": {
+                  "Value": "storagegateway.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sts": {
                 "Value": "sts",
                 "endpoint": {
@@ -25925,6 +25967,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.cn-north-1.amazonaws.com.cn"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -27297,6 +27342,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.cn-northwest-1.amazonaws.com.cn"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -29346,6 +29394,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.eu-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -32995,6 +33046,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.eu-north-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -34883,6 +34937,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.eu-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -38329,6 +38386,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.eu-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -41060,6 +41120,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.eu-west-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -43262,6 +43325,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.eu-west-3.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -46288,6 +46354,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.me-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -47846,6 +47915,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.me-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -49653,6 +49725,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.sa-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -52338,6 +52413,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.us-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -55248,6 +55326,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.us-east-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -57235,6 +57316,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.us-gov-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -57597,6 +57681,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ses": {
+                "Value": "ses",
+                "endpoint": {
+                  "Value": "email.us-gov-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "signer": {
                 "Value": "signer",
                 "endpoint": {
@@ -58811,6 +58904,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.us-gov-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -60651,6 +60747,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.us-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
@@ -63267,6 +63366,9 @@
                 "Value": "launch-wizard",
                 "endpoint": {
                   "Value": "launchwizard.us-west-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "launchwizard": {
diff --git a/moto/ssm/resources/services.json b/moto/ssm/resources/services.json
index 86b4d1dd0..47ec67a4f 100644
--- a/moto/ssm/resources/services.json
+++ b/moto/ssm/resources/services.json
@@ -32638,31 +32638,6 @@
               }
             }
           },
-          "iot-roborunner": {
-            "longName": {
-              "Value": "AWS IoT RoboRunner"
-            },
-            "regions": {
-              "eu-central-1": {
-                "Value": "eu-central-1",
-                "endpoint": {
-                  "Value": "iotroborunner.eu-central-1.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
-              "us-east-1": {
-                "Value": "us-east-1",
-                "endpoint": {
-                  "Value": "iotroborunner.us-east-1.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              }
-            }
-          },
           "iot1click-devices": {
             "Value": "iot1click-devices",
             "longName": {
@@ -36786,60 +36761,90 @@
                 "Value": "af-south-1",
                 "endpoint": {
                   "Value": "launchwizard.af-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-east-1": {
                 "Value": "ap-east-1",
                 "endpoint": {
                   "Value": "launchwizard.ap-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-northeast-1": {
                 "Value": "ap-northeast-1",
                 "endpoint": {
                   "Value": "launchwizard.ap-northeast-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-northeast-2": {
                 "Value": "ap-northeast-2",
                 "endpoint": {
                   "Value": "launchwizard.ap-northeast-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-northeast-3": {
                 "Value": "ap-northeast-3",
                 "endpoint": {
                   "Value": "launchwizard.ap-northeast-3.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-south-1": {
                 "Value": "ap-south-1",
                 "endpoint": {
                   "Value": "launchwizard.ap-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-south-2": {
                 "Value": "ap-south-2",
                 "endpoint": {
                   "Value": "launchwizard.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-southeast-1": {
                 "Value": "ap-southeast-1",
                 "endpoint": {
                   "Value": "launchwizard.ap-southeast-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-southeast-2": {
                 "Value": "ap-southeast-2",
                 "endpoint": {
                   "Value": "launchwizard.ap-southeast-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-southeast-3": {
                 "Value": "ap-southeast-3",
                 "endpoint": {
                   "Value": "launchwizard.ap-southeast-3.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "ap-southeast-4": {
@@ -36855,24 +36860,36 @@
                 "Value": "ca-central-1",
                 "endpoint": {
                   "Value": "launchwizard.ca-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "cn-north-1": {
                 "Value": "cn-north-1",
                 "endpoint": {
                   "Value": "launchwizard.cn-north-1.amazonaws.com.cn"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "cn-northwest-1": {
                 "Value": "cn-northwest-1",
                 "endpoint": {
                   "Value": "launchwizard.cn-northwest-1.amazonaws.com.cn"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "eu-central-1": {
                 "Value": "eu-central-1",
                 "endpoint": {
                   "Value": "launchwizard.eu-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "eu-central-2": {
@@ -36888,12 +36905,18 @@
                 "Value": "eu-north-1",
                 "endpoint": {
                   "Value": "launchwizard.eu-north-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "eu-south-1": {
                 "Value": "eu-south-1",
                 "endpoint": {
                   "Value": "launchwizard.eu-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "eu-south-2": {
@@ -36909,72 +36932,108 @@
                 "Value": "eu-west-1",
                 "endpoint": {
                   "Value": "launchwizard.eu-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "eu-west-2": {
                 "Value": "eu-west-2",
                 "endpoint": {
                   "Value": "launchwizard.eu-west-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "eu-west-3": {
                 "Value": "eu-west-3",
                 "endpoint": {
                   "Value": "launchwizard.eu-west-3.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "me-central-1": {
                 "Value": "me-central-1",
                 "endpoint": {
                   "Value": "launchwizard.me-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "me-south-1": {
                 "Value": "me-south-1",
                 "endpoint": {
                   "Value": "launchwizard.me-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "sa-east-1": {
                 "Value": "sa-east-1",
                 "endpoint": {
                   "Value": "launchwizard.sa-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "us-east-1": {
                 "Value": "us-east-1",
                 "endpoint": {
                   "Value": "launchwizard.us-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "us-east-2": {
                 "Value": "us-east-2",
                 "endpoint": {
                   "Value": "launchwizard.us-east-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "us-gov-east-1": {
                 "Value": "us-gov-east-1",
                 "endpoint": {
                   "Value": "launchwizard.us-gov-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "us-gov-west-1": {
                 "Value": "us-gov-west-1",
                 "endpoint": {
                   "Value": "launchwizard.us-gov-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "us-west-1": {
                 "Value": "us-west-1",
                 "endpoint": {
                   "Value": "launchwizard.us-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               },
               "us-west-2": {
                 "Value": "us-west-2",
                 "endpoint": {
                   "Value": "launchwizard.us-west-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
                 }
               }
             }
@@ -58055,6 +58114,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "us-gov-east-1": {
+                "Value": "us-gov-east-1",
+                "endpoint": {
+                  "Value": "email.us-gov-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "us-gov-west-1": {
                 "Value": "us-gov-west-1",
                 "endpoint": {
@@ -61343,6 +61411,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ca-west-1": {
+                "Value": "ca-west-1",
+                "endpoint": {
+                  "Value": "storagegateway.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "cn-north-1": {
                 "Value": "cn-north-1",
                 "endpoint": {
diff --git a/scripts/ci_wait_for_server.py b/scripts/ci_wait_for_server.py
index 471afbda6..c5b229e7e 100755
--- a/scripts/ci_wait_for_server.py
+++ b/scripts/ci_wait_for_server.py
@@ -14,7 +14,6 @@ expected_host = "http://localhost:{}/".format(expected_port)
 print("Waiting for service to come up on {}".format(expected_host))
 while True:
     try:
-
         urllib.urlopen(expected_host, timeout=1)
         break
     except EXCEPTIONS:
diff --git a/scripts/cloudformation_coverage.py b/scripts/cloudformation_coverage.py
index 89a4ae005..b9ae0371c 100755
--- a/scripts/cloudformation_coverage.py
+++ b/scripts/cloudformation_coverage.py
@@ -121,7 +121,9 @@ def write_main_document(supported):
         file.write("## Supported CloudFormation resources")
         file.write("\n\n")
         file.write("A list of all resources that can be created via CloudFormation. \n")
-        file.write("Please let us know if you'd like support for a resource not yet listed here.")
+        file.write(
+            "Please let us know if you'd like support for a resource not yet listed here."
+        )
         file.write("\n\n")
 
         for checklist in supported:
@@ -145,19 +147,30 @@ def write_documentation(supported):
         file.write("==================================\n")
         file.write("\n\n")
         file.write("A list of all resources that can be created via CloudFormation. \n")
-        file.write("Please let us know if you'd like support for a resource not yet listed here.")
+        file.write(
+            "Please let us know if you'd like support for a resource not yet listed here."
+        )
         file.write("\n\n")
 
         max_resource_name_length = max([len(cf.resource_name) for cf in supported]) + 2
         max_fn_att_length = 40
 
         file.write(".. table:: \n\n")
-        file.write(f"  +{('-'*max_resource_name_length)}+--------+--------+--------+{('-' * max_fn_att_length)}+\n")
-        file.write(f"  |{(' '*max_resource_name_length)}| Create | Update | Delete | {('Fn::GetAtt'.ljust(max_fn_att_length-2))} |\n")
-        file.write(f"  +{('='*max_resource_name_length)}+========+========+========+{('=' * max_fn_att_length)}+\n")
+        file.write(
+            f"  +{('-'*max_resource_name_length)}+--------+--------+--------+{('-' * max_fn_att_length)}+\n"
+        )
+        file.write(
+            f"  |{(' '*max_resource_name_length)}| Create | Update | Delete | {('Fn::GetAtt'.ljust(max_fn_att_length-2))} |\n"
+        )
+        file.write(
+            f"  +{('='*max_resource_name_length)}+========+========+========+{('=' * max_fn_att_length)}+\n"
+        )
 
         for checklist in supported:
-            attrs = [f" - [{check(att not in checklist.missing_attrs)}] {att}" for att in checklist.expected_attrs]
+            attrs = [
+                f" - [{check(att not in checklist.missing_attrs)}] {att}"
+                for att in checklist.expected_attrs
+            ]
             first_attr = attrs[0] if attrs else ""
             file.write("  |")
             file.write(checklist.resource_name.ljust(max_resource_name_length))
@@ -172,13 +185,19 @@ def write_documentation(supported):
             for index, attr in enumerate(attrs[1:]):
                 if index % 2 == 0:
                     file.write(
-                        f"  +{('-' * max_resource_name_length)}+--------+--------+--------+{attr.ljust(max_fn_att_length)}|\n")
+                        f"  +{('-' * max_resource_name_length)}+--------+--------+--------+{attr.ljust(max_fn_att_length)}|\n"
+                    )
                 else:
                     file.write(
-                        f"  |{(' ' * max_resource_name_length)}|        |        |        |{attr.ljust(max_fn_att_length)}|\n")
+                        f"  |{(' ' * max_resource_name_length)}|        |        |        |{attr.ljust(max_fn_att_length)}|\n"
+                    )
             if len(attrs) > 1 and len(attrs) % 2 == 0:
-                file.write(f"  |{(' ' * max_resource_name_length)}|        |        |        |{(' ' * max_fn_att_length)}|\n")
-            file.write(f"  +{('-'*max_resource_name_length)}+--------+--------+--------+{('-' * max_fn_att_length)}+\n")
+                file.write(
+                    f"  |{(' ' * max_resource_name_length)}|        |        |        |{(' ' * max_fn_att_length)}|\n"
+                )
+            file.write(
+                f"  +{('-'*max_resource_name_length)}+--------+--------+--------+{('-' * max_fn_att_length)}+\n"
+            )
 
 
 if __name__ == "__main__":
@@ -189,6 +208,10 @@ if __name__ == "__main__":
     # Only collect checklists for models that implement CloudFormationModel;
     # otherwise the checklist is very long and mostly empty because there
     # are so many niche AWS services and resources that moto doesn't implement yet.
-    supported = [CloudFormationChecklist(resource_name, schema) for resource_name, schema in sorted(cfn_spec["ResourceTypes"].items()) if CloudFormationChecklist(resource_name, schema).moto_model]
-    #write_main_document(supported)
+    supported = [
+        CloudFormationChecklist(resource_name, schema)
+        for resource_name, schema in sorted(cfn_spec["ResourceTypes"].items())
+        if CloudFormationChecklist(resource_name, schema).moto_model
+    ]
+    # write_main_document(supported)
     write_documentation(supported)
diff --git a/scripts/ec2_get_instance_type_offerings.py b/scripts/ec2_get_instance_type_offerings.py
index 26af2828d..3a173b2f8 100755
--- a/scripts/ec2_get_instance_type_offerings.py
+++ b/scripts/ec2_get_instance_type_offerings.py
@@ -54,8 +54,12 @@ def main():
                     instances.extend(offerings["InstanceTypeOfferings"])
                     next_token = offerings.get("NextToken", None)
                 for i in instances:
-                    del i["LocationType"]  # This can be reproduced, no need to persist it
-                instances = sorted(instances, key=lambda i: (i['Location'], i["InstanceType"]))
+                    del i[
+                        "LocationType"
+                    ]  # This can be reproduced, no need to persist it
+                instances = sorted(
+                    instances, key=lambda i: (i["Location"], i["InstanceType"])
+                )
 
                 # Ensure we use the correct US-west availability zones
                 # There are only two - for some accounts they are called us-west-1b and us-west-1c
diff --git a/scripts/pull_down_aws_managed_rules.py b/scripts/pull_down_aws_managed_rules.py
index 3f92014d6..b37a8f2f7 100755
--- a/scripts/pull_down_aws_managed_rules.py
+++ b/scripts/pull_down_aws_managed_rules.py
@@ -41,6 +41,7 @@ Summary:
         ]
     }
 """
+
 import argparse
 
 import json
diff --git a/scripts/rds_get_orderable_db_cluster_options.py b/scripts/rds_get_orderable_db_cluster_options.py
index dd63e7bf7..17749feee 100755
--- a/scripts/rds_get_orderable_db_cluster_options.py
+++ b/scripts/rds_get_orderable_db_cluster_options.py
@@ -23,9 +23,7 @@ def main():
     rds = boto3.client("rds", region_name="us-east-1")
     for engine in engines:
         print(f"Engine {engine}...")
-        dest = os.path.join(
-            root_dir, "{0}/{1}.json".format(PATH, engine)
-        )
+        dest = os.path.join(root_dir, "{0}/{1}.json".format(PATH, engine))
         try:
             options = []
             response = rds.describe_orderable_db_instance_options(Engine=engine)
diff --git a/scripts/scaffold.py b/scripts/scaffold.py
index 1f1edabf6..f0f2b43dd 100755
--- a/scripts/scaffold.py
+++ b/scripts/scaffold.py
@@ -19,6 +19,7 @@ TODO:
   - Some services's operations might cause this script to crash. If that
     should happen, please create an issue for the problem.
 """
+
 import os
 import random
 import re
@@ -41,14 +42,24 @@ from moto.core.base_backend import BaseBackend
 from inflection import singularize
 from implementation_coverage import get_moto_implementation
 
-PRIMITIVE_SHAPES = ["string", "timestamp", "integer", "boolean", "sensitiveStringType", "long"]
+PRIMITIVE_SHAPES = [
+    "string",
+    "timestamp",
+    "integer",
+    "boolean",
+    "sensitiveStringType",
+    "long",
+]
 
 TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "./template")
 
 INPUT_IGNORED_IN_BACKEND = ["Marker", "PageSize"]
 OUTPUT_IGNORED_IN_BACKEND = ["NextMarker"]
 
-root_dir = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode().strip()
+root_dir = (
+    subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode().strip()
+)
+
 
 def print_progress(title, body, color):
     """Prints a color-code message describing current state of progress."""
@@ -217,9 +228,7 @@ def get_operation_name_in_keys(operation_name, operation_keys):
     return operation_keys[index]
 
 
-def get_function_in_responses(
-    service, operation, protocol
-):  # pylint: disable=too-many-locals
+def get_function_in_responses(service, operation, protocol):  # pylint: disable=too-many-locals
     """refers to definition of API in botocore, and autogenerates function
     You can see example of elbv2 from link below.
       https://github.com/boto/botocore/blob/develop/botocore/data/elbv2/2015-12-01/service-2.json
@@ -255,7 +264,7 @@ def get_function_in_responses(
     body += "    params = self._get_params()\n"
 
     for input_name, input_type in inputs.items():
-        body += f"    {to_snake_case(input_name)} = params.get(\"{input_name}\")\n"
+        body += f'    {to_snake_case(input_name)} = params.get("{input_name}")\n'
     if output_names:
         body += f"    {', '.join(output_names)} = self.{escaped_service}_backend.{operation}(\n"
     else:
@@ -319,10 +328,10 @@ def get_func_in_tests(service, operation):
     body = "\n\n"
     body += f"@mock_aws\n"
     body += f"def test_{operation}():\n"
-    body += f"    client = boto3.client(\"{service}\", region_name=\"{random_region}\")\n"
+    body += f'    client = boto3.client("{service}", region_name="{random_region}")\n'
     body += f"    resp = client.{operation}()\n"
     body += f"\n"
-    body += f"    raise Exception(\"NotYetImplemented\")"
+    body += f'    raise Exception("NotYetImplemented")'
     body += "\n"
     return body
 
@@ -431,7 +440,7 @@ def get_response_query_template(service, operation):  # pylint: disable=too-many
 
 def get_response_restxml_template(service, operation):
     """refers to definition of API in botocore, and autogenerates template
-        Assume that protocol is rest-xml. Shares some familiarity with protocol=query
+    Assume that protocol is rest-xml. Shares some familiarity with protocol=query
     """
     client = boto3.client(service)
     aws_operation_name = get_operation_name_in_keys(
@@ -477,11 +486,15 @@ def get_response_restxml_template(service, operation):
                 child = details.get("locationName", child)
                 if details["shape"] in PRIMITIVE_SHAPES:
                     t_member = etree.Element(child)
-                    t_member.text = "{{ " + ".".join(name_prefix) + f".{to_snake_case(child)} }}}}"
+                    t_member.text = (
+                        "{{ " + ".".join(name_prefix) + f".{to_snake_case(child)} }}}}"
+                    )
                     tree.append(t_member)
                 else:
                     t = etree.Element(child)
-                    _find_member(t, details["shape"], name_prefix + [to_snake_case(child)])
+                    _find_member(
+                        t, details["shape"], name_prefix + [to_snake_case(child)]
+                    )
                     tree.append(t)
 
     # build result
@@ -497,13 +510,23 @@ def get_response_restxml_template(service, operation):
             _find_member(t_result, name, name_prefix=["root"])
     t_root.append(t_result)
     xml_body = etree.tostring(t_root, pretty_print=True).decode("utf-8")
+
     #
     # Still need to add FOR-loops in this template
     #     <REPLACE_FOR for="x" in="y">
     # becomes
     #     {% for x in y %}
     def conv(m):
-        return m.group().replace("REPLACE_FOR", "").replace("=", "").replace('"', " ").replace("<", "{%").replace(">", "%}").strip()
+        return (
+            m.group()
+            .replace("REPLACE_FOR", "")
+            .replace("=", "")
+            .replace('"', " ")
+            .replace("<", "{%")
+            .replace(">", "%}")
+            .strip()
+        )
+
     xml_body = re.sub(r"<REPLACE_FOR[\sa-zA-Z\"=_.]+>", conv, xml_body)
     xml_body = xml_body.replace("</REPLACE_FOR>", "{% endfor %}")
     body = f'\n{operation.upper()}_TEMPLATE = """{xml_body}"""'
@@ -616,7 +639,7 @@ def insert_codes(service, operation, api_protocol):
     insert_url(service, operation, api_protocol)
 
     # Edit tests
-    tests_path= f"tests/test_{escaped_service}/test_{escaped_service}.py"
+    tests_path = f"tests/test_{escaped_service}/test_{escaped_service}.py"
     print_progress("inserting code", tests_path, "green")
     with open(tests_path, "a", encoding="utf-8") as fhandle:
         fhandle.write(func_in_tests)
@@ -624,12 +647,13 @@ def insert_codes(service, operation, api_protocol):
 
 @click.command()
 def main():
-
     click.echo("This script uses the click-module.\n")
-    click.echo(" - Start typing the name of the service you want to extend\n"
-               " - Use Tab to auto-complete the first suggest service\n"
-               " - Use the up and down-arrows on the keyboard to select something from the dropdown\n"
-               " - Press enter to continue\n")
+    click.echo(
+        " - Start typing the name of the service you want to extend\n"
+        " - Use Tab to auto-complete the first suggest service\n"
+        " - Use the up and down-arrows on the keyboard to select something from the dropdown\n"
+        " - Press enter to continue\n"
+    )
 
     """Create basic files needed for the user's choice of service and op."""
     service = select_service()
@@ -652,14 +676,16 @@ def main():
             )
 
         click.echo("Updating backend index...")
-        subprocess.check_output([f"{root_dir}/scripts/update_backend_index.py"]).decode().strip()
+        subprocess.check_output(
+            [f"{root_dir}/scripts/update_backend_index.py"]
+        ).decode().strip()
 
         click.echo(
             "\n"
             "Please select another operation, or Ctrl-X/Ctrl-C to cancel."
             "\n\n"
             "Remaining steps after development is complete:\n"
-            '- Run scripts/implementation_coverage.py,\n'
+            "- Run scripts/implementation_coverage.py,\n"
             "\n"
         )
 
diff --git a/scripts/ssm_get_default_amis.py b/scripts/ssm_get_default_amis.py
index ccd1bea5f..6c0e60ba3 100644
--- a/scripts/ssm_get_default_amis.py
+++ b/scripts/ssm_get_default_amis.py
@@ -15,7 +15,9 @@ def retrieve_by_path(client, path):
     parameters = x["Parameters"]
     next_token = x["NextToken"]
     while next_token:
-        x = client.get_parameters_by_path(Path=path, Recursive=True, NextToken=next_token)
+        x = client.get_parameters_by_path(
+            Path=path, Recursive=True, NextToken=next_token
+        )
         parameters.extend(x["Parameters"])
         next_token = x.get("NextToken")
 
@@ -32,7 +34,7 @@ def main():
     regions = boto3.Session().get_available_regions("ssm")
 
     for region in regions:
-        client = boto3.client('ssm', region_name=region)
+        client = boto3.client("ssm", region_name=region)
         ec2 = boto3.client("ec2", region_name=region)
 
         default_param_path = "/aws/service/ami-amazon-linux-latest"
@@ -41,14 +43,20 @@ def main():
         try:
             params = retrieve_by_path(client, default_param_path)
             for param in params:
-                param["LastModifiedDate"] = unix_time(param["LastModifiedDate"].astimezone(timezone.utc).replace(tzinfo=None))
+                param["LastModifiedDate"] = unix_time(
+                    param["LastModifiedDate"]
+                    .astimezone(timezone.utc)
+                    .replace(tzinfo=None)
+                )
 
             root_dir = (
                 subprocess.check_output(["git", "rev-parse", "--show-toplevel"])
-                    .decode()
-                    .strip()
+                .decode()
+                .strip()
+            )
+            dest = os.path.join(
+                root_dir, f"moto/ssm/resources/ami-amazon-linux-latest/{region}.json"
             )
-            dest = os.path.join(root_dir, f"moto/ssm/resources/ami-amazon-linux-latest/{region}.json")
             print("Writing data to {0}".format(dest))
             with open(dest, "w") as open_file:
                 json.dump(params, open_file, sort_keys=True, indent=2)
@@ -57,7 +65,9 @@ def main():
             image_ids = [p["Value"] for p in params]
             images = ec2.describe_images(ImageIds=image_ids)["Images"]
             image_as_dicts = gen_moto_amis(images)
-            dest = os.path.join(root_dir, f"moto/ec2/resources/latest_amis/{region}.json")
+            dest = os.path.join(
+                root_dir, f"moto/ec2/resources/latest_amis/{region}.json"
+            )
             print("Writing data to {0}".format(dest))
             with open(dest, "w") as open_file:
                 json.dump(image_as_dicts, open_file, sort_keys=True, indent=2)
diff --git a/scripts/ssm_get_default_params.py b/scripts/ssm_get_default_params.py
index b995bece4..a3e80a5b2 100755
--- a/scripts/ssm_get_default_params.py
+++ b/scripts/ssm_get_default_params.py
@@ -8,13 +8,17 @@ from moto.ssm.utils import convert_to_tree
 
 
 def retrieve_by_path(client, path):
-    print(f"Retrieving all parameters from {path}. "
-          f"AWS has around 14000 parameters, and we can only retrieve 10 at the time, so this may take a while.\n\n")
+    print(
+        f"Retrieving all parameters from {path}. "
+        f"AWS has around 14000 parameters, and we can only retrieve 10 at the time, so this may take a while.\n\n"
+    )
     x = client.get_parameters_by_path(Path=path, Recursive=True)
     parameters = x["Parameters"]
     next_token = x["NextToken"]
     while next_token:
-        x = client.get_parameters_by_path(Path=path, Recursive=True, NextToken=next_token)
+        x = client.get_parameters_by_path(
+            Path=path, Recursive=True, NextToken=next_token
+        )
         parameters.extend(x["Parameters"])
         next_token = x.get("NextToken")
         if len(parameters) % 100 == 0:
@@ -36,18 +40,20 @@ def main():
          So running this scripts takes a while.
     """
 
-    client = boto3.client('ssm', region_name="us-west-1")
+    client = boto3.client("ssm", region_name="us-west-1")
 
-    default_param_paths = ["/aws/service/global-infrastructure/regions",
-                           "/aws/service/global-infrastructure/services"]
+    default_param_paths = [
+        "/aws/service/global-infrastructure/regions",
+        "/aws/service/global-infrastructure/services",
+    ]
 
     for path in default_param_paths:
         params = retrieve_by_path(client, path)
         tree = convert_to_tree(params)
         root_dir = (
             subprocess.check_output(["git", "rev-parse", "--show-toplevel"])
-                .decode()
-                .strip()
+            .decode()
+            .strip()
         )
         filename = "{}.json".format(path.split("/")[-1])
         dest = os.path.join(root_dir, "moto/ssm/resources/{}".format(filename))
diff --git a/scripts/ssm_get_optimized_amis.py b/scripts/ssm_get_optimized_amis.py
index ce7fc1ece..97d0a6524 100644
--- a/scripts/ssm_get_optimized_amis.py
+++ b/scripts/ssm_get_optimized_amis.py
@@ -19,12 +19,15 @@ def retrieve_by_path(client, path):
     parameters = response["Parameters"]
     next_token = response["NextToken"]
     while next_token:
-        response = client.get_parameters_by_path(Path=path, Recursive=True, NextToken=next_token)
+        response = client.get_parameters_by_path(
+            Path=path, Recursive=True, NextToken=next_token
+        )
         parameters.extend(response["Parameters"])
         next_token = response.get("NextToken")
 
     return parameters
 
+
 def save_to_file(destination_path: str, params: dict):
     print("Attempting to save data to {}", destination_path)
     file_path = Path(Path.cwd(), destination_path)
@@ -49,13 +52,13 @@ def main():
      - Store this in the dedicated moto/ssm/resources-folder
     """
     for region in session.get_available_regions("ssm"):
-        ssm_client = session.client('ssm', region_name=region)
+        ssm_client = session.client("ssm", region_name=region)
 
         default_param_path = "/aws/service/ecs/optimized-ami"
 
         # Retrieve default AMI values
         try:
-            print("Retrieving data for {}" , region)
+            print("Retrieving data for {}", region)
 
             parameters = retrieve_by_path(ssm_client, default_param_path)
 
@@ -65,7 +68,11 @@ def main():
             image_ids = []
 
             for param in parameters:
-                param["LastModifiedDate"] = unix_time(param["LastModifiedDate"].astimezone(timezone.utc).replace(tzinfo=None))
+                param["LastModifiedDate"] = unix_time(
+                    param["LastModifiedDate"]
+                    .astimezone(timezone.utc)
+                    .replace(tzinfo=None)
+                )
 
                 if isinstance(param["Value"], str) and param["Value"][0] == "{":
                     param["Value"] = json.loads(param["Value"])
diff --git a/scripts/update_ssl_policies.py b/scripts/update_ssl_policies.py
index fcf447325..17b2f20cc 100755
--- a/scripts/update_ssl_policies.py
+++ b/scripts/update_ssl_policies.py
@@ -8,6 +8,7 @@ CAMEL_CASE_PATTERN = re.compile(r"(?<!^)(?=[A-Z])")
 
 KEY_BLACKLIST = ["SupportedLoadBalancerTypes"]
 
+
 def camel_case_to_snake_case(name: str):
     return CAMEL_CASE_PATTERN.sub("_", name).lower()
 
diff --git a/update_version_from_git.py b/update_version_from_git.py
index 94e9a498d..a6f644651 100644
--- a/update_version_from_git.py
+++ b/update_version_from_git.py
@@ -59,7 +59,7 @@ def migrate_version(new_version):
     )
     migrate_source_attribute(
         "version =",
-        to_this=f'version = {new_version}\n',
+        to_this=f"version = {new_version}\n",
         target_file=setupcfg,
     )
 


Test patch:
diff --git a/.github/workflows/tests_sdk_ruby.yml b/.github/workflows/tests_sdk_ruby.yml
index 61098b4e8..4faad6f66 100644
--- a/.github/workflows/tests_sdk_ruby.yml
+++ b/.github/workflows/tests_sdk_ruby.yml
@@ -8,7 +8,7 @@ jobs:
 
     strategy:
       matrix:
-        ruby-version: ['3.2']
+        ruby-version: ['3.3']
 
     steps:
       - uses: actions/checkout@v4
@@ -19,7 +19,7 @@ jobs:
       - name: Set up Python 3.8
         uses: actions/setup-python@v5
         with:
-          python-version: "3.8"
+          python-version: "3.10"
       - name: Start MotoServer
         run: |
           pip install build
diff --git a/other_langs/tests_ruby/Gemfile b/other_langs/tests_ruby/Gemfile
index e26f68af1..78e3b2ea6 100644
--- a/other_langs/tests_ruby/Gemfile
+++ b/other_langs/tests_ruby/Gemfile
@@ -1,4 +1,4 @@
 source "https://rubygems.org"
 
-gem 'aws-sdk-sqs', '1.65.0'
+gem 'aws-sdk-sqs', '1.70.0'
 gem "minitest"
\ No newline at end of file
diff --git a/tests/test_athena/test_athena.py b/tests/test_athena/test_athena.py
index 0b6dad04a..cd5e8f2de 100644
--- a/tests/test_athena/test_athena.py
+++ b/tests/test_athena/test_athena.py
@@ -107,6 +107,19 @@ def test_start_query_execution():
     assert response["QueryExecutionId"] != sec_response["QueryExecutionId"]
 
 
+@mock_aws
+def test_start_query_execution_without_result_configuration():
+    client = boto3.client("athena", region_name="us-east-1")
+
+    create_basic_workgroup(client=client, name="athena_workgroup")
+    response = client.start_query_execution(
+        QueryString="query1",
+        QueryExecutionContext={"Database": "string"},
+        WorkGroup="athena_workgroup",
+    )
+    assert "QueryExecutionId" in response
+
+
 @mock_aws
 def test_start_query_validate_workgroup():
     client = boto3.client("athena", region_name="us-east-1")
diff --git a/tests/test_cloudfront/test_cloudfront_distributions.py b/tests/test_cloudfront/test_cloudfront_distributions.py
index 67ec47fab..2ac78bc20 100644
--- a/tests/test_cloudfront/test_cloudfront_distributions.py
+++ b/tests/test_cloudfront/test_cloudfront_distributions.py
@@ -681,3 +681,88 @@ def test_get_distribution_config():
     }
 
     assert config["WebACLId"] == ""
+
+
+@mock_aws
+def test_add_new_gateway_basic():
+    # Setup
+    cloudfront_client_mock = boto3.client("cloudfront", region_name="us-east-1")
+    config = scaffold.example_distribution_config("ref")
+    distribution_id = cloudfront_client_mock.create_distribution(
+        DistributionConfig=config
+    )["Distribution"]["Id"]
+    existing_config = cloudfront_client_mock.get_distribution(Id=distribution_id)[
+        "Distribution"
+    ]["DistributionConfig"]
+    new_behaviors = [
+        {
+            "PathPattern": "bla/*",
+            "TargetOriginId": "test.invalid",
+            "ViewerProtocolPolicy": "redirect-to-https",
+            "AllowedMethods": {
+                "Quantity": 7,
+                "Items": ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"],
+                "CachedMethods": {"Quantity": 2, "Items": ["GET", "HEAD"]},
+            },
+            "SmoothStreaming": False,
+            "Compress": False,
+            "LambdaFunctionAssociations": {"Quantity": 0},
+            "FieldLevelEncryptionId": "",
+            "CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",  # CachingDisabled
+            "OriginRequestPolicyId": "b689b0a8-53d0-40ab-baf2-68738e2966ac",  # AllViewerExceptHostHeader
+        }
+    ]
+
+    new_config = existing_config
+    new_config["CacheBehaviors"]["Quantity"] = 1
+    new_config["CacheBehaviors"]["Items"] = new_behaviors
+
+    # Execute
+    cloudfront_client_mock.update_distribution(
+        DistributionConfig=new_config, Id=distribution_id, IfMatch="1"
+    )
+    result = cloudfront_client_mock.get_distribution(Id=distribution_id)[
+        "Distribution"
+    ]["DistributionConfig"]
+
+    # Verify
+    assert result["CacheBehaviors"]["Quantity"] == 1
+    assert (
+        result["CacheBehaviors"]["Items"][0]["PathPattern"]
+        == new_behaviors[0]["PathPattern"]
+    )
+    assert (
+        result["CacheBehaviors"]["Items"][0]["TargetOriginId"]
+        == new_behaviors[0]["TargetOriginId"]
+    )
+    assert (
+        result["CacheBehaviors"]["Items"][0]["ViewerProtocolPolicy"]
+        == new_behaviors[0]["ViewerProtocolPolicy"]
+    )
+    assert (
+        result["CacheBehaviors"]["Items"][0]["AllowedMethods"]
+        == new_behaviors[0]["AllowedMethods"]
+    )
+    assert (
+        result["CacheBehaviors"]["Items"][0]["SmoothStreaming"]
+        == new_behaviors[0]["SmoothStreaming"]
+    )
+    assert (
+        result["CacheBehaviors"]["Items"][0]["Compress"] == new_behaviors[0]["Compress"]
+    )
+    assert (
+        result["CacheBehaviors"]["Items"][0]["LambdaFunctionAssociations"]["Quantity"]
+        == new_behaviors[0]["LambdaFunctionAssociations"]["Quantity"]
+    )
+    assert (
+        result["CacheBehaviors"]["Items"][0]["FieldLevelEncryptionId"]
+        == new_behaviors[0]["FieldLevelEncryptionId"]
+    )
+    assert (
+        result["CacheBehaviors"]["Items"][0]["CachePolicyId"]
+        == new_behaviors[0]["CachePolicyId"]
+    )
+    assert (
+        result["CacheBehaviors"]["Items"][0]["OriginRequestPolicyId"]
+        == new_behaviors[0]["OriginRequestPolicyId"]
+    )
diff --git a/tests/test_ec2/test_elastic_ip_addresses.py b/tests/test_ec2/test_elastic_ip_addresses.py
index c43884291..c891173af 100644
--- a/tests/test_ec2/test_elastic_ip_addresses.py
+++ b/tests/test_ec2/test_elastic_ip_addresses.py
@@ -708,3 +708,16 @@ def test_describe_addresses_with_vpc_associated_eni():
     assert address["PrivateIpAddress"] == eni.private_ip_address
     assert address["AssociationId"] == association_id
     assert address["NetworkInterfaceOwnerId"] == eni.owner_id
+
+
+@mock_aws
+def test_describe_addresses_attributes():
+    client = boto3.client("ec2", region_name="us-east-1")
+    eip = client.allocate_address(Domain="vpc")
+
+    addresses = client.describe_addresses_attribute(
+        AllocationIds=[eip["AllocationId"]], Attribute="domain-name"
+    )["Addresses"]
+    assert addresses == [
+        {"PublicIp": eip["PublicIp"], "AllocationId": eip["AllocationId"]}
+    ]
diff --git a/tests/test_rekognition/test_rekognition.py b/tests/test_rekognition/test_rekognition.py
index 94239fcd5..6df5cc999 100644
--- a/tests/test_rekognition/test_rekognition.py
+++ b/tests/test_rekognition/test_rekognition.py
@@ -6,6 +6,7 @@ import string
 import boto3
 
 from moto import mock_aws
+from moto.moto_api._internal import mock_random
 
 # See our Development Tips on writing tests for hints on how to write good tests:
 # http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
@@ -116,3 +117,16 @@ def test_get_text_detection():
 
     assert resp["JobStatus"] == "SUCCEEDED"
     assert resp["TextDetections"][0]["TextDetection"]["DetectedText"] == "Hello world"
+
+
+@mock_aws
+def test_detect_custom_labels():
+    client = boto3.client("rekognition", region_name="us-east-2")
+    resp = client.detect_custom_labels(
+        Image={"S3Object": {"Bucket": "string", "Name": "name.jpg"}},
+        MaxResults=10,
+        MinConfidence=80,
+        ProjectVersionArn=mock_random.get_random_string(),
+    )
+    assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+    assert resp["CustomLabels"][0]["Name"] == "MyLogo"
diff --git a/tests/test_sagemaker/test_sagemaker_trial_component.py b/tests/test_sagemaker/test_sagemaker_trial_component.py
index 95cfdd7c7..fe1ad67cf 100644
--- a/tests/test_sagemaker/test_sagemaker_trial_component.py
+++ b/tests/test_sagemaker/test_sagemaker_trial_component.py
@@ -1,3 +1,4 @@
+import datetime
 import uuid
 
 import boto3
@@ -11,7 +12,7 @@ TEST_REGION_NAME = "us-east-1"
 
 
 @mock_aws
-def test_create__trial_component():
+def test_create_trial_component():
     client = boto3.client("sagemaker", region_name=TEST_REGION_NAME)
 
     trial_component_name = "some-trial-component-name"
@@ -20,16 +21,24 @@ def test_create__trial_component():
 
     assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
 
-    resp = client.list_trial_components()
+    resp = client.describe_trial_component(TrialComponentName=trial_component_name)
 
-    assert len(resp["TrialComponentSummaries"]) == 1
-    assert (
-        resp["TrialComponentSummaries"][0]["TrialComponentName"] == trial_component_name
-    )
-    assert resp["TrialComponentSummaries"][0]["TrialComponentArn"] == (
+    assert resp["TrialComponentName"] == trial_component_name
+    assert resp["TrialComponentArn"] == (
         f"arn:aws:sagemaker:{TEST_REGION_NAME}:{ACCOUNT_ID}"
         f":experiment-trial-component/{trial_component_name}"
     )
+    assert resp["DisplayName"] == trial_component_name
+    assert resp.get("Status") is None
+    assert resp.get("StartTime") is None
+    assert resp.get("EndTime") is None
+    assert resp.get("LastModifiedBy") == {}
+    assert resp.get("CreatedBy") == {}
+    assert resp.get("Parameters") == {}
+    assert resp.get("InputArtifacts") == {}
+    assert resp.get("OutputArtifacts") == {}
+    assert resp.get("Metrics") == []
+    assert resp.get("Sources") == []
 
 
 @mock_aws
@@ -263,3 +272,110 @@ def test_disassociate_trial_component():
         resp["TrialArn"]
         == f"arn:aws:sagemaker:{TEST_REGION_NAME}:{ACCOUNT_ID}:experiment-trial/does-not-exist"
     )
+
+
+@mock_aws
+def test_update_trial_component() -> None:
+    client = boto3.client("sagemaker", region_name=TEST_REGION_NAME)
+
+    trial_component_name = "some-trial-component-name"
+    client.create_trial_component(
+        TrialComponentName=trial_component_name,
+    )
+    given_status = {"PrimaryStatus": "InProgress", "Message": "a-message"}
+    given_display_name = "new-display-name"
+    given_parameters = {
+        "param1": {"StringValue": "param1-value", "NumberValue": 123.0},
+        "param2": {"StringValue": "param2-value", "NumberValue": 456.0},
+    }
+    given_input_artifacts = {
+        "artifact1": {"MediaType": "text/plain", "Value": "artifact1-value"}
+    }
+    given_output_artifacts = {
+        "artifact2": {"MediaType": "text/plain", "Value": "artifact2-value"}
+    }
+    given_start_date = datetime.datetime(2021, 1, 1, tzinfo=datetime.timezone.utc)
+    given_end_date = datetime.datetime(2021, 1, 2, tzinfo=datetime.timezone.utc)
+    resp_created = client.describe_trial_component(
+        TrialComponentName=trial_component_name
+    )
+
+    resp_update = client.update_trial_component(
+        TrialComponentName=trial_component_name,
+        DisplayName=given_display_name,
+        Status=given_status,
+        StartTime=given_start_date,
+        EndTime=given_end_date,
+        Parameters=given_parameters,
+        InputArtifacts=given_input_artifacts,
+        OutputArtifacts=given_output_artifacts,
+    )
+    resp_updated = client.describe_trial_component(
+        TrialComponentName=trial_component_name
+    )
+
+    assert (
+        resp_update["TrialComponentArn"]
+        == f"arn:aws:sagemaker:{TEST_REGION_NAME}:{ACCOUNT_ID}:experiment-trial-component/some-trial-component-name"
+    )
+    assert resp_updated["DisplayName"] == given_display_name
+    assert resp_updated["Status"] == given_status
+    assert resp_updated["StartTime"] == given_start_date
+    assert resp_updated["EndTime"] == given_end_date
+    assert resp_updated["Parameters"] == given_parameters
+    assert resp_updated["InputArtifacts"] == given_input_artifacts
+    assert resp_updated["OutputArtifacts"] == given_output_artifacts
+    assert resp_updated["LastModifiedTime"] >= resp_created["LastModifiedTime"]
+
+
+@mock_aws
+def test_update_trial_component_with_to_remove_options() -> None:
+    client = boto3.client("sagemaker", region_name=TEST_REGION_NAME)
+
+    trial_component_name = "some-trial-component-name"
+    client.create_trial_component(
+        TrialComponentName=trial_component_name,
+        Parameters={
+            "param1": {"StringValue": "param1-value", "NumberValue": 123.0},
+            "param2": {"StringValue": "param2-value", "NumberValue": 456.0},
+        },
+        InputArtifacts={
+            "artifact1": {"MediaType": "text/plain", "Value": "artifact1-value"}
+        },
+        OutputArtifacts={
+            "artifact2": {"MediaType": "text/plain", "Value": "artifact2-value"}
+        },
+    )
+
+    client.update_trial_component(
+        TrialComponentName=trial_component_name,
+        ParametersToRemove=["param1"],
+        InputArtifactsToRemove=["artifact1"],
+        OutputArtifactsToRemove=["artifact2"],
+    )
+    resp_updated = client.describe_trial_component(
+        TrialComponentName=trial_component_name
+    )
+
+    assert resp_updated["Parameters"].keys() == {"param2"}
+    assert resp_updated["InputArtifacts"] == {}
+    assert resp_updated["OutputArtifacts"] == {}
+
+
+@mock_aws
+def test_update_trial_component_should_return_an_error_if_trial_component_not_found() -> (
+    None
+):
+    client = boto3.client("sagemaker", region_name=TEST_REGION_NAME)
+
+    with pytest.raises(ClientError) as ex:
+        client.update_trial_component(
+            TrialComponentName="not-found",
+            DisplayName="new-display-name",
+        )
+
+    assert ex.value.response["Error"]["Code"] == "ValidationException"
+    assert ex.value.response["Error"]["Message"] == (
+        "Could not find trial component 'arn:aws:sagemaker:us-east-1:123456789012:experiment-trial-component/not-found'"
+    )
+    assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400