382 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import time
 | |
| from datetime import datetime
 | |
| 
 | |
| import boto3
 | |
| import sure  # noqa
 | |
| from botocore.exceptions import ClientError
 | |
| import pytest
 | |
| 
 | |
| from moto import mock_ram, mock_organizations
 | |
| from moto.core import ACCOUNT_ID
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| def test_create_resource_share():
 | |
|     # given
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
| 
 | |
|     # when
 | |
|     response = client.create_resource_share(name="test")
 | |
| 
 | |
|     # then
 | |
|     resource = response["resourceShare"]
 | |
|     resource["allowExternalPrincipals"].should.be.ok
 | |
|     resource["creationTime"].should.be.a(datetime)
 | |
|     resource["lastUpdatedTime"].should.be.a(datetime)
 | |
|     resource["name"].should.equal("test")
 | |
|     resource["owningAccountId"].should.equal(ACCOUNT_ID)
 | |
|     resource["resourceShareArn"].should.match(
 | |
|         r"arn:aws:ram:us-east-1:\d{12}:resource-share/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
 | |
|     )
 | |
|     resource["status"].should.equal("ACTIVE")
 | |
|     resource.should_not.have.key("featureSet")
 | |
| 
 | |
|     # creating a resource share with the name should result in a second one
 | |
|     # not overwrite/update the old one
 | |
|     # when
 | |
|     response = client.create_resource_share(
 | |
|         name="test",
 | |
|         allowExternalPrincipals=False,
 | |
|         resourceArns=[
 | |
|             "arn:aws:ec2:us-east-1:{}:transit-gateway/tgw-123456789".format(ACCOUNT_ID)
 | |
|         ],
 | |
|     )
 | |
| 
 | |
|     # then
 | |
|     resource = response["resourceShare"]
 | |
|     resource["allowExternalPrincipals"].should_not.be.ok
 | |
|     resource["creationTime"].should.be.a(datetime)
 | |
|     resource["lastUpdatedTime"].should.be.a(datetime)
 | |
|     resource["name"].should.equal("test")
 | |
|     resource["owningAccountId"].should.equal(ACCOUNT_ID)
 | |
|     resource["resourceShareArn"].should.match(
 | |
|         r"arn:aws:ram:us-east-1:\d{12}:resource-share/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
 | |
|     )
 | |
|     resource["status"].should.equal("ACTIVE")
 | |
| 
 | |
|     response = client.get_resource_shares(resourceOwner="SELF")
 | |
|     response["resourceShares"].should.have.length_of(2)
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| def test_create_resource_share_errors():
 | |
|     # given
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
| 
 | |
|     # invalid ARN
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as e:
 | |
|         client.create_resource_share(name="test", resourceArns=["inalid-arn"])
 | |
|     ex = e.value
 | |
|     ex.operation_name.should.equal("CreateResourceShare")
 | |
|     ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
 | |
|     ex.response["Error"]["Code"].should.contain("MalformedArnException")
 | |
|     ex.response["Error"]["Message"].should.equal(
 | |
|         "The specified resource ARN inalid-arn is not valid. "
 | |
|         "Verify the ARN and try again."
 | |
|     )
 | |
| 
 | |
|     # valid ARN, but not shareable resource type
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as e:
 | |
|         client.create_resource_share(
 | |
|             name="test", resourceArns=["arn:aws:iam::{}:role/test".format(ACCOUNT_ID)]
 | |
|         )
 | |
|     ex = e.value
 | |
|     ex.operation_name.should.equal("CreateResourceShare")
 | |
|     ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
 | |
|     ex.response["Error"]["Code"].should.contain("MalformedArnException")
 | |
|     ex.response["Error"]["Message"].should.equal(
 | |
|         "You cannot share the selected resource type."
 | |
|     )
 | |
| 
 | |
|     # invalid principal ID
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as e:
 | |
|         client.create_resource_share(
 | |
|             name="test",
 | |
|             principals=["invalid"],
 | |
|             resourceArns=[
 | |
|                 "arn:aws:ec2:us-east-1:{}:transit-gateway/tgw-123456789".format(
 | |
|                     ACCOUNT_ID
 | |
|                 )
 | |
|             ],
 | |
|         )
 | |
|     ex = e.value
 | |
|     ex.operation_name.should.equal("CreateResourceShare")
 | |
|     ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
 | |
|     ex.response["Error"]["Code"].should.contain("InvalidParameterException")
 | |
|     ex.response["Error"]["Message"].should.equal(
 | |
|         "Principal ID invalid is malformed. Verify the ID and try again."
 | |
|     )
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| @mock_organizations
 | |
| def test_create_resource_share_with_organization():
 | |
|     # given
 | |
|     client = boto3.client("organizations", region_name="us-east-1")
 | |
|     org_arn = client.create_organization(FeatureSet="ALL")["Organization"]["Arn"]
 | |
|     root_id = client.list_roots()["Roots"][0]["Id"]
 | |
|     ou_arn = client.create_organizational_unit(ParentId=root_id, Name="test")[
 | |
|         "OrganizationalUnit"
 | |
|     ]["Arn"]
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
| 
 | |
