diff --git a/source_control/gitlab_project.py b/source_control/gitlab_project.py
new file mode 100644
index 00000000000..602b9e832d7
--- /dev/null
+++ b/source_control/gitlab_project.py
@@ -0,0 +1,397 @@
+#!/usr/bin/python
+# (c) 2015, Werner Dijkerman (ikben@werner-dijkerman.nl)
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+
+DOCUMENTATION = '''
+---
+module: gitlab_project
+short_description: Creates/updates/deletes Gitlab Projects
+description:
+ - When the project does not exists in Gitlab, it will be created.
+ - When the project does exists and state=absent, the project will be deleted.
+ - When changes are made to the project, the project will be updated.
+version_added: "2.1"
+author: "Werner Dijkerman (@dj-wasabi)"
+requirements:
+ - pyapi-gitlab python module
+options:
+ server_url:
+ description:
+ - Url of Gitlab server, with protocol (http or https).
+ required: true
+ validate_certs:
+ description:
+ - When using https if SSL certificate needs to be verified.
+ required: false
+ default: true
+ aliases:
+ - verify_ssl
+ login_user:
+ description:
+ - Gitlab user name.
+ required: false
+ default: null
+ login_password:
+ description:
+ - Gitlab password for login_user
+ required: false
+ default: null
+ login_token:
+ description:
+ - Gitlab token for logging in.
+ required: false
+ default: null
+ group:
+ description:
+ - The name of the group of which this projects belongs to.
+ - When not provided, project will belong to user which is configured in 'login_user' or 'login_token'
+ - When provided with username, project will be created for this user. 'login_user' or 'login_token' needs admin rights.
+ required: false
+ default: null
+ name:
+ description:
+ - The name of the project
+ required: true
+ path:
+ description:
+ - The path of the project you want to create, this will be server_url//path
+ - If not supplied, name will be used.
+ required: false
+ default: null
+ description:
+ description:
+ - An description for the project.
+ required: false
+ default: null
+ issues_enabled:
+ description:
+ - Whether you want to create issues or not.
+ - Possible values are true and false.
+ required: false
+ default: true
+ merge_requests_enabled:
+ description:
+ - If merge requests can be made or not.
+ - Possible values are true and false.
+ required: false
+ default: true
+ wiki_enabled:
+ description:
+ - If an wiki for this project should be available or not.
+ - Possible values are true and false.
+ required: false
+ default: true
+ snippets_enabled:
+ description:
+ - If creating snippets should be available or not.
+ - Possible values are true and false.
+ required: false
+ default: true
+ public:
+ description:
+ - If the project is public available or not.
+ - Setting this to true is same as setting visibility_level to 20.
+ - Possible values are true and false.
+ required: false
+ default: false
+ visibility_level:
+ description:
+ - Private. visibility_level is 0. Project access must be granted explicitly for each user.
+ - Internal. visibility_level is 10. The project can be cloned by any logged in user.
+ - Public. visibility_level is 20. The project can be cloned without any authentication.
+ - Possible values are 0, 10 and 20.
+ required: false
+ default: 0
+ import_url:
+ description:
+ - Git repository which will me imported into gitlab.
+ - Gitlab server needs read access to this git repository.
+ required: false
+ default: false
+ state:
+ description:
+ - create or delete project.
+ - Possible values are present and absent.
+ required: false
+ default: "present"
+ choices: ["present", "absent"]
+'''
+
+EXAMPLES = '''
+- name: "Delete Gitlab Project"
+ local_action: gitlab_project
+ server_url="http://gitlab.dj-wasabi.local"
+ validate_certs=false
+ login_token="WnUzDsxjy8230-Dy_k"
+ name=my_first_project
+ state=absent
+
+- name: "Create Gitlab Project in group Ansible"
+ local_action: gitlab_project
+ server_url="https://gitlab.dj-wasabi.local"
+ validate_certs=true
+ login_user=dj-wasabi
+ login_password="MySecretPassword"
+ name=my_first_project
+ group=ansible
+ issues_enabled=false
+ wiki_enabled=true
+ snippets_enabled=true
+ import_url="http://git.example.com/example/lab.git"
+ state=present
+'''
+
+RETURN = '''# '''
+
+try:
+ import gitlab
+ HAS_GITLAB_PACKAGE = True
+except:
+ HAS_GITLAB_PACKAGE = False
+
+
+class GitLabProject(object):
+ def __init__(self, module, git):
+ self._module = module
+ self._gitlab = git
+
+ def createOrUpdateProject(self, project_exists, group_name, import_url, arguments):
+ is_user = False
+ group_id = self.getGroupId(group_name)
+ if not group_id:
+ group_id = self.getUserId(group_name)
+ is_user = True
+
+ if project_exists:
+ # Edit project
+ return self.updateProject(group_name, arguments)
+ else:
+ # Create project
+ if self._module.check_mode:
+ self._module.exit_json(changed=True)
+ return self.createProject(is_user, group_id, import_url, arguments)
+
+ def createProject(self, is_user, user_id, import_url, arguments):
+ if is_user:
+ return self._gitlab.createprojectuser(user_id=user_id, import_url=import_url, **arguments)
+ else:
+ group_id = user_id
+ return self._gitlab.createproject(namespace_id=group_id, import_url=import_url, **arguments)
+
+ def deleteProject(self, group_name, project_name):
+ if self.existsGroup(group_name):
+ project_owner = group_name
+ else:
+ project_owner = self._gitlab.currentuser()['username']
+
+ search_results = self._gitlab.searchproject(search=project_name)
+ for result in search_results:
+ owner = result['namespace']['name']
+ if owner == project_owner:
+ return self._gitlab.deleteproject(result['id'])
+
+ def existsProject(self, group_name, project_name):
+ if self.existsGroup(group_name):
+ project_owner = group_name
+ else:
+ project_owner = self._gitlab.currentuser()['username']
+
+ search_results = self._gitlab.searchproject(search=project_name)
+ for result in search_results:
+ owner = result['namespace']['name']
+ if owner == project_owner:
+ return True
+ return False
+
+ def existsGroup(self, group_name):
+ if group_name is not None:
+ # Find the group, if group not exists we try for user
+ for group in self._gitlab.getall(self._gitlab.getgroups):
+ if group['name'] == group_name:
+ return True
+
+ user_name = group_name
+ user_data = self._gitlab.getusers(search=user_name)
+ for data in user_data:
+ if 'id' in user_data:
+ return True
+ return False
+
+ def getGroupId(self, group_name):
+ if group_name is not None:
+ # Find the group, if group not exists we try for user
+ for group in self._gitlab.getall(self._gitlab.getgroups):
+ if group['name'] == group_name:
+ return group['id']
+
+ def getProjectId(self, group_name, project_name):
+ if self.existsGroup(group_name):
+ project_owner = group_name
+ else:
+ project_owner = self._gitlab.currentuser()['username']
+
+ search_results = self._gitlab.searchproject(search=project_name)
+ for result in search_results:
+ owner = result['namespace']['name']
+ if owner == project_owner:
+ return result['id']
+
+ def getUserId(self, user_name):
+ user_data = self._gitlab.getusers(search=user_name)
+
+ for data in user_data:
+ if 'id' in data:
+ return data['id']
+ return self._gitlab.currentuser()['id']
+
+ def to_bool(self, value):
+ if value:
+ return 1
+ else:
+ return 0
+
+ def updateProject(self, group_name, arguments):
+ project_changed = False
+ project_name = arguments['name']
+ project_id = self.getProjectId(group_name, project_name)
+ project_data = self._gitlab.getproject(project_id=project_id)
+
+ for arg_key, arg_value in arguments.items():
+ project_data_value = project_data[arg_key]
+
+ if isinstance(project_data_value, bool) or project_data_value is None:
+ to_bool = self.to_bool(project_data_value)
+ if to_bool != arg_value:
+ project_changed = True
+ continue
+ else:
+ if project_data_value != arg_value:
+ project_changed = True
+
+ if project_changed:
+ if self._module.check_mode:
+ self._module.exit_json(changed=True)
+ return self._gitlab.editproject(project_id=project_id, **arguments)
+ else:
+ return False
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ server_url=dict(required=True),
+ validate_certs=dict(required=False, default=True, type=bool, aliases=['verify_ssl']),
+ login_user=dict(required=False, no_log=True),
+ login_password=dict(required=False, no_log=True),
+ login_token=dict(required=False, no_log=True),
+ group=dict(required=False),
+ name=dict(required=True),
+ path=dict(required=False),
+ description=dict(required=False),
+ issues_enabled=dict(default=True, type=bool),
+ merge_requests_enabled=dict(default=True, type=bool),
+ wiki_enabled=dict(default=True, type=bool),
+ snippets_enabled=dict(default=True, type=bool),
+ public=dict(default=False, type=bool),
+ visibility_level=dict(default="0", choices=["0", "10", "20"]),
+ import_url=dict(required=False),
+ state=dict(default="present", choices=["present", 'absent']),
+ ),
+ supports_check_mode=True
+ )
+
+ if not HAS_GITLAB_PACKAGE:
+ module.fail_json(msg="Missing required gitlab module (check docs or install with: pip install pyapi-gitlab")
+
+ server_url = module.params['server_url']
+ verify_ssl = module.params['validate_certs']
+ login_user = module.params['login_user']
+ login_password = module.params['login_password']
+ login_token = module.params['login_token']
+ group_name = module.params['group']
+ project_name = module.params['name']
+ project_path = module.params['path']
+ description = module.params['description']
+ issues_enabled = module.params['issues_enabled']
+ merge_requests_enabled = module.params['merge_requests_enabled']
+ wiki_enabled = module.params['wiki_enabled']
+ snippets_enabled = module.params['snippets_enabled']
+ public = module.params['public']
+ visibility_level = module.params['visibility_level']
+ import_url = module.params['import_url']
+ state = module.params['state']
+
+ # We need both login_user and login_password or login_token, otherwise we fail.
+ if login_user is not None and login_password is not None:
+ use_credentials = True
+ elif login_token is not None:
+ use_credentials = False
+ else:
+ module.fail_json(msg="No login credentials are given. Use login_user with login_password, or login_token")
+
+ # Set project_path to project_name if it is empty.
+ if project_path is None:
+ project_path = project_name.replace(" ", "_")
+
+ # Gitlab API makes no difference between upper and lower cases, so we lower them.
+ project_name = project_name.lower()
+ project_path = project_path.lower()
+ if group_name is not None:
+ group_name = group_name.lower()
+
+ # Lets make an connection to the Gitlab server_url, with either login_user and login_password
+ # or with login_token
+ try:
+ if use_credentials:
+ git = gitlab.Gitlab(host=server_url)
+ git.login(user=login_user, password=login_password)
+ else:
+ git = gitlab.Gitlab(server_url, token=login_token, verify_ssl=verify_ssl)
+ except Exception, e:
+ module.fail_json(msg="Failed to connect to Gitlab server: %s " % e)
+
+ # Validate if project exists and take action based on "state"
+ project = GitLabProject(module, git)
+ project_exists = project.existsProject(group_name, project_name)
+
+ # Creating the project dict
+ arguments = {"name": project_name,
+ "path": project_path,
+ "description": description,
+ "issues_enabled": project.to_bool(issues_enabled),
+ "merge_requests_enabled": project.to_bool(merge_requests_enabled),
+ "wiki_enabled": project.to_bool(wiki_enabled),
+ "snippets_enabled": project.to_bool(snippets_enabled),
+ "public": project.to_bool(public),
+ "visibility_level": int(visibility_level)}
+
+ if project_exists and state == "absent":
+ project.deleteProject(group_name, project_name)
+ module.exit_json(changed=True, result="Successfully deleted project %s" % project_name)
+ else:
+ if state == "absent":
+ module.exit_json(changed=False, result="Project deleted or does not exists")
+ else:
+ if project.createOrUpdateProject(project_exists, group_name, import_url, arguments):
+ module.exit_json(changed=True, result="Successfully created or updated the project %s" % project_name)
+ else:
+ module.exit_json(changed=False)
+
+from ansible.module_utils.basic import *
+
+if __name__ == '__main__':
+ main()