[issue #1720] Add support for AWS Organizations
added exception handling in class OrganizationsBackend
This commit is contained in:
parent
40e422b74d
commit
05928b1497
@ -3147,7 +3147,7 @@
|
|||||||
- [ ] update_server
|
- [ ] update_server
|
||||||
- [ ] update_server_engine_attributes
|
- [ ] update_server_engine_attributes
|
||||||
|
|
||||||
## organizations - 28% implemented
|
## organizations - 30% implemented
|
||||||
- [ ] accept_handshake
|
- [ ] accept_handshake
|
||||||
- [ ] attach_policy
|
- [ ] attach_policy
|
||||||
- [ ] cancel_handshake
|
- [ ] cancel_handshake
|
||||||
@ -3176,7 +3176,7 @@
|
|||||||
- [X] list_accounts
|
- [X] list_accounts
|
||||||
- [X] list_accounts_for_parent
|
- [X] list_accounts_for_parent
|
||||||
- [ ] list_aws_service_access_for_organization
|
- [ ] list_aws_service_access_for_organization
|
||||||
- [ ] list_children
|
- [X] list_children
|
||||||
- [ ] list_create_account_status
|
- [ ] list_create_account_status
|
||||||
- [ ] list_handshakes_for_account
|
- [ ] list_handshakes_for_account
|
||||||
- [ ] list_handshakes_for_organization
|
- [ ] list_handshakes_for_organization
|
||||||
|
@ -112,7 +112,7 @@ It gets even better! Moto isn't just for Python code and it isn't just for S3. L
|
|||||||
|------------------------------------------------------------------------------|
|
|------------------------------------------------------------------------------|
|
||||||
| KMS | @mock_kms | basic endpoints done |
|
| KMS | @mock_kms | basic endpoints done |
|
||||||
|------------------------------------------------------------------------------|
|
|------------------------------------------------------------------------------|
|
||||||
| Organizations | @mock_organizations | some endpoints done |
|
| Organizations | @mock_organizations | some core endpoints done |
|
||||||
|------------------------------------------------------------------------------|
|
|------------------------------------------------------------------------------|
|
||||||
| Polly | @mock_polly | all endpoints done |
|
| Polly | @mock_polly | all endpoints done |
|
||||||
|------------------------------------------------------------------------------|
|
|------------------------------------------------------------------------------|
|
||||||
|
@ -4,6 +4,7 @@ import datetime
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from moto.core import BaseBackend, BaseModel
|
from moto.core import BaseBackend, BaseModel
|
||||||
|
from moto.core.exceptions import RESTError
|
||||||
from moto.core.utils import unix_time
|
from moto.core.utils import unix_time
|
||||||
from moto.organizations import utils
|
from moto.organizations import utils
|
||||||
|
|
||||||
@ -168,13 +169,31 @@ class OrganizationsBackend(BaseBackend):
|
|||||||
self.ou.append(new_ou)
|
self.ou.append(new_ou)
|
||||||
return new_ou.describe()
|
return new_ou.describe()
|
||||||
|
|
||||||
|
def get_organizational_unit_by_id(self, ou_id):
|
||||||
|
ou = next((ou for ou in self.ou if ou.id == ou_id), None)
|
||||||
|
if ou is None:
|
||||||
|
raise RESTError(
|
||||||
|
'OrganizationalUnitNotFoundException',
|
||||||
|
"You specified an organizational unit that doesn't exist."
|
||||||
|
)
|
||||||
|
return ou
|
||||||
|
|
||||||
|
def validate_parent_id(self, parent_id):
|
||||||
|
try:
|
||||||
|
self.get_organizational_unit_by_id(parent_id)
|
||||||
|
except RESTError as e:
|
||||||
|
raise RESTError(
|
||||||
|
'ParentNotFoundException',
|
||||||
|
"You specified parent that doesn't exist."
|
||||||
|
)
|
||||||
|
return parent_id
|
||||||
|
|
||||||
def describe_organizational_unit(self, **kwargs):
|
def describe_organizational_unit(self, **kwargs):
|
||||||
ou = [
|
ou = self.get_organizational_unit_by_id(kwargs['OrganizationalUnitId'])
|
||||||
ou for ou in self.ou if ou.id == kwargs['OrganizationalUnitId']
|
|
||||||
].pop(0)
|
|
||||||
return ou.describe()
|
return ou.describe()
|
||||||
|
|
||||||
def list_organizational_units_for_parent(self, **kwargs):
|
def list_organizational_units_for_parent(self, **kwargs):
|
||||||
|
parent_id = self.validate_parent_id(kwargs['ParentId'])
|
||||||
return dict(
|
return dict(
|
||||||
OrganizationalUnits=[
|
OrganizationalUnits=[
|
||||||
{
|
{
|
||||||
@ -183,7 +202,7 @@ class OrganizationsBackend(BaseBackend):
|
|||||||
'Name': ou.name,
|
'Name': ou.name,
|
||||||
}
|
}
|
||||||
for ou in self.ou
|
for ou in self.ou
|
||||||
if ou.parent_id == kwargs['ParentId']
|
if ou.parent_id == parent_id
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -192,11 +211,20 @@ class OrganizationsBackend(BaseBackend):
|
|||||||
self.accounts.append(new_account)
|
self.accounts.append(new_account)
|
||||||
return new_account.create_account_status
|
return new_account.create_account_status
|
||||||
|
|
||||||
def describe_account(self, **kwargs):
|
def get_account_by_id(self, account_id):
|
||||||
account = [
|
account = next((
|
||||||
account for account in self.accounts
|
account for account in self.accounts
|
||||||
if account.id == kwargs['AccountId']
|
if account.id == account_id
|
||||||
].pop(0)
|
), None)
|
||||||
|
if account is None:
|
||||||
|
raise RESTError(
|
||||||
|
'AccountNotFoundException',
|
||||||
|
"You specified an account that doesn't exist."
|
||||||
|
)
|
||||||
|
return account
|
||||||
|
|
||||||
|
def describe_account(self, **kwargs):
|
||||||
|
account = self.get_account_by_id(kwargs['AccountId'])
|
||||||
return account.describe()
|
return account.describe()
|
||||||
|
|
||||||
def list_accounts(self):
|
def list_accounts(self):
|
||||||
@ -205,35 +233,27 @@ class OrganizationsBackend(BaseBackend):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def list_accounts_for_parent(self, **kwargs):
|
def list_accounts_for_parent(self, **kwargs):
|
||||||
|
parent_id = self.validate_parent_id(kwargs['ParentId'])
|
||||||
return dict(
|
return dict(
|
||||||
Accounts=[
|
Accounts=[
|
||||||
account.describe()['Account']
|
account.describe()['Account']
|
||||||
for account in self.accounts
|
for account in self.accounts
|
||||||
if account.parent_id == kwargs['ParentId']
|
if account.parent_id == parent_id
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def move_account(self, **kwargs):
|
def move_account(self, **kwargs):
|
||||||
new_parent_id = kwargs['DestinationParentId']
|
new_parent_id = self.validate_parent_id(kwargs['DestinationParentId'])
|
||||||
all_parent_id = [parent.id for parent in self.ou]
|
self.validate_parent_id(kwargs['SourceParentId'])
|
||||||
account = [
|
account = self.get_account_by_id(kwargs['AccountId'])
|
||||||
account for account in self.accounts
|
|
||||||
if account.id == kwargs['AccountId']
|
|
||||||
].pop(0)
|
|
||||||
assert new_parent_id in all_parent_id
|
|
||||||
assert account.parent_id == kwargs['SourceParentId']
|
|
||||||
index = self.accounts.index(account)
|
index = self.accounts.index(account)
|
||||||
self.accounts[index].parent_id = new_parent_id
|
self.accounts[index].parent_id = new_parent_id
|
||||||
|
|
||||||
def list_parents(self, **kwargs):
|
def list_parents(self, **kwargs):
|
||||||
if re.compile(r'[0-9]{12}').match(kwargs['ChildId']):
|
if re.compile(r'[0-9]{12}').match(kwargs['ChildId']):
|
||||||
obj_list = self.accounts
|
child_object = self.get_account_by_id(kwargs['ChildId'])
|
||||||
else:
|
else:
|
||||||
obj_list = self.ou
|
child_object = self.get_organizational_unit_by_id(kwargs['ChildId'])
|
||||||
parent_id = [
|
|
||||||
obj.parent_id for obj in obj_list
|
|
||||||
if obj.id == kwargs['ChildId']
|
|
||||||
].pop(0)
|
|
||||||
return dict(
|
return dict(
|
||||||
Parents=[
|
Parents=[
|
||||||
{
|
{
|
||||||
@ -241,17 +261,21 @@ class OrganizationsBackend(BaseBackend):
|
|||||||
'Type': ou.type,
|
'Type': ou.type,
|
||||||
}
|
}
|
||||||
for ou in self.ou
|
for ou in self.ou
|
||||||
if ou.id == parent_id
|
if ou.id == child_object.parent_id
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def list_children(self, **kwargs):
|
def list_children(self, **kwargs):
|
||||||
|
parent_id = self.validate_parent_id(kwargs['ParentId'])
|
||||||
if kwargs['ChildType'] == 'ACCOUNT':
|
if kwargs['ChildType'] == 'ACCOUNT':
|
||||||
obj_list = self.accounts
|
obj_list = self.accounts
|
||||||
elif kwargs['ChildType'] == 'ORGANIZATIONAL_UNIT':
|
elif kwargs['ChildType'] == 'ORGANIZATIONAL_UNIT':
|
||||||
obj_list = self.ou
|
obj_list = self.ou
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise RESTError(
|
||||||
|
'InvalidInputException',
|
||||||
|
'You specified an invalid value.'
|
||||||
|
)
|
||||||
return dict(
|
return dict(
|
||||||
Children=[
|
Children=[
|
||||||
{
|
{
|
||||||
@ -259,7 +283,7 @@ class OrganizationsBackend(BaseBackend):
|
|||||||
'Type': kwargs['ChildType'],
|
'Type': kwargs['ChildType'],
|
||||||
}
|
}
|
||||||
for obj in obj_list
|
for obj in obj_list
|
||||||
if obj.parent_id == kwargs['ParentId']
|
if obj.parent_id == parent_id
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,9 +2,11 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import boto3
|
import boto3
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
import yaml
|
from botocore.exceptions import ClientError
|
||||||
|
from nose.tools import assert_raises
|
||||||
|
|
||||||
from moto import mock_organizations
|
from moto import mock_organizations
|
||||||
|
from moto.organizations import utils
|
||||||
from .organizations_test_utils import (
|
from .organizations_test_utils import (
|
||||||
validate_organization,
|
validate_organization,
|
||||||
validate_roots,
|
validate_roots,
|
||||||
@ -67,6 +69,20 @@ def test_describe_organizational_unit():
|
|||||||
validate_organizational_unit(org, response)
|
validate_organizational_unit(org, response)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_organizations
|
||||||
|
def test_describe_organizational_unit_exception():
|
||||||
|
client = boto3.client('organizations', region_name='us-east-1')
|
||||||
|
org = client.create_organization(FeatureSet='ALL')['Organization']
|
||||||
|
with assert_raises(ClientError) as e:
|
||||||
|
response = client.describe_organizational_unit(
|
||||||
|
OrganizationalUnitId=utils.make_random_root_id()
|
||||||
|
)
|
||||||
|
ex = e.exception
|
||||||
|
ex.operation_name.should.equal('DescribeOrganizationalUnit')
|
||||||
|
ex.response['Error']['Code'].should.equal('400')
|
||||||
|
ex.response['Error']['Message'].should.contain('OrganizationalUnitNotFoundException')
|
||||||
|
|
||||||
|
|
||||||
@mock_organizations
|
@mock_organizations
|
||||||
def test_list_organizational_units_for_parent():
|
def test_list_organizational_units_for_parent():
|
||||||
client = boto3.client('organizations', region_name='us-east-1')
|
client = boto3.client('organizations', region_name='us-east-1')
|
||||||
@ -81,6 +97,19 @@ def test_list_organizational_units_for_parent():
|
|||||||
validate_organizational_unit(org, dict(OrganizationalUnit=ou))
|
validate_organizational_unit(org, dict(OrganizationalUnit=ou))
|
||||||
|
|
||||||
|
|
||||||
|
@mock_organizations
|
||||||
|
def test_list_organizational_units_for_parent_exception():
|
||||||
|
client = boto3.client('organizations', region_name='us-east-1')
|
||||||
|
with assert_raises(ClientError) as e:
|
||||||
|
response = client.list_organizational_units_for_parent(
|
||||||
|
ParentId=utils.make_random_root_id()
|
||||||
|
)
|
||||||
|
ex = e.exception
|
||||||
|
ex.operation_name.should.equal('ListOrganizationalUnitsForParent')
|
||||||
|
ex.response['Error']['Code'].should.equal('400')
|
||||||
|
ex.response['Error']['Message'].should.contain('ParentNotFoundException')
|
||||||
|
|
||||||
|
|
||||||
# Accounts
|
# Accounts
|
||||||
mockname = 'mock-account'
|
mockname = 'mock-account'
|
||||||
mockdomain = 'moto-example.org'
|
mockdomain = 'moto-example.org'
|
||||||
@ -111,6 +140,17 @@ def test_describe_account():
|
|||||||
response['Account']['Email'].should.equal(mockemail)
|
response['Account']['Email'].should.equal(mockemail)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_organizations
|
||||||
|
def test_describe_account_exception():
|
||||||
|
client = boto3.client('organizations', region_name='us-east-1')
|
||||||
|
with assert_raises(ClientError) as e:
|
||||||
|
response = client.describe_account(AccountId=utils.make_random_account_id())
|
||||||
|
ex = e.exception
|
||||||
|
ex.operation_name.should.equal('DescribeAccount')
|
||||||
|
ex.response['Error']['Code'].should.equal('400')
|
||||||
|
ex.response['Error']['Message'].should.contain('AccountNotFoundException')
|
||||||
|
|
||||||
|
|
||||||
@mock_organizations
|
@mock_organizations
|
||||||
def test_list_accounts():
|
def test_list_accounts():
|
||||||
client = boto3.client('organizations', region_name='us-east-1')
|
client = boto3.client('organizations', region_name='us-east-1')
|
||||||
@ -211,7 +251,7 @@ def test_list_parents_for_accounts():
|
|||||||
|
|
||||||
|
|
||||||
@mock_organizations
|
@mock_organizations
|
||||||
def test_list_chidlren():
|
def test_list_children():
|
||||||
client = boto3.client('organizations', region_name='us-east-1')
|
client = boto3.client('organizations', region_name='us-east-1')
|
||||||
org = client.create_organization(FeatureSet='ALL')['Organization']
|
org = client.create_organization(FeatureSet='ALL')['Organization']
|
||||||
root_id = client.list_roots()['Roots'][0]['Id']
|
root_id = client.list_roots()['Roots'][0]['Id']
|
||||||
@ -244,3 +284,28 @@ def test_list_chidlren():
|
|||||||
response03['Children'][0]['Type'].should.equal('ACCOUNT')
|
response03['Children'][0]['Type'].should.equal('ACCOUNT')
|
||||||
response04['Children'][0]['Id'].should.equal(ou02_id)
|
response04['Children'][0]['Id'].should.equal(ou02_id)
|
||||||
response04['Children'][0]['Type'].should.equal('ORGANIZATIONAL_UNIT')
|
response04['Children'][0]['Type'].should.equal('ORGANIZATIONAL_UNIT')
|
||||||
|
|
||||||
|
|
||||||
|
@mock_organizations
|
||||||
|
def test_list_children_exception():
|
||||||
|
client = boto3.client('organizations', region_name='us-east-1')
|
||||||
|
org = client.create_organization(FeatureSet='ALL')['Organization']
|
||||||
|
root_id = client.list_roots()['Roots'][0]['Id']
|
||||||
|
with assert_raises(ClientError) as e:
|
||||||
|
response = client.list_children(
|
||||||
|
ParentId=utils.make_random_root_id(),
|
||||||
|
ChildType='ACCOUNT'
|
||||||
|
)
|
||||||
|
ex = e.exception
|
||||||
|
ex.operation_name.should.equal('ListChildren')
|
||||||
|
ex.response['Error']['Code'].should.equal('400')
|
||||||
|
ex.response['Error']['Message'].should.contain('ParentNotFoundException')
|
||||||
|
with assert_raises(ClientError) as e:
|
||||||
|
response = client.list_children(
|
||||||
|
ParentId=root_id,
|
||||||
|
ChildType='BLEE'
|
||||||
|
)
|
||||||
|
ex = e.exception
|
||||||
|
ex.operation_name.should.equal('ListChildren')
|
||||||
|
ex.response['Error']['Code'].should.equal('400')
|
||||||
|
ex.response['Error']['Message'].should.contain('InvalidInputException')
|
||||||
|
Loading…
Reference in New Issue
Block a user