DirectoryService: Create security group and ENI (#4588)
This commit is contained in:
parent
0e6157011f
commit
5c44a8945d
@ -22,7 +22,7 @@ class DsValidationException(JsonRESTError):
|
|||||||
)
|
)
|
||||||
msgs = []
|
msgs = []
|
||||||
for arg_name, arg_value, constraint in error_tuples:
|
for arg_name, arg_value, constraint in error_tuples:
|
||||||
value = "at" if arg_name == "password" else f"'{arg_value}' at"
|
value = "at" if "assword" in arg_name else f"'{arg_value}' at"
|
||||||
msgs.append(
|
msgs.append(
|
||||||
f"Value {value} '{arg_name}' failed to satisfy constraint: "
|
f"Value {value} '{arg_name}' failed to satisfy constraint: "
|
||||||
f"Member must {constraint}"
|
f"Member must {constraint}"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""DirectoryServiceBackend class with methods for supported APIs."""
|
"""DirectoryServiceBackend class with methods for supported APIs."""
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
import ipaddress
|
|
||||||
|
|
||||||
from boto3 import Session
|
from boto3 import Session
|
||||||
|
|
||||||
@ -16,22 +15,9 @@ from moto.ds.exceptions import (
|
|||||||
ValidationException,
|
ValidationException,
|
||||||
)
|
)
|
||||||
from moto.ds.utils import PAGINATION_MODEL
|
from moto.ds.utils import PAGINATION_MODEL
|
||||||
from moto.ds.validations import (
|
from moto.ds.validations import validate_args
|
||||||
validate_args,
|
|
||||||
validate_alias,
|
|
||||||
validate_description,
|
|
||||||
validate_directory_id,
|
|
||||||
validate_dns_ips,
|
|
||||||
validate_edition,
|
|
||||||
validate_name,
|
|
||||||
validate_password,
|
|
||||||
validate_short_name,
|
|
||||||
validate_size,
|
|
||||||
validate_sso_password,
|
|
||||||
validate_subnet_ids,
|
|
||||||
validate_user_name,
|
|
||||||
)
|
|
||||||
from moto.ec2.exceptions import InvalidSubnetIdError
|
from moto.ec2.exceptions import InvalidSubnetIdError
|
||||||
|
from moto.ec2 import ec2_backends
|
||||||
from moto.utilities.paginator import paginate
|
from moto.utilities.paginator import paginate
|
||||||
from moto.utilities.tagging_service import TaggingService
|
from moto.utilities.tagging_service import TaggingService
|
||||||
|
|
||||||
@ -62,10 +48,10 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
region,
|
||||||
name,
|
name,
|
||||||
password,
|
password,
|
||||||
directory_type,
|
directory_type,
|
||||||
subnets,
|
|
||||||
size=None,
|
size=None,
|
||||||
vpc_settings=None,
|
vpc_settings=None,
|
||||||
connect_settings=None,
|
connect_settings=None,
|
||||||
@ -73,6 +59,7 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
|
|||||||
description=None,
|
description=None,
|
||||||
edition=None,
|
edition=None,
|
||||||
): # pylint: disable=too-many-arguments
|
): # pylint: disable=too-many-arguments
|
||||||
|
self.region = region
|
||||||
self.name = name
|
self.name = name
|
||||||
self.password = password
|
self.password = password
|
||||||
self.directory_type = directory_type
|
self.directory_type = directory_type
|
||||||
@ -92,26 +79,43 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
|
|||||||
self.stage = "Active"
|
self.stage = "Active"
|
||||||
self.launch_time = datetime.now(timezone.utc).isoformat()
|
self.launch_time = datetime.now(timezone.utc).isoformat()
|
||||||
self.stage_last_updated_date_time = datetime.now(timezone.utc).isoformat()
|
self.stage_last_updated_date_time = datetime.now(timezone.utc).isoformat()
|
||||||
|
# Create a security group and ENI, returning the IPs for the ENI.
|
||||||
|
subnet_ips = self.create_eni()
|
||||||
|
|
||||||
if directory_type != "ADConnector":
|
if self.directory_type != "ADConnector":
|
||||||
self.dns_ip_addrs = self.subnet_ips(subnets)
|
self.dns_ip_addrs = subnet_ips
|
||||||
else:
|
else:
|
||||||
self.dns_ip_addrs = self.connect_settings["CustomerDnsIps"]
|
self.dns_ip_addrs = self.connect_settings["CustomerDnsIps"]
|
||||||
self.connect_settings["ConnectIps"] = self.subnet_ips(subnets)
|
self.connect_settings["ConnectIps"] = subnet_ips
|
||||||
|
|
||||||
@staticmethod
|
def create_eni(self):
|
||||||
def subnet_ips(subnets):
|
"""Return IP addrs after creating an ENI for each subnet."""
|
||||||
"""Return an IP from each of the given subnets.
|
if self.vpc_settings:
|
||||||
|
vpc_id = self.vpc_settings["VpcId"]
|
||||||
|
subnet_ids = self.vpc_settings["SubnetIds"]
|
||||||
|
else:
|
||||||
|
vpc_id = self.connect_settings["VpcId"]
|
||||||
|
subnet_ids = self.connect_settings["SubnetIds"]
|
||||||
|
|
||||||
|
# Need a security group for the ENI.
|
||||||
|
security_group = ec2_backends[self.region].create_security_group(
|
||||||
|
name=f"{self.directory_id}_controllers",
|
||||||
|
description=(
|
||||||
|
f"AWS created security group for {self.directory_id} "
|
||||||
|
f"directory controllers"
|
||||||
|
),
|
||||||
|
vpc_id=vpc_id,
|
||||||
|
)
|
||||||
|
|
||||||
This is a bit dodgey and may need to be reworked at a later time.
|
|
||||||
"""
|
|
||||||
ip_addrs = []
|
ip_addrs = []
|
||||||
for subnet in subnets:
|
for subnet_id in subnet_ids:
|
||||||
ips = ipaddress.ip_network(subnet.cidr_block)
|
eni_info = ec2_backends[self.region].create_network_interface(
|
||||||
# Not sure if the following could occur, but if it does,
|
subnet=subnet_id,
|
||||||
# the situation will be ignored.
|
private_ip_address=None,
|
||||||
if ips:
|
group_ids=[security_group.id],
|
||||||
ip_addrs.append(str(ips[1]) if ips.num_addresses > 1 else str(ips[0]))
|
description=f"AWS created network interface for {self.directory_id}",
|
||||||
|
)
|
||||||
|
ip_addrs.append(eni_info.private_ip_address)
|
||||||
return ip_addrs
|
return ip_addrs
|
||||||
|
|
||||||
def update_alias(self, alias):
|
def update_alias(self, alias):
|
||||||
@ -167,8 +171,8 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_subnets(region, vpc_settings):
|
def _verify_subnets(region, vpc_settings):
|
||||||
"""Return subnets if vpc_settings are invalid, else raise an exception.
|
"""Verify subnets are valid, else raise an exception.
|
||||||
|
|
||||||
If settings are valid, add AvailabilityZones to vpc_settings.
|
If settings are valid, add AvailabilityZones to vpc_settings.
|
||||||
"""
|
"""
|
||||||
@ -178,8 +182,6 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
"in different Availability Zones."
|
"in different Availability Zones."
|
||||||
)
|
)
|
||||||
|
|
||||||
from moto.ec2 import ec2_backends # pylint: disable=import-outside-toplevel
|
|
||||||
|
|
||||||
# Subnet IDs are checked before the VPC ID. The Subnet IDs must
|
# Subnet IDs are checked before the VPC ID. The Subnet IDs must
|
||||||
# be valid and in different availability zones.
|
# be valid and in different availability zones.
|
||||||
try:
|
try:
|
||||||
@ -202,9 +204,7 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
vpcs = ec2_backends[region].describe_vpcs()
|
vpcs = ec2_backends[region].describe_vpcs()
|
||||||
if vpc_settings["VpcId"] not in [x.id for x in vpcs]:
|
if vpc_settings["VpcId"] not in [x.id for x in vpcs]:
|
||||||
raise ClientException("Invalid VPC ID.")
|
raise ClientException("Invalid VPC ID.")
|
||||||
|
|
||||||
vpc_settings["AvailabilityZones"] = regions
|
vpc_settings["AvailabilityZones"] = regions
|
||||||
return subnets
|
|
||||||
|
|
||||||
def connect_directory(
|
def connect_directory(
|
||||||
self,
|
self,
|
||||||
@ -226,30 +226,24 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
|
|
||||||
validate_args(
|
validate_args(
|
||||||
[
|
[
|
||||||
(validate_password, "password", password),
|
("password", password),
|
||||||
(validate_size, "size", size),
|
("size", size),
|
||||||
(validate_name, "name", name),
|
("name", name),
|
||||||
(validate_description, "description", description),
|
("description", description),
|
||||||
(validate_short_name, "shortName", short_name),
|
("shortName", short_name),
|
||||||
(
|
(
|
||||||
validate_subnet_ids,
|
|
||||||
"connectSettings.vpcSettings.subnetIds",
|
"connectSettings.vpcSettings.subnetIds",
|
||||||
connect_settings["SubnetIds"],
|
connect_settings["SubnetIds"],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
validate_user_name,
|
|
||||||
"connectSettings.customerUserName",
|
"connectSettings.customerUserName",
|
||||||
connect_settings["CustomerUserName"],
|
connect_settings["CustomerUserName"],
|
||||||
),
|
),
|
||||||
(
|
("connectSettings.customerDnsIps", connect_settings["CustomerDnsIps"]),
|
||||||
validate_dns_ips,
|
|
||||||
"connectSettings.customerDnsIps",
|
|
||||||
connect_settings["CustomerDnsIps"],
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
# ConnectSettings and VpcSettings both have a VpcId and Subnets.
|
# ConnectSettings and VpcSettings both have a VpcId and Subnets.
|
||||||
subnets = self._get_subnets(region, connect_settings)
|
self._verify_subnets(region, connect_settings)
|
||||||
|
|
||||||
errmsg = self.tagger.validate_tags(tags or [])
|
errmsg = self.tagger.validate_tags(tags or [])
|
||||||
if errmsg:
|
if errmsg:
|
||||||
@ -258,10 +252,10 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
raise DirectoryLimitExceededException("Tag Limit is exceeding")
|
raise DirectoryLimitExceededException("Tag Limit is exceeding")
|
||||||
|
|
||||||
directory = Directory(
|
directory = Directory(
|
||||||
|
region,
|
||||||
name,
|
name,
|
||||||
password,
|
password,
|
||||||
"ADConnector",
|
"ADConnector",
|
||||||
subnets,
|
|
||||||
size=size,
|
size=size,
|
||||||
connect_settings=connect_settings,
|
connect_settings=connect_settings,
|
||||||
short_name=short_name,
|
short_name=short_name,
|
||||||
@ -286,19 +280,15 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
raise InvalidParameterException("VpcSettings must be specified.")
|
raise InvalidParameterException("VpcSettings must be specified.")
|
||||||
validate_args(
|
validate_args(
|
||||||
[
|
[
|
||||||
(validate_password, "password", password),
|
("password", password),
|
||||||
(validate_size, "size", size),
|
("size", size),
|
||||||
(validate_name, "name", name),
|
("name", name),
|
||||||
(validate_description, "description", description),
|
("description", description),
|
||||||
(validate_short_name, "shortName", short_name),
|
("shortName", short_name),
|
||||||
(
|
("vpcSettings.subnetIds", vpc_settings["SubnetIds"]),
|
||||||
validate_subnet_ids,
|
|
||||||
"vpcSettings.subnetIds",
|
|
||||||
vpc_settings["SubnetIds"],
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
subnets = self._get_subnets(region, vpc_settings)
|
self._verify_subnets(region, vpc_settings)
|
||||||
|
|
||||||
errmsg = self.tagger.validate_tags(tags or [])
|
errmsg = self.tagger.validate_tags(tags or [])
|
||||||
if errmsg:
|
if errmsg:
|
||||||
@ -307,10 +297,10 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
raise DirectoryLimitExceededException("Tag Limit is exceeding")
|
raise DirectoryLimitExceededException("Tag Limit is exceeding")
|
||||||
|
|
||||||
directory = Directory(
|
directory = Directory(
|
||||||
|
region,
|
||||||
name,
|
name,
|
||||||
password,
|
password,
|
||||||
"SimpleAD",
|
"SimpleAD",
|
||||||
subnets,
|
|
||||||
size=size,
|
size=size,
|
||||||
vpc_settings=vpc_settings,
|
vpc_settings=vpc_settings,
|
||||||
short_name=short_name,
|
short_name=short_name,
|
||||||
@ -323,7 +313,7 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
def _validate_directory_id(self, directory_id):
|
def _validate_directory_id(self, directory_id):
|
||||||
"""Raise an exception if the directory id is invalid or unknown."""
|
"""Raise an exception if the directory id is invalid or unknown."""
|
||||||
# Validation of ID takes precedence over a check for its existence.
|
# Validation of ID takes precedence over a check for its existence.
|
||||||
validate_args([(validate_directory_id, "directoryId", directory_id)])
|
validate_args([("directoryId", directory_id)])
|
||||||
if directory_id not in self.directories:
|
if directory_id not in self.directories:
|
||||||
raise EntityDoesNotExistException(
|
raise EntityDoesNotExistException(
|
||||||
f"Directory {directory_id} does not exist"
|
f"Directory {directory_id} does not exist"
|
||||||
@ -345,8 +335,7 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
# Is the alias already in use?
|
# Is the alias already in use?
|
||||||
if alias in [x.alias for x in self.directories.values()]:
|
if alias in [x.alias for x in self.directories.values()]:
|
||||||
raise EntityAlreadyExistsException(f"Alias '{alias}' already exists.")
|
raise EntityAlreadyExistsException(f"Alias '{alias}' already exists.")
|
||||||
|
validate_args([("alias", alias)])
|
||||||
validate_args([(validate_alias, "alias", alias)])
|
|
||||||
|
|
||||||
directory.update_alias(alias)
|
directory.update_alias(alias)
|
||||||
return {"DirectoryId": directory_id, "Alias": alias}
|
return {"DirectoryId": directory_id, "Alias": alias}
|
||||||
@ -372,19 +361,15 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
# boto3 looks for missing vpc_settings for create_microsoft_ad().
|
# boto3 looks for missing vpc_settings for create_microsoft_ad().
|
||||||
validate_args(
|
validate_args(
|
||||||
[
|
[
|
||||||
(validate_password, "password", password),
|
("password", password),
|
||||||
(validate_edition, "edition", edition),
|
("edition", edition),
|
||||||
(validate_name, "name", name),
|
("name", name),
|
||||||
(validate_description, "description", description),
|
("description", description),
|
||||||
(validate_short_name, "shortName", short_name),
|
("shortName", short_name),
|
||||||
(
|
("vpcSettings.subnetIds", vpc_settings["SubnetIds"]),
|
||||||
validate_subnet_ids,
|
|
||||||
"vpcSettings.subnetIds",
|
|
||||||
vpc_settings["SubnetIds"],
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
subnets = self._get_subnets(region, vpc_settings)
|
self._verify_subnets(region, vpc_settings)
|
||||||
|
|
||||||
errmsg = self.tagger.validate_tags(tags or [])
|
errmsg = self.tagger.validate_tags(tags or [])
|
||||||
if errmsg:
|
if errmsg:
|
||||||
@ -393,10 +378,10 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
raise DirectoryLimitExceededException("Tag Limit is exceeding")
|
raise DirectoryLimitExceededException("Tag Limit is exceeding")
|
||||||
|
|
||||||
directory = Directory(
|
directory = Directory(
|
||||||
|
region,
|
||||||
name,
|
name,
|
||||||
password,
|
password,
|
||||||
"MicrosoftAD",
|
"MicrosoftAD",
|
||||||
subnets,
|
|
||||||
vpc_settings=vpc_settings,
|
vpc_settings=vpc_settings,
|
||||||
short_name=short_name,
|
short_name=short_name,
|
||||||
description=description,
|
description=description,
|
||||||
@ -416,24 +401,14 @@ class DirectoryServiceBackend(BaseBackend):
|
|||||||
def disable_sso(self, directory_id, username=None, password=None):
|
def disable_sso(self, directory_id, username=None, password=None):
|
||||||
"""Disable single-sign on for a directory."""
|
"""Disable single-sign on for a directory."""
|
||||||
self._validate_directory_id(directory_id)
|
self._validate_directory_id(directory_id)
|
||||||
validate_args(
|
validate_args([("ssoPassword", password), ("userName", username)])
|
||||||
[
|
|
||||||
(validate_sso_password, "password", password),
|
|
||||||
(validate_user_name, "userName", username),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
directory = self.directories[directory_id]
|
directory = self.directories[directory_id]
|
||||||
directory.enable_sso(False)
|
directory.enable_sso(False)
|
||||||
|
|
||||||
def enable_sso(self, directory_id, username=None, password=None):
|
def enable_sso(self, directory_id, username=None, password=None):
|
||||||
"""Enable single-sign on for a directory."""
|
"""Enable single-sign on for a directory."""
|
||||||
self._validate_directory_id(directory_id)
|
self._validate_directory_id(directory_id)
|
||||||
validate_args(
|
validate_args([("ssoPassword", password), ("userName", username)])
|
||||||
[
|
|
||||||
(validate_sso_password, "password", password),
|
|
||||||
(validate_user_name, "userName", username),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
directory = self.directories[directory_id]
|
directory = self.directories[directory_id]
|
||||||
if directory.alias == directory_id:
|
if directory.alias == directory_id:
|
||||||
|
@ -11,13 +11,31 @@ def validate_args(validators):
|
|||||||
"""Raise exception if any of the validations fails.
|
"""Raise exception if any of the validations fails.
|
||||||
|
|
||||||
validators is a list of tuples each containing the following:
|
validators is a list of tuples each containing the following:
|
||||||
(validator_function, printable field name, field value)
|
(printable field name, field value)
|
||||||
|
|
||||||
The error messages are accumulated before the exception is raised.
|
The error messages are accumulated before the exception is raised.
|
||||||
"""
|
"""
|
||||||
|
validation_map = {
|
||||||
|
"alias": validate_alias,
|
||||||
|
"description": validate_description,
|
||||||
|
"directoryId": validate_directory_id,
|
||||||
|
"connectSettings.customerDnsIps": validate_dns_ips,
|
||||||
|
"edition": validate_edition,
|
||||||
|
"name": validate_name,
|
||||||
|
"password": validate_password,
|
||||||
|
"shortName": validate_short_name,
|
||||||
|
"size": validate_size,
|
||||||
|
"ssoPassword": validate_sso_password,
|
||||||
|
"connectSettings.vpcSettings.subnetIds": validate_subnet_ids,
|
||||||
|
"connectSettings.customerUserName": validate_user_name,
|
||||||
|
"userName": validate_user_name,
|
||||||
|
"vpcSettings.subnetIds": validate_subnet_ids,
|
||||||
|
}
|
||||||
err_msgs = []
|
err_msgs = []
|
||||||
for (func, fieldname, value) in validators:
|
# This eventually could be a switch (python 3.10), elminating the need
|
||||||
msg = func(value)
|
# for the above map and individual functions.
|
||||||
|
for (fieldname, value) in validators:
|
||||||
|
msg = validation_map[fieldname](value)
|
||||||
if msg:
|
if msg:
|
||||||
err_msgs.append((fieldname, value, msg))
|
err_msgs.append((fieldname, value, msg))
|
||||||
if err_msgs:
|
if err_msgs:
|
||||||
|
@ -116,7 +116,7 @@ def test_ds_describe_directories():
|
|||||||
assert dir_info["Type"] == "SimpleAD"
|
assert dir_info["Type"] == "SimpleAD"
|
||||||
assert dir_info["VpcSettings"]["VpcId"].startswith("vpc-")
|
assert dir_info["VpcSettings"]["VpcId"].startswith("vpc-")
|
||||||
assert len(dir_info["VpcSettings"]["SubnetIds"]) == 2
|
assert len(dir_info["VpcSettings"]["SubnetIds"]) == 2
|
||||||
assert set(dir_info["DnsIpAddrs"]) == set(["10.0.1.1", "10.0.0.1"])
|
assert len(dir_info["DnsIpAddrs"]) == 2
|
||||||
assert "NextToken" not in result
|
assert "NextToken" not in result
|
||||||
|
|
||||||
# Test with a specific directory ID.
|
# Test with a specific directory ID.
|
||||||
@ -250,7 +250,7 @@ def test_ds_enable_sso():
|
|||||||
err = exc.value.response["Error"]
|
err = exc.value.response["Error"]
|
||||||
assert err["Code"] == "ValidationException"
|
assert err["Code"] == "ValidationException"
|
||||||
assert (
|
assert (
|
||||||
"Value at 'password' failed to satisfy constraint: Member must "
|
"Value at 'ssoPassword' failed to satisfy constraint: Member must "
|
||||||
"have length less than or equal to 128"
|
"have length less than or equal to 128"
|
||||||
) in err["Message"]
|
) in err["Message"]
|
||||||
|
|
||||||
@ -295,7 +295,7 @@ def test_ds_disable_sso():
|
|||||||
err = exc.value.response["Error"]
|
err = exc.value.response["Error"]
|
||||||
assert err["Code"] == "ValidationException"
|
assert err["Code"] == "ValidationException"
|
||||||
assert (
|
assert (
|
||||||
"Value at 'password' failed to satisfy constraint: Member must "
|
"Value at 'ssoPassword' failed to satisfy constraint: Member must "
|
||||||
"have length less than or equal to 128"
|
"have length less than or equal to 128"
|
||||||
) in err["Message"]
|
) in err["Message"]
|
||||||
|
|
||||||
|
@ -251,9 +251,7 @@ def test_ds_connect_directory_describe():
|
|||||||
assert directory["ConnectSettings"]["VpcId"].startswith("vpc-")
|
assert directory["ConnectSettings"]["VpcId"].startswith("vpc-")
|
||||||
assert len(directory["ConnectSettings"]["SubnetIds"]) == 2
|
assert len(directory["ConnectSettings"]["SubnetIds"]) == 2
|
||||||
assert directory["ConnectSettings"]["CustomerUserName"] == "Admin"
|
assert directory["ConnectSettings"]["CustomerUserName"] == "Admin"
|
||||||
assert set(directory["ConnectSettings"]["ConnectIps"]) == set(
|
assert len(directory["ConnectSettings"]["ConnectIps"]) == 2
|
||||||
["10.0.0.1", "10.0.1.1"]
|
|
||||||
)
|
|
||||||
assert directory["Size"] == "Small"
|
assert directory["Size"] == "Small"
|
||||||
assert set(directory["DnsIpAddrs"]) == set(["1.2.3.4", "5.6.7.8"])
|
assert set(directory["DnsIpAddrs"]) == set(["1.2.3.4", "5.6.7.8"])
|
||||||
assert "NextToken" not in result
|
assert "NextToken" not in result
|
||||||
|
@ -192,7 +192,7 @@ def test_ds_create_microsoft_ad_describe():
|
|||||||
assert directory["VpcSettings"]["VpcId"].startswith("vpc-")
|
assert directory["VpcSettings"]["VpcId"].startswith("vpc-")
|
||||||
assert len(directory["VpcSettings"]["SubnetIds"]) == 2
|
assert len(directory["VpcSettings"]["SubnetIds"]) == 2
|
||||||
assert directory["Edition"] == "Standard"
|
assert directory["Edition"] == "Standard"
|
||||||
assert set(directory["DnsIpAddrs"]) == set(["10.0.1.1", "10.0.0.1"])
|
assert len(directory["DnsIpAddrs"]) == 2
|
||||||
assert "NextToken" not in result
|
assert "NextToken" not in result
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user