|     # share in whole Organization
 | |
|     # when
 | |
|     response = client.create_resource_share(
 | |
|         name="test",
 | |
|         principals=[org_arn],
 | |
|         resourceArns=[
 | |
|             "arn:aws:ec2:us-east-1:{}:transit-gateway/tgw-123456789".format(ACCOUNT_ID)
 | |
|         ],
 | |
|     )
 | |
| 
 | |
|     # then
 | |
|     response["resourceShare"]["name"].should.equal("test")
 | |
| 
 | |
|     # share in an OU
 | |
|     # when
 | |
|     response = client.create_resource_share(
 | |
|         name="test",
 | |
|         principals=[ou_arn],
 | |
|         resourceArns=[
 | |
|             "arn:aws:ec2:us-east-1:{}:transit-gateway/tgw-123456789".format(ACCOUNT_ID)
 | |
|         ],
 | |
|     )
 | |
| 
 | |
|     # then
 | |
|     response["resourceShare"]["name"].should.equal("test")
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| @mock_organizations
 | |
| def test_create_resource_share_with_organization_errors():
 | |
|     # given
 | |
|     client = boto3.client("organizations", region_name="us-east-1")
 | |
|     client.create_organization(FeatureSet="ALL")
 | |
|     root_id = client.list_roots()["Roots"][0]["Id"]
 | |
|     client.create_organizational_unit(ParentId=root_id, Name="test")
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
| 
 | |
|     # unknown Organization
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as e:
 | |
|         client.create_resource_share(
 | |
|             name="test",
 | |
|             principals=[
 | |
|                 "arn:aws:organizations::{}:organization/o-unknown".format(ACCOUNT_ID)
 | |
|             ],
 | |
|             resourceArns=[
 | |
|                 "arn:aws:ec2:us-east-1:{}:transit-gateway/tgw-123456789".format(
 | |
|                     ACCOUNT_ID
 | |
|                 )
 | |
|             ],
 | |
|         )
 | |
|     ex = e.value
 | |
|     ex.operation_name.should.equal("CreateResourceShare")
 | |
|     ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
 | |
|     ex.response["Error"]["Code"].should.contain("UnknownResourceException")
 | |
|     ex.response["Error"]["Message"].should.equal(
 | |
|         "Organization o-unknown could not be found."
 | |
|     )
 | |
| 
 | |
|     # unknown OU
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as e:
 | |
|         client.create_resource_share(
 | |
|             name="test",
 | |
|             principals=[
 | |
|                 "arn:aws:organizations::{}:ou/o-unknown/ou-unknown".format(ACCOUNT_ID)
 | |
|             ],
 | |
|             resourceArns=[
 | |
|                 "arn:aws:ec2:us-east-1:{}:transit-gateway/tgw-123456789".format(
 | |
|                     ACCOUNT_ID
 | |
|                 )
 | |
|             ],
 | |
|         )
 | |
|     ex = e.value
 | |
|     ex.operation_name.should.equal("CreateResourceShare")
 | |
|     ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
 | |
|     ex.response["Error"]["Code"].should.contain("UnknownResourceException")
 | |
|     ex.response["Error"]["Message"].should.equal(
 | |
|         "OrganizationalUnit ou-unknown in unknown organization could not be found."
 | |
|     )
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| def test_get_resource_shares():
 | |
|     # given
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
|     client.create_resource_share(name="test")
 | |
| 
 | |
|     # when
 | |
|     response = client.get_resource_shares(resourceOwner="SELF")
 | |
| 
 | |
|     # then
 | |
|     response["resourceShares"].should.have.length_of(1)
 | |
|     resource = response["resourceShares"][0]
 | |
|     resource["allowExternalPrincipals"].should.be.ok
 | |
|     resource["creationTime"].should.be.a(datetime)
 | |
|     resource["featureSet"].should.equal("STANDARD")
 | |
|     resource["lastUpdatedTime"].should.be.a(datetime)
 | |
|     resource["name"].should.equal("test")
 | |
|     resource["owningAccountId"].should.equal(ACCOUNT_ID)
 | |
|     resource["resourceShareArn"].should.match(
 | |
|         r"arn:aws:ram:us-east-1:\d{12}:resource-share/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
 | |
|     )
 | |
|     resource["status"].should.equal("ACTIVE")
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| def test_get_resource_shares_errors():
 | |
|     # given
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
| 
 | |
|     # invalid resource owner
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as e:
 | |
|         client.get_resource_shares(resourceOwner="invalid")
 | |
|     ex = e.value
 | |
|     ex.operation_name.should.equal("GetResourceShares")
 | |
|     ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
 | |
|     ex.response["Error"]["Code"].should.contain("InvalidParameterException")
 | |
|     ex.response["Error"]["Message"].should.equal(
 | |
|         "invalid is not a valid resource owner. "
 | |
|         "Specify either SELF or OTHER-ACCOUNTS and try again."
 | |
|     )
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| def test_update_resource_share():
 | |
