From df005d9df382c9d0a34c06d2a05580ed6b8b7daa Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 12 Oct 2018 13:08:07 -0700 Subject: [PATCH 01/49] Adding basic singular resource code --- provider/inspec.rb | 1 + templates/inspec/singular_resource.erb | 59 +++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 7f66da907f30..5c309a87d08a 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 diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 3628efec92c1..8251616a6796 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -14,4 +14,61 @@ -%> <%= 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. +<%= lines(indent( + emit_rubocop(binding, :class, + ['Google', @api.prefix.upcase, object.name].join('::'), + :disabled), + 4)) -%> +class <%= object.name -%> < Inspec.resource(1) + + name 'google_<%= product_ns.downcase -%>_<%= object.name.downcase -%>' + desc '<%= object.name -%>' + supports platform: 'gcp-mm' + +<% object.properties.reject(&:input).each do |prop| -%> + <%= "attr_reader :#{prop.out_name}" -%> + +<% end -%> + +<% +base = "'#{object.self_link_url[0].join}'" +url = object.self_link_url[1] +if url.is_a?(Array) + url = url.join('') +else + url = url.split("\n").join('') +end +url = "'#{url}'" +-%> + def base + <%= base %> + end + + def url + <%= url %> + 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? -%> + + # TODO + def parse end + + def exists? + !@fetched.nil? + end +end \ No newline at end of file From 186718936990deadc267b2ab78848a7370a7eef8 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 12 Oct 2018 13:08:07 -0700 Subject: [PATCH 02/49] Adding basic singular resource code --- provider/inspec.rb | 1 + templates/inspec/singular_resource.erb | 59 +++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 7f66da907f30..5c309a87d08a 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 diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 3628efec92c1..8251616a6796 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -14,4 +14,61 @@ -%> <%= 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. +<%= lines(indent( + emit_rubocop(binding, :class, + ['Google', @api.prefix.upcase, object.name].join('::'), + :disabled), + 4)) -%> +class <%= object.name -%> < Inspec.resource(1) + + name 'google_<%= product_ns.downcase -%>_<%= object.name.downcase -%>' + desc '<%= object.name -%>' + supports platform: 'gcp-mm' + +<% object.properties.reject(&:input).each do |prop| -%> + <%= "attr_reader :#{prop.out_name}" -%> + +<% end -%> + +<% +base = "'#{object.self_link_url[0].join}'" +url = object.self_link_url[1] +if url.is_a?(Array) + url = url.join('') +else + url = url.split("\n").join('') +end +url = "'#{url}'" +-%> + def base + <%= base %> + end + + def url + <%= url %> + 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? -%> + + # TODO + def parse end + + def exists? + !@fetched.nil? + end +end \ No newline at end of file From e7b703edc0f1c262cfd7f25f4a186aed5dd04102 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 12 Oct 2018 14:01:37 -0700 Subject: [PATCH 03/49] Add nested objects and parsing logic --- provider/inspec.rb | 40 ++++++++++++++++++++++++-- templates/inspec/nested_object.erb | 40 ++++++++++++++++++++++++++ templates/inspec/singular_resource.erb | 19 +++++++++++- 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 templates/inspec/nested_object.erb diff --git a/provider/inspec.rb b/provider/inspec.rb index 5c309a87d08a..3bd7dba091f8 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -65,8 +65,44 @@ def generate_typed_array(data, prop) end def emit_resourceref_object(data) end - def emit_nested_object(data) end - def generate_network_datas(data, object) end + + def emit_nested_object(data) + target = if data[:emit_array] + data[:property].item_type.property_file + else + data[:property].property_file + end + { + source: File.join('templates', 'inspec', 'nested_object.erb'), + target: "libraries/#{target}.rb", + overrides: emit_nested_object_overrides(data) + } + end + + def emit_nested_object_overrides(data) + data.clone.merge( + api_name: data[:api_name].camelize(:upper), + object_type: data[:obj_name].camelize(:upper), + product_ns: data[:product_name].camelize(:upper), + class_name: if data[:emit_array] + data[:property].item_type.property_class.last + else + data[:property].property_class.last + end + ) + 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)) + end + + def resource_ref? (property) + return property.is_a?(::Api::Type::ResourceRef) + end + + def typed_array? (property) + return property.is_a?(::Api::Type::Array) + end end end diff --git a/templates/inspec/nested_object.erb b/templates/inspec/nested_object.erb new file mode 100644 index 000000000000..a41299cd137b --- /dev/null +++ b/templates/inspec/nested_object.erb @@ -0,0 +1,40 @@ +<%# The license inside this block applies to this file. +# 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. +-%> +<%= compile('templates/license.erb') -%> + +<%= lines(autogen_notice :ruby) -%> + +module Google + module <%= product_ns %> + module Property + class <%= class_name -%> + + def initialize(args = nil) + return nil + end + end + +<% if emit_array -%> + class <%= class_name %>Array + def self.parse(value) + return if value.nil? + return <%= class_name %>.new(value) unless value.is_a?(::Array) + value.map { |v| <%= class_name %>.new(v) } + end + end +<% end #if emit_array -%> + end + end +end \ No newline at end of file diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 8251616a6796..1495fa55c5c6 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -66,7 +66,24 @@ url = "'#{url}'" <% end # object.self_link_query.nil? -%> # TODO - def parse end + def parse + <% + fetch_code = object.properties.reject(&:input).map do |prop| + name = prop.out_name + + if primitive?(prop) || resource_ref?(prop) + init = "@fetched['#{prop.api_name}']" + elsif typed_array?(prop) + init = "#{prop.property_type}.parse(@fetched['#{prop.api_name}'])" + else + init = "#{prop.property_type}.new(@fetched['#{prop.api_name}'])" + end + + assignment = "@#{name} = #{init}" + end +-%> +<%= lines(indent(fetch_code, 4)) -%> + end def exists? !@fetched.nil? From d8d6b8534bd7ef959e9ebd9a459616a3c61e5639 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 12 Oct 2018 14:04:47 -0700 Subject: [PATCH 04/49] Remove todo --- templates/inspec/singular_resource.erb | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 1495fa55c5c6..f248f8181ec3 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -65,7 +65,6 @@ url = "'#{url}'" end <% end # object.self_link_query.nil? -%> - # TODO def parse <% fetch_code = object.properties.reject(&:input).map do |prop| From b9815968dcd917db3054b3e02cbd091fab464691 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 12 Oct 2018 14:25:05 -0700 Subject: [PATCH 05/49] Add nested constructor --- templates/inspec/nested_object.erb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/templates/inspec/nested_object.erb b/templates/inspec/nested_object.erb index a41299cd137b..9e4bc5b78c8d 100644 --- a/templates/inspec/nested_object.erb +++ b/templates/inspec/nested_object.erb @@ -22,10 +22,23 @@ module Google class <%= class_name -%> def initialize(args = nil) - return nil + return nil if args.nil? +<% nested_properties.each do |prop| -%> +<% + if primitive?(prop) + init = "args['#{prop.api_name}']" + elsif typed_array?(prop) + init = "#{prop.property_type}.parse(args['#{prop.api_name}'])" + else + init = "#{prop.property_type}.new(args['#{prop.api_name}'])" + end + parse_code = "@#{prop.out_name} = #{init}" +-%> +<%= lines(indent(parse_code, 10)) -%> +<% end # nested_properties.each -%> end end - + <% if emit_array -%> class <%= class_name %>Array def self.parse(value) From 1993bf5e771b221cb4f40fbb3c9b6abfbf5723ec Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 12 Oct 2018 14:26:39 -0700 Subject: [PATCH 06/49] Add attr readers for nested object --- templates/inspec/nested_object.erb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/templates/inspec/nested_object.erb b/templates/inspec/nested_object.erb index 9e4bc5b78c8d..ed5e4280b978 100644 --- a/templates/inspec/nested_object.erb +++ b/templates/inspec/nested_object.erb @@ -21,6 +21,13 @@ module Google module Property class <%= class_name -%> +<% if !nested_properties.empty? -%> +<% nested_properties.each do |prop| -%> + attr_reader :<%= prop.out_name %> +<% end # nested_properties.each -%> + +<% end # if !nested_properties.empty? -%> + def initialize(args = nil) return nil if args.nil? <% nested_properties.each do |prop| -%> From ec005438bcd97535aa2da3b046801423218f0d60 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 12 Oct 2018 14:46:56 -0700 Subject: [PATCH 07/49] Add basic requires --- provider/inspec.rb | 12 ++++++++++++ templates/inspec/singular_resource.erb | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/provider/inspec.rb b/provider/inspec.rb index 3bd7dba091f8..dbf08a36d308 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -104,5 +104,17 @@ def resource_ref? (property) def typed_array? (property) return property.is_a?(::Api::Type::Array) end + + def nested_object? (property) + return 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| primitive?(type) || resource_ref?(type) || nested_object?(type) }.collect(&:requires)) + 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 end end diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index f248f8181ec3..4d69292f0932 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -16,6 +16,17 @@ <%= lines(autogen_notice :ruby) -%> +<% + require 'google/string_utils' + + inside_indent = 8 + + requires = generate_requires(object.all_user_properties) + requires << 'inspec/resource' + requires << 'google/hash_utils' +-%> +<%= lines(emit_requires(requires)) -%> + # A provider to manage <%= @api.name -%> resources. <%= lines(indent( emit_rubocop(binding, :class, From cee98a6da8c7767e5d907eabbbefd159cfeb437b Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 15 Oct 2018 15:39:19 -0700 Subject: [PATCH 08/49] 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 6dafb256757449e53b91d6c6a10ef6ad2a3de6f4 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 16 Oct 2018 12:46:49 -0700 Subject: [PATCH 09/49] Adding list link, plural generation --- api/resource.rb | 6 + products/compute/api.yaml | 205 +++++++++++++++++++++++++++ provider/inspec.rb | 11 ++ templates/inspec/plural_resource.erb | 74 +++++++++- 4 files changed, 295 insertions(+), 1 deletion(-) diff --git a/api/resource.rb b/api/resource.rb index 6ce933b2a936..5af01cef7564 100644 --- a/api/resource.rb +++ b/api/resource.rb @@ -42,6 +42,12 @@ module Properties # GET/DELETE requests only. In particular, this is often used # to add query parameters. attr_reader :self_link_query + # URL to be used to retrieve a list of these resources (for InSpec) + attr_reader :list_url + # This is the type of response from the list URL. It contains + # the name of the list of items within the json, as well as the + # type that this list should be + attr_reader :list_url_response # This is an array with items that uniquely identify the resource. # This is useful in case an API returns a list result and we need # to fetch the particular resource we're interested in from that diff --git a/products/compute/api.yaml b/products/compute/api.yaml index 337381d54768..d0ba6132ceb8 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -31,6 +31,11 @@ objects: name: 'Address' kind: 'compute#address' base_url: projects/{{project}}/regions/{{region}}/addresses + list_url: | + projects/{{project}}/regions/{{region}}/addresses + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#addressList' + items: 'items' exports: - !ruby/object:Api::Type::FetchedExternal name: address @@ -151,6 +156,11 @@ objects: kind: 'compute#autoscaler' base_url: projects/{{project}}/zones/{{zone}}/autoscalers update_url: projects/{{project}}/zones/{{zone}}/autoscalers?autoscaler={{name}} + list_url: | + projects/{{project}}/zone/{{zone}}/autoscalers + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#autoscalerList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: selfLink @@ -187,6 +197,11 @@ objects: name: 'BackendBucket' kind: 'compute#backendBucket' base_url: projects/{{project}}/global/backendBuckets + list_url: | + projects/{{project}}/global/backendBuckets + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#backendBucketLists' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -240,6 +255,11 @@ objects: name: 'BackendService' kind: 'compute#backendService' base_url: projects/{{project}}/global/backendServices + list_url: | + projects/{{project}}/global/backendServices + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#backendServicesList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -539,6 +559,11 @@ objects: name: 'DiskType' kind: 'compute#diskType' base_url: projects/{{project}}/zones/{{zone}}/diskTypes + list_url: | + projects/{{project}}/zones/{{zone}}/diskTypes + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#diskTypeList' + items: 'items' # TODO(nelsonjr): Search all documentation for references of using URL (like # the description below) and replace with the proper reference to the # corresponding type. @@ -572,6 +597,11 @@ objects: kind: 'compute#disk' input: true base_url: projects/{{project}}/zones/{{zone}}/disks + list_url: | + projects/{{project}}/zones/{{zone}}/disks + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#diskList' + items: 'items' exports: - 'name' - !ruby/object:Api::Type::SelfLink @@ -681,6 +711,11 @@ objects: name: 'Firewall' kind: 'compute#firewall' base_url: projects/{{project}}/global/firewalls + list_url: | + projects/{{project}}/global/firewalls + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#firewallList' + items: 'items' update_verb: :PATCH exports: - !ruby/object:Api::Type::SelfLink @@ -917,6 +952,11 @@ objects: name: 'ForwardingRule' kind: 'compute#forwardingRule' base_url: projects/{{project}}/regions/{{region}}/forwardingRules + list_url: | + projects/{{project}}/regions/{{region}}/forwardingRules + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#forwardingRuleList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -1010,6 +1050,11 @@ objects: name: 'GlobalAddress' kind: 'compute#address' base_url: projects/{{project}}/global/addresses + list_url: | + projects/{{project}}/global/addresses + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#addressList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -1134,6 +1179,11 @@ objects: name: 'GlobalForwardingRule' kind: 'compute#forwardingRule' base_url: projects/{{project}}/global/forwardingRules + list_url: | + projects/{{project}}/global/forwardingRules + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#forwardingRulesList' + items: 'items' description: | Represents a GlobalForwardingRule resource. Global forwarding rules are used to forward traffic to the correct load balancer for HTTP load @@ -1168,6 +1218,11 @@ objects: name: 'HttpHealthCheck' kind: 'compute#httpHealthCheck' base_url: projects/{{project}}/global/httpHealthChecks + list_url: | + projects/{{project}}/global/httpHealthChecks + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#httpHealthCheckList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -1249,6 +1304,11 @@ objects: name: 'HttpsHealthCheck' kind: 'compute#httpsHealthCheck' base_url: projects/{{project}}/global/httpsHealthChecks + list_url: | + projects/{{project}}/global/httpsHealthChecks + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#httpsHealthCheckList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -1329,6 +1389,11 @@ objects: name: 'HealthCheck' kind: 'compute#healthCheck' base_url: projects/{{project}}/global/healthChecks + list_url: | + projects/{{project}}/global/healthChecks + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#healthCheckList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -1449,6 +1514,11 @@ objects: kind: 'compute#instanceTemplate' input: true base_url: projects/{{project}}/global/instanceTemplates + list_url: | + projects/{{project}}/global/instanceTemplates + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#instanceTemplatesListResponse' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -1530,6 +1600,11 @@ objects: name: 'License' kind: 'compute#license' base_url: /projects/{{project}}/global/licenses + list_url: | + /projects/{{project}}/global/licenses + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#TODO???' + items: 'items' readonly: true exports: - !ruby/object:Api::Type::SelfLink @@ -1555,6 +1630,11 @@ objects: name: 'Image' kind: 'compute#image' base_url: projects/{{project}}/global/images + list_url: | + projects/{{project}}/global/images + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#imageList' + items: 'items' description: | Represents an Image resource. @@ -1786,6 +1866,11 @@ objects: name: 'Instance' kind: 'compute#instance' base_url: projects/{{project}}/zones/{{zone}}/instances + list_url: | + projects/{{project}}/zones/{{zone}}/instances + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#instanceList' + items: 'items' input: true exports: - !ruby/object:Api::Type::SelfLink @@ -1892,6 +1977,11 @@ objects: name: 'InstanceGroup' kind: 'compute#instanceGroup' base_url: projects/{{project}}/zones/{{zone}}/instanceGroups + list_url: | + projects/{{project}}/zones/{{zone}}/instanceGroups + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#instanceGroupList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -1990,6 +2080,11 @@ objects: name: 'InstanceGroupManager' kind: 'compute#instanceGroupManager' base_url: projects/{{project}}/zones/{{zone}}/instanceGroupManagers + list_url: | + projects/{{project}}/zones/{{zone}}/instanceGroupManagers + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#instanceGroupManagerList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -2173,6 +2268,11 @@ objects: name: 'InterconnectAttachment' kind: 'compute#interconnectAttachment' base_url: 'projects/{{project}}/regions/{{region}}/interconnectAttachments' + list_url: | + projects/{{project}}/regions/{{region}}/interconnectAttachments + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#interconnectAttachementList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -2273,6 +2373,11 @@ objects: name: 'MachineType' kind: 'compute#machineType' base_url: projects/{{project}}/zones/{{zone}}/machineTypes + list_url: | + projects/{{project}}/zones/{{zone}}/machineTypes + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#machineTypeList' + items: 'items' exports: - 'name' - !ruby/object:Api::Type::SelfLink @@ -2383,6 +2488,11 @@ objects: name: 'Network' kind: 'compute#network' base_url: projects/{{project}}/global/networks + list_url: | + projects/{{project}}/global/networks + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#networkList' + items: 'items' update_verb: :PATCH exports: - !ruby/object:Api::Type::SelfLink @@ -2495,6 +2605,11 @@ objects: name: 'Region' kind: 'compute#region' base_url: projects/{{project}}/regions + list_url: | + projects/{{project}}/regions + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#regionList' + items: 'items' exports: - name - !ruby/object:Api::Type::SelfLink @@ -2573,6 +2688,11 @@ objects: kind: 'compute#autoscaler' base_url: projects/{{project}}/regions/{{region}}/autoscalers update_url: projects/{{project}}/regions/{{region}}/autoscalers?autoscaler={{name}} + list_url: | + projects/{{project}}/regions/{{region}}/autoscalers + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#autoscalerList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: selfLink @@ -2610,6 +2730,11 @@ objects: name: 'RegionDiskType' kind: 'compute#diskType' base_url: projects/{{project}}/regions/{{region}}/diskTypes + list_url: | + projects/{{project}}/regions/{{region}}/diskTypes + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#diskTypeList' + items: 'items' description: | Represents a regional DiskType resource. A DiskType resource represents the type of disk to use, such as a pd-ssd or pd-standard. To reference a @@ -2632,6 +2757,11 @@ objects: kind: 'compute#disk' input: true base_url: projects/{{project}}/regions/{{region}}/disks + list_url: | + projects/{{project}}/regions/{{region}}/disks + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#diskList' + items: 'items' exports: - 'name' - !ruby/object:Api::Type::SelfLink @@ -2698,6 +2828,11 @@ objects: name: 'Route' kind: 'compute#route' base_url: projects/{{project}}/global/routes + list_url: | + projects/{{project}}/global/routes + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#routeList' + items: 'items' input: true exports: - !ruby/object:Api::Type::SelfLink @@ -2822,6 +2957,11 @@ objects: name: 'Router' kind: 'compute#router' base_url: projects/{{project}}/regions/{{region}}/routers + list_url: | + projects/{{project}}/regions/{{region}}/routers + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#routerList' + items: 'items' # Since Terraform has separate resources for router, router interface, and # router peer, calling PUT on the router will delete the interface and peer. # Use patch instead. @@ -2945,6 +3085,11 @@ objects: name: 'Snapshot' kind: 'compute#snapshot' base_url: projects/{{project}}/global/snapshots + list_url: | + projects/{{project}}/global/snapshots + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#snapshotList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -3073,6 +3218,11 @@ objects: name: 'SslCertificate' kind: 'compute#sslCertificate' base_url: projects/{{project}}/global/sslCertificates + list_url: | + projects/{{project}}/global/sslCertificates + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#sslCertificateList' + items: 'items' references: !ruby/object:Api::Resource::ReferenceLinks guides: 'Official Documentation': 'https://cloud.google.com/load-balancing/docs/ssl-certificates' @@ -3125,6 +3275,11 @@ objects: # TODO(https://github.com/GoogleCloudPlatform/magic-modules/issues/173): Enable kind: 'compute#sslPolicy' base_url: projects/{{project}}/global/sslPolicies + list_url: | + projects/{{project}}/global/sslPolicies + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#sslPoliciesList' + items: 'items' update_verb: :PATCH exports: - !ruby/object:Api::Type::SelfLink @@ -3224,6 +3379,11 @@ objects: name: 'Subnetwork' kind: 'compute#subnetwork' base_url: projects/{{project}}/regions/{{region}}/subnetworks + list_url: | + projects/{{project}}/regions/{{region}}/subnetworks + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#subnetworkList' + items: 'items' input: true exports: - !ruby/object:Api::Type::SelfLink @@ -3379,6 +3539,11 @@ objects: name: 'TargetHttpProxy' kind: 'compute#targetHttpProxy' base_url: projects/{{project}}/global/targetHttpProxies + list_url: | + projects/{{project}}/global/targetHttpProxies + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#targetHttpProxyList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -3429,6 +3594,11 @@ objects: name: 'TargetHttpsProxy' kind: 'compute#targetHttpsProxy' base_url: projects/{{project}}/global/targetHttpsProxies + list_url: | + projects/{{project}}/global/targetHttpsProxies + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#targetHttpProxyList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -3524,6 +3694,11 @@ objects: name: 'TargetPool' kind: 'compute#targetPool' base_url: projects/{{project}}/regions/{{region}}/targetPools + list_url: | + projects/{{project}}/regions/{{region}}/targetPools + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#targetPoolList' + items: 'items' description: 'Represents a TargetPool resource, used for Load Balancing.' exports: - !ruby/object:Api::Type::SelfLink @@ -3648,6 +3823,11 @@ objects: name: 'TargetSslProxy' kind: 'compute#targetSslProxy' base_url: projects/{{project}}/global/targetSslProxies + list_url: | + projects/{{project}}/global/targetSslProxies + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#targetSslProxyList' + items: 'items' input: true exports: - !ruby/object:Api::Type::SelfLink @@ -3737,6 +3917,11 @@ objects: name: 'TargetTcpProxy' kind: 'compute#targetTcpProxy' base_url: projects/{{project}}/global/targetTcpProxies + list_url: | + projects/{{project}}/global/targetTcpProxies + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#targetTcpProxyList' + items: 'items' input: true exports: - !ruby/object:Api::Type::SelfLink @@ -3799,6 +3984,11 @@ objects: name: 'TargetVpnGateway' kind: 'compute#targetVpnGateway' base_url: projects/{{project}}/regions/{{region}}/targetVpnGateways + list_url: | + projects/{{project}}/regions/{{region}}/targetVpnGateways + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#targetVpnGatewayList' + items: 'items' input: true exports: - !ruby/object:Api::Type::SelfLink @@ -3874,6 +4064,11 @@ objects: name: 'UrlMap' kind: 'compute#urlMap' base_url: projects/{{project}}/global/urlMaps + list_url: | + projects/{{project}}/global/urlMaps + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#urlMapList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -4022,6 +4217,11 @@ objects: description: 'VPN tunnel resource.' input: true base_url: projects/{{project}}/regions/{{region}}/vpnTunnels + list_url: | + projects/{{project}}/regions/{{region}}/vpnTunnels + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#vpnTunnelList' + items: 'items' exports: - !ruby/object:Api::Type::SelfLink name: 'selfLink' @@ -4137,6 +4337,11 @@ objects: name: 'Zone' kind: 'compute#zone' base_url: projects/{{project}}/zones + list_url: | + projects/{{project}}/zones + list_url_response: !ruby/object:Api::Resource::ResponseList + kind: 'compute#zoneList' + items: 'items' exports: - name - !ruby/object:Api::Type::SelfLink diff --git a/provider/inspec.rb b/provider/inspec.rb index dbf08a36d308..d24867851610 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -116,5 +116,16 @@ def generate_requires(properties, requires = []) requires.concat(nested_props.map{|nested_prop| nested_prop.property_file }) requires end + + def plural(word) + # TODO use a real ruby gem for this? Pluralization is hard + if word[-1] == 's' + return word + 'es' + end + if word[-1] == 'y' + return word[0...-1] + 'ies' + end + return word + 's' + end end end diff --git a/templates/inspec/plural_resource.erb b/templates/inspec/plural_resource.erb index 3628efec92c1..20e44ddcefae 100644 --- a/templates/inspec/plural_resource.erb +++ b/templates/inspec/plural_resource.erb @@ -14,4 +14,76 @@ -%> <%= compile 'templates/license.erb' -%> -<%= lines(autogen_notice :ruby) -%> \ No newline at end of file +<%= lines(autogen_notice :ruby) -%> + +class <%= object.name -%>s < Inspec.resource(1) + +<% +name = 'google_#{product_ns.downcase}_#{object.name.downcase}' +-%> + name '<%= plural(name) -%>' + desc '<%= object.name -%> plural resource' + supports platform: 'gcp-mm' + + filter_table_config = FilterTable.create + +<% object.all_user_properties.each do |prop| -%> +<% unless prop.input -%> +<% +out = plural(prop.out_name) +-%> + <%= "filter_table_config.add(:#{out}, field: :#{prop.out_name})" -%> +<% end # unless prop.input -%> +<% end # object.all_user_properties.each do -%> + + filter_table_config.connect(self, :fetch_data) + +<% +base = "'#{object.self_link_url[0].join}'" +url = object.base_url +if url.is_a?(Array) + url = url.join('') +else + url = url.split("\n").join('') +end +-%> + def base + <%= base %> + end + + def url + '<%= url %>' + end + + def initialize(params = {}) + @params = params + end + + def fetch_resource(params) + get_request = inspec.backend.fetch(base, url, params) + end + +<% +link_query = object.self_link_query || object.list_url_response +-%> + def fetch_data + @data = fetch_wrapped_resource(<%= '#{link_query.kind}' -%>, <%= '#{link_query.items}' -%>) + end + + def fetch_wrapped_resource(wrap_kind, wrap_path) + result = fetch_resource(@params) + return if result.nil? || !result.key?(wrap_path) + + # TODO hacky conversion of string => string hash to symbol => string hash that InSpec needs + res = result[wrap_path] + real = [] + res.each do |x| + n = {} + x.each_pair { |k, v| n[k.to_sym] = v } + real.push(n) + end + + real + end + +end \ No newline at end of file From 38d3e19e6ae8ccec126eb8cbee17114a8485e2f5 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 16 Oct 2018 13:03:02 -0700 Subject: [PATCH 10/49] Underscoring names --- provider/inspec.rb | 2 +- templates/inspec/plural_resource.erb | 2 +- templates/inspec/singular_resource.erb | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index d24867851610..92d846616eb9 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -50,7 +50,7 @@ def generate_resource(data) ) generate_resource_file data.clone.merge( default_template: 'templates/inspec/plural_resource.erb', - out_file: File.join(target_folder, "google_#{data[:product_name]}_#{name}s.rb") + out_file: File.join(target_folder, plural("google_#{data[:product_name]}_#{name}") + ".rb") ) end diff --git a/templates/inspec/plural_resource.erb b/templates/inspec/plural_resource.erb index 20e44ddcefae..e02f39753f05 100644 --- a/templates/inspec/plural_resource.erb +++ b/templates/inspec/plural_resource.erb @@ -19,7 +19,7 @@ class <%= object.name -%>s < Inspec.resource(1) <% -name = 'google_#{product_ns.downcase}_#{object.name.downcase}' +name = "google_#{product_ns.downcase}_#{object.name.underscore}" -%> name '<%= plural(name) -%>' desc '<%= object.name -%> plural resource' diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index b618619a3dbe..39f1cfc15902 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -35,7 +35,7 @@ 4)) -%> class <%= object.name -%> < Inspec.resource(1) - name 'google_<%= product_ns.downcase -%>_<%= object.name.downcase -%>' + name 'google_<%= product_ns.downcase -%>_<%= object.name.underscore -%>' desc '<%= object.name -%>' supports platform: 'gcp-mm' @@ -52,14 +52,13 @@ if url.is_a?(Array) else url = url.split("\n").join('') end -url = "'#{url}'" -%> def base <%= base %> end def url - <%= url %> + '<%= url %>' end <% if object.self_link_query.nil? -%> From 332c3689900500e12e4d7c965d882dffaf07c6c9 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 16 Oct 2018 13:06:07 -0700 Subject: [PATCH 11/49] Templating issues, spacing --- templates/inspec/plural_resource.erb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/inspec/plural_resource.erb b/templates/inspec/plural_resource.erb index e02f39753f05..4fd9e36b58c4 100644 --- a/templates/inspec/plural_resource.erb +++ b/templates/inspec/plural_resource.erb @@ -26,16 +26,17 @@ name = "google_#{product_ns.downcase}_#{object.name.underscore}" supports platform: 'gcp-mm' filter_table_config = FilterTable.create - <% object.all_user_properties.each do |prop| -%> <% unless prop.input -%> <% out = plural(prop.out_name) -%> + <%= "filter_table_config.add(:#{out}, field: :#{prop.out_name})" -%> <% end # unless prop.input -%> <% end # object.all_user_properties.each do -%> + filter_table_config.connect(self, :fetch_data) <% @@ -67,7 +68,7 @@ end link_query = object.self_link_query || object.list_url_response -%> def fetch_data - @data = fetch_wrapped_resource(<%= '#{link_query.kind}' -%>, <%= '#{link_query.items}' -%>) + @data = fetch_wrapped_resource(<%= "'#{link_query.kind}'" -%>, <%= "'#{link_query.items}'" -%>) end def fetch_wrapped_resource(wrap_kind, wrap_path) From 9ec20975e00038f9a2e39691eb527f14cc501b4d Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 16 Oct 2018 15:25:05 -0700 Subject: [PATCH 12/49] Fixing resource imports --- provider/inspec.rb | 10 +++++++++- templates/inspec/nested_object.erb | 2 +- templates/inspec/plural_resource.erb | 2 +- templates/inspec/singular_resource.erb | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 92d846616eb9..5c09f80f195b 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -94,7 +94,9 @@ 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)) + array_nested = (property.is_a?(Api::Type::Array) && !property.item_type.is_a?(::Api::Type::NestedObject)) + map = property.is_a?(::Api::Type::NameValues) + return property.is_a?(::Api::Type::Primitive) || array_nested || map end def resource_ref? (property) @@ -117,6 +119,12 @@ def generate_requires(properties, requires = []) requires end + # InSpec doesn't need wrappers for primitives, so exclude them + def emit_requires(requires) + primitives = ['boolean', 'enum', 'string', 'time', 'integer', 'array', 'string_array', 'double'] + requires.flatten.sort.uniq.reject{|r| primitives.include?(r.split('/').last)}.map { |r| "require '#{r}'" }.join("\n") + end + def plural(word) # TODO use a real ruby gem for this? Pluralization is hard if word[-1] == 's' diff --git a/templates/inspec/nested_object.erb b/templates/inspec/nested_object.erb index ed5e4280b978..2afc86658eca 100644 --- a/templates/inspec/nested_object.erb +++ b/templates/inspec/nested_object.erb @@ -32,7 +32,7 @@ module Google return nil if args.nil? <% nested_properties.each do |prop| -%> <% - if primitive?(prop) + if primitive?(prop) || resource_ref?(prop) init = "args['#{prop.api_name}']" elsif typed_array?(prop) init = "#{prop.property_type}.parse(args['#{prop.api_name}'])" diff --git a/templates/inspec/plural_resource.erb b/templates/inspec/plural_resource.erb index 4fd9e36b58c4..4a007b8184a1 100644 --- a/templates/inspec/plural_resource.erb +++ b/templates/inspec/plural_resource.erb @@ -23,7 +23,7 @@ name = "google_#{product_ns.downcase}_#{object.name.underscore}" -%> name '<%= plural(name) -%>' desc '<%= object.name -%> plural resource' - supports platform: 'gcp-mm' + supports platform: 'gcp2' filter_table_config = FilterTable.create <% object.all_user_properties.each do |prop| -%> diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 39f1cfc15902..7c5ba82e9d6d 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -37,7 +37,7 @@ class <%= object.name -%> < Inspec.resource(1) name 'google_<%= product_ns.downcase -%>_<%= object.name.underscore -%>' desc '<%= object.name -%>' - supports platform: 'gcp-mm' + supports platform: 'gcp2' <% object.properties.reject(&:input).each do |prop| -%> <%= "attr_reader :#{prop.out_name}" -%> From 13615e7c32b44f6f7f653dc39b78a6d73ba1de07 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 16 Oct 2018 16:44:58 -0700 Subject: [PATCH 13/49] Requires changes --- provider/inspec.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 5c09f80f195b..4442a012518d 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -96,7 +96,7 @@ def emit_nested_object_overrides(data) def primitive? (property) array_nested = (property.is_a?(Api::Type::Array) && !property.item_type.is_a?(::Api::Type::NestedObject)) map = property.is_a?(::Api::Type::NameValues) - return property.is_a?(::Api::Type::Primitive) || array_nested || map + return property.is_a?(::Api::Type::Primitive) || array_nested || map || property.is_a?(::Api::Type::Fingerprint) end def resource_ref? (property) @@ -113,12 +113,21 @@ 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| primitive?(type) || resource_ref?(type) || nested_object?(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 easy_requires(type) + File.join( + 'google', + 'compute', + 'property', + type.type + ).downcase + end + # InSpec doesn't need wrappers for primitives, so exclude them def emit_requires(requires) primitives = ['boolean', 'enum', 'string', 'time', 'integer', 'array', 'string_array', 'double'] From 7d8f69a8b21a32fb9c678440339bb2c24cf11022 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 10:05:10 -0700 Subject: [PATCH 14/49] Fixing requires --- provider/inspec.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 4442a012518d..bde539e6ac84 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -120,11 +120,14 @@ def generate_requires(properties, requires = []) 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.type + type.property_file ).downcase end From 63e63e2ba321ec5034b2223520a22ab7ddc8c6db Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 10:10:33 -0700 Subject: [PATCH 15/49] Fix inspec branch name --- .ci/magic-modules/create-pr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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." From 1e7f972775555bd05412df1592516203e2da1b51 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 11:08:15 -0700 Subject: [PATCH 16/49] Adding excludes for resources --- products/compute/inspec.yaml | 81 ++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) 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: From c393fa1de02c9b7227671cb0010e6df3e42a43c3 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 10:10:33 -0700 Subject: [PATCH 17/49] Fix inspec branch name From 051cfca86f9204a1bce1768cda2a154f48dc8b7e Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 11:08:15 -0700 Subject: [PATCH 18/49] Adding excludes for resources --- products/compute/inspec.yaml | 81 ++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) 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: From 19e7706691f55c3e203c6084a4cb0905878c88cd Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 12:40:39 -0700 Subject: [PATCH 19/49] Making PR simple --- provider/inspec.rb | 12 +++++++++ templates/inspec/singular_resource.erb | 34 +++----------------------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 5c309a87d08a..0345db85a5f0 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -54,6 +54,18 @@ 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] + if url.is_a?(Array) + url = url.join('') + else + url = url.split("\n").join('') + end + url + end + # TODO? def generate_resource_tests(data) end diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 8251616a6796..00c2784bbc2d 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -17,54 +17,28 @@ <%= lines(autogen_notice :ruby) -%> # A provider to manage <%= @api.name -%> resources. -<%= lines(indent( - emit_rubocop(binding, :class, - ['Google', @api.prefix.upcase, object.name].join('::'), - :disabled), - 4)) -%> class <%= object.name -%> < Inspec.resource(1) name 'google_<%= product_ns.downcase -%>_<%= object.name.downcase -%>' desc '<%= object.name -%>' supports platform: 'gcp-mm' -<% object.properties.reject(&:input).each do |prop| -%> +<% object.properties.each do |prop| -%> <%= "attr_reader :#{prop.out_name}" -%> <% end -%> <% -base = "'#{object.self_link_url[0].join}'" -url = object.self_link_url[1] -if url.is_a?(Array) - url = url.join('') -else - url = url.split("\n").join('') -end -url = "'#{url}'" +base = "''" -%> def base - <%= base %> + '<%= object.self_link_url[0].join %>' end def url - <%= url %> + '<%= 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? -%> - # TODO def parse end From 15c1ded61e10f18be88446246ace6455114e688d Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 12:41:54 -0700 Subject: [PATCH 20/49] Unused code --- templates/inspec/singular_resource.erb | 3 --- 1 file changed, 3 deletions(-) diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 34b9cb2d6127..fd8b83606697 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -39,9 +39,6 @@ class <%= object.name -%> < Inspec.resource(1) <% end -%> -<% -base = "''" --%> def base '<%= object.self_link_url[0].join %>' end From 517e065c8068b20ab36b2f007b091bb8c6452e4f Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 12:42:54 -0700 Subject: [PATCH 21/49] Remove unused code --- templates/inspec/singular_resource.erb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 00c2784bbc2d..0858313284a5 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -27,10 +27,6 @@ class <%= object.name -%> < Inspec.resource(1) <%= "attr_reader :#{prop.out_name}" -%> <% end -%> - -<% -base = "''" --%> def base '<%= object.self_link_url[0].join %>' end From 5c121cf1d0775c4d1391869249fefbed57f448ab Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 12:53:58 -0700 Subject: [PATCH 22/49] Rubocop fixes --- provider/inspec.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index 0345db85a5f0..e2f299adc386 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -54,16 +54,12 @@ def generate_resource(data) ) end - # Returns the url that this object can be retrieved from + # 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] - if url.is_a?(Array) - url = url.join('') - else - url = url.split("\n").join('') - end - url + return url.join('') if url.is_a?(Array) + return url.split("\n").join('') end # TODO? From de953789bb5a98d7870b6eca5624a44761837f1e Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 12:58:02 -0700 Subject: [PATCH 23/49] Rubocop fixes --- provider/inspec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index e2f299adc386..69439d7b94de 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -59,7 +59,7 @@ def generate_resource(data) def url(object) url = object.self_link_url[1] return url.join('') if url.is_a?(Array) - return url.split("\n").join('') + url.split("\n").join('') end # TODO? From bc71bff551d4f801f21e0bedf91bdf399f475cf8 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 13:26:25 -0700 Subject: [PATCH 24/49] Input is fine --- templates/inspec/singular_resource.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 6921740ffcdb..1a8056d7daa3 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -62,7 +62,7 @@ class <%= object.name -%> < Inspec.resource(1) def parse <% - fetch_code = object.properties.reject(&:input).map do |prop| + fetch_code = object.properties.map do |prop| name = prop.out_name if primitive?(prop) || resource_ref?(prop) From ac73051144bcd39361e72a1f01b0b9afb4bbd6e2 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 14:07:49 -0700 Subject: [PATCH 25/49] 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 26/49] 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 27/49] 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 0583ac67b646962f078ca11b8a7159b4a7047fed Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 16:14:46 -0700 Subject: [PATCH 28/49] Fixing up time parsing, input only stuff --- provider/inspec.rb | 4 ++++ templates/inspec/nested_object.erb | 4 +++- templates/inspec/singular_resource.erb | 6 ++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/provider/inspec.rb b/provider/inspec.rb index ca73e61e6091..66839f99e37e 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -107,6 +107,10 @@ def primitive? (property) return property.is_a?(::Api::Type::Primitive) || array_nested || map || property.is_a?(::Api::Type::Fingerprint) end + def time?(property) + property.is_a?(::Api::Type::Time) + end + def resource_ref? (property) return property.is_a?(::Api::Type::ResourceRef) end diff --git a/templates/inspec/nested_object.erb b/templates/inspec/nested_object.erb index 2afc86658eca..bbab30ff509a 100644 --- a/templates/inspec/nested_object.erb +++ b/templates/inspec/nested_object.erb @@ -32,7 +32,9 @@ module Google return nil if args.nil? <% nested_properties.each do |prop| -%> <% - if primitive?(prop) || resource_ref?(prop) + if time?(prop) + init = "Time.new(@fetched['#{prop.api_name}'])" + elsif primitive?(prop) || resource_ref?(prop) init = "args['#{prop.api_name}']" elsif typed_array?(prop) init = "#{prop.property_type}.parse(args['#{prop.api_name}'])" diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 4465244d43fd..23e9b3ae534e 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -70,10 +70,12 @@ class <%= object.name -%> < Inspec.resource(1) def parse <% - parse_code = object.properties.reject(&:input).map do |prop| + parse_code = object.properties.map do |prop| name = prop.out_name - if primitive?(prop) || resource_ref?(prop) + if time?(prop) + init = "Time.new(@fetched['#{prop.api_name}'])" + elsif primitive?(prop) || resource_ref?(prop) init = "@fetched['#{prop.api_name}']" elsif typed_array?(prop) init = "#{prop.property_type}.parse(@fetched['#{prop.api_name}'])" From ac12bb6333a62b4dd8eb1759328f217a934edae8 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Thu, 18 Oct 2018 13:12:07 -0700 Subject: [PATCH 29/49] prop.input is fine --- templates/inspec/plural_resource.erb | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/inspec/plural_resource.erb b/templates/inspec/plural_resource.erb index 4a007b8184a1..a22bda7ea057 100644 --- a/templates/inspec/plural_resource.erb +++ b/templates/inspec/plural_resource.erb @@ -27,13 +27,11 @@ name = "google_#{product_ns.downcase}_#{object.name.underscore}" filter_table_config = FilterTable.create <% object.all_user_properties.each do |prop| -%> -<% unless prop.input -%> <% out = plural(prop.out_name) -%> <%= "filter_table_config.add(:#{out}, field: :#{prop.out_name})" -%> -<% end # unless prop.input -%> <% end # object.all_user_properties.each do -%> From 957ad0cb052fd91d6dfa00364527a9ffac632ca6 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Thu, 18 Oct 2018 13:38:09 -0700 Subject: [PATCH 30/49] 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 31/49] 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 32/49] 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 33/49] 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 15257fe19c3c2b8088de46d2f471f3241becf6ef Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 19 Oct 2018 11:22:11 -0700 Subject: [PATCH 34/49] First pass at generating docs for resources --- provider/inspec.rb | 23 +++++++++++++++++------ templates/inspec/doc.md.erb | 17 +++++++++++++++++ templates/inspec/nested_object.erb | 11 ++++++++++- templates/inspec/singular_resource.erb | 2 +- 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 templates/inspec/doc.md.erb diff --git a/provider/inspec.rb b/provider/inspec.rb index ffda3a9508ae..70545b29905d 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -52,6 +52,10 @@ def generate_resource(data) default_template: 'templates/inspec/plural_resource.erb', out_file: File.join(target_folder, plural("google_#{data[:product_name]}_#{name}") + ".rb") ) + generate_resource_file data.clone.merge( + default_template: 'templates/inspec/doc.md.erb', + out_file: File.join(target_folder, "google_#{data[:product_name]}_#{name}.md") + ) end # Returns the url that this object can be retrieved from @@ -162,12 +166,6 @@ def nested_object_requires(nested_object_type) ).downcase end - # InSpec doesn't need wrappers for primitives, so exclude them - def emit_requires(requires) - primitives = ['boolean', 'enum', 'string', 'time', 'integer', 'array', 'string_array', 'double'] - requires.flatten.sort.uniq.reject{|r| primitives.include?(r.split('/').last)}.map { |r| "require '#{r}'" }.join("\n") - end - def plural(word) # TODO use a real ruby gem for this? Pluralization is hard if word[-1] == 's' @@ -178,5 +176,18 @@ def plural(word) end return word + 's' end + + def resource_name(object, product_ns) + "google_#{product_ns.downcase}_#{object.name.underscore}" + end + + def sub_property_descriptions(property) + if nested_object?(property) + return property.properties.map { |prop| " * `#{prop.name}`: #{prop.description}" }.join("\n") + end + if typed_array?(property) + return property.item_type.properties.map { |prop| " * `#{prop.name}`: #{prop.description}" }.join("\n") + end + end end end diff --git a/templates/inspec/doc.md.erb b/templates/inspec/doc.md.erb new file mode 100644 index 000000000000..775ce9f0d92a --- /dev/null +++ b/templates/inspec/doc.md.erb @@ -0,0 +1,17 @@ +<%= lines(autogen_notice :markdown) -%> + +--- +title: About the <%= object.name -%> resource +platform: gcp +--- + +## Syntax +A `<%= resource_name(object, product_ns) -%>` is used to test a Google <%= object.name -%> resource + +## Properties +Properties that can be accessed from the `<%= resource_name(object, product_ns) -%>` resource: +<% object.properties.each do |prop| -%> + * `<%= "#{prop.out_name}" -%>`, <%= "#{prop.description}" -%> + +<%= sub_property_descriptions(prop) -%> +<% end -%> \ No newline at end of file diff --git a/templates/inspec/nested_object.erb b/templates/inspec/nested_object.erb index bbab30ff509a..4a7c077bcfa0 100644 --- a/templates/inspec/nested_object.erb +++ b/templates/inspec/nested_object.erb @@ -16,6 +16,15 @@ <%= lines(autogen_notice :ruby) -%> +<% + require 'google/string_utils' + + inside_indent = 8 + + requires = generate_requires(nested_properties) + requires << 'google/hash_utils' +-%> +<%= lines(emit_requires(requires)) -%> module Google module <%= product_ns %> module Property @@ -34,7 +43,7 @@ module Google <% if time?(prop) init = "Time.new(@fetched['#{prop.api_name}'])" - elsif primitive?(prop) || resource_ref?(prop) + elsif primitive?(prop) init = "args['#{prop.api_name}']" elsif typed_array?(prop) init = "#{prop.property_type}.parse(args['#{prop.api_name}'])" diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index dac30726ccdd..edfb19bce5d5 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -30,7 +30,7 @@ # A provider to manage <%= @api.name -%> resources. class <%= object.name -%> < Inspec.resource(1) - name 'google_<%= product_ns.downcase -%>_<%= object.name.underscore -%>' + name '<%= resource_name(object, product_ns) -%>' desc '<%= object.name -%>' supports platform: 'gcp2' From 98c08323f94a3f711ecc0620bf0498a2588f81a1 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 19 Oct 2018 13:19:57 -0700 Subject: [PATCH 35/49] 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 From ba40d67b2bd1142b76e8ec1c8ec490d69e6a7726 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 19 Oct 2018 13:22:29 -0700 Subject: [PATCH 36/49] Add wrapped resource handling --- templates/inspec/singular_resource.erb | 56 ++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 051eee280d03..b1fffe3ae991 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -52,10 +52,16 @@ class <%= object.name -%> < Inspec.resource(1) parse unless @fetched.nil? end <% else # object.self_link_query.nil? -%> - # TODO(slevenick) for other products - def initialize(params) - raise 'Not implemented' - end + 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 <% end # object.self_link_query.nil? -%> def fetch_resource(params) @@ -86,4 +92,46 @@ 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 From d0f6217c57529df31b70f13dedd337c9a6f47529 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 22 Oct 2018 14:38:54 -0700 Subject: [PATCH 37/49] Add tests, datetime fixes --- templates/inspec/nested_object.erb | 2 +- templates/inspec/singular_resource.erb | 5 ++--- templates/inspec/tests/zone_test.rb | 9 +++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 templates/inspec/tests/zone_test.rb diff --git a/templates/inspec/nested_object.erb b/templates/inspec/nested_object.erb index 4a7c077bcfa0..3f786b70b8fc 100644 --- a/templates/inspec/nested_object.erb +++ b/templates/inspec/nested_object.erb @@ -42,7 +42,7 @@ module Google <% nested_properties.each do |prop| -%> <% if time?(prop) - init = "Time.new(@fetched['#{prop.api_name}'])" + init = "DateTime.parse(@fetched['#{prop.api_name}'])" elsif primitive?(prop) init = "args['#{prop.api_name}']" elsif typed_array?(prop) diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index b1fffe3ae991..761660c8e5cd 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -74,7 +74,7 @@ class <%= object.name -%> < Inspec.resource(1) name = prop.out_name if time?(prop) - init = "Time.new(@fetched['#{prop.api_name}'])" + init = "DateTime.parse(@fetched['#{prop.api_name}'])" elsif primitive?(prop) init = "@fetched['#{prop.api_name}']" elsif typed_array?(prop) @@ -93,7 +93,7 @@ class <%= object.name -%> < Inspec.resource(1) !@fetched.nil? end - <% unless object.self_link_query.nil? -%> +<% 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) @@ -130,7 +130,6 @@ class <%= object.name -%> < Inspec.resource(1) 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? -%> diff --git a/templates/inspec/tests/zone_test.rb b/templates/inspec/tests/zone_test.rb new file mode 100644 index 000000000000..1a9313f93f5e --- /dev/null +++ b/templates/inspec/tests/zone_test.rb @@ -0,0 +1,9 @@ +require 'google_compute_zone' + +RSpec.describe Zone, "zone" do + it "first test" do + t = Zone.new + expect(t.thing).to eq 1 + + end +end \ No newline at end of file From b2ad7b8b989dfa0db8e074d53836a311f154a189 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 22 Oct 2018 14:40:19 -0700 Subject: [PATCH 38/49] Add real tests --- templates/inspec/tests/zone_test.rb | 36 ++++++++++++++-- templates/inspec/tests/zones_test.rb | 62 ++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 templates/inspec/tests/zones_test.rb diff --git a/templates/inspec/tests/zone_test.rb b/templates/inspec/tests/zone_test.rb index 1a9313f93f5e..706d27b0ed4b 100644 --- a/templates/inspec/tests/zone_test.rb +++ b/templates/inspec/tests/zone_test.rb @@ -1,9 +1,37 @@ require 'google_compute_zone' -RSpec.describe Zone, "zone" do - it "first test" do - t = Zone.new - expect(t.thing).to eq 1 +class ZoneTest < Zone + def initialize(data) + @fetched = data + end +end + +zone_fixture = {"kind"=>"compute#zone", + "id"=>"2231", + "creationTimestamp"=>"1989-11-28T00:00:00-05:00", + "name"=>"us-east1-b", + "description"=>"us-east1-b", + "status"=>"UP", + "region"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/regions/us-east1", + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/zones/us-east1-b", + "availableCpuPlatforms"=> + ["Intel Skylake", "Intel Broadwell", "Intel Haswell"]} +RSpec.describe Zone, "zone resource" do + it "parse test" do + t = ZoneTest.new(zone_fixture) + t.parse + expect(t.exists?).to be true + expect(t.name).to eq 'us-east1-b' + expect(t.status).to eq 'UP' + expect(t.deprecated.obsolete).to eq nil + time = Time.at(628232400).to_datetime + expect(t.creation_timestamp).to eq time + end + it "no response" do + t = ZoneTest.new(nil) + expect(t.exists?).to be false end end \ No newline at end of file diff --git a/templates/inspec/tests/zones_test.rb b/templates/inspec/tests/zones_test.rb new file mode 100644 index 000000000000..40390da74235 --- /dev/null +++ b/templates/inspec/tests/zones_test.rb @@ -0,0 +1,62 @@ +require 'google_compute_zones' + +class ZonesTest < Zones + def fetch_resource(data) + return data + end +end + +zones_fixture = {"kind"=>"compute#zoneList", + "id"=>"projects/sam-inspec/zones", + "items"=> + [{"kind"=>"compute#zone", + "id"=>"2231", + "creationTimestamp"=>"1969-12-31T16:00:00.000-08:00", + "name"=>"us-east1-b", + "description"=>"us-east1-b", + "status"=>"UP", + "region"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/regions/us-east1", + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/zones/us-east1-b", + "availableCpuPlatforms"=> + ["Intel Skylake", "Intel Broadwell", "Intel Haswell"]}, + {"kind"=>"compute#zone", + "id"=>"2233", + "creationTimestamp"=>"1969-12-31T16:00:00.000-08:00", + "name"=>"us-east1-c", + "description"=>"us-east1-c", + "status"=>"UP", + "region"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/regions/us-east1", + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/zones/us-east1-c", + "availableCpuPlatforms"=> + ["Intel Skylake", "Intel Broadwell", "Intel Haswell"]}, + {"kind"=>"compute#zone", + "id"=>"2234", + "creationTimestamp"=>"1969-12-31T16:00:00.000-08:00", + "name"=>"us-east1-d", + "description"=>"us-east1-d", + "status"=>"UP", + "region"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/regions/us-east1", + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/zones/us-east1-d", + "availableCpuPlatforms"=> + ["Intel Skylake", "Intel Broadwell", "Intel Haswell"]}]} + +RSpec.describe Zones, "zones" do + it "first test" do + t = ZonesTest.new(zones_fixture) + expect(t.names.size).to eq 3 + expect(t.names).to include 'us-east1-d' + expect(t.names).to include 'us-east1-b' + expect(t.names).to include 'us-east1-c' + expect(t.statuses).to include 'UP' + expect(t.statuses.size).to eq 3 + expect(t.ids).to include '2231' + expect(t.ids).to include '2234' + expect(t.ids).to include '2233' + end +end \ No newline at end of file From 70b137bf57dce46a49771b285a314e261487abdd Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Mon, 22 Oct 2018 15:35:59 -0700 Subject: [PATCH 39/49] Spacing --- templates/inspec/tests/zone_test.rb | 3 ++- templates/inspec/tests/zones_test.rb | 32 ++++++++++++++-------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/templates/inspec/tests/zone_test.rb b/templates/inspec/tests/zone_test.rb index 706d27b0ed4b..d2cf3db5b640 100644 --- a/templates/inspec/tests/zone_test.rb +++ b/templates/inspec/tests/zone_test.rb @@ -34,4 +34,5 @@ def initialize(data) t = ZoneTest.new(nil) expect(t.exists?).to be false end -end \ No newline at end of file +end + diff --git a/templates/inspec/tests/zones_test.rb b/templates/inspec/tests/zones_test.rb index 40390da74235..44a5b0c05a4c 100644 --- a/templates/inspec/tests/zones_test.rb +++ b/templates/inspec/tests/zones_test.rb @@ -1,11 +1,11 @@ require 'google_compute_zones' class ZonesTest < Zones - def fetch_resource(data) - return data - end + def fetch_resource(data) + return data + end end - + zones_fixture = {"kind"=>"compute#zoneList", "id"=>"projects/sam-inspec/zones", "items"=> @@ -47,16 +47,16 @@ def fetch_resource(data) ["Intel Skylake", "Intel Broadwell", "Intel Haswell"]}]} RSpec.describe Zones, "zones" do - it "first test" do - t = ZonesTest.new(zones_fixture) - expect(t.names.size).to eq 3 - expect(t.names).to include 'us-east1-d' - expect(t.names).to include 'us-east1-b' - expect(t.names).to include 'us-east1-c' - expect(t.statuses).to include 'UP' - expect(t.statuses.size).to eq 3 - expect(t.ids).to include '2231' - expect(t.ids).to include '2234' - expect(t.ids).to include '2233' - end + it "first test" do + t = ZonesTest.new(zones_fixture) + expect(t.names.size).to eq 3 + expect(t.names).to include 'us-east1-d' + expect(t.names).to include 'us-east1-b' + expect(t.names).to include 'us-east1-c' + expect(t.statuses).to include 'UP' + expect(t.statuses.size).to eq 3 + expect(t.ids).to include '2231' + expect(t.ids).to include '2234' + expect(t.ids).to include '2233' + end end \ No newline at end of file From 0e4276380257b0b398342130d4001ec8f0019485 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 23 Oct 2018 12:50:10 -0700 Subject: [PATCH 40/49] Adding tests, not sure if CI will work. --- .ci/unit-tests/inspec.sh | 7 +- products/compute/inspec.yaml | 2 - provider/inspec.rb | 10 +- templates/inspec/plural_resource.erb | 1 + templates/inspec/tests/firewall_test.rb | 61 ++++++++++++ templates/inspec/tests/firewalls_test.rb | 122 +++++++++++++++++++++++ templates/inspec/tests/zone_test.rb | 26 ++--- 7 files changed, 210 insertions(+), 19 deletions(-) create mode 100644 templates/inspec/tests/firewall_test.rb create mode 100644 templates/inspec/tests/firewalls_test.rb diff --git a/.ci/unit-tests/inspec.sh b/.ci/unit-tests/inspec.sh index eb1427683a1f..23687c78cedf 100755 --- a/.ci/unit-tests/inspec.sh +++ b/.ci/unit-tests/inspec.sh @@ -3,4 +3,9 @@ set -e set -x -echo 'TODO slevenick write tests' \ No newline at end of file +echo 'TODO slevenick write tests' +pushd "magic-modules/build/inspec/libraries" + +rspec -I . ../test/unit + +popd \ No newline at end of file diff --git a/products/compute/inspec.yaml b/products/compute/inspec.yaml index bcd603cee34d..9fe44f4cf33a 100644 --- a/products/compute/inspec.yaml +++ b/products/compute/inspec.yaml @@ -32,8 +32,6 @@ overrides: !ruby/object:Provider::ResourceOverrides 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 diff --git a/provider/inspec.rb b/provider/inspec.rb index 70545b29905d..cb0303262acc 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -41,7 +41,7 @@ def property_override # This function uses the resource templates to create singular and plural # resources that can be used by InSpec def generate_resource(data) - target_folder = File.join(data[:output_folder], 'inspec') + target_folder = File.join(data[:output_folder], 'libraries') FileUtils.mkpath target_folder name = data[:object].name.underscore generate_resource_file data.clone.merge( @@ -66,8 +66,12 @@ def url(object) url.split("\n").join('') end - # TODO? - def generate_resource_tests(data) end + # Copies InSpec unit tests to build folder + def generate_resource_tests(data) + target_folder = File.join(data[:output_folder], 'test/unit') + FileUtils.mkpath target_folder + FileUtils.cp_r 'templates/inspec/tests/.', target_folder + end def generate_base_property(data) end diff --git a/templates/inspec/plural_resource.erb b/templates/inspec/plural_resource.erb index a22bda7ea057..a63dc8b6ee09 100644 --- a/templates/inspec/plural_resource.erb +++ b/templates/inspec/plural_resource.erb @@ -16,6 +16,7 @@ <%= lines(autogen_notice :ruby) -%> +require 'inspec/resource' class <%= object.name -%>s < Inspec.resource(1) <% diff --git a/templates/inspec/tests/firewall_test.rb b/templates/inspec/tests/firewall_test.rb new file mode 100644 index 000000000000..f9f15300b523 --- /dev/null +++ b/templates/inspec/tests/firewall_test.rb @@ -0,0 +1,61 @@ +require 'google_compute_firewall' + +class FirewallTest < Firewall + def initialize(data) + @fetched = data + end +end +description = "My description" +firewall_fixture = {"kind"=>"compute#firewall", + "id"=>"960238098441931407", + "creationTimestamp"=>"2018-10-11T22:42:56.000-07:00", + "name"=>"default-uuaca3r2wn7yqlbdyzue5lrn", + "description"=>description, + "network"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/networks/default", + "priority"=>1000, + "sourceRanges"=> + ["103.104.152.0/22", + "104.132.0.0/14", + "113.197.104.0/22", + "185.25.28.0/22", + "193.200.222.0/24", + "89.207.224.0/21"], + "targetTags"=>["https-server"], + "allowed"=>[{"IPProtocol"=>"tcp", "ports"=>["443"]}], + "denied"=>[{"IPProtocol"=>"udp", "ports"=>["555"]}], + "direction"=>"INGRESS", + "disabled"=>false, + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/firewalls/default-uuaca3r2wn7yqlbdyzue5lrn"} + + +RSpec.describe Firewall, '#parse' do + before do + @firewall_mock = FirewallTest.new(firewall_fixture) + @firewall_mock.parse + end + context 'firewall attributes' do + it { expect(@firewall_mock.exists?).to be true } + it { expect(@firewall_mock.creation_timestamp).to eq Time.at(1539322976).to_datetime } + it { expect(@firewall_mock.description).to eq description } + it { expect(@firewall_mock.allowed.size).to be 1 } + it { expect(@firewall_mock.allowed[0].ip_protocol).to eq 'tcp' } + it { expect(@firewall_mock.allowed[0].ports).to include "443" } + it { expect(@firewall_mock.denied.size).to be 1 } + it { expect(@firewall_mock.denied[0].ip_protocol).to eq 'udp' } + it { expect(@firewall_mock.denied[0].ports).to include "555" } + it { expect(@firewall_mock.direction).to eq 'INGRESS' } + it { expect(@firewall_mock.network).to match('/default$') } + it { expect(@firewall_mock.source_ranges).to include "113.197.104.0/22" } + + end +end + + +no_firewall = FirewallTest.new(nil) +RSpec.describe Firewall, "#parse" do + it "does not exist" do + expect(no_firewall.exists?).to be false + end +end \ No newline at end of file diff --git a/templates/inspec/tests/firewalls_test.rb b/templates/inspec/tests/firewalls_test.rb new file mode 100644 index 000000000000..3b8a15d2933b --- /dev/null +++ b/templates/inspec/tests/firewalls_test.rb @@ -0,0 +1,122 @@ +require 'google_compute_firewalls' + +class FirewallsTest < Firewalls + def fetch_resource(data) + return data + end +end + +firewalls_fixture = {"kind"=>"compute#firewallList", + "id"=>"projects/sam-inspec/global/firewalls", + "items"=> + [{"kind"=>"compute#firewall", + "id"=>"2182788659368790689", + "creationTimestamp"=>"2018-10-11T22:42:38.647-07:00", + "name"=>"default-2wnao3jebww7ldrn463stwke", + "description"=> + "Desc", + "network"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/networks/default", + "priority"=>1000, + "sourceRanges"=> + ["103.104.152.0/22", + "104.132.0.0/14", + "113.197.104.0/22", + "185.25.28.0/22", + "193.200.222.0/24", + "89.207.224.0/21"], + "allowed"=>[{"IPProtocol"=>"tcp", "ports"=>["22", "3389"]}], + "direction"=>"INGRESS", + "disabled"=>false, + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/firewalls/default-2wnao3jebww7ldrn463stwke"}, + {"kind"=>"compute#firewall", + "id"=>"8832388878237010597", + "creationTimestamp"=>"2018-10-11T22:42:34.846-07:00", + "name"=>"default-7mzjmae3tlidh4yoidvnpe53", + "description"=> + "Description 2", + "network"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/networks/default", + "priority"=>1000, + "sourceRanges"=> + ["108.170.224.0/19", + "108.177.8.0/21", + "108.177.80.0/20", + "108.177.96.0/19", + "172.102.8.0/21", + "172.217.0.0/19", + "172.217.128.0/19", + "172.217.160.0/20", + "172.217.176.1/32", + "172.217.176.2/32", + "172.217.192.0/19", + "172.217.64.0/18", + "172.253.50.0/23", + "172.253.64.0/20", + "173.194.0.0/16", + "185.150.148.0/22", + "192.104.160.0/23", + "209.107.176.0/20", + "209.85.128.0/17", + "216.239.32.0/19", + "216.252.220.0/22", + "216.58.192.0/19", + "64.233.160.0/19", + "66.102.0.0/20", + "66.249.64.0/19", + "72.14.192.0/18", + "74.125.0.0/16", + "8.8.4.0/24", + "8.8.8.0/24"], + "targetTags"=>["https-server"], + "allowed"=>[{"IPProtocol"=>"tcp", "ports"=>["443"]}], + "direction"=>"INGRESS", + "disabled"=>false, + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/firewalls/default-7mzjmae3tlidh4yoidvnpe53"}, + {"kind"=>"compute#firewall", + "id"=>"8107033807488387752", + "creationTimestamp"=>"2018-10-11T22:42:31.919-07:00", + "name"=>"default-knsku4qwwbtr3bhcf3y6vcmu", + "description"=> + "Destination resource: \"gcp/gce/g-on-g-default\"\nExposure: \"gcp/gce/g-on-g-default/gce_internal\"\nSource resource: \"gcp/gce/internal/gce_internal\"", + "network"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/networks/default", + "priority"=>1000, + "sourceRanges"=>["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"], + "allowed"=>[{"IPProtocol"=>"tcp"}, {"IPProtocol"=>"udp"}], + "direction"=>"INGRESS", + "disabled"=>false, + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/firewalls/default-knsku4qwwbtr3bhcf3y6vcmu"} + ], + + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/firewalls"} + +RSpec.describe Firewalls, '#fetch_resource' do + before do + @firewalls_mock = FirewallsTest.new(firewalls_fixture) + end + context 'firewalls plural' do + it { expect(@firewalls_mock.names.size).to eq 3 } + it { expect(@firewalls_mock.names).to include 'default-knsku4qwwbtr3bhcf3y6vcmu' } + it { expect(@firewalls_mock.names).to include 'default-7mzjmae3tlidh4yoidvnpe53' } + it { expect(@firewalls_mock.names).to include 'default-2wnao3jebww7ldrn463stwke' } + + end +end + +no_firewalls_fixture = {"kind"=>"compute#firewallList", + "id"=>"projects/sam-inspec/global/firewalls", + "items"=>[], + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/firewalls"} + +no_firewalls = FirewallsTest.new(no_firewalls_fixture) +RSpec.describe Firewalls, "#fetch_resource" do + it "no firewalls" do + expect(no_firewalls.names.size).to eq 0 + end +end \ No newline at end of file diff --git a/templates/inspec/tests/zone_test.rb b/templates/inspec/tests/zone_test.rb index d2cf3db5b640..26ae0d0f7a32 100644 --- a/templates/inspec/tests/zone_test.rb +++ b/templates/inspec/tests/zone_test.rb @@ -19,20 +19,20 @@ def initialize(data) "availableCpuPlatforms"=> ["Intel Skylake", "Intel Broadwell", "Intel Haswell"]} -RSpec.describe Zone, "zone resource" do - it "parse test" do - t = ZoneTest.new(zone_fixture) - t.parse - expect(t.exists?).to be true - expect(t.name).to eq 'us-east1-b' - expect(t.status).to eq 'UP' - expect(t.deprecated.obsolete).to eq nil - time = Time.at(628232400).to_datetime - expect(t.creation_timestamp).to eq time - end +RSpec.describe Zone, "parse" do + it "zone attributes" do + t = ZoneTest.new(zone_fixture) + t.parse + expect(t.exists?).to be true + expect(t.name).to eq 'us-east1-b' + expect(t.status).to eq 'UP' + expect(t.deprecated.obsolete).to eq nil + time = Time.at(628232400).to_datetime + expect(t.creation_timestamp).to eq time + end it "no response" do - t = ZoneTest.new(nil) - expect(t.exists?).to be false + t = ZoneTest.new(nil) + expect(t.exists?).to be false end end From 43b94d54bbec2340354be27768927d2a31f771bc Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 23 Oct 2018 16:40:23 -0700 Subject: [PATCH 41/49] Adding instance --- products/compute/inspec.yaml | 78 ++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/products/compute/inspec.yaml b/products/compute/inspec.yaml index 9fe44f4cf33a..6bf91a6580cc 100644 --- a/products/compute/inspec.yaml +++ b/products/compute/inspec.yaml @@ -20,83 +20,81 @@ manifest: !ruby/object:Provider::Inspec::Manifest description: | InSpec resources for verifying GCP infrastructure overrides: !ruby/object:Provider::ResourceOverrides - Address: !ruby/object:Provider::Chef::ResourceOverride + Address: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Autoscaler: !ruby/object:Provider::Chef::ResourceOverride + Autoscaler: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - BackendBucket: !ruby/object:Provider::Chef::ResourceOverride + BackendBucket: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - BackendService: !ruby/object:Provider::Chef::ResourceOverride + BackendService: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Disk: !ruby/object:Provider::Chef::ResourceOverride + Disk: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - DiskType: !ruby/object:Provider::Chef::ResourceOverride + DiskType: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - ForwardingRule: !ruby/object:Provider::Chef::ResourceOverride + ForwardingRule: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - GlobalAddress: !ruby/object:Provider::Chef::ResourceOverride + GlobalAddress: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - GlobalForwardingRule: !ruby/object:Provider::Chef::ResourceOverride + GlobalForwardingRule: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - HealthCheck: !ruby/object:Provider::Chef::ResourceOverride + HealthCheck: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - HttpHealthCheck: !ruby/object:Provider::Chef::ResourceOverride + HttpHealthCheck: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - HttpsHealthCheck: !ruby/object:Provider::Chef::ResourceOverride + HttpsHealthCheck: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Image: !ruby/object:Provider::Chef::ResourceOverride + Image: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Instance: !ruby/object:Provider::Chef::ResourceOverride + InstanceGroup: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - InstanceGroup: !ruby/object:Provider::Chef::ResourceOverride + InstanceGroupManager: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - InstanceGroupManager: !ruby/object:Provider::Chef::ResourceOverride + InstanceTemplate: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - InstanceTemplate: !ruby/object:Provider::Chef::ResourceOverride + InterconnectAttachment: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - InterconnectAttachment: !ruby/object:Provider::Chef::ResourceOverride + License: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - License: !ruby/object:Provider::Chef::ResourceOverride + MachineType: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - MachineType: !ruby/object:Provider::Chef::ResourceOverride + Network: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Network: !ruby/object:Provider::Chef::ResourceOverride + Region: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Region: !ruby/object:Provider::Chef::ResourceOverride + RegionAutoscaler: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - RegionAutoscaler: !ruby/object:Provider::Chef::ResourceOverride + RegionDisk: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - RegionDisk: !ruby/object:Provider::Chef::ResourceOverride + RegionDiskType: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - RegionDiskType: !ruby/object:Provider::Chef::ResourceOverride + Route: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Route: !ruby/object:Provider::Chef::ResourceOverride + Router: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Router: !ruby/object:Provider::Chef::ResourceOverride + Snapshot: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Snapshot: !ruby/object:Provider::Chef::ResourceOverride + SslCertificate: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - SslCertificate: !ruby/object:Provider::Chef::ResourceOverride + SslPolicy: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - SslPolicy: !ruby/object:Provider::Chef::ResourceOverride + Subnetwork: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - Subnetwork: !ruby/object:Provider::Chef::ResourceOverride + TargetHttpProxy: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - TargetHttpProxy: !ruby/object:Provider::Chef::ResourceOverride + TargetHttpsProxy: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - TargetHttpsProxy: !ruby/object:Provider::Chef::ResourceOverride + TargetPool: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - TargetPool: !ruby/object:Provider::Chef::ResourceOverride + TargetTcpProxy: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - TargetTcpProxy: !ruby/object:Provider::Chef::ResourceOverride + TargetVpnGateway: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - TargetVpnGateway: !ruby/object:Provider::Chef::ResourceOverride + TargetSslProxy: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - TargetSslProxy: !ruby/object:Provider::Chef::ResourceOverride + UrlMap: !ruby/object:Provider::Inspec::ResourceOverride exclude: true - UrlMap: !ruby/object:Provider::Chef::ResourceOverride - exclude: true - VpnTunnel: !ruby/object:Provider::Chef::ResourceOverride + VpnTunnel: !ruby/object:Provider::Inspec::ResourceOverride exclude: true files: !ruby/object:Provider::Config::Files style: From 6a0bd63f71bdb5bfa5a6f85e8fa9f7e8f587f1be Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 23 Oct 2018 16:46:59 -0700 Subject: [PATCH 42/49] Spacing --- templates/inspec/plural_resource.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/inspec/plural_resource.erb b/templates/inspec/plural_resource.erb index 2da0afc27b3e..f56ea78f27ae 100644 --- a/templates/inspec/plural_resource.erb +++ b/templates/inspec/plural_resource.erb @@ -27,8 +27,8 @@ name = "google_#{product_ns.downcase}_#{object.name.underscore}" filter_table_config = FilterTable.create <% object.all_user_properties.each do |prop| -%> - <%= "filter_table_config.add(:#{prop.out_name.pluralize}, field: :#{prop.out_name})" -%> + <%= "filter_table_config.add(:#{prop.out_name.pluralize}, field: :#{prop.out_name})" -%> <% end # object.all_user_properties.each do -%> From 9e2eee6744372ba5c9c147f8474dcb75576b9a8f Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 23 Oct 2018 16:48:00 -0700 Subject: [PATCH 43/49] requires fixes --- templates/inspec/nested_object.erb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/templates/inspec/nested_object.erb b/templates/inspec/nested_object.erb index 29073667f268..29596d5bdd60 100644 --- a/templates/inspec/nested_object.erb +++ b/templates/inspec/nested_object.erb @@ -17,12 +17,9 @@ <%= lines(autogen_notice :ruby) -%> <% - require 'google/string_utils' - - inside_indent = 8 - requires = generate_requires(nested_properties) requires << 'google/hash_utils' + requires << 'google/string_utils' -%> <%= lines(emit_requires(requires)) -%> module Google From cec8036c3e304c02239be2eb1a84621d2187bda6 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 24 Oct 2018 08:31:51 -0700 Subject: [PATCH 44/49] Refactor a bit, rubocop --- provider/inspec.rb | 21 +++++++++++++++---- .../{doc.md.erb => doc-template.md.erb} | 4 +++- templates/inspec/nested_object.erb | 2 -- 3 files changed, 20 insertions(+), 7 deletions(-) rename templates/inspec/{doc.md.erb => doc-template.md.erb} (84%) diff --git a/provider/inspec.rb b/provider/inspec.rb index cc3965eb8050..c9020a0da2e8 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. + # rubocop:disable Metrics/ClassLength class Inspec < Provider::Core include Google::RubyUtils # Settings for the provider @@ -54,9 +55,16 @@ def generate_resource(data) out_file: \ File.join(target_folder, "google_#{data[:product_name]}_#{name}".pluralize + '.rb') ) + generate_documentation(data) + end + + # Generates InSpec markdown documents for the resource + def generate_documentation(data) + name = data[:object].name.underscore + docs_folder = File.join(data[:output_folder], 'docs', 'resources') generate_resource_file data.clone.merge( - default_template: 'templates/inspec/doc.md.erb', - out_file: File.join(target_folder, "google_#{data[:product_name]}_#{name}.md") + default_template: 'templates/inspec/doc-template.md.erb', + out_file: File.join(docs_folder, "google_#{data[:product_name]}_#{name}.md") ) end @@ -172,11 +180,16 @@ def resource_name(object, product_ns) def sub_property_descriptions(property) if nested_object?(property) - return property.properties.map { |prop| " * `#{prop.name}`: #{prop.description}" }.join("\n") + return property.properties.map \ + { |prop| " * `#{prop.name}`: #{prop.description}" }.join("\n") end + # rubocop:disable Style/GuardClause if typed_array?(property) - return property.item_type.properties.map { |prop| " * `#{prop.name}`: #{prop.description}" }.join("\n") + return property.item_type.properties.map \ + { |prop| " * `#{prop.name}`: #{prop.description}" }.join("\n") end + # rubocop:enable Style/GuardClause end end + # rubocop:enable Metrics/ClassLength end diff --git a/templates/inspec/doc.md.erb b/templates/inspec/doc-template.md.erb similarity index 84% rename from templates/inspec/doc.md.erb rename to templates/inspec/doc-template.md.erb index 775ce9f0d92a..870205c5d8f4 100644 --- a/templates/inspec/doc.md.erb +++ b/templates/inspec/doc-template.md.erb @@ -8,10 +8,12 @@ platform: gcp ## Syntax A `<%= resource_name(object, product_ns) -%>` is used to test a Google <%= object.name -%> resource +TODO: Examples + ## Properties Properties that can be accessed from the `<%= resource_name(object, product_ns) -%>` resource: <% object.properties.each do |prop| -%> - * `<%= "#{prop.out_name}" -%>`, <%= "#{prop.description}" -%> + * `<%= "#{prop.out_name}" -%>`: <%= "#{prop.description}" -%> <%= sub_property_descriptions(prop) -%> <% end -%> \ No newline at end of file diff --git a/templates/inspec/nested_object.erb b/templates/inspec/nested_object.erb index 29596d5bdd60..9a20dd7bb7b1 100644 --- a/templates/inspec/nested_object.erb +++ b/templates/inspec/nested_object.erb @@ -18,8 +18,6 @@ <% requires = generate_requires(nested_properties) - requires << 'google/hash_utils' - requires << 'google/string_utils' -%> <%= lines(emit_requires(requires)) -%> module Google From db282478e69cf6f1f960c3decfb8e92cb6b2adf7 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 24 Oct 2018 09:55:41 -0700 Subject: [PATCH 45/49] Add bundle install --- .ci/unit-tests/inspec.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/unit-tests/inspec.sh b/.ci/unit-tests/inspec.sh index 23687c78cedf..3f740940a69f 100755 --- a/.ci/unit-tests/inspec.sh +++ b/.ci/unit-tests/inspec.sh @@ -6,6 +6,7 @@ set -x echo 'TODO slevenick write tests' pushd "magic-modules/build/inspec/libraries" +bundle install rspec -I . ../test/unit popd \ No newline at end of file From de72a6e052825ee4a5e4d05d72eaf55f49759b52 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 24 Oct 2018 10:26:29 -0700 Subject: [PATCH 46/49] Add hash& string utils, ci --- .ci/unit-tests/inspec.sh | 2 +- products/compute/inspec.yaml | 2 ++ provider/inspec/common~copy.yaml | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 provider/inspec/common~copy.yaml diff --git a/.ci/unit-tests/inspec.sh b/.ci/unit-tests/inspec.sh index 3f740940a69f..b47977f743cd 100755 --- a/.ci/unit-tests/inspec.sh +++ b/.ci/unit-tests/inspec.sh @@ -7,6 +7,6 @@ echo 'TODO slevenick write tests' pushd "magic-modules/build/inspec/libraries" bundle install -rspec -I . ../test/unit +rspec -I . ../test/unit/* popd \ No newline at end of file diff --git a/products/compute/inspec.yaml b/products/compute/inspec.yaml index 6bf91a6580cc..0c32199b9f4d 100644 --- a/products/compute/inspec.yaml +++ b/products/compute/inspec.yaml @@ -97,6 +97,8 @@ overrides: !ruby/object:Provider::ResourceOverrides VpnTunnel: !ruby/object:Provider::Inspec::ResourceOverride exclude: true files: !ruby/object:Provider::Config::Files + copy: +<%= lines(indent(compile('provider/inspec/common~copy.yaml'), 4)) -%> style: functions: changelog: diff --git a/provider/inspec/common~copy.yaml b/provider/inspec/common~copy.yaml new file mode 100644 index 000000000000..a64db1e1d2e9 --- /dev/null +++ b/provider/inspec/common~copy.yaml @@ -0,0 +1,17 @@ +# 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. + +# These files are okay to be copied "AS-IS" to the module's final destination: + +'libraries/google/hash_utils.rb': 'google/hash_utils.rb' +'libraries/google/string_utils.rb': 'google/string_utils.rb' From dbe395a82d3ff27232448f70cf7663725d71c055 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 24 Oct 2018 12:55:08 -0700 Subject: [PATCH 47/49] Test fixes --- templates/inspec/tests/firewalls_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/inspec/tests/firewalls_test.rb b/templates/inspec/tests/firewalls_test.rb index 3b8a15d2933b..32af17e2acd8 100644 --- a/templates/inspec/tests/firewalls_test.rb +++ b/templates/inspec/tests/firewalls_test.rb @@ -80,7 +80,7 @@ def fetch_resource(data) "creationTimestamp"=>"2018-10-11T22:42:31.919-07:00", "name"=>"default-knsku4qwwbtr3bhcf3y6vcmu", "description"=> - "Destination resource: \"gcp/gce/g-on-g-default\"\nExposure: \"gcp/gce/g-on-g-default/gce_internal\"\nSource resource: \"gcp/gce/internal/gce_internal\"", + "Another description", "network"=> "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/networks/default", "priority"=>1000, From 3903a089742f5b3fb41475d78f928b52049cc4ac Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 24 Oct 2018 12:59:18 -0700 Subject: [PATCH 48/49] Spacing --- templates/inspec/tests/firewall_test.rb | 48 ++++++++++++------------ templates/inspec/tests/firewalls_test.rb | 33 ++++++++-------- templates/inspec/tests/zone_test.rb | 28 +++++++------- templates/inspec/tests/zones_test.rb | 22 +++++------ 4 files changed, 65 insertions(+), 66 deletions(-) diff --git a/templates/inspec/tests/firewall_test.rb b/templates/inspec/tests/firewall_test.rb index f9f15300b523..b5ac19ad711d 100644 --- a/templates/inspec/tests/firewall_test.rb +++ b/templates/inspec/tests/firewall_test.rb @@ -1,9 +1,9 @@ require 'google_compute_firewall' class FirewallTest < Firewall - def initialize(data) - @fetched = data - end + def initialize(data) + @fetched = data + end end description = "My description" firewall_fixture = {"kind"=>"compute#firewall", @@ -31,31 +31,31 @@ def initialize(data) RSpec.describe Firewall, '#parse' do - before do - @firewall_mock = FirewallTest.new(firewall_fixture) - @firewall_mock.parse - end - context 'firewall attributes' do - it { expect(@firewall_mock.exists?).to be true } - it { expect(@firewall_mock.creation_timestamp).to eq Time.at(1539322976).to_datetime } - it { expect(@firewall_mock.description).to eq description } - it { expect(@firewall_mock.allowed.size).to be 1 } - it { expect(@firewall_mock.allowed[0].ip_protocol).to eq 'tcp' } - it { expect(@firewall_mock.allowed[0].ports).to include "443" } - it { expect(@firewall_mock.denied.size).to be 1 } - it { expect(@firewall_mock.denied[0].ip_protocol).to eq 'udp' } - it { expect(@firewall_mock.denied[0].ports).to include "555" } - it { expect(@firewall_mock.direction).to eq 'INGRESS' } - it { expect(@firewall_mock.network).to match('/default$') } - it { expect(@firewall_mock.source_ranges).to include "113.197.104.0/22" } + before do + @firewall_mock = FirewallTest.new(firewall_fixture) + @firewall_mock.parse + end + context 'firewall attributes' do + it { expect(@firewall_mock.exists?).to be true } + it { expect(@firewall_mock.creation_timestamp).to eq Time.at(1539322976).to_datetime } + it { expect(@firewall_mock.description).to eq description } + it { expect(@firewall_mock.allowed.size).to be 1 } + it { expect(@firewall_mock.allowed[0].ip_protocol).to eq 'tcp' } + it { expect(@firewall_mock.allowed[0].ports).to include "443" } + it { expect(@firewall_mock.denied.size).to be 1 } + it { expect(@firewall_mock.denied[0].ip_protocol).to eq 'udp' } + it { expect(@firewall_mock.denied[0].ports).to include "555" } + it { expect(@firewall_mock.direction).to eq 'INGRESS' } + it { expect(@firewall_mock.network).to match('/default$') } + it { expect(@firewall_mock.source_ranges).to include "113.197.104.0/22" } - end + end end no_firewall = FirewallTest.new(nil) RSpec.describe Firewall, "#parse" do - it "does not exist" do - expect(no_firewall.exists?).to be false - end + it "does not exist" do + expect(no_firewall.exists?).to be false + end end \ No newline at end of file diff --git a/templates/inspec/tests/firewalls_test.rb b/templates/inspec/tests/firewalls_test.rb index 32af17e2acd8..4065cd6b77bc 100644 --- a/templates/inspec/tests/firewalls_test.rb +++ b/templates/inspec/tests/firewalls_test.rb @@ -1,9 +1,9 @@ require 'google_compute_firewalls' class FirewallsTest < Firewalls - def fetch_resource(data) - return data - end + def fetch_resource(data) + return data + end end firewalls_fixture = {"kind"=>"compute#firewallList", @@ -14,7 +14,7 @@ def fetch_resource(data) "creationTimestamp"=>"2018-10-11T22:42:38.647-07:00", "name"=>"default-2wnao3jebww7ldrn463stwke", "description"=> - "Desc", + "Desc", "network"=> "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/networks/default", "priority"=>1000, @@ -96,16 +96,15 @@ def fetch_resource(data) "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/firewalls"} RSpec.describe Firewalls, '#fetch_resource' do - before do - @firewalls_mock = FirewallsTest.new(firewalls_fixture) - end - context 'firewalls plural' do - it { expect(@firewalls_mock.names.size).to eq 3 } - it { expect(@firewalls_mock.names).to include 'default-knsku4qwwbtr3bhcf3y6vcmu' } - it { expect(@firewalls_mock.names).to include 'default-7mzjmae3tlidh4yoidvnpe53' } - it { expect(@firewalls_mock.names).to include 'default-2wnao3jebww7ldrn463stwke' } - - end + before do + @firewalls_mock = FirewallsTest.new(firewalls_fixture) + end + context 'firewalls plural' do + it { expect(@firewalls_mock.names.size).to eq 3 } + it { expect(@firewalls_mock.names).to include 'default-knsku4qwwbtr3bhcf3y6vcmu' } + it { expect(@firewalls_mock.names).to include 'default-7mzjmae3tlidh4yoidvnpe53' } + it { expect(@firewalls_mock.names).to include 'default-2wnao3jebww7ldrn463stwke' } + end end no_firewalls_fixture = {"kind"=>"compute#firewallList", @@ -116,7 +115,7 @@ def fetch_resource(data) no_firewalls = FirewallsTest.new(no_firewalls_fixture) RSpec.describe Firewalls, "#fetch_resource" do - it "no firewalls" do - expect(no_firewalls.names.size).to eq 0 - end + it "no firewalls" do + expect(no_firewalls.names.size).to eq 0 + end end \ No newline at end of file diff --git a/templates/inspec/tests/zone_test.rb b/templates/inspec/tests/zone_test.rb index 26ae0d0f7a32..8f10f459b36d 100644 --- a/templates/inspec/tests/zone_test.rb +++ b/templates/inspec/tests/zone_test.rb @@ -1,9 +1,9 @@ require 'google_compute_zone' class ZoneTest < Zone - def initialize(data) - @fetched = data - end + def initialize(data) + @fetched = data + end end zone_fixture = {"kind"=>"compute#zone", @@ -21,18 +21,18 @@ def initialize(data) RSpec.describe Zone, "parse" do it "zone attributes" do - t = ZoneTest.new(zone_fixture) - t.parse - expect(t.exists?).to be true - expect(t.name).to eq 'us-east1-b' - expect(t.status).to eq 'UP' - expect(t.deprecated.obsolete).to eq nil + zone_mock = ZoneTest.new(zone_fixture) + zone_mock.parse + expect(zone_mock.exists?).to be true + expect(zone_mock.name).to eq 'us-east1-b' + expect(zone_mock.status).to eq 'UP' + expect(zone_mock.deprecated.obsolete).to eq nil time = Time.at(628232400).to_datetime - expect(t.creation_timestamp).to eq time + expect(zone_mock.creation_timestamp).to eq time + end + it "no response" do + no_zone_resource = ZoneTest.new(nil) + expect(no_zone_resource.exists?).to be false end - it "no response" do - t = ZoneTest.new(nil) - expect(t.exists?).to be false - end end diff --git a/templates/inspec/tests/zones_test.rb b/templates/inspec/tests/zones_test.rb index 44a5b0c05a4c..566f0118a91c 100644 --- a/templates/inspec/tests/zones_test.rb +++ b/templates/inspec/tests/zones_test.rb @@ -47,16 +47,16 @@ def fetch_resource(data) ["Intel Skylake", "Intel Broadwell", "Intel Haswell"]}]} RSpec.describe Zones, "zones" do - it "first test" do - t = ZonesTest.new(zones_fixture) - expect(t.names.size).to eq 3 - expect(t.names).to include 'us-east1-d' - expect(t.names).to include 'us-east1-b' - expect(t.names).to include 'us-east1-c' - expect(t.statuses).to include 'UP' - expect(t.statuses.size).to eq 3 - expect(t.ids).to include '2231' - expect(t.ids).to include '2234' - expect(t.ids).to include '2233' + it "plural test" do + zones_mock_resource = ZonesTest.new(zones_fixture) + expect(zones_mock_resource.names.size).to eq 3 + expect(zones_mock_resource.names).to include 'us-east1-d' + expect(zones_mock_resource.names).to include 'us-east1-b' + expect(zones_mock_resource.names).to include 'us-east1-c' + expect(zones_mock_resource.statuses).to include 'UP' + expect(zones_mock_resource.statuses.size).to eq 3 + expect(zones_mock_resource.ids).to include '2231' + expect(zones_mock_resource.ids).to include '2234' + expect(zones_mock_resource.ids).to include '2233' end end \ No newline at end of file From fc269cad1ee35f4d09c129b67b01fc072a104949 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 24 Oct 2018 15:29:31 -0700 Subject: [PATCH 49/49] Add instance test --- templates/inspec/tests/instance_test.rb | 103 ++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 templates/inspec/tests/instance_test.rb diff --git a/templates/inspec/tests/instance_test.rb b/templates/inspec/tests/instance_test.rb new file mode 100644 index 000000000000..e64f2528ceb4 --- /dev/null +++ b/templates/inspec/tests/instance_test.rb @@ -0,0 +1,103 @@ +require 'google_compute_instance' + +class InstanceTest < Instance + def initialize(data) + @fetched = data + end +end + +instance_fixture = {"kind"=>"compute#instance", + "id"=>"1154794430415066980", + "creationTimestamp"=>"2018-10-24T14:52:15.794-07:00", + "name"=>"gcp-inspec-app-mig3-4pp8", + "tags"=> + {"items"=>["allow-gcp-inspec-app-mig3", "allow-ssh"], + "fingerprint"=>"rOcaWmVHbAQ="}, + "machineType"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/zones/europe-west2-c/machineTypes/f1-micro", + "status"=>"RUNNING", + "zone"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/zones/europe-west2-c", + "networkInterfaces"=> + [{"kind"=>"compute#networkInterface", + "network"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/global/networks/default", + "subnetwork"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/regions/europe-west2/subnetworks/default", + "networkIP"=>"10.154.0.7", + "name"=>"nic0", + "accessConfigs"=> + [{"kind"=>"compute#accessConfig", + "type"=>"ONE_TO_ONE_NAT", + "name"=>"external-nat", + "natIP"=>"35.242.153.92", + "networkTier"=>"PREMIUM"}], + "fingerprint"=>"gqa1nAlsW2g="}], + "disks"=> + [{"kind"=>"compute#attachedDisk", + "type"=>"PERSISTENT", + "mode"=>"READ_WRITE", + "source"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/zones/europe-west2-c/disks/gcp-inspec-app-mig3-4pp8", + "deviceName"=>"persistent-disk-0", + "index"=>0, + "boot"=>true, + "autoDelete"=>true, + "licenses"=> + ["https://www.googleapis.com/compute/v1/projects/debian-cloud/global/licenses/debian-9-stretch"], + "interface"=>"SCSI", + "guestOsFeatures"=>[{"type"=>"VIRTIO_SCSI_MULTIQUEUE"}]}], + "metadata"=> + {"kind"=>"compute#metadata", + "fingerprint"=>"8sKIPG5hwJ8=", + "items"=> + [{"key"=>"instance-template", + "value"=> + "projects/577278241961/global/instanceTemplates/default-20181011161843755000000001"}, + {"key"=>"created-by", + "value"=> + "projects/577278241961/zones/europe-west2-c/instanceGroupManagers/gcp-inspec-app-mig3"}, + {"key"=>"tf_depends_id", "value"=>""}, + {"key"=>"startup-script", + "value"=>"val"}]}, + "serviceAccounts"=> + [{"email"=>"577278241961-compute@developer.gserviceaccount.com", + "scopes"=> + ["https://www.googleapis.com/auth/devstorage.full_control", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/monitoring.write"]}], + "selfLink"=> + "https://www.googleapis.com/compute/v1/projects/sam-inspec/zones/europe-west2-c/instances/gcp-inspec-app-mig3-4pp8", + "scheduling"=> + {"onHostMaintenance"=>"MIGRATE", + "automaticRestart"=>true, + "preemptible"=>false}, + "cpuPlatform"=>"Intel Broadwell", + "labelFingerprint"=>"42WmSpB8rSM=", + "startRestricted"=>false, + "deletionProtection"=>false} + +RSpec.describe Instance, "#parse" do + it "compute instance parse" do + instance_mock = InstanceTest.new(instance_fixture) + instance_mock.parse + expect(instance_mock.exists?).to be true + expect(instance_mock.disks.size).to eq 1 + expect(instance_mock.disks[0].mode).to eq 'READ_WRITE' + expect(instance_mock.disks[0].auto_delete).to be true + expect(instance_mock.scheduling.preemptible).to be false + expect(instance_mock.scheduling.automatic_restart).to be true + expect(instance_mock.service_accounts.size).to eq 1 + expect(instance_mock.service_accounts[0].email).to eq "577278241961-compute@developer.gserviceaccount.com" + expect(instance_mock.service_accounts[0].scopes).to include "https://www.googleapis.com/auth/compute" + + end +end + +RSpec.describe Instance, "none" do + it "no result" do + instance_mock = InstanceTest.new(nil) + expect(instance_mock.exists?).to be false + end +end \ No newline at end of file