241 lines
8.7 KiB
Python
241 lines
8.7 KiB
Python
import boto3
|
|
import pytest
|
|
from botocore.exceptions import ClientError
|
|
|
|
from moto import mock_aws, settings
|
|
from moto.moto_api._internal import mock_random
|
|
|
|
TEST_REGION = "us-east-1" if settings.TEST_SERVER_MODE else "us-west-2"
|
|
|
|
|
|
def create_vpc(ec2_client):
|
|
"""Return the ID for a valid VPC."""
|
|
return ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
|
|
|
|
|
|
def create_subnets(
|
|
ec2_client, vpc_id, region1=TEST_REGION + "a", region2=TEST_REGION + "b"
|
|
):
|
|
"""Return list of two subnets IDs."""
|
|
subnet_ids = []
|
|
for cidr_block, region in [("10.0.1.0/24", region1), ("10.0.0.0/24", region2)]:
|
|
subnet_ids.append(
|
|
ec2_client.create_subnet(
|
|
VpcId=vpc_id, CidrBlock=cidr_block, AvailabilityZone=region
|
|
)["Subnet"]["SubnetId"]
|
|
)
|
|
return subnet_ids
|
|
|
|
|
|
def create_test_directory(ds_client, ec2_client, vpc_settings=None, tags=None):
|
|
"""Return ID of a newly created valid directory."""
|
|
if not vpc_settings:
|
|
good_vpc_id = create_vpc(ec2_client)
|
|
good_subnet_ids = create_subnets(ec2_client, good_vpc_id)
|
|
vpc_settings = {"VpcId": good_vpc_id, "SubnetIds": good_subnet_ids}
|
|
|
|
if not tags:
|
|
tags = []
|
|
|
|
result = ds_client.create_directory(
|
|
Name=f"test-{mock_random.get_random_hex(6)}.test",
|
|
Password="Password4TheAges",
|
|
Size="Large",
|
|
VpcSettings=vpc_settings,
|
|
Tags=tags,
|
|
)
|
|
return result["DirectoryId"]
|
|
|
|
|
|
@mock_aws
|
|
def test_ds_create_directory_validations():
|
|
"""Test validation errs that aren't caught by botocore."""
|
|
client = boto3.client("ds", region_name=TEST_REGION)
|
|
random_num = mock_random.get_random_hex(6)
|
|
|
|
# Verify ValidationException error messages are accumulated properly.
|
|
bad_name = f"bad_name_{random_num}"
|
|
bad_password = "bad_password"
|
|
bad_size = "big"
|
|
ok_vpc_settings = {
|
|
"VpcId": f"vpc-{random_num}",
|
|
"SubnetIds": [f"subnet-{random_num}01", f"subnet-{random_num}02"],
|
|
}
|
|
with pytest.raises(ClientError) as exc:
|
|
client.create_directory(
|
|
Name=bad_name,
|
|
Password=bad_password,
|
|
Size=bad_size,
|
|
VpcSettings=ok_vpc_settings,
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "ValidationException"
|
|
assert "3 validation errors detected" in err["Message"]
|
|
assert (
|
|
r"Value at 'password' failed to satisfy constraint: "
|
|
r"Member must satisfy regular expression pattern: "
|
|
r"^(?=^.{8,64}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|"
|
|
r"(?=.*\d)(?=.*[^A-Za-z0-9\s])(?=.*[a-z])|"
|
|
r"(?=.*[^A-Za-z0-9\s])(?=.*[A-Z])(?=.*[a-z])|"
|
|
r"(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9\s]))^.*$" in err["Message"]
|
|
)
|
|
assert (
|
|
f"Value '{bad_size}' at 'size' failed to satisfy constraint: "
|
|
f"Member must satisfy enum value set: [Small, Large];" in err["Message"]
|
|
)
|
|
assert (
|
|
rf"Value '{bad_name}' at 'name' failed to satisfy constraint: "
|
|
rf"Member must satisfy regular expression pattern: "
|
|
rf"^([a-zA-Z0-9]+[\.-])+([a-zA-Z0-9])+$" in err["Message"]
|
|
)
|
|
|
|
too_long = (
|
|
"Test of directory service 0123456789 0123456789 0123456789 "
|
|
"0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 "
|
|
"0123456789 0123456789"
|
|
)
|
|
short_name = "a:b.c"
|
|
with pytest.raises(ClientError) as exc:
|
|
client.create_directory(
|
|
Name=f"test{random_num}.test",
|
|
Password="TESTfoobar1",
|
|
Size="Large",
|
|
VpcSettings=ok_vpc_settings,
|
|
Description=too_long,
|
|
ShortName=short_name,
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "ValidationException"
|
|
assert "2 validation errors detected" in err["Message"]
|
|
assert (
|
|
f"Value '{too_long}' at 'description' failed to satisfy constraint: "
|
|
f"Member must have length less than or equal to 128" in err["Message"]
|
|
)
|
|
pattern = r'^[^\/:*?"<>|.]+[^\/:*?"<>|]*$'
|
|
assert (
|
|
f"Value '{short_name}' at 'shortName' failed to satisfy constraint: "
|
|
f"Member must satisfy regular expression pattern: " + pattern
|
|
) in err["Message"]
|
|
|
|
bad_vpc_settings = {"VpcId": f"vpc-{random_num}", "SubnetIds": ["foo"]}
|
|
with pytest.raises(ClientError) as exc:
|
|
client.create_directory(
|
|
Name=f"test{random_num}.test",
|
|
Password="TESTfoobar1",
|
|
Size="Large",
|
|
VpcSettings=bad_vpc_settings,
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "ValidationException"
|
|
assert "1 validation error detected" in err["Message"]
|
|
assert (
|
|
rf"Value '['{bad_vpc_settings['SubnetIds'][0]}']' at "
|
|
rf"'vpcSettings.subnetIds' failed to satisfy constraint: "
|
|
rf"Member must satisfy regular expression pattern: "
|
|
rf"^(subnet-[0-9a-f]{{8}}|subnet-[0-9a-f]{{17}})$" in err["Message"]
|
|
)
|
|
|
|
|
|
@mock_aws
|
|
def test_ds_create_directory_bad_vpc_settings():
|
|
"""Test validation of bad vpc that doesn't raise ValidationException."""
|
|
client = boto3.client("ds", region_name=TEST_REGION)
|
|
|
|
# Error if no VpcSettings argument.
|
|
with pytest.raises(ClientError) as exc:
|
|
client.create_directory(
|
|
Name=f"test-{mock_random.get_random_hex(6)}.test",
|
|
Password="TESTfoobar1",
|
|
Size="Small",
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "InvalidParameterException"
|
|
assert "VpcSettings must be specified" in err["Message"]
|
|
|
|
# Error if VPC is bogus.
|
|
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
|
|
good_subnet_ids = create_subnets(ec2_client, create_vpc(ec2_client))
|
|
with pytest.raises(ClientError) as exc:
|
|
create_test_directory(
|
|
client, ec2_client, {"VpcId": "vpc-12345678", "SubnetIds": good_subnet_ids}
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "ClientException"
|
|
assert "Invalid VPC ID" in err["Message"]
|
|
|
|
|
|
@mock_aws
|
|
def test_ds_create_directory_bad_subnets():
|
|
"""Test validation of VPC subnets."""
|
|
client = boto3.client("ds", region_name=TEST_REGION)
|
|
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
|
|
|
|
# Error if VPC subnets are bogus.
|
|
good_vpc_id = create_vpc(ec2_client)
|
|
with pytest.raises(ClientError) as exc:
|
|
create_test_directory(
|
|
client,
|
|
ec2_client,
|
|
{"VpcId": good_vpc_id, "SubnetIds": ["subnet-12345678", "subnet-87654321"]},
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "InvalidParameterException"
|
|
assert (
|
|
"Invalid subnet ID(s). They must correspond to two subnets in "
|
|
"different Availability Zones."
|
|
) in err["Message"]
|
|
|
|
# Error if both VPC subnets are in the same region.
|
|
subnets_same_region = create_subnets(
|
|
ec2_client, good_vpc_id, region1=TEST_REGION + "a", region2=TEST_REGION + "a"
|
|
)
|
|
with pytest.raises(ClientError) as exc:
|
|
create_test_directory(
|
|
client, ec2_client, {"VpcId": good_vpc_id, "SubnetIds": subnets_same_region}
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "ClientException"
|
|
assert (
|
|
"Invalid subnet ID(s). The two subnets must be in different "
|
|
"Availability Zones."
|
|
) in err["Message"]
|
|
ec2_client.delete_subnet(SubnetId=subnets_same_region[0])
|
|
ec2_client.delete_subnet(SubnetId=subnets_same_region[1])
|
|
|
|
# Error if only one VPC subnet.
|
|
good_subnet_ids = create_subnets(ec2_client, good_vpc_id)
|
|
with pytest.raises(ClientError) as exc:
|
|
create_test_directory(
|
|
client,
|
|
ec2_client,
|
|
{"VpcId": good_vpc_id, "SubnetIds": [good_subnet_ids[0]]},
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "InvalidParameterException"
|
|
assert "Invalid subnet ID(s). They must correspond to two subnets" in err["Message"]
|
|
|
|
|
|
@mock_aws
|
|
def test_ds_create_directory_good_args():
|
|
"""Test creation of AD directory using good arguments."""
|
|
client = boto3.client("ds", region_name=TEST_REGION)
|
|
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
|
|
|
|
# Verify a good call to create_directory()
|
|
directory_id = create_test_directory(client, ec2_client)
|
|
assert directory_id.startswith("d-")
|
|
|
|
# Verify that too many directories can't be created.
|
|
limits = client.get_directory_limits()["DirectoryLimits"]
|
|
for _ in range(limits["CloudOnlyDirectoriesLimit"]):
|
|
create_test_directory(client, ec2_client)
|
|
with pytest.raises(ClientError) as exc:
|
|
create_test_directory(client, ec2_client)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "DirectoryLimitExceededException"
|
|
assert (
|
|
f"Directory limit exceeded. A maximum of "
|
|
f"{limits['CloudOnlyDirectoriesLimit']} "
|
|
f"directories may be created" in err["Message"]
|
|
)
|