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