From b75e8d19beb215fd58f3db44fcbec2a52401ac48 Mon Sep 17 00:00:00 2001
From: The Magician <magic-modules@google.com>
Date: Fri, 19 Jul 2019 11:32:05 -0700
Subject: [PATCH] Bug fixes for GCP (as of 2019-07-09T06:06:53Z) (#59132)

* Bug fixes for GCP modules

* ignore syntax
---
 .../google/gcp_resourcemanager_project.py     |  8 +++-
 .../cloud/google/gcp_sourcerepo_repository.py |  1 +
 .../cloud/google/gcp_spanner_database.py      | 48 ++++++++++++++++---
 .../google/gcp_spanner_database_facts.py      |  1 +
 .../cloud/google/gcp_spanner_instance.py      |  9 +++-
 .../modules/cloud/google/gcp_sql_database.py  |  4 ++
 .../cloud/google/gcp_sql_database_facts.py    |  1 +
 .../modules/cloud/google/gcp_sql_instance.py  | 37 ++++++++++++++
 .../modules/cloud/google/gcp_sql_user.py      |  4 ++
 .../cloud/google/gcp_sql_user_facts.py        |  1 +
 .../cloud/google/gcp_storage_bucket.py        | 43 +++++++++++++++++
 .../gcp_storage_bucket_access_control.py      |  7 +++
 .../cloud/google/gcp_storage_object.py        |  4 ++
 .../modules/cloud/google/gcp_tpu_node.py      | 11 ++++-
 .../cloud/google/gcp_tpu_node_facts.py        |  1 +
 .../gcp_spanner_database/tasks/main.yml       |  4 +-
 .../gcp_spanner_instance/tasks/main.yml       |  4 +-
 test/sanity/validate-modules/ignore.txt       |  4 --
 18 files changed, 174 insertions(+), 18 deletions(-)

diff --git a/lib/ansible/modules/cloud/google/gcp_resourcemanager_project.py b/lib/ansible/modules/cloud/google/gcp_resourcemanager_project.py
index 5418514d314..bf9c2556203 100644
--- a/lib/ansible/modules/cloud/google/gcp_resourcemanager_project.py
+++ b/lib/ansible/modules/cloud/google/gcp_resourcemanager_project.py
@@ -54,6 +54,7 @@ options:
       Allowed characters are: lowercase and uppercase letters, numbers, hyphen, single-quote,
       double-quote, space, and exclamation point.'
     required: false
+    type: str
   labels:
     description:
     - The labels associated with this Project.
@@ -65,25 +66,30 @@ options:
     - Clients should store labels in a representation such as JSON that does not depend
       on specific characters being disallowed .
     required: false
+    type: dict
   parent:
     description:
     - A parent organization.
     required: false
+    type: dict
     suboptions:
       type:
         description:
         - Must be organization.
         required: false
+        type: str
       id:
         description:
         - Id of the organization.
         required: false
+        type: str
   id:
     description:
     - The unique, user-assigned ID of the Project. It must be 6 to 30 lowercase letters,
       digits, or hyphens. It must start with a letter.
     - Trailing hyphens are prohibited.
     required: true
+    type: str
 extends_documentation_fragment: gcp
 '''
 
@@ -329,7 +335,7 @@ def wait_for_operation(module, response):
         return {}
     status = navigate_hash(op_result, ['done'])
     wait_done = wait_for_completion(status, op_result, module)
-    raise_if_errors(op_result, ['error'], module)
+    raise_if_errors(wait_done, ['error'], module)
     return navigate_hash(wait_done, ['response'])
 
 
diff --git a/lib/ansible/modules/cloud/google/gcp_sourcerepo_repository.py b/lib/ansible/modules/cloud/google/gcp_sourcerepo_repository.py
index 3e0d482a9db..cb690e9bae1 100644
--- a/lib/ansible/modules/cloud/google/gcp_sourcerepo_repository.py
+++ b/lib/ansible/modules/cloud/google/gcp_sourcerepo_repository.py
@@ -53,6 +53,7 @@ options:
     - The repo name may contain slashes. eg, projects/myproject/repos/name/with/slash
       .
     required: true
+    type: str
 extends_documentation_fragment: gcp
 notes:
 - 'API Reference: U(https://cloud.google.com/source-repositories/docs/reference/rest/v1/projects.repos)'
diff --git a/lib/ansible/modules/cloud/google/gcp_spanner_database.py b/lib/ansible/modules/cloud/google/gcp_spanner_database.py
index fe585a05e82..e59d4c950e7 100644
--- a/lib/ansible/modules/cloud/google/gcp_spanner_database.py
+++ b/lib/ansible/modules/cloud/google/gcp_spanner_database.py
@@ -52,6 +52,7 @@ options:
     - A unique identifier for the database, which cannot be changed after the instance
       is created. Values are of the form [a-z][-a-z0-9]*[a-z0-9].
     required: true
+    type: str
   extra_statements:
     description:
     - 'An optional list of DDL statements to run inside the newly created database.
@@ -59,6 +60,7 @@ options:
       with the creation of the database: if there is an error in any statement, the
       database is not created.'
     required: false
+    type: list
   instance:
     description:
     - The instance to create the database on.
@@ -68,6 +70,7 @@ options:
       to a gcp_spanner_instance task and then set this instance field to "{{ name-of-resource
       }}"'
     required: true
+    type: dict
 extends_documentation_fragment: gcp
 notes:
 - 'API Reference: U(https://cloud.google.com/spanner/docs/reference/rest/v1/projects.instances.databases)'
@@ -127,6 +130,7 @@ instance:
 
 from ansible.module_utils.gcp_utils import navigate_hash, GcpSession, GcpModule, GcpRequest, replace_resource_dict
 import json
+import time
 
 ################################################################################
 # Main
@@ -177,7 +181,7 @@ def main():
 
 def create(module, link):
     auth = GcpSession(module, 'spanner')
-    return return_if_object(module, auth.post(link, resource_to_request(module)))
+    return wait_for_operation(module, auth.post(link, resource_to_request(module)))
 
 
 def update(module, link):
@@ -191,11 +195,7 @@ def delete(module, link):
 
 
 def resource_to_request(module):
-    request = {
-        u'instance': replace_resource_dict(module.params.get(u'instance', {}), 'name'),
-        u'name': module.params.get('name'),
-        u'extraStatements': module.params.get('extra_statements'),
-    }
+    request = {u'name': module.params.get('name'), u'extraStatements': module.params.get('extra_statements')}
     request = encode_request(request, module)
     return_vals = {}
     for k, v in request.items():
@@ -268,6 +268,42 @@ def response_to_hash(module, response):
     return {u'name': module.params.get('name'), u'extraStatements': module.params.get('extra_statements')}
 
 
+def async_op_url(module, extra_data=None):
+    if extra_data is None:
+        extra_data = {}
+    url = "https://spanner.googleapis.com/v1/{op_id}"
+    combined = extra_data.copy()
+    combined.update(module.params)
+    return url.format(**combined)
+
+
+def wait_for_operation(module, response):
+    op_result = return_if_object(module, response)
+    if op_result is None:
+        return {}
+    status = navigate_hash(op_result, ['done'])
+    wait_done = wait_for_completion(status, op_result, module)
+    raise_if_errors(wait_done, ['error'], module)
+    return navigate_hash(wait_done, ['response'])
+
+
+def wait_for_completion(status, op_result, module):
+    op_id = navigate_hash(op_result, ['name'])
+    op_uri = async_op_url(module, {'op_id': op_id})
+    while not status:
+        raise_if_errors(op_result, ['error'], module)
+        time.sleep(1.0)
+        op_result = fetch_resource(module, op_uri, False)
+        status = navigate_hash(op_result, ['done'])
+    return op_result
+
+
+def raise_if_errors(response, err_path, module):
+    errors = navigate_hash(response, err_path)
+    if errors is not None:
+        module.fail_json(msg=errors)
+
+
 def decode_response(response, module):
     if not response:
         return response
diff --git a/lib/ansible/modules/cloud/google/gcp_spanner_database_facts.py b/lib/ansible/modules/cloud/google/gcp_spanner_database_facts.py
index d3e0c5795d1..90130dd5f82 100644
--- a/lib/ansible/modules/cloud/google/gcp_spanner_database_facts.py
+++ b/lib/ansible/modules/cloud/google/gcp_spanner_database_facts.py
@@ -49,6 +49,7 @@ options:
       to a gcp_spanner_instance task and then set this instance field to "{{ name-of-resource
       }}"'
     required: true
+    type: dict
 extends_documentation_fragment: gcp
 '''
 
diff --git a/lib/ansible/modules/cloud/google/gcp_spanner_instance.py b/lib/ansible/modules/cloud/google/gcp_spanner_instance.py
index 588a594a242..8b7dd74bd5f 100644
--- a/lib/ansible/modules/cloud/google/gcp_spanner_instance.py
+++ b/lib/ansible/modules/cloud/google/gcp_spanner_instance.py
@@ -52,6 +52,7 @@ options:
     - A unique identifier for the instance, which cannot be changed after the instance
       is created. The name must be between 6 and 30 characters in length.
     required: true
+    type: str
   config:
     description:
     - The name of the instance's configuration (similar but not quite the same as
@@ -61,21 +62,25 @@ options:
     - In order to obtain a valid list please consult the [Configuration section of
       the docs](U(https://cloud.google.com/spanner/docs/instances)).
     required: true
+    type: str
   display_name:
     description:
     - The descriptive name for this instance as it appears in UIs. Must be unique
       per project and between 4 and 30 characters in length.
     required: true
+    type: str
   node_count:
     description:
     - The number of nodes allocated to this instance.
     required: false
     default: '1'
+    type: int
   labels:
     description:
     - 'An object containing a list of "key": value pairs.'
     - 'Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.'
     required: false
+    type: dict
 extends_documentation_fragment: gcp
 notes:
 - 'API Reference: U(https://cloud.google.com/spanner/docs/reference/rest/v1/projects.instances)'
@@ -202,7 +207,7 @@ def update(module, link):
 
 def delete(module, link):
     auth = GcpSession(module, 'spanner')
-    return wait_for_operation(module, auth.delete(link))
+    return return_if_object(module, auth.delete(link))
 
 
 def resource_to_request(module):
@@ -303,7 +308,7 @@ def wait_for_operation(module, response):
         return {}
     status = navigate_hash(op_result, ['done'])
     wait_done = wait_for_completion(status, op_result, module)
-    raise_if_errors(op_result, ['error'], module)
+    raise_if_errors(wait_done, ['error'], module)
     return navigate_hash(wait_done, ['response'])
 
 
diff --git a/lib/ansible/modules/cloud/google/gcp_sql_database.py b/lib/ansible/modules/cloud/google/gcp_sql_database.py
index 608cb2a6eb5..523ea834db2 100644
--- a/lib/ansible/modules/cloud/google/gcp_sql_database.py
+++ b/lib/ansible/modules/cloud/google/gcp_sql_database.py
@@ -51,19 +51,23 @@ options:
     description:
     - The MySQL charset value.
     required: false
+    type: str
   collation:
     description:
     - The MySQL collation value.
     required: false
+    type: str
   name:
     description:
     - The name of the database in the Cloud SQL instance.
     - This does not include the project ID or instance name.
     required: true
+    type: str
   instance:
     description:
     - The name of the Cloud SQL instance. This does not include the project ID.
     required: true
+    type: str
 extends_documentation_fragment: gcp
 '''
 
diff --git a/lib/ansible/modules/cloud/google/gcp_sql_database_facts.py b/lib/ansible/modules/cloud/google/gcp_sql_database_facts.py
index 41d96b1e091..017f5d3161d 100644
--- a/lib/ansible/modules/cloud/google/gcp_sql_database_facts.py
+++ b/lib/ansible/modules/cloud/google/gcp_sql_database_facts.py
@@ -44,6 +44,7 @@ options:
     description:
     - The name of the Cloud SQL instance. This does not include the project ID.
     required: true
+    type: str
 extends_documentation_fragment: gcp
 '''
 
diff --git a/lib/ansible/modules/cloud/google/gcp_sql_instance.py b/lib/ansible/modules/cloud/google/gcp_sql_instance.py
index 536a7b643bf..a3c5284c1c1 100644
--- a/lib/ansible/modules/cloud/google/gcp_sql_instance.py
+++ b/lib/ansible/modules/cloud/google/gcp_sql_instance.py
@@ -56,10 +56,12 @@ options:
     - "* EXTERNAL: A database server that is not managed by Google."
     - 'Some valid choices include: "FIRST_GEN", "SECOND_GEN", "EXTERNAL"'
     required: false
+    type: str
   connection_name:
     description:
     - Connection name of the Cloud SQL instance used in connection strings.
     required: false
+    type: str
   database_version:
     description:
     - The database engine type and version. For First Generation instances, can be
@@ -69,11 +71,13 @@ options:
       changed after instance creation.'
     - 'Some valid choices include: "MYSQL_5_5", "MYSQL_5_6", "MYSQL_5_7", "POSTGRES_9_6"'
     required: false
+    type: str
   failover_replica:
     description:
     - The name and status of the failover replica. This property is applicable only
       to Second Generation instances.
     required: false
+    type: dict
     suboptions:
       name:
         description:
@@ -81,6 +85,7 @@ options:
           replica is created for the instance. The name doesn't include the project
           ID. This property is applicable only to Second Generation instances.
         required: false
+        type: str
   instance_type:
     description:
     - The instance type. This can be one of the following.
@@ -89,32 +94,39 @@ options:
     - "* READ_REPLICA_INSTANCE: A Cloud SQL instance configured as a read-replica."
     - 'Some valid choices include: "CLOUD_SQL_INSTANCE", "ON_PREMISES_INSTANCE", "READ_REPLICA_INSTANCE"'
     required: false
+    type: str
   ipv6_address:
     description:
     - The IPv6 address assigned to the instance. This property is applicable only
       to First Generation instances.
     required: false
+    type: str
   master_instance_name:
     description:
     - The name of the instance which will act as master in the replication setup.
     required: false
+    type: str
   max_disk_size:
     description:
     - The maximum disk size of the instance in bytes.
     required: false
+    type: int
   name:
     description:
     - Name of the Cloud SQL instance. This does not include the project ID.
     required: true
+    type: str
   region:
     description:
     - The geographical region. Defaults to us-central or us-central1 depending on
       the instance type (First Generation or Second Generation/PostgreSQL).
     required: false
+    type: str
   replica_configuration:
     description:
     - Configuration specific to failover replicas and read replicas.
     required: false
+    type: dict
     suboptions:
       failover_target:
         description:
@@ -134,24 +146,29 @@ options:
           is used only to set up the replication connection and is stored by MySQL
           in a file named master.info in the data directory.
         required: false
+        type: dict
         suboptions:
           ca_certificate:
             description:
             - PEM representation of the trusted CA's x509 certificate.
             required: false
+            type: str
           client_certificate:
             description:
             - PEM representation of the slave's x509 certificate .
             required: false
+            type: str
           client_key:
             description:
             - PEM representation of the slave's private key. The corresponding public
               key is encoded in the client's certificate.
             required: false
+            type: str
           connect_retry_interval:
             description:
             - Seconds to wait between connect retries. MySQL's default is 60 seconds.
             required: false
+            type: int
           dump_file_path:
             description:
             - Path to a SQL dump file in Google Cloud Storage from which the slave
@@ -160,22 +177,27 @@ options:
               binlog co-ordinates from which replication should begin. This can be
               accomplished by setting --master-data to 1 when using mysqldump.
             required: false
+            type: str
           master_heartbeat_period:
             description:
             - Interval in milliseconds between replication heartbeats.
             required: false
+            type: int
           password:
             description:
             - The password for the replication connection.
             required: false
+            type: str
           ssl_cipher:
             description:
             - A list of permissible ciphers to use for SSL encryption.
             required: false
+            type: str
           username:
             description:
             - The username for the replication connection.
             required: false
+            type: str
           verify_server_certificate:
             description:
             - Whether or not to check the master's Common Name value in the certificate
@@ -186,20 +208,24 @@ options:
         description:
         - The replicas of the instance.
         required: false
+        type: list
       service_account_email_address:
         description:
         - The service account email address assigned to the instance. This property
           is applicable only to Second Generation instances.
         required: false
+        type: str
   settings:
     description:
     - The user settings.
     required: false
+    type: dict
     suboptions:
       database_flags:
         description:
         - The database flags passed to the instance at startup.
         required: false
+        type: list
         version_added: 2.9
         suboptions:
           name:
@@ -208,17 +234,20 @@ options:
               include both server options and system variables for MySQL. Flags should
               be specified with underscores, not hyphens.
             required: false
+            type: str
           value:
             description:
             - The value of the flag. Booleans should be set to on for true and off
               for false. This field must be omitted if the flag doesn't take a value.
             required: false
+            type: str
       ip_configuration:
         description:
         - The settings for IP Management. This allows to enable or disable the instance
           IP and manage which external networks can connect to the instance. The IPv4
           address cannot be disabled for Second Generation instances.
         required: false
+        type: dict
         suboptions:
           ipv4_enabled:
             description:
@@ -231,22 +260,26 @@ options:
               using the IP. In CIDR notation, also known as 'slash' notation (e.g.
               192.168.100.0/24).
             required: false
+            type: list
             suboptions:
               expiration_time:
                 description:
                 - The time when this access control entry expires in RFC 3339 format,
                   for example 2012-11-15T16:19:00.094Z.
                 required: false
+                type: str
               name:
                 description:
                 - An optional label to identify this entry.
                 required: false
+                type: str
               value:
                 description:
                 - The whitelisted value for the access control list. For example,
                   to grant access to a client from an external IP (IPv4 or IPv6) address
                   or subnet, use that address or subnet here.
                 required: false
+                type: str
           require_ssl:
             description:
             - Whether the mysqld should default to 'REQUIRE X509' for users connecting
@@ -259,15 +292,18 @@ options:
           For MySQL instances, this field determines whether the instance is Second
           Generation (recommended) or First Generation.
         required: false
+        type: str
       availability_type:
         description:
         - The availabilityType define if your postgres instance is run zonal or regional.
         - 'Some valid choices include: "ZONAL", "REGIONAL"'
         required: false
+        type: str
       backup_configuration:
         description:
         - The daily backup configuration for the instance.
         required: false
+        type: dict
         suboptions:
           enabled:
             description:
@@ -284,6 +320,7 @@ options:
             description:
             - Define the backup start time in UTC (HH:MM) .
             required: false
+            type: str
 extends_documentation_fragment: gcp
 '''
 
diff --git a/lib/ansible/modules/cloud/google/gcp_sql_user.py b/lib/ansible/modules/cloud/google/gcp_sql_user.py
index 6428e7f3416..0c52ef27d6c 100644
--- a/lib/ansible/modules/cloud/google/gcp_sql_user.py
+++ b/lib/ansible/modules/cloud/google/gcp_sql_user.py
@@ -53,10 +53,12 @@ options:
       to an empty string. For update operations, host is specified as part of the
       request URL. The host name cannot be updated after insertion.
     required: true
+    type: str
   name:
     description:
     - The name of the user in the Cloud SQL instance.
     required: true
+    type: str
   instance:
     description:
     - The name of the Cloud SQL instance. This does not include the project ID.
@@ -66,10 +68,12 @@ options:
       to a gcp_sql_instance task and then set this instance field to "{{ name-of-resource
       }}"'
     required: true
+    type: dict
   password:
     description:
     - The password for the user.
     required: false
+    type: str
 extends_documentation_fragment: gcp
 '''
 
diff --git a/lib/ansible/modules/cloud/google/gcp_sql_user_facts.py b/lib/ansible/modules/cloud/google/gcp_sql_user_facts.py
index 5b151ef59e6..989652cbce7 100644
--- a/lib/ansible/modules/cloud/google/gcp_sql_user_facts.py
+++ b/lib/ansible/modules/cloud/google/gcp_sql_user_facts.py
@@ -49,6 +49,7 @@ options:
       to a gcp_sql_instance task and then set this instance field to "{{ name-of-resource
       }}"'
     required: true
+    type: dict
 extends_documentation_fragment: gcp
 '''
 
diff --git a/lib/ansible/modules/cloud/google/gcp_storage_bucket.py b/lib/ansible/modules/cloud/google/gcp_storage_bucket.py
index 3c483eff404..58ac095b85c 100644
--- a/lib/ansible/modules/cloud/google/gcp_storage_bucket.py
+++ b/lib/ansible/modules/cloud/google/gcp_storage_bucket.py
@@ -56,6 +56,7 @@ options:
     description:
     - Access controls on the bucket.
     required: false
+    type: list
     suboptions:
       bucket:
         description:
@@ -66,6 +67,7 @@ options:
           to a gcp_storage_bucket task and then set this bucket field to "{{ name-of-resource
           }}"'
         required: true
+        type: dict
       entity:
         description:
         - 'The entity holding the permission, in one of the following forms: user-userId
@@ -76,59 +78,71 @@ options:
         - To refer to all members of the Google Apps for Business domain example.com,
           the entity would be domain-example.com.
         required: true
+        type: str
       entity_id:
         description:
         - The ID for the entity.
         required: false
+        type: str
       project_team:
         description:
         - The project team associated with the entity.
         required: false
+        type: dict
         suboptions:
           project_number:
             description:
             - The project team associated with the entity.
             required: false
+            type: str
           team:
             description:
             - The team.
             - 'Some valid choices include: "editors", "owners", "viewers"'
             required: false
+            type: str
       role:
         description:
         - The access permission for the entity.
         - 'Some valid choices include: "OWNER", "READER", "WRITER"'
         required: false
+        type: str
   cors:
     description:
     - The bucket's Cross-Origin Resource Sharing (CORS) configuration.
     required: false
+    type: list
     suboptions:
       max_age_seconds:
         description:
         - The value, in seconds, to return in the Access-Control-Max-Age header used
           in preflight responses.
         required: false
+        type: int
       method:
         description:
         - 'The list of HTTP methods on which to include CORS response headers, (GET,
           OPTIONS, POST, etc) Note: "*" is permitted in the list of methods, and means
           "any method".'
         required: false
+        type: list
       origin:
         description:
         - The list of Origins eligible to receive CORS response headers.
         - 'Note: "*" is permitted in the list of origins, and means "any Origin".'
         required: false
+        type: list
       response_header:
         description:
         - The list of HTTP headers other than the simple response headers to give
           permission for the user-agent to share across domains.
         required: false
+        type: list
   default_object_acl:
     description:
     - Default access controls to apply to new objects when no ACL is provided.
     required: false
+    type: list
     version_added: 2.7
     suboptions:
       bucket:
@@ -140,6 +154,7 @@ options:
           to a gcp_storage_bucket task and then set this bucket field to "{{ name-of-resource
           }}"'
         required: true
+        type: dict
       entity:
         description:
         - 'The entity holding the permission, in one of the following forms: * user-{{userId}}
@@ -148,58 +163,69 @@ options:
           (such as "domain-example.com") * project-team-{{projectId}} * allUsers *
           allAuthenticatedUsers .'
         required: true
+        type: str
       object:
         description:
         - The name of the object, if applied to an object.
         required: false
+        type: str
       role:
         description:
         - The access permission for the entity.
         - 'Some valid choices include: "OWNER", "READER"'
         required: true
+        type: str
   lifecycle:
     description:
     - The bucket's lifecycle configuration.
     - See U(https://developers.google.com/storage/docs/lifecycle) for more information.
     required: false
+    type: dict
     suboptions:
       rule:
         description:
         - A lifecycle management rule, which is made of an action to take and the
           condition(s) under which the action will be taken.
         required: false
+        type: list
         suboptions:
           action:
             description:
             - The action to take.
             required: false
+            type: dict
             suboptions:
               storage_class:
                 description:
                 - Target storage class. Required iff the type of the action is SetStorageClass.
                 required: false
+                type: str
               type:
                 description:
                 - Type of the action. Currently, only Delete and SetStorageClass are
                   supported.
                 - 'Some valid choices include: "Delete", "SetStorageClass"'
                 required: false
+                type: str
           condition:
             description:
             - The condition(s) under which the action will be taken.
             required: false
+            type: dict
             suboptions:
               age_days:
                 description:
                 - Age of an object (in days). This condition is satisfied when an
                   object reaches the specified age.
                 required: false
+                type: int
               created_before:
                 description:
                 - A date in RFC 3339 format with only the date part (for instance,
                   "2013-01-15"). This condition is satisfied when an object is created
                   before midnight of the specified date in UTC.
                 required: false
+                type: str
               is_live:
                 description:
                 - Relevant only for versioned objects. If the value is true, this
@@ -213,49 +239,59 @@ options:
                   will be matched. Values include MULTI_REGIONAL, REGIONAL, NEARLINE,
                   COLDLINE, STANDARD, and DURABLE_REDUCED_AVAILABILITY.
                 required: false
+                type: list
               num_newer_versions:
                 description:
                 - Relevant only for versioned objects. If the value is N, this condition
                   is satisfied when there are at least N versions (including the live
                   version) newer than this version of the object.
                 required: false
+                type: int
   location:
     description:
     - The location of the bucket. Object data for objects in the bucket resides in
       physical storage within this region. Defaults to US. See the developer's guide
       for the authoritative list.
     required: false
+    type: str
   logging:
     description:
     - The bucket's logging configuration, which defines the destination bucket and
       optional name prefix for the current bucket's logs.
     required: false
+    type: dict
     suboptions:
       log_bucket:
         description:
         - The destination bucket where the current bucket's logs should be placed.
         required: false
+        type: str
       log_object_prefix:
         description:
         - A prefix for log object names.
         required: false
+        type: str
   metageneration:
     description:
     - The metadata generation of this bucket.
     required: false
+    type: int
   name:
     description:
     - The name of the bucket.
     required: false
+    type: str
   owner:
     description:
     - The owner of the bucket. This is always the project team's owner group.
     required: false
+    type: dict
     suboptions:
       entity:
         description:
         - The entity, in the form project-owner-projectId.
         required: false
+        type: str
   storage_class:
     description:
     - The bucket's default storage class, used whenever no storageClass is specified
@@ -267,10 +303,12 @@ options:
     - 'Some valid choices include: "MULTI_REGIONAL", "REGIONAL", "STANDARD", "NEARLINE",
       "COLDLINE", "DURABLE_REDUCED_AVAILABILITY"'
     required: false
+    type: str
   versioning:
     description:
     - The bucket's versioning configuration.
     required: false
+    type: dict
     suboptions:
       enabled:
         description:
@@ -283,6 +321,7 @@ options:
       accessing bucket contents as a web site. See the Static Website Examples for
       more information.
     required: false
+    type: dict
     suboptions:
       main_page_suffix:
         description:
@@ -291,16 +330,19 @@ options:
           object. This allows the creation of index.html objects to represent directory
           pages.
         required: false
+        type: str
       not_found_page:
         description:
         - If the requested object path is missing, and any mainPageSuffix object is
           missing, if applicable, the service will return the named object from this
           bucket as the content for a 404 Not Found result.
         required: false
+        type: str
   project:
     description:
     - A valid API project identifier.
     required: false
+    type: str
   predefined_default_object_acl:
     description:
     - Apply a predefined set of default object access controls to this bucket.
@@ -317,6 +359,7 @@ options:
     - 'Some valid choices include: "authenticatedRead", "bucketOwnerFullControl",
       "bucketOwnerRead", "private", "projectPrivate", "publicRead"'
     required: false
+    type: str
 extends_documentation_fragment: gcp
 '''
 
diff --git a/lib/ansible/modules/cloud/google/gcp_storage_bucket_access_control.py b/lib/ansible/modules/cloud/google/gcp_storage_bucket_access_control.py
index a91068cb32b..4dac53b42db 100644
--- a/lib/ansible/modules/cloud/google/gcp_storage_bucket_access_control.py
+++ b/lib/ansible/modules/cloud/google/gcp_storage_bucket_access_control.py
@@ -65,6 +65,7 @@ options:
       to a gcp_storage_bucket task and then set this bucket field to "{{ name-of-resource
       }}"'
     required: true
