1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

added option to share projects with a group

This commit is contained in:
Acarnesecchi 2024-07-29 11:21:30 +02:00
parent 37c8560542
commit a243ecce27
2 changed files with 262 additions and 101 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- gitlab_project_members - add ``gitlab_group`` option to share and unshare project with groups (https://github.com/ansible-collections/community.general/pull/8676).

View file

@ -42,7 +42,7 @@ options:
gitlab_user: gitlab_user:
description: description:
- A username or a list of usernames to add to/remove from the GitLab project. - A username or a list of usernames to add to/remove from the GitLab project.
- Mutually exclusive with O(gitlab_users_access). - Mutually exclusive with O(gitlab_users_access) and O(gitlab_group).
type: list type: list
elements: str elements: str
access_level: access_level:
@ -55,7 +55,7 @@ options:
description: description:
- Provide a list of user to access level mappings. - Provide a list of user to access level mappings.
- Every dictionary in this list specifies a user (by username) and the access level the user should have. - Every dictionary in this list specifies a user (by username) and the access level the user should have.
- Mutually exclusive with O(gitlab_user) and O(access_level). - Mutually exclusive with O(gitlab_user), O(access_level), and O(gitlab_group).
- Use together with O(purge_users) to remove all users not specified here from the project. - Use together with O(purge_users) to remove all users not specified here from the project.
type: list type: list
elements: dict elements: dict
@ -89,6 +89,27 @@ options:
elements: str elements: str
choices: ['guest', 'reporter', 'developer', 'maintainer'] choices: ['guest', 'reporter', 'developer', 'maintainer']
version_added: 3.7.0 version_added: 3.7.0
gitlab_group:
description:
- A list of IDs or the full paths of the groups to share/unshare the project with.
- Every dictionary in this list specifies a group (by ID or full path) and the access level the group should have.
- Mutually exclusive with O(gitlab_user), O(access_level) and O(gitlab_users_access).
type: list
elements: dict
version_added: 9.3.0
suboptions:
name:
description: ID or the full path of the group to share/unshare the project with.
type: str
required: true
group_access_level:
description:
- The access level for the group.
- Required if O(state=present), group state is set to present.
type: str
choices: ['guest', 'reporter', 'developer', 'maintainer']
required: false
''' '''
EXAMPLES = r''' EXAMPLES = r'''
@ -154,6 +175,27 @@ EXAMPLES = r'''
- name: user2 - name: user2
access_level: maintainer access_level: maintainer
state: absent state: absent
- name: Share a project with a list of Groups with Dedicated Access Levels to A GitLab project
community.general.gitlab_project_members:
api_url: 'https://gitlab.example.com'
api_token: 'Your-Private-Token'
project: projectname
gitlab_group:
- name: group1
group_access_level: developer
- name: group2
group_access_level: maintainer
state: present
- name: Unshare a project with a group
community.general.gitlab_project_members:
api_url: 'https://gitlab.example.com'
api_token: 'Your-Private-Token'
project: projectname
gitlab_group:
- name: group1
state: absent
''' '''
RETURN = r''' # ''' RETURN = r''' # '''
@ -190,6 +232,11 @@ class GitLabProjectMembers(object):
project = self._gitlab.projects.get(gitlab_project_id) project = self._gitlab.projects.get(gitlab_project_id)
return project.members.list(all=True) return project.members.list(all=True)
# get all groups in a project
def get_groups_in_a_project(self, gitlab_project_id):
project = self._gitlab.projects.get(gitlab_project_id)
return [group['group_id'] for group in project.shared_with_groups]
# get single member in a project by user name # get single member in a project by user name
def get_member_in_a_project(self, gitlab_project_id, gitlab_user_id): def get_member_in_a_project(self, gitlab_project_id, gitlab_user_id):
member = None member = None
@ -208,6 +255,13 @@ class GitLabProjectMembers(object):
return True return True
return False return False
# check if the group is in the project
def is_group_in_project(self, groups, group_id):
for gid in groups:
if gid == group_id:
return True
return False
# add user to a project # add user to a project
def add_member_to_project(self, gitlab_user_id, gitlab_project_id, access_level): def add_member_to_project(self, gitlab_user_id, gitlab_project_id, access_level):
project = self._gitlab.projects.get(gitlab_project_id) project = self._gitlab.projects.get(gitlab_project_id)
@ -232,6 +286,23 @@ class GitLabProjectMembers(object):
member.access_level = access_level member.access_level = access_level
member.save() member.save()
def get_group_id(self, group_name):
try:
group_exists = self._gitlab.groups.get(group_name)
return group_exists.id
except gitlab.exceptions.GitlabGetError as e:
return None
# share project with a group
def share_project_with_group(self, gitlab_project_id, group_id, access_level):
project = self._gitlab.projects.get(gitlab_project_id)
project.share(group_id, access_level)
# unshare project with a group
def unshare_project_with_group(self, gitlab_project_id, group_id):
project = self._gitlab.projects.get(gitlab_project_id)
project.unshare(group_id)
def main(): def main():
argument_spec = basic_auth_argument_spec() argument_spec = basic_auth_argument_spec()
@ -252,6 +323,15 @@ def main():
'guest', 'reporter', 'developer', 'maintainer'], required=True), 'guest', 'reporter', 'developer', 'maintainer'], required=True),
) )
), ),
gitlab_group=dict(
type='list',
elements='dict',
options=dict(
name=dict(type='str', required=True),
group_access_level=dict(type='str', choices=[
'guest', 'reporter', 'developer', 'maintainer'], required=False),
)
),
)) ))
module = AnsibleModule( module = AnsibleModule(
@ -262,8 +342,8 @@ def main():
['api_username', 'api_job_token'], ['api_username', 'api_job_token'],
['api_token', 'api_oauth_token'], ['api_token', 'api_oauth_token'],
['api_token', 'api_job_token'], ['api_token', 'api_job_token'],
['gitlab_user', 'gitlab_users_access'], ['gitlab_user', 'gitlab_users_access', 'gitlab_group'],
['access_level', 'gitlab_users_access'], ['access_level', 'gitlab_users_access', 'gitlab_group'],
], ],
required_together=[ required_together=[
['api_username', 'api_password'], ['api_username', 'api_password'],
@ -271,10 +351,10 @@ def main():
], ],
required_one_of=[ required_one_of=[
['api_username', 'api_token', 'api_oauth_token', 'api_job_token'], ['api_username', 'api_token', 'api_oauth_token', 'api_job_token'],
['gitlab_user', 'gitlab_users_access'], ['gitlab_user', 'gitlab_users_access', 'gitlab_group'],
], ],
required_if=[ required_if=[
['state', 'present', ['access_level', 'gitlab_users_access'], True], ['state', 'present', ['access_level', 'gitlab_users_access', 'gitlab_group'], True],
], ],
supports_check_mode=True, supports_check_mode=True,
) )
@ -293,6 +373,9 @@ def main():
state = module.params['state'] state = module.params['state']
access_level = module.params['access_level'] access_level = module.params['access_level']
purge_users = module.params['purge_users'] purge_users = module.params['purge_users']
gitlab_users_access = []
# initializing for the main loop
gitlab_groups = []
if purge_users: if purge_users:
purge_users = [access_level_int[level] for level in purge_users] purge_users = [access_level_int[level] for level in purge_users]
@ -306,8 +389,8 @@ def main():
module.fail_json(msg="project '%s' not found." % gitlab_project) module.fail_json(msg="project '%s' not found." % gitlab_project)
members = [] members = []
groups = []
if module.params['gitlab_user'] is not None: if module.params['gitlab_user'] is not None:
gitlab_users_access = []
gitlab_users = module.params['gitlab_user'] gitlab_users = module.params['gitlab_user']
for gl_user in gitlab_users: for gl_user in gitlab_users:
gitlab_users_access.append( gitlab_users_access.append(
@ -316,8 +399,13 @@ def main():
gitlab_users_access = module.params['gitlab_users_access'] gitlab_users_access = module.params['gitlab_users_access']
for user_level in gitlab_users_access: for user_level in gitlab_users_access:
user_level['access_level'] = access_level_int[user_level['access_level']] user_level['access_level'] = access_level_int[user_level['access_level']]
elif module.params['gitlab_group'] is not None:
gitlab_groups = module.params['gitlab_group']
groups = project.get_groups_in_a_project(gitlab_project_id)
if len(gitlab_users_access) == 1 and not purge_users: if gitlab_groups:
pass
elif len(gitlab_users_access) == 1 and not purge_users:
# only single user given # only single user given
members = [project.get_member_in_a_project( members = [project.get_member_in_a_project(
gitlab_project_id, project.get_user_id(gitlab_users_access[0]['name']))] gitlab_project_id, project.get_user_id(gitlab_users_access[0]['name']))]
@ -327,120 +415,191 @@ def main():
# list of users given # list of users given
members = project.get_members_in_a_project(gitlab_project_id) members = project.get_members_in_a_project(gitlab_project_id)
else: else:
module.exit_json(changed='OK', result="Nothing to do, please give at least one user or set purge_users true.", module.exit_json(changed='OK', result="Nothing to do, please give at least one user or group or set purge_users true.",
result_data=[]) result_data=[])
changed = False changed = False
error = False error = False
changed_users = [] changed_users = []
changed_groups = []
changed_data = [] changed_data = []
for gitlab_user in gitlab_users_access: if gitlab_users_access:
gitlab_user_id = project.get_user_id(gitlab_user['name']) for gitlab_user in gitlab_users_access:
gitlab_user_id = project.get_user_id(gitlab_user['name'])
# user doesn't exist # user doesn't exist
if not gitlab_user_id: if not gitlab_user_id:
if state == 'absent': if state == 'absent':
changed_users.append("user '%s' not found, and thus also not part of the project" % gitlab_user['name']) changed_users.append("user '%s' not found, and thus also not part of the project" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'OK',
'msg': "user '%s' not found, and thus also not part of the project" % gitlab_user['name']})
else:
error = True
changed_users.append("user '%s' not found." % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED',
'msg': "user '%s' not found." % gitlab_user['name']})
continue
is_user_a_member = project.is_user_a_member(members, gitlab_user_id)
# check if the user is a member in the project
if not is_user_a_member:
if state == 'present':
# add user to the project
try:
if not module.check_mode:
project.add_member_to_project(gitlab_user_id, gitlab_project_id, gitlab_user['access_level'])
changed = True
changed_users.append("Successfully added user '%s' to project" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'CHANGED',
'msg': "Successfully added user '%s' to project" % gitlab_user['name']})
except (gitlab.exceptions.GitlabCreateError) as e:
error = True
changed_users.append("Failed to updated the access level for the user, '%s'" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED',
'msg': "Not allowed to add the access level for the member, %s: %s" % (gitlab_user['name'], e)})
# state as absent
else:
changed_users.append("User, '%s', is not a member in the project. No change to report" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'OK',
'msg': "User, '%s', is not a member in the project. No change to report" % gitlab_user['name']})
# in case that a user is a member
else:
if state == 'present':
# compare the access level
user_access_level = project.get_user_access_level(members, gitlab_user_id)
if user_access_level == gitlab_user['access_level']:
changed_users.append("User, '%s', is already a member in the project. No change to report" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'OK', changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'OK',
'msg': "User, '%s', is already a member in the project. No change to report" % gitlab_user['name']}) 'msg': "user '%s' not found, and thus also not part of the project" % gitlab_user['name']})
else: else:
# update the access level for the user error = True
changed_users.append("user '%s' not found." % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED',
'msg': "user '%s' not found." % gitlab_user['name']})
continue
is_user_a_member = project.is_user_a_member(members, gitlab_user_id)
# check if the user is a member in the project
if not is_user_a_member:
if state == 'present':
# add user to the project
try: try:
if not module.check_mode: if not module.check_mode:
project.update_user_access_level(members, gitlab_user_id, gitlab_user['access_level']) project.add_member_to_project(gitlab_user_id, gitlab_project_id, gitlab_user['access_level'])
changed = True changed = True
changed_users.append("Successfully updated the access level for the user, '%s'" % gitlab_user['name']) changed_users.append("Successfully added user '%s' to project" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'CHANGED', changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'CHANGED',
'msg': "Successfully updated the access level for the user, '%s'" % gitlab_user['name']}) 'msg': "Successfully added user '%s' to project" % gitlab_user['name']})
except (gitlab.exceptions.GitlabUpdateError) as e: except (gitlab.exceptions.GitlabCreateError) as e:
error = True error = True
changed_users.append("Failed to updated the access level for the user, '%s'" % gitlab_user['name']) changed_users.append("Failed to updated the access level for the user, '%s'" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED', changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED',
'msg': "Not allowed to update the access level for the member, %s: %s" % (gitlab_user['name'], e)}) 'msg': "Not allowed to add the access level for the member, %s: %s" % (gitlab_user['name'], e)})
# state as absent
else:
changed_users.append("User, '%s', is not a member in the project. No change to report" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'OK',
'msg': "User, '%s', is not a member in the project. No change to report" % gitlab_user['name']})
# in case that a user is a member
else: else:
# remove the user from the project if state == 'present':
try: # compare the access level
if not module.check_mode: user_access_level = project.get_user_access_level(members, gitlab_user_id)
project.remove_user_from_project(gitlab_user_id, gitlab_project_id) if user_access_level == gitlab_user['access_level']:
changed = True changed_users.append("User, '%s', is already a member in the project. No change to report" % gitlab_user['name'])
changed_users.append("Successfully removed user, '%s', from the project" % gitlab_user['name']) changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'OK',
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'CHANGED', 'msg': "User, '%s', is already a member in the project. No change to report" % gitlab_user['name']})
'msg': "Successfully removed user, '%s', from the project" % gitlab_user['name']}) else:
except (gitlab.exceptions.GitlabDeleteError) as e: # update the access level for the user
try:
if not module.check_mode:
project.update_user_access_level(members, gitlab_user_id, gitlab_user['access_level'])
changed = True
changed_users.append("Successfully updated the access level for the user, '%s'" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'CHANGED',
'msg': "Successfully updated the access level for the user, '%s'" % gitlab_user['name']})
except (gitlab.exceptions.GitlabUpdateError) as e:
error = True
changed_users.append("Failed to updated the access level for the user, '%s'" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED',
'msg': "Not allowed to update the access level for the member, %s: %s" % (gitlab_user['name'], e)})
else:
# remove the user from the project
try:
if not module.check_mode:
project.remove_user_from_project(gitlab_user_id, gitlab_project_id)
changed = True
changed_users.append("Successfully removed user, '%s', from the project" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'CHANGED',
'msg': "Successfully removed user, '%s', from the project" % gitlab_user['name']})
except (gitlab.exceptions.GitlabDeleteError) as e:
error = True
changed_users.append("Failed to removed user, '%s', from the project" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED',
'msg': "Failed to remove user, '%s' from the project: %s" % (gitlab_user['name'], e)})
# if state = present and purge_users set delete users which are in members having give access level but not in gitlab_users
if state == 'present' and purge_users:
uppercase_names_in_gitlab_users_access = []
for name in gitlab_users_access:
uppercase_names_in_gitlab_users_access.append(name['name'].upper())
for member in members:
if member.access_level in purge_users and member.username.upper() not in uppercase_names_in_gitlab_users_access:
try:
if not module.check_mode:
project.remove_user_from_project(member.id, gitlab_project_id)
changed = True
changed_users.append("Successfully removed user '%s', from project. Was not in given list" % member.username)
changed_data.append({'gitlab_user': member.username, 'result': 'CHANGED',
'msg': "Successfully removed user '%s', from project. Was not in given list" % member.username})
except (gitlab.exceptions.GitlabDeleteError) as e:
error = True
changed_users.append("Failed to removed user, '%s', from the project" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED',
'msg': "Failed to remove user, '%s' from the project: %s" % (gitlab_user['name'], e)})
if len(gitlab_users_access) == 1 and error:
# if single user given and an error occurred return error for list errors will be per user
module.fail_json(msg="FAILED: '%s '" % changed_users[0], result_data=changed_data)
elif error:
module.fail_json(
msg='FAILED: At least one given user/permission could not be set', result_data=changed_data)
module.exit_json(changed=changed, msg='Successfully set memberships', result="\n".join(changed_users), result_data=changed_data)
elif gitlab_groups:
# group logic goes here
for gitlab_group in gitlab_groups:
group_id = project.get_group_id(gitlab_group['name'])
# group doesn't exist
if not group_id:
if state == 'absent':
changed_groups.append("group '%s' not found, and thus also not part of the project" % gitlab_group['name'])
changed_data.append({'gitlab_group': gitlab_group['name'], 'result': 'OK',
'msg': "group '%s' not found, and thus also not part of the project" % gitlab_group['name']})
else:
error = True error = True
changed_users.append("Failed to removed user, '%s', from the project" % gitlab_user['name']) changed_groups.append("group '%s' not found." % gitlab_group['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED', changed_data.append({'gitlab_group': gitlab_group['name'], 'result': 'FAILED',
'msg': "Failed to remove user, '%s' from the project: %s" % (gitlab_user['name'], e)}) 'msg': "group '%s' not found." % gitlab_group['name']})
continue
is_group_in_project = project.is_group_in_project(groups, group_id)
# if state = present and purge_users set delete users which are in members having give access level but not in gitlab_users # check if the group is in the project
if state == 'present' and purge_users: if not is_group_in_project:
uppercase_names_in_gitlab_users_access = [] if state == 'present':
for name in gitlab_users_access: # share project with the group
uppercase_names_in_gitlab_users_access.append(name['name'].upper()) try:
if not module.check_mode:
project.share_project_with_group(gitlab_project_id, group_id, access_level_int[gitlab_group['group_access_level']])
changed = True
changed_groups.append("Successfully shared project with group '%s'" % gitlab_group['name'])
changed_data.append({'gitlab_group': gitlab_group['name'], 'result': 'CHANGED',
'msg': "Successfully shared project with group '%s'" % gitlab_group['name']})
except (gitlab.exceptions.GitlabCreateError) as e:
error = False
changed_groups.append("Failed to share project with group '%s'" % gitlab_group['name'])
changed_data.append({'gitlab_group': gitlab_group['name'], 'result': 'OK',
'msg': "Failed to share project with group '%s': %s. No change to report." % (gitlab_group['name'], e)})
# state as absent
else:
changed_groups.append("Group, '%s', is not in the project. No change to report" % gitlab_group['name'])
changed_data.append({'gitlab_group': gitlab_group['name'], 'result': 'OK',
'msg': "Group, '%s', is not in the project. No change to report" % gitlab_group['name']})
# in case that a group is in the project
else:
if state == 'present':
# i have not seen a way to get/update the access level for a group in a project, we are skipping this
changed_groups.append("Group, '%s', is already in the project. No change to report" % gitlab_group['name'])
changed_data.append({'gitlab_group': gitlab_group['name'], 'result': 'OK',
'msg': "Group, '%s', is already in the project. No change to report" % gitlab_group['name']})
else:
# unshare the project with the group
try:
if not module.check_mode:
project.unshare_project_with_group(gitlab_project_id, group_id)
changed = True
changed_groups.append("Successfully unshared project with group, '%s'" % gitlab_group['name'])
changed_data.append({'gitlab_group': gitlab_group['name'], 'result': 'CHANGED',
'msg': "Successfully unshared project with group, '%s'" % gitlab_group['name']})
except (gitlab.exceptions.GitlabDeleteError) as e:
error = True
changed_groups.append("Failed to unshare project with group, '%s'" % gitlab_group['name'])
changed_data.append({'gitlab_group': gitlab_group['name'], 'result': 'FAILED',
'msg': "Failed to unshare project with group, '%s': %s" % (gitlab_group['name'], e)})
if len(gitlab_groups) == 1 and error:
# if single group given and an error occurred return error for list errors will be per group
module.fail_json(msg="FAILED: '%s '" % changed_groups[0], result_data=changed_data)
elif error:
module.fail_json(
msg='FAILED: At least one given group could not be set', result_data=changed_data)
for member in members: module.exit_json(changed=changed, msg='Successfully set memberships', result="\n".join(changed_groups), result_data=changed_data)
if member.access_level in purge_users and member.username.upper() not in uppercase_names_in_gitlab_users_access:
try:
if not module.check_mode:
project.remove_user_from_project(member.id, gitlab_project_id)
changed = True
changed_users.append("Successfully removed user '%s', from project. Was not in given list" % member.username)
changed_data.append({'gitlab_user': member.username, 'result': 'CHANGED',
'msg': "Successfully removed user '%s', from project. Was not in given list" % member.username})
except (gitlab.exceptions.GitlabDeleteError) as e:
error = True
changed_users.append("Failed to removed user, '%s', from the project" % gitlab_user['name'])
changed_data.append({'gitlab_user': gitlab_user['name'], 'result': 'FAILED',
'msg': "Failed to remove user, '%s' from the project: %s" % (gitlab_user['name'], e)})
if len(gitlab_users_access) == 1 and error:
# if single user given and an error occurred return error for list errors will be per user
module.fail_json(msg="FAILED: '%s '" % changed_users[0], result_data=changed_data)
elif error:
module.fail_json(
msg='FAILED: At least one given user/permission could not be set', result_data=changed_data)
module.exit_json(changed=changed, msg='Successfully set memberships', result="\n".join(changed_users), result_data=changed_data)
if __name__ == '__main__': if __name__ == '__main__':