From 97e386f85be886e333b39743ce4e62983824bb03 Mon Sep 17 00:00:00 2001 From: Aleksei Loginov Date: Fri, 6 Jun 2025 06:15:47 +0200 Subject: [PATCH] Add api_url in github_key module (#10191) * github_key: add api_url parameter for GitHub Enterprise support * github_key: add tests * Add changelog * Review fixes: remove test from CI & fix version --------- Co-authored-by: Aleksei Loginov --- ...10191-github_key-add-api_url-parameter.yml | 2 + plugins/modules/github_key.py | 31 +++++++--- tests/integration/targets/github_key/aliases | 6 ++ .../targets/github_key/tasks/main.yml | 58 +++++++++++++++++++ .../targets/github_key/vars/main.yml | 11 ++++ 5 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 changelogs/fragments/10191-github_key-add-api_url-parameter.yml create mode 100644 tests/integration/targets/github_key/aliases create mode 100644 tests/integration/targets/github_key/tasks/main.yml create mode 100644 tests/integration/targets/github_key/vars/main.yml diff --git a/changelogs/fragments/10191-github_key-add-api_url-parameter.yml b/changelogs/fragments/10191-github_key-add-api_url-parameter.yml new file mode 100644 index 0000000000..de0cd7054a --- /dev/null +++ b/changelogs/fragments/10191-github_key-add-api_url-parameter.yml @@ -0,0 +1,2 @@ +minor_changes: + - github_key - add ``api_url`` parameter to support GitHub Enterprise Server installations (https://github.com/ansible-collections/community.general/pull/10191). diff --git a/plugins/modules/github_key.py b/plugins/modules/github_key.py index f3d5863d54..4a9c80e26d 100644 --- a/plugins/modules/github_key.py +++ b/plugins/modules/github_key.py @@ -14,6 +14,7 @@ module: github_key short_description: Manage GitHub access keys description: - Creates, removes, or updates GitHub access keys. + - Works with both GitHub.com and GitHub Enterprise Server installations. extends_documentation_fragment: - community.general.attributes attributes: @@ -48,6 +49,12 @@ options: the key will only be set if no key with the given O(name) exists. type: bool default: true + api_url: + description: + - URL to the GitHub API if not using github.com but your own GitHub Enterprise instance. + type: str + default: 'https://api.github.com' + version_added: "11.0.0" author: Robert Estelle (@erydo) """ @@ -91,6 +98,14 @@ EXAMPLES = r""" name: Access Key for Some Machine token: '{{ github_access_token }}' pubkey: "{{ lookup('ansible.builtin.file', '/home/foo/.ssh/id_rsa.pub') }}" + +# GitHub Enterprise Server usage +- name: Authorize key with GitHub Enterprise + community.general.github_key: + name: Access Key for Some Machine + token: '{{ github_enterprise_token }}' + pubkey: "{{ lookup('ansible.builtin.file', '/home/foo/.ssh/id_rsa.pub') }}" + api_url: 'https://github.company.com/api/v3' """ import datetime @@ -105,9 +120,6 @@ from ansible_collections.community.general.plugins.module_utils.datetime import ) -API_BASE = 'https://api.github.com' - - class GitHubResponse(object): def __init__(self, response, info): self.content = response.read() @@ -127,9 +139,10 @@ class GitHubResponse(object): class GitHubSession(object): - def __init__(self, module, token): + def __init__(self, module, token, api_url): self.module = module self.token = token + self.api_url = api_url.rstrip('/') def request(self, method, url, data=None): headers = { @@ -147,7 +160,7 @@ class GitHubSession(object): def get_all_keys(session): - url = API_BASE + '/user/keys' + url = session.api_url + '/user/keys' result = [] while url: r = session.request('GET', url) @@ -171,7 +184,7 @@ def create_key(session, name, pubkey, check_mode): else: return session.request( 'POST', - API_BASE + '/user/keys', + session.api_url + '/user/keys', data=json.dumps({'title': name, 'key': pubkey})).json() @@ -180,7 +193,7 @@ def delete_keys(session, to_delete, check_mode): return for key in to_delete: - session.request('DELETE', API_BASE + '/user/keys/%s' % key["id"]) + session.request('DELETE', session.api_url + '/user/keys/%s' % key["id"]) def ensure_key_absent(session, name, check_mode): @@ -228,6 +241,7 @@ def main(): 'pubkey': {}, 'state': {'choices': ['present', 'absent'], 'default': 'present'}, 'force': {'default': True, 'type': 'bool'}, + 'api_url': {'default': 'https://api.github.com', 'type': 'str'}, } module = AnsibleModule( argument_spec=argument_spec, @@ -239,6 +253,7 @@ def main(): state = module.params['state'] force = module.params['force'] pubkey = module.params.get('pubkey') + api_url = module.params.get('api_url') if pubkey: pubkey_parts = pubkey.split(' ') @@ -248,7 +263,7 @@ def main(): elif state == 'present': module.fail_json(msg='"pubkey" is required when state=present') - session = GitHubSession(module, token) + session = GitHubSession(module, token, api_url) if state == 'present': result = ensure_key_present(module, session, name, pubkey, force=force, check_mode=module.check_mode) diff --git a/tests/integration/targets/github_key/aliases b/tests/integration/targets/github_key/aliases new file mode 100644 index 0000000000..9ee6676643 --- /dev/null +++ b/tests/integration/targets/github_key/aliases @@ -0,0 +1,6 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +unsupported +destructive diff --git a/tests/integration/targets/github_key/tasks/main.yml b/tests/integration/targets/github_key/tasks/main.yml new file mode 100644 index 0000000000..d9bbf9d229 --- /dev/null +++ b/tests/integration/targets/github_key/tasks/main.yml @@ -0,0 +1,58 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Test code for the github_key module. +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Test api_url parameter with GitHub.com + community.general.github_key: + token: "{{ fake_token }}" + name: "{{ test_key_name }}" + pubkey: "{{ test_pubkey }}" + state: present + api_url: "{{ github_api_url }}" + register: github_api_result + ignore_errors: true + +- name: Assert api_url parameter works with GitHub.com + assert: + that: + - github_api_result is failed + - '"Unauthorized" in github_api_result.msg or "401" in github_api_result.msg' + +- name: Test api_url parameter with GitHub Enterprise + community.general.github_key: + token: "{{ fake_token }}" + name: "{{ test_key_name }}" + pubkey: "{{ test_pubkey }}" + state: present + api_url: "{{ enterprise_api_url }}" + register: enterprise_api_result + ignore_errors: true + +- name: Assert api_url parameter works with GitHub Enterprise + assert: + that: + - enterprise_api_result is failed + - '"github.company.com" in enterprise_api_result.msg' + +- name: Test api_url with trailing slash + community.general.github_key: + token: "{{ fake_token }}" + name: "{{ test_key_name }}" + pubkey: "{{ test_pubkey }}" + state: present + api_url: "{{ enterprise_api_url_trailing }}" + register: trailing_slash_result + ignore_errors: true + +- name: Assert trailing slash is handled correctly + assert: + that: + - trailing_slash_result is failed + - '"github.company.com" in trailing_slash_result.msg' diff --git a/tests/integration/targets/github_key/vars/main.yml b/tests/integration/targets/github_key/vars/main.yml new file mode 100644 index 0000000000..076392298e --- /dev/null +++ b/tests/integration/targets/github_key/vars/main.yml @@ -0,0 +1,11 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +fake_token: "fake_token_for_testing" +test_key_name: "ansible-test-key" +github_api_url: "https://api.github.com" +enterprise_api_url: "https://github.company.com/api/v3" +enterprise_api_url_trailing: "https://github.company.com/api/v3/" +test_pubkey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTgvwjlRHZ8E1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ test@example.com"