+    type: dict
   entity:
     description:
     - 'The entity holding the permission, in one of the following forms: user-userId
@@ -74,29 +75,35 @@ options:
     - To refer to all members of the Google Apps for Business domain example.com,
       the entity would be domain-example.com.
     required: true
+    type: str
   entity_id:
     description:
     - The ID for the entity.
     required: false
+    type: str
   project_team:
     description:
     - The project team associated with the entity.
     required: false
+    type: dict
     suboptions:
       project_number:
         description:
         - The project team associated with the entity.
         required: false
+        type: str
       team:
         description:
         - The team.
         - 'Some valid choices include: "editors", "owners", "viewers"'
         required: false
+        type: str
   role:
     description:
     - The access permission for the entity.
     - 'Some valid choices include: "OWNER", "READER", "WRITER"'
     required: false
+    type: str
 extends_documentation_fragment: gcp
 '''
 
diff --git a/lib/ansible/modules/cloud/google/gcp_storage_object.py b/lib/ansible/modules/cloud/google/gcp_storage_object.py
index f373c6b7af5..99b5c464f40 100644
--- a/lib/ansible/modules/cloud/google/gcp_storage_object.py
+++ b/lib/ansible/modules/cloud/google/gcp_storage_object.py
@@ -52,6 +52,7 @@ options:
     - Upload or download from the bucket.
     - 'Some valid choices include: "download", "upload"'
     required: false
+    type: str
   overwrite:
     description:
     - "'Overwrite the file on the bucket/local machine. If overwrite is false and
@@ -62,14 +63,17 @@ options:
     description:
     - Source location of file (may be local machine or cloud depending on action).
     required: false
+    type: path
   dest:
     description:
     - Destination location of file (may be local machine or cloud depending on action).
     required: false
+    type: path
   bucket:
     description:
     - The name of the bucket.
     required: false
+    type: str
 extends_documentation_fragment: gcp
 '''
 
