Revert "remove switch statements between api versions for compute"

This reverts commit bf1b3a24e8c1cac832e74d9d0e145c7010a4c2f1.
This commit is contained in:
Paddy Carver 2018-05-04 07:51:45 -07:00
parent d59fcbbc59
commit aa2626b490
13 changed files with 1021 additions and 151 deletions

254
google/api_versions.go Normal file
View File

@ -0,0 +1,254 @@
package google
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
type ApiVersion uint8
const (
v1 ApiVersion = iota
v0beta
v1beta1
)
var OrderedComputeApiVersions = []ApiVersion{
v0beta,
v1,
}
var OrderedContainerApiVersions = []ApiVersion{
v1beta1,
v1,
}
// Convert between two types by converting to/from JSON. Intended to switch
// between multiple API versions, as they are strict supersets of one another.
// item and out are pointers to structs
func Convert(item, out interface{}) error {
bytes, err := json.Marshal(item)
if err != nil {
return err
}
err = json.Unmarshal(bytes, out)
if err != nil {
return err
}
// Converting between maps and structs only occurs when autogenerated resources convert the result
// of an HTTP request. Those results do not contain omitted fields, so no need to set them.
if _, ok := item.(map[string]interface{}); !ok {
setOmittedFields(item, out)
}
return nil
}
func setOmittedFields(item, out interface{}) {
// Both inputs must be pointers, see https://blog.golang.org/laws-of-reflection:
// "To modify a reflection object, the value must be settable."
iVal := reflect.ValueOf(item).Elem()
oVal := reflect.ValueOf(out).Elem()
// Loop through all the fields of the struct to look for omitted fields and nested fields
for i := 0; i < iVal.NumField(); i++ {
iField := iVal.Field(i)
if isEmptyValue(iField) {
continue
}
fieldInfo := iVal.Type().Field(i)
oField := oVal.FieldByName(fieldInfo.Name)
// Only look at fields that exist in the output struct
if !oField.IsValid() {
continue
}
// If the field contains a 'json:"="' tag, then it was omitted from the Marshal/Unmarshal
// call and needs to be added back in.
if fieldInfo.Tag.Get("json") == "-" {
oField.Set(iField)
}
// If this field is a struct, *struct, []struct, or []*struct, recurse.
if iField.Kind() == reflect.Struct {
setOmittedFields(iField.Addr().Interface(), oField.Addr().Interface())
}
if iField.Kind() == reflect.Ptr && iField.Type().Elem().Kind() == reflect.Struct {
setOmittedFields(iField.Interface(), oField.Interface())
}
if iField.Kind() == reflect.Slice && iField.Type().Elem().Kind() == reflect.Struct {
for j := 0; j < iField.Len(); j++ {
setOmittedFields(iField.Index(j).Addr().Interface(), oField.Index(j).Addr().Interface())
}
}
if iField.Kind() == reflect.Slice && iField.Type().Elem().Kind() == reflect.Ptr &&
iField.Type().Elem().Elem().Kind() == reflect.Struct {
for j := 0; j < iField.Len(); j++ {
setOmittedFields(iField.Index(j).Interface(), oField.Index(j).Interface())
}
}
}
}
type TerraformResourceData interface {
HasChange(string) bool
GetOk(string) (interface{}, bool)
Set(string, interface{}) error
SetId(string)
Id() string
}
// Compare the fields set in schema against a list of features and their versions to determine
// what version of the API is required in order to manage the resource.
func getApiVersion(d TerraformResourceData, resourceVersion ApiVersion, features []Feature, maxVersionFunc func(map[ApiVersion]struct{}) ApiVersion) ApiVersion {
versions := map[ApiVersion]struct{}{resourceVersion: struct{}{}}
for _, feature := range features {
if feature.InUseByDefault(d) {
versions[feature.Version] = struct{}{}
}
}
return maxVersionFunc(versions)
}
func getComputeApiVersion(d TerraformResourceData, resourceVersion ApiVersion, features []Feature) ApiVersion {
return getApiVersion(d, resourceVersion, features, maxComputeVersion)
}
func getContainerApiVersion(d TerraformResourceData, resourceVersion ApiVersion, features []Feature) ApiVersion {
return getApiVersion(d, resourceVersion, features, maxContainerVersion)
}
// Compare the fields set in schema against a list of features and their version, and a
// list of features that exist at the base resource version that can only be update at some other
// version, to determine what version of the API is required in order to update the resource.
func getApiVersionUpdate(d TerraformResourceData, resourceVersion ApiVersion, features, updateOnlyFields []Feature, maxVersionFunc func(map[ApiVersion]struct{}) ApiVersion) ApiVersion {
versions := map[ApiVersion]struct{}{resourceVersion: struct{}{}}
for _, feature := range features {
if feature.InUseByUpdate(d) {
versions[feature.Version] = struct{}{}
}
}
for _, feature := range updateOnlyFields {
if feature.HasChangeBy(d) {
versions[feature.Version] = struct{}{}
}
}
return maxVersionFunc(versions)
}
func getComputeApiVersionUpdate(d TerraformResourceData, resourceVersion ApiVersion, features, updateOnlyFields []Feature) ApiVersion {
return getApiVersionUpdate(d, resourceVersion, features, updateOnlyFields, maxComputeVersion)
}
func getContainerApiVersionUpdate(d TerraformResourceData, resourceVersion ApiVersion, features, updateOnlyFields []Feature) ApiVersion {
return getApiVersionUpdate(d, resourceVersion, features, updateOnlyFields, maxContainerVersion)
}
// A field of a resource and the version of the Compute API required to use it.
type Feature struct {
Version ApiVersion
// Path to the beta field.
//
// The feature is considered to be in-use if the field referenced by "Item" is set in the state.
// The path can reference:
// - a beta field at the top-level (e.g. "min_cpu_platform").
// - a beta field nested inside a list (e.g. "network_interface.*.alias_ip_range" is considered to be
// in-use if the "alias_ip_range" field is set in the state for any of the network interfaces).
//
// Note: beta field nested inside a SET are NOT supported at the moment.
Item string
// Optional, only set if your field has a default value.
// If the value for the field is equal to the DefaultValue, we assume the beta feature is not activated.
DefaultValue interface{}
}
// Returns true when a feature has been modified.
// This is most important when updating a resource to remove versioned feature usage; if the
// resource is reverting to its base version, it needs to perform a final update at the higher
// version in order to remove high version features.
func (s Feature) HasChangeBy(d TerraformResourceData) bool {
return d.HasChange(s.Item)
}
type InUseFunc func(d TerraformResourceData, path string, defaultValue interface{}) bool
func defaultInUseFunc(d TerraformResourceData, path string, defaultValue interface{}) bool {
// At read and delete time, there is no change.
// At create time, all fields are marked has changed. We should only consider the feature active if the field has
// a value set and that this value is not the default value.
value, ok := d.GetOk(path)
return ok && value != defaultValue
}
func updateInUseFunc(d TerraformResourceData, path string, defaultValue interface{}) bool {
// During a resource update, if the beta field has changes, the feature is considered active even if the new value
// is the default value. This is because the beta API must be called to change the value of the field back to the
// default value.
value, ok := d.GetOk(path)
return (ok && value != defaultValue) || d.HasChange(path)
}
// Return true when a feature appears in schema and doesn't hold the default value.
func (s Feature) InUseByDefault(d TerraformResourceData) bool {
return inUseBy(d, s.Item, s.DefaultValue, defaultInUseFunc)
}
func (s Feature) InUseByUpdate(d TerraformResourceData) bool {
return inUseBy(d, s.Item, s.DefaultValue, updateInUseFunc)
}
func inUseBy(d TerraformResourceData, path string, defaultValue interface{}, inUseFunc InUseFunc) bool {
pos := strings.Index(path, "*")
if pos == -1 {
return inUseFunc(d, path, defaultValue)
}
prefix := path[0:pos]
suffix := path[pos+1:]
v, ok := d.GetOk(prefix + "#")
if !ok {
return false
}
count := v.(int)
for i := 0; i < count; i++ {
nestedPath := fmt.Sprintf("%s%d%s", prefix, i, suffix)
if inUseBy(d, nestedPath, defaultValue, inUseFunc) {
return true
}
}
return false
}
func maxVersion(versionsInUse map[ApiVersion]struct{}, orderedVersions []ApiVersion) ApiVersion {
for _, version := range orderedVersions {
if _, ok := versionsInUse[version]; ok {
return version
}
}
// Fallback to the final, most stable version
return orderedVersions[len(orderedVersions)-1]
}
func maxComputeVersion(versionsInUse map[ApiVersion]struct{}) ApiVersion {
return maxVersion(versionsInUse, OrderedComputeApiVersions)
}
func maxContainerVersion(versionsInUse map[ApiVersion]struct{}) ApiVersion {
return maxVersion(versionsInUse, OrderedContainerApiVersions)
}

