From 3a6ea5ce59621670bc97a0150ceea7364209370f Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Tue, 19 Mar 2019 14:25:10 -0700 Subject: [PATCH] Inspec cloudfunction (#1545) Merged PR #1545. --- build/inspec | 2 +- products/cloudfunctions/api.yaml | 156 ++++++++++++++++++ products/cloudfunctions/inspec.yaml | 21 +++ .../google_cloudfunctions_cloud_function.erb | 14 ++ ...oudfunctions_cloud_function_attributes.erb | 2 + .../google_cloudfunctions_cloud_functions.erb | 6 + .../inspec/tests/integration/build/gcp-mm.tf | 27 +++ .../integration/configuration/index.js.zip | Bin 0 -> 611 bytes .../configuration/mm-attributes.yml | 12 +- 9 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 products/cloudfunctions/api.yaml create mode 100644 products/cloudfunctions/inspec.yaml create mode 100644 templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_function.erb create mode 100644 templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_function_attributes.erb create mode 100644 templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_functions.erb create mode 100644 templates/inspec/tests/integration/configuration/index.js.zip diff --git a/build/inspec b/build/inspec index 609a15ab04c3..4ba4a00d06e3 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 609a15ab04c3184305cd0e8ff06d462f500ec5bf +Subproject commit 4ba4a00d06e3d93df84f13c1e58f4f1771b714bb diff --git a/products/cloudfunctions/api.yaml b/products/cloudfunctions/api.yaml new file mode 100644 index 000000000000..174ac6085250 --- /dev/null +++ b/products/cloudfunctions/api.yaml @@ -0,0 +1,156 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- !ruby/object:Api::Product +name: CloudFunctions +display_name: Cloud Functions +versions: + - !ruby/object:Api::Product::Version + name: ga + base_url: https://cloudfunctions.googleapis.com/v1/ +scopes: + - https://www.googleapis.com/auth/cloudfunctions +apis_required: + - !ruby/object:Api::Product::ApiReference + name: Cloud Functions API + url: https://console.cloud.google.com/apis/library/cloudfunctions.googleapis.com/ +objects: + - !ruby/object:Api::Resource + name: 'CloudFunction' + base_url: projects/{{project}}/locations/{{location}}/functions + create_verb: :POST + description: | + A Cloud Function that contains user computation executed in response to an event. + properties: + - !ruby/object:Api::Type::String + name: 'name' + required: true + description: | + A user-defined name of the function. Function names must + be unique globally and match pattern `projects/*/locations/*/functions/*`. + - !ruby/object:Api::Type::String + name: 'description' + description: 'User-provided description of a function.' + - !ruby/object:Api::Type::Enum + name: 'status' + output: true + description: | + Status of the function deployment. + values: + - :CLOUD_FUNCTION_STATUS_UNSPECIFIED + - :ACTOVE + - :OFFLINE + - :DEPLOY_IN_PROGRESS + - :DELETE_IN_PROGRESS + - :UNKNOWN + - !ruby/object:Api::Type::String + name: 'entryPoint' + description: | + The name of the function (as defined in source code) that will be executed. + Defaults to the resource name suffix, if not specified. For backward + compatibility, if function with given name is not found, then the system + will try to use function named "function". For Node.js this is name of a + function exported by the module specified in source_location. + - !ruby/object:Api::Type::String + name: 'runtime' + description: | + The runtime in which the function is going to run. If empty, + defaults to Node.js 6. + - !ruby/object:Api::Type::String + name: 'timeout' + description: | + The function execution timeout. Execution is considered failed and can + be terminated if the function is not completed at the end of the timeout + period. Defaults to 60 seconds. + - !ruby/object:Api::Type::Integer + name: 'availableMemoryMb' + description: 'The amount of memory in MB available for a function.' + - !ruby/object:Api::Type::String + name: 'serviceAccountEmail' + output: true + description: 'The email of the service account for this function.' + - !ruby/object:Api::Type::String + name: 'updateTime' + output: true + description: 'The last update timestamp of a Cloud Function' + - !ruby/object:Api::Type::String + name: 'versionId' + output: true + description: | + The version identifier of the Cloud Function. Each deployment attempt + results in a new version of a function being created. + - !ruby/object:Api::Type::KeyValuePairs + name: 'labels' + description: | + A set of key/value label pairs associated with this Cloud Function. + - !ruby/object:Api::Type::KeyValuePairs + name: 'environmentVariables' + description: | + Environment variables that shall be available during function execution. + - !ruby/object:Api::Type::String + name: 'sourceArchiveUrl' + description: | + The Google Cloud Storage URL, starting with gs://, pointing to the zip + archive which contains the function. + - !ruby/object:Api::Type::String + name: 'sourceUploadUrl' + description: | + The Google Cloud Storage signed URL used for source uploading. + - !ruby/object:Api::Type::NestedObject + name: 'sourceRepository' + description: | + The source repository where a function is hosted. + properties: + - !ruby/object:Api::Type::String + name: 'url' + description: | + The URL pointing to the hosted repository where the function is defined + - !ruby/object:Api::Type::String + name: 'deployedUrl' + output: true + description: | + The URL pointing to the hosted repository where the function were defined + at the time of deployment. + - !ruby/object:Api::Type::NestedObject + name: 'httpsTrigger' + description: | + An HTTPS endpoint type of source that can be triggered via URL. + properties: + - !ruby/object:Api::Type::String + name: 'url' + output: true + description: 'The deployed url for the function.' + - !ruby/object:Api::Type::NestedObject + name: 'eventTrigger' + description: | + An HTTPS endpoint type of source that can be triggered via URL. + properties: + - !ruby/object:Api::Type::String + name: 'eventType' + required: true + description: | + The type of event to observe. For example: + `providers/cloud.storage/eventTypes/object.change` and + `providers/cloud.pubsub/eventTypes/topic.publish`. + - !ruby/object:Api::Type::String + name: 'resource' + required: true + description: | + The resource(s) from which to observe events, + for example, `projects/_/buckets/myBucket.` + - !ruby/object:Api::Type::String + name: 'service' + description: | + The hostname of the service that should be observed. + collection_url_response: !ruby/object:Api::Resource::ResponseList + items: 'functions' \ No newline at end of file diff --git a/products/cloudfunctions/inspec.yaml b/products/cloudfunctions/inspec.yaml new file mode 100644 index 000000000000..93a96f4ad422 --- /dev/null +++ b/products/cloudfunctions/inspec.yaml @@ -0,0 +1,21 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- !ruby/object:Provider::Inspec::Config +manifest: !ruby/object:Provider::Inspec::Manifest + summary: 'InSpec resources for verifying GCP infrastructure' + description: | + InSpec resources for verifying GCP infrastructure +files: !ruby/object:Provider::Config::Files + copy: + 'Gemfile': 'provider/inspec/Gemfile' diff --git a/templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_function.erb b/templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_function.erb new file mode 100644 index 000000000000..4eb23963b3d3 --- /dev/null +++ b/templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_function.erb @@ -0,0 +1,14 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% cloudfunction = grab_attributes['cloudfunction'] -%> +describe google_cloudfunctions_cloud_function(project: <%= gcp_project_id -%>, location: <%= doc_generation ? "'#{cloudfunction['location']}'" : "cloudfunction['location']" -%>, name: <%= doc_generation ? "'#{cloudfunction['name']}'" : "cloudfunction['name']" -%>) do + it { should exist } + its('description') { should eq <%= doc_generation ? "'#{cloudfunction['description']}'" : "cloudfunction['description']" -%> } + its('available_memory_mb') { should eq <%= doc_generation ? "'#{cloudfunction['available_memory_mb']}'" : "cloudfunction['available_memory_mb']" -%> } + its('https_trigger.url') { should match /\/<%= "#{grab_attributes['cloudfunction']['name']}" -%>$/ } + its('entry_point') { should eq <%= doc_generation ? "'#{cloudfunction['entry_point']}'" : "cloudfunction['entry_point']" -%> } + its('environment_variables') { should include('MY_ENV_VAR' => <%= doc_generation ? "'#{cloudfunction['env_var_value']}'" : "cloudfunction['env_var_value']" -%>) } +end + +describe google_cloudfunctions_cloud_function(project: <%= gcp_project_id -%>, location: <%= doc_generation ? "'#{cloudfunction['location']}'" : "cloudfunction['location']" -%>, name: 'nonexistent') do + it { should_not exist } +end \ No newline at end of file diff --git a/templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_function_attributes.erb b/templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_function_attributes.erb new file mode 100644 index 000000000000..2be17fc5dbeb --- /dev/null +++ b/templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_function_attributes.erb @@ -0,0 +1,2 @@ +gcp_project_id = attribute(:gcp_project_id, default: '<%= external_attribute('gcp_project_id') -%>', description: 'The GCP project identifier.') +cloudfunction = attribute('cloudfunction', default: <%= JSON.pretty_generate(grab_attributes['cloudfunction']) -%>, description: 'Cloud Function definition') \ No newline at end of file diff --git a/templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_functions.erb b/templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_functions.erb new file mode 100644 index 000000000000..85e5b7ff56af --- /dev/null +++ b/templates/inspec/examples/google_cloudfunctions_cloud_function/google_cloudfunctions_cloud_functions.erb @@ -0,0 +1,6 @@ +<% gcp_project_id = "#{external_attribute('gcp_project_id', doc_generation)}" -%> +<% cloudfunction = grab_attributes['cloudfunction'] -%> +describe google_cloudfunctions_cloud_functions(project: <%= gcp_project_id -%>, location: <%= doc_generation ? "'#{cloudfunction['location']}'" : "cloudfunction['location']" -%>) do + its('descriptions') { should include <%= doc_generation ? "'#{cloudfunction['description']}'" : "cloudfunction['description']" -%> } + its('entry_points') { should include <%= doc_generation ? "'#{cloudfunction['entry_point']}'" : "cloudfunction['entry_point']" -%> } +end \ No newline at end of file diff --git a/templates/inspec/tests/integration/build/gcp-mm.tf b/templates/inspec/tests/integration/build/gcp-mm.tf index 822f9bc8ac53..d7a4b3d2101f 100644 --- a/templates/inspec/tests/integration/build/gcp-mm.tf +++ b/templates/inspec/tests/integration/build/gcp-mm.tf @@ -119,6 +119,10 @@ variable "gcp_organization_id" { default = "none" } +variable "cloudfunction" { + type = "map" +} + resource "google_compute_ssl_policy" "custom-ssl-policy" { name = "${var.ssl_policy["name"]}" min_tls_version = "${var.ssl_policy["min_tls_version"]}" @@ -457,4 +461,27 @@ resource "google_folder" "inspec-gcp-folder" { count = "${var.gcp_organization_id == "none" ? 0 : var.gcp_enable_privileged_resources}" display_name = "${var.folder["display_name"]}" parent = "${var.gcp_organization_id}" +} + +resource "google_storage_bucket_object" "archive" { + name = "index.js.zip" + bucket = "${google_storage_bucket.generic-storage-bucket.name}" + source = "../configuration/index.js.zip" +} + +resource "google_cloudfunctions_function" "function" { + project = "${var.gcp_project_id}" + region = "${var.cloudfunction["location"]}" + name = "${var.cloudfunction["name"]}" + description = "${var.cloudfunction["description"]}" + available_memory_mb = "${var.cloudfunction["available_memory_mb"]}" + source_archive_bucket = "${google_storage_bucket.generic-storage-bucket.name}" + source_archive_object = "${google_storage_bucket_object.archive.name}" + trigger_http = "${var.cloudfunction["trigger_http"]}" + timeout = "${var.cloudfunction["timeout"]}" + entry_point = "${var.cloudfunction["entry_point"]}" + + environment_variables = { + MY_ENV_VAR = "${var.cloudfunction["env_var_value"]}" + } } \ No newline at end of file diff --git a/templates/inspec/tests/integration/configuration/index.js.zip b/templates/inspec/tests/integration/configuration/index.js.zip new file mode 100644 index 0000000000000000000000000000000000000000..a9e89ba3040e700a46e8c7bb9a79bc3460ca21e4 GIT binary patch literal 611 zcmWIWW@Zs#-~hsp)kS^`NPvSufFUz4CAC5?t2i`*he1|lLX4ZtgqQ=rg5$l{YMjv5 z(!6%sWU1II4Iq)N6;fQtl)3lOl|3T7rTiOmD(V9L zx)$wH3)f``@Mh--aEzPh0JP5?hy#GG-~!vdr~%a#oIqE^$NM@u`v*tpgPb7`bOs26 zoB`5?-SNW6+VtXa`6D?&G&wyXDdB^!PuK_ku#SKRrU??v>H^Y?ADI-`KI$6^u*x_) zH7)R&&?oA!rgd41pyIkobLOv}9uY8M@~Y{xqbALm6fr47bpG4dk0K|{l8%}aB{FG7 z+?fLtLMBX(j7dt^(7>F^b*+9j7*};2=^lg2nPcXI5>cW z!;(f26X698cvJ*