diff --git a/lib/ansible/modules/cloud/google/gcp_tpu_node.py b/lib/ansible/modules/cloud/google/gcp_tpu_node.py
index d0b815c27f5..afd00aceb0d 100644
--- a/lib/ansible/modules/cloud/google/gcp_tpu_node.py
+++ b/lib/ansible/modules/cloud/google/gcp_tpu_node.py
@@ -51,24 +51,29 @@ options:
     description:
     - The immutable name of the TPU.
     required: true
+    type: str
   description:
     description:
     - The user-supplied description of the TPU. Maximum of 512 characters.
     required: false
+    type: str
   accelerator_type:
     description:
     - The type of hardware accelerators associated with this node.
     required: true
+    type: str
   tensorflow_version:
     description:
     - The version of Tensorflow running in the Node.
     required: true
+    type: str
   network:
     description:
     - The name of a network to peer the TPU node to. It must be a preexisting Compute
       Engine network inside of the project on which this API has been activated. If
       none is provided, "default" will be used.
     required: false
+    type: str
   cidr_block:
     description:
     - The CIDR block that the TPU node will use when selecting an IP address. This
@@ -80,10 +85,12 @@ options:
       network, or the provided network is peered with another network that is using
       that CIDR block.
     required: true
+    type: str
   scheduling_config:
     description:
     - Sets the scheduling options for this TPU instance.
     required: false
