Skip to content

Commit

Permalink
added roles to object_diff (#454)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivarmu authored Jan 4, 2023
1 parent 513e372 commit 500d8e8
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 38 deletions.
4 changes: 4 additions & 0 deletions changelogs/fragments/object_diff_role_and_plugin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
minor_changes:
- Add roles object to object_diff role and controller_object_diff lookup plugin
...
102 changes: 91 additions & 11 deletions plugins/lookup/controller_object_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
from ansible.errors import AnsibleError, AnsibleLookupError
from ansible.module_utils._text import to_native
from ansible.utils.display import Display
import copy


class LookupModule(LookupBase):
Expand Down Expand Up @@ -131,6 +132,8 @@ def run(self, terms, variables=None, **kwargs):
elif api_list[0]["type"] == "group" or api_list[0]["type"] == "host":
keys_to_keep = ["name", "inventory"]
api_keys_to_keep = ["name", "summary_fields"]
elif api_list[0]["type"] == "role":
pass
else:
keys_to_keep = ["name", "organization"]
api_keys_to_keep = ["name", "summary_fields"]
Expand All @@ -143,19 +146,24 @@ def run(self, terms, variables=None, **kwargs):
keys_to_keep.append("inventory")
api_keys_to_keep.append("inventory")

for item in compare_list:
for key in keys_to_keep:
if key not in item.keys():
self.handle_error(msg="Key: '{0}' missing from item in compare_list item: {1}".format(key, item))
if api_list[0]["type"] != "role":
for item in compare_list:
for key in keys_to_keep:
if key not in item.keys():
self.handle_error(msg="Key: '{0}' missing from item in compare_list item: {1}".format(key, item))

for item in api_list:
for key in api_keys_to_keep:
if key not in item.keys():
self.handle_error(msg="Key: '{0}' missing from item in api_list. Does this object come from the api? item: {1}".format(key, item))
for item in api_list:
for key in api_keys_to_keep:
if key not in item.keys():
self.handle_error(msg="Key: '{0}' missing from item in api_list. Does this object come from the api? item: {1}".format(key, item))

# Reduce list to name and organization
compare_list_reduced = [{key: item[key] for key in keys_to_keep} for item in compare_list]
api_list_reduced = [{key: item[key] for key in api_keys_to_keep} for item in api_list]
if api_list[0]["type"] != "role":
compare_list_reduced = [{key: item[key] for key in keys_to_keep} for item in compare_list]
api_list_reduced = [{key: item[key] for key in api_keys_to_keep} for item in api_list]
else:
compare_list_reduced = copy.deepcopy(compare_list)
api_list_reduced = copy.deepcopy(api_list)

# Convert summary field name into org name Only if not type organization
if api_list[0]["type"] == "group" or api_list[0]["type"] == "host":
Expand All @@ -177,6 +185,64 @@ def run(self, terms, variables=None, **kwargs):
item.update({"unified_job_template": item["summary_fields"]["unified_job_template"]["name"]})
item.update({"workflow_job_template": item["summary_fields"]["workflow_job_template"]["name"]})
item.pop("summary_fields")
elif api_list[0]["type"] == "role":
for item in api_list_reduced:
if item["resource_type"] == "organization":
item.update({"organizations": [item[item["resource_type"]]]})
item.update({"role": item["name"].lower()})
# Remove the extra fields
item.pop("users")
item.pop("teams")
item.pop("name")
item.pop("resource_type")
if "organization" in item:
item.pop("organization")
if "type" in item:
item.pop("type")
list_to_extend = []
list_to_remove = []
for item in compare_list_reduced:
target_teams_expanded = False
job_templates_expanded = False
workflows_expanded = False
if "target_teams" in item:
for team in item["target_teams"]:
new_item = copy.deepcopy(item)
new_item.update({"team": team})
new_item.pop("target_teams")
if "job_templates" in new_item:
new_item.pop("job_templates")
if "workflows" in new_item:
new_item.pop("workflows")
list_to_extend.append(new_item)
target_teams_expanded = True
if "job_templates" in item:
for job_template in item["job_templates"]:
new_item = copy.deepcopy(item)
new_item.update({"job_template": job_template})
new_item.pop("job_templates")
if "target_teams" in new_item:
new_item.pop("target_teams")
if "workflows" in new_item:
new_item.pop("workflows")
list_to_extend.append(new_item)
job_templates_expanded = True
if "workflows" in item:
for workflow in item["workflows"]:
new_item = copy.deepcopy(item)
new_item.update({"workflow_job_template": workflow})
new_item.pop("workflows")
if "target_teams" in new_item:
new_item.pop("target_teams")
if "job_templates" in new_item:
new_item.pop("job_templates")
list_to_extend.append(new_item)
workflows_expanded = True
if target_teams_expanded or job_templates_expanded or workflows_expanded:
list_to_remove.append(item)
for item in list_to_remove:
compare_list_reduced.remove(item)
compare_list_reduced.extend(list_to_extend)
elif api_list[0]["type"] != "organization" and api_list[0]["type"] != "user" and api_list[0]["type"] != "credential_type":
for item in api_list_reduced:
item.update({"organization": item["summary_fields"]["organization"]["name"]})
Expand All @@ -186,7 +252,13 @@ def run(self, terms, variables=None, **kwargs):
self.display.warning("api_list_reduced: {0}".format(api_list_reduced))

# Find difference between lists
difference = [i for i in api_list_reduced if i not in compare_list_reduced]
if api_list[0]["type"] != "role":
difference = [i for i in api_list_reduced if i not in compare_list_reduced]
else:
difference = []
for item in api_list_reduced:
if item not in compare_list_reduced:
difference.append(item)

# Set
if self.get_option("set_absent"):
Expand All @@ -200,4 +272,12 @@ def run(self, terms, variables=None, **kwargs):
# Return Compare list with difference attached
difference = compare_list

if api_list[0]["type"] == "role":
difference_to_remove = []
for item in difference:
if "no_resource_type" in item or len(item) <= 3:
difference_to_remove.append(item)
for item in difference_to_remove:
difference.remove(item)

return [difference]
48 changes: 25 additions & 23 deletions roles/filetree_read/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,14 @@ orgs_vars/Organization1
│   │   ├── controller_projects_inventory_sourcea_prod.yml
│   │   ├── controller_projects_inventory_sourceb_dev.yml
│   │   └── controller_projects_inventory_sourceb_prod.yml
│   ├── controller_roles.d
│   │   ├── app-example
│   │   │   ├── controller_roles_cmdb_approvals.yml
│   │   │   ├── controller_roles_inventories.yml
│   │   │   ├── controller_roles_inventory_wf_update.yml
│   │   │   ├── controller_roles_teams.yml
│   │   │   └── controller_roles_users.yml
│   │   └── controller_roles.yml
│   ├── controller_roles.d (1)
│   │   ├── app-example (1)
│   │   │   ├── controller_roles_cmdb_approvals.yml (1)
│   │   │   ├── controller_roles_inventories.yml (1)
│   │   │   ├── controller_roles_inventory_wf_update.yml (1)
│   │   │   ├── controller_roles_teams.yml (1)
│   │   │   └── controller_roles_users.yml (1)
│   │   └── controller_roles.yml (1)
│   ├── controller_schedules.d
│   │   ├── app-casc
│   │   │   └── controller_schedules_casc.yml
Expand Down Expand Up @@ -207,13 +207,13 @@ orgs_vars/Organization1
│   │   │   ├── controller_inventory_sources_sourceb_dev.yml
│   │   │   └── controller_inventory_sources_sourceb_prod.yml
│   │   └── controller_inventory_sources.yml
│   └── controller_settings.d (1)
│   ├── app-examples (1)
│   │   ├── controller_settings_jobs.yml (1)
│   │   ├── controller_settings_ldap.yml (1)
│   │   ├── controller_settings_system.yml (1)
│   │   └── controller_settings_user_interface.yml (1)
│   └── controller_settings.yml (1)
│   └── controller_settings.d (2)
│   ├── app-examples (2)
│   │   ├── controller_settings_jobs.yml (2)
│   │   ├── controller_settings_ldap.yml (2)
│   │   ├── controller_settings_system.yml (2)
│   │   └── controller_settings_user_interface.yml (2)
│   └── controller_settings.yml (2)
└── demo-prd
├── controller_credentials.d
│   ├── app-examples
Expand Down Expand Up @@ -255,16 +255,18 @@ orgs_vars/Organization1
│   │   ├── controller_inventory_sources_sourceb_dev.yml
│   │   └── controller_inventory_sources_sourceb_prod.yml
│   └── controller_inventory_sources.yml
└── controller_settings.d (1)
├── app-examples (1)
│   ├── controller_settings_jobs.yml (1)
│   ├── controller_settings_ldap.yml (1)
│   ├── controller_settings_system.yml (1)
│   └── controller_settings_user_interface.yml (1)
└── controller_settings.yml (1)
└── controller_settings.d (2)
├── app-examples (2)
│   ├── controller_settings_jobs.yml (2)
│   ├── controller_settings_ldap.yml (2)
│   ├── controller_settings_system.yml (2)
│   └── controller_settings_user_interface.yml (2)
└── controller_settings.yml (2)
```

> **NOTE (1):** These directories and files must belong to SuperAdmin Organization ONLY, because must have admin super powers.
> **NOTE (1):** These directory and files may belong to SuperAdmin Organization ONLY. If any other organization defines it's own `roles`, they must duplicate the ones given by the SuperAdmin Organization or they will be dropped.
>
> **NOTE (2):** These directories and files must belong to SuperAdmin Organization ONLY, because must have admin super powers.
## Role Tags

Expand Down
8 changes: 7 additions & 1 deletion roles/object_diff/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The following Variables set the organization where should be applied the configu
| :------------ | :-----------: | :------: | :---------- |
| `controller_api_plugin` | `ansible.controller` | yes | Full path for the controller_api_plugin to be used. <br/> Can have two possible values: <br/>&nbsp;&nbsp;- awx.awx.controller_api # For the community Collection version <br/>&nbsp;&nbsp;- ansible.controller.controller_api # For the Red Hat Certified Collection version|
| `drop_user_external_accounts` | `False` | no | When is true, all users will be taken to compare with SCM configuration as code |
| `drop_teams` | `False` | no | When is true, all teams will be taken to compare with SCM configuration as code |
<!--- | `drop_teams` | `False` | no | When is true, all teams will be taken to compare with SCM configuration as code | -->
| `protect_not_empty_orgs` | `N/A` | no | When is true, orgs which are not empty, will not be removed |

## Role Tags
Expand All @@ -31,6 +31,10 @@ $ ansible-playbook object_diff.yml --list-tags

```

## IMPORTANT

To correctly manage `roles`, they can only be defined by a super-admin organization, so all the roles in the Ansible Controller instance are managed by only one organization.

## Example Playbook

```bash
Expand Down Expand Up @@ -123,6 +127,8 @@ GPLv3+

- [Ivan Aragonés](https://github.com/ivarmu)

- [Adonis García](https://github.com/adonisgarciac)

## Important things to take into account

- Issues:
Expand Down
1 change: 1 addition & 0 deletions roles/object_diff/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# populate_controller_api_user_accounts_without_external_accounts
drop_user_external_accounts: false
query_controller_api_max_objects: 10000

# Automation Controller Object Lists
controller_settings: []
Expand Down
38 changes: 35 additions & 3 deletions roles/object_diff/tasks/roles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,46 @@

- name: "Get the API list of all roles"
ansible.builtin.set_fact:
__controller_api_roles: "{{ query(controller_api_plugin, 'roles',
host=controller_hostname, oauth_token=controller_oauthtoken, verify_ssl=controller_validate_certs)
__controller_api_roles: "{{ (__controller_api_roles | default([])) + [{
'users': current_users,
'teams': current_teams,
'name': current_role.name,
'role': current_role.name,
'type': current_role.type,
'resource_type': (current_role.summary_fields.resource_type|default('no_resource_type')),
(current_role.summary_fields.resource_type|default('no_resource_type')): (current_role.summary_fields.resource_name|default('no_resource_name'))
}]
}}"
vars:
current_users: "{{ query(controller_api_plugin, current_role.related.users,
host=controller_hostname, oauth_token=controller_oauthtoken, verify_ssl=controller_validate_certs,
return_all=true, max_objects=query_controller_api_max_objects) | map(attribute='username')
}}"
current_teams: "{{ query(controller_api_plugin, current_role.related.teams,
host=controller_hostname, oauth_token=controller_oauthtoken, verify_ssl=controller_validate_certs,
return_all=true, max_objects=query_controller_api_max_objects) | map(attribute='name')
}}"
loop: "{{ query(controller_api_plugin, 'roles',
host=controller_hostname, oauth_token=controller_oauthtoken, verify_ssl=controller_validate_certs,
return_all=true, max_objects=query_controller_api_max_objects)
}}"
loop_control:
loop_var: current_role

- name: "Explode the roles for users"
ansible.builtin.set_fact:
__full_controller_api_roles: "{{ (__full_controller_api_roles | default([])) + [item.0 | combine({'user': item.1})] }}"
loop: "{{ (__controller_api_roles | subelements('users')) }}"

- name: "Explode the roles for teams"
ansible.builtin.set_fact:
__full_controller_api_roles: "{{ (__full_controller_api_roles | default([])) + [item.0 | combine({'team': item.1})] }}"
loop: "{{ (__controller_api_roles | subelements('teams')) }}"

- name: "Find the difference of Roles between what is on the Controller versus CasC on SCM"
ansible.builtin.set_fact:
__roles_difference: "{{ lookup('redhat_cop.controller_configuration.controller_object_diff',
api_list=__controller_api_roles, compare_list=controller_roles,
api_list=__full_controller_api_roles, compare_list=controller_roles,
with_present=false, set_absent=true)
}}"

Expand Down

0 comments on commit 500d8e8

Please sign in to comment.