2020-07-21 13:15:13 +00:00
|
|
|
import time
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
import boto3
|
2021-10-18 19:44:29 +00:00
|
|
|
import sure # noqa # pylint: disable=unused-import
|
2020-07-21 13:15:13 +00:00
|
|
|
from botocore.exceptions import ClientError
|
2020-10-06 05:54:49 +00:00
|
|
|
import pytest
|
2020-07-21 13:15:13 +00:00
|
|
|
|
|
|
|
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
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as e:
|
2020-07-21 13:15:13 +00:00
|
|
|
client.create_resource_share(name="test", resourceArns=["inalid-arn"])
|
2020-10-06 06:04:09 +00:00
|
|
|
ex = e.value
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as e:
|
2020-07-21 13:15:13 +00:00
|
|
|
client.create_resource_share(
|
|
|
|
name="test", resourceArns=["arn:aws:iam::{}:role/test".format(ACCOUNT_ID)]
|
|
|
|
)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex = e.value
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as e:
|
2020-07-21 13:15:13 +00:00
|
|
|
client.create_resource_share(
|
|
|
|
name="test",
|
|
|
|
principals=["invalid"],
|
|
|
|
resourceArns=[
|
|
|
|
"arn:aws:ec2:us-east-1:{}:transit-gateway/tgw-123456789".format(
|
|
|
|
ACCOUNT_ID
|
|
|
|
)
|
|
|
|
],
|
|
|
|
)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex = e.value
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as e:
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
|
|
|
)
|
|
|
|
],
|
|
|
|
)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex = e.value
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as e:
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
|
|
|
)
|
|
|
|
],
|
|
|
|
)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex = e.value
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as e:
|
2020-07-21 13:15:13 +00:00
|
|
|
client.get_resource_shares(resourceOwner="invalid")
|
2020-10-06 06:04:09 +00:00
|
|
|
ex = e.value
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as e:
|
2020-07-21 13:15:13 +00:00
|
|
|
client.update_resource_share(
|
|
|
|
resourceShareArn="arn:aws:ram:us-east-1:{}:resource-share/not-existing".format(
|
|
|
|
ACCOUNT_ID
|
|
|
|
),
|
|
|
|
name="test-update",
|
|
|
|
)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex = e.value
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as e:
|
2020-07-21 13:15:13 +00:00
|
|
|
client.delete_resource_share(
|
|
|
|
resourceShareArn="arn:aws:ram:us-east-1:{}:resource-share/not-existing".format(
|
|
|
|
ACCOUNT_ID
|
|
|
|
)
|
|
|
|
)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex = e.value
|
2020-07-21 13:15:13 +00:00
|
|
|
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
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as e:
|
2020-07-21 13:15:13 +00:00
|
|
|
client.enable_sharing_with_aws_organization()
|
2020-10-06 06:04:09 +00:00
|
|
|
ex = e.value
|
2020-07-21 13:15:13 +00:00
|
|
|
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."
|
|
|
|
)
|