1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4# Copyright: (c) 2017 John Kwiatkoski (@JayKayy) <jkwiat40@gmail.com>
5# Copyright: (c) 2018 Alexander Bethke (@oolongbrothers) <oolongbrothers@gmx.net>
6# Copyright: (c) 2017 Ansible Project
7# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
8
9
10# ATTENTION CONTRIBUTORS!
11#
12# TL;DR: Run this module's integration tests manually before opening a pull request
13#
14# Long explanation:
15# The integration tests for this module are currently NOT run on the Ansible project's continuous
16# delivery pipeline. So please: When you make changes to this module, make sure that you run the
17# included integration tests manually for both Python 2 and Python 3:
18#
19#   Python 2:
20#       ansible-test integration -v --docker fedora28 --docker-privileged --allow-unsupported --python 2.7 flatpak_remote
21#   Python 3:
22#       ansible-test integration -v --docker fedora28 --docker-privileged --allow-unsupported --python 3.6 flatpak_remote
23#
24# Because of external dependencies, the current integration tests are somewhat too slow and brittle
25# to be included right now. I have plans to rewrite the integration tests based on a local flatpak
26# repository so that they can be included into the normal CI pipeline.
27# //oolongbrothers
28
29
30from __future__ import (absolute_import, division, print_function)
31__metaclass__ = type
32
33ANSIBLE_METADATA = {'metadata_version': '1.1',
34                    'status': ['preview'],
35                    'supported_by': 'community'}
36
37DOCUMENTATION = r'''
38---
39module: flatpak_remote
40version_added: '2.6'
41short_description: Manage flatpak repository remotes
42description:
43- Allows users to add or remove flatpak remotes.
44- The flatpak remotes concept is comparable to what is called repositories in other packaging
45  formats.
46- Currently, remote addition is only supported via I(flatpakrepo) file URLs.
47- Existing remotes will not be updated.
48- See the M(flatpak) module for managing flatpaks.
49author:
50- John Kwiatkoski (@JayKayy)
51- Alexander Bethke (@oolongbrothers)
52requirements:
53- flatpak
54options:
55  executable:
56    description:
57    - The path to the C(flatpak) executable to use.
58    - By default, this module looks for the C(flatpak) executable on the path.
59    default: flatpak
60  flatpakrepo_url:
61    description:
62    - The URL to the I(flatpakrepo) file representing the repository remote to add.
63    - When used with I(state=present), the flatpak remote specified under the I(flatpakrepo_url)
64      is added using the specified installation C(method).
65    - When used with I(state=absent), this is not required.
66    - Required when I(state=present).
67  method:
68    description:
69    - The installation method to use.
70    - Defines if the I(flatpak) is supposed to be installed globally for the whole C(system)
71      or only for the current C(user).
72    choices: [ system, user ]
73    default: system
74  name:
75    description:
76    - The desired name for the flatpak remote to be registered under on the managed host.
77    - When used with I(state=present), the remote will be added to the managed host under
78      the specified I(name).
79    - When used with I(state=absent) the remote with that name will be removed.
80    required: true
81  state:
82    description:
83    - Indicates the desired package state.
84    choices: [ absent, present ]
85    default: present
86'''
87
88EXAMPLES = r'''
89- name: Add the Gnome flatpak remote to the system installation
90  flatpak_remote:
91    name: gnome
92    state: present
93    flatpakrepo_url: https://sdk.gnome.org/gnome-apps.flatpakrepo
94
95- name: Add the flathub flatpak repository remote to the user installation
96  flatpak_remote:
97    name: flathub
98    state: present
99    flatpakrepo_url: https://dl.flathub.org/repo/flathub.flatpakrepo
100    method: user
101
102- name: Remove the Gnome flatpak remote from the user installation
103  flatpak_remote:
104    name: gnome
105    state: absent
106    method: user
107
108- name: Remove the flathub remote from the system installation
109  flatpak_remote:
110    name: flathub
111    state: absent
112'''
113
114RETURN = r'''
115command:
116  description: The exact flatpak command that was executed
117  returned: When a flatpak command has been executed
118  type: str
119  sample: "/usr/bin/flatpak remote-add --system flatpak-test https://dl.flathub.org/repo/flathub.flatpakrepo"
120msg:
121  description: Module error message
122  returned: failure
123  type: str
124  sample: "Executable '/usr/local/bin/flatpak' was not found on the system."
125rc:
126  description: Return code from flatpak binary
127  returned: When a flatpak command has been executed
128  type: int
129  sample: 0
130stderr:
131  description: Error output from flatpak binary
132  returned: When a flatpak command has been executed
133  type: str
134  sample: "error: GPG verification enabled, but no summary found (check that the configured URL in remote config is correct)\n"
135stdout:
136  description: Output from flatpak binary
137  returned: When a flatpak command has been executed
138  type: str
139  sample: "flathub\tFlathub\thttps://dl.flathub.org/repo/\t1\t\n"
140'''
141
142import subprocess
143from ansible.module_utils.basic import AnsibleModule
144from ansible.module_utils._text import to_bytes, to_native
145
146
147def add_remote(module, binary, name, flatpakrepo_url, method):
148    """Add a new remote."""
149    global result
150    command = "{0} remote-add --{1} {2} {3}".format(
151        binary, method, name, flatpakrepo_url)
152    _flatpak_command(module, module.check_mode, command)
153    result['changed'] = True
154
155
156def remove_remote(module, binary, name, method):
157    """Remove an existing remote."""
158    global result
159    command = "{0} remote-delete --{1} --force {2} ".format(
160        binary, method, name)
161    _flatpak_command(module, module.check_mode, command)
162    result['changed'] = True
163
164
165def remote_exists(module, binary, name, method):
166    """Check if the remote exists."""
167    command = "{0} remote-list -d --{1}".format(binary, method)
168    # The query operation for the remote needs to be run even in check mode
169    output = _flatpak_command(module, False, command)
170    for line in output.splitlines():
171        listed_remote = line.split()
172        if len(listed_remote) == 0:
173            continue
174        if listed_remote[0] == to_native(name):
175            return True
176    return False
177
178
179def _flatpak_command(module, noop, command):
180    global result
181    if noop:
182        result['rc'] = 0
183        result['command'] = command
184        return ""
185
186    process = subprocess.Popen(
187        command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
188    stdout_data, stderr_data = process.communicate()
189    result['rc'] = process.returncode
190    result['command'] = command
191    result['stdout'] = stdout_data
192    result['stderr'] = stderr_data
193    if result['rc'] != 0:
194        module.fail_json(msg="Failed to execute flatpak command", **result)
195    return to_native(stdout_data)
196
197
198def main():
199    module = AnsibleModule(
200        argument_spec=dict(
201            name=dict(type='str', required=True),
202            flatpakrepo_url=dict(type='str'),
203            method=dict(type='str', default='system',
204                        choices=['user', 'system']),
205            state=dict(type='str', default="present",
206                       choices=['absent', 'present']),
207            executable=dict(type='str', default="flatpak")
208        ),
209        # This module supports check mode
210        supports_check_mode=True,
211    )
212
213    name = module.params['name']
214    flatpakrepo_url = module.params['flatpakrepo_url']
215    method = module.params['method']
216    state = module.params['state']
217    executable = module.params['executable']
218    binary = module.get_bin_path(executable, None)
219
220    if flatpakrepo_url is None:
221        flatpakrepo_url = ''
222
223    global result
224    result = dict(
225        changed=False
226    )
227
228    # If the binary was not found, fail the operation
229    if not binary:
230        module.fail_json(msg="Executable '%s' was not found on the system." % executable, **result)
231
232    remote_already_exists = remote_exists(module, binary, to_bytes(name), method)
233
234    if state == 'present' and not remote_already_exists:
235        add_remote(module, binary, name, flatpakrepo_url, method)
236    elif state == 'absent' and remote_already_exists:
237        remove_remote(module, binary, name, method)
238
239    module.exit_json(**result)
240
241
242if __name__ == '__main__':
243    main()
244