Initial commit

This commit is contained in:
Ansible Core Team 2020-03-09 09:11:07 +00:00
commit aebc1b03fd
4861 changed files with 812621 additions and 0 deletions

View file

@ -0,0 +1,333 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 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
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: github_deploy_key
author: "Ali (@bincyber)"
short_description: Manages deploy keys for GitHub repositories.
description:
- "Adds or removes deploy keys for GitHub repositories. Supports authentication using username and password,
username and password and 2-factor authentication code (OTP), OAuth2 token, or personal access token. Admin
rights on the repository are required."
options:
github_url:
description:
- The base URL of the GitHub API
required: false
type: str
default: https://api.github.com
owner:
description:
- The name of the individual account or organization that owns the GitHub repository.
required: true
aliases: [ 'account', 'organization' ]
repo:
description:
- The name of the GitHub repository.
required: true
aliases: [ 'repository' ]
name:
description:
- The name for the deploy key.
required: true
aliases: [ 'title', 'label' ]
key:
description:
- The SSH public key to add to the repository as a deploy key.
required: true
read_only:
description:
- If C(true), the deploy key will only be able to read repository contents. Otherwise, the deploy key will be able to read and write.
type: bool
default: 'yes'
state:
description:
- The state of the deploy key.
default: "present"
choices: [ "present", "absent" ]
force:
description:
- If C(true), forcefully adds the deploy key by deleting any existing deploy key with the same public key or title.
type: bool
default: 'no'
username:
description:
- The username to authenticate with. Should not be set when using personal access token
password:
description:
- The password to authenticate with. Alternatively, a personal access token can be used instead of I(username) and I(password) combination.
token:
description:
- The OAuth2 token or personal access token to authenticate with. Mutually exclusive with I(password).
otp:
description:
- The 6 digit One Time Password for 2-Factor Authentication. Required together with I(username) and I(password).
aliases: ['2fa_token']
notes:
- "Refer to GitHub's API documentation here: https://developer.github.com/v3/repos/keys/."
'''
EXAMPLES = '''
# add a new read-only deploy key to a GitHub repository using basic authentication
- github_deploy_key:
owner: "johndoe"
repo: "example"
name: "new-deploy-key"
key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAwXxn7kIMNWzcDfou..."
read_only: yes
username: "johndoe"
password: "supersecretpassword"
# remove an existing deploy key from a GitHub repository
- github_deploy_key:
owner: "johndoe"
repository: "example"
name: "new-deploy-key"
key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAwXxn7kIMNWzcDfou..."
force: yes
username: "johndoe"
password: "supersecretpassword"
state: absent
# add a new deploy key to a GitHub repository, replace an existing key, use an OAuth2 token to authenticate
- github_deploy_key:
owner: "johndoe"
repository: "example"
name: "new-deploy-key"
key: "{{ lookup('file', '~/.ssh/github.pub') }}"
force: yes
token: "ABAQDAwXxn7kIMNWzcDfo..."
# re-add a deploy key to a GitHub repository but with a different name
- github_deploy_key:
owner: "johndoe"
repository: "example"
name: "replace-deploy-key"
key: "{{ lookup('file', '~/.ssh/github.pub') }}"
username: "johndoe"
password: "supersecretpassword"
# add a new deploy key to a GitHub repository using 2FA
- github_deploy_key:
owner: "johndoe"
repo: "example"
name: "new-deploy-key-2"
key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAwXxn7kIMNWzcDfou..."
username: "johndoe"
password: "supersecretpassword"
otp: 123456
# add a read-only deploy key to a repository hosted on GitHub Enterprise
- github_deploy_key:
github_url: "https://api.example.com"
owner: "janedoe"
repo: "example"
name: "new-deploy-key"
key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAwXxn7kIMNWzcDfou..."
read_only: yes
username: "janedoe"
password: "supersecretpassword"
'''
RETURN = '''
msg:
description: the status message describing what occurred
returned: always
type: str
sample: "Deploy key added successfully"
http_status_code:
description: the HTTP status code returned by the GitHub API
returned: failed
type: int
sample: 400
error:
description: the error message returned by the GitHub API
returned: failed
type: str
sample: "key is already in use"
id:
description: the key identifier assigned by GitHub for the deploy key
returned: changed
type: int
sample: 24381901
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
from re import findall
class GithubDeployKey(object):
def __init__(self, module):
self.module = module
self.github_url = self.module.params['github_url']
self.name = module.params['name']
self.key = module.params['key']
self.state = module.params['state']
self.read_only = module.params.get('read_only', True)
self.force = module.params.get('force', False)
self.username = module.params.get('username', None)
self.password = module.params.get('password', None)
self.token = module.params.get('token', None)
self.otp = module.params.get('otp', None)
@property
def url(self):
owner = self.module.params['owner']
repo = self.module.params['repo']
return "{0}/repos/{1}/{2}/keys".format(self.github_url, owner, repo)
@property
def headers(self):
if self.username is not None and self.password is not None:
self.module.params['url_username'] = self.username
self.module.params['url_password'] = self.password
self.module.params['force_basic_auth'] = True
if self.otp is not None:
return {"X-GitHub-OTP": self.otp}
elif self.token is not None:
return {"Authorization": "token {0}".format(self.token)}
else:
return None
def paginate(self, url):
while url:
resp, info = fetch_url(self.module, url, headers=self.headers, method="GET")
if info["status"] == 200:
yield self.module.from_json(resp.read())
links = {}
for x, y in findall(r'<([^>]+)>;\s*rel="(\w+)"', info["link"]):
links[y] = x
url = links.get('next')
else:
self.handle_error(method="GET", info=info)
def get_existing_key(self):
for keys in self.paginate(self.url):
if keys:
for i in keys:
existing_key_id = str(i["id"])
if i["key"].split() == self.key.split()[:2]:
return existing_key_id
elif i['title'] == self.name and self.force:
return existing_key_id
else:
return None
def add_new_key(self):
request_body = {"title": self.name, "key": self.key, "read_only": self.read_only}
resp, info = fetch_url(self.module, self.url, data=self.module.jsonify(request_body), headers=self.headers, method="POST", timeout=30)
status_code = info["status"]
if status_code == 201:
response_body = self.module.from_json(resp.read())
key_id = response_body["id"]
self.module.exit_json(changed=True, msg="Deploy key successfully added", id=key_id)
elif status_code == 422:
self.module.exit_json(changed=False, msg="Deploy key already exists")
else:
self.handle_error(method="POST", info=info)
def remove_existing_key(self, key_id):
resp, info = fetch_url(self.module, "{0}/{1}".format(self.url, key_id), headers=self.headers, method="DELETE")
status_code = info["status"]
if status_code == 204:
if self.state == 'absent':
self.module.exit_json(changed=True, msg="Deploy key successfully deleted", id=key_id)
else:
self.handle_error(method="DELETE", info=info, key_id=key_id)
def handle_error(self, method, info, key_id=None):
status_code = info['status']
body = info.get('body')
if body:
err = self.module.from_json(body)['message']
if status_code == 401:
self.module.fail_json(msg="Failed to connect to {0} due to invalid credentials".format(self.github_url), http_status_code=status_code, error=err)
elif status_code == 404:
self.module.fail_json(msg="GitHub repository does not exist", http_status_code=status_code, error=err)
else:
if method == "GET":
self.module.fail_json(msg="Failed to retrieve existing deploy keys", http_status_code=status_code, error=err)
elif method == "POST":
self.module.fail_json(msg="Failed to add deploy key", http_status_code=status_code, error=err)
elif method == "DELETE":
self.module.fail_json(msg="Failed to delete existing deploy key", id=key_id, http_status_code=status_code, error=err)
def main():
module = AnsibleModule(
argument_spec=dict(
github_url=dict(required=False, type='str', default="https://api.github.com"),
owner=dict(required=True, type='str', aliases=['account', 'organization']),
repo=dict(required=True, type='str', aliases=['repository']),
name=dict(required=True, type='str', aliases=['title', 'label']),
key=dict(required=True, type='str'),
read_only=dict(required=False, type='bool', default=True),
state=dict(default='present', choices=['present', 'absent']),
force=dict(required=False, type='bool', default=False),
username=dict(required=False, type='str'),
password=dict(required=False, type='str', no_log=True),
otp=dict(required=False, type='int', aliases=['2fa_token'], no_log=True),
token=dict(required=False, type='str', no_log=True)
),
mutually_exclusive=[
['password', 'token']
],
required_together=[
['username', 'password'],
['otp', 'username', 'password']
],
required_one_of=[
['username', 'token']
],
supports_check_mode=True,
)
deploy_key = GithubDeployKey(module)
if module.check_mode:
key_id = deploy_key.get_existing_key()
if deploy_key.state == "present" and key_id is None:
module.exit_json(changed=True)
elif deploy_key.state == "present" and key_id is not None:
module.exit_json(changed=False)
# to forcefully modify an existing key, the existing key must be deleted first
if deploy_key.state == 'absent' or deploy_key.force:
key_id = deploy_key.get_existing_key()
if key_id is not None:
deploy_key.remove_existing_key(key_id)
elif deploy_key.state == 'absent':
module.exit_json(changed=False, msg="Deploy key does not exist")
if deploy_key.state == "present":
deploy_key.add_new_key()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,197 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Phillip Gentry <phillip@cx.com>
# 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
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: github_hooks
short_description: Manages GitHub service hooks.
deprecated:
removed_in: "2.12"
why: Replaced by more granular modules
alternative: Use M(github_webhook) and M(github_webhook_info) instead.
description:
- Adds service hooks and removes service hooks that have an error status.
options:
user:
description:
- GitHub username.
required: true
oauthkey:
description:
- The oauth key provided by GitHub. It can be found/generated on GitHub under "Edit Your Profile" >> "Developer settings" >> "Personal Access Tokens"
required: true
repo:
description:
- >
This is the API url for the repository you want to manage hooks for. It should be in the form of: https://api.github.com/repos/user:/repo:.
Note this is different than the normal repo url.
required: true
hookurl:
description:
- When creating a new hook, this is the url that you want GitHub to post to. It is only required when creating a new hook.
required: false
action:
description:
- This tells the githooks module what you want it to do.
required: true
choices: [ "create", "cleanall", "list", "clean504" ]
validate_certs:
description:
- If C(no), SSL certificates for the target repo will not be validated. This should only be used
on personally controlled sites using self-signed certificates.
required: false
default: 'yes'
type: bool
content_type:
description:
- Content type to use for requests made to the webhook
required: false
default: 'json'
choices: ['json', 'form']
author: "Phillip Gentry, CX Inc (@pcgentry)"
'''
EXAMPLES = '''
# Example creating a new service hook. It ignores duplicates.
- github_hooks:
action: create
hookurl: http://11.111.111.111:2222
user: '{{ gituser }}'
oauthkey: '{{ oauthkey }}'
repo: https://api.github.com/repos/pcgentry/Github-Auto-Deploy
# Cleaning all hooks for this repo that had an error on the last update. Since this works for all hooks in a repo it is probably best that this would
# be called from a handler.
- github_hooks:
action: cleanall
user: '{{ gituser }}'
oauthkey: '{{ oauthkey }}'
repo: '{{ repo }}'
delegate_to: localhost
'''
import json
import base64
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
from ansible.module_utils._text import to_bytes
def request(module, url, user, oauthkey, data='', method='GET'):
auth = base64.b64encode(to_bytes('%s:%s' % (user, oauthkey)).replace('\n', ''))
headers = {
'Authorization': 'Basic %s' % auth,
}
response, info = fetch_url(module, url, headers=headers, data=data, method=method)
return response, info
def _list(module, oauthkey, repo, user):
url = "%s/hooks" % repo
response, info = request(module, url, user, oauthkey)
if info['status'] != 200:
return False, ''
else:
return False, response.read()
def _clean504(module, oauthkey, repo, user):
current_hooks = _list(module, oauthkey, repo, user)[1]
decoded = json.loads(current_hooks)
for hook in decoded:
if hook['last_response']['code'] == 504:
_delete(module, oauthkey, repo, user, hook['id'])
return 0, current_hooks
def _cleanall(module, oauthkey, repo, user):
current_hooks = _list(module, oauthkey, repo, user)[1]
decoded = json.loads(current_hooks)
for hook in decoded:
if hook['last_response']['code'] != 200:
_delete(module, oauthkey, repo, user, hook['id'])
return 0, current_hooks
def _create(module, hookurl, oauthkey, repo, user, content_type):
url = "%s/hooks" % repo
values = {
"active": True,
"name": "web",
"config": {
"url": "%s" % hookurl,
"content_type": "%s" % content_type
}
}
data = json.dumps(values)
response, info = request(module, url, user, oauthkey, data=data, method='POST')
if info['status'] != 200:
return 0, '[]'
else:
return 0, response.read()
def _delete(module, oauthkey, repo, user, hookid):
url = "%s/hooks/%s" % (repo, hookid)
response, info = request(module, url, user, oauthkey, method='DELETE')
return response.read()
def main():
module = AnsibleModule(
argument_spec=dict(
action=dict(required=True, choices=['list', 'clean504', 'cleanall', 'create']),
hookurl=dict(required=False),
oauthkey=dict(required=True, no_log=True),
repo=dict(required=True),
user=dict(required=True),
validate_certs=dict(default='yes', type='bool'),
content_type=dict(default='json', choices=['json', 'form']),
)
)
action = module.params['action']
hookurl = module.params['hookurl']
oauthkey = module.params['oauthkey']
repo = module.params['repo']
user = module.params['user']
content_type = module.params['content_type']
if action == "list":
(rc, out) = _list(module, oauthkey, repo, user)
if action == "clean504":
(rc, out) = _clean504(module, oauthkey, repo, user)
if action == "cleanall":
(rc, out) = _cleanall(module, oauthkey, repo, user)
if action == "create":
(rc, out) = _create(module, hookurl, oauthkey, repo, user, content_type)
if rc != 0:
module.fail_json(msg="failed", result=out)
module.exit_json(msg="success", result=out)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,118 @@
#!/usr/bin/python
# Copyright: (c) 2017-18, Abhijeet Kasurde <akasurde@redhat.com>
#
# 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
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
module: github_issue
short_description: View GitHub issue.
description:
- View GitHub issue for a given repository and organization.
options:
repo:
description:
- Name of repository from which issue needs to be retrieved.
required: true
organization:
description:
- Name of the GitHub organization in which the repository is hosted.
required: true
issue:
description:
- Issue number for which information is required.
required: true
action:
description:
- Get various details about issue depending upon action specified.
default: 'get_status'
choices:
- 'get_status'
author:
- Abhijeet Kasurde (@Akasurde)
'''
RETURN = '''
get_status:
description: State of the GitHub issue
type: str
returned: success
sample: open, closed
'''
EXAMPLES = '''
- name: Check if GitHub issue is closed or not
github_issue:
organization: ansible
repo: ansible
issue: 23642
action: get_status
register: r
- name: Take action depending upon issue status
debug:
msg: Do something when issue 23642 is open
when: r.issue_status == 'open'
'''
import json
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
def main():
module = AnsibleModule(
argument_spec=dict(
organization=dict(required=True),
repo=dict(required=True),
issue=dict(type='int', required=True),
action=dict(choices=['get_status'], default='get_status'),
),
supports_check_mode=True,
)
organization = module.params['organization']
repo = module.params['repo']
issue = module.params['issue']
action = module.params['action']
result = dict()
headers = {
'Content-Type': 'application/json',
'Accept': 'application/vnd.github.v3+json',
}
url = "https://api.github.com/repos/%s/%s/issues/%s" % (organization, repo, issue)
response, info = fetch_url(module, url, headers=headers)
if not (200 <= info['status'] < 400):
if info['status'] == 404:
module.fail_json(msg="Failed to find issue %s" % issue)
module.fail_json(msg="Failed to send request to %s: %s" % (url, info['msg']))
gh_obj = json.loads(response.read())
if action == 'get_status' or action is None:
if module.check_mode:
result.update(changed=True)
else:
result.update(changed=True, issue_status=gh_obj['state'])
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,242 @@
#!/usr/bin/python
# Copyright: Ansible Project
# 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
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
module: github_key
short_description: Manage GitHub access keys.
description:
- Creates, removes, or updates GitHub access keys.
options:
token:
description:
- GitHub Access Token with permission to list and create public keys.
required: true
name:
description:
- SSH key name
required: true
pubkey:
description:
- SSH public key value. Required when C(state=present).
state:
description:
- Whether to remove a key, ensure that it exists, or update its value.
choices: ['present', 'absent']
default: 'present'
force:
description:
- The default is C(yes), which will replace the existing remote key
if it's different than C(pubkey). If C(no), the key will only be
set if no key with the given C(name) exists.
type: bool
default: 'yes'
author: Robert Estelle (@erydo)
'''
RETURN = '''
deleted_keys:
description: An array of key objects that were deleted. Only present on state=absent
type: list
returned: When state=absent
sample: [{'id': 0, 'key': 'BASE64 encoded key', 'url': 'http://example.com/github key', 'created_at': 'YYYY-MM-DDTHH:MM:SZ', 'read_only': False}]
matching_keys:
description: An array of keys matching the specified name. Only present on state=present
type: list
returned: When state=present
sample: [{'id': 0, 'key': 'BASE64 encoded key', 'url': 'http://example.com/github key', 'created_at': 'YYYY-MM-DDTHH:MM:SZ', 'read_only': False}]
key:
description: Metadata about the key just created. Only present on state=present
type: dict
returned: success
sample: {'id': 0, 'key': 'BASE64 encoded key', 'url': 'http://example.com/github key', 'created_at': 'YYYY-MM-DDTHH:MM:SZ', 'read_only': False}
'''
EXAMPLES = '''
- name: Read SSH public key to authorize
shell: cat /home/foo/.ssh/id_rsa.pub
register: ssh_pub_key
- name: Authorize key with GitHub
local_action:
module: github_key
name: Access Key for Some Machine
token: '{{ github_access_token }}'
pubkey: '{{ ssh_pub_key.stdout }}'
'''
import json
import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
API_BASE = 'https://api.github.com'
class GitHubResponse(object):
def __init__(self, response, info):
self.content = response.read()
self.info = info
def json(self):
return json.loads(self.content)
def links(self):
links = {}
if 'link' in self.info:
link_header = self.info['link']
matches = re.findall('<([^>]+)>; rel="([^"]+)"', link_header)
for url, rel in matches:
links[rel] = url
return links
class GitHubSession(object):
def __init__(self, module, token):
self.module = module
self.token = token
def request(self, method, url, data=None):
headers = {
'Authorization': 'token %s' % self.token,
'Content-Type': 'application/json',
'Accept': 'application/vnd.github.v3+json',
}
response, info = fetch_url(
self.module, url, method=method, data=data, headers=headers)
if not (200 <= info['status'] < 400):
self.module.fail_json(
msg=(" failed to send request %s to %s: %s"
% (method, url, info['msg'])))
return GitHubResponse(response, info)
def get_all_keys(session):
url = API_BASE + '/user/keys'
result = []
while url:
r = session.request('GET', url)
result.extend(r.json())
url = r.links().get('next')
return result
def create_key(session, name, pubkey, check_mode):
if check_mode:
from datetime import datetime
now = datetime.utcnow()
return {
'id': 0,
'key': pubkey,
'title': name,
'url': 'http://example.com/CHECK_MODE_GITHUB_KEY',
'created_at': datetime.strftime(now, '%Y-%m-%dT%H:%M:%SZ'),
'read_only': False,
'verified': False
}
else:
return session.request(
'POST',
API_BASE + '/user/keys',
data=json.dumps({'title': name, 'key': pubkey})).json()
def delete_keys(session, to_delete, check_mode):
if check_mode:
return
for key in to_delete:
session.request('DELETE', API_BASE + '/user/keys/%s' % key["id"])
def ensure_key_absent(session, name, check_mode):
to_delete = [key for key in get_all_keys(session) if key['title'] == name]
delete_keys(session, to_delete, check_mode=check_mode)
return {'changed': bool(to_delete),
'deleted_keys': to_delete}
def ensure_key_present(module, session, name, pubkey, force, check_mode):
all_keys = get_all_keys(session)
matching_keys = [k for k in all_keys if k['title'] == name]
deleted_keys = []
new_signature = pubkey.split(' ')[1]
for key in all_keys:
existing_signature = key['key'].split(' ')[1]
if new_signature == existing_signature and key['title'] != name:
module.fail_json(msg=(
"another key with the same content is already registered "
"under the name |{0}|").format(key['title']))
if matching_keys and force and matching_keys[0]['key'].split(' ')[1] != new_signature:
delete_keys(session, matching_keys, check_mode=check_mode)
(deleted_keys, matching_keys) = (matching_keys, [])
if not matching_keys:
key = create_key(session, name, pubkey, check_mode=check_mode)
else:
key = matching_keys[0]
return {
'changed': bool(deleted_keys or not matching_keys),
'deleted_keys': deleted_keys,
'matching_keys': matching_keys,
'key': key
}
def main():
argument_spec = {
'token': {'required': True, 'no_log': True},
'name': {'required': True},
'pubkey': {},
'state': {'choices': ['present', 'absent'], 'default': 'present'},
'force': {'default': True, 'type': 'bool'},
}
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)
token = module.params['token']
name = module.params['name']
state = module.params['state']
force = module.params['force']
pubkey = module.params.get('pubkey')
if pubkey:
pubkey_parts = pubkey.split(' ')
# Keys consist of a protocol, the key data, and an optional comment.
if len(pubkey_parts) < 2:
module.fail_json(msg='"pubkey" parameter has an invalid format')
elif state == 'present':
module.fail_json(msg='"pubkey" is required when state=present')
session = GitHubSession(module, token)
if state == 'present':
result = ensure_key_present(module, session, name, pubkey, force=force,
check_mode=module.check_mode)
elif state == 'absent':
result = ensure_key_absent(session, name, check_mode=module.check_mode)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,218 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: Ansible Team
# 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
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: github_release
short_description: Interact with GitHub Releases
description:
- Fetch metadata about GitHub Releases
options:
token:
description:
- GitHub Personal Access Token for authenticating. Mutually exclusive with C(password).
user:
description:
- The GitHub account that owns the repository
required: true
password:
description:
- The GitHub account password for the user. Mutually exclusive with C(token).
repo:
description:
- Repository name
required: true
action:
description:
- Action to perform
required: true
choices: [ 'latest_release', 'create_release' ]
tag:
description:
- Tag name when creating a release. Required when using action is set to C(create_release).
target:
description:
- Target of release when creating a release
name:
description:
- Name of release when creating a release
body:
description:
- Description of the release when creating a release
draft:
description:
- Sets if the release is a draft or not. (boolean)
type: 'bool'
default: 'no'
prerelease:
description:
- Sets if the release is a prerelease or not. (boolean)
type: bool
default: 'no'
author:
- "Adrian Moisey (@adrianmoisey)"
requirements:
- "github3.py >= 1.0.0a3"
'''
EXAMPLES = '''
- name: Get latest release of a public repository
github_release:
user: ansible
repo: ansible
action: latest_release
- name: Get latest release of testuseer/testrepo
github_release:
token: tokenabc1234567890
user: testuser
repo: testrepo
action: latest_release
- name: Get latest release of test repo using username and password. Ansible 2.4.
github_release:
user: testuser
password: secret123
repo: testrepo
action: latest_release
- name: Create a new release
github_release:
token: tokenabc1234567890
user: testuser
repo: testrepo
action: create_release
tag: test
target: master
name: My Release
body: Some description
'''
RETURN = '''
create_release:
description:
- Version of the created release
- "For Ansible version 2.5 and later, if specified release version already exists, then State is unchanged"
- "For Ansible versions prior to 2.5, if specified release version already exists, then State is skipped"
type: str
returned: success
sample: 1.1.0
latest_release:
description: Version of the latest release
type: str
returned: success
sample: 1.1.0
'''
import traceback
GITHUB_IMP_ERR = None
try:
import github3
HAS_GITHUB_API = True
except ImportError:
GITHUB_IMP_ERR = traceback.format_exc()
HAS_GITHUB_API = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils._text import to_native
def main():
module = AnsibleModule(
argument_spec=dict(
repo=dict(required=True),
user=dict(required=True),
password=dict(no_log=True),
token=dict(no_log=True),
action=dict(
required=True, choices=['latest_release', 'create_release']),
tag=dict(type='str'),
target=dict(type='str'),
name=dict(type='str'),
body=dict(type='str'),
draft=dict(type='bool', default=False),
prerelease=dict(type='bool', default=False),
),
supports_check_mode=True,
mutually_exclusive=(('password', 'token'),),
required_if=[('action', 'create_release', ['tag']),
('action', 'create_release', ['password', 'token'], True)],
)
if not HAS_GITHUB_API:
module.fail_json(msg=missing_required_lib('github3.py >= 1.0.0a3'),
exception=GITHUB_IMP_ERR)
repo = module.params['repo']
user = module.params['user']
password = module.params['password']
login_token = module.params['token']
action = module.params['action']
tag = module.params.get('tag')
target = module.params.get('target')
name = module.params.get('name')
body = module.params.get('body')
draft = module.params.get('draft')
prerelease = module.params.get('prerelease')
# login to github
try:
if password:
gh_obj = github3.login(user, password=password)
elif login_token:
gh_obj = github3.login(token=login_token)
else:
gh_obj = github3.GitHub()
# test if we're actually logged in
if password or login_token:
gh_obj.me()
except github3.exceptions.AuthenticationFailed as e:
module.fail_json(msg='Failed to connect to GitHub: %s' % to_native(e),
details="Please check username and password or token "
"for repository %s" % repo)
repository = gh_obj.repository(user, repo)
if not repository:
module.fail_json(msg="Repository %s/%s doesn't exist" % (user, repo))
if action == 'latest_release':
release = repository.latest_release()
if release:
module.exit_json(tag=release.tag_name)
else:
module.exit_json(tag=None)
if action == 'create_release':
release_exists = repository.release_from_tag(tag)
if release_exists:
module.exit_json(changed=False, msg="Release for tag %s already exists." % tag)
release = repository.create_release(
tag, target, name, body, draft, prerelease)
if release:
module.exit_json(changed=True, tag=release.tag_name)
else:
module.exit_json(changed=False, tag=None)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,284 @@
#!/usr/bin/python
#
# Copyright: (c) 2018, Ansible Project
# 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)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: github_webhook
short_description: Manage GitHub webhooks
description:
- "Create and delete GitHub webhooks"
requirements:
- "PyGithub >= 1.3.5"
options:
repository:
description:
- Full name of the repository to configure a hook for
required: true
aliases:
- repo
url:
description:
- URL to which payloads will be delivered
required: true
content_type:
description:
- The media type used to serialize the payloads
required: false
choices: [ form, json ]
default: form
secret:
description:
- The shared secret between GitHub and the payload URL.
required: false
insecure_ssl:
description:
- >
Flag to indicate that GitHub should skip SSL verification when calling
the hook.
required: false
type: bool
default: false
events:
description:
- >
A list of GitHub events the hook is triggered for. Events are listed at
U(https://developer.github.com/v3/activity/events/types/). Required
unless C(state) is C(absent)
required: false
active:
description:
- Whether or not the hook is active
required: false
type: bool
default: true
state:
description:
- Whether the hook should be present or absent
required: false
choices: [ absent, present ]
default: present
user:
description:
- User to authenticate to GitHub as
required: true
password:
description:
- Password to authenticate to GitHub with
required: false
token:
description:
- Token to authenticate to GitHub with
required: false
github_url:
description:
- Base URL of the GitHub API
required: false
default: https://api.github.com
author:
- "Chris St. Pierre (@stpierre)"
'''
EXAMPLES = '''
- name: create a new webhook that triggers on push (password auth)
github_webhook:
repository: ansible/ansible
url: https://www.example.com/hooks/
events:
- push
user: "{{ github_user }}"
password: "{{ github_password }}"
- name: create a new webhook in a github enterprise installation with multiple event triggers (token auth)
github_webhook:
repository: myorg/myrepo
url: https://jenkins.example.com/ghprbhook/
content_type: json
secret: "{{ github_shared_secret }}"
insecure_ssl: True
events:
- issue_comment
- pull_request
user: "{{ github_user }}"
token: "{{ github_user_api_token }}"
github_url: https://github.example.com
- name: delete a webhook (password auth)
github_webhook:
repository: ansible/ansible
url: https://www.example.com/hooks/
state: absent
user: "{{ github_user }}"
password: "{{ github_password }}"
'''
RETURN = '''
---
hook_id:
description: The GitHub ID of the hook created/updated
returned: when state is 'present'
type: int
sample: 6206
'''
import traceback
GITHUB_IMP_ERR = None
try:
import github
HAS_GITHUB = True
except ImportError:
GITHUB_IMP_ERR = traceback.format_exc()
HAS_GITHUB = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils._text import to_native
def _create_hook_config(module):
return {
"url": module.params["url"],
"content_type": module.params["content_type"],
"secret": module.params.get("secret"),
"insecure_ssl": "1" if module.params["insecure_ssl"] else "0"
}
def create_hook(repo, module):
config = _create_hook_config(module)
try:
hook = repo.create_hook(
name="web",
config=config,
events=module.params["events"],
active=module.params["active"])
except github.GithubException as err:
module.fail_json(msg="Unable to create hook for repository %s: %s" % (
repo.full_name, to_native(err)))
data = {"hook_id": hook.id}
return True, data
def update_hook(repo, hook, module):
config = _create_hook_config(module)
try:
hook.update()
hook.edit(
name="web",
config=config,
events=module.params["events"],
active=module.params["active"])
changed = hook.update()
except github.GithubException as err:
module.fail_json(msg="Unable to modify hook for repository %s: %s" % (
repo.full_name, to_native(err)))
data = {"hook_id": hook.id}
return changed, data
def main():
module = AnsibleModule(
argument_spec=dict(
repository=dict(type='str', required=True, aliases=['repo']),
url=dict(type='str', required=True),
content_type=dict(
type='str',
choices=('json', 'form'),
required=False,
default='form'),
secret=dict(type='str', required=False, no_log=True),
insecure_ssl=dict(type='bool', required=False, default=False),
events=dict(type='list', elements='str', required=False),
active=dict(type='bool', required=False, default=True),
state=dict(
type='str',
required=False,
choices=('absent', 'present'),
default='present'),
user=dict(type='str', required=True),
password=dict(type='str', required=False, no_log=True),
token=dict(type='str', required=False, no_log=True),
github_url=dict(
type='str', required=False, default="https://api.github.com")),
mutually_exclusive=(('password', 'token'),),
required_one_of=(("password", "token"),),
required_if=(("state", "present", ("events",)),),
)
if not HAS_GITHUB:
module.fail_json(msg=missing_required_lib('PyGithub'),
exception=GITHUB_IMP_ERR)
try:
github_conn = github.Github(
module.params["user"],
module.params.get("password") or module.params.get("token"),
base_url=module.params["github_url"])
except github.GithubException as err:
module.fail_json(msg="Could not connect to GitHub at %s: %s" % (
module.params["github_url"], to_native(err)))
try:
repo = github_conn.get_repo(module.params["repository"])
except github.BadCredentialsException as err:
module.fail_json(msg="Could not authenticate to GitHub at %s: %s" % (
module.params["github_url"], to_native(err)))
except github.UnknownObjectException as err:
module.fail_json(
msg="Could not find repository %s in GitHub at %s: %s" % (
module.params["repository"], module.params["github_url"],
to_native(err)))
except Exception as err:
module.fail_json(
msg="Could not fetch repository %s from GitHub at %s: %s" %
(module.params["repository"], module.params["github_url"],
to_native(err)),
exception=traceback.format_exc())
hook = None
try:
for hook in repo.get_hooks():
if hook.config.get("url") == module.params["url"]:
break
else:
hook = None
except github.GithubException as err:
module.fail_json(msg="Unable to get hooks from repository %s: %s" % (
module.params["repository"], to_native(err)))
changed = False
data = {}
if hook is None and module.params["state"] == "present":
changed, data = create_hook(repo, module)
elif hook is not None and module.params["state"] == "absent":
try:
hook.delete()
except github.GithubException as err:
module.fail_json(
msg="Unable to delete hook from repository %s: %s" % (
repo.full_name, to_native(err)))
else:
changed = True
elif hook is not None and module.params["state"] == "present":
changed, data = update_hook(repo, hook, module)
# else, there is no hook and we want there to be no hook
module.exit_json(changed=changed, **data)
if __name__ == '__main__':
main()

View file

@ -0,0 +1 @@
github_webhook_info.py

View file

@ -0,0 +1,174 @@
#!/usr/bin/python
#
# Copyright: (c) 2018, Ansible Project
# 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)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: github_webhook_info
short_description: Query information about GitHub webhooks
description:
- "Query information about GitHub webhooks"
- This module was called C(github_webhook_facts) before Ansible 2.9. The usage did not change.
requirements:
- "PyGithub >= 1.3.5"
options:
repository:
description:
- Full name of the repository to configure a hook for
required: true
aliases:
- repo
user:
description:
- User to authenticate to GitHub as
required: true
password:
description:
- Password to authenticate to GitHub with
required: false
token:
description:
- Token to authenticate to GitHub with
required: false
github_url:
description:
- Base URL of the github api
required: false
default: https://api.github.com
author:
- "Chris St. Pierre (@stpierre)"
'''
EXAMPLES = '''
- name: list hooks for a repository (password auth)
github_webhook_info:
repository: ansible/ansible
user: "{{ github_user }}"
password: "{{ github_password }}"
register: ansible_webhooks
- name: list hooks for a repository on GitHub Enterprise (token auth)
github_webhook_info:
repository: myorg/myrepo
user: "{{ github_user }}"
token: "{{ github_user_api_token }}"
github_url: https://github.example.com/api/v3/
register: myrepo_webhooks
'''
RETURN = '''
---
hooks:
description: A list of hooks that exist for the repo
returned: always
type: list
sample: >
[{"has_shared_secret": true,
"url": "https://jenkins.example.com/ghprbhook/",
"events": ["issue_comment", "pull_request"],
"insecure_ssl": "1",
"content_type": "json",
"active": true,
"id": 6206,
"last_response": {"status": "active", "message": "OK", "code": 200}}]
'''
import traceback
GITHUB_IMP_ERR = None
try:
import github
HAS_GITHUB = True
except ImportError:
GITHUB_IMP_ERR = traceback.format_exc()
HAS_GITHUB = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils._text import to_native
def _munge_hook(hook_obj):
retval = {
"active": hook_obj.active,
"events": hook_obj.events,
"id": hook_obj.id,
"url": hook_obj.url,
}
retval.update(hook_obj.config)
retval["has_shared_secret"] = "secret" in retval
if "secret" in retval:
del retval["secret"]
retval["last_response"] = hook_obj.last_response.raw_data
return retval
def main():
module = AnsibleModule(
argument_spec=dict(
repository=dict(type='str', required=True, aliases=["repo"]),
user=dict(type='str', required=True),
password=dict(type='str', required=False, no_log=True),
token=dict(type='str', required=False, no_log=True),
github_url=dict(
type='str', required=False, default="https://api.github.com")),
mutually_exclusive=(('password', 'token'), ),
required_one_of=(("password", "token"), ),
supports_check_mode=True)
if module._name == 'github_webhook_facts':
module.deprecate("The 'github_webhook_facts' module has been renamed to 'github_webhook_info'", version='2.13')
if not HAS_GITHUB:
module.fail_json(msg=missing_required_lib('PyGithub'),
exception=GITHUB_IMP_ERR)
try:
github_conn = github.Github(
module.params["user"],
module.params.get("password") or module.params.get("token"),
base_url=module.params["github_url"])
except github.GithubException as err:
module.fail_json(msg="Could not connect to GitHub at %s: %s" % (
module.params["github_url"], to_native(err)))
try:
repo = github_conn.get_repo(module.params["repository"])
except github.BadCredentialsException as err:
module.fail_json(msg="Could not authenticate to GitHub at %s: %s" % (
module.params["github_url"], to_native(err)))
except github.UnknownObjectException as err:
module.fail_json(
msg="Could not find repository %s in GitHub at %s: %s" % (
module.params["repository"], module.params["github_url"],
to_native(err)))
except Exception as err:
module.fail_json(
msg="Could not fetch repository %s from GitHub at %s: %s" %
(module.params["repository"], module.params["github_url"],
to_native(err)),
exception=traceback.format_exc())
try:
hooks = [_munge_hook(h) for h in repo.get_hooks()]
except github.GithubException as err:
module.fail_json(
msg="Unable to get hooks from repository %s: %s" %
(module.params["repository"], to_native(err)),
exception=traceback.format_exc())
module.exit_json(changed=False, hooks=hooks)
if __name__ == '__main__':
main()