diff --git a/api/resource.rb b/api/resource.rb index b3b7fbaec166..0abc79d6f79b 100644 --- a/api/resource.rb +++ b/api/resource.rb @@ -26,6 +26,10 @@ module Properties attr_reader :description attr_reader :kind attr_reader :base_url + # URL to use for updating the resource. If not specified, the self link + # will be used. This currently can only be used with Terraform resources. + # TODO(#302): Add support for the other providers. + attr_reader :update_url attr_reader :self_link # This is useful in case you need to change the query made for # GET/DELETE requests only. In particular, this is often used @@ -223,6 +227,7 @@ def validate super check_optional_property :async, Api::Async check_optional_property :base_url, String + check_optional_property :update_url, String check_property :description, String check_optional_property :exclude, :boolean check_optional_property :kind, String diff --git a/build/ansible b/build/ansible index ceedcc7d5a37..200d0710fbd1 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit ceedcc7d5a37b7f51978bf71e205f934cb9ab7cd +Subproject commit 200d0710fbd14ed77f767b0ffb0008315e566a73 diff --git a/build/chef/compute b/build/chef/compute index 691bc36dfd94..8e8e5370a860 160000 --- a/build/chef/compute +++ b/build/chef/compute @@ -1 +1 @@ -Subproject commit 691bc36dfd94619fa990e0b8fab91cc29f3d4395 +Subproject commit 8e8e5370a860e91147b458547c038e06b55b0195 diff --git a/build/puppet/compute b/build/puppet/compute index d6540d7a2efb..8b513d74400b 160000 --- a/build/puppet/compute +++ b/build/puppet/compute @@ -1 +1 @@ -Subproject commit d6540d7a2efb3c3b37cc8f46bd130d34f92c9fc1 +Subproject commit 8b513d74400bf2155301af23cb2c3a2994a11a86 diff --git a/build/terraform b/build/terraform index 97b8419ea56f..cf4451177e5b 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 97b8419ea56fbbbfc6e71275f92e81e24915777e +Subproject commit cf4451177e5b8d4f5b428b6bdab396ec5e9c9ea1 diff --git a/coverage/.last_run.json b/coverage/.last_run.json index 69e2f9081150..3c082d7b3c6b 100644 --- a/coverage/.last_run.json +++ b/coverage/.last_run.json @@ -1,5 +1,5 @@ { "result": { - "covered_percent": 80.37 + "covered_percent": 80.43 } } diff --git a/products/compute/ansible.yaml b/products/compute/ansible.yaml index a1658abd3b24..3859ccb41f5d 100644 --- a/products/compute/ansible.yaml +++ b/products/compute/ansible.yaml @@ -146,6 +146,10 @@ overrides: !ruby/object:Provider::ResourceOverrides TargetVpnGateway: !ruby/object:Provider::Ansible::ResourceOverride exclude: false # Not yet implemented. + Autoscaler: !ruby/object:Provider::Ansible::ResourceOverride + exclude: true + RegionAutoscaler: !ruby/object:Provider::Ansible::ResourceOverride + exclude: true Snapshot: !ruby/object:Provider::Ansible::ResourceOverride exclude: true # Ansible tasks must alter infrastructure. diff --git a/products/compute/api.yaml b/products/compute/api.yaml index 41db83e2076c..789781ead6b3 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -130,6 +130,43 @@ objects: description: 'The URLs of the resources that are using this address.' item_type: Api::Type::String output: true + - !ruby/object:Api::Resource + name: 'Autoscaler' + kind: 'compute#autoscaler' + base_url: projects/{{project}}/zones/{{zone}}/autoscalers + update_url: projects/{{project}}/zones/{{zone}}/autoscalers?autoscaler={{name}} + exports: + - !ruby/object:Api::Type::SelfLink + name: selfLink + description: | + Represents an Autoscaler resource. + + Autoscalers allow you to automatically scale virtual machine instances in + managed instance groups according to an autoscaling policy that you + define. + references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Autoscaling Groups of Instances': 'https://cloud.google.com/compute/docs/autoscaler/' + api: 'https://cloud.google.com/compute/docs/reference/rest/v1/autoscalers' +<%= indent(compile_file({}, 'templates/zonal_async.yaml.erb'), 4) %> + parameters: + - !ruby/object:Api::Type::ResourceRef + name: 'zone' + resource: 'Zone' + imports: 'name' + description: | + URL of the zone where the instance group resides. + required: true + input: true + properties: +<%= indent(compile('products/compute/autoscaler_properties.yaml'), 6) %> + - !ruby/object:Api::Type::ResourceRef + name: 'target' + resource: 'InstanceGroupManager' + imports: 'selfLink' + description: | + URL of the managed instance group that this autoscaler will scale. + required: true - !ruby/object:Api::Resource name: 'BackendBucket' kind: 'compute#backendBucket' @@ -1919,6 +1956,9 @@ objects: name: 'InstanceGroupManager' kind: 'compute#instanceGroupManager' base_url: projects/{{project}}/zones/{{zone}}/instanceGroupManagers + exports: + - !ruby/object:Api::Type::SelfLink + name: 'selfLink' description: | Creates a managed instance group using the information that you specify in the request. After the group is created, it schedules an action to create @@ -2361,6 +2401,44 @@ objects: description: 'List of zones within the region' item_type: Api::Type::String output: true + - !ruby/object:Api::Resource + name: 'RegionAutoscaler' + kind: 'compute#autoscaler' + base_url: projects/{{project}}/regions/{{region}}/autoscalers + update_url: projects/{{project}}/regions/{{region}}/autoscalers?autoscaler={{name}} + exports: + - !ruby/object:Api::Type::SelfLink + name: selfLink + description: | + Represents an Autoscaler resource. + + Autoscalers allow you to automatically scale virtual machine instances in + managed instance groups according to an autoscaling policy that you + define. + references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Autoscaling Groups of Instances': 'https://cloud.google.com/compute/docs/autoscaler/' + api: 'https://cloud.google.com/compute/docs/reference/rest/v1/regionAutoscalers' +<%= indent(compile_file({}, 'templates/regional_async.yaml.erb'), 4) %> + parameters: + - !ruby/object:Api::Type::ResourceRef + name: 'region' + resource: 'Region' + imports: 'name' + description: | + URL of the region where the instance group resides. + required: true + input: true + properties: +<%= indent(compile('products/compute/autoscaler_properties.yaml'), 6) %> + - !ruby/object:Api::Type::String + name: 'target' + # TODO(#303): resourceref once RegionIGM exists + # resource: 'RegionInstanceGroupManager' + # imports: 'selfLink' + description: | + URL of the managed instance group that this autoscaler will scale. + required: true - !ruby/object:Api::Resource name: 'Route' kind: 'compute#route' diff --git a/products/compute/autoscaler_properties.yaml b/products/compute/autoscaler_properties.yaml new file mode 100644 index 000000000000..1b2ee0c8897c --- /dev/null +++ b/products/compute/autoscaler_properties.yaml @@ -0,0 +1,149 @@ +# 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::Type::Integer + name: 'id' + description: 'Unique identifier for the resource.' + output: true +- !ruby/object:Api::Type::Time + name: 'creationTimestamp' + description: 'Creation timestamp in RFC3339 text format.' + output: true +- !ruby/object:Api::Type::String + name: 'name' + description: | + Name of the resource. The name must be 1-63 characters long and match + the regular expression `[a-z]([-a-z0-9]*[a-z0-9])?` which means the + first character must be a lowercase letter, and all following + characters must be a dash, lowercase letter, or digit, except the last + character, which cannot be a dash. + required: true + input: true +- !ruby/object:Api::Type::String + name: 'description' + description: | + An optional description of this resource. +- !ruby/object:Api::Type::NestedObject + name: 'autoscalingPolicy' + description: | + The configuration parameters for the autoscaling algorithm. You can + define one or more of the policies for an autoscaler: cpuUtilization, + customMetricUtilizations, and loadBalancingUtilization. + + If none of these are specified, the default will be to autoscale based + on cpuUtilization to 0.6 or 60%. + required: true + properties: + - !ruby/object:Api::Type::Integer + name: 'minNumReplicas' + description: | + The minimum number of replicas that the autoscaler can scale down + to. This cannot be less than 0. If not provided, autoscaler will + choose a default value depending on maximum number of instances + allowed. + - !ruby/object:Api::Type::Integer + name: 'maxNumReplicas' + description: | + The maximum number of instances that the autoscaler can scale up + to. This is required when creating or updating an autoscaler. The + maximum number of replicas should not be lower than minimal number + of replicas. + required: true + - !ruby/object:Api::Type::Integer + name: 'coolDownPeriodSec' + description: | + The number of seconds that the autoscaler should wait before it + starts collecting information from a new instance. This prevents + the autoscaler from collecting information when the instance is + initializing, during which the collected usage would not be + reliable. The default time autoscaler waits is 60 seconds. + + Virtual machine initialization times might vary because of + numerous factors. We recommend that you test how long an + instance may take to initialize. To do this, create an instance + and time the startup process. + default_value: 60 + - !ruby/object:Api::Type::NestedObject + name: 'cpuUtilization' + description: | + Defines the CPU utilization policy that allows the autoscaler to + scale based on the average CPU utilization of a managed instance + group. + properties: + - !ruby/object:Api::Type::Double + name: 'utilizationTarget' + description: | + The target CPU utilization that the autoscaler should maintain. + Must be a float value in the range (0, 1]. If not specified, the + default is 0.6. + + If the CPU level is below the target utilization, the autoscaler + scales down the number of instances until it reaches the minimum + number of instances you specified or until the average CPU of + your instances reaches the target utilization. + + If the average CPU is above the target utilization, the autoscaler + scales up until it reaches the maximum number of instances you + specified or until the average utilization reaches the target + utilization. + - !ruby/object:Api::Type::Array + name: 'customMetricUtilizations' + description: | + Defines the CPU utilization policy that allows the autoscaler to + scale based on the average CPU utilization of a managed instance + group. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'metric' + description: | + The identifier (type) of the Stackdriver Monitoring metric. + The metric cannot have negative values. + + The metric must have a value type of INT64 or DOUBLE. + required: true + - !ruby/object:Api::Type::Double + name: 'utilizationTarget' + description: | + The target value of the metric that autoscaler should + maintain. This must be a positive value. A utilization + metric scales number of virtual machines handling requests + to increase or decrease proportionally to the metric. + + For example, a good metric to use as a utilizationTarget is + www.googleapis.com/compute/instance/network/received_bytes_count. + The autoscaler will work to keep this value constant for each + of the instances. + required: true + - !ruby/object:Api::Type::Enum + name: 'utilizationTargetType' + description: | + Defines how target utilization value is expressed for a + Stackdriver Monitoring metric. Either GAUGE, DELTA_PER_SECOND, + or DELTA_PER_MINUTE. + values: + - :GAUGE + - :DELTA_PER_SECOND + - :DELTA_PER_MINUTE + required: true + - !ruby/object:Api::Type::NestedObject + name: 'loadBalancingUtilization' + description: | + Configuration parameters of autoscaling based on a load balancer. + properties: + - !ruby/object:Api::Type::Double + name: 'utilizationTarget' + description: | + Fraction of backend capacity utilization (set in HTTP(s) load + balancing configuration) that autoscaler should maintain. Must + be a positive float value. If not defined, the default is 0.8. diff --git a/products/compute/chef.yaml b/products/compute/chef.yaml index e21a65971aec..a15a21aa0f53 100644 --- a/products/compute/chef.yaml +++ b/products/compute/chef.yaml @@ -70,6 +70,10 @@ overrides: !ruby/object:Provider::ResourceOverrides provider_helpers: - 'products/compute/helpers/ruby/provider_target_pool.rb' # Not yet implemented. + Autoscaler: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + RegionAutoscaler: !ruby/object:Provider::Chef::ResourceOverride + exclude: true SslPolicy: !ruby/object:Provider::Chef::ResourceOverride exclude: true TargetVpnGateway: !ruby/object:Provider::Chef::ResourceOverride diff --git a/products/compute/helpers/terraform/autoscaler_overrides.yaml b/products/compute/helpers/terraform/autoscaler_overrides.yaml new file mode 100644 index 000000000000..369824537b86 --- /dev/null +++ b/products/compute/helpers/terraform/autoscaler_overrides.yaml @@ -0,0 +1,48 @@ +# 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. + +id: !ruby/object:Provider::Terraform::PropertyOverride + exclude: true +name: !ruby/object:Provider::Terraform::PropertyOverride + validation: !ruby/object:Provider::Terraform::Validation + function: 'validateGCPName' +autoscalingPolicy.minNumReplicas: !ruby/object:Provider::Terraform::PropertyOverride + name: minReplicas + # Even though this field isn't actually required by the API, it makes our + # lives easier if we treat it as a required field. It has a default that is + # set by the API based on the value of maxNumReplicas. Normally in this case + # we would just set it to Optional+Computed. However, this currently doesn't + # work with nested fields. Make it required so we don't set the field to zero + # when our users aren't expecting us to. For more info, see + # https://github.com/GoogleCloudPlatform/magic-modules/issues/305. + required: true +autoscalingPolicy.maxNumReplicas: !ruby/object:Provider::Terraform::PropertyOverride + name: maxReplicas +autoscalingPolicy.coolDownPeriodSec: !ruby/object:Provider::Terraform::PropertyOverride + name: cooldownPeriod +autoscalingPolicy.cpuUtilization: !ruby/object:Provider::Terraform::PropertyOverride + default_from_api: true +autoscalingPolicy.cpuUtilization.utilizationTarget: !ruby/object:Provider::Terraform::PropertyOverride + name: target + required: true # See comment for minReplicas +autoscalingPolicy.customMetricUtilizations: !ruby/object:Provider::Terraform::PropertyOverride + name: metric +autoscalingPolicy.customMetricUtilizations.metric: !ruby/object:Provider::Terraform::PropertyOverride + name: name +autoscalingPolicy.customMetricUtilizations.utilizationTarget: !ruby/object:Provider::Terraform::PropertyOverride + name: target +autoscalingPolicy.customMetricUtilizations.utilizationTargetType: !ruby/object:Provider::Terraform::PropertyOverride + name: type +autoscalingPolicy.loadBalancingUtilization.utilizationTarget: !ruby/object:Provider::Terraform::PropertyOverride + name: target + required: true # See comment for minReplicas diff --git a/products/compute/puppet.yaml b/products/compute/puppet.yaml index 6de05341cb15..5746f568f9a3 100644 --- a/products/compute/puppet.yaml +++ b/products/compute/puppet.yaml @@ -103,6 +103,10 @@ overrides: !ruby/object:Provider::ResourceOverrides handlers: !ruby/object:Provider::Puppet::Handlers flush: raise 'Region cannot be edited' if @dirty # Not yet implemented. + Autoscaler: !ruby/object:Provider::Puppet::ResourceOverride + exclude: true + RegionAutoscaler: !ruby/object:Provider::Puppet::ResourceOverride + exclude: true TargetVpnGateway: !ruby/object:Provider::Puppet::ResourceOverride exclude: true SslPolicy: !ruby/object:Provider::Puppet::ResourceOverride diff --git a/products/compute/terraform.yaml b/products/compute/terraform.yaml index 0cc7df28af68..e28ad70e9db0 100644 --- a/products/compute/terraform.yaml +++ b/products/compute/terraform.yaml @@ -65,6 +65,70 @@ overrides: !ruby/object:Provider::ResourceOverrides docs: !ruby/object:Provider::Terraform::Docs attributes: | * `address`: The IP of the created resource. + Autoscaler: !ruby/object:Provider::Terraform::ResourceOverride + id_format: "{{zone}}/{{name}}" + examples: | + ```hcl + resource "google_compute_instance_template" "foobar" { + name = "foobar" + machine_type = "n1-standard-1" + can_ip_forward = false + + tags = ["foo", "bar"] + + disk { + source_image = "debian-cloud/debian-8" + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_target_pool" "foobar" { + name = "foobar" + } + + resource "google_compute_instance_group_manager" "foobar" { + name = "foobar" + zone = "us-central1-f" + + instance_template = "${google_compute_instance_template.foobar.self_link}" + target_pools = ["${google_compute_target_pool.foobar.self_link}"] + base_instance_name = "foobar" + } + + resource "google_compute_autoscaler" "foobar" { + name = "scaler" + zone = "us-central1-f" + target = "${google_compute_instance_group_manager.foobar.self_link}" + + autoscaling_policy = { + max_replicas = 5 + min_replicas = 1 + cooldown_period = 60 + + cpu_utilization { + target = 0.5 + } + } + } + ``` + properties: +<%= indent(compile_file({}, 'products/compute/helpers/terraform/autoscaler_overrides.yaml'), 6) %> + target: !ruby/object:Provider::Terraform::PropertyOverride + custom_expand: 'templates/terraform/custom_expand/compute_full_url.erb' + zone: !ruby/object:Provider::Terraform::PropertyOverride + required: false + default_from_api: true BackendBucket: !ruby/object:Provider::Terraform::ResourceOverride examples: | ```hcl @@ -345,6 +409,68 @@ overrides: !ruby/object:Provider::ResourceOverrides exclude: true Region: !ruby/object:Provider::Terraform::ResourceOverride exclude: true + RegionAutoscaler: !ruby/object:Provider::Terraform::ResourceOverride + id_format: "{{region}}/{{name}}" + examples: | + ```hcl + resource "google_compute_instance_template" "foobar" { + name = "foobar" + machine_type = "n1-standard-1" + can_ip_forward = false + + tags = ["foo", "bar"] + + disk { + source_image = "debian-cloud/debian-8" + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_target_pool" "foobar" { + name = "foobar" + } + + resource "google_compute_region_instance_group_manager" "foobar" { + name = "foobar" + region = "us-central1" + + instance_template = "${google_compute_instance_template.foobar.self_link}" + target_pools = ["${google_compute_target_pool.foobar.self_link}"] + base_instance_name = "foobar" + } + + resource "google_compute_region_autoscaler" "foobar" { + name = "scaler" + region = "us-central1" + target = "${google_compute_region_instance_group_manager.foobar.self_link}" + + autoscaling_policy = { + max_replicas = 5 + min_replicas = 1 + cooldown_period = 60 + + cpu_utilization { + target = 0.5 + } + } + } + ``` + properties: +<%= indent(compile_file({}, 'products/compute/helpers/terraform/autoscaler_overrides.yaml'), 6) %> + region: !ruby/object:Provider::Terraform::PropertyOverride + required: false + default_from_api: true Route: !ruby/object:Provider::Terraform::ResourceOverride examples: | ```hcl @@ -851,8 +977,12 @@ files: !ruby/object:Provider::Config::Files # Adding them here allows updating the tests as part of a MM pull request. 'google/resource_compute_address_test.go': 'templates/terraform/tests/resource_compute_address_test.go' + 'google/resource_compute_autoscaler_test.go': + 'templates/terraform/tests/resource_compute_autoscaler_test.go' 'google/resource_compute_global_address_test.go': 'templates/terraform/tests/resource_compute_global_address_test.go' + 'google/resource_compute_region_autoscaler_test.go': + 'templates/terraform/tests/resource_compute_region_autoscaler_test.go' 'google/resource_compute_target_https_proxy_test.go': 'templates/terraform/tests/resource_compute_target_https_proxy_test.go' 'google/resource_compute_target_ssl_proxy_test.go': diff --git a/templates/terraform/custom_expand/compute_full_url.erb b/templates/terraform/custom_expand/compute_full_url.erb new file mode 100644 index 000000000000..04a23f1db431 --- /dev/null +++ b/templates/terraform/custom_expand/compute_full_url.erb @@ -0,0 +1,21 @@ +<% if false # the license inside this if block pertains 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. +<% end -%> +func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + f, err := <%= build_expand_resource_ref('v.(string)', property) %> + if err != nil { + return nil, fmt.Errorf("Invalid value for <%= Google::StringUtils.underscore(property.name) -%>: %s", err) + } + return "https://www.googleapis.com/compute/v1/" + f.RelativeLink(), nil +} diff --git a/templates/terraform/expand_property_method.erb b/templates/terraform/expand_property_method.erb index 184f7b961fb6..3a41db093be3 100644 --- a/templates/terraform/expand_property_method.erb +++ b/templates/terraform/expand_property_method.erb @@ -48,7 +48,7 @@ func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d * if err != nil { return nil, err } - transformed["<%= prop.name -%>"] = transformed<%= titlelize_property(prop) -%> + transformed["<%= prop.api_name -%>"] = transformed<%= titlelize_property(prop) -%> <% end -%> diff --git a/templates/terraform/flatten_property_method.erb b/templates/terraform/flatten_property_method.erb index 7c0c9d46d593..a83825619d24 100644 --- a/templates/terraform/flatten_property_method.erb +++ b/templates/terraform/flatten_property_method.erb @@ -27,7 +27,7 @@ func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}) in transformed := make(map[string]interface{}) <% property.properties.each do |prop| -%> transformed["<%= Google::StringUtils.underscore(prop.name) -%>"] = - flatten<%= prefix -%><%= titlelize_property(property) -%><%= titlelize_property(prop) -%>(original["<%= prop.name -%>"]) + flatten<%= prefix -%><%= titlelize_property(property) -%><%= titlelize_property(prop) -%>(original["<%= prop.api_name -%>"]) <% end -%> return []interface{}{transformed} <% elsif property.is_a?(Api::Type::Array) && property.item_type.is_a?(Api::Type::NestedObject) -%> @@ -40,7 +40,7 @@ func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}) in original := raw.(map[string]interface{}) transformed = append(transformed, map[string]interface{}{ <% property.item_type.properties.each do |prop| -%> - "<%= Google::StringUtils.underscore(prop.name) -%>": flatten<%= prefix -%><%= titlelize_property(property) -%><%= titlelize_property(prop) -%>(original["<%= prop.name -%>"]), + "<%= Google::StringUtils.underscore(prop.name) -%>": flatten<%= prefix -%><%= titlelize_property(property) -%><%= titlelize_property(prop) -%>(original["<%= prop.api_name -%>"]), <% end -%> }) } diff --git a/templates/terraform/resource.erb b/templates/terraform/resource.erb index 9a921f0cd27f..ef34c2bc1623 100644 --- a/templates/terraform/resource.erb +++ b/templates/terraform/resource.erb @@ -322,7 +322,7 @@ func resource<%= resource_name -%>Update(d *schema.ResourceData, meta interface{ <% elsif object.custom_code.encoder -%> obj, err = resource<%= resource_name -%>Encoder(d, meta, obj) <% end -%> - url, err := replaceVars(d, config, "<%= self_link_url(object) -%>") + url, err := replaceVars(d, config, "<%= update_url(object, object.update_url) -%>") if err != nil { return err } diff --git a/templates/terraform/tests/resource_compute_autoscaler_test.go b/templates/terraform/tests/resource_compute_autoscaler_test.go new file mode 100644 index 000000000000..8f3e49ebb9b2 --- /dev/null +++ b/templates/terraform/tests/resource_compute_autoscaler_test.go @@ -0,0 +1,338 @@ +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeAutoscaler_basic(t *testing.T) { + t.Parallel() + + var ascaler compute.Autoscaler + + var it_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + var tp_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + var igm_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + var autoscaler_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeAutoscalerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeAutoscaler_basic(it_name, tp_name, igm_name, autoscaler_name), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeAutoscalerExists( + "google_compute_autoscaler.foobar", &ascaler), + ), + }, + + resource.TestStep{ + ResourceName: "google_compute_autoscaler.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeAutoscaler_update(t *testing.T) { + t.Parallel() + + var ascaler compute.Autoscaler + + var it_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + var tp_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + var igm_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + var autoscaler_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeAutoscalerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeAutoscaler_basic(it_name, tp_name, igm_name, autoscaler_name), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeAutoscalerExists( + "google_compute_autoscaler.foobar", &ascaler), + ), + }, + resource.TestStep{ + Config: testAccComputeAutoscaler_update(it_name, tp_name, igm_name, autoscaler_name), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeAutoscalerExists( + "google_compute_autoscaler.foobar", &ascaler), + testAccCheckComputeAutoscalerUpdated( + "google_compute_autoscaler.foobar", 10), + ), + }, + }, + }) +} + +func TestAccComputeAutoscaler_multicondition(t *testing.T) { + t.Parallel() + + var it_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + var tp_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + var igm_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + var autoscaler_name = fmt.Sprintf("autoscaler-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeAutoscalerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeAutoscaler_multicondition(it_name, tp_name, igm_name, autoscaler_name), + Check: testAccCheckComputeAutoscalerMultifunction("google_compute_autoscaler.foobar"), + }, + resource.TestStep{ + ResourceName: "google_compute_autoscaler.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckComputeAutoscalerDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_autoscaler" { + continue + } + + idParts := strings.Split(rs.Primary.ID, "/") + zone, name := idParts[0], idParts[1] + _, err := config.clientCompute.Autoscalers.Get( + config.Project, zone, name).Do() + if err == nil { + return fmt.Errorf("Autoscaler still exists") + } + } + + return nil +} + +func testAccCheckComputeAutoscalerExists(n string, ascaler *compute.Autoscaler) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + idParts := strings.Split(rs.Primary.ID, "/") + zone, name := idParts[0], idParts[1] + found, err := config.clientCompute.Autoscalers.Get( + config.Project, zone, name).Do() + if err != nil { + return err + } + + if found.Name != name { + return fmt.Errorf("Autoscaler not found") + } + + *ascaler = *found + + return nil + } +} + +func testAccCheckComputeAutoscalerMultifunction(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + idParts := strings.Split(rs.Primary.ID, "/") + zone, name := idParts[0], idParts[1] + found, err := config.clientCompute.Autoscalers.Get( + config.Project, zone, name).Do() + if err != nil { + return err + } + + if found.Name != name { + return fmt.Errorf("Autoscaler not found") + } + + if found.AutoscalingPolicy.CpuUtilization.UtilizationTarget == 0.5 && found.AutoscalingPolicy.LoadBalancingUtilization.UtilizationTarget == 0.5 { + return nil + } + return fmt.Errorf("Util target for CPU: %f, for LB: %f - should have been 0.5 for each.", + found.AutoscalingPolicy.CpuUtilization.UtilizationTarget, + found.AutoscalingPolicy.LoadBalancingUtilization.UtilizationTarget) + + } +} + +func testAccCheckComputeAutoscalerUpdated(n string, max int64) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + idParts := strings.Split(rs.Primary.ID, "/") + zone, name := idParts[0], idParts[1] + ascaler, err := config.clientCompute.Autoscalers.Get( + config.Project, zone, name).Do() + if err != nil { + return err + } + + if ascaler.AutoscalingPolicy.MaxNumReplicas != max { + return fmt.Errorf("maximum replicas incorrect") + } + + return nil + } +} + +func testAccComputeAutoscaler_scaffolding(it_name, tp_name, igm_name string) string { + return fmt.Sprintf(` +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "debian-cloud/debian-8-jessie-v20160803" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_target_pool" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" +} + +resource "google_compute_instance_group_manager" "foobar" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.foobar.self_link}" + target_pools = ["${google_compute_target_pool.foobar.self_link}"] + base_instance_name = "foobar" + zone = "us-central1-a" +} +`, it_name, tp_name, igm_name) + +} + +func testAccComputeAutoscaler_basic(it_name, tp_name, igm_name, autoscaler_name string) string { + return testAccComputeAutoscaler_scaffolding(it_name, tp_name, igm_name) + fmt.Sprintf(` +resource "google_compute_autoscaler" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + zone = "us-central1-a" + target = "${google_compute_instance_group_manager.foobar.self_link}" + autoscaling_policy = { + max_replicas = 5 + min_replicas = 1 + cooldown_period = 60 + cpu_utilization = { + target = 0.5 + } + } + +} +`, autoscaler_name) +} + +func testAccComputeAutoscaler_update(it_name, tp_name, igm_name, autoscaler_name string) string { + return testAccComputeAutoscaler_scaffolding(it_name, tp_name, igm_name) + fmt.Sprintf(` +resource "google_compute_autoscaler" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + zone = "us-central1-a" + target = "${google_compute_instance_group_manager.foobar.self_link}" + autoscaling_policy = { + max_replicas = 10 + min_replicas = 1 + cooldown_period = 60 + cpu_utilization = { + target = 0.5 + } + } + +} +`, autoscaler_name) +} + +func testAccComputeAutoscaler_multicondition(it_name, tp_name, igm_name, autoscaler_name string) string { + return testAccComputeAutoscaler_scaffolding(it_name, tp_name, igm_name) + fmt.Sprintf(` +resource "google_compute_autoscaler" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + zone = "us-central1-a" + target = "${google_compute_instance_group_manager.foobar.self_link}" + autoscaling_policy = { + max_replicas = 10 + min_replicas = 1 + cooldown_period = 60 + cpu_utilization = { + target = 0.5 + } + load_balancing_utilization = { + target = 0.5 + } + metric { + name = "compute.googleapis.com/instance/network/received_bytes_count" + target = 75 + type = "GAUGE" + } + metric { + name = "compute.googleapis.com/instance/network/sent_bytes_count" + target = 50 + type = "GAUGE" + } + } + +} +`, autoscaler_name) +} diff --git a/templates/terraform/tests/resource_compute_region_autoscaler_test.go b/templates/terraform/tests/resource_compute_region_autoscaler_test.go new file mode 100644 index 000000000000..4a5ee3c87efd --- /dev/null +++ b/templates/terraform/tests/resource_compute_region_autoscaler_test.go @@ -0,0 +1,271 @@ +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeRegionAutoscaler_basic(t *testing.T) { + var ascaler compute.Autoscaler + + var it_name = fmt.Sprintf("region-autoscaler-test-%s", acctest.RandString(10)) + var tp_name = fmt.Sprintf("region-autoscaler-test-%s", acctest.RandString(10)) + var igm_name = fmt.Sprintf("region-autoscaler-test-%s", acctest.RandString(10)) + var autoscaler_name = fmt.Sprintf("region-autoscaler-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionAutoscalerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRegionAutoscaler_basic(it_name, tp_name, igm_name, autoscaler_name), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionAutoscalerExists( + "google_compute_region_autoscaler.foobar", &ascaler), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_region_autoscaler.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRegionAutoscaler_update(t *testing.T) { + var ascaler compute.Autoscaler + + var it_name = fmt.Sprintf("region-autoscaler-test-%s", acctest.RandString(10)) + var tp_name = fmt.Sprintf("region-autoscaler-test-%s", acctest.RandString(10)) + var igm_name = fmt.Sprintf("region-autoscaler-test-%s", acctest.RandString(10)) + var autoscaler_name = fmt.Sprintf("region-autoscaler-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionAutoscalerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRegionAutoscaler_basic(it_name, tp_name, igm_name, autoscaler_name), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionAutoscalerExists( + "google_compute_region_autoscaler.foobar", &ascaler), + ), + }, + resource.TestStep{ + Config: testAccComputeRegionAutoscaler_update(it_name, tp_name, igm_name, autoscaler_name), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionAutoscalerExists( + "google_compute_region_autoscaler.foobar", &ascaler), + testAccCheckComputeRegionAutoscalerUpdated( + "google_compute_region_autoscaler.foobar", 10), + ), + }, + }, + }) +} + +func testAccCheckComputeRegionAutoscalerDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_region_autoscaler" { + continue + } + + idParts := strings.Split(rs.Primary.ID, "/") + region, name := idParts[0], idParts[1] + _, err := config.clientCompute.RegionAutoscalers.Get(config.Project, region, name).Do() + if err == nil { + return fmt.Errorf("Autoscaler still exists") + } + } + + return nil +} + +func testAccCheckComputeRegionAutoscalerExists(n string, ascaler *compute.Autoscaler) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + idParts := strings.Split(rs.Primary.ID, "/") + region, name := idParts[0], idParts[1] + found, err := config.clientCompute.RegionAutoscalers.Get(config.Project, region, name).Do() + if err != nil { + return err + } + + if found.Name != name { + return fmt.Errorf("Autoscaler not found") + } + + *ascaler = *found + + return nil + } +} + +func testAccCheckComputeRegionAutoscalerUpdated(n string, max int64) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + idParts := strings.Split(rs.Primary.ID, "/") + region, name := idParts[0], idParts[1] + ascaler, err := config.clientCompute.RegionAutoscalers.Get(config.Project, region, name).Do() + if err != nil { + return err + } + + if ascaler.AutoscalingPolicy.MaxNumReplicas != max { + return fmt.Errorf("maximum replicas incorrect") + } + + return nil + } +} + +func testAccComputeRegionAutoscaler_basic(it_name, tp_name, igm_name, autoscaler_name string) string { + return fmt.Sprintf(` +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "debian-cloud/debian-8-jessie-v20160803" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_target_pool" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" +} + +resource "google_compute_region_instance_group_manager" "foobar" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.foobar.self_link}" + target_pools = ["${google_compute_target_pool.foobar.self_link}"] + base_instance_name = "foobar" + region = "us-central1" +} + +resource "google_compute_region_autoscaler" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + region = "us-central1" + target = "${google_compute_region_instance_group_manager.foobar.self_link}" + autoscaling_policy = { + max_replicas = 5 + min_replicas = 1 + cooldown_period = 60 + cpu_utilization = { + target = 0.5 + } + } + +} +`, it_name, tp_name, igm_name, autoscaler_name) +} + +func testAccComputeRegionAutoscaler_update(it_name, tp_name, igm_name, autoscaler_name string) string { + return fmt.Sprintf(` +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "debian-cloud/debian-8-jessie-v20160803" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_target_pool" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" +} + +resource "google_compute_region_instance_group_manager" "foobar" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.foobar.self_link}" + target_pools = ["${google_compute_target_pool.foobar.self_link}"] + base_instance_name = "foobar" + region = "us-central1" +} + +resource "google_compute_region_autoscaler" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + region = "us-central1" + target = "${google_compute_region_instance_group_manager.foobar.self_link}" + autoscaling_policy = { + max_replicas = 10 + min_replicas = 1 + cooldown_period = 60 + cpu_utilization = { + target = 0.5 + } + } + +} +`, it_name, tp_name, igm_name, autoscaler_name) +}