diff --git a/build/terraform b/build/terraform index 07af08027ed8..966229afbea2 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 07af08027ed88a90f6ecfdee2ad71c048f005fa6 +Subproject commit 966229afbea201ac17047a22fe5bbb39796714ca diff --git a/build/terraform-beta b/build/terraform-beta index 0bce4ac22cf4..12d01b8b4442 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 0bce4ac22cf4ebf371a0a38d0c69b7b09876a65e +Subproject commit 12d01b8b4442821554e091c189e830a05100d30b diff --git a/build/terraform-mapper b/build/terraform-mapper index 43007d527e20..32bf92893d78 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 43007d527e20b5357d5767f18cf465bb1ccc0d58 +Subproject commit 32bf92893d78d82bce7ddc4aac384d08f160a23a diff --git a/products/logging/api.yaml b/products/logging/api.yaml new file mode 100644 index 000000000000..af89a798f0e6 --- /dev/null +++ b/products/logging/api.yaml @@ -0,0 +1,190 @@ +# Copyright 2019 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: Logging +display_name: Stackdriver Logging +versions: + - !ruby/object:Api::Product::Version + name: ga + base_url: https://logging.googleapis.com/v2/ +scopes: + - https://www.googleapis.com/auth/cloud-platform +apis_required: + - !ruby/object:Api::Product::ApiReference + name: Stackdriver Logging API + url: https://console.cloud.google.com/apis/library/logging.googleapis.com/ +objects: + - !ruby/object:Api::Resource + name: "Metric" + base_url: projects/{{project}}/metrics + self_link: "projects/{{project}}/metrics/{{%name}}" + update_verb: :PUT + description: | + Logs-based metric can also be used to extract values from logs and create a a distribution + of the values. The distribution records the statistics of the extracted values along with + an optional histogram of the values as specified by the bucket options. + references: !ruby/object:Api::Resource::ReferenceLinks + guides: + "Official Documentation": "https://cloud.google.com/logging/docs/apis" + api: "https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.metrics/create" + properties: + - !ruby/object:Api::Type::String + name: name + description: | + The client-assigned metric identifier. Examples - "error_count", "nginx/requests". + Metric identifiers are limited to 100 characters and can include only the following + characters A-Z, a-z, 0-9, and the special characters _-.,+!*',()%/. The forward-slash + character (/) denotes a hierarchy of name pieces, and it cannot be the first character + of the name. + required: true + - !ruby/object:Api::Type::String + name: description + description: | + A description of this metric, which is used in documentation. The maximum length of the + description is 8000 characters. + required: false + - !ruby/object:Api::Type::String + name: filter + description: | + An advanced logs filter (https://cloud.google.com/logging/docs/view/advanced-filters) which + is used to match log entries. + required: true + - !ruby/object:Api::Type::NestedObject + name: metricDescriptor + description: | + The metric descriptor associated with the logs-based metric. + required: true + properties: + - !ruby/object:Api::Type::Enum + name: valueType + description: | + Whether the measurement is an integer, a floating-point number, etc. + Some combinations of metricKind and valueType might not be supported. + values: + - :BOOL + - :INT64 + - :DOUBLE + - :STRING + - :DISTRIBUTION + - :MONEY + required: true + - !ruby/object:Api::Type::Enum + name: metricKind + description: | + Whether the metric records instantaneous values, changes to a value, etc. + Some combinations of metricKind and valueType might not be supported. + values: + - :DELTA + - :GAUGE + - :CUMULATIVE + required: true + - !ruby/object:Api::Type::Array + name: labels + description: | + The set of labels that can be used to describe a specific instance of this metric type. For + example, the appengine.googleapis.com/http/server/response_latencies metric type has a label + for the HTTP response code, response_code, so you can look at latencies for successful responses + or just for responses that failed. + required: false + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: key + description: | + The label key. + required: true + - !ruby/object:Api::Type::String + name: description + description: | + A human-readable description for the label. + required: false + - !ruby/object:Api::Type::Enum + name: valueType + description: | + The type of data that can be assigned to the label. + values: + - :BOOL + - :INT64 + - :STRING + required: false + default_value: :STRING + - !ruby/object:Api::Type::KeyValuePairs + name: labelExtractors + description: | + A map from a label key string to an extractor expression which is used to extract data from a log + entry field and assign as the label value. Each label key specified in the LabelDescriptor must + have an associated extractor expression in this map. The syntax of the extractor expression is + the same as for the valueExtractor field. + - !ruby/object:Api::Type::String + name: valueExtractor + description: | + A valueExtractor is required when using a distribution logs-based metric to extract the values to + record from a log entry. Two functions are supported for value extraction - EXTRACT(field) or + REGEXP_EXTRACT(field, regex). The argument are 1. field - The name of the log entry field from which + the value is to be extracted. 2. regex - A regular expression using the Google RE2 syntax + (https://github.com/google/re2/wiki/Syntax) with a single capture group to extract data from the specified + log entry field. The value of the field is converted to a string before applying the regex. It is an + error to specify a regex that does not include exactly one capture group. + - !ruby/object:Api::Type::NestedObject + name: bucketOptions + description: | + The bucketOptions are required when the logs-based metric is using a DISTRIBUTION value type and it + describes the bucket boundaries used to create a histogram of the extracted values. + properties: + - !ruby/object:Api::Type::NestedObject + name: linearBuckets + description: | + Specifies a linear sequence of buckets that all have the same width (except overflow and underflow). + Each bucket represents a constant absolute uncertainty on the specific value in the bucket. + properties: + - !ruby/object:Api::Type::Integer + name: numFiniteBuckets + description: | + Must be greater than 0. + - !ruby/object:Api::Type::Integer + name: width + description: | + Must be greater than 0. + - !ruby/object:Api::Type::Double + name: offset + description: | + Lower bound of the first bucket. + - !ruby/object:Api::Type::NestedObject + name: exponentialBuckets + description: | + Specifies an exponential sequence of buckets that have a width that is proportional to the value of + the lower bound. Each bucket represents a constant relative uncertainty on a specific value in the bucket. + properties: + - !ruby/object:Api::Type::Integer + name: numFiniteBuckets + description: | + Must be greater than 0. + - !ruby/object:Api::Type::Integer + name: growthFactor + description: | + Must be greater than 1. + - !ruby/object:Api::Type::Double + name: scale + description: | + Must be greater than 0. + - !ruby/object:Api::Type::NestedObject + name: explicit + description: | + Specifies a set of buckets with arbitrary widths. + properties: + - !ruby/object:Api::Type::Array + name: bounds + item_type: Api::Type::String + description: | + The values must be monotonically increasing. diff --git a/products/logging/terraform.yaml b/products/logging/terraform.yaml new file mode 100644 index 000000000000..d9e0600f030c --- /dev/null +++ b/products/logging/terraform.yaml @@ -0,0 +1,36 @@ +# Copyright 2019 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::Terraform::Config +overrides: !ruby/object:Overrides::ResourceOverrides + Metric: !ruby/object:Overrides::Terraform::ResourceOverride + id_format: "{{name}}" + import_format: ["{{name}}"] + mutex: customMetric/{{project}} + examples: + - !ruby/object:Provider::Terraform::Examples + name: "logging_metric_basic" + primary_resource_id: "logging_metric" + vars: + logging_metric_name: "my-(custom)/metric" + custom_code: !ruby/object:Provider::Terraform::CustomCode + custom_import: templates/terraform/custom_import/self_link_as_name.erb + post_create: templates/terraform/post_create/set_computed_name.erb + properties: + metricDescriptor.labels.valueType: !ruby/object:Overrides::Terraform::PropertyOverride + custom_flatten: 'templates/terraform/custom_flatten/default_if_empty.erb' + +files: !ruby/object:Provider::Config::Files + # These files have templating (ERB) code that will be run. + # This is usually to add licensing info, autogeneration notices, etc. + compile: +<%= lines(indent(compile('provider/terraform/product~compile.yaml'), 4)) -%> diff --git a/templates/terraform/examples/logging_metric_basic.tf.erb b/templates/terraform/examples/logging_metric_basic.tf.erb new file mode 100644 index 000000000000..9ec050790734 --- /dev/null +++ b/templates/terraform/examples/logging_metric_basic.tf.erb @@ -0,0 +1,22 @@ +resource "google_logging_metric" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]["logging_metric_name"] %>" + filter = "resource.type=gae_app AND severity>=ERROR" + metric_descriptor { + metric_kind = "DELTA" + value_type = "DISTRIBUTION" + labels { + key = "mass" + value_type = "STRING" + description = "amount of matter" + } + } + value_extractor = "EXTRACT(jsonPayload.request)" + label_extractors = { "mass": "EXTRACT(jsonPayload.request)" } + bucket_options { + linear_buckets { + num_finite_buckets = 3 + width = 1 + offset = 1 + } + } +} diff --git a/third_party/terraform/tests/resource_logging_metric_test.go b/third_party/terraform/tests/resource_logging_metric_test.go new file mode 100644 index 000000000000..f1585e1e9554 --- /dev/null +++ b/third_party/terraform/tests/resource_logging_metric_test.go @@ -0,0 +1,53 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccLoggingMetric_update(t *testing.T) { + t.Parallel() + + suffix := acctest.RandString(10) + filter := "resource.type=gae_app AND severity>=ERROR" + updatedFilter := "resource.type=gae_app AND severity=ERROR" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLoggingMetricDestroy, + Steps: []resource.TestStep{ + { + Config: testAccLoggingMetric_update(suffix, filter), + }, + { + ResourceName: "google_logging_metric.logging_metric", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccLoggingMetric_update(suffix, updatedFilter), + }, + { + ResourceName: "google_logging_metric.logging_metric", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccLoggingMetric_update(suffix string, filter string) string { + return fmt.Sprintf(` +resource "google_logging_metric" "logging_metric" { + name = "my-custom-metric-%s" + filter = "%s" + metric_descriptor { + metric_kind = "DELTA" + value_type = "INT64" + } +}`, suffix, filter) +} diff --git a/third_party/terraform/utils/provider.go.erb b/third_party/terraform/utils/provider.go.erb index 0f1531d6b662..df7afaa419e6 100644 --- a/third_party/terraform/utils/provider.go.erb +++ b/third_party/terraform/utils/provider.go.erb @@ -211,6 +211,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_folder_organization_policy": resourceGoogleFolderOrganizationPolicy(), "google_logging_billing_account_sink": resourceLoggingBillingAccountSink(), "google_logging_billing_account_exclusion": ResourceLoggingExclusion(BillingAccountLoggingExclusionSchema, NewBillingAccountLoggingExclusionUpdater, billingAccountLoggingExclusionIdParseFunc), + "google_logging_metric": resourceLoggingMetric(), "google_logging_organization_sink": resourceLoggingOrganizationSink(), "google_logging_organization_exclusion": ResourceLoggingExclusion(OrganizationLoggingExclusionSchema, NewOrganizationLoggingExclusionUpdater, organizationLoggingExclusionIdParseFunc), "google_logging_folder_sink": resourceLoggingFolderSink(), diff --git a/third_party/terraform/utils/transport.go b/third_party/terraform/utils/transport.go index dc847552f749..23970c47d94b 100644 --- a/third_party/terraform/utils/transport.go +++ b/third_party/terraform/utils/transport.go @@ -120,7 +120,8 @@ func addQueryParams(rawurl string, params map[string]string) (string, error) { } func replaceVars(d TerraformResourceData, config *Config, linkTmpl string) (string, error) { - re := regexp.MustCompile("{{([[:word:]]+)}}") + // https://github.com/google/re2/wiki/Syntax + re := regexp.MustCompile("{{([%[:word:]]+)}}") f, err := buildReplacementFunc(re, d, config, linkTmpl) if err != nil { return "", err @@ -128,6 +129,9 @@ func replaceVars(d TerraformResourceData, config *Config, linkTmpl string) (stri return re.ReplaceAllStringFunc(linkTmpl, f), nil } +// This function replaces references to Terraform properties (in the form of {{var}}) with their value in Terraform +// It also replaces {{project}}, {{region}}, and {{zone}} with their appropriate values +// This function supports URL-encoding the result by prepending '%' to the field name e.g. {{%var}} func buildReplacementFunc(re *regexp.Regexp, d TerraformResourceData, config *Config, linkTmpl string) (func(string) string, error) { var project, region, zone string var err error @@ -164,9 +168,16 @@ func buildReplacementFunc(re *regexp.Regexp, d TerraformResourceData, config *Co if m == "zone" { return zone } - v, ok := d.GetOkExists(m) - if ok { - return fmt.Sprintf("%v", v) + if string(m[0]) == "%" { + v, ok := d.GetOkExists(m[1:]) + if ok { + return url.PathEscape(fmt.Sprintf("%v", v)) + } + } else { + v, ok := d.GetOkExists(m) + if ok { + return fmt.Sprintf("%v", v) + } } return "" }