1# -*- coding: utf-8 -*-
2
3# Copyright: (c) 2021, Phillipe Smith <phsmithcc@gmail.com>
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import absolute_import, division, print_function
7__metaclass__ = type
8
9import json
10
11from ansible.module_utils.urls import fetch_url, url_argument_spec
12from ansible.module_utils.common.text.converters import to_native
13
14
15def api_argument_spec():
16    '''
17    Creates an argument spec that can be used with any module
18    that will be requesting content via Rundeck API
19    '''
20    api_argument_spec = url_argument_spec()
21    api_argument_spec.update(dict(
22        url=dict(required=True, type="str"),
23        api_version=dict(type="int", default=39),
24        api_token=dict(required=True, type="str", no_log=True)
25    ))
26
27    return api_argument_spec
28
29
30def api_request(module, endpoint, data=None, method="GET"):
31    """Manages Rundeck API requests via HTTP(S)
32
33    :arg module:   The AnsibleModule (used to get url, api_version, api_token, etc).
34    :arg endpoint: The API endpoint to be used.
35    :kwarg data:   The data to be sent (in case of POST/PUT).
36    :kwarg method: "POST", "PUT", etc.
37
38    :returns: A tuple of (**response**, **info**). Use ``response.read()`` to read the data.
39        The **info** contains the 'status' and other meta data. When a HttpError (status >= 400)
40        occurred then ``info['body']`` contains the error response data::
41
42    Example::
43
44        data={...}
45        resp, info = fetch_url(module,
46                               "http://rundeck.example.org",
47                               data=module.jsonify(data),
48                               method="POST")
49        status_code = info["status"]
50        body = resp.read()
51        if status_code >= 400 :
52            body = info['body']
53    """
54
55    response, info = fetch_url(
56        module=module,
57        url="%s/api/%s/%s" % (
58            module.params["url"],
59            module.params["api_version"],
60            endpoint
61        ),
62        data=json.dumps(data),
63        method=method,
64        headers={
65            "Content-Type": "application/json",
66            "Accept": "application/json",
67            "X-Rundeck-Auth-Token": module.params["api_token"]
68        }
69    )
70
71    if info["status"] == 403:
72        module.fail_json(msg="Token authorization failed",
73                         execution_info=json.loads(info["body"]))
74    if info["status"] == 409:
75        module.fail_json(msg="Job executions limit reached",
76                         execution_info=json.loads(info["body"]))
77    elif info["status"] >= 500:
78        module.fail_json(msg="Rundeck API error",
79                         execution_info=json.loads(info["body"]))
80
81    try:
82        content = response.read()
83        json_response = json.loads(content)
84        return json_response, info
85    except AttributeError as error:
86        module.fail_json(msg="Rundeck API request error",
87                         exception=to_native(error),
88                         execution_info=info)
89    except ValueError as error:
90        module.fail_json(
91            msg="No valid JSON response",
92            exception=to_native(error),
93            execution_info=content
94        )
95