|     # given
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
|     arn = client.create_resource_share(name="test")["resourceShare"]["resourceShareArn"]
 | |
| 
 | |
|     # when
 | |
|     time.sleep(0.1)
 | |
|     response = client.update_resource_share(resourceShareArn=arn, name="test-update")
 | |
| 
 | |
|     # then
 | |
|     resource = response["resourceShare"]
 | |
|     resource["allowExternalPrincipals"].should.be.ok
 | |
|     resource["name"].should.equal("test-update")
 | |
|     resource["owningAccountId"].should.equal(ACCOUNT_ID)
 | |
|     resource["resourceShareArn"].should.match(
 | |
|         r"arn:aws:ram:us-east-1:\d{12}:resource-share/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
 | |
|     )
 | |
|     resource["status"].should.equal("ACTIVE")
 | |
|     resource.should_not.have.key("featureSet")
 | |
|     creation_time = resource["creationTime"]
 | |
|     resource["lastUpdatedTime"].should.be.greater_than(creation_time)
 | |
| 
 | |
|     response = client.get_resource_shares(resourceOwner="SELF")
 | |
|     response["resourceShares"].should.have.length_of(1)
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| def test_update_resource_share_errors():
 | |
|     # given
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
| 
 | |
|     # invalid resource owner
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as e:
 | |
|         client.update_resource_share(
 | |
|             resourceShareArn="arn:aws:ram:us-east-1:{}:resource-share/not-existing".format(
 | |
|                 ACCOUNT_ID
 | |
|             ),
 | |
|             name="test-update",
 | |
|         )
 | |
|     ex = e.value
 | |
|     ex.operation_name.should.equal("UpdateResourceShare")
 | |
|     ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
 | |
|     ex.response["Error"]["Code"].should.contain("UnknownResourceException")
 | |
|     ex.response["Error"]["Message"].should.equal(
 | |
|         "ResourceShare arn:aws:ram:us-east-1:{}:resource-share/not-existing could not be found.".format(
 | |
|             ACCOUNT_ID
 | |
|         )
 | |
|     )
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| def test_delete_resource_share():
 | |
|     # given
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
|     arn = client.create_resource_share(name="test")["resourceShare"]["resourceShareArn"]
 | |
| 
 | |
|     # when
 | |
|     time.sleep(0.1)
 | |
|     response = client.delete_resource_share(resourceShareArn=arn)
 | |
| 
 | |
|     # then
 | |
|     response["returnValue"].should.be.ok
 | |
| 
 | |
|     response = client.get_resource_shares(resourceOwner="SELF")
 | |
|     response["resourceShares"].should.have.length_of(1)
 | |
|     resource = response["resourceShares"][0]
 | |
|     resource["status"].should.equal("DELETED")
 | |
|     creation_time = resource["creationTime"]
 | |
|     resource["lastUpdatedTime"].should.be.greater_than(creation_time)
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| def test_delete_resource_share_errors():
 | |
|     # given
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
| 
 | |
|     # invalid resource owner
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as e:
 | |
|         client.delete_resource_share(
 | |
|             resourceShareArn="arn:aws:ram:us-east-1:{}:resource-share/not-existing".format(
 | |
|                 ACCOUNT_ID
 | |
|             )
 | |
|         )
 | |
|     ex = e.value
 | |
|     ex.operation_name.should.equal("DeleteResourceShare")
 | |
|     ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
 | |
|     ex.response["Error"]["Code"].should.contain("UnknownResourceException")
 | |
|     ex.response["Error"]["Message"].should.equal(
 | |
|         "ResourceShare arn:aws:ram:us-east-1:{}:resource-share/not-existing could not be found.".format(
 | |
|             ACCOUNT_ID
 | |
|         )
 | |
|     )
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| @mock_organizations
 | |
| def test_enable_sharing_with_aws_organization():
 | |
|     # given
 | |
|     client = boto3.client("organizations", region_name="us-east-1")
 | |
|     client.create_organization(FeatureSet="ALL")
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
| 
 | |
|     # when
 | |
|     response = client.enable_sharing_with_aws_organization()
 | |
| 
 | |
|     # then
 | |
|     response["returnValue"].should.be.ok
 | |
| 
 | |
| 
 | |
| @mock_ram
 | |
| @mock_organizations
 | |
| def test_enable_sharing_with_aws_organization_errors():
 | |
|     # given
 | |
|     client = boto3.client("ram", region_name="us-east-1")
 | |
| 
 | |
|     # no Organization defined
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as e:
 | |
|         client.enable_sharing_with_aws_organization()
 | |
|     ex = e.value
 | |
|     ex.operation_name.should.equal("EnableSharingWithAwsOrganization")
 | |
|     ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
 | |
|     ex.response["Error"]["Code"].should.contain("OperationNotPermittedException")
 | |
|     ex.response["Error"]["Message"].should.equal(
 | |
|         "Unable to enable sharing with AWS Organizations. "
 | |
|         "Received AccessDeniedException from AWSOrganizations with the following error message: "
 | |
|         "You don't have permissions to access this resource."
 | |
|     )
 |