View File

@ -4,7 +4,6 @@ import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
)
@ -124,7 +123,3 @@ func flattenSecondaryRanges(secondaryRanges []*compute.SubnetworkSecondaryRange)
func createSubnetID(s *compute.Subnetwork) string {
return fmt.Sprintf("%s/%s", s.Region, s.Name)
}
func createSubnetIDBeta(s *computeBeta.Subnetwork) string {
return fmt.Sprintf("%s/%s", s.Region, s.Name)
}

View File

@ -12,6 +12,9 @@ import (
compute "google.golang.org/api/compute/v1"
)
var BackendServiceBaseApiVersion = v1
var BackendServiceVersionedFeatures = []Feature{Feature{Version: v0beta, Item: "security_policy"}}
func resourceComputeBackendService() *schema.Resource {
return &schema.Resource{
Create: resourceComputeBackendServiceCreate,
@ -285,6 +288,7 @@ func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{
}
func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, BackendServiceBaseApiVersion, BackendServiceVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -292,9 +296,25 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{})
return err
}
service, err := config.clientComputeBeta.BackendServices.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Backend Service %q", d.Get("name").(string)))
service := &computeBeta.BackendService{}
switch computeApiVersion {
case v1:
v1Service, err := config.clientCompute.BackendServices.Get(
project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Backend Service %q", d.Get("name").(string)))
}
err = Convert(v1Service, service)
if err != nil {
return err
}
case v0beta:
var err error
service, err = config.clientComputeBeta.BackendServices.Get(
project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Backend Service %q", d.Get("name").(string)))
}
}
d.Set("name", service.Name)

View File

