1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2017 Google
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6# ----------------------------------------------------------------------------
7#
8#     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
9#
10# ----------------------------------------------------------------------------
11#
12#     This file is automatically generated by Magic Modules and manual
13#     changes will be clobbered when the file is regenerated.
14#
15#     Please read more about how to change this file at
16#     https://www.github.com/GoogleCloudPlatform/magic-modules
17#
18# ----------------------------------------------------------------------------
19
20from __future__ import absolute_import, division, print_function
21
22__metaclass__ = type
23
24################################################################################
25# Documentation
26################################################################################
27
28ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ["preview"], 'supported_by': 'community'}
29
30DOCUMENTATION = '''
31---
32module: gcp_compute_ssl_certificate
33description:
34- An SslCertificate resource, used for HTTPS load balancing. This resource provides
35  a mechanism to upload an SSL key and certificate to the load balancer to serve secure
36  connections from the user.
37short_description: Creates a GCP SslCertificate
38version_added: 2.6
39author: Google Inc. (@googlecloudplatform)
40requirements:
41- python >= 2.6
42- requests >= 2.18.4
43- google-auth >= 1.3.0
44options:
45  state:
46    description:
47    - Whether the given object should exist in GCP
48    choices:
49    - present
50    - absent
51    default: present
52    type: str
53  certificate:
54    description:
55    - The certificate in PEM format.
56    - The certificate chain must be no greater than 5 certs long.
57    - The chain must include at least one intermediate cert.
58    required: true
59    type: str
60  description:
61    description:
62    - An optional description of this resource.
63    required: false
64    type: str
65  name:
66    description:
67    - Name of the resource. Provided by the client when the resource is created. The
68      name must be 1-63 characters long, and comply with RFC1035. Specifically, the
69      name must be 1-63 characters long and match the regular expression `[a-z]([-a-z0-9]*[a-z0-9])?`
70      which means the first character must be a lowercase letter, and all following
71      characters must be a dash, lowercase letter, or digit, except the last character,
72      which cannot be a dash.
73    required: false
74    type: str
75  private_key:
76    description:
77    - The write-only private key in PEM format.
78    required: true
79    type: str
80extends_documentation_fragment: gcp
81notes:
82- 'API Reference: U(https://cloud.google.com/compute/docs/reference/rest/v1/sslCertificates)'
83- 'Official Documentation: U(https://cloud.google.com/load-balancing/docs/ssl-certificates)'
84'''
85
86EXAMPLES = '''
87- name: create a SSL certificate
88  gcp_compute_ssl_certificate:
89    name: test_object
90    description: A certificate for testing. Do not use this certificate in production
91    certificate: |-
92      -----BEGIN CERTIFICATE-----
93      MIICqjCCAk+gAwIBAgIJAIuJ+0352Kq4MAoGCCqGSM49BAMCMIGwMQswCQYDVQQG
94      EwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxFTAT
95      BgNVBAoMDEdvb2dsZSwgSW5jLjEeMBwGA1UECwwVR29vZ2xlIENsb3VkIFBsYXRm
96      b3JtMR8wHQYDVQQDDBZ3d3cubXktc2VjdXJlLXNpdGUuY29tMSEwHwYJKoZIhvcN
97      AQkBFhJuZWxzb25hQGdvb2dsZS5jb20wHhcNMTcwNjI4MDQ1NjI2WhcNMjcwNjI2
98      MDQ1NjI2WjCBsDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xETAP
99      BgNVBAcMCEtpcmtsYW5kMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xHjAcBgNVBAsM
100      FUdvb2dsZSBDbG91ZCBQbGF0Zm9ybTEfMB0GA1UEAwwWd3d3Lm15LXNlY3VyZS1z
101      aXRlLmNvbTEhMB8GCSqGSIb3DQEJARYSbmVsc29uYUBnb29nbGUuY29tMFkwEwYH
102      KoZIzj0CAQYIKoZIzj0DAQcDQgAEHGzpcRJ4XzfBJCCPMQeXQpTXwlblimODQCuQ
103      4mzkzTv0dXyB750fOGN02HtkpBOZzzvUARTR10JQoSe2/5PIwaNQME4wHQYDVR0O
104      BBYEFKIQC3A2SDpxcdfn0YLKineDNq/BMB8GA1UdIwQYMBaAFKIQC3A2SDpxcdfn
105      0YLKineDNq/BMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhALs4vy+O
106      M3jcqgA4fSW/oKw6UJxp+M6a+nGMX+UJR3YgAiEAvvl39QRVAiv84hdoCuyON0lJ
107      zqGNhIPGq2ULqXKK8BY=
108      -----END CERTIFICATE-----
109    private_key: |-
110      -----BEGIN EC PRIVATE KEY-----
111      MHcCAQEEIObtRo8tkUqoMjeHhsOh2ouPpXCgBcP+EDxZCB/tws15oAoGCCqGSM49
112      AwEHoUQDQgAEHGzpcRJ4XzfBJCCPMQeXQpTXwlblimODQCuQ4mzkzTv0dXyB750f
113      OGN02HtkpBOZzzvUARTR10JQoSe2/5PIwQ==
114      -----END EC PRIVATE KEY-----
115    project: test_project
116    auth_kind: serviceaccount
117    service_account_file: "/tmp/auth.pem"
118    state: present
119'''
120
121RETURN = '''
122certificate:
123  description:
124  - The certificate in PEM format.
125  - The certificate chain must be no greater than 5 certs long.
126  - The chain must include at least one intermediate cert.
127  returned: success
128  type: str
129creationTimestamp:
130  description:
131  - Creation timestamp in RFC3339 text format.
132  returned: success
133  type: str
134description:
135  description:
136  - An optional description of this resource.
137  returned: success
138  type: str
139id:
140  description:
141  - The unique identifier for the resource.
142  returned: success
143  type: int
144name:
145  description:
146  - Name of the resource. Provided by the client when the resource is created. The
147    name must be 1-63 characters long, and comply with RFC1035. Specifically, the
148    name must be 1-63 characters long and match the regular expression `[a-z]([-a-z0-9]*[a-z0-9])?`
149    which means the first character must be a lowercase letter, and all following
150    characters must be a dash, lowercase letter, or digit, except the last character,
151    which cannot be a dash.
152  returned: success
153  type: str
154privateKey:
155  description:
156  - The write-only private key in PEM format.
157  returned: success
158  type: str
159'''
160
161################################################################################
162# Imports
163################################################################################
164
165from ansible.module_utils.gcp_utils import navigate_hash, GcpSession, GcpModule, GcpRequest, replace_resource_dict
166import json
167import time
168
169################################################################################
170# Main
171################################################################################
172
173
174def main():
175    """Main function"""
176
177    module = GcpModule(
178        argument_spec=dict(
179            state=dict(default='present', choices=['present', 'absent'], type='str'),
180            certificate=dict(required=True, type='str'),
181            description=dict(type='str'),
182            name=dict(type='str'),
183            private_key=dict(required=True, type='str', no_log=True),
184        )
185    )
186
187    if not module.params['scopes']:
188        module.params['scopes'] = ['https://www.googleapis.com/auth/compute']
189
190    state = module.params['state']
191    kind = 'compute#sslCertificate'
192
193    fetch = fetch_resource(module, self_link(module), kind)
194    changed = False
195
196    if fetch:
197        if state == 'present':
198            if is_different(module, fetch):
199                update(module, self_link(module), kind)
200                fetch = fetch_resource(module, self_link(module), kind)
201                changed = True
202        else:
203            delete(module, self_link(module), kind)
204            fetch = {}
205            changed = True
206    else:
207        if state == 'present':
208            fetch = create(module, collection(module), kind)
209            changed = True
210        else:
211            fetch = {}
212
213    fetch.update({'changed': changed})
214
215    module.exit_json(**fetch)
216
217
218def create(module, link, kind):
219    auth = GcpSession(module, 'compute')
220    return wait_for_operation(module, auth.post(link, resource_to_request(module)))
221
222
223def update(module, link, kind):
224    delete(module, self_link(module), kind)
225    create(module, collection(module), kind)
226
227
228def delete(module, link, kind):
229    auth = GcpSession(module, 'compute')
230    return wait_for_operation(module, auth.delete(link))
231
232
233def resource_to_request(module):
234    request = {
235        u'kind': 'compute#sslCertificate',
236        u'certificate': module.params.get('certificate'),
237        u'description': module.params.get('description'),
238        u'name': module.params.get('name'),
239        u'privateKey': module.params.get('private_key'),
240    }
241    return_vals = {}
242    for k, v in request.items():
243        if v or v is False:
244            return_vals[k] = v
245
246    return return_vals
247
248
249def fetch_resource(module, link, kind, allow_not_found=True):
250    auth = GcpSession(module, 'compute')
251    return return_if_object(module, auth.get(link), kind, allow_not_found)
252
253
254def self_link(module):
255    return "https://www.googleapis.com/compute/v1/projects/{project}/global/sslCertificates/{name}".format(**module.params)
256
257
258def collection(module):
259    return "https://www.googleapis.com/compute/v1/projects/{project}/global/sslCertificates".format(**module.params)
260
261
262def return_if_object(module, response, kind, allow_not_found=False):
263    # If not found, return nothing.
264    if allow_not_found and response.status_code == 404:
265        return None
266
267    # If no content, return nothing.
268    if response.status_code == 204:
269        return None
270
271    try:
272        module.raise_for_status(response)
273        result = response.json()
274    except getattr(json.decoder, 'JSONDecodeError', ValueError):
275        module.fail_json(msg="Invalid JSON response with error: %s" % response.text)
276
277    if navigate_hash(result, ['error', 'errors']):
278        module.fail_json(msg=navigate_hash(result, ['error', 'errors']))
279
280    return result
281
282
283def is_different(module, response):
284    request = resource_to_request(module)
285    response = response_to_hash(module, response)
286
287    # Remove all output-only from response.
288    response_vals = {}
289    for k, v in response.items():
290        if k in request:
291            response_vals[k] = v
292
293    request_vals = {}
294    for k, v in request.items():
295        if k in response:
296            request_vals[k] = v
297
298    return GcpRequest(request_vals) != GcpRequest(response_vals)
299
300
301# Remove unnecessary properties from the response.
302# This is for doing comparisons with Ansible's current parameters.
303def response_to_hash(module, response):
304    return {
305        u'certificate': response.get(u'certificate'),
306        u'creationTimestamp': response.get(u'creationTimestamp'),
307        u'description': response.get(u'description'),
308        u'id': response.get(u'id'),
309        u'name': response.get(u'name'),
310        u'privateKey': module.params.get('private_key'),
311    }
312
313
314def async_op_url(module, extra_data=None):
315    if extra_data is None:
316        extra_data = {}
317    url = "https://www.googleapis.com/compute/v1/projects/{project}/global/operations/{op_id}"
318    combined = extra_data.copy()
319    combined.update(module.params)
320    return url.format(**combined)
321
322
323def wait_for_operation(module, response):
324    op_result = return_if_object(module, response, 'compute#operation')
325    if op_result is None:
326        return {}
327    status = navigate_hash(op_result, ['status'])
328    wait_done = wait_for_completion(status, op_result, module)
329    return fetch_resource(module, navigate_hash(wait_done, ['targetLink']), 'compute#sslCertificate')
330
331
332def wait_for_completion(status, op_result, module):
333    op_id = navigate_hash(op_result, ['name'])
334    op_uri = async_op_url(module, {'op_id': op_id})
335    while status != 'DONE':
336        raise_if_errors(op_result, ['error', 'errors'], module)
337        time.sleep(1.0)
338        op_result = fetch_resource(module, op_uri, 'compute#operation', False)
339        status = navigate_hash(op_result, ['status'])
340    return op_result
341
342
343def raise_if_errors(response, err_path, module):
344    errors = navigate_hash(response, err_path)
345    if errors is not None:
346        module.fail_json(msg=errors)
347
348
349if __name__ == '__main__':
350    main()
351