def test_upsert_policy_update(self): ''' Tests OrganizationService.upsert_policy when the action is update ''' action = {'action': 'update'} updated_policies_mock = { "policy_a": { "id": "p-a", "name": "policy_a", "description": "policy_a description", "document": {"content": {"foo": "bar"}}}} policies_mock = { "policy_a": { "name": "policy_a", "description": "policy_a description", "document": {"content": {"foo": "bar"}}}} expected_update_policy_params = { "Content": '{"foo": "bar"}', "Description": "policy_a description", "Name": "policy_a", "PolicyId": "p-a"} update_policy_response = { "Policy": {"PolicySummary": {"Id": "p-a"}}} expected_changes = {"change": "updated", "id": "p-a"} self.stubber.add_response("update_policy", update_policy_response, expected_update_policy_params) updated_org_mock = self._get_mock_org(policies=updated_policies_mock) org_mock = self._get_mock_org(updated_model=updated_org_mock, policies=policies_mock) org_service = self._get_org_service() changes = org_service.upsert_policy( organization=org_mock, policy_name="policy_a", action=action) helpers.print_expected_actual_diff(expected_changes, changes) assert changes == expected_changes
def test_dry_run(self, initialize_mock, raise_mock): ''' Test Organization.dry_run ''' provisioner_overrides = {"profile": "foo"} provisioner = {"role": "bar"} expected_provisioner = {"profile": "foo", "role": "bar"} expected_report = {"actions": {}} with mock.patch( 'cumulogenesis.models.aws_entities.Organization.validate' ) as validate_mock: validate_mock.return_value = {"organization": "some_problem"} expected_compare_report_arg = { "aws_model_problems": { "organization": "some_problem" } } with mock.patch( 'cumulogenesis.models.aws_entities.Organization.compare_against_aws_model' ) as compare_mock: compare_mock.return_value = {"actions": {}} org_mock = self._get_base_organization() org_mock.aws_model = self._get_base_organization() org_mock.provisioner = provisioner report = org_mock.dry_run( provisioner_overrides=provisioner_overrides) helpers.print_expected_actual_diff(expected_report, report) compare_mock.assert_called_with( report=expected_compare_report_arg) assert expected_report == report helpers.print_expected_actual_diff(expected_provisioner, provisioner) assert org_mock.provisioner == expected_provisioner
def test_load_policies(self): ''' Tests OrganizationService.load_policies ''' list_policies_response = { "Policies": [ {"Id": "p-123456", "Name": "PolicyOne", "Description": "The first policy", "AwsManaged": True}]} describe_policy_response = { "Policy": { "Content": '{"This": "is a mock JSON document"}'}} #pylint: disable=invalid-name list_targets_for_policy_response = { "Targets": [ {"Type": "Mock"}]} self.stubber.add_response('list_policies', list_policies_response) self.stubber.add_response('describe_policy', describe_policy_response) self.stubber.add_response('list_targets_for_policy', list_targets_for_policy_response) expected_document_content = OrderedDict({"This": "is a mock JSON document"}) expected_policies = { "PolicyOne": {"id": "p-123456", "description": "The first policy", "aws_managed": True, "name": "PolicyOne", "document": {"content": expected_document_content}}} org_mock = self._get_mock_org(policies={}) with mock.patch.object(OrganizationService, "_add_policy_to_target") as policy_to_target_mock: org_service = self._get_org_service() org_service.load_policies(organization=org_mock) policy_to_target_mock.assert_called_with( org_model=org_mock, target=list_targets_for_policy_response['Targets'][0], policy_name="PolicyOne") helpers.print_expected_actual_diff(expected_policies, org_mock.policies) assert expected_policies == org_mock.policies
def test_move_account_orgunit(self): ''' Tests OrganizationService.move_account when the parent_name is an orgunit (not "root") ''' updated_accounts_mock = {"account_a": {"id": "987654321"}} updated_orgunits_mock = {"orgunit_a": {"id": "ou-654321"}} expected_changes = {"changes": "reassociated", "parent": "ou-654321"} list_parents_response = { "Parents": [{"Id": "ou-123456"}]} expected_list_parents_params = {"ChildId": "987654321"} expected_move_account_params = { "AccountId": "987654321", "SourceParentId": "ou-123456", "DestinationParentId": "ou-654321"} self.stubber.add_response("list_parents", list_parents_response, expected_list_parents_params) self.stubber.add_response("move_account", {}, expected_move_account_params) updated_org_mock = self._get_mock_org(accounts=updated_accounts_mock, orgunits=updated_orgunits_mock) aws_org_mock = self._get_mock_org() org_mock = self._get_mock_org(aws_model=aws_org_mock, updated_model=updated_org_mock) org_service = self._get_org_service() changes = org_service.move_account(organization=org_mock, account_name="account_a", parent_name="orgunit_a") helpers.print_expected_actual_diff(expected_changes, changes) assert changes == expected_changes
def test_get_orgunit_hierarchy_valid(self): ''' Test Organization._get_orgunit_hierarchy when no orphans When the Organization contains no orphans, a nested dict representing the Organizations's orgunit and account hierarchy should be returned that shouldn't contain the ORPHANED root key ''' accounts_mock = { "account_a": { "parent_references": ["ou_a"] }, "account_b": { "parent_references": ["ou_b"] }, "account_c": { "parent_references": ["ou_c"] } } orgunits_mock = { "ou_a": { "child_orgunits": ["ou_b"], "accounts": ["account_a"], "parent_references": [] }, "ou_b": { "child_orgunits": [], "accounts": ["account_b"], "parent_references": [] }, "ou_c": { "child_orgunits": [], "accounts": ["account_c"], "parent_references": [] } } org = self._get_base_organization() org.orgunits = self._add_name_to_entity_mocks(orgunits_mock) org.accounts = self._add_name_to_entity_mocks(accounts_mock) expected_hierarchy = { "ROOT_ACCOUNT": { "orgunits": { "ou_a": { "accounts": ["account_a"], "orgunits": { "ou_b": { "accounts": ["account_b"] } } }, "ou_c": { "accounts": ["account_c"] } } } } hierarchy = org.get_orgunit_hierarchy() helpers.print_expected_actual_diff(expected_hierarchy, hierarchy) assert hierarchy == expected_hierarchy
def test_get_orgunit_hierarchy_invalid_orphan(self): ''' Test Organization._get_orgunit_hierarchy when orphans When the Organization contains orphans, the resulting hierarchy should contain an top level ORPHANED key containing the orphaned account. ''' accounts_mock = { "account_a": { "parent_references": ["ou_a"] }, "account_b": { "parent_references": ["ou_b"] }, "account_c": { "parent_references": [] } } orgunits_mock = { "ou_a": { "child_orgunits": ["ou_b"], "accounts": ["account_a"], "parent_references": [] }, "ou_b": { "child_orgunits": [], "accounts": ["account_b"], "parent_references": [] }, "ou_c": { "child_orgunits": [], "accounts": [], "parent_references": [] } } org = self._get_base_organization() org.orgunits = self._add_name_to_entity_mocks(orgunits_mock) org.accounts = self._add_name_to_entity_mocks(accounts_mock) expected_hierarchy = { "ORPHANED_ACCOUNTS": ["account_c"], "ROOT_ACCOUNT": { "orgunits": { "ou_a": { "accounts": ["account_a"], "orgunits": { "ou_b": { "accounts": ["account_b"] } } }, "ou_c": {} } } } hierarchy = org.get_orgunit_hierarchy() helpers.print_expected_actual_diff(expected_hierarchy, hierarchy) assert hierarchy == expected_hierarchy
def test_upsert_organization_exists(self): ''' Tests OrganizationService.upsert_organization when the action is not create ''' actions = {'organization': {'action': 'update'}} expected_changes = {} org_mock = self._get_mock_org(featureset='ALL', root_parent_id=None) org_service = self._get_org_service() changes = org_service.upsert_organization(organization=org_mock, actions=actions) helpers.print_expected_actual_diff(expected_changes, changes) assert expected_changes == changes
def test_add_policy_to_target_root(self): ''' Tests OrganizationService._add_policy_to_target when the target type is ROOT ''' mock_policy_name = "SomePolicy" target_mock = {"Type": "ROOT"} expected_policies = ["SomePolicy"] org_mock = self._get_mock_org(root_policies=[]) org_service = self._get_org_service() org_service._add_policy_to_target(org_model=org_mock, target=target_mock, policy_name=mock_policy_name) helpers.print_expected_actual_diff(expected_policies, org_mock.root_policies) assert org_mock.root_policies == expected_policies
def test_create_accounts(self): ''' Tests OrganizationService.create_accounts Tests one each of a create account result status where the state is SUCCEEDED, FAILED, or an unknown status. ''' account_a_response = { "CreateAccountStatus": { "Id": "req-123456", "AccountName": "account_a", "State": "IN_PROGRESS"}} account_b_response = { "CreateAccountStatus": { "Id": "req-654321", "AccountName": "account_b", "State": "IN_PROGRESS"}} account_c_response = { "CreateAccountStatus": { "Id": "req-456789", "AccountName": "account_c", "State": "IN_PROGRESS"}} account_a_expected_params = { "AccountName": "account_a", "Email": "*****@*****.**"} account_b_expected_params = { "AccountName": "account_b", "Email": "*****@*****.**"} account_c_expected_params = { "AccountName": "account_c", "Email": "*****@*****.**"} self.stubber.add_response('create_account', account_a_response, account_a_expected_params) self.stubber.add_response('create_account', account_b_response, account_b_expected_params) self.stubber.add_response('create_account', account_c_response, account_c_expected_params) accounts_mock = { "account_a": {"owner": "*****@*****.**", "name": "account_a"}, "account_b": {"owner": "*****@*****.**", "name": "account_b"}, "account_c": {"owner": "*****@*****.**", "name": "account_c"}} accounts_to_create = ['account_a', 'account_b', 'account_c'] expected_changes = { "account_a": {"change": "created"}, "account_b": {"change": "failed"}, "account_c": {"change": "unknown"}} # self is just provided here as the method being mocked is an instance method #pylint: disable=unused-argument def _new_wait_on_account_creation(self, creation_statuses): waiter_response = { "account_a": {"State": "SUCCEEDED"}, "account_b": {"State": "FAILED"}, "account_c": {"State": "ERROR"}} for account in creation_statuses: creation_statuses[account] = waiter_response[account] with mock.patch.object(OrganizationService, "_wait_on_account_creation", new=_new_wait_on_account_creation): org_mock = self._get_mock_org(accounts=accounts_mock) org_service = self._get_org_service() changes = org_service.create_accounts(organization=org_mock, accounts=accounts_to_create) helpers.print_expected_actual_diff(expected_changes, changes) assert changes == expected_changes
def test_add_policy_to_target_orgunit(self): ''' Tests OrganizationService._add_policy_to_target when the target type is ORGANIZATIONAL_UNIT ''' mock_policy_name = "SomePolicy" target_mock = {"Type": "ORGANIZATIONAL_UNIT", "Name": "orgunit_a"} mock_orgunits = {"orgunit_a": {}} expected_orgunits = {"orgunit_a": {"policies": [mock_policy_name]}} org_mock = self._get_mock_org(orgunits=mock_orgunits) org_service = self._get_org_service() org_service._add_policy_to_target(org_model=org_mock, target=target_mock, policy_name=mock_policy_name) helpers.print_expected_actual_diff(expected_orgunits, org_mock.orgunits) assert expected_orgunits == org_mock.orgunits
def test_add_policy_to_target_account(self): ''' Tests OrganizationService._add_policy_to_target when the target type is ACCOUNT ''' mock_policy_name = "SomePolicy" target_mock = {"Type": "ACCOUNT", "Name": "account_a"} mock_accounts = {"account_a": {}} expected_accounts = {"account_a": {"policies": [mock_policy_name]}} org_mock = self._get_mock_org(accounts=mock_accounts) org_service = self._get_org_service() org_service._add_policy_to_target(org_model=org_mock, target=target_mock, policy_name=mock_policy_name) helpers.print_expected_actual_diff(expected_accounts, org_mock.accounts) assert expected_accounts == org_mock.accounts
def test_delete_policy(self): ''' Tests OrganizationService.delete_policy ''' aws_policies_mock = { "policy_a": {"id": "p-a"}} expected_delete_policy_params = {"PolicyId": "p-a"} expected_changes = {"change": "deleted", "id": "p-a"} self.stubber.add_response("delete_policy", {}, expected_delete_policy_params) aws_org_mock = self._get_mock_org(policies=aws_policies_mock) org_mock = self._get_mock_org(aws_model=aws_org_mock) org_service = self._get_org_service() changes = org_service.delete_policy(organization=org_mock, policy_name="policy_a") helpers.print_expected_actual_diff(expected_changes, changes) assert changes == expected_changes
def test_delete_orgunit_error(self): ''' Tests OrganizationService.delete_orgunit When a client error is raised when deleting the Orgunit, the method should return None, with the assumption that the Orgunit doesn't exist. ''' self.stubber.add_client_error('delete_organizational_unit') orgunit_mock = {"orgunit_a": {"id": "ou-123456"}} aws_org_mock = self._get_mock_org(orgunits=orgunit_mock) org_mock = self._get_mock_org(aws_model=aws_org_mock) org_service = self._get_org_service() result = org_service.delete_orgunit(organization=org_mock, orgunit_name="orgunit_a") helpers.print_expected_actual_diff(None, result) assert result is None
def test_delete_orgunit(self): ''' Tests OrganizationService.delete_orgunit ''' expected_delete_orgunit_params = { "OrganizationalUnitId": "ou-123456"} self.stubber.add_response('delete_organizational_unit', {}, expected_delete_orgunit_params) orgunit_mock = {"orgunit_a": {"id": "ou-123456"}} expected_changes = {"change": "deleted", "id": "ou-123456"} aws_org_mock = self._get_mock_org(orgunits=orgunit_mock) org_mock = self._get_mock_org(aws_model=aws_org_mock) org_service = self._get_org_service() changes = org_service.delete_orgunit(organization=org_mock, orgunit_name="orgunit_a") helpers.print_expected_actual_diff(expected_changes, changes) assert expected_changes == changes
def test_load_orgunits(self): ''' Tests OrganizationService.load_orgunits This tests the loading of orgunits for an effective organization hierarchy of: r-1234: orgunits: ou-123456789: accounts: - 123456789 orgunits: ou-987654321: {} ''' ids_to_children_mock = { "r-1234": {"orgunits": ["ou-123456789"], "accounts": []}, "ou-123456789": {"orgunits": ["ou-987654321"], "accounts": ["123456789"]}, "ou-987654321": {"orgunits": [], "accounts": []}} desc_ou_response1 = { "OrganizationalUnit": { "Id": "ou-123456789", "Name": "orgunit_a"}} desc_ou_response2 = { "OrganizationalUnit": { "Id": "ou-987654321", "Name": "orgunit_b"}} for response in [desc_ou_response1, desc_ou_response2]: self.stubber.add_response('describe_organizational_unit', response) account_ids_to_names_mock = {"123456789": "account_a"} orgunit_ids_to_names_mock = { "ou-123456789": "orgunit_a", "ou-987654321": "orgunit_b"} expected_orgunits = { "orgunit_a": { "id": "ou-123456789", "name": "orgunit_a", "child_orgunits": ["orgunit_b"], "accounts": ["account_a"]}, "orgunit_b": { "id": "ou-987654321", "name": "orgunit_b", "child_orgunits": [], "accounts": []}} org_mock = self._get_mock_org(ids_to_children=ids_to_children_mock, account_ids_to_names=account_ids_to_names_mock, orgunit_ids_to_names=orgunit_ids_to_names_mock, root_parent_id="r-1234", orgunits={}) org_service = self._get_org_service() org_service.load_orgunits(organization=org_mock) helpers.print_expected_actual_diff(expected_orgunits, org_mock.orgunits) assert expected_orgunits == org_mock.orgunits
def test_move_account_no_change(self): ''' Tests OrganizationService.move_account when the source and destination IDs match ''' updated_accounts_mock = {"account_a": {"id": "987654321"}} expected_changes = {} list_parents_response = { "Parents": [{"Id": "r-1234"}]} expected_list_parents_params = {"ChildId": "987654321"} self.stubber.add_response("list_parents", list_parents_response, expected_list_parents_params) updated_org_mock = self._get_mock_org(accounts=updated_accounts_mock) aws_org_mock = self._get_mock_org(root_parent_id='r-1234') org_mock = self._get_mock_org(aws_model=aws_org_mock, updated_model=updated_org_mock) org_service = self._get_org_service() changes = org_service.move_account(organization=org_mock, account_name="account_a", parent_name="root") helpers.print_expected_actual_diff(expected_changes, changes) assert changes == expected_changes
def test_set_org_ids_to_children(self): ''' Tests OrganizationService._set_org_ids_to_children This tests the enumeration for an effective organization hierarchy of: r-1234: orgunits: ou-123456789: accounts: - 123456789 orgunits: ou-987654321: {} This test is sensitive to ordering and assumes that list_children will first be called with ChildType=ORGANIZATIONAL_UNIT and then ChildType=ACCOUNT in each iteration of the recursive function. ''' child_orgunit_response1 = {"Children": [{"Id": "ou-123456789"}]} child_orgunit_response2 = {"Children": [{"Id": "ou-987654321"}]} child_account_response2 = {"Children": [{"Id": "123456789"}]} child_empty_response = {"Children": []} response_order = [ # First iteration: orgunit, account child_orgunit_response1, child_empty_response, # Second iteration child_orgunit_response2, child_account_response2, # Third iteration child_empty_response, child_empty_response] # Set the stub responses in order for response in response_order: self.stubber.add_response('list_children', response) expected_ids_to_children = { "r-1234": {"orgunits": ["ou-123456789"], "accounts": []}, "ou-123456789": {"orgunits": ["ou-987654321"], "accounts": ["123456789"]}, "ou-987654321": {"orgunits": [], "accounts": []}} org_service = self._get_org_service() org_mock = self._get_mock_org(ids_to_children={}) org_service._set_org_ids_to_children(org_model=org_mock, parent="r-1234") helpers.print_expected_actual_diff(expected_ids_to_children, org_mock.ids_to_children) assert expected_ids_to_children == org_mock.ids_to_children
def test_load_accounts(self): ''' Tests OrganizationService.load_accounts ''' list_accounts_response = { "Accounts": [{ "Name": "account_a", "Email": "*****@*****.**", "Id": "123456789"}]} self.stubber.add_response('list_accounts', list_accounts_response) expected_accounts = { "account_a": { "name": "account_a", "owner": "*****@*****.**", "id": "123456789", "regions": []}} expected_account_ids_to_names = { "123456789": "account_a"} org_mock = self._get_mock_org(accounts={}, account_ids_to_names={}) org_service = self._get_org_service() org_service.load_accounts(org_mock) helpers.print_expected_actual_diff(expected_accounts, org_mock.accounts) assert expected_accounts == org_mock.accounts helpers.print_expected_actual_diff(expected_account_ids_to_names, org_mock.account_ids_to_names) assert expected_account_ids_to_names == org_mock.account_ids_to_names
def test_wait_on_account_creation(self): ''' Tests OrganizationService.wait_on_account_creation ''' creation_statuses = { "account_a": {"Id": "req-123456", "State": "IN_PROGRESS"}} in_progress_response = { "CreateAccountStatus": {"Id": "req-123456", "State": "IN_PROGRESS"}} succeeded_response = { "CreateAccountStatus": {"Id": "req-123456", "State": "SUCCEEDED"}} expected_describe_status_params = {"CreateAccountRequestId": "req-123456"} self.stubber.add_response("describe_create_account_status", in_progress_response, expected_describe_status_params) self.stubber.add_response("describe_create_account_status", succeeded_response, expected_describe_status_params) expected_creation_statuses = { "account_a": {"Id": "req-123456", "State": "SUCCEEDED"}} # Patch time.sleep so we don't actually wait 20 seconds for this test to complete. with mock.patch('cumulogenesis.services.organization.time.sleep'): org_service = self._get_org_service() org_service._wait_on_account_creation(creation_statuses) helpers.print_expected_actual_diff(expected_creation_statuses, creation_statuses) assert expected_creation_statuses == creation_statuses
def test_valid_model_configuration_2018_05_04(self): #pylint: disable=line-too-long ''' Test loading/validating/dumping an Organization model from valid config for config version 2018-05-04 When loading a valid organization, we expect the model to load without problems, the orgunit hierarchy to match what we expect, and for it to render into a comparable configuration to what was input. ''' loader_version = '2018-05-04' fixture_name = 'valid-model-all-features-%s' % loader_version config = self._load_yaml_config_fixture(fixture_name) expected_hierarchy = self._load_yaml_hierarchy_fixture(fixture_name) org_model = config_loader.load_organization_from_config(config) problems = org_model.validate() print("Problems:") helpers.pretty_print(problems) assert not problems hierarchy = org_model.get_orgunit_hierarchy() assert hierarchy == expected_hierarchy rendered_config = config_loader.dump_organization_to_config(org_model, loader_version) helpers.print_expected_actual_diff(config, rendered_config) difference = helpers.deep_diff(config, rendered_config) assert difference == {}
def test_upsert_organization(self): ''' Tests OrganizationService.upsert_organization when the action is create ''' actions = {'organization': {'action': 'create'}} list_parents_response = { 'Parents': [ {'Id': 'r-1234', 'Type': 'ROOT'}]} expected_create_params = {'FeatureSet': 'ALL'} expected_list_parents_params = {'ChildId': '123456789'} expected_enable_policy_params = { 'RootId': 'r-1234', 'PolicyType': 'SERVICE_CONTROL_POLICY'} self.stubber.add_response('create_organization', {'Organization': {}}, expected_create_params) self.stubber.add_response('list_parents', list_parents_response, expected_list_parents_params) self.stubber.add_response('enable_policy_type', {'Root': {}}, expected_enable_policy_params) expected_changes = {"organization": {"change": "created"}} org_mock = self._get_mock_org(featureset='ALL', root_parent_id=None) org_service = self._get_org_service() changes = org_service.upsert_organization(organization=org_mock, actions=actions) helpers.print_expected_actual_diff(expected_changes, changes) assert expected_changes == changes
def test_validate(self): ''' Test Organization.validate This test should generate a problem for one of each potential invalid Organization state. ''' orgunits_mock = { "missing_account": { "accounts": ["nonexistent"], "policies": [], "child_orgunits": [] }, "missing_policy": { "accounts": [], "policies": ["nonexistent"], "child_orgunits": [] }, "missing_orgunit": { "accounts": [], "policies": [], "child_orgunits": ["nonexistent"] }, "valid_ou_a": { "accounts": ["multiple_references", "valid_account_a"], "policies": [], "child_orgunits": [] }, "valid_ou_b": { "accounts": ["multiple_references"], "policies": [], "child_orgunits": [] } } accounts_mock = { "orphaned_account": { "parent_references": None, "regions": ["us-east-1"] }, "multiple_references": { "parent_references": None, "regions": ["us-east-1"] }, "valid_account_a": { "parent_references": None, "regions": ["us-east-1"] } } org = self._get_base_organization() org.orgunits = self._add_name_to_entity_mocks(orgunits_mock) org.accounts = self._add_name_to_entity_mocks(accounts_mock) expected_problems = { 'orgunits': { 'missing_account': ['references missing account nonexistent'], 'missing_orgunit': ['references missing child orgunit nonexistent'], 'missing_policy': ['references missing policy nonexistent'] }, 'accounts': { 'orphaned_account': ['orphaned'], #pylint: disable=line-too-long 'multiple_references': [ 'referenced as a child of multiple orgunits: valid_ou_a, valid_ou_b' ] } } problems = org.validate() helpers.print_expected_actual_diff(expected_problems, problems) assert expected_problems == problems