@ -10,8 +10,12 @@ import (
"github.com/hashicorp/terraform/helper/validation"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
)
var FirewallBaseApiVersion = v1
var FirewallVersionedFeatures = []Feature{}
func resourceComputeFirewall() *schema.Resource {
return &schema.Resource{
Create: resourceComputeFirewallCreate,
@ -189,6 +193,7 @@ func resourceComputeFirewallRuleHash(v interface{}) int {
}
func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, FirewallBaseApiVersion, FirewallVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -201,9 +206,30 @@ func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) err
return err
}
op, err := config.clientComputeBeta.Firewalls.Insert(project, firewall).Do()
if err != nil {
return fmt.Errorf("Error creating firewall: %s", err)
var op interface{}
switch computeApiVersion {
case v1:
firewallV1 := &compute.Firewall{}
err = Convert(firewall, firewallV1)
if err != nil {
return err
}
op, err = config.clientCompute.Firewalls.Insert(project, firewallV1).Do()
if err != nil {
return fmt.Errorf("Error creating firewall: %s", err)
}
case v0beta:
firewallV0Beta := &computeBeta.Firewall{}
err = Convert(firewall, firewallV0Beta)
if err != nil {
return err
}
op, err = config.clientComputeBeta.Firewalls.Insert(project, firewallV0Beta).Do()
if err != nil {
return fmt.Errorf("Error creating firewall: %s", err)
}
}
// It probably maybe worked, so store the ID now
@ -242,6 +268,7 @@ func flattenFirewallDenied(denied []*computeBeta.FirewallDenied) []map[string]in
}
func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, FirewallBaseApiVersion, FirewallVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -249,9 +276,28 @@ func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error
return err
}
firewall, err := config.clientComputeBeta.Firewalls.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Firewall %q", d.Get("name").(string)))
firewall := &computeBeta.Firewall{}
switch computeApiVersion {
case v1:
firewallV1, err := config.clientCompute.Firewalls.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Firewall %q", d.Get("name").(string)))
}
err = Convert(firewallV1, firewall)
if err != nil {
return err
}
case v0beta:
firewallV0Beta, err := config.clientComputeBeta.Firewalls.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Firewall %q", d.Get("name").(string)))
}
err = Convert(firewallV0Beta, firewall)
if err != nil {
return err
}
}
d.Set("self_link", ConvertSelfLinkToV1(firewall.SelfLink))
@ -273,6 +319,7 @@ func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error
}
func resourceComputeFirewallUpdate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersionUpdate(d, FirewallBaseApiVersion, FirewallVersionedFeatures, []Feature{})
config := meta.(*Config)
project, err := getProject(d, config)
@ -287,9 +334,30 @@ func resourceComputeFirewallUpdate(d *schema.ResourceData, meta interface{}) err
return err
}
op, err := config.clientComputeBeta.Firewalls.Update(project, d.Id(), firewall).Do()
if err != nil {
return fmt.Errorf("Error updating firewall: %s", err)
var op interface{}
switch computeApiVersion {
case v1:
firewallV1 := &compute.Firewall{}
err = Convert(firewall, firewallV1)
if err != nil {
return err
}
op, err = config.clientCompute.Firewalls.Update(project, d.Id(), firewallV1).Do()
if err != nil {
return fmt.Errorf("Error updating firewall: %s", err)
}
case v0beta:
firewallV0Beta := &computeBeta.Firewall{}
err = Convert(firewall, firewallV0Beta)
if err != nil {
return err
}
op, err = config.clientComputeBeta.Firewalls.Update(project, d.Id(), firewallV0Beta).Do()
if err != nil {
return fmt.Errorf("Error updating firewall: %s", err)
}
}
err = computeSharedOperationWait(config.clientCompute, op, project, "Updating Firewall")
@ -303,6 +371,7 @@ func resourceComputeFirewallUpdate(d *schema.ResourceData, meta interface{}) err
}
func resourceComputeFirewallDelete(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, FirewallBaseApiVersion, FirewallVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -311,9 +380,18 @@ func resourceComputeFirewallDelete(d *schema.ResourceData, meta interface{}) err
}
// Delete the firewall
op, err := config.clientComputeBeta.Firewalls.Delete(project, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting firewall: %s", err)
var op interface{}
switch computeApiVersion {
case v1:
op, err = config.clientCompute.Firewalls.Delete(project, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting firewall: %s", err)
}
case v0beta:
op, err = config.clientComputeBeta.Firewalls.Delete(project, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting firewall: %s", err)
}
}
err = computeSharedOperationWait(config.clientCompute, op, project, "Deleting Firewall")

View File

@ -8,9 +8,14 @@ import (
"github.com/hashicorp/terraform/helper/validation"
computeBeta "google.golang.org/api/compute/v0.beta"
compute "google.golang.org/api/compute/v1"
"google.golang.org/api/compute/v1"
)
var GlobalForwardingRuleBaseApiVersion = v1
var GlobalForwardingRuleVersionedFeatures = []Feature{
{Version: v0beta, Item: "labels"},
}
func resourceComputeGlobalForwardingRule() *schema.Resource {
return &schema.Resource{
Create: resourceComputeGlobalForwardingRuleCreate,
@ -104,6 +109,7 @@ func resourceComputeGlobalForwardingRule() *schema.Resource {
}
func resourceComputeGlobalForwardingRuleCreate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, GlobalForwardingRuleBaseApiVersion, GlobalForwardingRuleVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -121,9 +127,30 @@ func resourceComputeGlobalForwardingRuleCreate(d *schema.ResourceData, meta inte
Target: d.Get("target").(string),
}
op, err := config.clientComputeBeta.GlobalForwardingRules.Insert(project, frule).Do()
if err != nil {
return fmt.Errorf("Error creating Global Forwarding Rule: %s", err)
var op interface{}
switch computeApiVersion {
case v1:
v1Frule := &compute.ForwardingRule{}
err = Convert(frule, v1Frule)
if err != nil {
return err
}
op, err = config.clientCompute.GlobalForwardingRules.Insert(project, v1Frule).Do()
if err != nil {
return fmt.Errorf("Error creating Global Forwarding Rule: %s", err)
}
case v0beta:
v0BetaFrule := &computeBeta.ForwardingRule{}
err = Convert(frule, v0BetaFrule)
if err != nil {
return err
}
op, err = config.clientComputeBeta.GlobalForwardingRules.Insert(project, v0BetaFrule).Do()
if err != nil {
return fmt.Errorf("Error creating Global Forwarding Rule: %s", err)
}
}
// It probably maybe worked, so store the ID now
@ -138,12 +165,12 @@ func resourceComputeGlobalForwardingRuleCreate(d *schema.ResourceData, meta inte
if _, ok := d.GetOk("labels"); ok {
labels := expandLabels(d)
// Do a read to get the fingerprint value so we can update
fingerprint, err := resourceComputeGlobalForwardingRuleReadLabelFingerprint(config, project, frule.Name)
fingerprint, err := resourceComputeGlobalForwardingRuleReadLabelFingerprint(config, computeApiVersion, project, frule.Name)
if err != nil {
return err
}
err = resourceComputeGlobalForwardingRuleSetLabels(config, project, frule.Name, labels, fingerprint)
err = resourceComputeGlobalForwardingRuleSetLabels(config, computeApiVersion, project, frule.Name, labels, fingerprint)
if err != nil {
return err
}
@ -153,6 +180,7 @@ func resourceComputeGlobalForwardingRuleCreate(d *schema.ResourceData, meta inte
}
func resourceComputeGlobalForwardingRuleUpdate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersionUpdate(d, GlobalForwardingRuleBaseApiVersion, GlobalForwardingRuleVersionedFeatures, []Feature{})
config := meta.(*Config)
project, err := getProject(d, config)
@ -164,12 +192,34 @@ func resourceComputeGlobalForwardingRuleUpdate(d *schema.ResourceData, meta inte
if d.HasChange("target") {
target := d.Get("target").(string)
targetRef := &compute.TargetReference{Target: target}
targetRef := &computeBeta.TargetReference{Target: target}
op, err := config.clientCompute.GlobalForwardingRules.SetTarget(
project, d.Id(), targetRef).Do()
if err != nil {
return fmt.Errorf("Error updating target: %s", err)
var op interface{}
switch computeApiVersion {
case v1:
v1TargetRef := &compute.TargetReference{}
err = Convert(targetRef, v1TargetRef)
if err != nil {
return err
}
op, err = config.clientCompute.GlobalForwardingRules.SetTarget(
project, d.Id(), v1TargetRef).Do()
if err != nil {
return fmt.Errorf("Error updating target: %s", err)
}
case v0beta:
v0BetaTargetRef := &compute.TargetReference{}
err = Convert(targetRef, v0BetaTargetRef)
if err != nil {
return err
}
op, err = config.clientCompute.GlobalForwardingRules.SetTarget(
project, d.Id(), v0BetaTargetRef).Do()
if err != nil {
return fmt.Errorf("Error updating target: %s", err)
}
}
err = computeSharedOperationWait(config.clientCompute, op, project, "Updating Global Forwarding Rule")
@ -183,7 +233,7 @@ func resourceComputeGlobalForwardingRuleUpdate(d *schema.ResourceData, meta inte
labels := expandLabels(d)
fingerprint := d.Get("label_fingerprint").(string)
err = resourceComputeGlobalForwardingRuleSetLabels(config, project, d.Get("name").(string), labels, fingerprint)
err = resourceComputeGlobalForwardingRuleSetLabels(config, computeApiVersion, project, d.Get("name").(string), labels, fingerprint)
if err != nil {
return err
}
@ -197,6 +247,7 @@ func resourceComputeGlobalForwardingRuleUpdate(d *schema.ResourceData, meta inte
}
func resourceComputeGlobalForwardingRuleRead(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, GlobalForwardingRuleBaseApiVersion, GlobalForwardingRuleVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -204,9 +255,28 @@ func resourceComputeGlobalForwardingRuleRead(d *schema.ResourceData, meta interf
return err
}
frule, err := config.clientComputeBeta.GlobalForwardingRules.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Global Forwarding Rule %q", d.Get("name").(string)))
frule := &computeBeta.ForwardingRule{}
switch computeApiVersion {
case v1:
v1Frule, err := config.clientCompute.GlobalForwardingRules.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Global Forwarding Rule %q", d.Get("name").(string)))
}
err = Convert(v1Frule, frule)
if err != nil {
return err
}
case v0beta:
v0BetaFrule, err := config.clientComputeBeta.GlobalForwardingRules.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Global Forwarding Rule %q", d.Get("name").(string)))
}
err = Convert(v0BetaFrule, frule)
if err != nil {
return err
}
}
d.Set("name", frule.Name)
@ -225,6 +295,7 @@ func resourceComputeGlobalForwardingRuleRead(d *schema.ResourceData, meta interf
}
func resourceComputeGlobalForwardingRuleDelete(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, GlobalForwardingRuleBaseApiVersion, GlobalForwardingRuleVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -234,10 +305,20 @@ func resourceComputeGlobalForwardingRuleDelete(d *schema.ResourceData, meta inte
// Delete the GlobalForwardingRule
log.Printf("[DEBUG] GlobalForwardingRule delete request")
op, err := config.clientCompute.GlobalForwardingRules.Delete(project, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting GlobalForwardingRule: %s", err)
var op interface{}
switch computeApiVersion {
case v1:
op, err = config.clientCompute.GlobalForwardingRules.Delete(project, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting GlobalForwardingRule: %s", err)
}
case v0beta:
op, err = config.clientComputeBeta.GlobalForwardingRules.Delete(project, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting GlobalForwardingRule: %s", err)
}
}
err = computeSharedOperationWait(config.clientCompute, op, project, "Deleting GlobalForwarding Rule")
if err != nil {
return err
@ -249,24 +330,43 @@ func resourceComputeGlobalForwardingRuleDelete(d *schema.ResourceData, meta inte
// resourceComputeGlobalForwardingRuleReadLabelFingerprint performs a read on the remote resource and returns only the
// fingerprint. Used on create when setting labels as we don't know the label fingerprint initially.
func resourceComputeGlobalForwardingRuleReadLabelFingerprint(config *Config, project, name string) (string, error) {
frule, err := config.clientComputeBeta.GlobalForwardingRules.Get(project, name).Do()
if err != nil {
return "", fmt.Errorf("Unable to read global forwarding rule to update labels: %s", err)
}
func resourceComputeGlobalForwardingRuleReadLabelFingerprint(config *Config, computeApiVersion ApiVersion,
project, name string) (string, error) {
switch computeApiVersion {
case v0beta:
frule, err := config.clientComputeBeta.GlobalForwardingRules.Get(project, name).Do()
if err != nil {
return "", fmt.Errorf("Unable to read global forwarding rule to update labels: %s", err)
}
return frule.LabelFingerprint, nil
return frule.LabelFingerprint, nil
default:
return "", fmt.Errorf(
"Unable to read label fingerprint due to an internal error: can only handle v0beta but compute api logic indicates %d",
computeApiVersion)
}
}
// resourceComputeGlobalForwardingRuleSetLabels sets the Labels attribute on a forwarding rule.
func resourceComputeGlobalForwardingRuleSetLabels(config *Config, project, name string, labels map[string]string, fingerprint string) error {
setLabels := computeBeta.GlobalSetLabelsRequest{
Labels: labels,
LabelFingerprint: fingerprint,
}
op, err := config.clientComputeBeta.GlobalForwardingRules.SetLabels(project, name, &setLabels).Do()
if err != nil {
return err
func resourceComputeGlobalForwardingRuleSetLabels(config *Config, computeApiVersion ApiVersion, project,
name string, labels map[string]string, fingerprint string) error {
var op interface{}
var err error
switch computeApiVersion {
case v0beta:
setLabels := computeBeta.GlobalSetLabelsRequest{
Labels: labels,
LabelFingerprint: fingerprint,
}
op, err = config.clientComputeBeta.GlobalForwardingRules.SetLabels(project, name, &setLabels).Do()
if err != nil {
return err
}
default:
return fmt.Errorf(
"Unable to set labels due to an internal error: can only handle v0beta but compute api logic indicates %d",
computeApiVersion)
}
err = computeSharedOperationWait(config.clientCompute, op, project, "Setting labels on Global Forwarding Rule")

View File

@ -19,6 +19,9 @@ import (
"google.golang.org/api/googleapi"
)
var InstanceBaseApiVersion = v1
var InstanceVersionedFeatures = []Feature{}
func resourceComputeInstance() *schema.Resource {
return &schema.Resource{
Create: resourceComputeInstanceCreate,
@ -592,9 +595,21 @@ func getInstance(config *Config, d *schema.ResourceData) (*computeBeta.Instance,
if err != nil {
return nil, err
}
instance, err := config.clientComputeBeta.Instances.Get(project, zone, d.Id()).Do()
if err != nil {
return nil, handleNotFoundError(err, d, fmt.Sprintf("Instance %s", d.Get("name").(string)))
instance := &computeBeta.Instance{}
switch getComputeApiVersion(d, InstanceBaseApiVersion, InstanceVersionedFeatures) {
case v1:
instanceV1, err := config.clientCompute.Instances.Get(project, zone, d.Id()).Do()
if err != nil {
return nil, handleNotFoundError(err, d, fmt.Sprintf("Instance %s", d.Get("name").(string)))
}
if err := Convert(instanceV1, instance); err != nil {
return nil, err
}
case v0beta:
instance, err = config.clientComputeBeta.Instances.Get(project, zone, d.Id()).Do()
if err != nil {
return nil, handleNotFoundError(err, d, fmt.Sprintf("Instance %s", d.Get("name").(string)))
}
}
return instance, nil
}
@ -730,7 +745,17 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
}
log.Printf("[INFO] Requesting instance creation")
op, err := config.clientComputeBeta.Instances.Insert(project, zone.Name, instance).Do()
var op interface{}
switch getComputeApiVersion(d, InstanceBaseApiVersion, InstanceVersionedFeatures) {
case v1:
instanceV1 := &compute.Instance{}
if err := Convert(instance, instanceV1); err != nil {
return err
}
op, err = config.clientCompute.Instances.Insert(project, zone.Name, instanceV1).Do()
case v0beta:
op, err = config.clientComputeBeta.Instances.Insert(project, zone.Name, instance).Do()
}
if err != nil {
return fmt.Errorf("Error creating instance: %s", err)
}

View File

@ -14,6 +14,12 @@ import (
"google.golang.org/api/compute/v1"
)
var InstanceGroupManagerBaseApiVersion = v1
var InstanceGroupManagerVersionedFeatures = []Feature{
Feature{Version: v0beta, Item: "auto_healing_policies"},
Feature{Version: v0beta, Item: "rolling_update_policy"},
}
func resourceComputeInstanceGroupManager() *schema.Resource {
return &schema.Resource{
Create: resourceComputeInstanceGroupManagerCreate,
@ -228,6 +234,7 @@ func getNamedPortsBeta(nps []interface{}) []*computeBeta.NamedPort {
}
func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, InstanceGroupManagerBaseApiVersion, InstanceGroupManagerVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -259,8 +266,29 @@ func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta inte
}
log.Printf("[DEBUG] InstanceGroupManager insert request: %#v", manager)
op, err := config.clientComputeBeta.InstanceGroupManagers.Insert(
project, zone, manager).Do()
var op interface{}
switch computeApiVersion {
case v1:
managerV1 := &compute.InstanceGroupManager{}
err = Convert(manager, managerV1)
if err != nil {
return err
}
managerV1.ForceSendFields = manager.ForceSendFields
op, err = config.clientCompute.InstanceGroupManagers.Insert(
project, zone, managerV1).Do()
case v0beta:
managerV0beta := &computeBeta.InstanceGroupManager{}
err = Convert(manager, managerV0beta)
if err != nil {
return err
}
managerV0beta.ForceSendFields = manager.ForceSendFields
op, err = config.clientComputeBeta.InstanceGroupManagers.Insert(
project, zone, managerV0beta).Do()
}
if err != nil {
return fmt.Errorf("Error creating InstanceGroupManager: %s", err)
@ -291,6 +319,7 @@ func flattenNamedPortsBeta(namedPorts []*computeBeta.NamedPort) []map[string]int
}
func getManager(d *schema.ResourceData, meta interface{}) (*computeBeta.InstanceGroupManager, error) {
computeApiVersion := getComputeApiVersion(d, InstanceGroupManagerBaseApiVersion, InstanceGroupManagerVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -303,38 +332,82 @@ func getManager(d *schema.ResourceData, meta interface{}) (*computeBeta.Instance
return nil, err
}
getInstanceGroupManager := func(zone string) (interface{}, error) {
return config.clientComputeBeta.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
}
var manager *computeBeta.InstanceGroupManager
var e error
if zone, _ := getZone(d, config); zone != "" {
manager, e = config.clientComputeBeta.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
if e != nil {
return nil, handleNotFoundError(e, d, fmt.Sprintf("Instance Group Manager %q", d.Get("name").(string)))
}
} else {
// If the resource was imported, the only info we have is the ID. Try to find the resource
// by searching in the region of the project.
var resource interface{}
resource, e = getZonalBetaResourceFromRegion(getInstanceGroupManager, region, config.clientComputeBeta, project)
if e != nil {
return nil, e
manager := &computeBeta.InstanceGroupManager{}
switch computeApiVersion {
case v1:
getInstanceGroupManager := func(zone string) (interface{}, error) {
return config.clientCompute.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
}
manager = resource.(*computeBeta.InstanceGroupManager)
var v1Manager *compute.InstanceGroupManager
var e error
if zone, _ := getZone(d, config); zone != "" {
v1Manager, e = config.clientCompute.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
if e != nil {
return nil, handleNotFoundError(e, d, fmt.Sprintf("Instance Group Manager %q", d.Get("name").(string)))
}
} else {
// If the resource was imported, the only info we have is the ID. Try to find the resource
// by searching in the region of the project.
var resource interface{}
resource, e = getZonalResourceFromRegion(getInstanceGroupManager, region, config.clientCompute, project)
if e != nil {
return nil, e
}
v1Manager = resource.(*compute.InstanceGroupManager)
}
if v1Manager == nil {
log.Printf("[WARN] Removing Instance Group Manager %q because it's gone", d.Get("name").(string))
// The resource doesn't exist anymore
d.SetId("")
return nil, nil
}
err = Convert(v1Manager, manager)
if err != nil {
return nil, err
}
case v0beta:
getInstanceGroupManager := func(zone string) (interface{}, error) {
return config.clientComputeBeta.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
}
var v0betaManager *computeBeta.InstanceGroupManager
var e error
if zone, _ := getZone(d, config); zone != "" {
v0betaManager, e = config.clientComputeBeta.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
if e != nil {
return nil, handleNotFoundError(e, d, fmt.Sprintf("Instance Group Manager %q", d.Get("name").(string)))
}
} else {
// If the resource was imported, the only info we have is the ID. Try to find the resource
// by searching in the region of the project.
var resource interface{}
resource, e = getZonalBetaResourceFromRegion(getInstanceGroupManager, region, config.clientComputeBeta, project)
if e != nil {
return nil, e
}
v0betaManager = resource.(*computeBeta.InstanceGroupManager)
}
if v0betaManager == nil {
log.Printf("[WARN] Removing Instance Group Manager %q because it's gone", d.Get("name").(string))
// The resource doesn't exist anymore
d.SetId("")
return nil, nil
}
manager = v0betaManager
}
if manager == nil {
log.Printf("[WARN] Removing Instance Group Manager %q because it's gone", d.Get("name").(string))
// The resource doesn't exist anymore
d.SetId("")
return nil, nil
}
return manager, nil
}
@ -386,6 +459,7 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf
}
func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersionUpdate(d, InstanceGroupManagerBaseApiVersion, InstanceGroupManagerVersionedFeatures, []Feature{})
config := meta.(*Config)
project, err := getProject(d, config)
@ -414,8 +488,27 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
TargetPools: targetPools,
}
op, err := config.clientComputeBeta.InstanceGroupManagers.SetTargetPools(
project, zone, d.Id(), setTargetPools).Do()
var op interface{}
switch computeApiVersion {
case v1:
setTargetPoolsV1 := &compute.InstanceGroupManagersSetTargetPoolsRequest{}
err = Convert(setTargetPools, setTargetPoolsV1)
if err != nil {
return err
}
op, err = config.clientCompute.InstanceGroupManagers.SetTargetPools(
project, zone, d.Id(), setTargetPoolsV1).Do()
case v0beta:
setTargetPoolsV0beta := &computeBeta.InstanceGroupManagersSetTargetPoolsRequest{}
err = Convert(setTargetPools, setTargetPoolsV0beta)
if err != nil {
return err
}
op, err = config.clientComputeBeta.InstanceGroupManagers.SetTargetPools(
project, zone, d.Id(), setTargetPoolsV0beta).Do()
}
if err != nil {
return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
@ -437,8 +530,27 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
InstanceTemplate: d.Get("instance_template").(string),
}
op, err := config.clientComputeBeta.InstanceGroupManagers.SetInstanceTemplate(
project, zone, d.Id(), setInstanceTemplate).Do()
var op interface{}
switch computeApiVersion {
case v1:
setInstanceTemplateV1 := &compute.InstanceGroupManagersSetInstanceTemplateRequest{}
err = Convert(setInstanceTemplate, setInstanceTemplateV1)
if err != nil {
return err
}
op, err = config.clientCompute.InstanceGroupManagers.SetInstanceTemplate(
project, zone, d.Id(), setInstanceTemplateV1).Do()
case v0beta:
setInstanceTemplateV0beta := &computeBeta.InstanceGroupManagersSetInstanceTemplateRequest{}
err = Convert(setInstanceTemplate, setInstanceTemplateV0beta)
if err != nil {
return err
}
op, err = config.clientComputeBeta.InstanceGroupManagers.SetInstanceTemplate(
project, zone, d.Id(), setInstanceTemplateV0beta).Do()
}
if err != nil {
return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
@ -451,10 +563,30 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
}
if d.Get("update_strategy").(string) == "RESTART" {
managedInstances, err := config.clientComputeBeta.InstanceGroupManagers.ListManagedInstances(
project, zone, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error getting instance group managers instances: %s", err)
managedInstances := &computeBeta.InstanceGroupManagersListManagedInstancesResponse{}
switch computeApiVersion {
case v1:
managedInstancesV1, err := config.clientCompute.InstanceGroupManagers.ListManagedInstances(
project, zone, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error getting instance group managers instances: %s", err)
}
err = Convert(managedInstancesV1, managedInstances)
if err != nil {
return err
}
case v0beta:
managedInstancesV0beta, err := config.clientComputeBeta.InstanceGroupManagers.ListManagedInstances(
project, zone, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error getting instance group managers instances: %s", err)
}
err = Convert(managedInstancesV0beta, managedInstances)
if err != nil {
return err
}
}
managedInstanceCount := len(managedInstances.ManagedInstances)
@ -467,10 +599,32 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
Instances: instances,
}
op, err = config.clientComputeBeta.InstanceGroupManagers.RecreateInstances(
project, zone, d.Id(), recreateInstances).Do()
if err != nil {
return fmt.Errorf("Error restarting instance group managers instances: %s", err)
var op interface{}
switch computeApiVersion {
case v1:
recreateInstancesV1 := &compute.InstanceGroupManagersRecreateInstancesRequest{}
err = Convert(recreateInstances, recreateInstancesV1)
if err != nil {
return err
}
op, err = config.clientCompute.InstanceGroupManagers.RecreateInstances(
project, zone, d.Id(), recreateInstancesV1).Do()
if err != nil {
return fmt.Errorf("Error restarting instance group managers instances: %s", err)
}
case v0beta:
recreateInstancesV0beta := &computeBeta.InstanceGroupManagersRecreateInstancesRequest{}
err = Convert(recreateInstances, recreateInstancesV0beta)
if err != nil {
return err
}
op, err = config.clientComputeBeta.InstanceGroupManagers.RecreateInstances(
project, zone, d.Id(), recreateInstancesV0beta).Do()
if err != nil {
return fmt.Errorf("Error restarting instance group managers instances: %s", err)
}
}
// Wait for the operation to complete
@ -515,8 +669,27 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
}
// Make the request:
op, err := config.clientComputeBeta.InstanceGroups.SetNamedPorts(
project, zone, d.Id(), setNamedPorts).Do()
var op interface{}
switch computeApiVersion {
case v1:
setNamedPortsV1 := &compute.InstanceGroupsSetNamedPortsRequest{}
err = Convert(setNamedPorts, setNamedPortsV1)
if err != nil {
return err
}
op, err = config.clientCompute.InstanceGroups.SetNamedPorts(
project, zone, d.Id(), setNamedPortsV1).Do()
case v0beta:
setNamedPortsV0beta := &computeBeta.InstanceGroupsSetNamedPortsRequest{}
err = Convert(setNamedPorts, setNamedPortsV0beta)
if err != nil {
return err
}
op, err = config.clientComputeBeta.InstanceGroups.SetNamedPorts(
project, zone, d.Id(), setNamedPortsV0beta).Do()
}
if err != nil {
return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
@ -533,8 +706,15 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
if d.HasChange("target_size") {
targetSize := int64(d.Get("target_size").(int))
op, err := config.clientComputeBeta.InstanceGroupManagers.Resize(
project, zone, d.Id(), targetSize).Do()
var op interface{}
switch computeApiVersion {
case v1:
op, err = config.clientCompute.InstanceGroupManagers.Resize(
project, zone, d.Id(), targetSize).Do()
case v0beta:
op, err = config.clientComputeBeta.InstanceGroupManagers.Resize(
project, zone, d.Id(), targetSize).Do()
}
if err != nil {
return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
@ -578,6 +758,7 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
}
func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, InstanceGroupManagerBaseApiVersion, InstanceGroupManagerVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -590,12 +771,24 @@ func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta inte
return err
}
op, err := config.clientComputeBeta.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
attempt := 0
for err != nil && attempt < 20 {
attempt++
time.Sleep(2000 * time.Millisecond)
var op interface{}
switch computeApiVersion {
case v1:
op, err = config.clientCompute.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
attempt := 0
for err != nil && attempt < 20 {
attempt++
time.Sleep(2000 * time.Millisecond)
op, err = config.clientCompute.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
}
case v0beta:
op, err = config.clientComputeBeta.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
attempt := 0
for err != nil && attempt < 20 {
attempt++
time.Sleep(2000 * time.Millisecond)
op, err = config.clientComputeBeta.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
}
}
if err != nil {
@ -612,13 +805,25 @@ func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta inte
return err
}
instanceGroup, err := config.clientComputeBeta.InstanceGroups.Get(
project, zone, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error getting instance group size: %s", err)
}
var instanceGroupSize int64
switch computeApiVersion {
case v1:
instanceGroup, err := config.clientCompute.InstanceGroups.Get(
project, zone, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error getting instance group size: %s", err)
}
instanceGroupSize := instanceGroup.Size
instanceGroupSize = instanceGroup.Size
case v0beta:
instanceGroup, err := config.clientComputeBeta.InstanceGroups.Get(
project, zone, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error getting instance group size: %s", err)
}
instanceGroupSize = instanceGroup.Size
}
if instanceGroupSize >= currentSize {
return fmt.Errorf("Error, instance group isn't shrinking during delete")

View File

@ -292,11 +292,9 @@ func TestAccInstanceGroupManager_autoHealingPolicies(t *testing.T) {
testAccCheckInstanceGroupManagerAutoHealingPolicies("google_compute_instance_group_manager.igm-basic", hck, 10),
),
},
resource.TestStep{
ResourceName: "google_compute_instance_group_manager.igm-basic",
ImportState: true,
ImportStateVerify: true,
},
// TODO: Add import test for auto healing policies
// Import doesn't work for auto healing policies because import is not supported
// for beta features. See https://github.com/terraform-providers/terraform-provider-google/issues/694
},
})
}

View File

@ -6,9 +6,13 @@ import (
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)
var InstanceTemplateBaseApiVersion = v1
var InstanceTemplateVersionedFeatures = []Feature{}
func resourceComputeInstanceTemplate() *schema.Resource {
return &schema.Resource{
Create: resourceComputeInstanceTemplateCreate,
@ -606,7 +610,17 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac
Name: itName,
}
op, err := config.clientComputeBeta.InstanceTemplates.Insert(project, instanceTemplate).Do()
var op interface{}
switch getComputeApiVersion(d, InstanceTemplateBaseApiVersion, InstanceGroupManagerVersionedFeatures) {
case v1:
instanceTemplateV1 := &compute.InstanceTemplate{}
if err := Convert(instanceTemplate, instanceTemplateV1); err != nil {
return err
}
op, err = config.clientCompute.InstanceTemplates.Insert(project, instanceTemplateV1).Do()
case v0beta:
op, err = config.clientComputeBeta.InstanceTemplates.Insert(project, instanceTemplate).Do()
}
if err != nil {
return fmt.Errorf("Error creating instance template: %s", err)
}
@ -656,9 +670,22 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{
return err
}
instanceTemplate, err := config.clientComputeBeta.InstanceTemplates.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Instance Template %q", d.Get("name").(string)))
instanceTemplate := &computeBeta.InstanceTemplate{}
switch getComputeApiVersion(d, InstanceBaseApiVersion, InstanceVersionedFeatures) {
case v1:
instanceTemplateV1, err := config.clientCompute.InstanceTemplates.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Instance Template %q", d.Get("name").(string)))
}
if err := Convert(instanceTemplateV1, instanceTemplate); err != nil {
return err
}
case v0beta:
var err error
instanceTemplate, err = config.clientComputeBeta.InstanceTemplates.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Instance Template %q", d.Get("name").(string)))
}
}
// Set the metadata fingerprint if there is one.

View File

@ -12,8 +12,16 @@ import (
"github.com/hashicorp/terraform/helper/validation"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
)
var RegionInstanceGroupManagerBaseApiVersion = v1
var RegionInstanceGroupManagerVersionedFeatures = []Feature{
Feature{Version: v0beta, Item: "auto_healing_policies"},
Feature{Version: v0beta, Item: "distribution_policy_zones"},
Feature{Version: v0beta, Item: "rolling_update_policy"},
}
func resourceComputeRegionInstanceGroupManager() *schema.Resource {
return &schema.Resource{
Create: resourceComputeRegionInstanceGroupManagerCreate,
@ -222,6 +230,7 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource {
}
func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, RegionInstanceGroupManagerBaseApiVersion, RegionInstanceGroupManagerVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -247,7 +256,19 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met
ForceSendFields: []string{"TargetSize"},
}
op, err := config.clientComputeBeta.RegionInstanceGroupManagers.Insert(project, d.Get("region").(string), manager).Do()
var op interface{}
switch computeApiVersion {
case v1:
managerV1 := &compute.InstanceGroupManager{}
err = Convert(manager, managerV1)
if err != nil {
return err
}
managerV1.ForceSendFields = manager.ForceSendFields
op, err = config.clientCompute.RegionInstanceGroupManagers.Insert(project, d.Get("region").(string), managerV1).Do()
case v0beta:
op, err = config.clientComputeBeta.RegionInstanceGroupManagers.Insert(project, d.Get("region").(string), manager).Do()
}
if err != nil {
return fmt.Errorf("Error creating RegionInstanceGroupManager: %s", err)
@ -266,6 +287,7 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met
type getInstanceManagerFunc func(*schema.ResourceData, interface{}) (*computeBeta.InstanceGroupManager, error)
func getRegionalManager(d *schema.ResourceData, meta interface{}) (*computeBeta.InstanceGroupManager, error) {
computeApiVersion := getComputeApiVersion(d, RegionInstanceGroupManagerBaseApiVersion, RegionInstanceGroupManagerVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -274,12 +296,32 @@ func getRegionalManager(d *schema.ResourceData, meta interface{}) (*computeBeta.
}
region := d.Get("region").(string)
manager := &computeBeta.InstanceGroupManager{}
switch computeApiVersion {
case v1:
v1Manager := &compute.InstanceGroupManager{}
v1Manager, err = config.clientCompute.RegionInstanceGroupManagers.Get(project, region, d.Id()).Do()
if v1Manager == nil {
log.Printf("[WARN] Removing Region Instance Group Manager %q because it's gone", d.Get("name").(string))
// The resource doesn't exist anymore
d.SetId("")
return nil, nil
}
err = Convert(v1Manager, manager)
if err != nil {
return nil, err
}
case v0beta:
manager, err = config.clientComputeBeta.RegionInstanceGroupManagers.Get(project, region, d.Id()).Do()
}
manager, err := config.clientComputeBeta.RegionInstanceGroupManagers.Get(project, region, d.Id()).Do()
if err != nil {
return nil, handleNotFoundError(err, d, fmt.Sprintf("Region Instance Manager %q", d.Get("name").(string)))
}
return manager, nil
}
@ -344,6 +386,7 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta
}
func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersionUpdate(d, RegionInstanceGroupManagerBaseApiVersion, RegionInstanceGroupManagerVersionedFeatures, []Feature{})
config := meta.(*Config)
project, err := getProject(d, config)
@ -368,8 +411,27 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
TargetPools: targetPools,
}
op, err := config.clientComputeBeta.RegionInstanceGroupManagers.SetTargetPools(
project, region, d.Id(), setTargetPools).Do()
var op interface{}
switch computeApiVersion {
case v1:
setTargetPoolsV1 := &compute.RegionInstanceGroupManagersSetTargetPoolsRequest{}
err = Convert(setTargetPools, setTargetPoolsV1)
if err != nil {
return err
}
op, err = config.clientCompute.RegionInstanceGroupManagers.SetTargetPools(
project, region, d.Id(), setTargetPoolsV1).Do()
case v0beta:
setTargetPoolsV0beta := &computeBeta.RegionInstanceGroupManagersSetTargetPoolsRequest{}
err = Convert(setTargetPools, setTargetPoolsV0beta)
if err != nil {
return err
}
op, err = config.clientComputeBeta.RegionInstanceGroupManagers.SetTargetPools(
project, region, d.Id(), setTargetPoolsV0beta).Do()
}
if err != nil {
return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err)
@ -390,8 +452,27 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
InstanceTemplate: d.Get("instance_template").(string),
}
op, err := config.clientComputeBeta.RegionInstanceGroupManagers.SetInstanceTemplate(
project, region, d.Id(), setInstanceTemplate).Do()
var op interface{}
switch computeApiVersion {
case v1:
setInstanceTemplateV1 := &compute.RegionInstanceGroupManagersSetTemplateRequest{}
err = Convert(setInstanceTemplate, setInstanceTemplateV1)
if err != nil {
return err
}
op, err = config.clientCompute.RegionInstanceGroupManagers.SetInstanceTemplate(
project, region, d.Id(), setInstanceTemplateV1).Do()
case v0beta:
setInstanceTemplateV0beta := &computeBeta.RegionInstanceGroupManagersSetTemplateRequest{}
err = Convert(setInstanceTemplate, setInstanceTemplateV0beta)
if err != nil {
return err
}
op, err = config.clientComputeBeta.RegionInstanceGroupManagers.SetInstanceTemplate(
project, region, d.Id(), setInstanceTemplateV0beta).Do()
}
if err != nil {
return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err)
@ -436,8 +517,27 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
}
// Make the request:
op, err := config.clientComputeBeta.RegionInstanceGroups.SetNamedPorts(
project, region, d.Id(), setNamedPorts).Do()
var op interface{}
switch computeApiVersion {
case v1:
setNamedPortsV1 := &compute.RegionInstanceGroupsSetNamedPortsRequest{}
err = Convert(setNamedPorts, setNamedPortsV1)
if err != nil {
return err
}
op, err = config.clientCompute.RegionInstanceGroups.SetNamedPorts(
project, region, d.Id(), setNamedPortsV1).Do()
case v0beta:
setNamedPortsV0beta := &computeBeta.RegionInstanceGroupsSetNamedPortsRequest{}
err = Convert(setNamedPorts, setNamedPortsV0beta)
if err != nil {
return err
}
op, err = config.clientComputeBeta.RegionInstanceGroups.SetNamedPorts(
project, region, d.Id(), setNamedPortsV0beta).Do()
}
if err != nil {
return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err)
@ -454,8 +554,15 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
if d.HasChange("target_size") {
targetSize := int64(d.Get("target_size").(int))
op, err := config.clientComputeBeta.RegionInstanceGroupManagers.Resize(
project, region, d.Id(), targetSize).Do()
var op interface{}
switch computeApiVersion {
case v1:
op, err = config.clientCompute.RegionInstanceGroupManagers.Resize(
project, region, d.Id(), targetSize).Do()
case v0beta:
op, err = config.clientComputeBeta.RegionInstanceGroupManagers.Resize(
project, region, d.Id(), targetSize).Do()
}
if err != nil {
return fmt.Errorf("Error resizing RegionInstanceGroupManager: %s", err)
@ -498,6 +605,7 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
}
func resourceComputeRegionInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, RegionInstanceGroupManagerBaseApiVersion, RegionInstanceGroupManagerVersionedFeatures)
config := meta.(*Config)
project, err := getProject(d, config)
@ -507,7 +615,13 @@ func resourceComputeRegionInstanceGroupManagerDelete(d *schema.ResourceData, met
region := d.Get("region").(string)
op, err := config.clientComputeBeta.RegionInstanceGroupManagers.Delete(project, region, d.Id()).Do()
var op interface{}
switch computeApiVersion {
case v1:
op, err = config.clientCompute.RegionInstanceGroupManagers.Delete(project, region, d.Id()).Do()
case v0beta:
op, err = config.clientComputeBeta.RegionInstanceGroupManagers.Delete(project, region, d.Id()).Do()
}
if err != nil {
return fmt.Errorf("Error deleting region instance group manager: %s", err)

View File

@ -14,6 +14,14 @@ import (
"google.golang.org/api/compute/v1"
)
var (
SubnetworkBaseApiVersion = v1
SubnetworkVersionedFeatures = []Feature{
{Version: v0beta, Item: "secondary_ip_range"},
{Version: v0beta, Item: "enable_flow_logs"},
}
)
func resourceComputeSubnetwork() *schema.Resource {
return &schema.Resource{
Create: resourceComputeSubnetworkCreate,
@ -122,6 +130,7 @@ func resourceComputeSubnetwork() *schema.Resource {
}
func resourceComputeSubnetworkCreate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, SubnetworkBaseApiVersion, SubnetworkVersionedFeatures)
config := meta.(*Config)
network, err := ParseNetworkFieldValue(d.Get("network").(string), d, config)
if err != nil {
@ -151,7 +160,21 @@ func resourceComputeSubnetworkCreate(d *schema.ResourceData, meta interface{}) e
log.Printf("[DEBUG] Subnetwork insert request: %#v", subnetwork)
op, err := config.clientComputeBeta.Subnetworks.Insert(project, region, subnetwork).Do()
subnetworkV1 := &compute.Subnetwork{}
err = Convert(subnetwork, subnetworkV1)
if err != nil {
return err
}
subnetworkV1.ForceSendFields = subnetwork.ForceSendFields
var op interface{}
switch computeApiVersion {
case v1:
op, err = config.clientCompute.Subnetworks.Insert(project, region, subnetworkV1).Do()
case v0beta:
op, err = config.clientComputeBeta.Subnetworks.Insert(project, region, subnetwork).Do()
}
if err != nil {
return fmt.Errorf("Error creating subnetwork: %s", err)
}
@ -161,8 +184,9 @@ func resourceComputeSubnetworkCreate(d *schema.ResourceData, meta interface{}) e
// "When creating a new subnetwork, its name has to be unique in that project for that region, even across networks.
// The same name can appear twice in a project, as long as each one is in a different region."
// https://cloud.google.com/compute/docs/subnetworks
subnetworkV1.Region = region
subnetwork.Region = region
d.SetId(createSubnetIDBeta(subnetwork))
d.SetId(createSubnetID(subnetworkV1))
err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), "Creating Subnetwork")
if err != nil {
@ -173,6 +197,45 @@ func resourceComputeSubnetworkCreate(d *schema.ResourceData, meta interface{}) e
}
func resourceComputeSubnetworkRead(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, SubnetworkBaseApiVersion, SubnetworkVersionedFeatures)
if computeApiVersion == v0beta {
return resourceComputeSubnetworkReadV0Beta(d, meta)
}
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
name := d.Get("name").(string)
subnetwork, err := config.clientCompute.Subnetworks.Get(project, region, name).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Subnetwork %q", name))
}
d.Set("name", subnetwork.Name)
d.Set("ip_cidr_range", subnetwork.IpCidrRange)
d.Set("network", subnetwork.Network)
d.Set("description", subnetwork.Description)
d.Set("private_ip_google_access", subnetwork.PrivateIpGoogleAccess)
d.Set("gateway_address", subnetwork.GatewayAddress)
d.Set("secondary_ip_range", flattenSecondaryRanges(subnetwork.SecondaryIpRanges))
d.Set("project", project)
d.Set("region", region)
d.Set("self_link", ConvertSelfLinkToV1(subnetwork.SelfLink))
return nil
}
func resourceComputeSubnetworkReadV0Beta(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
@ -209,6 +272,7 @@ func resourceComputeSubnetworkRead(d *schema.ResourceData, meta interface{}) err
}
func resourceComputeSubnetworkUpdate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, SubnetworkBaseApiVersion, SubnetworkVersionedFeatures)
config := meta.(*Config)
region, err := getRegion(d, config)
@ -264,7 +328,7 @@ func resourceComputeSubnetworkUpdate(d *schema.ResourceData, meta interface{}) e
d.SetPartial("ip_cidr_range")
}
if d.HasChange("secondary_ip_range") || d.HasChange("enable_flow_logs") {
if (d.HasChange("secondary_ip_range") || d.HasChange("enable_flow_logs")) && computeApiVersion == v0beta {
v0BetaSubnetwork := &computeBeta.Subnetwork{
Fingerprint: d.Get("fingerprint").(string),
}

View File

@ -193,11 +193,7 @@ func TestAccComputeSubnetwork_flowLogs(t *testing.T) {
"enable_flow_logs", "true"),
),
},
{
ResourceName: "google_compute_subnetwork.network-with-flow-logs",
ImportState: true,
ImportStateVerify: true,
},
// no import, as import currently doesn't work for beta
{
Config: testAccComputeSubnetwork_flowLogs(cnName, subnetworkName, false),
Check: resource.ComposeTestCheckFunc(
@ -207,11 +203,6 @@ func TestAccComputeSubnetwork_flowLogs(t *testing.T) {
"enable_flow_logs", "false"),
),
},
{
ResourceName: "google_compute_subnetwork.network-with-flow-logs",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -112,6 +112,5 @@ deprecation policy, and no SLA, but are otherwise considered to be feature-compl
with only minor outstanding issues after their Alpha period. Beta is when a GCP feature
is publicly announced, and is when they generally become publicly available.
Terraform resources that support beta features will always use the Beta APIs to provision
the resource. Importing a resource that supports beta features will always import those
features, even if the resource was created in a matter that was not explicitly beta.
Resources will automatically be provisioned using Beta APIs when you specify a feature
marked Beta in your Terraform config file.