From 87c6d4be57d1a4c02e5f4786a3622258b5466e40 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 15 Aug 2018 11:27:41 -0700 Subject: [PATCH] Bug fixes for gcp_storage_bucket (#42835) --- .../cloud/google/gcp_storage_bucket.py | 644 +++++++++++------- .../targets/gcp_storage_bucket/tasks/main.yml | 20 +- 2 files changed, 411 insertions(+), 253 deletions(-) diff --git a/lib/ansible/modules/cloud/google/gcp_storage_bucket.py b/lib/ansible/modules/cloud/google/gcp_storage_bucket.py index c2601fa43a2..0f47994284c 100644 --- a/lib/ansible/modules/cloud/google/gcp_storage_bucket.py +++ b/lib/ansible/modules/cloud/google/gcp_storage_bucket.py @@ -32,12 +32,11 @@ DOCUMENTATION = ''' --- module: gcp_storage_bucket description: - - The Buckets resource represents a bucket in Google Cloud Storage. There is - a single global namespace shared by all buckets. For more information, see - Bucket Name Requirements. - - Buckets contain objects which can be accessed by their own methods. In - addition to the acl property, buckets contain bucketAccessControls, for - use in fine-grained manipulation of an existing bucket's access controls. + - The Buckets resource represents a bucket in Google Cloud Storage. There is a single + global namespace shared by all buckets. For more information, see Bucket Name Requirements. + - Buckets contain objects which can be accessed by their own methods. In addition + to the acl property, buckets contain bucketAccessControls, for use in fine-grained + manipulation of an existing bucket's access controls. - A bucket is always owned by the project team owners group. short_description: Creates a GCP Bucket version_added: 2.6 @@ -50,7 +49,6 @@ options: state: description: - Whether the given object should exist in GCP - required: true choices: ['present', 'absent'] default: 'present' acl: @@ -60,7 +58,7 @@ options: suboptions: bucket: description: - - A reference to Bucket resource. + - The name of the bucket. required: true domain: description: @@ -72,16 +70,13 @@ options: required: false entity: description: - - | - The entity holding the permission, in one of the following - forms: user-userId user-email group-groupId - group-email domain-domain project-team-projectId - allUsers allAuthenticatedUsers Examples: The user - liz@example.com would be user-liz@example.com. - - The group example@googlegroups.com would be - group-example@googlegroups.com. - - To refer to all members of the Google Apps for Business domain - example.com, the entity would be domain-example.com. + - 'The entity holding the permission, in one of the following forms: user-userId + user-email group-groupId group-email domain-domain project-team-projectId allUsers + allAuthenticatedUsers Examples: The user liz@example.com would be + user-liz@example.com.' + - The group example@googlegroups.com would be group-example@googlegroups.com. + - To refer to all members of the Google Apps for Business domain example.com, the + entity would be domain-example.com. required: true entity_id: description: @@ -117,41 +112,97 @@ options: suboptions: max_age_seconds: description: - - The value, in seconds, to return in the Access-Control-Max-Age - header used in preflight responses. + - The value, in seconds, to return in the Access-Control-Max-Age header used in preflight + responses. required: false 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". + - '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 origin: description: - The list of Origins eligible to receive CORS response headers. - - | - Note: "*" is permitted in the list of origins, and means "any - Origin". + - 'Note: "*" is permitted in the list of origins, and means "any Origin".' required: false 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. + - The list of HTTP headers other than the simple response headers to give permission + for the user-agent to share across domains. required: false + default_object_acl: + description: + - Default access controls to apply to new objects when no ACL is provided. + required: false + version_added: 2.7 + suboptions: + bucket: + description: + - The name of the bucket. + required: true + domain: + description: + - The domain associated with the entity. + required: false + email: + description: + - The email address associated with the entity. + required: false + entity: + description: + - 'The entity holding the permission, in one of the following forms: user-userId + user-email group-groupId group-email domain-domain project-team-projectId allUsers + allAuthenticatedUsers Examples: The user liz@example.com would be + user-liz@example.com.' + - The group example@googlegroups.com would be group-example@googlegroups.com. + - To refer to all members of the Google Apps for Business domain example.com, the + entity would be domain-example.com. + required: true + entity_id: + description: + - The ID for the entity. + required: false + generation: + description: + - The content generation of the object, if applied to an object. + required: false + id: + description: + - The ID of the access-control entry. + required: false + object: + description: + - The name of the object, if applied to an object. + required: false + project_team: + description: + - The project team associated with the entity. + required: false + suboptions: + project_number: + description: + - The project team associated with the entity. + required: false + team: + description: + - The team. + required: false + choices: ['editors', 'owners', 'viewers'] + role: + description: + - The access permission for the entity. + required: false + choices: ['OWNER', 'READER'] lifecycle: description: - The bucket's lifecycle configuration. - - | - See https://developers.google.com/storage/docs/lifecycle for more - information. + - See U(https://developers.google.com/storage/docs/lifecycle) for more information. required: false 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. + - 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 suboptions: action: @@ -161,13 +212,11 @@ options: suboptions: storage_class: description: - - Target storage class. Required iff the type of the - action is SetStorageClass. + - Target storage class. Required iff the type of the action is SetStorageClass. required: false type: description: - - Type of the action. Currently, only Delete and - SetStorageClass are supported. + - Type of the action. Currently, only Delete and SetStorageClass are supported. required: false choices: ['Delete', 'SetStorageClass'] condition: @@ -177,53 +226,47 @@ options: suboptions: age_days: description: - - Age of an object (in days). This condition is - satisfied when an object reaches the specified age. + - Age of an object (in days). This condition is satisfied when an object reaches the + specified age. required: false 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. + - 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 is_live: description: - - Relevant only for versioned objects. If the value is - true, this condition matches live objects; if the - value is false, it matches archived objects. + - Relevant only for versioned objects. If the value is true, this condition matches + live objects; if the value is false, it matches archived objects. required: false type: bool matches_storage_class: description: - - Objects having any of the storage classes specified by - this condition will be matched. Values include - MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, - STANDARD, and DURABLE_REDUCED_AVAILABILITY. + - Objects having any of the storage classes specified by this condition will be matched. + Values include MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, STANDARD, and DURABLE_REDUCED_AVAILABILITY. required: false 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. + - 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 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. + - 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 logging: description: - - The bucket's logging configuration, which defines the destination - bucket and optional name prefix for the current bucket's logs. + - The bucket's logging configuration, which defines the destination bucket and optional + name prefix for the current bucket's logs. required: false suboptions: log_bucket: description: - - The destination bucket where the current bucket's logs should - be placed. + - The destination bucket where the current bucket's logs should be placed. required: false log_object_prefix: description: @@ -239,8 +282,7 @@ options: required: false owner: description: - - The owner of the bucket. This is always the project team's owner - group. + - The owner of the bucket. This is always the project team's owner group. required: false suboptions: entity: @@ -253,13 +295,11 @@ options: required: false storage_class: description: - - The bucket's default storage class, used whenever no storageClass - is specified for a newly-created object. This defines how objects - in the bucket are stored and determines the SLA and the cost of - storage. - - Values include MULTI_REGIONAL, REGIONAL, STANDARD, NEARLINE, - COLDLINE, and DURABLE_REDUCED_AVAILABILITY. If this value is not - specified when the bucket is created, it will default to STANDARD. + - The bucket's default storage class, used whenever no storageClass is specified for + a newly-created object. This defines how objects in the bucket are stored and determines + the SLA and the cost of storage. + - Values include MULTI_REGIONAL, REGIONAL, STANDARD, NEARLINE, COLDLINE, and DURABLE_REDUCED_AVAILABILITY. + If this value is not specified when the bucket is created, it will default to STANDARD. For more information, see storage classes. required: false choices: ['MULTI_REGIONAL', 'REGIONAL', 'STANDARD', 'NEARLINE', 'COLDLINE', 'DURABLE_REDUCED_AVAILABILITY'] @@ -275,24 +315,21 @@ options: type: bool website: description: - - The bucket's website configuration, controlling how the service - behaves when accessing bucket contents as a web site. See the - Static Website Examples for more information. + - The bucket's website configuration, controlling how the service behaves when accessing + bucket contents as a web site. See the Static Website Examples for more information. required: false suboptions: main_page_suffix: description: - - If the requested object path is missing, the service will - ensure the path has a trailing '/', append this suffix, and - attempt to retrieve the resulting object. This allows the - creation of index.html objects to represent directory pages. + - If the requested object path is missing, the service will ensure the path has a + trailing '/', append this suffix, and attempt to retrieve the resulting object. + This allows the creation of index.html objects to represent directory pages. required: false 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. + - 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 project: description: @@ -300,25 +337,17 @@ options: required: false predefined_default_object_acl: description: - - Apply a predefined set of default object access controls to this - bucket. - - | - Acceptable values are: - "authenticatedRead": Object owner gets - OWNER access, and allAuthenticatedUsers get READER access. - - | - - "bucketOwnerFullControl": Object owner gets OWNER access, and - project team owners get OWNER access. - - | - - "bucketOwnerRead": Object owner gets OWNER access, and project - team owners get READER access. - - | - - "private": Object owner gets OWNER access. - - | - - "projectPrivate": Object owner gets OWNER access, and project - team members get access according to their roles. - - | - - "publicRead": Object owner gets OWNER access, and allUsers get - READER access. + - Apply a predefined set of default object access controls to this bucket. + - 'Acceptable values are: - "authenticatedRead": Object owner gets OWNER access, + and allAuthenticatedUsers get READER access.' + - '- "bucketOwnerFullControl": Object owner gets OWNER access, and project team + owners get OWNER access.' + - '- "bucketOwnerRead": Object owner gets OWNER access, and project team owners + get READER access.' + - '- "private": Object owner gets OWNER access.' + - '- "projectPrivate": Object owner gets OWNER access, and project team members + get access according to their roles.' + - '- "publicRead": Object owner gets OWNER access, and allUsers get READER access.' required: false choices: ['authenticatedRead', 'bucketOwnerFullControl', 'bucketOwnerRead', 'private', 'projectPrivate', 'publicRead'] extends_documentation_fragment: gcp @@ -327,12 +356,10 @@ extends_documentation_fragment: gcp EXAMPLES = ''' - name: create a bucket gcp_storage_bucket: - name: 'ansible-storage-module' - project: testProject - auth_kind: service_account - service_account_file: /tmp/auth.pem - scopes: - - https://www.googleapis.com/auth/devstorage.full_control + name: ansible-storage-module + project: "test_project" + auth_kind: "service_account" + service_account_file: "/tmp/auth.pem" state: present ''' @@ -345,7 +372,7 @@ RETURN = ''' contains: bucket: description: - - A reference to Bucket resource. + - The name of the bucket. returned: success type: dict domain: @@ -360,16 +387,13 @@ RETURN = ''' type: str entity: description: - - | - The entity holding the permission, in one of the following - forms: user-userId user-email group-groupId - group-email domain-domain project-team-projectId - allUsers allAuthenticatedUsers Examples: The user - liz@example.com would be user-liz@example.com. - - The group example@googlegroups.com would be - group-example@googlegroups.com. - - To refer to all members of the Google Apps for Business domain - example.com, the entity would be domain-example.com. + - 'The entity holding the permission, in one of the following forms: user-userId + user-email group-groupId group-email domain-domain project-team-projectId allUsers + allAuthenticatedUsers Examples: The user liz@example.com would be + user-liz@example.com.' + - The group example@googlegroups.com would be group-example@googlegroups.com. + - To refer to all members of the Google Apps for Business domain example.com, the + entity would be domain-example.com. returned: success type: str entity_id: @@ -411,52 +435,117 @@ RETURN = ''' contains: max_age_seconds: description: - - The value, in seconds, to return in the Access-Control-Max-Age - header used in preflight responses. + - The value, in seconds, to return in the Access-Control-Max-Age header used in preflight + responses. returned: success 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". + - '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".' returned: success 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". + - 'Note: "*" is permitted in the list of origins, and means "any Origin".' returned: success 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. + - The list of HTTP headers other than the simple response headers to give permission + for the user-agent to share across domains. returned: success type: list + default_object_acl: + description: + - Default access controls to apply to new objects when no ACL is provided. + returned: success + type: complex + contains: + bucket: + description: + - The name of the bucket. + returned: success + type: dict + domain: + description: + - The domain associated with the entity. + returned: success + type: str + email: + description: + - The email address associated with the entity. + returned: success + type: str + entity: + description: + - 'The entity holding the permission, in one of the following forms: user-userId + user-email group-groupId group-email domain-domain project-team-projectId allUsers + allAuthenticatedUsers Examples: The user liz@example.com would be + user-liz@example.com.' + - The group example@googlegroups.com would be group-example@googlegroups.com. + - To refer to all members of the Google Apps for Business domain example.com, the + entity would be domain-example.com. + returned: success + type: str + entity_id: + description: + - The ID for the entity. + returned: success + type: str + generation: + description: + - The content generation of the object, if applied to an object. + returned: success + type: int + id: + description: + - The ID of the access-control entry. + returned: success + type: str + object: + description: + - The name of the object, if applied to an object. + returned: success + type: str + project_team: + description: + - The project team associated with the entity. + returned: success + type: complex + contains: + project_number: + description: + - The project team associated with the entity. + returned: success + type: str + team: + description: + - The team. + returned: success + type: str + role: + description: + - The access permission for the entity. + returned: success + type: str id: description: - - The ID of the bucket. For buckets, the id and name properities are - the same. + - The ID of the bucket. For buckets, the id and name properities are the same. returned: success type: str lifecycle: description: - The bucket's lifecycle configuration. - - | - See https://developers.google.com/storage/docs/lifecycle for more - information. + - See U(https://developers.google.com/storage/docs/lifecycle) for more information. returned: success type: complex contains: 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. + - A lifecycle management rule, which is made of an action to take and the condition(s) + under which the action will be taken. returned: success type: complex contains: @@ -468,14 +557,12 @@ RETURN = ''' contains: storage_class: description: - - Target storage class. Required iff the type of the - action is SetStorageClass. + - Target storage class. Required iff the type of the action is SetStorageClass. returned: success type: str type: description: - - Type of the action. Currently, only Delete and - SetStorageClass are supported. + - Type of the action. Currently, only Delete and SetStorageClass are supported. returned: success type: str condition: @@ -486,59 +573,53 @@ RETURN = ''' contains: age_days: description: - - Age of an object (in days). This condition is - satisfied when an object reaches the specified age. + - Age of an object (in days). This condition is satisfied when an object reaches the + specified age. returned: success 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. + - 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. returned: success type: str is_live: description: - - Relevant only for versioned objects. If the value is - true, this condition matches live objects; if the - value is false, it matches archived objects. + - Relevant only for versioned objects. If the value is true, this condition matches + live objects; if the value is false, it matches archived objects. returned: success type: bool matches_storage_class: description: - - Objects having any of the storage classes specified by - this condition will be matched. Values include - MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, - STANDARD, and DURABLE_REDUCED_AVAILABILITY. + - Objects having any of the storage classes specified by this condition will be matched. + Values include MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, STANDARD, and DURABLE_REDUCED_AVAILABILITY. returned: success 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. + - 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. returned: success 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. + - 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. returned: success type: str logging: description: - - The bucket's logging configuration, which defines the destination - bucket and optional name prefix for the current bucket's logs. + - The bucket's logging configuration, which defines the destination bucket and optional + name prefix for the current bucket's logs. returned: success type: complex contains: log_bucket: description: - - The destination bucket where the current bucket's logs should - be placed. + - The destination bucket where the current bucket's logs should be placed. returned: success type: str log_object_prefix: @@ -558,8 +639,7 @@ RETURN = ''' type: str owner: description: - - The owner of the bucket. This is always the project team's owner - group. + - The owner of the bucket. This is always the project team's owner group. returned: success type: complex contains: @@ -580,13 +660,11 @@ RETURN = ''' type: int storage_class: description: - - The bucket's default storage class, used whenever no storageClass - is specified for a newly-created object. This defines how objects - in the bucket are stored and determines the SLA and the cost of - storage. - - Values include MULTI_REGIONAL, REGIONAL, STANDARD, NEARLINE, - COLDLINE, and DURABLE_REDUCED_AVAILABILITY. If this value is not - specified when the bucket is created, it will default to STANDARD. + - The bucket's default storage class, used whenever no storageClass is specified for + a newly-created object. This defines how objects in the bucket are stored and determines + the SLA and the cost of storage. + - Values include MULTI_REGIONAL, REGIONAL, STANDARD, NEARLINE, COLDLINE, and DURABLE_REDUCED_AVAILABILITY. + If this value is not specified when the bucket is created, it will default to STANDARD. For more information, see storage classes. returned: success type: str @@ -613,26 +691,23 @@ RETURN = ''' type: bool website: description: - - The bucket's website configuration, controlling how the service - behaves when accessing bucket contents as a web site. See the - Static Website Examples for more information. + - The bucket's website configuration, controlling how the service behaves when accessing + bucket contents as a web site. See the Static Website Examples for more information. returned: success type: complex contains: main_page_suffix: description: - - If the requested object path is missing, the service will - ensure the path has a trailing '/', append this suffix, and - attempt to retrieve the resulting object. This allows the - creation of index.html objects to represent directory pages. + - If the requested object path is missing, the service will ensure the path has a + trailing '/', append this suffix, and attempt to retrieve the resulting object. + This allows the creation of index.html objects to represent directory pages. returned: success 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. + - 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. returned: success type: str project: @@ -642,25 +717,17 @@ RETURN = ''' type: str predefined_default_object_acl: description: - - Apply a predefined set of default object access controls to this - bucket. - - | - Acceptable values are: - "authenticatedRead": Object owner gets - OWNER access, and allAuthenticatedUsers get READER access. - - | - - "bucketOwnerFullControl": Object owner gets OWNER access, and - project team owners get OWNER access. - - | - - "bucketOwnerRead": Object owner gets OWNER access, and project - team owners get READER access. - - | - - "private": Object owner gets OWNER access. - - | - - "projectPrivate": Object owner gets OWNER access, and project - team members get access according to their roles. - - | - - "publicRead": Object owner gets OWNER access, and allUsers get - READER access. + - Apply a predefined set of default object access controls to this bucket. + - 'Acceptable values are: - "authenticatedRead": Object owner gets OWNER access, + and allAuthenticatedUsers get READER access.' + - '- "bucketOwnerFullControl": Object owner gets OWNER access, and project team + owners get OWNER access.' + - '- "bucketOwnerRead": Object owner gets OWNER access, and project team owners + get READER access.' + - '- "private": Object owner gets OWNER access.' + - '- "projectPrivate": Object owner gets OWNER access, and project team members + get access according to their roles.' + - '- "publicRead": Object owner gets OWNER access, and allUsers get READER access.' returned: success type: str ''' @@ -702,6 +769,21 @@ def main(): origin=dict(type='list', elements='str'), response_header=dict(type='list', elements='str') )), + default_object_acl=dict(type='list', elements='dict', options=dict( + bucket=dict(required=True, type='dict'), + domain=dict(type='str'), + email=dict(type='str'), + entity=dict(required=True, type='str'), + entity_id=dict(type='str'), + generation=dict(type='int'), + id=dict(type='str'), + object=dict(type='str'), + project_team=dict(type='dict', options=dict( + project_number=dict(type='str'), + team=dict(type='str', choices=['editors', 'owners', 'viewers']) + )), + role=dict(type='str', choices=['OWNER', 'READER']) + )), lifecycle=dict(type='dict', options=dict( rule=dict(type='list', elements='dict', options=dict( action=dict(type='dict', options=dict( @@ -746,6 +828,9 @@ def main(): ) ) + if not module.params['scopes']: + module.params['scopes'] = ['https://www.googleapis.com/auth/devstorage.full_control'] + state = module.params['state'] kind = 'storage#bucket' @@ -793,17 +878,18 @@ def resource_to_request(module): u'kind': 'storage#bucket', u'project': module.params.get('project'), u'predefinedDefaultObjectAcl': module.params.get('predefined_default_object_acl'), - u'acl': BucketAclArray(module.params.get('acl', [])).to_request(), - u'cors': BucketCorsArray(module.params.get('cors', [])).to_request(), - u'lifecycle': BucketLifecycle(module.params.get('lifecycle', {})).to_request(), + u'acl': BucketAclArray(module.params.get('acl', []), module).to_request(), + u'cors': BucketCorsArray(module.params.get('cors', []), module).to_request(), + u'defaultObjectAcl': BucketDefaultObjectAclArray(module.params.get('default_object_acl', []), module).to_request(), + u'lifecycle': BucketLifecycle(module.params.get('lifecycle', {}), module).to_request(), u'location': module.params.get('location'), - u'logging': BucketLogging(module.params.get('logging', {})).to_request(), + u'logging': BucketLogging(module.params.get('logging', {}), module).to_request(), u'metageneration': module.params.get('metageneration'), u'name': module.params.get('name'), - u'owner': BucketOwner(module.params.get('owner', {})).to_request(), + u'owner': BucketOwner(module.params.get('owner', {}), module).to_request(), u'storageClass': module.params.get('storage_class'), - u'versioning': BucketVersioning(module.params.get('versioning', {})).to_request(), - u'website': BucketWebsite(module.params.get('website', {})).to_request() + u'versioning': BucketVersioning(module.params.get('versioning', {}), module).to_request(), + u'website': BucketWebsite(module.params.get('website', {}), module).to_request() } return_vals = {} for k, v in request.items(): @@ -871,26 +957,28 @@ def is_different(module, response): # This is for doing comparisons with Ansible's current parameters. def response_to_hash(module, response): return { - u'acl': BucketAclArray(response.get(u'acl', [])).from_response(), - u'cors': BucketCorsArray(response.get(u'cors', [])).from_response(), + u'acl': BucketAclArray(response.get(u'acl', []), module).from_response(), + u'cors': BucketCorsArray(response.get(u'cors', []), module).from_response(), + u'defaultObjectAcl': BucketDefaultObjectAclArray(module.params.get('default_object_acl', []), module).to_request(), u'id': response.get(u'id'), - u'lifecycle': BucketLifecycle(response.get(u'lifecycle', {})).from_response(), + u'lifecycle': BucketLifecycle(response.get(u'lifecycle', {}), module).from_response(), u'location': response.get(u'location'), - u'logging': BucketLogging(response.get(u'logging', {})).from_response(), + u'logging': BucketLogging(response.get(u'logging', {}), module).from_response(), u'metageneration': response.get(u'metageneration'), u'name': response.get(u'name'), - u'owner': BucketOwner(response.get(u'owner', {})).from_response(), + u'owner': BucketOwner(response.get(u'owner', {}), module).from_response(), u'projectNumber': response.get(u'projectNumber'), u'storageClass': response.get(u'storageClass'), u'timeCreated': response.get(u'timeCreated'), u'updated': response.get(u'updated'), - u'versioning': BucketVersioning(response.get(u'versioning', {})).from_response(), - u'website': BucketWebsite(response.get(u'website', {})).from_response() + u'versioning': BucketVersioning(response.get(u'versioning', {}), module).from_response(), + u'website': BucketWebsite(response.get(u'website', {}), module).from_response() } class BucketAclArray(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -916,7 +1004,7 @@ class BucketAclArray(object): u'entity': item.get('entity'), u'entityId': item.get('entity_id'), u'id': item.get('id'), - u'projectTeam': BucketProjectTeam(item.get('project_team', {})).to_request(), + u'projectTeam': BucketProjectTeam(item.get('project_team', {}), self.module).to_request(), u'role': item.get('role') }) @@ -928,13 +1016,14 @@ class BucketAclArray(object): u'entity': item.get(u'entity'), u'entityId': item.get(u'entityId'), u'id': item.get(u'id'), - u'projectTeam': BucketProjectTeam(item.get(u'projectTeam', {})).from_response(), + u'projectTeam': BucketProjectTeam(item.get(u'projectTeam', {}), self.module).from_response(), u'role': item.get(u'role') }) class BucketProjectTeam(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -954,7 +1043,8 @@ class BucketProjectTeam(object): class BucketCorsArray(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -989,8 +1079,79 @@ class BucketCorsArray(object): }) +class BucketDefaultObjectAclArray(object): + def __init__(self, request, module): + self.module = module + if request: + self.request = request + else: + self.request = [] + + def to_request(self): + items = [] + for item in self.request: + items.append(self._request_for_item(item)) + return items + + def from_response(self): + items = [] + for item in self.request: + items.append(self._response_from_item(item)) + return items + + def _request_for_item(self, item): + return remove_nones_from_dict({ + u'bucket': replace_resource_dict(item.get(u'bucket', {}), 'name'), + u'domain': item.get('domain'), + u'email': item.get('email'), + u'entity': item.get('entity'), + u'entityId': item.get('entity_id'), + u'generation': item.get('generation'), + u'id': item.get('id'), + u'object': item.get('object'), + u'projectTeam': BucketProjectTeam(item.get('project_team', {}), self.module).to_request(), + u'role': item.get('role') + }) + + def _response_from_item(self, item): + return remove_nones_from_dict({ + u'bucket': item.get(u'bucket'), + u'domain': item.get(u'domain'), + u'email': item.get(u'email'), + u'entity': item.get(u'entity'), + u'entityId': item.get(u'entityId'), + u'generation': item.get(u'generation'), + u'id': item.get(u'id'), + u'object': item.get(u'object'), + u'projectTeam': BucketProjectTeam(item.get(u'projectTeam', {}), self.module).from_response(), + u'role': item.get(u'role') + }) + + +class BucketProjectTeam(object): + def __init__(self, request, module): + self.module = module + if request: + self.request = request + else: + self.request = {} + + def to_request(self): + return remove_nones_from_dict({ + u'projectNumber': self.request.get('project_number'), + u'team': self.request.get('team') + }) + + def from_response(self): + return remove_nones_from_dict({ + u'projectNumber': self.request.get(u'projectNumber'), + u'team': self.request.get(u'team') + }) + + class BucketLifecycle(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -998,17 +1159,18 @@ class BucketLifecycle(object): def to_request(self): return remove_nones_from_dict({ - u'rule': BucketRuleArray(self.request.get('rule', [])).to_request() + u'rule': BucketRuleArray(self.request.get('rule', []), self.module).to_request() }) def from_response(self): return remove_nones_from_dict({ - u'rule': BucketRuleArray(self.request.get(u'rule', [])).from_response() + u'rule': BucketRuleArray(self.request.get(u'rule', []), self.module).from_response() }) class BucketRuleArray(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -1028,19 +1190,20 @@ class BucketRuleArray(object): def _request_for_item(self, item): return remove_nones_from_dict({ - u'action': BucketAction(item.get('action', {})).to_request(), - u'condition': BucketCondition(item.get('condition', {})).to_request() + u'action': BucketAction(item.get('action', {}), self.module).to_request(), + u'condition': BucketCondition(item.get('condition', {}), self.module).to_request() }) def _response_from_item(self, item): return remove_nones_from_dict({ - u'action': BucketAction(item.get(u'action', {})).from_response(), - u'condition': BucketCondition(item.get(u'condition', {})).from_response() + u'action': BucketAction(item.get(u'action', {}), self.module).from_response(), + u'condition': BucketCondition(item.get(u'condition', {}), self.module).from_response() }) class BucketAction(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -1060,7 +1223,8 @@ class BucketAction(object): class BucketCondition(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -1086,7 +1250,8 @@ class BucketCondition(object): class BucketLogging(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -1106,7 +1271,8 @@ class BucketLogging(object): class BucketOwner(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -1126,7 +1292,8 @@ class BucketOwner(object): class BucketVersioning(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -1144,7 +1311,8 @@ class BucketVersioning(object): class BucketWebsite(object): - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: diff --git a/test/integration/targets/gcp_storage_bucket/tasks/main.yml b/test/integration/targets/gcp_storage_bucket/tasks/main.yml index 2becee52141..56363734e18 100644 --- a/test/integration/targets/gcp_storage_bucket/tasks/main.yml +++ b/test/integration/targets/gcp_storage_bucket/tasks/main.yml @@ -15,22 +15,18 @@ # Pre-test setup - name: delete a bucket gcp_storage_bucket: - name: 'ansible-storage-module' + name: ansible-storage-module project: "{{ gcp_project }}" auth_kind: "{{ gcp_cred_kind }}" service_account_file: "{{ gcp_cred_file }}" - scopes: - - https://www.googleapis.com/auth/devstorage.full_control state: absent #---------------------------------------------------------- - name: create a bucket gcp_storage_bucket: - name: 'ansible-storage-module' + name: ansible-storage-module project: "{{ gcp_project }}" auth_kind: "{{ gcp_cred_kind }}" service_account_file: "{{ gcp_cred_file }}" - scopes: - - https://www.googleapis.com/auth/devstorage.full_control state: present register: result - name: assert changed is true @@ -41,12 +37,10 @@ # ---------------------------------------------------------------------------- - name: create a bucket that already exists gcp_storage_bucket: - name: 'ansible-storage-module' + name: ansible-storage-module project: "{{ gcp_project }}" auth_kind: "{{ gcp_cred_kind }}" service_account_file: "{{ gcp_cred_file }}" - scopes: - - https://www.googleapis.com/auth/devstorage.full_control state: present register: result - name: assert changed is false @@ -57,12 +51,10 @@ #---------------------------------------------------------- - name: delete a bucket gcp_storage_bucket: - name: 'ansible-storage-module' + name: ansible-storage-module project: "{{ gcp_project }}" auth_kind: "{{ gcp_cred_kind }}" service_account_file: "{{ gcp_cred_file }}" - scopes: - - https://www.googleapis.com/auth/devstorage.full_control state: absent register: result - name: assert changed is true @@ -73,12 +65,10 @@ # ---------------------------------------------------------------------------- - name: delete a bucket that does not exist gcp_storage_bucket: - name: 'ansible-storage-module' + name: ansible-storage-module project: "{{ gcp_project }}" auth_kind: "{{ gcp_cred_kind }}" service_account_file: "{{ gcp_cred_file }}" - scopes: - - https://www.googleapis.com/auth/devstorage.full_control state: absent register: result - name: assert changed is false