From cee98a6da8c7767e5d907eabbbefd159cfeb437b Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 15 Oct 2018 15:39:19 -0700 Subject: [PATCH 01/10] Add resource unwrapping for singular resource --- templates/inspec/singular_resource.erb | 64 +++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 4d69292f0932..b618619a3dbe 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -64,21 +64,29 @@ url = "'#{url}'" <% if object.self_link_query.nil? -%> def initialize(params) - @fetched = <%= method_call('fetch_resource', ['params', ("'#{object.kind}'" if object.kind?)], 2) %> + @fetched = fetch_resource(params) parse unless @fetched.nil? end <% else # object.self_link_query.nil? -%> def initialize(params) - @fetched = <%= method_call('fetch_wrapped_resource', ['params', ("'#{object.kind}'" if object.kind?), - "'#{object.self_link_query.kind}'", - "'#{object.self_link_query.items}'"], 2) %> + @fetched = <%= method_call('fetch_wrapped_resource', + [ + 'params', + "'#{object.kind}'", + "'#{object.self_link_query.kind}'", + "'#{object.self_link_query.items}'" + ], 2) %> parse unless @fetched.nil? end <% end # object.self_link_query.nil? -%> + def fetch_resource(params) + get_request = inspec.backend.fetch(base, url, params) + end + def parse - <% - fetch_code = object.properties.reject(&:input).map do |prop| +<% + parse_code = object.properties.reject(&:input).map do |prop| name = prop.out_name if primitive?(prop) || resource_ref?(prop) @@ -92,10 +100,52 @@ url = "'#{url}'" assignment = "@#{name} = #{init}" end -%> -<%= lines(indent(fetch_code, 4)) -%> +<%= lines(indent(parse_code, 4)) -%> end def exists? !@fetched.nil? end + +<% unless object.self_link_query.nil? -%> + def fetch_wrapped_resource(params, kind, wrap_kind, wrap_path) + result = fetch_resource(params, wrap_kind) + return if result.nil? || !result.key?(wrap_path) + result = unwrap_resource(result[wrap_path], params) + return if result.nil? + raise "Incorrect result: #{result['kind']} (expected #{kind})" \ + unless result['kind'] == kind + result + end + + + def unwrap_resource(result, resource) + query_predicate = unwrap_resource_filter(resource) + matches = result.select do |entry| + query_predicate.all? do |k, v| + entry[k.id2name] == v + end + end + raise "More than 1 result found: #{matches}" if matches.size > 1 + return if matches.empty? + matches.first + end + +<% + urf_code = [ + '{', + indent_list( + Hash[object.identity.map { |i| [i, "resource[:#{property_out_name(i)}]"] }] + .map { |k, v| "#{k.out_name}: #{v}" }, 2 + ), + '}' + ] +-%> + def unwrap_resource_filter(resource) + self.class.unwrap_resource_filter(resource) + end + +<%= lines(indent(emit_method('self.unwrap_resource_filter', %w[resource], + urf_code, file_relative), 2), 1) -%> +<% end # object.self_link_query.nil? -%> end \ No newline at end of file From fcd777704629022309bd3b55c08a25efb1e21e10 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 13:50:13 -0700 Subject: [PATCH 02/10] Basic structure for singular InSpec resources (#573) Merged PR #573. --- .ci/magic-modules/create-pr.sh | 2 +- build/inspec | 2 +- build/terraform | 2 +- products/compute/inspec.yaml | 81 ++++++++++++++++++++++++++ provider/inspec.rb | 9 +++ templates/inspec/singular_resource.erb | 29 ++++++++- 6 files changed, 121 insertions(+), 4 deletions(-) diff --git a/.ci/magic-modules/create-pr.sh b/.ci/magic-modules/create-pr.sh index 041f98dcb2a2..885492e1d0c9 100755 --- a/.ci/magic-modules/create-pr.sh +++ b/.ci/magic-modules/create-pr.sh @@ -82,7 +82,7 @@ if [ "$BRANCH_NAME" = "$ORIGINAL_PR_BRANCH" ]; then fi git checkout -b "$BRANCH_NAME" - if INSPEC_PR=$(hub pull-request -b "$INSPEC_REPO_USER/inspec:master" -F ./downstream_body); then + if INSPEC_PR=$(hub pull-request -b "$INSPEC_REPO_USER/inspec-gcp:master" -F ./downstream_body); then DEPENDENCIES="${DEPENDENCIES}depends: $INSPEC_PR ${NEWLINE}" else echo "InSpec - did not generate a PR." diff --git a/build/inspec b/build/inspec index 257d3f760307..f359ebbf01dc 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 257d3f760307559938568aeed74261df5cde4b87 +Subproject commit f359ebbf01dc1294dc5338ad2dc380a888a14563 diff --git a/build/terraform b/build/terraform index 56f6530712f9..d622ec466e68 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 56f6530712f9cbfa5db40e9238d35703931fa951 +Subproject commit d622ec466e688b09c601d34f5ce1c7e5796dadf3 diff --git a/products/compute/inspec.yaml b/products/compute/inspec.yaml index 6523b93ef4ab..bcd603cee34d 100644 --- a/products/compute/inspec.yaml +++ b/products/compute/inspec.yaml @@ -19,6 +19,87 @@ manifest: !ruby/object:Provider::Inspec::Manifest summary: 'InSpec resources for verifying GCP infrastructure' description: | InSpec resources for verifying GCP infrastructure +overrides: !ruby/object:Provider::ResourceOverrides + Address: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Autoscaler: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + BackendBucket: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + BackendService: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Disk: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + DiskType: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Firewall: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + ForwardingRule: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + GlobalAddress: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + GlobalForwardingRule: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + HealthCheck: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + HttpHealthCheck: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + HttpsHealthCheck: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Image: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Instance: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + InstanceGroup: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + InstanceGroupManager: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + InstanceTemplate: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + InterconnectAttachment: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + License: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + MachineType: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Network: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Region: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + RegionAutoscaler: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + RegionDisk: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + RegionDiskType: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Route: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Router: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Snapshot: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + SslCertificate: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + SslPolicy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Subnetwork: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetHttpProxy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetHttpsProxy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetPool: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetTcpProxy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetVpnGateway: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetSslProxy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + UrlMap: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + VpnTunnel: !ruby/object:Provider::Chef::ResourceOverride + exclude: true files: !ruby/object:Provider::Config::Files style: functions: diff --git a/provider/inspec.rb b/provider/inspec.rb index 7f66da907f30..69439d7b94de 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -21,6 +21,7 @@ module Provider # Code generator for Example Cookbooks that manage Google Cloud Platform # resources. class Inspec < Provider::Core + include Google::RubyUtils # Settings for the provider class Config < Provider::Config attr_reader :manifest @@ -53,6 +54,14 @@ def generate_resource(data) ) end + # Returns the url that this object can be retrieved from + # based off of the self link + def url(object) + url = object.self_link_url[1] + return url.join('') if url.is_a?(Array) + url.split("\n").join('') + end + # TODO? def generate_resource_tests(data) end diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 3628efec92c1..0858313284a5 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -14,4 +14,31 @@ -%> <%= compile 'templates/license.erb' -%> -<%= lines(autogen_notice :ruby) -%> \ No newline at end of file +<%= lines(autogen_notice :ruby) -%> + +# A provider to manage <%= @api.name -%> resources. +class <%= object.name -%> < Inspec.resource(1) + + name 'google_<%= product_ns.downcase -%>_<%= object.name.downcase -%>' + desc '<%= object.name -%>' + supports platform: 'gcp-mm' + +<% object.properties.each do |prop| -%> + <%= "attr_reader :#{prop.out_name}" -%> + +<% end -%> + def base + '<%= object.self_link_url[0].join %>' + end + + def url + '<%= url(object) %>' + end + + # TODO + def parse end + + def exists? + !@fetched.nil? + end +end \ No newline at end of file From ac73051144bcd39361e72a1f01b0b9afb4bbd6e2 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 14:07:49 -0700 Subject: [PATCH 03/10] Backporting things --- provider/inspec.rb | 29 ++++++++++++++++++++++++-- templates/inspec/singular_resource.erb | 14 ------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index ac6e7ab631a8..c0b60578874e 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -102,7 +102,10 @@ def emit_nested_object_overrides(data) end def primitive? (property) - return property.is_a?(::Api::Type::Primitive) || (property.is_a?(Api::Type::Array) && !property.item_type.is_a?(::Api::Type::NestedObject)) + is_primitive = property.is_a?(::Api::Type::Primitive) + is_primitive_array = (property.is_a?(Api::Type::Array)\ + && !property.item_type.is_a?(::Api::Type::NestedObject)) + is_primitive || is_primitive_array end def resource_ref? (property) @@ -119,10 +122,32 @@ def nested_object? (property) def generate_requires(properties, requires = []) nested_props = properties.select{ |type| nested_object?(type) } - requires.concat(properties.reject{ |type| primitive?(type) || resource_ref?(type) || nested_object?(type) }.collect(&:requires)) + requires.concat(properties.reject{ |type| no_requires?(type) }\ + .collect{|type| easy_requires(type)}) requires.concat(nested_props.map{|nested_prop| generate_requires(nested_prop.properties) } ) requires.concat(nested_props.map{|nested_prop| nested_prop.property_file }) requires end + + def no_requires?(type) + primitive?(type) || resource_ref?(type) || nested_object?(type) + end + + def easy_requires(type) + if typed_array?(type) + return File.join( + 'google', + 'compute', + 'property', + [type.__resource.name.downcase, type.item_type.name.underscore].join('_') + ) + end + File.join( + 'google', + 'compute', + 'property', + type.property_file + ).downcase + end end end diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 1a8056d7daa3..20825a4d5dbf 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -46,20 +46,6 @@ class <%= object.name -%> < Inspec.resource(1) '<%= url(object) %>' end -<% if object.self_link_query.nil? -%> - def initialize(params) - @fetched = <%= method_call('fetch_resource', ['params', ("'#{object.kind}'" if object.kind?)], 2) %> - parse unless @fetched.nil? - end -<% else # object.self_link_query.nil? -%> - def initialize(params) - @fetched = <%= method_call('fetch_wrapped_resource', ['params', ("'#{object.kind}'" if object.kind?), - "'#{object.self_link_query.kind}'", - "'#{object.self_link_query.items}'"], 2) %> - parse unless @fetched.nil? - end -<% end # object.self_link_query.nil? -%> - def parse <% fetch_code = object.properties.map do |prop| From 38e652f07dffb82ac51a103244c8d777ae949710 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 14:11:40 -0700 Subject: [PATCH 04/10] Spacing for rubocop --- provider/inspec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index c0b60578874e..f7e3f9e6156b 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -123,9 +123,9 @@ def nested_object? (property) def generate_requires(properties, requires = []) nested_props = properties.select{ |type| nested_object?(type) } requires.concat(properties.reject{ |type| no_requires?(type) }\ - .collect{|type| easy_requires(type)}) - requires.concat(nested_props.map{|nested_prop| generate_requires(nested_prop.properties) } ) - requires.concat(nested_props.map{|nested_prop| nested_prop.property_file }) + .collect { |type| easy_requires(type) } ) + requires.concat(nested_props.map { |nested_prop| generate_requires(nested_prop.properties) } ) + requires.concat(nested_props.map { |nested_prop| nested_prop.property_file } ) requires end From 06c73d95ae4f86a45a790de2fb845d34e2bb15ce Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 14:15:51 -0700 Subject: [PATCH 05/10] Rubocop --- provider/inspec.rb | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index f7e3f9e6156b..5629b19501e7 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -59,6 +59,7 @@ def generate_resource(data) def url(object) url = object.self_link_url[1] return url.join('') if url.is_a?(Array) + url.split("\n").join('') end @@ -101,31 +102,31 @@ def emit_nested_object_overrides(data) ) end - def primitive? (property) + def primitive?(property) is_primitive = property.is_a?(::Api::Type::Primitive) is_primitive_array = (property.is_a?(Api::Type::Array)\ && !property.item_type.is_a?(::Api::Type::NestedObject)) is_primitive || is_primitive_array end - def resource_ref? (property) - return property.is_a?(::Api::Type::ResourceRef) + def resource_ref?(property) + property.is_a?(::Api::Type::ResourceRef) end - def typed_array? (property) - return property.is_a?(::Api::Type::Array) + def typed_array?(property) + property.is_a?(::Api::Type::Array) end - def nested_object? (property) - return property.is_a?(::Api::Type::NestedObject) + def nested_object?(property) + property.is_a?(::Api::Type::NestedObject) end def generate_requires(properties, requires = []) - nested_props = properties.select{ |type| nested_object?(type) } - requires.concat(properties.reject{ |type| no_requires?(type) }\ - .collect { |type| easy_requires(type) } ) - requires.concat(nested_props.map { |nested_prop| generate_requires(nested_prop.properties) } ) - requires.concat(nested_props.map { |nested_prop| nested_prop.property_file } ) + nested_props = properties.select { |type| nested_object?(type) } + requires.concat(properties.reject { |type| no_requires?(type) }\ + .collect { |type| easy_requires(type) }) + requires.concat(nested_props.map { |nested_prop| generate_requires(nested_prop.properties) }) + requires.concat(nested_props.map(&:property_file)) requires end From 957ad0cb052fd91d6dfa00364527a9ffac632ca6 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Thu, 18 Oct 2018 13:38:09 -0700 Subject: [PATCH 06/10] Requires improvements --- provider/inspec.rb | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 5629b19501e7..27605d75a08c 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -59,7 +59,6 @@ def generate_resource(data) def url(object) url = object.self_link_url[1] return url.join('') if url.is_a?(Array) - url.split("\n").join('') end @@ -121,17 +120,18 @@ def nested_object?(property) property.is_a?(::Api::Type::NestedObject) end - def generate_requires(properties, requires = []) + def generate_requires(properties) + requires = [] nested_props = properties.select { |type| nested_object?(type) } requires.concat(properties.reject { |type| no_requires?(type) }\ .collect { |type| easy_requires(type) }) requires.concat(nested_props.map { |nested_prop| generate_requires(nested_prop.properties) }) - requires.concat(nested_props.map(&:property_file)) + requires.concat(nested_props.map { |nested_object| nested_object_requires(nested_object) } ) requires end def no_requires?(type) - primitive?(type) || resource_ref?(type) || nested_object?(type) + primitive?(type) || resource_ref?(type) || nested_object?(type) || type.is_a?(::Api::Type::NameValues) end def easy_requires(type) @@ -143,11 +143,20 @@ def easy_requires(type) [type.__resource.name.downcase, type.item_type.name.underscore].join('_') ) end - File.join( + return File.join( 'google', 'compute', 'property', - type.property_file + type.name.underscore + ) + end + + def nested_object_requires(nested_object_type) + File.join( + 'google', + nested_object_type.__resource.__product.prefix[1..-1], + 'property', + [nested_object_type.__resource.name, nested_object_type.name.underscore].join('_') ).downcase end end From a59fe2f397646a33c287411d690ea14172d3369c Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Thu, 18 Oct 2018 13:53:43 -0700 Subject: [PATCH 07/10] Improve requires generation --- provider/inspec.rb | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 27605d75a08c..830fed2711e9 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -101,17 +101,23 @@ def emit_nested_object_overrides(data) ) end - def primitive?(property) - is_primitive = property.is_a?(::Api::Type::Primitive) - is_primitive_array = (property.is_a?(Api::Type::Array)\ - && !property.item_type.is_a?(::Api::Type::NestedObject)) - is_primitive || is_primitive_array + # Figuring out if a property is a primitive ruby type is a hassle. But it is important + # Fingerprints are strings, NameValues are hashes, and arrays of primitives are arrays + # Arrays of NestedObjects need to have their contents parsed and returned in an array + def primitive? (property) + array_primitive = (property.is_a?(Api::Type::Array) && !property.item_type.is_a?(::Api::Type::NestedObject)) + property.is_a?(::Api::Type::Primitive)\ + || array_primitive\ + || property.is_a?(::Api::Type::NameValues)\ + || property.is_a?(::Api::Type::Fingerprint) end + # ResourceRefs are strings def resource_ref?(property) property.is_a?(::Api::Type::ResourceRef) end + # Arrays need special requires statements def typed_array?(property) property.is_a?(::Api::Type::Array) end @@ -123,38 +129,32 @@ def nested_object?(property) def generate_requires(properties) requires = [] nested_props = properties.select { |type| nested_object?(type) } - requires.concat(properties.reject { |type| no_requires?(type) }\ - .collect { |type| easy_requires(type) }) + requires.concat(properties.select { |type| typed_array?(type) && nested_object?(type.item_type) }\ + .collect { |type| array_requires(type) }) requires.concat(nested_props.map { |nested_prop| generate_requires(nested_prop.properties) }) requires.concat(nested_props.map { |nested_object| nested_object_requires(nested_object) } ) requires end + # Primitives don't need requires statements. + # Nested objects will have their requires statements handled separately def no_requires?(type) - primitive?(type) || resource_ref?(type) || nested_object?(type) || type.is_a?(::Api::Type::NameValues) + primitive?(type) || resource_ref?(type) || nested_object?(type) end - def easy_requires(type) - if typed_array?(type) - return File.join( - 'google', - 'compute', - 'property', - [type.__resource.name.downcase, type.item_type.name.underscore].join('_') - ) - end + def array_requires(type) return File.join( 'google', - 'compute', + type.__resource.__product.prefix[1..-1], 'property', - type.name.underscore + [type.__resource.name.downcase, type.item_type.name.underscore].join('_') ) end def nested_object_requires(nested_object_type) File.join( 'google', - nested_object_type.__resource.__product.prefix[1..-1], + nested_object_type.__resource.__product.prefix[1..-1], 'property', [nested_object_type.__resource.name, nested_object_type.name.underscore].join('_') ).downcase From f5b82dd18c8233fdfe16d00a62e18b32af5754aa Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Thu, 18 Oct 2018 14:41:28 -0700 Subject: [PATCH 08/10] Rubocop --- provider/inspec.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 830fed2711e9..0507bee40db0 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -104,8 +104,9 @@ def emit_nested_object_overrides(data) # Figuring out if a property is a primitive ruby type is a hassle. But it is important # Fingerprints are strings, NameValues are hashes, and arrays of primitives are arrays # Arrays of NestedObjects need to have their contents parsed and returned in an array - def primitive? (property) - array_primitive = (property.is_a?(Api::Type::Array) && !property.item_type.is_a?(::Api::Type::NestedObject)) + def primitive?(property) + array_primitive = (property.is_a?(Api::Type::Array)\ + && !property.item_type.is_a?(::Api::Type::NestedObject)) property.is_a?(::Api::Type::Primitive)\ || array_primitive\ || property.is_a?(::Api::Type::NameValues)\ @@ -127,13 +128,15 @@ def nested_object?(property) end def generate_requires(properties) - requires = [] nested_props = properties.select { |type| nested_object?(type) } - requires.concat(properties.select { |type| typed_array?(type) && nested_object?(type.item_type) }\ - .collect { |type| array_requires(type) }) - requires.concat(nested_props.map { |nested_prop| generate_requires(nested_prop.properties) }) - requires.concat(nested_props.map { |nested_object| nested_object_requires(nested_object) } ) - requires + nested_object_arrays = properties.select\ + { |type| typed_array?(type) && nested_object?(type.item_type) } + nested_array_requires = nested_object_arrays.collect { |type| array_requires(type) } + nested_prop_requires = nested_props.map\ + { |nested_prop| generate_requires(nested_prop.properties) } + nested_object_requires = nested_props.map\ + { |nested_object| nested_object_requires(nested_object) } + nested_object_requires + nested_prop_requires + nested_array_requires end # Primitives don't need requires statements. @@ -143,7 +146,7 @@ def no_requires?(type) end def array_requires(type) - return File.join( + File.join( 'google', type.__resource.__product.prefix[1..-1], 'property', @@ -153,7 +156,7 @@ def array_requires(type) def nested_object_requires(nested_object_type) File.join( - 'google', + 'google', nested_object_type.__resource.__product.prefix[1..-1], 'property', [nested_object_type.__resource.name, nested_object_type.name.underscore].join('_') From 6688934a64a91cdc311a13a0dff9905851cd45ff Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 19 Oct 2018 08:08:09 -0700 Subject: [PATCH 09/10] Refactoring, comments --- provider/inspec.rb | 23 +++++++++-------------- templates/inspec/singular_resource.erb | 4 ++-- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 0507bee40db0..0b01ed0a2aa5 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -104,34 +104,35 @@ def emit_nested_object_overrides(data) # Figuring out if a property is a primitive ruby type is a hassle. But it is important # Fingerprints are strings, NameValues are hashes, and arrays of primitives are arrays # Arrays of NestedObjects need to have their contents parsed and returned in an array + # ResourceRefs are strings def primitive?(property) array_primitive = (property.is_a?(Api::Type::Array)\ && !property.item_type.is_a?(::Api::Type::NestedObject)) property.is_a?(::Api::Type::Primitive)\ || array_primitive\ || property.is_a?(::Api::Type::NameValues)\ - || property.is_a?(::Api::Type::Fingerprint) - end - - # ResourceRefs are strings - def resource_ref?(property) - property.is_a?(::Api::Type::ResourceRef) + || property.is_a?(::Api::Type::Fingerprint)\ + || property.is_a?(::Api::Type::ResourceRef) end - # Arrays need special requires statements + # Arrays of nested objects need special requires statements def typed_array?(property) - property.is_a?(::Api::Type::Array) + property.is_a?(::Api::Type::Array) && nested_object?(property.item_type) end def nested_object?(property) property.is_a?(::Api::Type::NestedObject) end + # Only arrays of nested objects and nested object properties need require statements + # for InSpec. Primitives are all handled natively def generate_requires(properties) nested_props = properties.select { |type| nested_object?(type) } nested_object_arrays = properties.select\ { |type| typed_array?(type) && nested_object?(type.item_type) } nested_array_requires = nested_object_arrays.collect { |type| array_requires(type) } + # Need to include requires statements for the requirements of a nested object + # TODO is this needed? Not sure how ruby works so well nested_prop_requires = nested_props.map\ { |nested_prop| generate_requires(nested_prop.properties) } nested_object_requires = nested_props.map\ @@ -139,12 +140,6 @@ def generate_requires(properties) nested_object_requires + nested_prop_requires + nested_array_requires end - # Primitives don't need requires statements. - # Nested objects will have their requires statements handled separately - def no_requires?(type) - primitive?(type) || resource_ref?(type) || nested_object?(type) - end - def array_requires(type) File.join( 'google', diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 20825a4d5dbf..dd01389cda92 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -47,11 +47,11 @@ class <%= object.name -%> < Inspec.resource(1) end def parse - <% +<% fetch_code = object.properties.map do |prop| name = prop.out_name - if primitive?(prop) || resource_ref?(prop) + if primitive?(prop) init = "@fetched['#{prop.api_name}']" elsif typed_array?(prop) init = "#{prop.property_type}.parse(@fetched['#{prop.api_name}'])" From 98c08323f94a3f711ecc0620bf0498a2588f81a1 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 19 Oct 2018 13:19:57 -0700 Subject: [PATCH 10/10] Remove extra code for sql --- templates/inspec/singular_resource.erb | 62 +++----------------------- 1 file changed, 7 insertions(+), 55 deletions(-) diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 6424f77b6e35..f8753b55a32b 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -47,25 +47,19 @@ class <%= object.name -%> < Inspec.resource(1) end <% if object.self_link_query.nil? -%> - def initialize(params) + def initialize(params) @fetched = fetch_resource(params) parse unless @fetched.nil? - end -<% else # object.self_link_query.nil? -%> - def initialize(params) - @fetched = <%= method_call('fetch_wrapped_resource', - [ - 'params', - "'#{object.kind}'", - "'#{object.self_link_query.kind}'", - "'#{object.self_link_query.items}'" - ], 2) %> - parse unless @fetched.nil? end +<% else # object.self_link_query.nil? -%> + # TODO(slevenick) for other products + def initialize(params) + raise 'Not implemented' + end <% end # object.self_link_query.nil? -%> def fetch_resource(params) - get_request = inspec.backend.fetch(base, url, params) + get_request = inspec.backend.fetch(base, url, params) end def parse @@ -90,46 +84,4 @@ class <%= object.name -%> < Inspec.resource(1) def exists? !@fetched.nil? end - -<% unless object.self_link_query.nil? -%> - def fetch_wrapped_resource(params, kind, wrap_kind, wrap_path) - result = fetch_resource(params, wrap_kind) - return if result.nil? || !result.key?(wrap_path) - result = unwrap_resource(result[wrap_path], params) - return if result.nil? - raise "Incorrect result: #{result['kind']} (expected #{kind})" \ - unless result['kind'] == kind - result - end - - - def unwrap_resource(result, resource) - query_predicate = unwrap_resource_filter(resource) - matches = result.select do |entry| - query_predicate.all? do |k, v| - entry[k.id2name] == v - end - end - raise "More than 1 result found: #{matches}" if matches.size > 1 - return if matches.empty? - matches.first - end - -<% - urf_code = [ - '{', - indent_list( - Hash[object.identity.map { |i| [i, "resource[:#{property_out_name(i)}]"] }] - .map { |k, v| "#{k.out_name}: #{v}" }, 2 - ), - '}' - ] --%> - def unwrap_resource_filter(resource) - self.class.unwrap_resource_filter(resource) - end - -<%= lines(indent(emit_method('self.unwrap_resource_filter', %w[resource], - urf_code, file_relative), 2), 1) -%> -<% end # object.self_link_query.nil? -%> end \ No newline at end of file