From 6c3232f3296f7a9c4cdc8da1312a01d1b40dcc78 Mon Sep 17 00:00:00 2001 From: aboulay Date: Wed, 15 Apr 2020 20:54:22 +0200 Subject: [PATCH 1/3] Add harbor_project_members module --- plugins/modules/harbor_project_members.py | 189 ++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 plugins/modules/harbor_project_members.py diff --git a/plugins/modules/harbor_project_members.py b/plugins/modules/harbor_project_members.py new file mode 100644 index 0000000..343afa6 --- /dev/null +++ b/plugins/modules/harbor_project_members.py @@ -0,0 +1,189 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2019, SFR +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) + +ANSIBLE_METADATA = { + 'status': ['preview'], + 'supported_by': 'community', + 'metadata_version': '1.1' +} + +DOCUMENTATION = ''' +--- +module: harbor_project_members +author: + - Adrien Boulay (!UNKNOWN) +description: + - Create/update/delete Harbor projects members using Harbor's REST API. +short_description: Create members on a specific project in Harbor +options: + project_name: + description: name of the project + required: true + type: str + members: + description: members of the projects + required: true + type: list +extends_documentation_fragment: + - sfr.harbor.harbor +''' + +EXAMPLES = ''' + +- name: add members to a projects + harbor_project_members: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + project_name: test_project + members: + - type: Group + name: group_name + role: admin + - type: User + name: user_name + role: admin + +''' + +RETURN = ''' +--- +''' + +import itertools +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.sfr.harbor.plugins.module_utils.base import harbor_argument_spec +from ansible_collections.sfr.harbor.plugins.module_utils.harbor import HarborBaseInterface + +__metaclass__ = type + + +class HarborInterface(HarborBaseInterface): + def get_project_by_name(self, name): + url = "/api/projects?name={name}".format(name=name) + response = self._send_request(url, headers=self.headers, method="GET") + if not response: + return None + if not len(response) == 1: + raise AssertionError("Expected 1 project, got %d" % len(response)) + project = response[0] + project_id = project['project_id'] + project['members'] = self.get_project_members(project_id) + return project + + def get_project_members(self, project_id): + url = "/api/projects/{project_id}/members".format(project_id=project_id) + response = self._send_request(url, headers=self.headers, method="GET") + return response + + def create_project_member(self, project_id, member): + url = "/api/projects/{project_id}/members".format(project_id=project_id) + payload = dict(role_id=harbor_roles_equivalence(member["role"]), member_user=dict(username=member["name"])) + response = self._send_request(url, data=payload, headers=self.headers, method="POST") + return response + + def update_project_member(self, project_id, member_id, member_updated): + url = "/api/projects/{project_id}/members/{member_id}".format(project_id=project_id, member_id=member_id) + payload = dict(role_id=harbor_roles_equivalence(member_updated["role"])) + response = self._send_request(url, data=payload, headers=self.headers, method="PUT") + return response + + def delete_project_member(self, project_id, member_id): + url = "/api/projects/{project_id}/members/{member_id}".format(project_id=project_id, member_id=member_id) + response = self._send_request(url, headers=self.headers, method="DELETE") + return response + + +def setup_module_object(): + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=False, + required_together=[['harbor_username', 'harbor_password']] + ) + return module + + +argument_spec = harbor_argument_spec() + +argument_spec.update( + project_name=dict(type='str', required=True), + members=dict(type='list', defaults=[]), +) + + +def harbor_roles_equivalence(role_name): + if role_name == "projectAdmin": + return 1 + elif role_name == "developer": + return 2 + elif role_name == "guest": + return 3 + elif role_name == "master": + return 4 + else: + return 99 + + +def convert_harbor_members_dict_to_ansible(harbor_members): + existing_members = [] + for harbor_member in harbor_members: + existing_members.append({ + "name": harbor_member["entity_name"], + "id": harbor_member["id"], + "type": ("Group" if harbor_member["entity_type"] == "g" else "User"), + "role": harbor_member["role_name"] + }) + return existing_members + + +def main(): + module = setup_module_object() + name = module.params['project_name'] + members = module.params['members'] + + changed = False + created = False + + harbor_iface = HarborInterface(module) + + project = harbor_iface.get_project_by_name(name) + project_id = harbor_iface.get_project_by_name(name)["project_id"] + + if project is None: + module.exit_json(failed=True, changed=False, message="No project found") + + existing_members = harbor_iface.get_project_members(project_id) + existing_members = convert_harbor_members_dict_to_ansible(existing_members) + admin_json = [emember for emember in existing_members if (emember["name"] == "admin" and emember["type"] == "User")] + existing_members.remove(admin_json[0]) + + for member in members: + matches = [emember for emember in existing_members if (emember["name"] == member["name"] and emember["type"] == member["type"])] + if len(matches) == 0: + harbor_iface.create_project_member(project_id, member) + created = True + changed = True + continue + existing_member = matches[0] + if member["role"] != existing_member["role"]: + harbor_iface.update_project_member(project_id, existing_member["id"], member) + changed = True + existing_members.remove(existing_member) + + for existing_member in existing_members: + harbor_iface.delete_project_member(project_id, existing_member["id"]) + changed = True + + members_list = harbor_iface.get_project_members(project_id) + members_list = convert_harbor_members_dict_to_ansible(members_list) + + module.exit_json(failed=False, changed=changed, members=members_list, created=created) + + +if __name__ == '__main__': + main() From b3bf136d7d99497792647ed95561014e799683ad Mon Sep 17 00:00:00 2001 From: aboulay Date: Wed, 15 Apr 2020 21:11:49 +0200 Subject: [PATCH 2/3] Add tests for harbor_project_member --- .../harbor_project_members/defaults/main.yml | 7 ++ .../tasks/clean_environment.yml | 29 ++++++++ .../harbor_project_members/tasks/main.yml | 7 ++ .../tasks/setup_environment.yml | 29 ++++++++ .../harbor_project_members/tasks/tests.yml | 68 +++++++++++++++++++ 5 files changed, 140 insertions(+) create mode 100644 tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/defaults/main.yml create mode 100644 tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/clean_environment.yml create mode 100644 tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/main.yml create mode 100644 tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/setup_environment.yml create mode 100644 tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/tests.yml diff --git a/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/defaults/main.yml b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/defaults/main.yml new file mode 100644 index 0000000..5910a55 --- /dev/null +++ b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/defaults/main.yml @@ -0,0 +1,7 @@ +--- + +harbor_url: "http://127.0.0.1" +harbor_admin_user: "admin" +harbor_admin_password: "Harbor12345" + +... diff --git a/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/clean_environment.yml b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/clean_environment.yml new file mode 100644 index 0000000..d1358db --- /dev/null +++ b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/clean_environment.yml @@ -0,0 +1,29 @@ +--- + +- name: remove testproject + harbor_project: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + name: test_project + state: present + +- name: clean a test user + harbor_user: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + username: "rrey" + email: "rrey@octo.com" + state: present + +- name: clean a test user + harbor_user: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + username: "adbo" + email: "adbo@octo.com" + state: present + +... \ No newline at end of file diff --git a/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/main.yml b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/main.yml new file mode 100644 index 0000000..3038305 --- /dev/null +++ b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/main.yml @@ -0,0 +1,7 @@ +--- + +- import_tasks: setup_environment.yml +- import_tasks: tests.yml +- import_tasks: clean_environment.yml + +... \ No newline at end of file diff --git a/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/setup_environment.yml b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/setup_environment.yml new file mode 100644 index 0000000..8c47341 --- /dev/null +++ b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/setup_environment.yml @@ -0,0 +1,29 @@ +--- + +- name: create new project + harbor_project: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + name: test_project + state: present + +- name: create a user + harbor_user: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + username: "rrey" + email: "rrey@octo.com" + state: present + +- name: create a user + harbor_user: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + username: "adbo" + email: "adbo@octo.com" + state: present + +... \ No newline at end of file diff --git a/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/tests.yml b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/tests.yml new file mode 100644 index 0000000..6a65e1d --- /dev/null +++ b/tests/integration/targets/harbor_project_members/tests/integration/targets/harbor_project_members/tasks/tests.yml @@ -0,0 +1,68 @@ +--- + +- name: add members to project test_project + harbor_project_members: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + project_name: test_project + members: + - type: User + name: adbo + role: master + register: add_member_result + +- assert: + that: + - "add_member_result.changed == true" + - "add_member_result.created == true" + +- name: add members to project test_project (idempotency) + harbor_project_members: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + project_name: test_project + members: + - type: User + name: adbo + role: master + register: add_member_idempotency_result + +- assert: + that: + - "add_member_idempotency_result.changed == false" + - "add_member_idempotency_result.created == false" + +- name: change role of members project test_project + harbor_project_members: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + project_name: test_project + members: + - type: User + name: adbo + role: developer + register: update_member_result + +- assert: + that: + - "update_member_result.changed == true" + - "update_member_result.created == false" + +- name: delete all members project test_project + harbor_project_members: + harbor_url: "{{ harbor_url }}" + harbor_username: "{{ harbor_admin_user }}" + harbor_password: "{{ harbor_admin_password }}" + project_name: test_project + members: [] + register: delete_member_result + +- assert: + that: + - "delete_member_result.changed == true" + - "delete_member_result.created == false" + +... From fd46bcd90e6d6f5cf5458240e4daa61f8a12feb9 Mon Sep 17 00:00:00 2001 From: aboulay Date: Wed, 15 Apr 2020 21:42:26 +0200 Subject: [PATCH 3/3] Update sfr -> octo in module dependancies (will be changed probably soon) --- plugins/modules/harbor_project_members.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/modules/harbor_project_members.py b/plugins/modules/harbor_project_members.py index 343afa6..79dc21e 100644 --- a/plugins/modules/harbor_project_members.py +++ b/plugins/modules/harbor_project_members.py @@ -30,7 +30,7 @@ required: true type: list extends_documentation_fragment: - - sfr.harbor.harbor + - octo.harbor.harbor ''' EXAMPLES = ''' @@ -57,8 +57,8 @@ import itertools from ansible.module_utils.basic import AnsibleModule -from ansible_collections.sfr.harbor.plugins.module_utils.base import harbor_argument_spec -from ansible_collections.sfr.harbor.plugins.module_utils.harbor import HarborBaseInterface +from ansible_collections.octo.harbor.plugins.module_utils.base import harbor_argument_spec +from ansible_collections.octo.harbor.plugins.module_utils.harbor import HarborBaseInterface __metaclass__ = type