+    type: dict
     suboptions:
       preemptible:
         description:
@@ -95,10 +102,12 @@ options:
     description:
     - Resource labels to represent user provided metadata.
     required: false
+    type: dict
   zone:
     description:
     - The GCP location for the TPU.
     required: true
+    type: str
 extends_documentation_fragment: gcp
 notes:
 - 'API Reference: U(https://cloud.google.com/tpu/docs/reference/rest/)'
@@ -402,7 +411,7 @@ def wait_for_operation(module, response):
         return {}
     status = navigate_hash(op_result, ['done'])
     wait_done = wait_for_completion(status, op_result, module)
-    raise_if_errors(op_result, ['error'], module)
+    raise_if_errors(wait_done, ['error'], module)
     return navigate_hash(wait_done, ['response'])
 
 
diff --git a/lib/ansible/modules/cloud/google/gcp_tpu_node_facts.py b/lib/ansible/modules/cloud/google/gcp_tpu_node_facts.py
index f11e6ae1db1..ab4bd8c9c47 100644
--- a/lib/ansible/modules/cloud/google/gcp_tpu_node_facts.py
+++ b/lib/ansible/modules/cloud/google/gcp_tpu_node_facts.py
@@ -44,6 +44,7 @@ options:
     description:
     - The GCP location for the TPU.
     required: true
