From 990353f07f0cffe4a9bcfd8991492ecc3d7eac20 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 11 Apr 2018 23:28:48 +0000 Subject: [PATCH 01/16] Ansible: Input: true properties should use module values This involves changes to pass module around so that input:true properties can pick up on it. Change-Id: I4913accdb988e5eb051626e08b7115feaee2aebd --- provider/ansible/documentation.rb | 12 ++++-- provider/ansible/request.rb | 71 +++++++++++++++++++++---------- provider/ansible/selflink.rb | 3 +- templates/ansible/properties.erb | 6 ++- 4 files changed, 61 insertions(+), 31 deletions(-) diff --git a/provider/ansible/documentation.rb b/provider/ansible/documentation.rb index b4c33ebdf907..7c7fbc104ce4 100644 --- a/provider/ansible/documentation.rb +++ b/provider/ansible/documentation.rb @@ -20,6 +20,7 @@ module Provider module Ansible # Responsible for building out YAML documentation blocks. + # rubocop:disable Metrics/ModuleLength module Documentation # This is not a comprehensive list of unsafe characters. # Ansible's YAML linter is more forgiving than Ruby's. @@ -123,12 +124,13 @@ def nested_doc(properties, config, spaces) end # Builds out the minimal YAML block for DOCUMENTATION + # rubocop:disable Metrics/AbcSize def minimal_doc_block(prop, config, spaces) [ minimal_yaml(prop, spaces), indent([ "required: #{prop.required ? 'true' : 'false'}", - ("type: bool" if prop.is_a? Api::Type::Boolean), + ('type: bool' if prop.is_a? Api::Type::Boolean), ("aliases: [#{config['aliases'][prop.name].join(', ')}]" \ if config['aliases']&.keys&.include?(prop.name)), (if prop.is_a? Api::Type::Enum @@ -140,6 +142,7 @@ def minimal_doc_block(prop, config, spaces) ].compact, 4) ] end + # rubocop:enable Metrics/AbcSize # Builds out the minimal YAML block for RETURNS def minimal_return_block(prop, spaces) @@ -147,9 +150,9 @@ def minimal_return_block(prop, spaces) # Complex types only mentioned in reference to RETURNS YAML block # Complex types are nested objects traditionally, but arrays of nested # objects will be included to avoid linting errors. - type = 'complex' if prop.is_a?(Api::Type::NestedObject)|| \ - (prop.is_a?(Api::Type::Array) && \ - prop.item_type.is_a?(Api::Type::NestedObject)) + type = 'complex' if prop.is_a?(Api::Type::NestedObject) \ + || (prop.is_a?(Api::Type::Array) \ + && prop.item_type.is_a?(Api::Type::NestedObject)) [ minimal_yaml(prop, spaces), indent([ @@ -180,5 +183,6 @@ def autogen_notice_contrib 'https://www.github.com/GoogleCloudPlatform/magic-modules'] end end + # rubocop:enable Metrics/ModuleLength end end diff --git a/provider/ansible/request.rb b/provider/ansible/request.rb index 5e68299eaae1..b6104ec4886b 100644 --- a/provider/ansible/request.rb +++ b/provider/ansible/request.rb @@ -15,35 +15,46 @@ module Provider module Ansible # Responsible for building out the resource_to_request and # request_from_hash methods. + # rubocop:disable Metrics/ModuleLength module Request # Takes in a list of properties and outputs a python hash that takes # in a module and outputs a formatted JSON request. def request_properties(properties, indent = 4) indent_list( - properties.map { |prop| request_property(prop, 'module.params') }, + properties.map do |prop| + request_property(prop, 'module.params', 'module') + end, indent ) end def response_properties(properties, indent = 8) indent_list( - properties.map { |prop| response_property(prop, 'response') }, + properties.map do |prop| + response_property(prop, 'response', 'module') + end, indent ) end def request_properties_in_classes(properties, indent = 4, - hash_name = 'self.request') + hash_name = 'self.request', + module_name = 'self.module') indent_list( - properties.map { |prop| request_property(prop, hash_name) }, + properties.map do |prop| + request_property(prop, hash_name, module_name) + end, indent ) end def response_properties_in_classes(properties, indent = 8, - hash_name = 'self.request') + hash_name = 'self.request', + module_name = 'self.module') indent_list( - properties.map { |prop| response_property(prop, hash_name) }, + properties.map do |prop| + response_property(prop, hash_name, module_name) + end, indent ) end @@ -54,7 +65,7 @@ def properties_with_classes(properties) if p.is_a? Api::Type::NestedObject [p] + properties_with_classes(p.properties) elsif p.is_a?(Api::Type::Array) && \ - p.item_type.is_a?(Api::Type::NestedObject) + p.item_type.is_a?(Api::Type::NestedObject) [p] + properties_with_classes(p.item_type.properties) end end.compact.flatten @@ -62,52 +73,61 @@ def properties_with_classes(properties) private - def request_property(prop, hash_name) + def request_property(prop, hash_name, module_name) [ "#{unicode_string(prop.field_name)}:", - request_output(prop, hash_name).to_s + request_output(prop, hash_name, module_name).to_s ].join(' ') end - def response_property(prop, hash_name) + def response_property(prop, hash_name, module_name) [ "#{unicode_string(prop.field_name)}:", - response_output(prop, hash_name).to_s + response_output(prop, hash_name, module_name).to_s ].join(' ') end - def response_output(prop, hash_name) + # rubocop:disable Metrics/MethodLength + def response_output(prop, hash_name, module_name) + # If input true, treat like request, but use module names. + return request_output(prop, "#{module_name}.params", module_name) \ + if prop.input if prop.is_a? Api::Type::NestedObject [ "#{prop.property_class[-1]}(", "#{hash_name}.get(#{unicode_string(prop.name)}, {})", - ').from_response()' + ", #{module_name}).from_response()" ].join elsif prop.is_a?(Api::Type::Array) && \ - prop.item_type.is_a?(Api::Type::NestedObject) + prop.item_type.is_a?(Api::Type::NestedObject) [ "#{prop.property_class[-1]}(", "#{hash_name}.get(#{unicode_string(prop.name)}, [])", - ').from_response()' + ", #{module_name}).from_response()" ].join else "#{hash_name}.get(#{unicode_string(prop.name)})" end end + # rubocop:enable Metrics/MethodLength - def request_output(prop, hash_name) + # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/CyclomaticComplexity + # rubocop:disable Metrics/PerceivedComplexity + def request_output(prop, hash_name, module_name) if prop.is_a? Api::Type::NestedObject [ "#{prop.property_class[-1]}(", "#{hash_name}.get(#{quote_string(prop.out_name)}, {})", - ').to_request()' + ", #{module_name}).to_request()" ].join elsif prop.is_a?(Api::Type::Array) && \ - prop.item_type.is_a?(Api::Type::NestedObject) + prop.item_type.is_a?(Api::Type::NestedObject) [ "#{prop.property_class[-1]}(", "#{hash_name}.get(#{quote_string(prop.out_name)}, [])", - ').to_request()' + ", #{module_name}).to_request()" ].join elsif prop.is_a?(Api::Type::ResourceRef) && !prop.resource_ref.virtual prop_name = Google::StringUtils.underscore(prop.name) @@ -117,15 +137,15 @@ def request_output(prop, hash_name) "#{quote_string(prop.imports)})" ].join elsif prop.is_a?(Api::Type::ResourceRef) && \ - prop.resource_ref.virtual && prop.imports == 'selfLink' + prop.resource_ref.virtual && prop.imports == 'selfLink' func_name = Google::StringUtils.underscore("#{prop.name}_selflink") [ "#{func_name}(#{hash_name}.get(#{quote_string(prop.out_name)}),", - "module.params)" + "#{module_name}.params)" ].join(' ') elsif prop.is_a?(Api::Type::Array) && \ - prop.item_type.is_a?(Api::Type::ResourceRef) && \ - !prop.item_type.resource_ref.virtual + prop.item_type.is_a?(Api::Type::ResourceRef) && \ + !prop.item_type.resource_ref.virtual [ "replace_resource_dict(#{hash_name}", ".get(#{unicode_string(prop.name)}, []), ", @@ -135,6 +155,11 @@ def request_output(prop, hash_name) "#{hash_name}.get(#{quote_string(prop.out_name)})" end end + # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/CyclomaticComplexity + # rubocop:enable Metrics/PerceivedComplexity end + # rubocop:enable Metrics/ModuleLength end end diff --git a/provider/ansible/selflink.rb b/provider/ansible/selflink.rb index be2b5309925d..d7ffc4b55f14 100644 --- a/provider/ansible/selflink.rb +++ b/provider/ansible/selflink.rb @@ -38,7 +38,7 @@ def selflink_functions(object) virtuals = virtual_selflink_rrefs(object).map(&:resource_ref) .uniq virtuals.map do |virt| - if virt = virtuals.last + if virt == virtuals.last lines(selflink_function(virt)) else lines(selflink_function(virt), 2) @@ -62,7 +62,6 @@ def selflink_function(resource) "url = r#{url}", 'if not re.match(url, name):', # '%s' confuses Rubocop (it's Python code, not Ruby) - # rubocop:disable Style/FormatStringToken indent([ "name = #{self_link_url(resource).gsub('{name}', '%s')}", '.format(**params) % name' diff --git a/templates/ansible/properties.erb b/templates/ansible/properties.erb index ba89b373472a..7fd505bf52e4 100644 --- a/templates/ansible/properties.erb +++ b/templates/ansible/properties.erb @@ -1,7 +1,8 @@ <% properties_with_classes(object.all_user_properties).each do |prop| -%> class <%= prop.property_class[-1] -%>(object): <% if prop.is_a?(Api::Type::NestedObject) -%> - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: @@ -17,7 +18,8 @@ class <%= prop.property_class[-1] -%>(object): <%= lines(response_properties_in_classes(prop.properties, 12)) -%> }) <% elsif prop.is_a?(Api::Type::Array) && prop.item_type.is_a?(Api::Type::NestedObject) -%> - def __init__(self, request): + def __init__(self, request, module): + self.module = module if request: self.request = request else: From a4a4e9e637a155c1d2b7f4d5565bc205e54e908f Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 11 Apr 2018 20:45:30 +0000 Subject: [PATCH 02/16] Adding GcpRequestException back and fetch_wrapped_resource change Change-Id: I3c4b3a4e1083f38803744d890bf3d86963041c50 --- provider/ansible/gcp_utils.py | 8 ++++++-- templates/ansible/aliases | 1 + templates/ansible/transport.erb | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/provider/ansible/gcp_utils.py b/provider/ansible/gcp_utils.py index 0c6b62b61067..599e8fca3e08 100644 --- a/provider/ansible/gcp_utils.py +++ b/provider/ansible/gcp_utils.py @@ -37,11 +37,15 @@ def navigate_hash(source, path, default=None): return result +class GcpRequestException(Exception): + pass + + def remove_nones_from_dict(obj): new_obj = {} for key in obj: value = obj[key] - if value is not None: + if value is not None and value != {} and value != []: new_obj[key] = value return new_obj @@ -50,7 +54,7 @@ def remove_nones_from_dict(obj): def replace_resource_dict(item, value): if isinstance(item, list): items = [] - for i in items: + for i in item: items.append(replace_resource_dict(i, value)) return items else: diff --git a/templates/ansible/aliases b/templates/ansible/aliases index 26507c23cd3c..9812f019ca4b 100644 --- a/templates/ansible/aliases +++ b/templates/ansible/aliases @@ -1 +1,2 @@ cloud/gcp +unsupported diff --git a/templates/ansible/transport.erb b/templates/ansible/transport.erb index 9acb26bb8576..13021e67910a 100644 --- a/templates/ansible/transport.erb +++ b/templates/ansible/transport.erb @@ -36,6 +36,7 @@ def fetch_wrapped_resource(module, kind, wrap_kind, wrap_path): return None if result['kind'] != kind: - raise "Incorrect result: {0} (expected {1})".format(result['kind'], kind) + module.fail_json(msg="Incorrect result: {kind}".format(**result)) + return result <% end -%> From f32b6e58f830c25abebfc5d5f941d4bb6353a339 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Mon, 26 Mar 2018 22:32:19 +0000 Subject: [PATCH 03/16] Ansible: Instance This also adds support for NameValues Change-Id: Iba8513f382a7a18da983cb987829fcbd640f94c7 --- products/compute/ansible.yaml | 29 ++++++++ .../compute/examples/ansible/instance.yaml | 73 +++++++++++++++++++ products/compute/helpers/instance_metadata.py | 49 +++++++++++++ products/compute/helpers/provider_instance.py | 25 +++++++ products/compute/instance_disks.yaml | 1 - provider/ansible.rb | 3 +- 6 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 products/compute/examples/ansible/instance.yaml create mode 100644 products/compute/helpers/instance_metadata.py create mode 100644 products/compute/helpers/provider_instance.py diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index 5dbc61d22824..c627c806a015 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -78,6 +78,35 @@ objects: !ruby/object:Api::Resource::HashArray aliases: timeoutSec: - timeout_seconds + Firewall: + skip: true + Instance: + provider_helpers: + include: + - 'products/compute/helpers/provider_instance.py' + - 'products/compute/helpers/instance_metadata.py' + InstanceGroupManager: + skip: true + InstanceTemplate: + skip: true + Snapshot: + skip: true + TargetHttpsProxy: + skip: true + TargetSslProxy: + skip: true + # Ansible tasks must alter infrastructure. + # This means that virtual objects are a poor fit. + DiskType: + skip: true + License: + skip: true + MachineType: + skip: true + Region: + skip: true + Zone: + skip: true examples: !ruby/object:Api::Resource::HashArray files: !ruby/object:Provider::Config::Files copy: diff --git a/products/compute/examples/ansible/instance.yaml b/products/compute/examples/ansible/instance.yaml new file mode 100644 index 000000000000..6c68b7bce73d --- /dev/null +++ b/products/compute/examples/ansible/instance.yaml @@ -0,0 +1,73 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- !ruby/object:Provider::Ansible::Example +dependencies: + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_disk + register: disk + code: | + name: <%= dependency_name('disk', 'instance') %> + size_gb: 50 + source_image: 'projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts' + zone: us-central1-a + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_network + register: network + code: | + name: <%= dependency_name('network', 'instance') %> + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_address + register: address + code: | + name: <%= dependency_name('address', 'instance') %> + region: 'us-central1' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +task: !ruby/object:Provider::Ansible::Task + name: gcp_compute_instance + code: | + name: <%= ctx[:name] %> + machine_type: n1-standard-1 + disks: + - auto_delete: true + boot: true + source: "{{ disk }}" + metadata: + startup-script-url: 'gs:://graphite-playground/bootstrap.sh' + cost-center: '12345' + network_interfaces: + - network: "{{ network }}" + access_configs: + - name: 'External NAT' + nat_ip: "{{ address }}" + type: 'ONE_TO_ONE_NAT' + zone: 'us-central1-a' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +verifier: !ruby/object:Provider::Ansible::Verifier + command: | + gcloud compute instances describe + --project="{{ gcp_project }}" + --zone="us-central1-a" + "{{ resource_name }}" + failure: !ruby/object:Provider::Ansible::ComputeFailureCondition + region: zones/us-central1-a + type: instances diff --git a/products/compute/helpers/instance_metadata.py b/products/compute/helpers/instance_metadata.py new file mode 100644 index 000000000000..4e9e63250492 --- /dev/null +++ b/products/compute/helpers/instance_metadata.py @@ -0,0 +1,49 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Mask the fact healthChecks array is actually a single object of type +# HttpHealthCheck. + +# TODO(alexstephen): Implement updating metadata on exsiting resources. + +# Expose instance 'metadata' as a simple name/value pair hash. However the API +# defines metadata as a NestedObject with the following layout: +# +# metadata { +# fingerprint: 'hash-of-last-metadata' +# items: [ +# { +# key: 'metadata1-key' +# value: 'metadata1-value' +# }, +# ... +# ] +# } +# +def metadata_encoder(metadata): + metadata_new = [] + for key in metadata: + value = metadata[key] + metadata_new.append({key: value}) + return { + 'items': metadata_new + } + + +# Map metadata.items[]{key:,value:} => metadata[key]=value +def metadata_decoder(metadata): + items = {} + if 'items' in metadata: + metadata_items = metadata['items'] + for item in metadata_items: + items[item.keys()[0]] = item[item.keys()[0]] + return items diff --git a/products/compute/helpers/provider_instance.py b/products/compute/helpers/provider_instance.py new file mode 100644 index 000000000000..ad32d240a6fe --- /dev/null +++ b/products/compute/helpers/provider_instance.py @@ -0,0 +1,25 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Mask the fact healthChecks array is actually a single object of type +# HttpHealthCheck. + +def encode_request(request, module): + if 'metadata' in request: + request['metadata'] = metadata_encoder(request['metadata']) + return request + + +def decode_response(response, module): + if 'metadata' in response: + response['metadata'] = metadata_decoder(response['metadata']) + return response diff --git a/products/compute/instance_disks.yaml b/products/compute/instance_disks.yaml index 1290aa2db57f..1b579a9eba1e 100644 --- a/products/compute/instance_disks.yaml +++ b/products/compute/instance_disks.yaml @@ -85,7 +85,6 @@ parameters to create boot disks or local SSDs attached to the new instance. input: true - required: true properties: - !ruby/object:Api::Type::String name: 'diskName' diff --git a/provider/ansible.rb b/provider/ansible.rb index 1d4d1100ddc6..bb446619d93c 100644 --- a/provider/ansible.rb +++ b/provider/ansible.rb @@ -56,7 +56,8 @@ class Core < Provider::Core 'Api::Type::NestedObject' => 'dict', 'Api::Type::Array' => 'list', 'Api::Type::Boolean' => 'bool', - 'Api::Type::Integer' => 'int' + 'Api::Type::Integer' => 'int', + 'Api::Type::NameValues' => 'dict' }.freeze include Provider::Ansible::Documentation From 01e3f15118a4660bbe57cfac94b48604c715ccea Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Mon, 26 Mar 2018 19:46:18 +0000 Subject: [PATCH 04/16] Ansible: Compute Instance Template Change-Id: Id301f6521c61306f7534f9fb580821a4e4a0184c --- products/compute/ansible.yaml | 7 ++- .../examples/ansible/instance_group.yaml | 1 - .../examples/ansible/instance_template.yaml | 59 +++++++++++++++++++ .../helpers/provider_instance_template.py | 27 +++++++++ 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 products/compute/examples/ansible/instance_template.yaml create mode 100644 products/compute/helpers/provider_instance_template.py diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index c627c806a015..7e0c242a2086 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -61,6 +61,11 @@ objects: !ruby/object:Api::Resource::HashArray aliases: timeoutSec: - timeout_seconds + InstanceTemplate: + provider_helpers: + include: + - 'products/compute/helpers/provider_instance.py' + - 'products/compute/helpers/instance_metadata.py' HttpHealthCheck: aliases: timeoutSec: @@ -87,8 +92,6 @@ objects: !ruby/object:Api::Resource::HashArray - 'products/compute/helpers/instance_metadata.py' InstanceGroupManager: skip: true - InstanceTemplate: - skip: true Snapshot: skip: true TargetHttpsProxy: diff --git a/products/compute/examples/ansible/instance_group.yaml b/products/compute/examples/ansible/instance_group.yaml index 305a7e46929c..a466b69dd7b0 100644 --- a/products/compute/examples/ansible/instance_group.yaml +++ b/products/compute/examples/ansible/instance_group.yaml @@ -20,7 +20,6 @@ dependencies: project: <%= ctx[:project] %> auth_kind: <%= ctx[:auth_kind] %> service_account_file: <%= ctx[:service_account_file] %> - task: !ruby/object:Provider::Ansible::Task name: gcp_compute_instance_group code: | diff --git a/products/compute/examples/ansible/instance_template.yaml b/products/compute/examples/ansible/instance_template.yaml new file mode 100644 index 000000000000..07ced2cafde8 --- /dev/null +++ b/products/compute/examples/ansible/instance_template.yaml @@ -0,0 +1,59 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- !ruby/object:Provider::Ansible::Example +dependencies: + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_network + register: network + code: | + name: <%= dependency_name('network', 'instanceTemplate') %> + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_address + register: address + code: | + name: <%= dependency_name('address', 'instanceTemplate') %> + region: 'us-west1' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +task: !ruby/object:Provider::Ansible::Task + name: gcp_compute_instance_template + code: | + name: <%= ctx[:name] %> + properties: + disks: + - auto_delete: true + boot: true + initialize_params: + source_image: 'projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts' + machine_type: n1-standard-1 + network_interfaces: + - network: "{{ network }}" + access_configs: + - name: 'test-config' + type: 'ONE_TO_ONE_NAT' + nat_ip: "{{ address }}" + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +verifier: !ruby/object:Provider::Ansible::Verifier + command: | + gcloud compute instance-templates describe + --project="{{ gcp_project}}" + "{{ resource_name }}" + failure: !ruby/object:Provider::Ansible::ComputeFailureCondition + region: global + type: instanceTemplates diff --git a/products/compute/helpers/provider_instance_template.py b/products/compute/helpers/provider_instance_template.py new file mode 100644 index 000000000000..d7d55e6d7f62 --- /dev/null +++ b/products/compute/helpers/provider_instance_template.py @@ -0,0 +1,27 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Mask the fact healthChecks array is actually a single object of type +# HttpHealthCheck. + +def encode_request(request, module): + if 'metadata' in request: + if 'properties' in request['metadata']: + request['metadata']['properties'] = metadata_encoder(request['metadata']['properties']) + return request + + +def decode_response(response, module): + if 'metadata' in response: + if 'properties' in response['metadata']: + response['metadata']['properties'] = metadata_encoder(response['metadata']['properties']) + return response From c7356d89a46698b5c414cc871bf372ed8d3e79ff Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Tue, 27 Mar 2018 05:37:58 +0000 Subject: [PATCH 05/16] Ansible: Compute Target HTTPS Proxy Change-Id: I96b968df0a5cb0086bd729df075499f5e1b55510 --- products/compute/ansible.yaml | 2 - .../examples/ansible/target_https_proxy.yaml | 109 ++++++++++++++++++ 2 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 products/compute/examples/ansible/target_https_proxy.yaml diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index 7e0c242a2086..9c8c5745be93 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -94,8 +94,6 @@ objects: !ruby/object:Api::Resource::HashArray skip: true Snapshot: skip: true - TargetHttpsProxy: - skip: true TargetSslProxy: skip: true # Ansible tasks must alter infrastructure. diff --git a/products/compute/examples/ansible/target_https_proxy.yaml b/products/compute/examples/ansible/target_https_proxy.yaml new file mode 100644 index 000000000000..3ad1af779ff0 --- /dev/null +++ b/products/compute/examples/ansible/target_https_proxy.yaml @@ -0,0 +1,109 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- !ruby/object:Provider::Ansible::Example +dependencies: + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_instance_group + register: instancegroup + code: | + name: <%= dependency_name('instanceGroup', 'targetHttpsProxy') %> + zone: 'us-central1-a' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_http_health_check + register: healthcheck + code: | + name: <%= dependency_name('httpHealthCheck', 'targetHttpsProxy') %> + healthy_threshold: 10 + port: 8080 + timeout_sec: 2 + unhealthy_threshold: 5 + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_backend_service + register: backendservice + code: | + name: <%= dependency_name('backendService', 'targetHttpsProxy') %> + backends: + - group: "{{ instancegroup }}" + health_checks: + - "{{ healthcheck.selfLink }}" + enable_cdn: true + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_url_map + register: urlmap + code: | + name: <%= dependency_name('urlMap', 'targetHttpsProxy') %> + default_service: "{{ backendservice }}" + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_ssl_certificate + register: sslcert + code: | + name: <%= dependency_name('sslCert', 'targetHttpsProxy') %> + description: | + "A certificate for testing. Do not use this certificate in production" + certificate: | + -----BEGIN CERTIFICATE----- + MIICqjCCAk+gAwIBAgIJAIuJ+0352Kq4MAoGCCqGSM49BAMCMIGwMQswCQYDVQQG + EwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxFTAT + BgNVBAoMDEdvb2dsZSwgSW5jLjEeMBwGA1UECwwVR29vZ2xlIENsb3VkIFBsYXRm + b3JtMR8wHQYDVQQDDBZ3d3cubXktc2VjdXJlLXNpdGUuY29tMSEwHwYJKoZIhvcN + AQkBFhJuZWxzb25hQGdvb2dsZS5jb20wHhcNMTcwNjI4MDQ1NjI2WhcNMjcwNjI2 + MDQ1NjI2WjCBsDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xETAP + BgNVBAcMCEtpcmtsYW5kMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xHjAcBgNVBAsM + FUdvb2dsZSBDbG91ZCBQbGF0Zm9ybTEfMB0GA1UEAwwWd3d3Lm15LXNlY3VyZS1z + aXRlLmNvbTEhMB8GCSqGSIb3DQEJARYSbmVsc29uYUBnb29nbGUuY29tMFkwEwYH + KoZIzj0CAQYIKoZIzj0DAQcDQgAEHGzpcRJ4XzfBJCCPMQeXQpTXwlblimODQCuQ + 4mzkzTv0dXyB750fOGN02HtkpBOZzzvUARTR10JQoSe2/5PIwaNQME4wHQYDVR0O + BBYEFKIQC3A2SDpxcdfn0YLKineDNq/BMB8GA1UdIwQYMBaAFKIQC3A2SDpxcdfn + 0YLKineDNq/BMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhALs4vy+O + M3jcqgA4fSW/oKw6UJxp+M6a+nGMX+UJR3YgAiEAvvl39QRVAiv84hdoCuyON0lJ + zqGNhIPGq2ULqXKK8BY= + -----END CERTIFICATE----- + private_key: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEIObtRo8tkUqoMjeHhsOh2ouPpXCgBcP+EDxZCB/tws15oAoGCCqGSM49 + AwEHoUQDQgAEHGzpcRJ4XzfBJCCPMQeXQpTXwlblimODQCuQ4mzkzTv0dXyB750f + OGN02HtkpBOZzzvUARTR10JQoSe2/5PIwQ== + -----END EC PRIVATE KEY----- + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +task: !ruby/object:Provider::Ansible::Task + name: gcp_compute_target_https_proxy + code: | + name: <%= ctx[:name] %> + ssl_certificates: + - "{{ sslcert }}" + url_map: "{{ urlmap }}" + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +verifier: !ruby/object:Provider::Ansible::Verifier + command: | + gcloud compute target-https-proxies describe + --project="{{ gcp_project}}" + "{{ resource_name }}" + failure: !ruby/object:Provider::Ansible::ComputeFailureCondition + region: global + type: targetHttpsProxies From d6e19ff9a2762e989ac025990f819f1785221672 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Thu, 12 Apr 2018 20:44:42 +0000 Subject: [PATCH 06/16] Ansible: Compute Instance Group Manager Change-Id: Id0da7cf24540e692c627119ea5be4890ec19cbb1 --- products/compute/ansible.yaml | 2 - .../ansible/instance_group_manager.yaml | 72 +++++++++++++++++++ provider/ansible/request.rb | 3 +- provider/ansible/selflink.rb | 2 +- 4 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 products/compute/examples/ansible/instance_group_manager.yaml diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index 9c8c5745be93..ec4035206d11 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -90,8 +90,6 @@ objects: !ruby/object:Api::Resource::HashArray include: - 'products/compute/helpers/provider_instance.py' - 'products/compute/helpers/instance_metadata.py' - InstanceGroupManager: - skip: true Snapshot: skip: true TargetSslProxy: diff --git a/products/compute/examples/ansible/instance_group_manager.yaml b/products/compute/examples/ansible/instance_group_manager.yaml new file mode 100644 index 000000000000..210a9398d49e --- /dev/null +++ b/products/compute/examples/ansible/instance_group_manager.yaml @@ -0,0 +1,72 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- !ruby/object:Provider::Ansible::Example +dependencies: + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_network + register: network + code: | + name: <%= dependency_name('network', 'instanceTemplate') %> + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_address + register: address + code: | + name: <%= dependency_name('address', 'instanceTemplate') %> + region: 'us-west1' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_instance_template + register: instancetemplate + code: | + name: <%= ctx[:name] %> + properties: + disks: + - auto_delete: true + boot: true + initialize_params: + source_image: 'projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts' + machine_type: n1-standard-1 + network_interfaces: + - network: "{{ network }}" + access_configs: + - name: 'test-config' + type: 'ONE_TO_ONE_NAT' + nat_ip: "{{ address }}" + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +task: !ruby/object:Provider::Ansible::Task + name: gcp_compute_instance_group_manager + code: | + name: <%= ctx[:name] %> + base_instance_name: 'test1-child' + instance_template: "{{ instancetemplate }}" + target_size: 3 + zone: 'us-west1-a' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +verifier: !ruby/object:Provider::Ansible::Verifier + command: | + gcloud compute instance-groups managed describe + --zone=us-west1-a + --project="{{ gcp_project}}" + "{{ resource_name }}" + failure: !ruby/object:Provider::Ansible::ComputeFailureCondition + region: zones/us-west1-a + type: instanceGroupManagers diff --git a/provider/ansible/request.rb b/provider/ansible/request.rb index b6104ec4886b..eea04567a80f 100644 --- a/provider/ansible/request.rb +++ b/provider/ansible/request.rb @@ -146,9 +146,10 @@ def request_output(prop, hash_name, module_name) elsif prop.is_a?(Api::Type::Array) && \ prop.item_type.is_a?(Api::Type::ResourceRef) && \ !prop.item_type.resource_ref.virtual + prop_name = Google::StringUtils.underscore(prop.name) [ "replace_resource_dict(#{hash_name}", - ".get(#{unicode_string(prop.name)}, []), ", + ".get(#{quote_string(prop_name)}, []), ", "#{quote_string(prop.item_type.imports)})" ].join else diff --git a/provider/ansible/selflink.rb b/provider/ansible/selflink.rb index d7ffc4b55f14..1523f5901d18 100644 --- a/provider/ansible/selflink.rb +++ b/provider/ansible/selflink.rb @@ -41,7 +41,7 @@ def selflink_functions(object) if virt == virtuals.last lines(selflink_function(virt)) else - lines(selflink_function(virt), 2) + lines(selflink_function(virt), 1) end end end From 8342cc72a0456087a868e37ef17361e7a4107b96 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Thu, 12 Apr 2018 19:58:23 +0000 Subject: [PATCH 07/16] Ansible: Compute Target SSL Proxy Change-Id: I64a3ff47163c1c151ebe4826bd0ca0f902571979 --- products/compute/ansible.yaml | 2 - .../examples/ansible/target_ssl_proxy.yaml | 104 ++++++++++++++++++ 2 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 products/compute/examples/ansible/target_ssl_proxy.yaml diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index ec4035206d11..01e49f230536 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -92,8 +92,6 @@ objects: !ruby/object:Api::Resource::HashArray - 'products/compute/helpers/instance_metadata.py' Snapshot: skip: true - TargetSslProxy: - skip: true # Ansible tasks must alter infrastructure. # This means that virtual objects are a poor fit. DiskType: diff --git a/products/compute/examples/ansible/target_ssl_proxy.yaml b/products/compute/examples/ansible/target_ssl_proxy.yaml new file mode 100644 index 000000000000..86c45f76f690 --- /dev/null +++ b/products/compute/examples/ansible/target_ssl_proxy.yaml @@ -0,0 +1,104 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- !ruby/object:Provider::Ansible::Example +dependencies: + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_instance_group + register: instancegroup + code: | + name: <%= dependency_name('instanceGroup', 'targetSslProxy') %> + zone: 'us-central1-a' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_health_check + register: healthcheck + code: | + name: <%= dependency_name('healthCheck', 'targetSslProxy') %> + type: TCP + tcp_health_check: + port_name: service-health + request: ping + response: pong + healthy_threshold: 10 + timeout_sec: 2 + unhealthy_threshold: 5 + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_backend_service + register: backendservice + code: | + name: <%= dependency_name('backendService', 'targetSslProxy') %> + backends: + - group: "{{ instancegroup }}" + health_checks: + - "{{ healthcheck.selfLink }}" + protocol: 'SSL' + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> + - !ruby/object:Provider::Ansible::Task + name: gcp_compute_ssl_certificate + register: sslcert + code: | + name: <%= dependency_name('sslCert', 'targetSslProxy') %> + description: | + "A certificate for testing. Do not use this certificate in production" + certificate: | + -----BEGIN CERTIFICATE----- + MIICqjCCAk+gAwIBAgIJAIuJ+0352Kq4MAoGCCqGSM49BAMCMIGwMQswCQYDVQQG + EwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxFTAT + BgNVBAoMDEdvb2dsZSwgSW5jLjEeMBwGA1UECwwVR29vZ2xlIENsb3VkIFBsYXRm + b3JtMR8wHQYDVQQDDBZ3d3cubXktc2VjdXJlLXNpdGUuY29tMSEwHwYJKoZIhvcN + AQkBFhJuZWxzb25hQGdvb2dsZS5jb20wHhcNMTcwNjI4MDQ1NjI2WhcNMjcwNjI2 + MDQ1NjI2WjCBsDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xETAP + BgNVBAcMCEtpcmtsYW5kMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xHjAcBgNVBAsM + FUdvb2dsZSBDbG91ZCBQbGF0Zm9ybTEfMB0GA1UEAwwWd3d3Lm15LXNlY3VyZS1z + aXRlLmNvbTEhMB8GCSqGSIb3DQEJARYSbmVsc29uYUBnb29nbGUuY29tMFkwEwYH + KoZIzj0CAQYIKoZIzj0DAQcDQgAEHGzpcRJ4XzfBJCCPMQeXQpTXwlblimODQCuQ + 4mzkzTv0dXyB750fOGN02HtkpBOZzzvUARTR10JQoSe2/5PIwaNQME4wHQYDVR0O + BBYEFKIQC3A2SDpxcdfn0YLKineDNq/BMB8GA1UdIwQYMBaAFKIQC3A2SDpxcdfn + 0YLKineDNq/BMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhALs4vy+O + M3jcqgA4fSW/oKw6UJxp+M6a+nGMX+UJR3YgAiEAvvl39QRVAiv84hdoCuyON0lJ + zqGNhIPGq2ULqXKK8BY= + -----END CERTIFICATE----- + private_key: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEIObtRo8tkUqoMjeHhsOh2ouPpXCgBcP+EDxZCB/tws15oAoGCCqGSM49 + AwEHoUQDQgAEHGzpcRJ4XzfBJCCPMQeXQpTXwlblimODQCuQ4mzkzTv0dXyB750f + OGN02HtkpBOZzzvUARTR10JQoSe2/5PIwQ== + -----END EC PRIVATE KEY----- + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +task: !ruby/object:Provider::Ansible::Task + name: gcp_compute_target_ssl_proxy + code: | + name: <%= ctx[:name] %> + ssl_certificates: + - "{{ sslcert }}" + service: "{{ backendservice }}" + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +verifier: !ruby/object:Provider::Ansible::Verifier + command: | + gcloud compute target-ssl-proxies describe + --project="{{ gcp_project}}" + "{{ resource_name }}" + failure: !ruby/object:Provider::Ansible::ComputeFailureCondition + region: global + type: targetSslProxies From 0b57656b0f8085ebd6f4bb2b456eb7f85fca4595 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Mon, 19 Mar 2018 22:13:28 +0000 Subject: [PATCH 08/16] Ansible: Compute Firewall Change-Id: I79fe83666821521eae652d18d05fa6839559c47b --- products/compute/ansible.yaml | 2 - .../compute/examples/ansible/firewall.yaml | 37 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 products/compute/examples/ansible/firewall.yaml diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index 01e49f230536..9fb7d4c480e1 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -83,8 +83,6 @@ objects: !ruby/object:Api::Resource::HashArray aliases: timeoutSec: - timeout_seconds - Firewall: - skip: true Instance: provider_helpers: include: diff --git a/products/compute/examples/ansible/firewall.yaml b/products/compute/examples/ansible/firewall.yaml new file mode 100644 index 000000000000..2d82e2bb9089 --- /dev/null +++ b/products/compute/examples/ansible/firewall.yaml @@ -0,0 +1,37 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- !ruby/object:Provider::Ansible::Example +task: !ruby/object:Provider::Ansible::Task + name: gcp_compute_firewall + code: | + name: <%= ctx[:name] %> + allowed: + - ip_protocol: 'tcp' + ports: + - "22" + target_tags: + - test-ssh-server + - staging-ssh-server + source_tags: + - test-ssh-clients + project: <%= ctx[:project] %> + auth_kind: <%= ctx[:auth_kind] %> + service_account_file: <%= ctx[:service_account_file] %> +verifier: !ruby/object:Provider::Ansible::Verifier + command: | + gcloud compute firewall-rules describe + --project="{{ gcp_project}}" + "{{ resource_name }}" + failure: !ruby/object:Provider::Ansible::ComputeFailureCondition + region: global + type: firewalls From c4fc4ba82c227aed2443499bb33345682dc9fdec Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Thu, 19 Apr 2018 18:11:53 +0000 Subject: [PATCH 09/16] Ansible: Adding HTTPHealthCheck alias for checkIntervalSec Change-Id: If1de77011d1c1055d915541413a16e12d7e96795 --- products/compute/ansible.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index 9fb7d4c480e1..3d658c2c236a 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -70,6 +70,8 @@ objects: !ruby/object:Api::Resource::HashArray aliases: timeoutSec: - timeout_seconds + checkIntervalSec: + - check_interval_seconds HttpsHealthCheck: aliases: timeoutSec: From 9f0844da60e158281cebba3ff57ac1de7dae3e2d Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Mon, 23 Apr 2018 17:51:59 +0000 Subject: [PATCH 10/16] Ansible: Selflink + collection for resourcerefs Change-Id: Ice3076b7c47b66e1c5529900e4cab317cb5c1e00 --- .../ansible_provider_resource_set.py.erb | 5 +- provider/ansible.rb | 48 +++++++++++++++++-- templates/ansible/async.erb | 4 +- templates/ansible/resource.erb | 8 ++-- 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/products/dns/helpers/ansible_provider_resource_set.py.erb b/products/dns/helpers/ansible_provider_resource_set.py.erb index 3ca8476b67a2..1cbd0b1aef46 100644 --- a/products/dns/helpers/ansible_provider_resource_set.py.erb +++ b/products/dns/helpers/ansible_provider_resource_set.py.erb @@ -36,6 +36,9 @@ class SOAForwardable(object): def fail_json(self, *args, **kwargs): self.module.fail_json(*args, **kwargs) + def raise_for_status(self, *args, **kwargs): + self.module.raise_for_status(*args, **kwargs) + def prefetch_soa_resource(module): name = module.params['name'].split('.')[1:] @@ -55,7 +58,7 @@ def prefetch_soa_resource(module): 'dns#resourceRecordSetsListResponse', 'rrsets') if not result: - raise ValueError("Google DNS Managed Zone %s not found" % module.params['managed_zone']) + raise ValueError("Google DNS Managed Zone %s not found" % module.params['managed_zone']['name']) return result diff --git a/provider/ansible.rb b/provider/ansible.rb index bb446619d93c..55a09806b40d 100644 --- a/provider/ansible.rb +++ b/provider/ansible.rb @@ -134,10 +134,18 @@ def build_object_data(object, output_folder) # * extra_data is a dict of extra information. # * extra_url will have a URL chunk to be appended after the URL. # rubocop:disable Metrics/MethodLength - def emit_link(name, url, extra_data = false) - params = emit_link_var_args(url, extra_data) + # rubocop:disable Metrics/AbcSize + def emit_link(name, url, object, has_extra_data = false) + params = emit_link_var_args(url, has_extra_data) extra = (' + extra_url' if url.include?('<|extra|>')) || '' - if extra_data + if rrefs_in_link(url, object) + url_code = "#{url}.format(**res)#{extra}" + [ + "def #{name}(#{params.join(', ')}):", + indent("res = #{resourceref_hash_for_links(url, object)}", 4), + indent("return #{url_code}", 4).gsub('<|extra|>', '') + ].join("\n") + elsif has_extra_data [ "def #{name}(#{params.join(', ')}):", indent([ @@ -160,6 +168,40 @@ def emit_link(name, url, extra_data = false) end end # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/AbcSize + + def rrefs_in_link(link, object) + props_in_link = link.scan(/{([a-z_]*)}/).flatten + (object.parameters || []).select do |p| + props_in_link.include?(Google::StringUtils.underscore(p.name)) && \ + p.is_a?(Api::Type::ResourceRef) && !p.resource_ref.virtual + end.any? + end + + # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/AbcSize + def resourceref_hash_for_links(link, object) + props_in_link = link.scan(/{([a-z_]*)}/).flatten + props = props_in_link.map do |p| + # Select a resourceref if it exists. + rref = (object.parameters || []).select do |prop| + Google::StringUtils.underscore(prop.name) == p && \ + prop.is_a?(Api::Type::ResourceRef) && !prop.resource_ref.virtual + end + if rref.any? + [ + "#{quote_string(p)}:", + "replace_resource_dict(module.params[#{quote_string(p)}],", + "#{quote_string(rref[0].imports)})" + ].join(' ') + else + "#{quote_string(p)}: module.params[#{quote_string(p)}]" + end + end + ['{', indent_list(props, 4), '}'].join("\n") + end + # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/AbcSize def emit_link_var_args(url, extra_data) extra_url = url.include?('<|extra|>') diff --git a/templates/ansible/async.erb b/templates/ansible/async.erb index a3366eda3135..664c3f6827bd 100644 --- a/templates/ansible/async.erb +++ b/templates/ansible/async.erb @@ -3,7 +3,9 @@ stat_path = path2navigate(object.async.status.path) res_path = path2navigate(object.async.result.path) -%> -<%= lines(emit_link('async_op_url', async_operation_url(object), true), 2) -%> +<%= + lines(emit_link('async_op_url', async_operation_url(object), object, true), 2) +-%> def wait_for_operation(module, response): <% if object.kind? -%> <% op_kind = object.async.operation.kind -%> diff --git a/templates/ansible/resource.erb b/templates/ansible/resource.erb index ce5d9a93895d..9038b2be4143 100644 --- a/templates/ansible/resource.erb +++ b/templates/ansible/resource.erb @@ -290,17 +290,17 @@ def resource_to_request(module): <%= lines(compile('templates/ansible/transport.erb'), 2) -%> <% custom_self_link = get_code_multiline config, 'self_link' -%> <% if custom_self_link.nil? -%> -<%= lines(emit_link('self_link', self_link_url(object))) -%> +<%= lines(emit_link('self_link', self_link_url(object), object)) -%> <% else # custom_self_link.nil? -%> -<%= lines(emit_link('self_link', custom_self_link)) -%> +<%= lines(emit_link('self_link', custom_self_link, object)) -%> <% end # custom_self_link.nil? -%> <% custom_collection = get_code_multiline config, 'collection' -%> <% if custom_collection.nil? -%> -<%= lines(emit_link('collection', collection_url(object))) -%> +<%= lines(emit_link('collection', collection_url(object), object)) -%> <% else # custom_collection.nil? -%> -<%= lines(emit_link('collection', custom_collection)) -%> +<%= lines(emit_link('collection', custom_collection, object)) -%> <% end # custom_collection.nil? -%> From 4d3e886ce095a5b9d0ea4cbbc594efebef36fbb8 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Thu, 26 Apr 2018 06:06:25 +0000 Subject: [PATCH 11/16] Ansible: Outputting description fields using a YAML dumper Change-Id: I20806546d8263819093af14964ee4eeeb2fed59b --- provider/ansible/documentation.rb | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/provider/ansible/documentation.rb b/provider/ansible/documentation.rb index 7c7fbc104ce4..77eab26c1a85 100644 --- a/provider/ansible/documentation.rb +++ b/provider/ansible/documentation.rb @@ -55,22 +55,26 @@ def bullet_lines(line, spaces) # the bullet properly # because of the : # character - def bullet_line(paragraph, spaces, multiline = true, add_period = true) - # + 2 for "- " and a potential period at the end of the sentence. - # Remove arbitrary newlines created by formatting in YAML files - indented = indent(wrap_field(paragraph.tr("\n", ' '), spaces + 3), 2) + def bullet_line(paragraph, spaces, _multiline = true, add_period = true) + paragraph += '.' unless paragraph.end_with?('.') || !add_period + paragraph = paragraph.tr("\n", ' ').strip - if multiline && UNSAFE_CHARS.any? { |c| paragraph.include?(c) } - indented = "- |\n" + indented - else - indented[0] = '-' - end + # Paragraph placed inside array to get bullet point. + yaml = [paragraph].to_yaml + # YAML documentation header is not necessary. + yaml = yaml.gsub("---\n", '') if yaml.include?("---\n") - if add_period - indented += '.' unless indented.end_with?('.') + # YAML dumper isn't very smart about line lengths. + # If any line is over 160 characters (with indents), build the YAML + # block using wrap_field. + # Using YAML.dump output ensures that all character escaping done + if yaml.split("\n").any? { |line| line.length > (160 - spaces) } + return wrap_field( + yaml.tr("\n", ' ').gsub(/\s+/, ' '), + spaces + 3 + ).each_with_index.map { |x, i| i.zero? ? x : indent(x, 2) } end - - indented + yaml end # Builds out a full YAML block for DOCUMENTATION From 9fab1272c0b0559b4af0983b5de394e80907ed04 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Mon, 23 Apr 2018 18:15:19 +0000 Subject: [PATCH 12/16] Ansible: Resource to request ignore input=false parameters Change-Id: Ifc26d50a5f91a2946c05db5176a3d7bd8cb79241 --- templates/ansible/resource.erb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/ansible/resource.erb b/templates/ansible/resource.erb index 9038b2be4143..6c629372b4cf 100644 --- a/templates/ansible/resource.erb +++ b/templates/ansible/resource.erb @@ -258,10 +258,10 @@ def main(): <% end # unless object.virtual -%> def resource_to_request(module): <% - properties_in_request = [] - properties_in_request += object.parameters if object.parameters - properties_in_request += object.properties.reject(&:output) - properties_in_request = properties_in_request.compact + properties_in_request = [ + object&.parameters&.select { |p| p.input }, + object.properties.reject(&:output) + ].flatten.compact -%> request = { <% if object.kind? -%> From 0eba2b044a620c24bc1c8f3155a57bca93fdf596 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Tue, 1 May 2018 23:46:20 +0000 Subject: [PATCH 13/16] DNS - changing quota to use nested property Change-Id: Ibacca5e151fc50817edb103fd3b85a0cc982f819 --- products/dns/api.yaml | 69 +++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/products/dns/api.yaml b/products/dns/api.yaml index 88325dab5845..fdfed2396443 100644 --- a/products/dns/api.yaml +++ b/products/dns/api.yaml @@ -89,39 +89,44 @@ objects: description: | Unique numeric identifier for the resource; defined by the server. output: true - - !ruby/object:Api::Type::Integer - name: 'quota.managedZones' - description: Maximum allowed number of managed zones in the project. - output: true - - !ruby/object:Api::Type::Integer - name: 'quota.resourceRecordsPerRrset' - description: | - Maximum allowed number of ResourceRecords per ResourceRecordSet. - output: true - - !ruby/object:Api::Type::Integer - name: 'quota.rrsetAdditionsPerChange' - description: | - Maximum allowed number of ResourceRecordSets to add per - ChangesCreateRequest. - output: true - - !ruby/object:Api::Type::Integer - name: 'quota.rrsetDeletionsPerChange' - description: | - Maximum allowed number of ResourceRecordSets to delete per - ChangesCreateRequest. - output: true - - !ruby/object:Api::Type::Integer - name: 'quota.rrsetsPerManagedZone' - description: | - Maximum allowed number of ResourceRecordSets per zone in the - project. - output: true - - !ruby/object:Api::Type::Integer - name: 'quota.totalRrdataSizePerChange' - description: | - Maximum allowed size for total rrdata in one ChangesCreateRequest - in bytes. + - !ruby/object:Api::Type::NestedObject + name: 'quota' + description: 'Quota allowed in project' output: true + properties: + - !ruby/object:Api::Type::Integer + name: 'managedZones' + description: Maximum allowed number of managed zones in the project. + output: true + - !ruby/object:Api::Type::Integer + name: 'resourceRecordsPerRrset' + description: | + Maximum allowed number of ResourceRecords per ResourceRecordSet. + output: true + - !ruby/object:Api::Type::Integer + name: 'rrsetAdditionsPerChange' + description: | + Maximum allowed number of ResourceRecordSets to add per + ChangesCreateRequest. + output: true + - !ruby/object:Api::Type::Integer + name: 'rrsetDeletionsPerChange' + description: | + Maximum allowed number of ResourceRecordSets to delete per + ChangesCreateRequest. + output: true + - !ruby/object:Api::Type::Integer + name: 'rrsetsPerManagedZone' + description: | + Maximum allowed number of ResourceRecordSets per zone in the + project. + output: true + - !ruby/object:Api::Type::Integer + name: 'totalRrdataSizePerChange' + description: | + Maximum allowed size for total rrdata in one ChangesCreateRequest + in bytes. + output: true - !ruby/object:Api::Resource name: 'ResourceRecordSet' kind: 'dns#resourceRecordSet' From a27c7b54b7078b31f9a38a088ab3af5508426357 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Tue, 1 May 2018 21:27:13 +0000 Subject: [PATCH 14/16] Override support for Ansible Change-Id: Iee42d18e0c562f4c880978a77b566f8f07e7c287 --- products/compute/ansible.yaml | 95 +++++++------------ products/container/ansible.yaml | 16 ++-- products/dns/ansible.yaml | 30 +++--- .../ansible_provider_resource_set.py.erb | 2 +- products/pubsub/ansible.yaml | 12 +-- provider/ansible/common~compile.yaml | 4 +- provider/ansible/documentation.rb | 18 ++-- provider/ansible/module.rb | 20 ++-- provider/ansible/resource_override.rb | 24 +++++ provider/config.rb | 2 + templates/ansible/provider_helpers.erb | 8 +- templates/ansible/resource.erb | 79 ++++++++------- 12 files changed, 146 insertions(+), 164 deletions(-) diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index 3d658c2c236a..4b5e49d2fc96 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -25,19 +25,43 @@ manifest: !ruby/object:Provider::Ansible::Manifest author: Google Inc. (@googlecloudplatform) # This is where custom code would be defined eventually. overrides: !ruby/object:Provider::ResourceOverrides - Firewall: !ruby/object:Provider::Ansible::ResourceOverride - exclude: true + BackendService: !ruby/object:Provider::Ansible::ResourceOverride + aliases: + timeoutSec: + - timeout_seconds + Disk: !ruby/object:Provider::Ansible::ResourceOverride + editable: false + HealthCheck: !ruby/object:Provider::Ansible::ResourceOverride + aliases: + timeoutSec: + - timeout_seconds + HttpHealthCheck: !ruby/object:Provider::Ansible::ResourceOverride + aliases: + timeoutSec: + - timeout_seconds + checkIntervalSec: + - check_interval_seconds + HttpsHealthCheck: !ruby/object:Provider::Ansible::ResourceOverride + aliases: + timeoutSec: + - timeout_seconds Instance: !ruby/object:Provider::Ansible::ResourceOverride - exclude: true - InstanceGroupManager: !ruby/object:Provider::Ansible::ResourceOverride - exclude: true + provider_helpers: + - 'products/compute/helpers/provider_instance.py' + - 'products/compute/helpers/instance_metadata.py' + InstanceGroup: !ruby/object:Provider::Ansible::ResourceOverride + editable: false InstanceTemplate: !ruby/object:Provider::Ansible::ResourceOverride - exclude: true + provider_helpers: + - 'products/compute/helpers/provider_instance.py' + - 'products/compute/helpers/instance_metadata.py' + TargetPool: !ruby/object:Provider::Ansible::ResourceOverride + provider_helpers: + - 'products/compute/helpers/provider_target_pool.py' + # Not yet implemented. Snapshot: !ruby/object:Provider::Ansible::ResourceOverride exclude: true - TargetHttpsProxy: !ruby/object:Provider::Ansible::ResourceOverride - exclude: true - TargetSslProxy: !ruby/object:Provider::Ansible::ResourceOverride + TargetVpnGateway: !ruby/object:Provider::Ansible::ResourceOverride exclude: true # Ansible tasks must alter infrastructure. # This means that virtual objects are a poor fit. @@ -51,59 +75,6 @@ overrides: !ruby/object:Provider::ResourceOverrides exclude: true Zone: !ruby/object:Provider::Ansible::ResourceOverride exclude: true -# TODO(https://github.com/GoogleCloudPlatform/magic-modules/issues/47): Migrate objects to overrides -objects: !ruby/object:Api::Resource::HashArray - Disk: - editable: false - InstanceGroup: - editable: false - HealthCheck: - aliases: - timeoutSec: - - timeout_seconds - InstanceTemplate: - provider_helpers: - include: - - 'products/compute/helpers/provider_instance.py' - - 'products/compute/helpers/instance_metadata.py' - HttpHealthCheck: - aliases: - timeoutSec: - - timeout_seconds - checkIntervalSec: - - check_interval_seconds - HttpsHealthCheck: - aliases: - timeoutSec: - - timeout_seconds - TargetPool: - provider_helpers: - include: - - 'products/compute/helpers/provider_target_pool.py' - # Not implemented yet. - BackendService: - aliases: - timeoutSec: - - timeout_seconds - Instance: - provider_helpers: - include: - - 'products/compute/helpers/provider_instance.py' - - 'products/compute/helpers/instance_metadata.py' - Snapshot: - skip: true - # Ansible tasks must alter infrastructure. - # This means that virtual objects are a poor fit. - DiskType: - skip: true - License: - skip: true - MachineType: - skip: true - Region: - skip: true - Zone: - skip: true examples: !ruby/object:Api::Resource::HashArray files: !ruby/object:Provider::Config::Files copy: diff --git a/products/container/ansible.yaml b/products/container/ansible.yaml index a6f0f27989cc..df44970caa65 100644 --- a/products/container/ansible.yaml +++ b/products/container/ansible.yaml @@ -24,17 +24,15 @@ manifest: !ruby/object:Provider::Ansible::Manifest version_added: '2.6' author: Google Inc. (@googlecloudplatform) # This is where custom code would be defined eventually. -objects: !ruby/object:Api::Resource::HashArray - Cluster: +overrides: !ruby/object:Provider::ResourceOverrides + Cluster: !ruby/object:Provider::Ansible::ResourceOverride provider_helpers: - include: - - 'products/container/helpers/provider_cluster.py' - NodePool: + - 'products/container/helpers/provider_cluster.py' + KubeConfig: !ruby/object:Provider::Ansible::ResourceOverride + exclude: true + NodePool: !ruby/object:Provider::Ansible::ResourceOverride provider_helpers: - include: - - 'products/container/helpers/provider_node_pool.py' - KubeConfig: - skip: true + - 'products/container/helpers/provider_node_pool.py' examples: !ruby/object:Api::Resource::HashArray files: !ruby/object:Provider::Config::Files copy: diff --git a/products/dns/ansible.yaml b/products/dns/ansible.yaml index 0fe235d3a0f0..e2ef3b3bc3ac 100644 --- a/products/dns/ansible.yaml +++ b/products/dns/ansible.yaml @@ -25,16 +25,11 @@ manifest: !ruby/object:Provider::Ansible::Manifest author: Google Inc. (@googlecloudplatform) # This is where custom code would be defined eventually. overrides: !ruby/object:Provider::ResourceOverrides - Project: !ruby/object:Provider::Ansible::ResourceOverride - # TODO(alexstephen): Re-evaluate merging Project into Ansible - exclude: true - Change: !ruby/object:Provider::Ansible::ResourceOverride - exclude: true -# TODO(https://github.com/GoogleCloudPlatform/magic-modules/issues/47): Migrate objects to overrides -objects: !ruby/object:Api::Resource::HashArray - ManagedZone: + ManagedZone: !ruby/object:Provider::Ansible::ResourceOverride editable: false - ResourceRecordSet: + ResourceRecordSet: !ruby/object:Provider::Ansible::ResourceOverride + access_api_results: true + editable: true version_added: '2.6' imports: - copy @@ -64,14 +59,17 @@ objects: !ruby/object:Api::Resource::HashArray return fetch_wrapped_resource(module, 'dns#resourceRecordSet', 'dns#resourceRecordSetsListResponse', 'rrsets') - access_api_results: true provider_helpers: - visible: - unwrap_resource: false - resource_to_request: false - return_if_object: false - include: - - 'products/dns/helpers/ansible_provider_resource_set.py.erb' + - 'products/dns/helpers/ansible_provider_resource_set.py.erb' + hidden: + - unwrap_resource + - resource_to_request + - return_if_object + Project: !ruby/object:Provider::Ansible::ResourceOverride + # TODO(alexstephen): Re-evaluate merging Project into Ansible + exclude: true + Change: !ruby/object:Provider::Ansible::ResourceOverride + exclude: true examples: !ruby/object:Api::Resource::HashArray files: !ruby/object:Provider::Config::Files copy: diff --git a/products/dns/helpers/ansible_provider_resource_set.py.erb b/products/dns/helpers/ansible_provider_resource_set.py.erb index 1cbd0b1aef46..912732470559 100644 --- a/products/dns/helpers/ansible_provider_resource_set.py.erb +++ b/products/dns/helpers/ansible_provider_resource_set.py.erb @@ -143,7 +143,7 @@ def return_if_change_object(module, response): try: response.raise_for_status() result = response.json() - except Exception as inst: + except getattr(json.decoder, 'JSONDecodeError', ValueError) as inst: module.fail_json(msg="Invalid JSON response with error: %s" % inst) if result['kind'] != 'dns#change': diff --git a/products/pubsub/ansible.yaml b/products/pubsub/ansible.yaml index 04971e0cc3ab..f0a629555236 100644 --- a/products/pubsub/ansible.yaml +++ b/products/pubsub/ansible.yaml @@ -24,18 +24,16 @@ manifest: !ruby/object:Provider::Ansible::Manifest version_added: '2.6' author: Google Inc. (@googlecloudplatform) # This is where custom code would be defined eventually. -objects: !ruby/object:Api::Resource::HashArray - Topic: +overrides: !ruby/object:Provider::ResourceOverrides + Topic: !ruby/object:Provider::Ansible::ResourceOverride encoder: encode_request provider_helpers: - include: - - 'products/pubsub/helpers/provider_topic.py' - Subscription: + - 'products/pubsub/helpers/provider_topic.py' + Subscription: !ruby/object:Provider::Ansible::ResourceOverride editable: false encoder: encode_request provider_helpers: - include: - - 'products/pubsub/helpers/provider_subscription.py' + - 'products/pubsub/helpers/provider_subscription.py' examples: !ruby/object:Api::Resource::HashArray files: !ruby/object:Provider::Config::Files copy: diff --git a/provider/ansible/common~compile.yaml b/provider/ansible/common~compile.yaml index 8c2d6b7c9cb7..9fc0711dbe9a 100644 --- a/provider/ansible/common~compile.yaml +++ b/provider/ansible/common~compile.yaml @@ -23,10 +23,8 @@ 'test/units/module_utils/gcp/test_gcp_utils.py': 'provider/ansible/test_gcp_utils.py' <% unless config.nil? -%> <% - skipped_props = config.objects.select { |k, v| v && (v['skip'] == true) } - .keys object_names = api.objects - .reject { |o| skipped_props.include?(o.name) } + .select { |o| !o.exclude } .map do |object| ["gcp_#{object.__product.prefix[1..-1]}", Google::StringUtils.underscore(object.name)].join('_') diff --git a/provider/ansible/documentation.rb b/provider/ansible/documentation.rb index 77eab26c1a85..24af0c651540 100644 --- a/provider/ansible/documentation.rb +++ b/provider/ansible/documentation.rb @@ -79,14 +79,14 @@ def bullet_line(paragraph, spaces, _multiline = true, add_period = true) # Builds out a full YAML block for DOCUMENTATION # This includes the YAML for the property as well as any nested props - def doc_property_yaml(prop, config, spaces) - block = minimal_doc_block(prop, config, spaces) + def doc_property_yaml(prop, object, spaces) + block = minimal_doc_block(prop, object, spaces) # Ansible linter does not support nesting options this deep. if prop.is_a?(Api::Type::NestedObject) - block.concat(nested_doc(prop.properties, config, spaces)) + block.concat(nested_doc(prop.properties, object, spaces)) elsif prop.is_a?(Api::Type::Array) && prop.item_type.is_a?(Api::Type::NestedObject) - block.concat(nested_doc(prop.item_type.properties, config, spaces)) + block.concat(nested_doc(prop.item_type.properties, object, spaces)) else block end @@ -118,25 +118,25 @@ def nested_return(properties, spaces) ) end - def nested_doc(properties, config, spaces) + def nested_doc(properties, object, spaces) block = [indent('suboptions:', 4)] block.concat( properties.map do |p| - indent(doc_property_yaml(p, config, spaces + 4), 8) + indent(doc_property_yaml(p, object, spaces + 4), 8) end ) end # Builds out the minimal YAML block for DOCUMENTATION # rubocop:disable Metrics/AbcSize - def minimal_doc_block(prop, config, spaces) + def minimal_doc_block(prop, object, spaces) [ minimal_yaml(prop, spaces), indent([ "required: #{prop.required ? 'true' : 'false'}", ('type: bool' if prop.is_a? Api::Type::Boolean), - ("aliases: [#{config['aliases'][prop.name].join(', ')}]" \ - if config['aliases']&.keys&.include?(prop.name)), + ("aliases: [#{object.aliases[prop.name].join(', ')}]" \ + if object&.aliases&.keys&.include?(prop.name)), (if prop.is_a? Api::Type::Enum [ 'choices:', diff --git a/provider/ansible/module.rb b/provider/ansible/module.rb index 4086208b6188..00c400e9ce1e 100644 --- a/provider/ansible/module.rb +++ b/provider/ansible/module.rb @@ -18,15 +18,15 @@ module Ansible module Module # Returns the Python dictionary representing a simple property for # validation. - def python_dict_for_property(prop, config, spaces = 0) + def python_dict_for_property(prop, object, spaces = 0) if prop.is_a?(Api::Type::Array) && \ prop.item_type.is_a?(Api::Type::NestedObject) - nested_obj_dict(prop, config, prop.item_type.properties, spaces) + nested_obj_dict(prop, object, prop.item_type.properties, spaces) elsif prop.is_a? Api::Type::NestedObject - nested_obj_dict(prop, config, prop.properties, spaces) + nested_obj_dict(prop, object, prop.properties, spaces) else name = Google::StringUtils.underscore(prop.out_name) - "#{name}=dict(#{prop_options(prop, config, spaces).join(', ')})" + "#{name}=dict(#{prop_options(prop, object, spaces).join(', ')})" end end @@ -34,13 +34,13 @@ def python_dict_for_property(prop, config, spaces = 0) # Creates a Python dictionary representing a nested object property # for validation. - def nested_obj_dict(prop, config, properties, spaces) + def nested_obj_dict(prop, object, properties, spaces) name = Google::StringUtils.underscore(prop.out_name) - options = prop_options(prop, config, spaces).join(', ') + options = prop_options(prop, object, spaces).join(', ') [ "#{name}=dict(#{options}, options=dict(", indent_list(properties.map do |p| - python_dict_for_property(p, config, spaces + 4) + python_dict_for_property(p, object, spaces + 4) end, 4), '))' ] @@ -48,15 +48,15 @@ def nested_obj_dict(prop, config, properties, spaces) # Returns an array of all base options for a given property. # rubocop:disable Metrics/AbcSize - def prop_options(prop, config, spaces) + def prop_options(prop, object, spaces) [ ('required=True' if prop.required), "type=#{quote_string(python_type(prop))}", (choices_enum(prop, spaces) if prop.is_a? Api::Type::Enum), ("elements=#{quote_string(python_type(prop.item_type))}" \ if prop.is_a? Api::Type::Array), - (if config['aliases']&.keys&.include?(prop.name) - "aliases=[#{config['aliases'][prop.name].map do |x| + (if object&.aliases&.keys&.include?(prop.name) + "aliases=[#{object.aliases[prop.name].map do |x| quote_string(x) end.join(', ')}]" end) diff --git a/provider/ansible/resource_override.rb b/provider/ansible/resource_override.rb index 7dd8856c1603..50029d250f50 100644 --- a/provider/ansible/resource_override.rb +++ b/provider/ansible/resource_override.rb @@ -18,6 +18,20 @@ module Ansible # Ansible specific properties to be added to Api::Resource module OverrideProperties attr_reader :access_api_results + attr_reader :aliases + attr_reader :collection + attr_reader :create + attr_reader :custom_self_link + attr_reader :decoder + attr_reader :delete + attr_reader :encoder + attr_reader :exclude + attr_reader :editable + attr_reader :hidden + attr_reader :imports + attr_reader :provider_helpers + attr_reader :update + attr_reader :version_added end # Product specific overriden properties for Ansible @@ -28,8 +42,18 @@ def validate super default_value_property :access_api_results, false + default_value_property :aliases, {} + default_value_property :decoder, false + default_value_property :exclude, false + default_value_property :editable, true + default_value_property :imports, [] + default_value_property :provider_helpers, [] check_property :access_api_results, :boolean + check_property :aliases, ::Hash + check_property :editable, :boolean + check_property :exclude, :boolean + check_property :imports, ::Array end private diff --git a/provider/config.rb b/provider/config.rb index fe4ebb0daaca..d8f0b1d84964 100644 --- a/provider/config.rb +++ b/provider/config.rb @@ -188,6 +188,8 @@ def self.parse(cfg_file, api = nil) raise "Config #{cfg_file}(#{config.class}) is not a Provider::Config" \ unless config.class <= Provider::Config # Config must be validated so items are properly setup for next compile + config.default_overrides + config.spread_api config, api, [], '' unless api.nil? config.validate # Compile step #2: Now that we have the target class, compile with that # class features diff --git a/templates/ansible/provider_helpers.erb b/templates/ansible/provider_helpers.erb index 39ce7338ab1f..db854570bd65 100644 --- a/templates/ansible/provider_helpers.erb +++ b/templates/ansible/provider_helpers.erb @@ -1,11 +1,9 @@ <% - provider_helpers = Google::HashUtils.navigate(config, - %w[provider_helpers include], []) -%> -<% unless provider_helpers.empty? -%> +<% unless object.provider_helpers.empty? -%> <%= - provider_helpers.map do |f| - if f == provider_helpers.last + object.provider_helpers.map do |f| + if f == object.provider_helpers.last lines(compile(f)) else lines(compile(f), 2) diff --git a/templates/ansible/resource.erb b/templates/ansible/resource.erb index 6c629372b4cf..689a71de81b0 100644 --- a/templates/ansible/resource.erb +++ b/templates/ansible/resource.erb @@ -41,7 +41,7 @@ options: choices: ['present', 'absent'] default: 'present' <% object.all_user_properties.reject(&:output).each do |prop| -%> -<%= lines(indent(doc_property_yaml(prop, config, 4), 4)) -%> +<%= lines(indent(doc_property_yaml(prop, object, 4), 4)) -%> <% end -%> extends_documentation_fragment: gcp ''' @@ -76,11 +76,11 @@ RETURN = ''' <%= lines(import) -%> import json <% - config['imports'] ||= [] - config['imports'] << 'time' if object.async - config['imports'] << 're' unless virtual_selflink_rrefs(object).empty? + imports = object.imports || [] + imports << 'time' if object.async + imports << 're' unless virtual_selflink_rrefs(object).empty? -%> -<%= lines(config['imports'].sort.uniq.map { |i| "import #{i}" }) -%> +<%= lines(imports.sort.uniq.map { |i| "import #{i}" }) -%> ################################################################################ # Main @@ -92,7 +92,7 @@ def main(): <% mod_props = object.all_user_properties.reject(&:output).map do |prop| - python_dict_for_property(prop, config) + python_dict_for_property(prop, object) end -%> module = GcpModule( @@ -124,6 +124,7 @@ def main(): if not fetch: module.fail_json(msg="<%= object.name -%> is not valid") <% else # object.virtual -%> + if fetch: if state == 'present': if is_different(module, fetch): @@ -173,12 +174,11 @@ def main(): <% prod_name = object.__product.prefix[1..-1] -%> <% unless object.virtual -%> -<% custom_create = get_code_multiline config, 'create' -%> <%# TODO: kind param not always needed. # https://github.com/GoogleCloudPlatform/magic-modules/issues/45 -%> <%= method_decl('create', ['module', 'link', ('kind' if object.kind?)]) %> -<% if custom_create.nil? -%> +<% if object.create.nil? -%> auth = GcpSession(module, <%= quote_string(prod_name) -%>) <% if object.create_verb.nil? || object.create_verb == :POST @@ -201,18 +201,16 @@ def main(): -%> return <%= method %> <% else -%> -<%= lines(indent(custom_create, 4)) -%> +<%= lines(indent(object.create, 4)) -%> <% end -%> -<% custom_update = get_code_multiline config, 'update' -%> -<% editable = config['editable'] -%> <%= lines(method_decl('update', ['module', 'link', ('kind' if object.kind?), ('fetch' if object.save_api_results?)])) -%> -<% if custom_update.nil? -%> -<% if !false?(editable) -%> +<% if object.update.nil? -%> +<% if !false?(object.editable) -%> auth = GcpSession(module, <%= quote_string(prod_name) -%>) <% method = method_call( @@ -225,20 +223,19 @@ def main(): ) -%> return <%= method %> -<% else # !false?(editable) -%> +<% else # !false?(object.editable) -%> module.fail_json(msg="<%= object.name -%> cannot be edited") -<% end # !false?(editable) -%> -<% else # custom_update.nil? -%> -<%= lines(indent(custom_update, 4)) -%> -<% end # custom_update.nil? -%> +<% end # !false?(object.editable) -%> +<% else # object.update.nil? -%> +<%= lines(indent(object.update, 4)) -%> +<% end # object.update.nil? -%> -<% custom_delete = get_code_multiline config, 'delete' -%> <%= lines(method_decl('delete', ['module', 'link', ('kind' if object.kind?), ('fetch' if object.save_api_results?)])) -%> -<% if custom_delete.nil? -%> +<% if object.delete.nil? -%> auth = GcpSession(module, <%= quote_string(prod_name) -%>) <% method = method_call( @@ -250,9 +247,9 @@ def main(): ) -%> return <%= method %> -<% else # if custom_delete.nil? -%> -<%= lines(indent(custom_delete, 4)) -%> -<% end # if custom_delete.nil? -%> +<% else # if object.delete.nil? -%> +<%= lines(indent(object.delete, 4)) -%> +<% end # if object.delete.nil? -%> <% end # unless object.virtual -%> @@ -272,11 +269,11 @@ def resource_to_request(module): <% encoder_name = if object.encoder? object.transport.encoder - else config['encoder'] - config['encoder'] + else object.encoder + object.encoder end -%> -<% if object.encoder? || config['encoder'] -%> +<% if object.encoder? || object.encoder -%> request = <%= encoder_name -%>(request, module) <% end -%> return_vals = {} @@ -288,20 +285,18 @@ def resource_to_request(module): <%= lines(compile('templates/ansible/transport.erb'), 2) -%> -<% custom_self_link = get_code_multiline config, 'self_link' -%> -<% if custom_self_link.nil? -%> +<% if object.custom_self_link.nil? -%> <%= lines(emit_link('self_link', self_link_url(object), object)) -%> -<% else # custom_self_link.nil? -%> -<%= lines(emit_link('self_link', custom_self_link, object)) -%> -<% end # custom_self_link.nil? -%> +<% else # object.self_link.nil? -%> +<%= lines(emit_link('self_link', object.custom_self_link, object)) -%> +<% end # object.self_link.nil? -%> -<% custom_collection = get_code_multiline config, 'collection' -%> -<% if custom_collection.nil? -%> +<% if object.collection.nil? -%> <%= lines(emit_link('collection', collection_url(object), object)) -%> -<% else # custom_collection.nil? -%> -<%= lines(emit_link('collection', custom_collection, object)) -%> -<% end # custom_collection.nil? -%> +<% else # object.collection.nil? -%> +<%= lines(emit_link('collection', object.collection, object)) -%> +<% end # object.collection.nil? -%> <%= @@ -325,11 +320,11 @@ def resource_to_request(module): <% decoder_name = if object.decoder? object.transport.decoder - else config['decoder'] - config['decoder'] + else object.decoder + object.decoder end -%> -<% if object.decoder? -%> +<% if object.decoder? || object.decoder -%> result = <%= decoder_name -%>(result, module) <% end -%> @@ -349,11 +344,11 @@ def is_different(module, response): <% decoder_name = if object.decoder? object.transport.decoder - else config['decoder'] - config['decoder'] + else object.decoder + object.decoder end -%> -<% if object.decoder? -%> +<% if object.decoder? || object.decoder -%> request = <%= decoder_name -%>(request, module) <% end -%> From 04cb1ac2b4da6720fb6cf4d54da0a7941653e7ab Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 2 May 2018 00:21:47 +0000 Subject: [PATCH 15/16] Changing Ansible to point to our fork Change-Id: I5ad6ecf1344621cb40d4123dfc80cc5861c28c8d --- .gitmodules | 3 +- products/compute/ansible.yaml | 33 ++++++++++++--------- products/dns/chef.yaml | 9 +++++- products/dns/puppet.yaml | 8 +++++- products/pubsub/ansible.yaml | 8 ++++-- provider/ansible/common~compile.yaml | 7 ++++- provider/ansible/documentation.rb | 3 +- provider/ansible/module.rb | 7 ++--- provider/ansible/property_override.rb | 6 ++++ provider/ansible/resource_override.rb | 16 +++++------ provider/config.rb | 2 -- templates/ansible/resource.erb | 41 ++++----------------------- 12 files changed, 70 insertions(+), 73 deletions(-) diff --git a/.gitmodules b/.gitmodules index a480d6d1f614..402741a5e646 100644 --- a/.gitmodules +++ b/.gitmodules @@ -66,7 +66,8 @@ url = git@github.com:GoogleCloudPlatform/chef-google-logging [submodule "build/ansible"] path = build/ansible - url = git@github.com:ansible/ansible.git + url = git@github.com:modular-magician/ansible + branch = devel [submodule "build/terraform"] path = build/terraform url = git@github.com:terraform-providers/terraform-provider-google.git diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index 4b5e49d2fc96..595d13464518 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -26,25 +26,30 @@ manifest: !ruby/object:Provider::Ansible::Manifest # This is where custom code would be defined eventually. overrides: !ruby/object:Provider::ResourceOverrides BackendService: !ruby/object:Provider::Ansible::ResourceOverride - aliases: - timeoutSec: - - timeout_seconds + properties: + timeoutSec: !ruby/object:Provider::Ansible::PropertyOverride + aliases: + - timeout_seconds Disk: !ruby/object:Provider::Ansible::ResourceOverride editable: false HealthCheck: !ruby/object:Provider::Ansible::ResourceOverride - aliases: - timeoutSec: - - timeout_seconds + properties: + timeoutSec: !ruby/object:Provider::Ansible::PropertyOverride + aliases: + - timeout_seconds HttpHealthCheck: !ruby/object:Provider::Ansible::ResourceOverride - aliases: - timeoutSec: - - timeout_seconds - checkIntervalSec: - - check_interval_seconds + properties: + timeoutSec: !ruby/object:Provider::Ansible::PropertyOverride + aliases: + - timeout_seconds + checkIntervalSec: !ruby/object:Provider::Ansible::PropertyOverride + aliases: + - check_interval_seconds HttpsHealthCheck: !ruby/object:Provider::Ansible::ResourceOverride - aliases: - timeoutSec: - - timeout_seconds + properties: + timeoutSec: !ruby/object:Provider::Ansible::PropertyOverride + aliases: + - timeout_seconds Instance: !ruby/object:Provider::Ansible::ResourceOverride provider_helpers: - 'products/compute/helpers/provider_instance.py' diff --git a/products/dns/chef.yaml b/products/dns/chef.yaml index 2e997150164a..bf5d0b9250b5 100644 --- a/products/dns/chef.yaml +++ b/products/dns/chef.yaml @@ -13,7 +13,7 @@ --- !ruby/object:Provider::Chef::Config manifest: !ruby/object:Provider::Chef::Manifest - version: '0.1.0' + version: '0.1.1' source: 'https://github.com/GoogleCloudPlatform/chef-google-dns' issues: 'https://github.com/GoogleCloudPlatform/chef-google-dns/issues' summary: 'A Chef cookbook to manage Google Cloud DNS resources' @@ -115,3 +115,10 @@ style: - function: self.resource_to_hash exceptions: - Metrics/MethodLength +changelog: + - !ruby/object:Provider::Config::Changelog + version: '0.1.1' + date: 2018-05-02T06:00:00-0700 + fixes: + - Changed quota_* fields to a Hash. All quota_ fields must now be + keys in a hash named `quota` diff --git a/products/dns/puppet.yaml b/products/dns/puppet.yaml index dbe0f82ad8da..d79adf2c5b50 100644 --- a/products/dns/puppet.yaml +++ b/products/dns/puppet.yaml @@ -13,7 +13,7 @@ --- !ruby/object:Provider::Puppet::Config manifest: !ruby/object:Provider::Puppet::Manifest - version: '0.1.1' + version: '0.1.3' source: 'https://github.com/GoogleCloudPlatform/puppet-google-dns' homepage: 'https://github.com/GoogleCloudPlatform/puppet-google-dns' issues: 'https://github.com/GoogleCloudPlatform/puppet-google-dns/issues' @@ -97,6 +97,12 @@ style: exceptions: - Metrics/MethodLength changelog: + - !ruby/object:Provider::Config::Changelog + version: '0.1.3' + date: 2018-05-02T06:00:00-0700 + fixes: + - Changed quota_* fields to a Hash. All quota_ fields must now be + keys in a hash named `quota` - !ruby/object:Provider::Config::Changelog version: '0.1.2' date: 2018-02-14T06:00:00-0700 diff --git a/products/pubsub/ansible.yaml b/products/pubsub/ansible.yaml index f0a629555236..b64b46391da9 100644 --- a/products/pubsub/ansible.yaml +++ b/products/pubsub/ansible.yaml @@ -26,12 +26,16 @@ manifest: !ruby/object:Provider::Ansible::Manifest # This is where custom code would be defined eventually. overrides: !ruby/object:Provider::ResourceOverrides Topic: !ruby/object:Provider::Ansible::ResourceOverride - encoder: encode_request + transport: !ruby/object:Api::Resource::Transport + encoder: encode_request + decoder: decode_request provider_helpers: - 'products/pubsub/helpers/provider_topic.py' Subscription: !ruby/object:Provider::Ansible::ResourceOverride editable: false - encoder: encode_request + transport: !ruby/object:Api::Resource::Transport + encoder: encode_request + decoder: decode_request provider_helpers: - 'products/pubsub/helpers/provider_subscription.py' examples: !ruby/object:Api::Resource::HashArray diff --git a/provider/ansible/common~compile.yaml b/provider/ansible/common~compile.yaml index 9fc0711dbe9a..7da9ee91128b 100644 --- a/provider/ansible/common~compile.yaml +++ b/provider/ansible/common~compile.yaml @@ -23,8 +23,13 @@ 'test/units/module_utils/gcp/test_gcp_utils.py': 'provider/ansible/test_gcp_utils.py' <% unless config.nil? -%> <% + # Overrides have not been assembled yet. + overrides = config.overrides + excludes = overrides.instance_variables + .select { |x| overrides.instance_variable_get(x).exclude } + .map { |x| x.to_s.tr(':@', '') } object_names = api.objects - .select { |o| !o.exclude } + .select { |o| !excludes.include?(o.name) } .map do |object| ["gcp_#{object.__product.prefix[1..-1]}", Google::StringUtils.underscore(object.name)].join('_') diff --git a/provider/ansible/documentation.rb b/provider/ansible/documentation.rb index 24af0c651540..9642acb7f221 100644 --- a/provider/ansible/documentation.rb +++ b/provider/ansible/documentation.rb @@ -135,8 +135,7 @@ def minimal_doc_block(prop, object, spaces) indent([ "required: #{prop.required ? 'true' : 'false'}", ('type: bool' if prop.is_a? Api::Type::Boolean), - ("aliases: [#{object.aliases[prop.name].join(', ')}]" \ - if object&.aliases&.keys&.include?(prop.name)), + ("aliases: [#{prop.aliases.join(', ')}]" if prop.aliases), (if prop.is_a? Api::Type::Enum [ 'choices:', diff --git a/provider/ansible/module.rb b/provider/ansible/module.rb index 00c400e9ce1e..62370848c044 100644 --- a/provider/ansible/module.rb +++ b/provider/ansible/module.rb @@ -55,11 +55,8 @@ def prop_options(prop, object, spaces) (choices_enum(prop, spaces) if prop.is_a? Api::Type::Enum), ("elements=#{quote_string(python_type(prop.item_type))}" \ if prop.is_a? Api::Type::Array), - (if object&.aliases&.keys&.include?(prop.name) - "aliases=[#{object.aliases[prop.name].map do |x| - quote_string(x) - end.join(', ')}]" - end) + ("aliases=[#{prop.aliases.map { |x| quote_string(x) }.join(', ')}]" \ + if prop.aliases) ].compact end # rubocop:enable Metrics/AbcSize diff --git a/provider/ansible/property_override.rb b/provider/ansible/property_override.rb index 1ee926014a36..143a22aebe85 100644 --- a/provider/ansible/property_override.rb +++ b/provider/ansible/property_override.rb @@ -20,11 +20,17 @@ module Ansible # Collection of fields allowed in the PropertyOverride section for # Ansible. All fields should be `attr_reader :` module OverrideFields + attr_reader :aliases end # Ansible-specific overrides to api.yaml. class PropertyOverride < Provider::PropertyOverride include OverrideFields + def validate + super + + check_optional_property :aliases, ::Array + end private diff --git a/provider/ansible/resource_override.rb b/provider/ansible/resource_override.rb index 50029d250f50..74f8cce45725 100644 --- a/provider/ansible/resource_override.rb +++ b/provider/ansible/resource_override.rb @@ -18,14 +18,9 @@ module Ansible # Ansible specific properties to be added to Api::Resource module OverrideProperties attr_reader :access_api_results - attr_reader :aliases attr_reader :collection attr_reader :create - attr_reader :custom_self_link - attr_reader :decoder attr_reader :delete - attr_reader :encoder - attr_reader :exclude attr_reader :editable attr_reader :hidden attr_reader :imports @@ -42,18 +37,21 @@ def validate super default_value_property :access_api_results, false - default_value_property :aliases, {} - default_value_property :decoder, false default_value_property :exclude, false default_value_property :editable, true default_value_property :imports, [] default_value_property :provider_helpers, [] check_property :access_api_results, :boolean - check_property :aliases, ::Hash + check_optional_property :collection, ::String + check_optional_property :create, ::String + check_optional_property :delete, ::String check_property :editable, :boolean - check_property :exclude, :boolean + check_optional_property :hidden, ::Array check_property :imports, ::Array + check_property :provider_helpers, ::Array + check_optional_property :update, ::String + check_optional_property :version_added, ::String end private diff --git a/provider/config.rb b/provider/config.rb index d8f0b1d84964..fe4ebb0daaca 100644 --- a/provider/config.rb +++ b/provider/config.rb @@ -188,8 +188,6 @@ def self.parse(cfg_file, api = nil) raise "Config #{cfg_file}(#{config.class}) is not a Provider::Config" \ unless config.class <= Provider::Config # Config must be validated so items are properly setup for next compile - config.default_overrides - config.spread_api config, api, [], '' unless api.nil? config.validate # Compile step #2: Now that we have the target class, compile with that # class features diff --git a/templates/ansible/resource.erb b/templates/ansible/resource.erb index 689a71de81b0..9db146423453 100644 --- a/templates/ansible/resource.erb +++ b/templates/ansible/resource.erb @@ -266,15 +266,8 @@ def resource_to_request(module): <% end # if object.kind? -%> <%= lines(indent(request_properties(properties_in_request), 4)) -%> } -<% - encoder_name = if object.encoder? - object.transport.encoder - else object.encoder - object.encoder - end --%> -<% if object.encoder? || object.encoder -%> - request = <%= encoder_name -%>(request, module) +<% if object.encoder? -%> + request = <%= object.transport.encoder -%>(request, module) <% end -%> return_vals = {} for k, v in request.items(): @@ -285,18 +278,10 @@ def resource_to_request(module): <%= lines(compile('templates/ansible/transport.erb'), 2) -%> -<% if object.custom_self_link.nil? -%> <%= lines(emit_link('self_link', self_link_url(object), object)) -%> -<% else # object.self_link.nil? -%> -<%= lines(emit_link('self_link', object.custom_self_link, object)) -%> -<% end # object.self_link.nil? -%> -<% if object.collection.nil? -%> <%= lines(emit_link('collection', collection_url(object), object)) -%> -<% else # object.collection.nil? -%> -<%= lines(emit_link('collection', object.collection, object)) -%> -<% end # object.collection.nil? -%> <%= @@ -317,15 +302,8 @@ def resource_to_request(module): except getattr(json.decoder, 'JSONDecodeError', ValueError) as inst: module.fail_json(msg="Invalid JSON response with error: %s" % inst) -<% - decoder_name = if object.decoder? - object.transport.decoder - else object.decoder - object.decoder - end --%> -<% if object.decoder? || object.decoder -%> - result = <%= decoder_name -%>(result, module) +<% if object.decoder? -%> + result = <%= object.transport.decoder -%>(result, module) <% end -%> if navigate_hash(result, ['error', 'errors']): @@ -341,15 +319,8 @@ def resource_to_request(module): def is_different(module, response): request = resource_to_request(module) response = response_to_hash(module, response) -<% - decoder_name = if object.decoder? - object.transport.decoder - else object.decoder - object.decoder - end --%> -<% if object.decoder? || object.decoder -%> - request = <%= decoder_name -%>(request, module) +<% if object.decoder? -%> + request = <%= object.transport.decoder -%>(request, module) <% end -%> # Remove all output-only from response. From f7f68b67fa7b83e1bf1bb10914eee5a949a7af05 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Fri, 4 May 2018 07:01:09 +0000 Subject: [PATCH 16/16] Changing Ansible commit Change-Id: Ida223aca0a6ddecc7918b6a3d82c117953fe9af8 --- build/ansible | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/ansible b/build/ansible index 9706abf68518..ab9b1c843533 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit 9706abf68518dc0f663f23f64475f2b270851ae4 +Subproject commit ab9b1c84353333ae6737a1e9c442daf2eb42002b