From a1442ccc35ca1334411db3a615cda7d5e7c7bef8 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sat, 13 Jun 2020 16:57:02 +0200 Subject: [PATCH] Remove modules that were moved to the google.cloud collection according to ansible/ansible's ansible_builtin_runtime.yml. --- .../cloud/google/gcp_backend_service.py | 400 ---------------- .../cloud/google/gcp_forwarding_rule.py | 349 -------------- .../modules/cloud/google/gcp_healthcheck.py | 444 ------------------ plugins/modules/cloud/google/gcpubsub.py | 328 ------------- plugins/modules/gcp_backend_service.py | 1 - plugins/modules/gcp_forwarding_rule.py | 1 - plugins/modules/gcp_healthcheck.py | 1 - plugins/modules/gcpubsub.py | 1 - .../cloud/google/test_gcp_forwarding_rule.py | 30 -- 9 files changed, 1555 deletions(-) delete mode 100644 plugins/modules/cloud/google/gcp_backend_service.py delete mode 100644 plugins/modules/cloud/google/gcp_forwarding_rule.py delete mode 100644 plugins/modules/cloud/google/gcp_healthcheck.py delete mode 100644 plugins/modules/cloud/google/gcpubsub.py delete mode 120000 plugins/modules/gcp_backend_service.py delete mode 120000 plugins/modules/gcp_forwarding_rule.py delete mode 120000 plugins/modules/gcp_healthcheck.py delete mode 120000 plugins/modules/gcpubsub.py delete mode 100644 tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py diff --git a/plugins/modules/cloud/google/gcp_backend_service.py b/plugins/modules/cloud/google/gcp_backend_service.py deleted file mode 100644 index b1bd18a5e3..0000000000 --- a/plugins/modules/cloud/google/gcp_backend_service.py +++ /dev/null @@ -1,400 +0,0 @@ -#!/usr/bin/python -# Copyright 2017 Google Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -DOCUMENTATION = ''' -module: gcp_backend_service -short_description: Create or Destroy a Backend Service. -description: - - Create or Destroy a Backend Service. See - U(https://cloud.google.com/compute/docs/load-balancing/http/backend-service) for an overview. - Full install/configuration instructions for the Google Cloud modules can - be found in the comments of ansible/test/gce_tests.py. -requirements: - - "python >= 2.6" - - "apache-libcloud >= 1.3.0" -notes: - - Update is not currently supported. - - Only global backend services are currently supported. Regional backends not currently supported. - - Internal load balancing not currently supported. -deprecated: - removed_in: 2.0.0 # was Ansible 2.12 - why: Updated modules released with increased functionality - alternative: Use M(gcp_compute_backend_service) instead. -author: - - "Tom Melendez (@supertom) " -options: - backend_service_name: - description: - - Name of the Backend Service. - required: true - backends: - description: - - List of backends that make up the backend service. A backend is made up of - an instance group and optionally several other parameters. See - U(https://cloud.google.com/compute/docs/reference/latest/backendServices) - for details. - required: true - healthchecks: - description: - - List of healthchecks. Only one healthcheck is supported. - required: true - enable_cdn: - description: - - If true, enable Cloud CDN for this Backend Service. - type: bool - port_name: - description: - - Name of the port on the managed instance group (MIG) that backend - services can forward data to. Required for external load balancing. - protocol: - description: - - The protocol this Backend Service uses to communicate with backends. - Possible values are HTTP, HTTPS, TCP, and SSL. The default is HTTP. - required: false - timeout: - description: - - How many seconds to wait for the backend before considering it a failed - request. Default is 30 seconds. Valid range is 1-86400. - required: false - service_account_email: - description: - - Service account email - credentials_file: - description: - - Path to the JSON file associated with the service account email. - project_id: - description: - - GCE project ID. - state: - description: - - Desired state of the resource - default: "present" - choices: ["absent", "present"] -''' - -EXAMPLES = ''' -- name: Create Minimum Backend Service - gcp_backend_service: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - backend_service_name: "{{ bes }}" - backends: - - instance_group: managed_instance_group_1 - healthchecks: - - healthcheck_name_for_backend_service - port_name: myhttpport - state: present - -- name: Create BES with extended backend parameters - gcp_backend_service: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - backend_service_name: "{{ bes }}" - backends: - - instance_group: managed_instance_group_1 - max_utilization: 0.6 - max_rate: 10 - - instance_group: managed_instance_group_2 - max_utilization: 0.5 - max_rate: 4 - healthchecks: - - healthcheck_name_for_backend_service - port_name: myhttpport - state: present - timeout: 60 -''' - -RETURN = ''' -backend_service_created: - description: Indicator Backend Service was created. - returned: When a Backend Service is created. - type: bool - sample: "True" -backend_service_deleted: - description: Indicator Backend Service was deleted. - returned: When a Backend Service is deleted. - type: bool - sample: "True" -backend_service_name: - description: Name of the Backend Service. - returned: Always. - type: str - sample: "my-backend-service" -backends: - description: List of backends (comprised of instance_group) that - make up a Backend Service. - returned: When a Backend Service exists. - type: list - sample: "[ { 'instance_group': 'mig_one', 'zone': 'us-central1-b'} ]" -enable_cdn: - description: If Cloud CDN is enabled. null if not set. - returned: When a backend service exists. - type: bool - sample: "True" -healthchecks: - description: List of healthchecks applied to the Backend Service. - returned: When a Backend Service exists. - type: list - sample: "[ 'my-healthcheck' ]" -protocol: - description: Protocol used to communicate with the Backends. - returned: When a Backend Service exists. - type: str - sample: "HTTP" -port_name: - description: Name of Backend Port. - returned: When a Backend Service exists. - type: str - sample: "myhttpport" -timeout: - description: In seconds, how long before a request sent to a backend is - considered failed. - returned: If specified. - type: int - sample: "myhttpport" -''' - -try: - from ast import literal_eval - HAS_PYTHON26 = True -except ImportError: - HAS_PYTHON26 = False - -try: - import libcloud - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - from libcloud.common.google import GoogleBaseError, QuotaExceededError, \ - ResourceExistsError, ResourceInUseError, ResourceNotFoundError - from libcloud.compute.drivers.gce import GCEAddress - _ = Provider.GCE - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.gce import gce_connect -from ansible_collections.community.general.plugins.module_utils.gcp import check_params - - -def _validate_params(params): - """ - Validate backend_service params. - - This function calls _validate_backend_params to verify - the backend-specific parameters. - - :param params: Ansible dictionary containing configuration. - :type params: ``dict`` - - :return: True or raises ValueError - :rtype: ``bool`` or `class:ValueError` - """ - fields = [ - {'name': 'timeout', 'type': int, 'min': 1, 'max': 86400}, - ] - try: - check_params(params, fields) - _validate_backend_params(params['backends']) - except Exception: - raise - - return (True, '') - - -def _validate_backend_params(backends): - """ - Validate configuration for backends. - - :param backends: Ansible dictionary containing backends configuration (only). - :type backends: ``dict`` - - :return: True or raises ValueError - :rtype: ``bool`` or `class:ValueError` - """ - fields = [ - {'name': 'balancing_mode', 'type': str, 'values': ['UTILIZATION', 'RATE', 'CONNECTION']}, - {'name': 'max_utilization', 'type': float}, - {'name': 'max_connections', 'type': int}, - {'name': 'max_rate', 'type': int}, - {'name': 'max_rate_per_instance', 'type': float}, - ] - - if not backends: - raise ValueError('backends should be a list.') - - for backend in backends: - try: - check_params(backend, fields) - except Exception: - raise - - if 'max_rate' in backend and 'max_rate_per_instance' in backend: - raise ValueError('Both maxRate or maxRatePerInstance cannot be set.') - - return (True, '') - - -def get_backend_service(gce, name): - """ - Get a Backend Service from GCE. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param name: Name of the Backend Service. - :type name: ``str`` - - :return: A GCEBackendService object or None. - :rtype: :class: `GCEBackendService` or None - """ - try: - # Does the Backend Service already exist? - return gce.ex_get_backendservice(name=name) - - except ResourceNotFoundError: - return None - - -def get_healthcheck(gce, name): - return gce.ex_get_healthcheck(name) - - -def get_instancegroup(gce, name, zone=None): - return gce.ex_get_instancegroup(name=name, zone=zone) - - -def create_backend_service(gce, params): - """ - Create a new Backend Service. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param params: Dictionary of parameters needed by the module. - :type params: ``dict`` - - :return: Tuple with changed stats - :rtype: tuple in the format of (bool, bool) - """ - from copy import deepcopy - - changed = False - return_data = False - # only one healthcheck is currently supported - hc_name = params['healthchecks'][0] - hc = get_healthcheck(gce, hc_name) - backends = [] - for backend in params['backends']: - ig = get_instancegroup(gce, backend['instance_group'], - backend.get('zone', None)) - kwargs = deepcopy(backend) - kwargs['instance_group'] = ig - backends.append(gce.ex_create_backend( - **kwargs)) - - bes = gce.ex_create_backendservice( - name=params['backend_service_name'], healthchecks=[hc], backends=backends, - enable_cdn=params['enable_cdn'], port_name=params['port_name'], - timeout_sec=params['timeout'], protocol=params['protocol']) - - if bes: - changed = True - return_data = True - - return (changed, return_data) - - -def delete_backend_service(bes): - """ - Delete a Backend Service. The Instance Groups are NOT destroyed. - """ - changed = False - return_data = False - if bes.destroy(): - changed = True - return_data = True - return (changed, return_data) - - -def main(): - module = AnsibleModule(argument_spec=dict( - backends=dict(type='list', required=True), - backend_service_name=dict(required=True), - healthchecks=dict(type='list', required=True), - service_account_email=dict(), - service_account_permissions=dict(type='list'), - enable_cdn=dict(type='bool'), - port_name=dict(type='str'), - protocol=dict(type='str', default='TCP', - choices=['HTTP', 'HTTPS', 'SSL', 'TCP']), - timeout=dict(type='int'), - state=dict(choices=['absent', 'present'], default='present'), - pem_file=dict(), - credentials_file=dict(), - project_id=dict(), ), ) - - if not HAS_PYTHON26: - module.fail_json( - msg="GCE module requires python's 'ast' module, python v2.6+") - if not HAS_LIBCLOUD: - module.fail_json( - msg='libcloud with GCE Backend Service support (1.3+) required for this module.') - - gce = gce_connect(module) - if not hasattr(gce, 'ex_create_instancegroupmanager'): - module.fail_json( - msg='libcloud with GCE Backend Service support (1.3+) required for this module.', - changed=False) - - params = {} - params['state'] = module.params.get('state') - params['backend_service_name'] = module.params.get('backend_service_name') - params['backends'] = module.params.get('backends') - params['healthchecks'] = module.params.get('healthchecks') - params['enable_cdn'] = module.params.get('enable_cdn', None) - params['port_name'] = module.params.get('port_name', None) - params['protocol'] = module.params.get('protocol', None) - params['timeout'] = module.params.get('timeout', None) - - try: - _validate_params(params) - except Exception as e: - module.fail_json(msg=e.message, changed=False) - - changed = False - json_output = {'state': params['state']} - bes = get_backend_service(gce, params['backend_service_name']) - - if not bes: - if params['state'] == 'absent': - # Doesn't exist and state==absent. - changed = False - module.fail_json( - msg="Cannot delete unknown backend service: %s" % - (params['backend_service_name'])) - else: - # Create - (changed, json_output['backend_service_created']) = create_backend_service(gce, - params) - elif params['state'] == 'absent': - # Delete - (changed, json_output['backend_service_deleted']) = delete_backend_service(bes) - else: - # TODO(supertom): Add update support when it is available in libcloud. - changed = False - - json_output['changed'] = changed - json_output.update(params) - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/cloud/google/gcp_forwarding_rule.py b/plugins/modules/cloud/google/gcp_forwarding_rule.py deleted file mode 100644 index 923b12065a..0000000000 --- a/plugins/modules/cloud/google/gcp_forwarding_rule.py +++ /dev/null @@ -1,349 +0,0 @@ -#!/usr/bin/python -# Copyright 2017 Google Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -DOCUMENTATION = ''' ---- -module: gcp_forwarding_rule -short_description: Create, Update or Destroy a Forwarding_Rule. -description: - - Create, Update or Destroy a Forwarding_Rule. See - U(https://cloud.google.com/compute/docs/load-balancing/http/target-proxies) for an overview. - More details on the Global Forwarding_Rule API can be found at - U(https://cloud.google.com/compute/docs/reference/latest/globalForwardingRules) - More details on the Forwarding Rules API can be found at - U(https://cloud.google.com/compute/docs/reference/latest/forwardingRules) -requirements: - - "python >= 2.6" - - "google-api-python-client >= 1.6.2" - - "google-auth >= 0.9.0" - - "google-auth-httplib2 >= 0.0.2" -deprecated: - removed_in: 2.0.0 # was Ansible 2.12 - why: Updated modules released with increased functionality - alternative: Use M(gcp_compute_forwarding_rule) or M(gcp_compute_global_forwarding_rule) instead. -notes: - - Currently only supports global forwarding rules. - As such, Load Balancing Scheme is always EXTERNAL. -author: - - "Tom Melendez (@supertom) " -options: - address: - description: - - IPv4 or named IP address. Must be of the same scope (regional, global). - Reserved addresses can (and probably should) be used for global - forwarding rules. You may reserve IPs from the console or - via the gce_eip module. - required: false - forwarding_rule_name: - description: - - Name of the Forwarding_Rule. - required: true - port_range: - description: - - For global forwarding rules, must be set to 80 or 8080 for TargetHttpProxy, and - 443 for TargetHttpsProxy or TargetSslProxy. - required: false - protocol: - description: - - For global forwarding rules, TCP, UDP, ESP, AH, SCTP or ICMP. Default is TCP. - required: false - region: - description: - - The region for this forwarding rule. Currently, only 'global' is supported. - required: false - state: - description: - - The state of the Forwarding Rule. 'present' or 'absent' - required: true - choices: ["present", "absent"] - target: - description: - - Target resource for forwarding rule. For global proxy, this is a Global - TargetProxy resource. Required for external load balancing (including Global load balancing) - required: false -''' - -EXAMPLES = ''' -- name: Create Minimum GLOBAL Forwarding_Rule - gcp_forwarding_rule: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - forwarding_rule_name: my-forwarding_rule - protocol: TCP - port_range: 80 - region: global - target: my-target-proxy - state: present - -- name: Create Forwarding_Rule w/reserved static address - gcp_forwarding_rule: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - forwarding_rule_name: my-forwarding_rule - protocol: TCP - port_range: 80 - address: my-reserved-static-address-name - region: global - target: my-target-proxy - state: present -''' - -RETURN = ''' -forwarding_rule_name: - description: Name of the Forwarding_Rule - returned: Always - type: str - sample: my-target-proxy -forwarding_rule: - description: GCP Forwarding_Rule dictionary - returned: Always. Refer to GCP documentation for detailed field descriptions. - type: dict - sample: { "name": "my-forwarding_rule", "target": "..." } -region: - description: Region for Forwarding Rule. - returned: Always - type: bool - sample: true -state: - description: state of the Forwarding_Rule - returned: Always. - type: str - sample: present -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.gcp import get_google_api_client, GCPUtils - - -USER_AGENT_PRODUCT = 'ansible-forwarding_rule' -USER_AGENT_VERSION = '0.0.1' - - -def _build_global_forwarding_rule_dict(params, project_id=None): - """ - Reformat services in Ansible Params. - - :param params: Params from AnsibleModule object - :type params: ``dict`` - - :param project_id: The GCP project ID. - :type project_id: ``str`` - - :return: dictionary suitable for submission to GCP API. - :rtype ``dict`` - """ - url = '' - if project_id: - url = GCPUtils.build_googleapi_url(project_id) - gcp_dict = GCPUtils.params_to_gcp_dict(params, 'forwarding_rule_name') - if 'target' in gcp_dict: - gcp_dict['target'] = '%s/global/targetHttpProxies/%s' % (url, - gcp_dict['target']) - if 'address' in gcp_dict: - gcp_dict['IPAddress'] = '%s/global/addresses/%s' % (url, - gcp_dict['address']) - del gcp_dict['address'] - if 'protocol' in gcp_dict: - gcp_dict['IPProtocol'] = gcp_dict['protocol'] - del gcp_dict['protocol'] - return gcp_dict - - -def get_global_forwarding_rule(client, name, project_id=None): - """ - Get a Global Forwarding Rule from GCP. - - :param client: An initialized GCE Compute Discovery resource. - :type client: :class: `googleapiclient.discovery.Resource` - - :param name: Name of the Global Forwarding Rule. - :type name: ``str`` - - :param project_id: The GCP project ID. - :type project_id: ``str`` - - :return: A dict resp from the respective GCP 'get' request. - :rtype: ``dict`` - """ - try: - req = client.globalForwardingRules().get( - project=project_id, forwardingRule=name) - return GCPUtils.execute_api_client_req(req, raise_404=False) - except Exception: - raise - - -def create_global_forwarding_rule(client, params, project_id): - """ - Create a new Global Forwarding Rule. - - :param client: An initialized GCE Compute Discovery resource. - :type client: :class: `googleapiclient.discovery.Resource` - - :param params: Dictionary of arguments from AnsibleModule. - :type params: ``dict`` - - :return: Tuple with changed status and response dict - :rtype: ``tuple`` in the format of (bool, dict) - """ - gcp_dict = _build_global_forwarding_rule_dict(params, project_id) - try: - req = client.globalForwardingRules().insert(project=project_id, body=gcp_dict) - return_data = GCPUtils.execute_api_client_req(req, client, raw=False) - if not return_data: - return_data = get_global_forwarding_rule(client, - name=params['forwarding_rule_name'], - project_id=project_id) - return (True, return_data) - except Exception: - raise - - -def delete_global_forwarding_rule(client, name, project_id): - """ - Delete a Global Forwarding Rule. - - :param client: An initialized GCE Compute Discovery resource. - :type client: :class: `googleapiclient.discovery.Resource` - - :param name: Name of the Target Proxy. - :type name: ``str`` - - :param project_id: The GCP project ID. - :type project_id: ``str`` - - :return: Tuple with changed status and response dict - :rtype: ``tuple`` in the format of (bool, dict) - """ - try: - req = client.globalForwardingRules().delete( - project=project_id, forwardingRule=name) - return_data = GCPUtils.execute_api_client_req(req, client) - return (True, return_data) - except Exception: - raise - - -def update_global_forwarding_rule(client, forwarding_rule, params, name, project_id): - """ - Update a Global Forwarding_Rule. Currently, only a target can be updated. - - If the forwarding_rule has not changed, the update will not occur. - - :param client: An initialized GCE Compute Discovery resource. - :type client: :class: `googleapiclient.discovery.Resource` - - :param forwarding_rule: Name of the Target Proxy. - :type forwarding_rule: ``dict`` - - :param params: Dictionary of arguments from AnsibleModule. - :type params: ``dict`` - - :param name: Name of the Global Forwarding Rule. - :type name: ``str`` - - :param project_id: The GCP project ID. - :type project_id: ``str`` - - :return: Tuple with changed status and response dict - :rtype: ``tuple`` in the format of (bool, dict) - """ - gcp_dict = _build_global_forwarding_rule_dict(params, project_id) - - GCPUtils.are_params_equal(forwarding_rule, gcp_dict) - if forwarding_rule['target'] == gcp_dict['target']: - return (False, 'no update necessary') - - try: - req = client.globalForwardingRules().setTarget(project=project_id, - forwardingRule=name, - body={'target': gcp_dict['target']}) - return_data = GCPUtils.execute_api_client_req( - req, client=client, raw=False) - return (True, return_data) - except Exception: - raise - - -def main(): - module = AnsibleModule(argument_spec=dict( - forwarding_rule_name=dict(required=True), - region=dict(required=True), - target=dict(required=False), - address=dict(type='str', required=False), - protocol=dict(required=False, default='TCP', choices=['TCP']), - port_range=dict(required=False), - load_balancing_scheme=dict( - required=False, default='EXTERNAL', choices=['EXTERNAL']), - state=dict(required=True, choices=['absent', 'present']), - service_account_email=dict(), - service_account_permissions=dict(type='list'), - pem_file=dict(), - credentials_file=dict(), - project_id=dict(), ), ) - - client, conn_params = get_google_api_client(module, 'compute', user_agent_product=USER_AGENT_PRODUCT, - user_agent_version=USER_AGENT_VERSION) - - params = {} - params['state'] = module.params.get('state') - params['forwarding_rule_name'] = module.params.get('forwarding_rule_name') - params['region'] = module.params.get('region') - params['target'] = module.params.get('target', None) - params['protocol'] = module.params.get('protocol', None) - params['port_range'] = module.params.get('port_range') - if module.params.get('address', None): - params['address'] = module.params.get('address', None) - - if params['region'] != 'global': - # This module currently doesn't support regional rules. - module.fail_json( - msg=("%s - Only global forwarding rules currently supported. " - "Be sure to specify 'global' for the region option.") % - (params['forwarding_rule_name'])) - - changed = False - json_output = {'state': params['state']} - forwarding_rule = None - if params['region'] == 'global': - forwarding_rule = get_global_forwarding_rule(client, - name=params['forwarding_rule_name'], - project_id=conn_params['project_id']) - if not forwarding_rule: - if params['state'] == 'absent': - # Doesn't exist in GCE, and state==absent. - changed = False - module.fail_json( - msg="Cannot delete unknown forwarding_rule: %s" % - (params['forwarding_rule_name'])) - else: - # Create - changed, json_output['forwarding_rule'] = create_global_forwarding_rule(client, - params=params, - project_id=conn_params['project_id']) - elif params['state'] == 'absent': - # Delete - changed, json_output['forwarding_rule'] = delete_global_forwarding_rule(client, - name=params['forwarding_rule_name'], - project_id=conn_params['project_id']) - else: - changed, json_output['forwarding_rule'] = update_global_forwarding_rule(client, - forwarding_rule=forwarding_rule, - params=params, - name=params['forwarding_rule_name'], - project_id=conn_params['project_id']) - - json_output['changed'] = changed - json_output.update(params) - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/cloud/google/gcp_healthcheck.py b/plugins/modules/cloud/google/gcp_healthcheck.py deleted file mode 100644 index b1c8b428bb..0000000000 --- a/plugins/modules/cloud/google/gcp_healthcheck.py +++ /dev/null @@ -1,444 +0,0 @@ -#!/usr/bin/python -# Copyright 2017 Google Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -DOCUMENTATION = ''' ---- -module: gcp_healthcheck -short_description: Create, Update or Destroy a Healthcheck. -description: - - Create, Update or Destroy a Healthcheck. Currently only HTTP and - HTTPS Healthchecks are supported. Healthchecks are used to monitor - individual instances, managed instance groups and/or backend - services. Healtchecks are reusable. - - Visit - U(https://cloud.google.com/compute/docs/load-balancing/health-checks) - for an overview of Healthchecks on GCP. - - See - U(https://cloud.google.com/compute/docs/reference/latest/httpHealthChecks) for - API details on HTTP Healthchecks. - - See - U(https://cloud.google.com/compute/docs/reference/latest/httpsHealthChecks) - for more details on the HTTPS Healtcheck API. -requirements: - - "python >= 2.6" - - "google-api-python-client >= 1.6.2" - - "google-auth >= 0.9.0" - - "google-auth-httplib2 >= 0.0.2" -notes: - - Only supports HTTP and HTTPS Healthchecks currently. -deprecated: - removed_in: 2.0.0 # was Ansible 2.12 - why: Updated modules released with increased functionality - alternative: > - Use M(gcp_compute_health_check), M(gcp_compute_http_health_check) or - M(gcp_compute_https_health_check) instead. -author: - - "Tom Melendez (@supertom) " -options: - check_interval: - description: - - How often (in seconds) to send a health check. - default: 5 - healthcheck_name: - description: - - Name of the Healthcheck. - required: true - healthcheck_type: - description: - - Type of Healthcheck. - required: true - choices: ["HTTP", "HTTPS"] - host_header: - description: - - The value of the host header in the health check request. If left - empty, the public IP on behalf of which this health - check is performed will be used. - required: true - default: "" - port: - description: - - The TCP port number for the health check request. The default value is - 443 for HTTPS and 80 for HTTP. - request_path: - description: - - The request path of the HTTPS health check request. - required: false - default: "/" - state: - description: State of the Healthcheck. - required: true - choices: ["present", "absent"] - timeout: - description: - - How long (in seconds) to wait for a response before claiming - failure. It is invalid for timeout - to have a greater value than check_interval. - default: 5 - unhealthy_threshold: - description: - - A so-far healthy instance will be marked unhealthy after this - many consecutive failures. - default: 2 - healthy_threshold: - description: - - A so-far unhealthy instance will be marked healthy after this - many consecutive successes. - default: 2 - service_account_email: - description: - - service account email - service_account_permissions: - description: - - service account permissions (see - U(https://cloud.google.com/sdk/gcloud/reference/compute/instances/create), - --scopes section for detailed information) - choices: [ - "bigquery", "cloud-platform", "compute-ro", "compute-rw", - "useraccounts-ro", "useraccounts-rw", "datastore", "logging-write", - "monitoring", "sql-admin", "storage-full", "storage-ro", - "storage-rw", "taskqueue", "userinfo-email" - ] - credentials_file: - description: - - Path to the JSON file associated with the service account email - project_id: - description: - - Your GCP project ID -''' - -EXAMPLES = ''' -- name: Create Minimum HealthCheck - gcp_healthcheck: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - healthcheck_name: my-healthcheck - healthcheck_type: HTTP - state: present -- name: Create HTTP HealthCheck - gcp_healthcheck: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - healthcheck_name: my-healthcheck - healthcheck_type: HTTP - host: my-host - request_path: /hc - check_interval: 10 - timeout: 30 - unhealthy_threshhold: 2 - healthy_threshhold: 1 - state: present -- name: Create HTTPS HealthCheck - gcp_healthcheck: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - healthcheck_name: "{{ https_healthcheck }}" - healthcheck_type: HTTPS - host_header: my-host - request_path: /hc - check_interval: 5 - timeout: 5 - unhealthy_threshold: 2 - healthy_threshold: 1 - state: present -''' - -RETURN = ''' -state: - description: state of the Healthcheck - returned: Always. - type: str - sample: present -healthcheck_name: - description: Name of the Healthcheck - returned: Always - type: str - sample: my-url-map -healthcheck_type: - description: Type of the Healthcheck - returned: Always - type: str - sample: HTTP -healthcheck: - description: GCP Healthcheck dictionary - returned: Always. Refer to GCP documentation for detailed field descriptions. - type: dict - sample: { "name": "my-hc", "port": 443, "requestPath": "/foo" } -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.gcp import get_google_api_client, GCPUtils - - -USER_AGENT_PRODUCT = 'ansible-healthcheck' -USER_AGENT_VERSION = '0.0.1' - - -def _validate_healthcheck_params(params): - """ - Validate healthcheck params. - - Simple validation has already assumed by AnsibleModule. - - :param params: Ansible dictionary containing configuration. - :type params: ``dict`` - - :return: True or raises ValueError - :rtype: ``bool`` or `class:ValueError` - """ - if params['timeout'] > params['check_interval']: - raise ValueError("timeout (%s) is greater than check_interval (%s)" % ( - params['timeout'], params['check_interval'])) - - return (True, '') - - -def _build_healthcheck_dict(params): - """ - Reformat services in Ansible Params for GCP. - - :param params: Params from AnsibleModule object - :type params: ``dict`` - - :param project_id: The GCP project ID. - :type project_id: ``str`` - - :return: dictionary suitable for submission to GCP - HealthCheck (HTTP/HTTPS) API. - :rtype ``dict`` - """ - gcp_dict = GCPUtils.params_to_gcp_dict(params, 'healthcheck_name') - if 'timeout' in gcp_dict: - gcp_dict['timeoutSec'] = gcp_dict['timeout'] - del gcp_dict['timeout'] - - if 'checkInterval' in gcp_dict: - gcp_dict['checkIntervalSec'] = gcp_dict['checkInterval'] - del gcp_dict['checkInterval'] - - if 'hostHeader' in gcp_dict: - gcp_dict['host'] = gcp_dict['hostHeader'] - del gcp_dict['hostHeader'] - - if 'healthcheckType' in gcp_dict: - del gcp_dict['healthcheckType'] - return gcp_dict - - -def _get_req_resource(client, resource_type): - if resource_type == 'HTTPS': - return (client.httpsHealthChecks(), 'httpsHealthCheck') - else: - return (client.httpHealthChecks(), 'httpHealthCheck') - - -def get_healthcheck(client, name, project_id=None, resource_type='HTTP'): - """ - Get a Healthcheck from GCP. - - :param client: An initialized GCE Compute Discovery resource. - :type client: :class: `googleapiclient.discovery.Resource` - - :param name: Name of the Url Map. - :type name: ``str`` - - :param project_id: The GCP project ID. - :type project_id: ``str`` - - :return: A dict resp from the respective GCP 'get' request. - :rtype: ``dict`` - """ - try: - resource, entity_name = _get_req_resource(client, resource_type) - args = {'project': project_id, entity_name: name} - req = resource.get(**args) - return GCPUtils.execute_api_client_req(req, raise_404=False) - except Exception: - raise - - -def create_healthcheck(client, params, project_id, resource_type='HTTP'): - """ - Create a new Healthcheck. - - :param client: An initialized GCE Compute Discovery resource. - :type client: :class: `googleapiclient.discovery.Resource` - - :param params: Dictionary of arguments from AnsibleModule. - :type params: ``dict`` - - :return: Tuple with changed status and response dict - :rtype: ``tuple`` in the format of (bool, dict) - """ - gcp_dict = _build_healthcheck_dict(params) - try: - resource, _ = _get_req_resource(client, resource_type) - args = {'project': project_id, 'body': gcp_dict} - req = resource.insert(**args) - return_data = GCPUtils.execute_api_client_req(req, client, raw=False) - if not return_data: - return_data = get_healthcheck(client, - name=params['healthcheck_name'], - project_id=project_id) - return (True, return_data) - except Exception: - raise - - -def delete_healthcheck(client, name, project_id, resource_type='HTTP'): - """ - Delete a Healthcheck. - - :param client: An initialized GCE Compute Discovery resource. - :type client: :class: `googleapiclient.discovery.Resource` - - :param name: Name of the Url Map. - :type name: ``str`` - - :param project_id: The GCP project ID. - :type project_id: ``str`` - - :return: Tuple with changed status and response dict - :rtype: ``tuple`` in the format of (bool, dict) - """ - try: - resource, entity_name = _get_req_resource(client, resource_type) - args = {'project': project_id, entity_name: name} - req = resource.delete(**args) - return_data = GCPUtils.execute_api_client_req(req, client) - return (True, return_data) - except Exception: - raise - - -def update_healthcheck(client, healthcheck, params, name, project_id, - resource_type='HTTP'): - """ - Update a Healthcheck. - - If the healthcheck has not changed, the update will not occur. - - :param client: An initialized GCE Compute Discovery resource. - :type client: :class: `googleapiclient.discovery.Resource` - - :param healthcheck: Name of the Url Map. - :type healthcheck: ``dict`` - - :param params: Dictionary of arguments from AnsibleModule. - :type params: ``dict`` - - :param name: Name of the Url Map. - :type name: ``str`` - - :param project_id: The GCP project ID. - :type project_id: ``str`` - - :return: Tuple with changed status and response dict - :rtype: ``tuple`` in the format of (bool, dict) - """ - gcp_dict = _build_healthcheck_dict(params) - ans = GCPUtils.are_params_equal(healthcheck, gcp_dict) - if ans: - return (False, 'no update necessary') - - try: - resource, entity_name = _get_req_resource(client, resource_type) - args = {'project': project_id, entity_name: name, 'body': gcp_dict} - req = resource.update(**args) - return_data = GCPUtils.execute_api_client_req( - req, client=client, raw=False) - return (True, return_data) - except Exception: - raise - - -def main(): - module = AnsibleModule(argument_spec=dict( - healthcheck_name=dict(required=True), - healthcheck_type=dict(required=True, - choices=['HTTP', 'HTTPS']), - request_path=dict(required=False, default='/'), - check_interval=dict(required=False, type='int', default=5), - healthy_threshold=dict(required=False, type='int', default=2), - unhealthy_threshold=dict(required=False, type='int', default=2), - host_header=dict(required=False, type='str', default=''), - timeout=dict(required=False, type='int', default=5), - port=dict(required=False, type='int'), - state=dict(choices=['absent', 'present'], default='present'), - service_account_email=dict(), - service_account_permissions=dict(type='list'), - credentials_file=dict(), - project_id=dict(), ), ) - - client, conn_params = get_google_api_client(module, 'compute', user_agent_product=USER_AGENT_PRODUCT, - user_agent_version=USER_AGENT_VERSION) - - params = {} - - params['healthcheck_name'] = module.params.get('healthcheck_name') - params['healthcheck_type'] = module.params.get('healthcheck_type') - params['request_path'] = module.params.get('request_path') - params['check_interval'] = module.params.get('check_interval') - params['healthy_threshold'] = module.params.get('healthy_threshold') - params['unhealthy_threshold'] = module.params.get('unhealthy_threshold') - params['host_header'] = module.params.get('host_header') - params['timeout'] = module.params.get('timeout') - params['port'] = module.params.get('port', None) - params['state'] = module.params.get('state') - - if not params['port']: - params['port'] = 80 - if params['healthcheck_type'] == 'HTTPS': - params['port'] = 443 - try: - _validate_healthcheck_params(params) - except Exception as e: - module.fail_json(msg=e.message, changed=False) - - changed = False - json_output = {'state': params['state']} - healthcheck = get_healthcheck(client, - name=params['healthcheck_name'], - project_id=conn_params['project_id'], - resource_type=params['healthcheck_type']) - - if not healthcheck: - if params['state'] == 'absent': - # Doesn't exist in GCE, and state==absent. - changed = False - module.fail_json( - msg="Cannot delete unknown healthcheck: %s" % - (params['healthcheck_name'])) - else: - # Create - changed, json_output['healthcheck'] = create_healthcheck(client, - params=params, - project_id=conn_params['project_id'], - resource_type=params['healthcheck_type']) - elif params['state'] == 'absent': - # Delete - changed, json_output['healthcheck'] = delete_healthcheck(client, - name=params['healthcheck_name'], - project_id=conn_params['project_id'], - resource_type=params['healthcheck_type']) - else: - changed, json_output['healthcheck'] = update_healthcheck(client, - healthcheck=healthcheck, - params=params, - name=params['healthcheck_name'], - project_id=conn_params['project_id'], - resource_type=params['healthcheck_type']) - json_output['changed'] = changed - json_output.update(params) - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/cloud/google/gcpubsub.py b/plugins/modules/cloud/google/gcpubsub.py deleted file mode 100644 index abaf9026ba..0000000000 --- a/plugins/modules/cloud/google/gcpubsub.py +++ /dev/null @@ -1,328 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, Google Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: gcpubsub -short_description: Create and Delete Topics/Subscriptions, Publish and pull messages on PubSub -description: - - Create and Delete Topics/Subscriptions, Publish and pull messages on PubSub. - See U(https://cloud.google.com/pubsub/docs) for an overview. -requirements: - - google-auth >= 0.5.0 - - google-cloud-pubsub >= 0.22.0 -notes: - - Subscription pull happens before publish. You cannot publish and pull in the same task. -author: - - Tom Melendez (@supertom) -options: - topic: - description: - - GCP pubsub topic name. - - Only the name, not the full path, is required. - required: yes - subscription: - description: - - Dictionary containing a subscription name associated with a topic (required), along with optional ack_deadline, push_endpoint and pull. - For pulling from a subscription, message_ack (bool), max_messages (int) and return_immediate are available as subfields. - See subfields name, push_endpoint and ack_deadline for more information. - name: - description: Subfield of subscription. Required if subscription is specified. See examples. - ack_deadline: - description: Subfield of subscription. Not required. Default deadline for subscriptions to ACK the message before it is resent. See examples. - pull: - description: - - Subfield of subscription. Not required. If specified, messages will be retrieved from topic via the provided subscription name. - max_messages (int; default None; max number of messages to pull), message_ack (bool; default False; acknowledge the message) and return_immediately - (bool; default True, don't wait for messages to appear). If the messages are acknowledged, changed is set to True, otherwise, changed is False. - push_endpoint: - description: - - Subfield of subscription. Not required. If specified, message will be sent to an endpoint. - See U(https://cloud.google.com/pubsub/docs/advanced#push_endpoints) for more information. - publish: - description: - - List of dictionaries describing messages and attributes to be published. Dictionary is in message(str):attributes(dict) format. - Only message is required. - state: - description: - - State of the topic or queue. - - Applies to the most granular resource. - - If subscription isspecified we remove it. - - If only topic is specified, that is what is removed. - - NOTE - A topic can be removed without first removing the subscription. - choices: [ absent, present ] - default: present -''' - -EXAMPLES = ''' -# (Message will be pushed; there is no check to see if the message was pushed before -- name: Create a topic and publish a message to it - gcpubsub: - topic: ansible-topic-example - state: present - -# Subscriptions associated with topic are not deleted. -- name: Delete Topic - gcpubsub: - topic: ansible-topic-example - state: absent - -# Setting absent will keep the messages from being sent -- name: Publish multiple messages, with attributes (key:value available with the message) - gcpubsub: - topic: '{{ topic_name }}' - state: present - publish: - - message: this is message 1 - attributes: - mykey1: myvalue - mykey2: myvalu2 - mykey3: myvalue3 - - message: this is message 2 - attributes: - server: prod - sla: "99.9999" - owner: fred - -- name: Create Subscription (pull) - gcpubsub: - topic: ansible-topic-example - subscription: - - name: mysub - state: present - -# pull is default, ack_deadline is not required -- name: Create Subscription with ack_deadline and push endpoint - gcpubsub: - topic: ansible-topic-example - subscription: - - name: mysub - ack_deadline: "60" - push_endpoint: http://pushendpoint.example.com - state: present - -# Setting push_endpoint to "None" converts subscription to pull. -- name: Subscription change from push to pull - gcpubsub: - topic: ansible-topic-example - subscription: - name: mysub - push_endpoint: "None" - -### Topic will not be deleted -- name: Delete subscription - gcpubsub: - topic: ansible-topic-example - subscription: - - name: mysub - state: absent - -# only pull keyword is required. -- name: Pull messages from subscription - gcpubsub: - topic: ansible-topic-example - subscription: - name: ansible-topic-example-sub - pull: - message_ack: yes - max_messages: "100" -''' - -RETURN = ''' -publish: - description: List of dictionaries describing messages and attributes to be published. Dictionary is in message(str):attributes(dict) format. - Only message is required. - returned: Only when specified - type: list - sample: "publish: ['message': 'my message', attributes: {'key1': 'value1'}]" - -pulled_messages: - description: list of dictionaries containing message info. Fields are ack_id, attributes, data, message_id. - returned: Only when subscription.pull is specified - type: list - sample: [{ "ack_id": "XkASTCcYREl...","attributes": {"key1": "val1",...}, "data": "this is message 1", "message_id": "49107464153705"},..] - -state: - description: The state of the topic or subscription. Value will be either 'absent' or 'present'. - returned: Always - type: str - sample: "present" - -subscription: - description: Name of subscription. - returned: When subscription fields are specified - type: str - sample: "mysubscription" - -topic: - description: Name of topic. - returned: Always - type: str - sample: "mytopic" -''' - -try: - from ast import literal_eval - HAS_PYTHON26 = True -except ImportError: - HAS_PYTHON26 = False - -try: - from google.cloud import pubsub - HAS_GOOGLE_CLOUD_PUBSUB = True -except ImportError as e: - HAS_GOOGLE_CLOUD_PUBSUB = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.gcp import check_min_pkg_version, get_google_cloud_credentials - - -CLOUD_CLIENT = 'google-cloud-pubsub' -CLOUD_CLIENT_MINIMUM_VERSION = '0.22.0' -CLOUD_CLIENT_USER_AGENT = 'ansible-pubsub-0.1' - - -def publish_messages(message_list, topic): - with topic.batch() as batch: - for message in message_list: - msg = message['message'] - attrs = {} - if 'attributes' in message: - attrs = message['attributes'] - batch.publish(bytes(msg), **attrs) - return True - - -def pull_messages(pull_params, sub): - """ - :rtype: tuple (output, changed) - """ - changed = False - max_messages = pull_params.get('max_messages', None) - message_ack = pull_params.get('message_ack', 'no') - return_immediately = pull_params.get('return_immediately', False) - - output = [] - pulled = sub.pull(return_immediately=return_immediately, max_messages=max_messages) - - for ack_id, msg in pulled: - msg_dict = {'message_id': msg.message_id, - 'attributes': msg.attributes, - 'data': msg.data, - 'ack_id': ack_id} - output.append(msg_dict) - - if message_ack: - ack_ids = [m['ack_id'] for m in output] - if ack_ids: - sub.acknowledge(ack_ids) - changed = True - return (output, changed) - - -def main(): - - module = AnsibleModule( - argument_spec=dict( - topic=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - publish=dict(type='list'), - subscription=dict(type='dict'), - service_account_email=dict(type='str'), - credentials_file=dict(type='str'), - project_id=dict(type='str'), - ), - ) - - if not HAS_PYTHON26: - module.fail_json( - msg="GCE module requires python's 'ast' module, python v2.6+") - - if not HAS_GOOGLE_CLOUD_PUBSUB: - module.fail_json(msg="Please install google-cloud-pubsub library.") - - if not check_min_pkg_version(CLOUD_CLIENT, CLOUD_CLIENT_MINIMUM_VERSION): - module.fail_json(msg="Please install %s client version %s" % (CLOUD_CLIENT, CLOUD_CLIENT_MINIMUM_VERSION)) - - mod_params = {} - mod_params['publish'] = module.params.get('publish') - mod_params['state'] = module.params.get('state') - mod_params['topic'] = module.params.get('topic') - mod_params['subscription'] = module.params.get('subscription') - - creds, params = get_google_cloud_credentials(module) - pubsub_client = pubsub.Client(project=params['project_id'], credentials=creds, use_gax=False) - pubsub_client.user_agent = CLOUD_CLIENT_USER_AGENT - - changed = False - json_output = {} - - t = None - if mod_params['topic']: - t = pubsub_client.topic(mod_params['topic']) - s = None - if mod_params['subscription']: - # Note: default ack deadline cannot be changed without deleting/recreating subscription - s = t.subscription(mod_params['subscription']['name'], - ack_deadline=mod_params['subscription'].get('ack_deadline', None), - push_endpoint=mod_params['subscription'].get('push_endpoint', None)) - - if mod_params['state'] == 'absent': - # Remove the most granular resource. If subscription is specified - # we remove it. If only topic is specified, that is what is removed. - # Note that a topic can be removed without first removing the subscription. - # TODO(supertom): Enhancement: Provide an option to only delete a topic - # if there are no subscriptions associated with it (which the API does not support). - if s is not None: - if s.exists(): - s.delete() - changed = True - else: - if t.exists(): - t.delete() - changed = True - elif mod_params['state'] == 'present': - if not t.exists(): - t.create() - changed = True - if s: - if not s.exists(): - s.create() - s.reload() - changed = True - else: - # Subscription operations - # TODO(supertom): if more 'update' operations arise, turn this into a function. - s.reload() - push_endpoint = mod_params['subscription'].get('push_endpoint', None) - if push_endpoint is not None: - if push_endpoint != s.push_endpoint: - if push_endpoint == 'None': - push_endpoint = None - s.modify_push_configuration(push_endpoint=push_endpoint) - s.reload() - changed = push_endpoint == s.push_endpoint - - if 'pull' in mod_params['subscription']: - if s.push_endpoint is not None: - module.fail_json(msg="Cannot pull messages, push_endpoint is configured.") - (json_output['pulled_messages'], changed) = pull_messages( - mod_params['subscription']['pull'], s) - - # publish messages to the topic - if mod_params['publish'] and len(mod_params['publish']) > 0: - changed = publish_messages(mod_params['publish'], t) - - json_output['changed'] = changed - json_output.update(mod_params) - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/gcp_backend_service.py b/plugins/modules/gcp_backend_service.py deleted file mode 120000 index 04a387bb88..0000000000 --- a/plugins/modules/gcp_backend_service.py +++ /dev/null @@ -1 +0,0 @@ -./cloud/google/gcp_backend_service.py \ No newline at end of file diff --git a/plugins/modules/gcp_forwarding_rule.py b/plugins/modules/gcp_forwarding_rule.py deleted file mode 120000 index 16dbf69275..0000000000 --- a/plugins/modules/gcp_forwarding_rule.py +++ /dev/null @@ -1 +0,0 @@ -./cloud/google/gcp_forwarding_rule.py \ No newline at end of file diff --git a/plugins/modules/gcp_healthcheck.py b/plugins/modules/gcp_healthcheck.py deleted file mode 120000 index 47b97c6c93..0000000000 --- a/plugins/modules/gcp_healthcheck.py +++ /dev/null @@ -1 +0,0 @@ -./cloud/google/gcp_healthcheck.py \ No newline at end of file diff --git a/plugins/modules/gcpubsub.py b/plugins/modules/gcpubsub.py deleted file mode 120000 index 38f778219f..0000000000 --- a/plugins/modules/gcpubsub.py +++ /dev/null @@ -1 +0,0 @@ -./cloud/google/gcpubsub.py \ No newline at end of file diff --git a/tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py b/tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py deleted file mode 100644 index ebaf6bb51f..0000000000 --- a/tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py +++ /dev/null @@ -1,30 +0,0 @@ -import unittest - -from ansible_collections.community.general.plugins.modules.cloud.google.gcp_forwarding_rule import _build_global_forwarding_rule_dict - - -class TestGCPFowardingRule(unittest.TestCase): - """Unit tests for gcp_fowarding_rule module.""" - params_dict = { - 'forwarding_rule_name': 'foo_fowarding_rule_name', - 'address': 'foo_external_address', - 'target': 'foo_targetproxy', - 'region': 'global', - 'port_range': 80, - 'protocol': 'TCP', - 'state': 'present', - } - - def test__build_global_forwarding_rule_dict(self): - - expected = { - 'name': 'foo_fowarding_rule_name', - 'IPAddress': 'https://www.googleapis.com/compute/v1/projects/my-project/global/addresses/foo_external_address', - 'target': 'https://www.googleapis.com/compute/v1/projects/my-project/global/targetHttpProxies/foo_targetproxy', - 'region': 'global', - 'portRange': 80, - 'IPProtocol': 'TCP', - } - actual = _build_global_forwarding_rule_dict( - self.params_dict, 'my-project') - self.assertEqual(expected, actual)