+    type: str
 extends_documentation_fragment: gcp
 '''
 
diff --git a/test/integration/targets/gcp_spanner_database/tasks/main.yml b/test/integration/targets/gcp_spanner_database/tasks/main.yml
index 25b399d72c5..693c6d59d43 100644
--- a/test/integration/targets/gcp_spanner_database/tasks/main.yml
+++ b/test/integration/targets/gcp_spanner_database/tasks/main.yml
@@ -60,7 +60,7 @@
 - name: verify that command succeeded
   assert:
     that:
-      - "'webstore' in \"{{ results['resources'] | map(attribute='name') | list }}\""
+      - results['resources'] | map(attribute='name') | select("match", ".*webstore.*") | list | length == 1
 # ----------------------------------------------------------------------------
 - name: create a database that already exists
   gcp_spanner_database:
@@ -101,7 +101,7 @@
 - name: verify that command succeeded
   assert:
     that:
-      - "'webstore' not in \"{{ results['resources'] | map(attribute='name') | list }}\""
+      - results['resources'] | map(attribute='name') | select("match", ".*webstore.*") | list | length == 0
 # ----------------------------------------------------------------------------
 - name: delete a database that does not exist
   gcp_spanner_database:
diff --git a/test/integration/targets/gcp_spanner_instance/tasks/main.yml b/test/integration/targets/gcp_spanner_instance/tasks/main.yml
index 41b1d78bee8..0dd460fa5f3 100644
--- a/test/integration/targets/gcp_spanner_instance/tasks/main.yml
+++ b/test/integration/targets/gcp_spanner_instance/tasks/main.yml
@@ -54,7 +54,7 @@
 - name: verify that command succeeded
   assert:
     that:
-      - "\"{{resource_name}}\" in \"{{ results['resources'] | map(attribute='name') | list }}\""
+      - results['resources'] | map(attribute='name') | select("match", ".*testinstance.*") | list | length == 1
 # ----------------------------------------------------------------------------
 - name: create a instance that already exists
   gcp_spanner_instance:
@@ -102,7 +102,7 @@
 - name: verify that command succeeded
   assert:
     that:
-      - "\"{{resource_name}}\" not in \"{{ results['resources'] | map(attribute='name') | list }}\""
+      - results['resources'] | map(attribute='name') | select("match", ".*testinstance.*") | list | length == 0
 # ----------------------------------------------------------------------------
 - name: delete a instance that does not exist
   gcp_spanner_instance:
diff --git a/test/sanity/validate-modules/ignore.txt b/test/sanity/validate-modules/ignore.txt
index 367fda7b211..41f5db8785e 100644
--- a/test/sanity/validate-modules/ignore.txt
+++ b/test/sanity/validate-modules/ignore.txt
@@ -743,13 +743,10 @@ lib/ansible/modules/cloud/google/gcp_redis_instance_facts.py E337
 lib/ansible/modules/cloud/google/gcp_redis_instance.py E337
 lib/ansible/modules/cloud/google/gcp_resourcemanager_project.py E337
 lib/ansible/modules/cloud/google/gcp_sourcerepo_repository.py E337
-lib/ansible/modules/cloud/google/gcp_spanner_database_facts.py E337
 lib/ansible/modules/cloud/google/gcp_spanner_database.py E337
 lib/ansible/modules/cloud/google/gcp_spanner_instance.py E337
-lib/ansible/modules/cloud/google/gcp_sql_database_facts.py E337
 lib/ansible/modules/cloud/google/gcp_sql_database.py E337
 lib/ansible/modules/cloud/google/gcp_sql_instance.py E337
-lib/ansible/modules/cloud/google/gcp_sql_user_facts.py E337
 lib/ansible/modules/cloud/google/gcp_sql_user.py E337
 lib/ansible/modules/cloud/google/gcp_storage_bucket_access_control.py E337
 lib/ansible/modules/cloud/google/gcp_storage_bucket.py E337
@@ -758,7 +755,6 @@ lib/ansible/modules/cloud/google/_gcp_target_proxy.py E322
 lib/ansible/modules/cloud/google/_gcp_target_proxy.py E326
 lib/ansible/modules/cloud/google/_gcp_target_proxy.py E337
 lib/ansible/modules/cloud/google/_gcp_target_proxy.py E338
-lib/ansible/modules/cloud/google/gcp_tpu_node_facts.py E337
 lib/ansible/modules/cloud/google/gcp_tpu_node.py E337
 lib/ansible/modules/cloud/google/gcpubsub_facts.py E322
 lib/ansible/modules/cloud/google/gcpubsub_facts.py E324