diff --git a/moto/organizations/models.py b/moto/organizations/models.py index 8a00918d1..609ce3831 100644 --- a/moto/organizations/models.py +++ b/moto/organizations/models.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import datetime +import re from moto.core import BaseBackend, BaseModel from moto.core.utils import unix_time @@ -19,6 +20,7 @@ class FakeOrganization(BaseModel): def __init__(self, feature_set): self.id = utils.make_random_org_id() + self.root_id = utils.make_random_root_id() self.feature_set = feature_set self.master_account_id = MASTER_ACCOUNT_ID self.master_account_email = MASTER_ACCOUNT_EMAIL @@ -51,7 +53,7 @@ class FakeOrganization(BaseModel): class FakeAccount(BaseModel): - def __init__(self, organization, root_id, **kwargs): + def __init__(self, organization, **kwargs): self.organization_id = organization.id self.master_account_id = organization.master_account_id self.create_account_status_id = utils.make_random_create_account_status_id() @@ -61,7 +63,7 @@ class FakeAccount(BaseModel): self.create_time = datetime.datetime.utcnow() self.status = 'ACTIVE' self.joined_method = 'CREATED' - self.parent_id = root_id + self.parent_id = organization.root_id @property def arn(self): @@ -100,16 +102,18 @@ class FakeAccount(BaseModel): class FakeOrganizationalUnit(BaseModel): - def __init__(self, organization, root_id, **kwargs): + def __init__(self, organization, **kwargs): + self.type = 'ORGANIZATIONAL_UNIT' self.organization_id = organization.id self.master_account_id = organization.master_account_id - self.id = utils.make_random_ou_id(root_id) - self.name = kwargs['Name'] - self.parent_id = kwargs['ParentId'] + self.id = utils.make_random_ou_id(organization.root_id) + self.name = kwargs.get('Name') + self.parent_id = kwargs.get('ParentId') + self._arn_format = OU_ARN_FORMAT @property def arn(self): - return OU_ARN_FORMAT.format( + return self._arn_format.format( self.master_account_id, self.organization_id, self.id @@ -125,25 +129,18 @@ class FakeOrganizationalUnit(BaseModel): } -class FakeRoot(BaseModel): +class FakeRoot(FakeOrganizationalUnit): def __init__(self, organization, **kwargs): - self.organization_id = organization.id - self.master_account_id = organization.master_account_id - self.id = utils.make_random_root_id() + super().__init__(organization, **kwargs) + self.type = 'ROOT' + self.id = organization.root_id self.name = 'Root' self.policy_types = [{ 'Type': 'SERVICE_CONTROL_POLICY', 'Status': 'ENABLED' }] - - @property - def arn(self): - return ROOT_ARN_FORMAT.format( - self.master_account_id, - self.organization_id, - self.id - ) + self._arn_format = ROOT_ARN_FORMAT def describe(self): return { @@ -154,18 +151,16 @@ class FakeRoot(BaseModel): } - class OrganizationsBackend(BaseBackend): def __init__(self): self.org = None self.accounts = [] - self.roots = [] self.ou = [] def create_organization(self, **kwargs): self.org = FakeOrganization(kwargs['FeatureSet']) - self.roots.append(FakeRoot(self.org)) + self.ou.append(FakeRoot(self.org)) return self.org.describe() def describe_organization(self): @@ -173,11 +168,11 @@ class OrganizationsBackend(BaseBackend): def list_roots(self): return dict( - Roots=[root.describe() for root in self.roots] + Roots=[ou.describe() for ou in self.ou if isinstance(ou, FakeRoot)] ) def create_organizational_unit(self, **kwargs): - new_ou = FakeOrganizationalUnit(self.org, self.roots[0].id, **kwargs) + new_ou = FakeOrganizationalUnit(self.org, **kwargs) self.ou.append(new_ou) return new_ou.describe() @@ -201,23 +196,29 @@ class OrganizationsBackend(BaseBackend): ) def list_parents(self, **kwargs): - parent_id = [ - ou.parent_id for ou in self.ou if ou.id == kwargs['ChildId'] - ].pop(0) - root_parents = [ - dict(Id=root.id, Type='ROOT') - for root in self.roots - if root.id == parent_id - ] - ou_parents = [ - dict(Id=ou.id, Type='ORGANIZATIONAL_UNIT') - for ou in self.ou - if ou.id == parent_id - ] - return dict(Parents=root_parents + ou_parents) + if re.compile(r'[0-9]{12}').match(kwargs['ChildId']): + parent_id = [ + account.parent_id for account in self.accounts + if account.id == kwargs['ChildId'] + ].pop(0) + else: + parent_id = [ + ou.parent_id for ou in self.ou + if ou.id == kwargs['ChildId'] + ].pop(0) + return dict( + Parents=[ + { + 'Id': ou.id, + 'Type': ou.type, + } + for ou in self.ou + if ou.id == parent_id + ] + ) def create_account(self, **kwargs): - new_account = FakeAccount(self.org, self.roots[0].id, **kwargs) + new_account = FakeAccount(self.org, **kwargs) self.accounts.append(new_account) return new_account.create_account_status @@ -244,9 +245,10 @@ class OrganizationsBackend(BaseBackend): def move_account(self, **kwargs): new_parent_id = kwargs['DestinationParentId'] - all_parent_id = [parent.id for parent in self.roots + self.ou] + all_parent_id = [parent.id for parent in self.ou] account = [ - account for account in self.accounts if account.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'] diff --git a/tests/test_organizations/test_organizations_boto3.py b/tests/test_organizations/test_organizations_boto3.py index 5355e2716..2b692489f 100644 --- a/tests/test_organizations/test_organizations_boto3.py +++ b/tests/test_organizations/test_organizations_boto3.py @@ -198,30 +198,6 @@ def test_list_organizational_units_for_parent(): #assert False -@mock_organizations -def test_list_parents(): - client = boto3.client('organizations', region_name='us-east-1') - org = client.create_organization(FeatureSet='ALL')['Organization'] - root_id = client.list_roots()['Roots'][0]['Id'] - - ou01 = client.create_organizational_unit(ParentId=root_id, Name='ou01') - ou01_id = ou01['OrganizationalUnit']['Id'] - response01 = client.list_parents(ChildId=ou01_id) - #print(yaml.dump(response01, default_flow_style=False)) - response01.should.have.key('Parents').should.be.a(list) - response01['Parents'][0].should.have.key('Id').should.equal(root_id) - response01['Parents'][0].should.have.key('Type').should.equal('ROOT') - - ou02 = client.create_organizational_unit(ParentId=ou01_id, Name='ou02') - ou02_id = ou02['OrganizationalUnit']['Id'] - response02 = client.list_parents(ChildId=ou02_id) - #print(yaml.dump(response02, default_flow_style=False)) - response02.should.have.key('Parents').should.be.a(list) - response02['Parents'][0].should.have.key('Id').should.equal(ou01_id) - response02['Parents'][0].should.have.key('Type').should.equal('ORGANIZATIONAL_UNIT') - #assert False - - # Accounts mockname = 'mock-account' mockdomain = 'moto-example.org' @@ -310,3 +286,58 @@ def test_move_account(): #print(yaml.dump(response, default_flow_style=False)) account_id.should.be.within([account['Id'] for account in response['Accounts']]) #assert False + + +@mock_organizations +def test_list_parents_for_ou(): + client = boto3.client('organizations', region_name='us-east-1') + org = client.create_organization(FeatureSet='ALL')['Organization'] + root_id = client.list_roots()['Roots'][0]['Id'] + ou01 = client.create_organizational_unit(ParentId=root_id, Name='ou01') + ou01_id = ou01['OrganizationalUnit']['Id'] + response01 = client.list_parents(ChildId=ou01_id) + #print(yaml.dump(response01, default_flow_style=False)) + response01.should.have.key('Parents').should.be.a(list) + response01['Parents'][0].should.have.key('Id').should.equal(root_id) + response01['Parents'][0].should.have.key('Type').should.equal('ROOT') + ou02 = client.create_organizational_unit(ParentId=ou01_id, Name='ou02') + ou02_id = ou02['OrganizationalUnit']['Id'] + response02 = client.list_parents(ChildId=ou02_id) + #print(yaml.dump(response02, default_flow_style=False)) + response02.should.have.key('Parents').should.be.a(list) + response02['Parents'][0].should.have.key('Id').should.equal(ou01_id) + response02['Parents'][0].should.have.key('Type').should.equal('ORGANIZATIONAL_UNIT') + #assert False + + +@mock_organizations +def test_list_parents_for_accounts(): + client = boto3.client('organizations', region_name='us-east-1') + org = client.create_organization(FeatureSet='ALL')['Organization'] + root_id = client.list_roots()['Roots'][0]['Id'] + ou01 = client.create_organizational_unit(ParentId=root_id, Name='ou01') + ou01_id = ou01['OrganizationalUnit']['Id'] + account01_id = client.create_account( + AccountName='account01', + Email='account01@moto-example.org' + )['CreateAccountStatus']['AccountId'] + account02_id = client.create_account( + AccountName='account02', + Email='account02@moto-example.org' + )['CreateAccountStatus']['AccountId'] + client.move_account( + AccountId=account02_id, + SourceParentId=root_id, + DestinationParentId=ou01_id, + ) + response01 = client.list_parents(ChildId=account01_id) + #print(yaml.dump(response01, default_flow_style=False)) + response01.should.have.key('Parents').should.be.a(list) + response01['Parents'][0].should.have.key('Id').should.equal(root_id) + response01['Parents'][0].should.have.key('Type').should.equal('ROOT') + response02 = client.list_parents(ChildId=account02_id) + #print(yaml.dump(response02, default_flow_style=False)) + response02.should.have.key('Parents').should.be.a(list) + response02['Parents'][0].should.have.key('Id').should.equal(ou01_id) + response02['Parents'][0].should.have.key('Type').should.equal('ORGANIZATIONAL_UNIT') + #assert False