From 3a430780f7f8ec888ac2390a77dbd5bb9d00ed22 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Sat, 25 Feb 2023 19:24:03 -0100 Subject: [PATCH] Terraform - Update to latest commit (#5982) --- moto/acmpca/models.py | 2 + moto/ce/models.py | 35 +++++++++++++-- moto/ce/responses.py | 4 ++ moto/ecs/models.py | 15 +++++-- moto/ecs/responses.py | 10 ++++- moto/efs/exceptions.py | 2 +- moto/iam/models.py | 4 +- ...h-Hardcode-endpoints-to-local-server.patch | 22 ++++++++-- tests/terraformtests/terraform-provider-aws | 2 +- .../terraform-tests.failures.txt | 10 +++++ .../terraform-tests.success.txt | 44 ++++++++++++++++--- tests/test_ce/test_ce.py | 18 ++++++++ tests/test_ecs/test_ecs_boto3.py | 2 + tests/test_efs/test_file_system.py | 2 + tests/test_efs/test_lifecycle_config.py | 2 +- tests/test_iam/test_iam_groups.py | 4 +- 16 files changed, 152 insertions(+), 26 deletions(-) diff --git a/moto/acmpca/models.py b/moto/acmpca/models.py index bf4394ff3..edf343123 100644 --- a/moto/acmpca/models.py +++ b/moto/acmpca/models.py @@ -39,6 +39,7 @@ class CertificateAuthority(BaseModel): self.created_at = unix_time() self.updated_at: Optional[float] = None self.status = "PENDING_CERTIFICATE" + self.usage_mode = "SHORT_LIVED_CERTIFICATE" common_name = self.certificate_authority_configuration.get("Subject", {}).get( "CommonName", "Moto.org" @@ -168,6 +169,7 @@ class CertificateAuthority(BaseModel): "RevocationConfiguration": self.revocation_configuration, "CreatedAt": self.created_at, "Status": self.status, + "UsageMode": self.usage_mode, } if self.updated_at: dct["LastStateChangeAt"] = self.updated_at diff --git a/moto/ce/models.py b/moto/ce/models.py index 2c2df6441..a4a37e589 100644 --- a/moto/ce/models.py +++ b/moto/ce/models.py @@ -3,8 +3,21 @@ from .exceptions import CostCategoryNotFound from moto.core import BaseBackend, BackendDict, BaseModel from moto.utilities.tagging_service import TaggingService +from moto.core.utils import iso_8601_datetime_without_milliseconds from moto.moto_api._internal import mock_random -from typing import Any, Dict, List, Tuple +from datetime import datetime +from typing import Any, Dict, List, Tuple, Optional + + +def first_day() -> str: + as_date = ( + datetime.today() + .replace(day=1) + .replace(hour=0) + .replace(minute=0) + .replace(second=0) + ) + return iso_8601_datetime_without_milliseconds(as_date) # type: ignore[return-value] class CostCategoryDefinition(BaseModel): @@ -12,6 +25,7 @@ class CostCategoryDefinition(BaseModel): self, account_id: str, name: str, + effective_start: Optional[str], rule_version: str, rules: List[Dict[str, Any]], default_value: str, @@ -23,10 +37,12 @@ class CostCategoryDefinition(BaseModel): self.default_value = default_value self.split_charge_rules = split_charge_rules self.arn = f"arn:aws:ce::{account_id}:costcategory/{str(mock_random.uuid4())}" + self.effective_start: str = effective_start or first_day() def update( self, rule_version: str, + effective_start: Optional[str], rules: List[Dict[str, Any]], default_value: str, split_charge_rules: List[Dict[str, Any]], @@ -35,11 +51,13 @@ class CostCategoryDefinition(BaseModel): self.rules = rules self.default_value = default_value self.split_charge_rules = split_charge_rules + self.effective_start = effective_start or first_day() def to_json(self) -> Dict[str, Any]: return { "CostCategoryArn": self.arn, "Name": self.name, + "EffectiveStart": self.effective_start, "RuleVersion": self.rule_version, "Rules": self.rules, "DefaultValue": self.default_value, @@ -58,6 +76,7 @@ class CostExplorerBackend(BaseBackend): def create_cost_category_definition( self, name: str, + effective_start: Optional[str], rule_version: str, rules: List[Dict[str, Any]], default_value: str, @@ -70,6 +89,7 @@ class CostExplorerBackend(BaseBackend): ccd = CostCategoryDefinition( self.account_id, name, + effective_start, rule_version, rules, default_value, @@ -77,7 +97,7 @@ class CostExplorerBackend(BaseBackend): ) self.cost_categories[ccd.arn] = ccd self.tag_resource(ccd.arn, tags) - return ccd.arn, "" + return ccd.arn, ccd.effective_start def describe_cost_category_definition( self, cost_category_arn: str @@ -102,6 +122,7 @@ class CostExplorerBackend(BaseBackend): def update_cost_category_definition( self, cost_category_arn: str, + effective_start: Optional[str], rule_version: str, rules: List[Dict[str, Any]], default_value: str, @@ -111,9 +132,15 @@ class CostExplorerBackend(BaseBackend): The EffectiveOn-parameter is not yet implemented """ cost_category = self.describe_cost_category_definition(cost_category_arn) - cost_category.update(rule_version, rules, default_value, split_charge_rules) + cost_category.update( + rule_version=rule_version, + rules=rules, + default_value=default_value, + split_charge_rules=split_charge_rules, + effective_start=effective_start, + ) - return cost_category_arn, "" + return cost_category_arn, cost_category.effective_start def list_tags_for_resource(self, resource_arn: str) -> List[Dict[str, str]]: return self.tagger.list_tags_for_resource(arn=resource_arn)["Tags"] diff --git a/moto/ce/responses.py b/moto/ce/responses.py index 778837acb..bc9ae2334 100644 --- a/moto/ce/responses.py +++ b/moto/ce/responses.py @@ -20,12 +20,14 @@ class CostExplorerResponse(BaseResponse): rules = params.get("Rules") default_value = params.get("DefaultValue") split_charge_rules = params.get("SplitChargeRules") + effective_start = params.get("EffectiveStart") tags = params.get("ResourceTags") ( cost_category_arn, effective_start, ) = self.ce_backend.create_cost_category_definition( name=name, + effective_start=effective_start, rule_version=rule_version, rules=rules, default_value=default_value, @@ -60,6 +62,7 @@ class CostExplorerResponse(BaseResponse): def update_cost_category_definition(self) -> str: params = json.loads(self.body) cost_category_arn = params.get("CostCategoryArn") + effective_start = params.get("EffectiveStart") rule_version = params.get("RuleVersion") rules = params.get("Rules") default_value = params.get("DefaultValue") @@ -69,6 +72,7 @@ class CostExplorerResponse(BaseResponse): effective_start, ) = self.ce_backend.update_cost_category_definition( cost_category_arn=cost_category_arn, + effective_start=effective_start, rule_version=rule_version, rules=rules, default_value=default_value, diff --git a/moto/ecs/models.py b/moto/ecs/models.py index 1ad967e36..5e4e7d9d2 100644 --- a/moto/ecs/models.py +++ b/moto/ecs/models.py @@ -65,6 +65,7 @@ class Cluster(BaseObject, CloudFormationModel): capacity_providers: Optional[List[str]] = None, default_capacity_provider_strategy: Optional[List[Dict[str, Any]]] = None, tags: Optional[List[Dict[str, str]]] = None, + service_connect_defaults: Optional[Dict[str, str]] = None, ): self.active_services_count = 0 self.arn = f"arn:aws:ecs:{region_name}:{account_id}:cluster/{cluster_name}" @@ -74,11 +75,14 @@ class Cluster(BaseObject, CloudFormationModel): self.running_tasks_count = 0 self.status = "ACTIVE" self.region_name = region_name - self.settings = cluster_settings + self.settings = cluster_settings or [ + {"name": "containerInsights", "value": "disabled"} + ] self.configuration = configuration self.capacity_providers = capacity_providers self.default_capacity_provider_strategy = default_capacity_provider_strategy self.tags = tags + self.service_connect_defaults = service_connect_defaults @property def physical_resource_id(self) -> str: @@ -962,6 +966,7 @@ class EC2ContainerServiceBackend(BaseBackend): configuration: Optional[Dict[str, Any]] = None, capacity_providers: Optional[List[str]] = None, default_capacity_provider_strategy: Optional[List[Dict[str, Any]]] = None, + service_connect_defaults: Optional[Dict[str, str]] = None, ) -> Cluster: cluster = Cluster( cluster_name, @@ -972,6 +977,7 @@ class EC2ContainerServiceBackend(BaseBackend): capacity_providers, default_capacity_provider_strategy, tags, + service_connect_defaults=service_connect_defaults, ) self.clusters[cluster_name] = cluster return cluster @@ -981,15 +987,18 @@ class EC2ContainerServiceBackend(BaseBackend): cluster_name: str, cluster_settings: Optional[List[Dict[str, str]]], configuration: Optional[Dict[str, Any]], + service_connect_defaults: Optional[Dict[str, str]], ) -> Cluster: """ The serviceConnectDefaults-parameter is not yet implemented """ cluster = self._get_cluster(cluster_name) - if cluster_settings: + if cluster_settings is not None: cluster.settings = cluster_settings - if configuration: + if configuration is not None: cluster.configuration = configuration + if service_connect_defaults is not None: + cluster.service_connect_defaults = service_connect_defaults return cluster def put_cluster_capacity_providers( diff --git a/moto/ecs/responses.py b/moto/ecs/responses.py index 27d42098d..402aba9d4 100644 --- a/moto/ecs/responses.py +++ b/moto/ecs/responses.py @@ -29,6 +29,7 @@ class EC2ContainerServiceResponse(BaseResponse): default_capacity_provider_strategy = self._get_param( "defaultCapacityProviderStrategy" ) + service_connect_defaults = self._get_param("serviceConnectDefaults") if cluster_name is None: cluster_name = "default" cluster = self.ecs_backend.create_cluster( @@ -38,6 +39,7 @@ class EC2ContainerServiceResponse(BaseResponse): configuration, capacity_providers, default_capacity_provider_strategy, + service_connect_defaults=service_connect_defaults, ) return json.dumps({"cluster": cluster.response_object}) @@ -49,7 +51,13 @@ class EC2ContainerServiceResponse(BaseResponse): cluster_name = self._get_param("cluster") settings = self._get_param("settings") configuration = self._get_param("configuration") - cluster = self.ecs_backend.update_cluster(cluster_name, settings, configuration) + service_connect_defaults = self._get_param("serviceConnectDefaults") + cluster = self.ecs_backend.update_cluster( + cluster_name=cluster_name, + cluster_settings=settings, + configuration=configuration, + service_connect_defaults=service_connect_defaults, + ) return json.dumps({"cluster": cluster.response_object}) def put_cluster_capacity_providers(self) -> str: diff --git a/moto/efs/exceptions.py b/moto/efs/exceptions.py index 92b8829cc..a405a9d85 100644 --- a/moto/efs/exceptions.py +++ b/moto/efs/exceptions.py @@ -30,7 +30,7 @@ class FileSystemNotFound(EFSError): def __init__(self, file_system_id: str): super().__init__( "FileSystemNotFound", - f"File system {file_system_id} does not exist.", + f"File system '{file_system_id}' does not exist.", ) diff --git a/moto/iam/models.py b/moto/iam/models.py index 31554d683..1e61d7111 100644 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -1127,9 +1127,9 @@ class Group(BaseModel): def arn(self): if self.path == "/": return f"arn:aws:iam::{self.account_id}:group/{self.name}" - else: - return f"arn:aws:iam::{self.account_id}:group/{self.path}/{self.name}" + # The path must by definition end and start with a forward slash. So we don't have to add more slashes to the ARN + return f"arn:aws:iam::{self.account_id}:group{self.path}{self.name}" def get_policy(self, policy_name): try: diff --git a/tests/terraformtests/etc/0001-Patch-Hardcode-endpoints-to-local-server.patch b/tests/terraformtests/etc/0001-Patch-Hardcode-endpoints-to-local-server.patch index 7afe7f4e9..716215ef7 100644 --- a/tests/terraformtests/etc/0001-Patch-Hardcode-endpoints-to-local-server.patch +++ b/tests/terraformtests/etc/0001-Patch-Hardcode-endpoints-to-local-server.patch @@ -1,8 +1,18 @@ +From 7b63ad24b4e5a9c874c0430431bf90cd12d9162b Mon Sep 17 00:00:00 2001 +From: Bert Blommers +Date: Thu, 23 Feb 2023 20:04:15 -0100 +Subject: [PATCH] Patch: Hardcode endpoints + +--- + internal/conns/config.go | 16 ++++++++++++++++ + internal/provider/provider.go | 4 ++-- + 2 files changed, 18 insertions(+), 2 deletions(-) + diff --git a/internal/conns/config.go b/internal/conns/config.go -index 13b7d153a7..1d981e9097 100644 +index 89ce54fc36..72d17bda71 100644 --- a/internal/conns/config.go +++ b/internal/conns/config.go -@@ -86,8 +86,23 @@ type Config struct { +@@ -77,8 +77,24 @@ type Config struct { UseFIPSEndpoint bool } @@ -15,6 +25,7 @@ index 13b7d153a7..1d981e9097 100644 + } + return localEndpoints +} ++ + // ConfigureProvider configures the provided provider Meta (instance data). func (c *Config) ConfigureProvider(ctx context.Context, client *AWSClient) (*AWSClient, diag.Diagnostics) { @@ -27,10 +38,10 @@ index 13b7d153a7..1d981e9097 100644 AccessKey: c.AccessKey, APNInfo: StdUserAgentProducts(c.TerraformVersion), diff --git a/internal/provider/provider.go b/internal/provider/provider.go -index c49bd366d3..2db930e61d 100644 +index 1c2fcaada9..636902d879 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go -@@ -2196,13 +2196,13 @@ func configure(ctx context.Context, provider *schema.Provider, d *schema.Resourc +@@ -2295,13 +2295,13 @@ func configure(ctx context.Context, provider *schema.Provider, d *schema.Resourc CustomCABundle: d.Get("custom_ca_bundle").(string), EC2MetadataServiceEndpoint: d.Get("ec2_metadata_service_endpoint").(string), EC2MetadataServiceEndpointMode: d.Get("ec2_metadata_service_endpoint_mode").(string), @@ -46,3 +57,6 @@ index c49bd366d3..2db930e61d 100644 SecretKey: d.Get("secret_key").(string), SkipCredsValidation: d.Get("skip_credentials_validation").(bool), SkipGetEC2Platforms: d.Get("skip_get_ec2_platforms").(bool), +-- +2.25.1 + diff --git a/tests/terraformtests/terraform-provider-aws b/tests/terraformtests/terraform-provider-aws index f9a6db6e3..a8163ccac 160000 --- a/tests/terraformtests/terraform-provider-aws +++ b/tests/terraformtests/terraform-provider-aws @@ -1 +1 @@ -Subproject commit f9a6db6e3c3f3299701747972fd6c37ba4af36f4 +Subproject commit a8163ccac8494c5beaa108369a0c8531f3800e18 diff --git a/tests/terraformtests/terraform-tests.failures.txt b/tests/terraformtests/terraform-tests.failures.txt index 23cb97aff..accba96d1 100644 --- a/tests/terraformtests/terraform-tests.failures.txt +++ b/tests/terraformtests/terraform-tests.failures.txt @@ -49,3 +49,13 @@ TestAccSsmParameterDataSource TestAccDataSourceLambdaLayerVersion TestAccDataSourceLambdaInvocation TestAccDataSourceNetworkInterface_ + +TestAccELBListenerPolicy_update +TestAccServiceDiscoveryService_private +TestAccIAMGroup_path +TestAccLambdaFunctionURL_Cors +TestAccVPCNATGateway_privateIP +TestAccVPCSecurityGroupRule_multiDescription + +# TF expects a wrong error message, which is not in-line with what AWS returns +TestAccEFSFileSystemDataSource_nonExistent_fileSystemID diff --git a/tests/terraformtests/terraform-tests.success.txt b/tests/terraformtests/terraform-tests.success.txt index 8e0a86195..20b8b2b50 100644 --- a/tests/terraformtests/terraform-tests.success.txt +++ b/tests/terraformtests/terraform-tests.success.txt @@ -135,9 +135,27 @@ ec2: - TestAccEC2VPNGatewayAttachment_ - TestAccVPCEgressOnlyInternetGateway_ - TestAccVPCInternetGateway - - TestAccVPCNATGateway_ + - TestAccVPCNATGateway_basic + - TestAccVPCNATGateway_disappears + - TestAccVPCNATGateway_ConnectivityType_private + - TestAccVPCNATGateway_tags - TestAccVPCSecurityGroupDataSource_basic - - TestAccVPCSecurityGroupRule_ + - TestAccVPCSecurityGroupRule_Ingress + - TestAccVPCSecurityGroupRule_egress + - TestAccVPCSecurityGroupRule_selfReference + - TestAccVPCSecurityGroupRule_expectInvalid + - TestAccVPCSecurityGroupRule_PartialMatching + - TestAccVPCSecurityGroupRule_issue5310 + - TestAccVPCSecurityGroupRule_race + - TestAccVPCSecurityGroupRule_selfSource + - TestAccVPCSecurityGroupRule_prefixList + - TestAccVPCSecurityGroupRule_ingressDescription + - TestAccVPCSecurityGroupRule_egressDescription + - TestAccVPCSecurityGroupRule_IngressDescription_updates + - TestAccVPCSecurityGroupRule_EgressDescription_updates + - TestAccVPCSecurityGroupRule_Description + - TestAccVPCSecurityGroupRule_MultipleRuleSearching_allProtocolCrash + - TestAccVPCSecurityGroupRule_protocolChange - TestAccVPCSecurityGroup_allowAll - TestAccVPCSecurityGroup_basic - TestAccVPCSecurityGroup_change @@ -220,7 +238,10 @@ ecs: efs: - TestAccEFSAccessPoint_ - TestAccEFSAccessPointDataSource - - TestAccEFSFileSystemDataSource + - TestAccEFSFileSystemDataSource_availabilityZone + - TestAccEFSFileSystemDataSource_id + - TestAccEFSFileSystemDataSource_name + - TestAccEFSFileSystemDataSource_tags elasticbeanstalk: - TestAccElasticBeanstalkHostedZoneDataSource - TestAccElasticBeanstalkSolutionStackDataSource @@ -228,7 +249,8 @@ elb: - TestAccELBAttachment - TestAccELBBackendServerPolicy - TestAccELBHostedZoneIDDataSource - - TestAccELBListenerPolicy + - TestAccELBListenerPolicy_basic + - TestAccELBListenerPolicy_disappears - TestAccELBServiceAccountDataSource - TestAccELBSSLNegotiationPolicy elbv2: @@ -251,7 +273,9 @@ guardduty: iam: - TestAccIAMAccessKey_ - TestAccIAMAccountAlias_ - - TestAccIAMGroup_ + - TestAccIAMGroup_basic + - TestAccIAMGroup_disappears + - TestAccIAMGroup_nameChange - TestAccIAMInstanceProfileDataSource_ - TestAccIAMGroupPolicy_ - TestAccIAMOpenIDConnectProvider_ @@ -298,7 +322,9 @@ lambda: - TestAccLambdaLayerVersion_licenseInfo - TestAccLambdaLayerVersion_s3 - TestAccLambdaLayerVersion_update - - TestAccLambdaFunctionURL + - TestAccLambdaFunctionURL_Alias + - TestAccLambdaFunctionURL_basic + - TestAccLambdaFunctionURL_TwoURLs meta: - TestAccMetaBillingServiceAccountDataSource mq: @@ -405,7 +431,11 @@ servicediscovery: - TestAccServiceDiscoveryHTTPNamespace - TestAccServiceDiscoveryPrivateDNSNamespace - TestAccServiceDiscoveryPublicDNSNamespace - - TestAccServiceDiscoveryService + - TestAccServiceDiscoveryService_disappears + - TestAccServiceDiscoveryService_http + - TestAccServiceDiscoveryService_tags + - TestAccServiceDiscoveryService_private_http + - TestAccServiceDiscoveryService_public signer: - TestAccSignerSigningProfileDataSource_basic - TestAccSignerSigningProfile_basic diff --git a/tests/test_ce/test_ce.py b/tests/test_ce/test_ce.py index e49d0ceb0..ee572557e 100644 --- a/tests/test_ce/test_ce.py +++ b/tests/test_ce/test_ce.py @@ -24,6 +24,24 @@ def test_create_cost_category_definition(): resp.should.have.key("CostCategoryArn").match( f"arn:aws:ce::{ACCOUNT_ID}:costcategory/" ) + resp.should.have.key("EffectiveStart") + + +@mock_ce +def test_create_cost_category_definition_with_effective_start(): + client = boto3.client("ce", region_name="ap-southeast-1") + resp = client.create_cost_category_definition( + Name="ccd", + RuleVersion="CostCategoryExpression.v1", + Rules=[ + {"Value": "v", "Rule": {"CostCategories": {"Key": "k", "Values": ["v"]}}} + ], + EffectiveStart="2022-11-01T00:00:00Z", + ) + resp.should.have.key("CostCategoryArn").match( + f"arn:aws:ce::{ACCOUNT_ID}:costcategory/" + ) + resp.should.have.key("EffectiveStart").equals("2022-11-01T00:00:00Z") @mock_ce diff --git a/tests/test_ecs/test_ecs_boto3.py b/tests/test_ecs/test_ecs_boto3.py index 9d750c093..0dfa8b2fd 100644 --- a/tests/test_ecs/test_ecs_boto3.py +++ b/tests/test_ecs/test_ecs_boto3.py @@ -45,12 +45,14 @@ def test_create_cluster_with_setting(): cluster = client.create_cluster( clusterName="test_ecs_cluster", settings=[{"name": "containerInsights", "value": "disabled"}], + serviceConnectDefaults={"namespace": "ns"}, )["cluster"] cluster["clusterName"].should.equal("test_ecs_cluster") cluster["status"].should.equal("ACTIVE") cluster.should.have.key("settings").equals( [{"name": "containerInsights", "value": "disabled"}] ) + cluster.should.have.key("serviceConnectDefaults").equals({"namespace": "ns"}) @mock_ecs diff --git a/tests/test_efs/test_file_system.py b/tests/test_efs/test_file_system.py index cf9af1576..9525b727f 100644 --- a/tests/test_efs/test_file_system.py +++ b/tests/test_efs/test_file_system.py @@ -181,6 +181,8 @@ def test_describe_file_systems_using_unknown_identifier(efs): efs.describe_file_systems(FileSystemId="unknown") err = exc.value.response["Error"] err["Code"].should.equal("FileSystemNotFound") + # Verified against AWS + err["Message"].should.equal("File system 'unknown' does not exist.") def test_describe_file_systems_minimal_case(efs): diff --git a/tests/test_efs/test_lifecycle_config.py b/tests/test_efs/test_lifecycle_config.py index 9fea49271..6c1f7f896 100644 --- a/tests/test_efs/test_lifecycle_config.py +++ b/tests/test_efs/test_lifecycle_config.py @@ -9,7 +9,7 @@ def test_describe_filesystem_config__unknown(efs): efs.describe_lifecycle_configuration(FileSystemId="unknown") err = exc_info.value.response["Error"] err["Code"].should.equal("FileSystemNotFound") - err["Message"].should.equal("File system unknown does not exist.") + err["Message"].should.equal("File system 'unknown' does not exist.") def test_describe_filesystem_config__initial(efs): diff --git a/tests/test_iam/test_iam_groups.py b/tests/test_iam/test_iam_groups.py index dc2d0ff60..332d4cf56 100644 --- a/tests/test_iam/test_iam_groups.py +++ b/tests/test_iam/test_iam_groups.py @@ -67,8 +67,8 @@ def test_get_group_current(): assert not result["Users"] # Make a group with a different path: - other_group = conn.create_group(GroupName="my-other-group", Path="some/location") - assert other_group["Group"]["Path"] == "some/location" + other_group = conn.create_group(GroupName="my-other-group", Path="/some/location/") + assert other_group["Group"]["Path"] == "/some/location/" assert ( other_group["Group"]["Arn"] == f"arn:aws:iam::{ACCOUNT_ID}:group/some/location/my-other-group"