Roll forward beta scaffolding PR (#1453)

* Revert "Merge pull request #1434 from terraform-providers/paddy_revert_beta"

This reverts commit 118cd71201, reversing
changes made to d59fcbbc59.

* add ConvertSelfLinkToV1 calls to places where beta links are stored
This commit is contained in:
Dana Hoffman 2018-05-09 11:24:40 -07:00 committed by GitHub
parent 4c4b4be413
commit 8907321d60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 411 additions and 1554 deletions

View File

@ -1,254 +0,0 @@
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

@ -1,326 +0,0 @@
package google
import (
"reflect"
"testing"
)
type ExpectedApiVersions struct {
Create ApiVersion
ReadDelete ApiVersion
Update ApiVersion
}
func TestApiVersion(t *testing.T) {
baseVersion := v1
betaVersion := v0beta
maxTestApiVersion := func(versionsInUse map[ApiVersion]struct{}) ApiVersion {
if _, ok := versionsInUse[betaVersion]; ok {
return betaVersion
}
return baseVersion
}
cases := map[string]struct {
Features []Feature
FieldsInSchema map[string]interface{}
UpdatedFields []string
UpdateOnlyFields []Feature
ExpectedApiVersions
}{
"no beta field": {
FieldsInSchema: map[string]interface{}{
"normal_field": "foo",
},
ExpectedApiVersions: ExpectedApiVersions{
Create: baseVersion,
ReadDelete: baseVersion,
Update: baseVersion,
},
},
"beta field not set": {
Features: []Feature{{Version: betaVersion, Item: "beta_field"}},
FieldsInSchema: map[string]interface{}{
"normal_field": "foo",
},
ExpectedApiVersions: ExpectedApiVersions{
Create: baseVersion,
ReadDelete: baseVersion,
Update: baseVersion,
},
},
"beta field set": {
Features: []Feature{{Version: betaVersion, Item: "beta_field"}},
FieldsInSchema: map[string]interface{}{
"normal_field": "foo",
"beta_field": "bar",
},
ExpectedApiVersions: ExpectedApiVersions{
Create: betaVersion,
ReadDelete: betaVersion,
Update: betaVersion,
},
},
"update only beta field": {
FieldsInSchema: map[string]interface{}{
"normal_field": "foo",
},
UpdatedFields: []string{"beta_update_field"},
UpdateOnlyFields: []Feature{{Version: betaVersion, Item: "beta_update_field"}},
ExpectedApiVersions: ExpectedApiVersions{
Create: baseVersion,
ReadDelete: baseVersion,
Update: betaVersion,
},
},
"nested beta field not set": {
Features: []Feature{{Version: betaVersion, Item: "list_field.*.beta_nested_field"}},
FieldsInSchema: map[string]interface{}{
"list_field.#": 2,
"list_field.0.normal_field": "foo",
"list_field.1.normal_field": "bar",
},
ExpectedApiVersions: ExpectedApiVersions{
Create: baseVersion,
ReadDelete: baseVersion,
Update: baseVersion,
},
},
"nested beta field set": {
Features: []Feature{{Version: betaVersion, Item: "list_field.*.beta_nested_field"}},
FieldsInSchema: map[string]interface{}{
"list_field.#": 2,
"list_field.0.normal_field": "foo",
"list_field.1.normal_field": "bar",
"list_field.1.beta_nested_field": "baz",
},
ExpectedApiVersions: ExpectedApiVersions{
Create: betaVersion,
ReadDelete: betaVersion,
Update: betaVersion,
},
},
"double nested fields set": {
Features: []Feature{{Version: betaVersion, Item: "list_field.*.nested_list_field.*.beta_nested_field"}},
FieldsInSchema: map[string]interface{}{
"list_field.#": 1,
"list_field.0.nested_list_field.#": 1,
"list_field.0.nested_list_field.0.beta_nested_field": "foo",
},
ExpectedApiVersions: ExpectedApiVersions{
Create: betaVersion,
ReadDelete: betaVersion,
Update: betaVersion,
},
},
"beta field has default value": {
Features: []Feature{{Version: betaVersion, Item: "beta_field", DefaultValue: "bar"}},
FieldsInSchema: map[string]interface{}{
"normal_field": "foo",
"beta_field": "bar",
},
ExpectedApiVersions: ExpectedApiVersions{
Create: baseVersion,
ReadDelete: baseVersion,
Update: baseVersion,
},
},
"beta field is updated to default value": {
Features: []Feature{{Version: betaVersion, Item: "beta_field", DefaultValue: "bar"}},
FieldsInSchema: map[string]interface{}{
"normal_field": "foo",
"beta_field": "bar",
},
UpdatedFields: []string{"beta_field"},
ExpectedApiVersions: ExpectedApiVersions{
Create: baseVersion,
ReadDelete: baseVersion,
Update: betaVersion,
},
},
"nested beta field has default value": {
Features: []Feature{{Version: betaVersion, Item: "list_field.*.beta_nested_field", DefaultValue: "baz"}},
FieldsInSchema: map[string]interface{}{
"list_field.#": 2,
"list_field.0.normal_field": "foo",
"list_field.1.normal_field": "bar",
"list_field.1.beta_nested_field": "baz",
},
ExpectedApiVersions: ExpectedApiVersions{
Create: baseVersion,
ReadDelete: baseVersion,
Update: baseVersion,
},
},
"nested beta field is updated default value": {
Features: []Feature{{Version: betaVersion, Item: "list_field.*.beta_nested_field", DefaultValue: "baz"}},
FieldsInSchema: map[string]interface{}{
"list_field.#": 2,
"list_field.0.normal_field": "foo",
"list_field.1.normal_field": "bar",
"list_field.1.beta_nested_field": "baz",
},
UpdatedFields: []string{"list_field.1.beta_nested_field"},
ExpectedApiVersions: ExpectedApiVersions{
Create: baseVersion,
ReadDelete: baseVersion,
Update: betaVersion,
},
},
}
for tn, tc := range cases {
// Create
// All fields with value have HasChange set to true.
keys := make([]string, 0, len(tc.FieldsInSchema))
for key := range tc.FieldsInSchema {
keys = append(keys, key)
}
d := &ResourceDataMock{
FieldsInSchema: tc.FieldsInSchema,
FieldsWithHasChange: keys,
}
apiVersion := getApiVersion(d, v1, tc.Features, maxTestApiVersion)
if apiVersion != tc.ExpectedApiVersions.Create {
t.Errorf("bad: %s, Expected to see version %v for create, got version %v", tn, tc.ExpectedApiVersions.Create, apiVersion)
}
// Read/Delete
// All fields have HasChange set to false.
d = &ResourceDataMock{
FieldsInSchema: tc.FieldsInSchema,
}
apiVersion = getApiVersion(d, v1, tc.Features, maxTestApiVersion)
if apiVersion != tc.ExpectedApiVersions.ReadDelete {
t.Errorf("bad: %s, Expected to see version %v for read/delete, got version %v", tn, tc.ExpectedApiVersions.ReadDelete, apiVersion)
}
// Update
// Only fields defined as updated in the test case have HasChange set to true.
d = &ResourceDataMock{
FieldsInSchema: tc.FieldsInSchema,
FieldsWithHasChange: tc.UpdatedFields,
}
apiVersion = getApiVersionUpdate(d, v1, tc.Features, tc.UpdateOnlyFields, maxTestApiVersion)
if apiVersion != tc.ExpectedApiVersions.Update {
t.Errorf("bad: %s, Expected to see version %v for update, got version %v", tn, tc.ExpectedApiVersions.Update, apiVersion)
}
}
}
func TestSetOmittedFields(t *testing.T) {
type Inner struct {
InnerNotOmitted string `json:"notOmitted"`
InnerOmitted []string `json:"-"`
}
type InputOuter struct {
NotOmitted string `json:"notOmitted"`
Omitted []string `json:"-"`
Struct Inner
Pointer *Inner
StructSlice []Inner
PointerSlice []*Inner
Unset *Inner
OnlyInInputType *Inner
}
type OutputOuter struct {
NotOmitted string `json:"notOmitted"`
Omitted []string `json:"-"`
Struct Inner
Pointer *Inner
StructSlice []Inner
PointerSlice []*Inner
Unset *Inner
OnlyInOutputType *Inner
}
input := &InputOuter{
NotOmitted: "foo",
Omitted: []string{"foo"},
Struct: Inner{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
},
Pointer: &Inner{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
},
StructSlice: []Inner{
{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
}, {
InnerNotOmitted: "bar",
InnerOmitted: []string{"bar"},
},
},
PointerSlice: []*Inner{
{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
}, {
InnerNotOmitted: "bar",
InnerOmitted: []string{"bar"},
},
},
OnlyInInputType: &Inner{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
},
}
output := &OutputOuter{}
Convert(input, output)
if input.NotOmitted != output.NotOmitted ||
!reflect.DeepEqual(input.Omitted, output.Omitted) ||
!reflect.DeepEqual(input.Struct, output.Struct) ||
!reflect.DeepEqual(input.Pointer, output.Pointer) ||
!reflect.DeepEqual(input.StructSlice, output.StructSlice) ||
!reflect.DeepEqual(input.PointerSlice, output.PointerSlice) ||
!(input.Unset == nil && output.Unset == nil) {
t.Errorf("Structs were not equivalent after conversion:\nInput:%#v\nOutput: %#v", input, output)
}
}
type ResourceDataMock struct {
FieldsInSchema map[string]interface{}
FieldsWithHasChange []string
id string
}
func (d *ResourceDataMock) HasChange(key string) bool {
exists := false
for _, val := range d.FieldsWithHasChange {
if key == val {
exists = true
}
}
return exists
}
func (d *ResourceDataMock) GetOk(key string) (interface{}, bool) {
for k, v := range d.FieldsInSchema {
if key == k {
return v, true
}
}
return nil, false
}
func (d *ResourceDataMock) Set(key string, value interface{}) error {
d.FieldsInSchema[key] = value
return nil
}
func (d *ResourceDataMock) SetId(v string) {
d.id = v
}
func (d *ResourceDataMock) Id() string {
return d.id
}

77
google/convert.go Normal file
View File

@ -0,0 +1,77 @@
package google
import (
"encoding/json"
"reflect"
)
// 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())
}
}
}
}

79
google/convert_test.go Normal file
View File

@ -0,0 +1,79 @@
package google
import (
"reflect"
"testing"
)
func TestSetOmittedFields(t *testing.T) {
type Inner struct {
InnerNotOmitted string `json:"notOmitted"`
InnerOmitted []string `json:"-"`
}
type InputOuter struct {
NotOmitted string `json:"notOmitted"`
Omitted []string `json:"-"`
Struct Inner
Pointer *Inner
StructSlice []Inner
PointerSlice []*Inner
Unset *Inner
OnlyInInputType *Inner
}
type OutputOuter struct {
NotOmitted string `json:"notOmitted"`
Omitted []string `json:"-"`
Struct Inner
Pointer *Inner
StructSlice []Inner
PointerSlice []*Inner
Unset *Inner
OnlyInOutputType *Inner
}
input := &InputOuter{
NotOmitted: "foo",
Omitted: []string{"foo"},
Struct: Inner{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
},
Pointer: &Inner{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
},
StructSlice: []Inner{
{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
}, {
InnerNotOmitted: "bar",
InnerOmitted: []string{"bar"},
},
},
PointerSlice: []*Inner{
{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
}, {
InnerNotOmitted: "bar",
InnerOmitted: []string{"bar"},
},
},
OnlyInInputType: &Inner{
InnerNotOmitted: "foo",
InnerOmitted: []string{"foo"},
},
}
output := &OutputOuter{}
Convert(input, output)
if input.NotOmitted != output.NotOmitted ||
!reflect.DeepEqual(input.Omitted, output.Omitted) ||
!reflect.DeepEqual(input.Struct, output.Struct) ||
!reflect.DeepEqual(input.Pointer, output.Pointer) ||
!reflect.DeepEqual(input.StructSlice, output.StructSlice) ||
!reflect.DeepEqual(input.PointerSlice, output.PointerSlice) ||
!(input.Unset == nil && output.Unset == nil) {
t.Errorf("Structs were not equivalent after conversion:\nInput:%#v\nOutput: %#v", input, output)
}
}

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
) )
@ -123,3 +124,7 @@ func flattenSecondaryRanges(secondaryRanges []*compute.SubnetworkSecondaryRange)
func createSubnetID(s *compute.Subnetwork) string { func createSubnetID(s *compute.Subnetwork) string {
return fmt.Sprintf("%s/%s", s.Region, s.Name) 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,9 +12,6 @@ import (
compute "google.golang.org/api/compute/v1" compute "google.golang.org/api/compute/v1"
) )
var BackendServiceBaseApiVersion = v1
var BackendServiceVersionedFeatures = []Feature{Feature{Version: v0beta, Item: "security_policy"}}
func resourceComputeBackendService() *schema.Resource { func resourceComputeBackendService() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceComputeBackendServiceCreate, Create: resourceComputeBackendServiceCreate,
@ -288,7 +285,6 @@ func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{
} }
func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) error { func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, BackendServiceBaseApiVersion, BackendServiceVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -296,25 +292,9 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{})
return err return err
} }
service := &computeBeta.BackendService{} service, err := config.clientComputeBeta.BackendServices.Get(project, d.Id()).Do()
switch computeApiVersion { if err != nil {
case v1: return handleNotFoundError(err, d, fmt.Sprintf("Backend Service %q", d.Get("name").(string)))
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) d.Set("name", service.Name)
@ -325,7 +305,7 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{})
d.Set("session_affinity", service.SessionAffinity) d.Set("session_affinity", service.SessionAffinity)
d.Set("timeout_sec", service.TimeoutSec) d.Set("timeout_sec", service.TimeoutSec)
d.Set("fingerprint", service.Fingerprint) d.Set("fingerprint", service.Fingerprint)
d.Set("self_link", service.SelfLink) d.Set("self_link", ConvertSelfLinkToV1(service.SelfLink))
d.Set("backend", flattenBackends(service.Backends)) d.Set("backend", flattenBackends(service.Backends))
d.Set("connection_draining_timeout_sec", service.ConnectionDraining.DrainingTimeoutSec) d.Set("connection_draining_timeout_sec", service.ConnectionDraining.DrainingTimeoutSec)
d.Set("iap", flattenIap(service.Iap)) d.Set("iap", flattenIap(service.Iap))

View File

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

View File

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

View File

@ -19,9 +19,6 @@ import (
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
var InstanceBaseApiVersion = v1
var InstanceVersionedFeatures = []Feature{}
func resourceComputeInstance() *schema.Resource { func resourceComputeInstance() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceComputeInstanceCreate, Create: resourceComputeInstanceCreate,
@ -595,21 +592,9 @@ func getInstance(config *Config, d *schema.ResourceData) (*computeBeta.Instance,
if err != nil { if err != nil {
return nil, err return nil, err
} }
instance := &computeBeta.Instance{} instance, err := config.clientComputeBeta.Instances.Get(project, zone, d.Id()).Do()
switch getComputeApiVersion(d, InstanceBaseApiVersion, InstanceVersionedFeatures) { if err != nil {
case v1: return nil, handleNotFoundError(err, d, fmt.Sprintf("Instance %s", d.Get("name").(string)))
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 return instance, nil
} }
@ -745,17 +730,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
} }
log.Printf("[INFO] Requesting instance creation") log.Printf("[INFO] Requesting instance creation")
var op interface{} op, err := config.clientComputeBeta.Instances.Insert(project, zone.Name, instance).Do()
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 { if err != nil {
return fmt.Errorf("Error creating instance: %s", err) return fmt.Errorf("Error creating instance: %s", err)
} }
@ -887,7 +862,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
} }
adIndex, inConfig := attachedDiskSources[source.RelativeLink()] adIndex, inConfig := attachedDiskSources[source.RelativeLink()]
di := map[string]interface{}{ di := map[string]interface{}{
"source": disk.Source, "source": ConvertSelfLinkToV1(disk.Source),
"device_name": disk.DeviceName, "device_name": disk.DeviceName,
"mode": disk.Mode, "mode": disk.Mode,
} }
@ -1608,7 +1583,7 @@ func flattenBootDisk(d *schema.ResourceData, disk *computeBeta.AttachedDisk, con
result := map[string]interface{}{ result := map[string]interface{}{
"auto_delete": disk.AutoDelete, "auto_delete": disk.AutoDelete,
"device_name": disk.DeviceName, "device_name": disk.DeviceName,
"source": disk.Source, "source": ConvertSelfLinkToV1(disk.Source),
// disk_encryption_key_raw is not returned from the API, so copy it from what the user // disk_encryption_key_raw is not returned from the API, so copy it from what the user
// originally specified to avoid diffs. // originally specified to avoid diffs.
"disk_encryption_key_raw": d.Get("boot_disk.0.disk_encryption_key_raw"), "disk_encryption_key_raw": d.Get("boot_disk.0.disk_encryption_key_raw"),

View File

@ -14,12 +14,6 @@ import (
"google.golang.org/api/compute/v1" "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 { func resourceComputeInstanceGroupManager() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceComputeInstanceGroupManagerCreate, Create: resourceComputeInstanceGroupManagerCreate,
@ -234,7 +228,6 @@ func getNamedPortsBeta(nps []interface{}) []*computeBeta.NamedPort {
} }
func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error { func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, InstanceGroupManagerBaseApiVersion, InstanceGroupManagerVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -266,29 +259,8 @@ func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta inte
} }
log.Printf("[DEBUG] InstanceGroupManager insert request: %#v", manager) log.Printf("[DEBUG] InstanceGroupManager insert request: %#v", manager)
var op interface{} op, err := config.clientComputeBeta.InstanceGroupManagers.Insert(
switch computeApiVersion { project, zone, manager).Do()
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 { if err != nil {
return fmt.Errorf("Error creating InstanceGroupManager: %s", err) return fmt.Errorf("Error creating InstanceGroupManager: %s", err)
@ -319,7 +291,6 @@ func flattenNamedPortsBeta(namedPorts []*computeBeta.NamedPort) []map[string]int
} }
func getManager(d *schema.ResourceData, meta interface{}) (*computeBeta.InstanceGroupManager, error) { func getManager(d *schema.ResourceData, meta interface{}) (*computeBeta.InstanceGroupManager, error) {
computeApiVersion := getComputeApiVersion(d, InstanceGroupManagerBaseApiVersion, InstanceGroupManagerVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -332,82 +303,38 @@ func getManager(d *schema.ResourceData, meta interface{}) (*computeBeta.Instance
return nil, err return nil, err
} }
manager := &computeBeta.InstanceGroupManager{} getInstanceGroupManager := func(zone string) (interface{}, error) {
switch computeApiVersion { return config.clientComputeBeta.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
case v1:
getInstanceGroupManager := func(zone string) (interface{}, error) {
return config.clientCompute.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
}
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
} }
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 = resource.(*computeBeta.InstanceGroupManager)
}
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 return manager, nil
} }
@ -424,7 +351,7 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf
} }
d.Set("base_instance_name", manager.BaseInstanceName) d.Set("base_instance_name", manager.BaseInstanceName)
d.Set("instance_template", manager.InstanceTemplate) d.Set("instance_template", ConvertSelfLinkToV1(manager.InstanceTemplate))
d.Set("name", manager.Name) d.Set("name", manager.Name)
d.Set("zone", GetResourceNameFromSelfLink(manager.Zone)) d.Set("zone", GetResourceNameFromSelfLink(manager.Zone))
d.Set("description", manager.Description) d.Set("description", manager.Description)
@ -433,7 +360,7 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf
d.Set("target_pools", manager.TargetPools) d.Set("target_pools", manager.TargetPools)
d.Set("named_port", flattenNamedPortsBeta(manager.NamedPorts)) d.Set("named_port", flattenNamedPortsBeta(manager.NamedPorts))
d.Set("fingerprint", manager.Fingerprint) d.Set("fingerprint", manager.Fingerprint)
d.Set("instance_group", manager.InstanceGroup) d.Set("instance_group", ConvertSelfLinkToV1(manager.InstanceGroup))
d.Set("self_link", ConvertSelfLinkToV1(manager.SelfLink)) d.Set("self_link", ConvertSelfLinkToV1(manager.SelfLink))
update_strategy, ok := d.GetOk("update_strategy") update_strategy, ok := d.GetOk("update_strategy")
if !ok { if !ok {
@ -459,7 +386,6 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf
} }
func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error { func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersionUpdate(d, InstanceGroupManagerBaseApiVersion, InstanceGroupManagerVersionedFeatures, []Feature{})
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -488,27 +414,8 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
TargetPools: targetPools, TargetPools: targetPools,
} }
var op interface{} op, err := config.clientComputeBeta.InstanceGroupManagers.SetTargetPools(
switch computeApiVersion { project, zone, d.Id(), setTargetPools).Do()
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 { if err != nil {
return fmt.Errorf("Error updating InstanceGroupManager: %s", err) return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
@ -530,27 +437,8 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
InstanceTemplate: d.Get("instance_template").(string), InstanceTemplate: d.Get("instance_template").(string),
} }
var op interface{} op, err := config.clientComputeBeta.InstanceGroupManagers.SetInstanceTemplate(
switch computeApiVersion { project, zone, d.Id(), setInstanceTemplate).Do()
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 { if err != nil {
return fmt.Errorf("Error updating InstanceGroupManager: %s", err) return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
@ -563,30 +451,10 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
} }
if d.Get("update_strategy").(string) == "RESTART" { if d.Get("update_strategy").(string) == "RESTART" {
managedInstances := &computeBeta.InstanceGroupManagersListManagedInstancesResponse{} managedInstances, err := config.clientComputeBeta.InstanceGroupManagers.ListManagedInstances(
switch computeApiVersion { project, zone, d.Id()).Do()
case v1: if err != nil {
managedInstancesV1, err := config.clientCompute.InstanceGroupManagers.ListManagedInstances( return fmt.Errorf("Error getting instance group managers instances: %s", err)
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) managedInstanceCount := len(managedInstances.ManagedInstances)
@ -599,32 +467,10 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
Instances: instances, Instances: instances,
} }
var op interface{} op, err = config.clientComputeBeta.InstanceGroupManagers.RecreateInstances(
switch computeApiVersion { project, zone, d.Id(), recreateInstances).Do()
case v1: if err != nil {
recreateInstancesV1 := &compute.InstanceGroupManagersRecreateInstancesRequest{} return fmt.Errorf("Error restarting instance group managers instances: %s", err)
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 // Wait for the operation to complete
@ -669,27 +515,8 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
} }
// Make the request: // Make the request:
var op interface{} op, err := config.clientComputeBeta.InstanceGroups.SetNamedPorts(
switch computeApiVersion { project, zone, d.Id(), setNamedPorts).Do()
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 { if err != nil {
return fmt.Errorf("Error updating InstanceGroupManager: %s", err) return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
@ -706,15 +533,8 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
if d.HasChange("target_size") { if d.HasChange("target_size") {
targetSize := int64(d.Get("target_size").(int)) targetSize := int64(d.Get("target_size").(int))
var op interface{} op, err := config.clientComputeBeta.InstanceGroupManagers.Resize(
switch computeApiVersion { project, zone, d.Id(), targetSize).Do()
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 { if err != nil {
return fmt.Errorf("Error updating InstanceGroupManager: %s", err) return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
@ -758,7 +578,6 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
} }
func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error { func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, InstanceGroupManagerBaseApiVersion, InstanceGroupManagerVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -771,24 +590,12 @@ func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta inte
return err return err
} }
var op interface{} op, err := config.clientComputeBeta.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
switch computeApiVersion { attempt := 0
case v1: for err != nil && attempt < 20 {
op, err = config.clientCompute.InstanceGroupManagers.Delete(project, zone, d.Id()).Do() attempt++
attempt := 0 time.Sleep(2000 * time.Millisecond)
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() 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 { if err != nil {
@ -805,26 +612,14 @@ func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta inte
return err return err
} }
var instanceGroupSize int64 instanceGroup, err := config.clientComputeBeta.InstanceGroups.Get(
switch computeApiVersion { project, zone, d.Id()).Do()
case v1: if err != nil {
instanceGroup, err := config.clientCompute.InstanceGroups.Get( return fmt.Errorf("Error getting instance group size: %s", err)
project, zone, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error getting instance group size: %s", err)
}
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
} }
instanceGroupSize := instanceGroup.Size
if instanceGroupSize >= currentSize { if instanceGroupSize >= currentSize {
return fmt.Errorf("Error, instance group isn't shrinking during delete") return fmt.Errorf("Error, instance group isn't shrinking during delete")
} }

View File

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

View File

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

View File

@ -8,9 +8,6 @@ import (
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
) )
var ProjectMetadataBaseApiVersion = v1
var ProjectMetadataVersionedFeatures = []Feature{}
func resourceComputeProjectMetadata() *schema.Resource { func resourceComputeProjectMetadata() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceComputeProjectMetadataCreate, Create: resourceComputeProjectMetadataCreate,

View File

@ -12,16 +12,8 @@ import (
"github.com/hashicorp/terraform/helper/validation" "github.com/hashicorp/terraform/helper/validation"
computeBeta "google.golang.org/api/compute/v0.beta" 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 { func resourceComputeRegionInstanceGroupManager() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceComputeRegionInstanceGroupManagerCreate, Create: resourceComputeRegionInstanceGroupManagerCreate,
@ -230,7 +222,6 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource {
} }
func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error { func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, RegionInstanceGroupManagerBaseApiVersion, RegionInstanceGroupManagerVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -256,19 +247,7 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met
ForceSendFields: []string{"TargetSize"}, ForceSendFields: []string{"TargetSize"},
} }
var op interface{} op, err := config.clientComputeBeta.RegionInstanceGroupManagers.Insert(project, d.Get("region").(string), manager).Do()
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 { if err != nil {
return fmt.Errorf("Error creating RegionInstanceGroupManager: %s", err) return fmt.Errorf("Error creating RegionInstanceGroupManager: %s", err)
@ -287,7 +266,6 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met
type getInstanceManagerFunc func(*schema.ResourceData, interface{}) (*computeBeta.InstanceGroupManager, error) type getInstanceManagerFunc func(*schema.ResourceData, interface{}) (*computeBeta.InstanceGroupManager, error)
func getRegionalManager(d *schema.ResourceData, meta interface{}) (*computeBeta.InstanceGroupManager, error) { func getRegionalManager(d *schema.ResourceData, meta interface{}) (*computeBeta.InstanceGroupManager, error) {
computeApiVersion := getComputeApiVersion(d, RegionInstanceGroupManagerBaseApiVersion, RegionInstanceGroupManagerVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -299,32 +277,12 @@ func getRegionalManager(d *schema.ResourceData, meta interface{}) (*computeBeta.
if err != nil { if err != nil {
return nil, err return nil, err
} }
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 { if err != nil {
return nil, handleNotFoundError(err, d, fmt.Sprintf("Region Instance Manager %q", d.Get("name").(string))) return nil, handleNotFoundError(err, d, fmt.Sprintf("Region Instance Manager %q", d.Get("name").(string)))
} }
return manager, nil return manager, nil
} }
@ -389,7 +347,6 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta
} }
func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error { func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersionUpdate(d, RegionInstanceGroupManagerBaseApiVersion, RegionInstanceGroupManagerVersionedFeatures, []Feature{})
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -414,27 +371,8 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
TargetPools: targetPools, TargetPools: targetPools,
} }
var op interface{} op, err := config.clientComputeBeta.RegionInstanceGroupManagers.SetTargetPools(
switch computeApiVersion { project, region, d.Id(), setTargetPools).Do()
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 { if err != nil {
return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err) return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err)
@ -455,27 +393,8 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
InstanceTemplate: d.Get("instance_template").(string), InstanceTemplate: d.Get("instance_template").(string),
} }
var op interface{} op, err := config.clientComputeBeta.RegionInstanceGroupManagers.SetInstanceTemplate(
switch computeApiVersion { project, region, d.Id(), setInstanceTemplate).Do()
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 { if err != nil {
return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err) return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err)
@ -520,27 +439,8 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
} }
// Make the request: // Make the request:
var op interface{} op, err := config.clientComputeBeta.RegionInstanceGroups.SetNamedPorts(
switch computeApiVersion { project, region, d.Id(), setNamedPorts).Do()
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 { if err != nil {
return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err) return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err)
@ -557,15 +457,8 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
if d.HasChange("target_size") { if d.HasChange("target_size") {
targetSize := int64(d.Get("target_size").(int)) targetSize := int64(d.Get("target_size").(int))
var op interface{} op, err := config.clientComputeBeta.RegionInstanceGroupManagers.Resize(
switch computeApiVersion { project, region, d.Id(), targetSize).Do()
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 { if err != nil {
return fmt.Errorf("Error resizing RegionInstanceGroupManager: %s", err) return fmt.Errorf("Error resizing RegionInstanceGroupManager: %s", err)
@ -608,7 +501,6 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
} }
func resourceComputeRegionInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error { func resourceComputeRegionInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, RegionInstanceGroupManagerBaseApiVersion, RegionInstanceGroupManagerVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -618,13 +510,7 @@ func resourceComputeRegionInstanceGroupManagerDelete(d *schema.ResourceData, met
region := d.Get("region").(string) region := d.Get("region").(string)
var op interface{} op, err := config.clientComputeBeta.RegionInstanceGroupManagers.Delete(project, region, d.Id()).Do()
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 { if err != nil {
return fmt.Errorf("Error deleting region instance group manager: %s", err) return fmt.Errorf("Error deleting region instance group manager: %s", err)

View File

@ -14,14 +14,6 @@ import (
"google.golang.org/api/compute/v1" "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 { func resourceComputeSubnetwork() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceComputeSubnetworkCreate, Create: resourceComputeSubnetworkCreate,
@ -130,7 +122,6 @@ func resourceComputeSubnetwork() *schema.Resource {
} }
func resourceComputeSubnetworkCreate(d *schema.ResourceData, meta interface{}) error { func resourceComputeSubnetworkCreate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, SubnetworkBaseApiVersion, SubnetworkVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
network, err := ParseNetworkFieldValue(d.Get("network").(string), d, config) network, err := ParseNetworkFieldValue(d.Get("network").(string), d, config)
if err != nil { if err != nil {
@ -160,21 +151,7 @@ func resourceComputeSubnetworkCreate(d *schema.ResourceData, meta interface{}) e
log.Printf("[DEBUG] Subnetwork insert request: %#v", subnetwork) log.Printf("[DEBUG] Subnetwork insert request: %#v", subnetwork)
subnetworkV1 := &compute.Subnetwork{} op, err := config.clientComputeBeta.Subnetworks.Insert(project, region, subnetwork).Do()
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 { if err != nil {
return fmt.Errorf("Error creating subnetwork: %s", err) return fmt.Errorf("Error creating subnetwork: %s", err)
} }
@ -184,9 +161,8 @@ 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. // "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." // 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 // https://cloud.google.com/compute/docs/subnetworks
subnetworkV1.Region = region
subnetwork.Region = region subnetwork.Region = region
d.SetId(createSubnetID(subnetworkV1)) d.SetId(createSubnetIDBeta(subnetwork))
err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), "Creating Subnetwork") err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), "Creating Subnetwork")
if err != nil { if err != nil {
@ -197,45 +173,6 @@ func resourceComputeSubnetworkCreate(d *schema.ResourceData, meta interface{}) e
} }
func resourceComputeSubnetworkRead(d *schema.ResourceData, meta interface{}) error { 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) config := meta.(*Config)
region, err := getRegion(d, config) region, err := getRegion(d, config)
@ -272,7 +209,6 @@ func resourceComputeSubnetworkReadV0Beta(d *schema.ResourceData, meta interface{
} }
func resourceComputeSubnetworkUpdate(d *schema.ResourceData, meta interface{}) error { func resourceComputeSubnetworkUpdate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, SubnetworkBaseApiVersion, SubnetworkVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
region, err := getRegion(d, config) region, err := getRegion(d, config)
@ -328,7 +264,7 @@ func resourceComputeSubnetworkUpdate(d *schema.ResourceData, meta interface{}) e
d.SetPartial("ip_cidr_range") d.SetPartial("ip_cidr_range")
} }
if (d.HasChange("secondary_ip_range") || d.HasChange("enable_flow_logs")) && computeApiVersion == v0beta { if d.HasChange("secondary_ip_range") || d.HasChange("enable_flow_logs") {
v0BetaSubnetwork := &computeBeta.Subnetwork{ v0BetaSubnetwork := &computeBeta.Subnetwork{
Fingerprint: d.Get("fingerprint").(string), Fingerprint: d.Get("fingerprint").(string),
} }

View File

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

View File

@ -12,23 +12,12 @@ import (
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation" "github.com/hashicorp/terraform/helper/validation"
"google.golang.org/api/container/v1"
containerBeta "google.golang.org/api/container/v1beta1" containerBeta "google.golang.org/api/container/v1beta1"
) )
var ( var (
instanceGroupManagerURL = regexp.MustCompile(fmt.Sprintf("^https://www.googleapis.com/compute/v1/projects/(%s)/zones/([a-z0-9-]*)/instanceGroupManagers/([^/]*)", ProjectRegex)) instanceGroupManagerURL = regexp.MustCompile(fmt.Sprintf("^https://www.googleapis.com/compute/v1/projects/(%s)/zones/([a-z0-9-]*)/instanceGroupManagers/([^/]*)", ProjectRegex))
ContainerClusterBaseApiVersion = v1
ContainerClusterVersionedFeatures = []Feature{
{Version: v1beta1, Item: "pod_security_policy_config"},
{Version: v1beta1, Item: "node_config.*.taint"},
{Version: v1beta1, Item: "node_config.*.workload_metadata_config"},
{Version: v1beta1, Item: "private_cluster"},
{Version: v1beta1, Item: "master_ipv4_cidr_block"},
{Version: v1beta1, Item: "region"},
}
networkConfig = &schema.Resource{ networkConfig = &schema.Resource{
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
"cidr_blocks": { "cidr_blocks": {
@ -475,7 +464,6 @@ func resourceContainerCluster() *schema.Resource {
} }
func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) error { func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) error {
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -651,25 +639,8 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
mutexKV.Lock(containerClusterMutexKey(project, location, clusterName)) mutexKV.Lock(containerClusterMutexKey(project, location, clusterName))
defer mutexKV.Unlock(containerClusterMutexKey(project, location, clusterName)) defer mutexKV.Unlock(containerClusterMutexKey(project, location, clusterName))
var op interface{} parent := fmt.Sprintf("projects/%s/locations/%s", project, location)
switch containerAPIVersion { op, err := config.clientContainerBeta.Projects.Locations.Clusters.Create(parent, req).Do()
case v1:
reqV1 := &container.CreateClusterRequest{}
err = Convert(req, reqV1)
if err != nil {
return err
}
op, err = config.clientContainer.Projects.Zones.Clusters.Create(project, location, reqV1).Do()
case v1beta1:
reqV1Beta := &containerBeta.CreateClusterRequest{}
err = Convert(req, reqV1Beta)
if err != nil {
return err
}
parent := fmt.Sprintf("projects/%s/locations/%s", project, location)
op, err = config.clientContainerBeta.Projects.Locations.Clusters.Create(parent, reqV1Beta).Do()
}
if err != nil { if err != nil {
return err return err
} }
@ -687,15 +658,8 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
log.Printf("[INFO] GKE cluster %s has been created", clusterName) log.Printf("[INFO] GKE cluster %s has been created", clusterName)
if d.Get("remove_default_node_pool").(bool) { if d.Get("remove_default_node_pool").(bool) {
var op interface{} parent := fmt.Sprintf("%s/nodePools/%s", containerClusterFullName(project, location, clusterName), "default-pool")
switch containerAPIVersion { op, err = config.clientContainerBeta.Projects.Locations.Clusters.NodePools.Delete(parent).Do()
case v1:
op, err = config.clientContainer.Projects.Zones.Clusters.NodePools.Delete(
project, location, clusterName, "default-pool").Do()
case v1beta1:
parent := fmt.Sprintf("%s/nodePools/%s", containerClusterFullName(project, location, clusterName), "default-pool")
op, err = config.clientContainerBeta.Projects.Locations.Clusters.NodePools.Delete(parent).Do()
}
if err != nil { if err != nil {
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err) return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
} }
@ -709,7 +673,6 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
} }
func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) error { func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) error {
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -723,20 +686,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro
} }
cluster := &containerBeta.Cluster{} cluster := &containerBeta.Cluster{}
var clust interface{}
err = resource.Retry(2*time.Minute, func() *resource.RetryError { err = resource.Retry(2*time.Minute, func() *resource.RetryError {
switch containerAPIVersion { name := containerClusterFullName(project, location, d.Get("name").(string))
case v1: cluster, err = config.clientContainerBeta.Projects.Locations.Clusters.Get(name).Do()
clust, err = config.clientContainer.Projects.Zones.Clusters.Get(
project, location, d.Get("name").(string)).Do()
case v1beta1:
name := containerClusterFullName(project, location, d.Get("name").(string))
clust, err = config.clientContainerBeta.Projects.Locations.Clusters.Get(name).Do()
}
if err != nil {
return resource.NonRetryableError(err)
}
err = Convert(clust, cluster)
if err != nil { if err != nil {
return resource.NonRetryableError(err) return resource.NonRetryableError(err)
} }
@ -834,7 +786,6 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro
} }
func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) error { func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) error {
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
config := meta.(*Config) config := meta.(*Config)
project, err := getProject(d, config) project, err := getProject(d, config)
@ -854,22 +805,10 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
lockKey := containerClusterMutexKey(project, location, clusterName) lockKey := containerClusterMutexKey(project, location, clusterName)
updateFunc := func(req *container.UpdateClusterRequest, updateDescription string) func() error { updateFunc := func(req *containerBeta.UpdateClusterRequest, updateDescription string) func() error {
return func() error { return func() error {
var err error name := containerClusterFullName(project, location, clusterName)
var op interface{} op, err := config.clientContainerBeta.Projects.Locations.Clusters.Update(name, req).Do()
switch containerAPIVersion {
case v1:
op, err = config.clientContainer.Projects.Zones.Clusters.Update(project, location, clusterName, req).Do()
case v1beta1:
reqV1Beta := &containerBeta.UpdateClusterRequest{}
err = Convert(req, reqV1Beta)
if err != nil {
return err
}
name := containerClusterFullName(project, location, clusterName)
op, err = config.clientContainerBeta.Projects.Locations.Clusters.Update(name, reqV1Beta).Do()
}
if err != nil { if err != nil {
return err return err
} }
@ -883,14 +822,9 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
// if the order of updating fields does matter, it is called out explicitly. // if the order of updating fields does matter, it is called out explicitly.
if d.HasChange("master_authorized_networks_config") { if d.HasChange("master_authorized_networks_config") {
c := d.Get("master_authorized_networks_config") c := d.Get("master_authorized_networks_config")
conf := &container.MasterAuthorizedNetworksConfig{} req := &containerBeta.UpdateClusterRequest{
err := Convert(expandMasterAuthorizedNetworksConfig(c), conf) Update: &containerBeta.ClusterUpdate{
if err != nil { DesiredMasterAuthorizedNetworksConfig: expandMasterAuthorizedNetworksConfig(c),
return err
}
req := &container.UpdateClusterRequest{
Update: &container.ClusterUpdate{
DesiredMasterAuthorizedNetworksConfig: conf,
}, },
} }
@ -918,8 +852,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
// Only upgrade the master if the current version is lower than the desired version // Only upgrade the master if the current version is lower than the desired version
if cur.LessThan(des) { if cur.LessThan(des) {
req := &container.UpdateClusterRequest{ req := &containerBeta.UpdateClusterRequest{
Update: &container.ClusterUpdate{ Update: &containerBeta.ClusterUpdate{
DesiredMasterVersion: desiredMasterVersion, DesiredMasterVersion: desiredMasterVersion,
}, },
} }
@ -936,8 +870,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
if d.HasChange("node_version") { if d.HasChange("node_version") {
desiredNodeVersion := d.Get("node_version").(string) desiredNodeVersion := d.Get("node_version").(string)
req := &container.UpdateClusterRequest{ req := &containerBeta.UpdateClusterRequest{
Update: &container.ClusterUpdate{ Update: &containerBeta.ClusterUpdate{
DesiredNodeVersion: desiredNodeVersion, DesiredNodeVersion: desiredNodeVersion,
}, },
} }
@ -955,14 +889,9 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
if d.HasChange("addons_config") { if d.HasChange("addons_config") {
if ac, ok := d.GetOk("addons_config"); ok { if ac, ok := d.GetOk("addons_config"); ok {
conf := &container.AddonsConfig{} req := &containerBeta.UpdateClusterRequest{
err := Convert(expandClusterAddonsConfig(ac), conf) Update: &containerBeta.ClusterUpdate{
if err != nil { DesiredAddonsConfig: expandClusterAddonsConfig(ac),
return err
}
req := &container.UpdateClusterRequest{
Update: &container.ClusterUpdate{
DesiredAddonsConfig: conf,
}, },
} }
@ -979,37 +908,20 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
} }
if d.HasChange("maintenance_policy") { if d.HasChange("maintenance_policy") {
var req *container.SetMaintenancePolicyRequest var req *containerBeta.SetMaintenancePolicyRequest
if mp, ok := d.GetOk("maintenance_policy"); ok { if mp, ok := d.GetOk("maintenance_policy"); ok {
pol := &container.MaintenancePolicy{} req = &containerBeta.SetMaintenancePolicyRequest{
err := Convert(expandMaintenancePolicy(mp), pol) MaintenancePolicy: expandMaintenancePolicy(mp),
if err != nil {
return err
}
req = &container.SetMaintenancePolicyRequest{
MaintenancePolicy: pol,
} }
} else { } else {
req = &container.SetMaintenancePolicyRequest{ req = &containerBeta.SetMaintenancePolicyRequest{
NullFields: []string{"MaintenancePolicy"}, NullFields: []string{"MaintenancePolicy"},
} }
} }
updateF := func() error { updateF := func() error {
var op interface{} name := containerClusterFullName(project, location, clusterName)
switch containerAPIVersion { op, err := config.clientContainerBeta.Projects.Locations.Clusters.SetMaintenancePolicy(name, req).Do()
case v1:
op, err = config.clientContainer.Projects.Zones.Clusters.SetMaintenancePolicy(
project, location, clusterName, req).Do()
case v1beta1:
reqV1Beta := &containerBeta.SetMaintenancePolicyRequest{}
err = Convert(req, reqV1Beta)
if err != nil {
return err
}
name := containerClusterFullName(project, location, clusterName)
op, err = config.clientContainerBeta.Projects.Locations.Clusters.SetMaintenancePolicy(name, reqV1Beta).Do()
}
if err != nil { if err != nil {
return err return err
@ -1044,8 +956,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
azSet.Add(location) azSet.Add(location)
} }
req := &container.UpdateClusterRequest{ req := &containerBeta.UpdateClusterRequest{
Update: &container.ClusterUpdate{ Update: &containerBeta.ClusterUpdate{
DesiredLocations: convertStringSet(azSet), DesiredLocations: convertStringSet(azSet),
}, },
} }
@ -1060,8 +972,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
azSetNew.Add(location) azSetNew.Add(location)
} }
if !azSet.Equal(azSetNew) { if !azSet.Equal(azSetNew) {
req = &container.UpdateClusterRequest{ req = &containerBeta.UpdateClusterRequest{
Update: &container.ClusterUpdate{ Update: &containerBeta.ClusterUpdate{
DesiredLocations: convertStringSet(azSetNew), DesiredLocations: convertStringSet(azSetNew),
}, },
} }
@ -1080,26 +992,15 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
if d.HasChange("enable_legacy_abac") { if d.HasChange("enable_legacy_abac") {
enabled := d.Get("enable_legacy_abac").(bool) enabled := d.Get("enable_legacy_abac").(bool)
req := &container.SetLegacyAbacRequest{ req := &containerBeta.SetLegacyAbacRequest{
Enabled: enabled, Enabled: enabled,
ForceSendFields: []string{"Enabled"}, ForceSendFields: []string{"Enabled"},
} }
updateF := func() error { updateF := func() error {
log.Println("[DEBUG] updating enable_legacy_abac") log.Println("[DEBUG] updating enable_legacy_abac")
var op interface{} name := containerClusterFullName(project, location, clusterName)
switch containerAPIVersion { op, err := config.clientContainerBeta.Projects.Locations.Clusters.SetLegacyAbac(name, req).Do()
case v1:
op, err = config.clientContainer.Projects.Zones.Clusters.LegacyAbac(project, location, clusterName, req).Do()
case v1beta1:
reqV1Beta := &containerBeta.SetLegacyAbacRequest{}
err = Convert(req, reqV1Beta)
if err != nil {
return err
}
name := containerClusterFullName(project, location, clusterName)
op, err = config.clientContainerBeta.Projects.Locations.Clusters.SetLegacyAbac(name, reqV1Beta).Do()
}
if err != nil { if err != nil {
return err return err
} }
@ -1123,8 +1024,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
if d.HasChange("monitoring_service") { if d.HasChange("monitoring_service") {
desiredMonitoringService := d.Get("monitoring_service").(string) desiredMonitoringService := d.Get("monitoring_service").(string)
req := &container.UpdateClusterRequest{ req := &containerBeta.UpdateClusterRequest{
Update: &container.ClusterUpdate{ Update: &containerBeta.ClusterUpdate{
DesiredMonitoringService: desiredMonitoringService, DesiredMonitoringService: desiredMonitoringService,
}, },
} }
@ -1142,32 +1043,14 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
if d.HasChange("network_policy") { if d.HasChange("network_policy") {
np := d.Get("network_policy") np := d.Get("network_policy")
req := &containerBeta.SetNetworkPolicyRequest{
pol := &container.NetworkPolicy{} NetworkPolicy: expandNetworkPolicy(np),
err := Convert(expandNetworkPolicy(np), pol)
if err != nil {
return err
}
req := &container.SetNetworkPolicyRequest{
NetworkPolicy: pol,
} }
updateF := func() error { updateF := func() error {
log.Println("[DEBUG] updating network_policy") log.Println("[DEBUG] updating network_policy")
var op interface{} name := containerClusterFullName(project, location, clusterName)
switch containerAPIVersion { op, err := config.clientContainerBeta.Projects.Locations.Clusters.SetNetworkPolicy(name, req).Do()
case v1:
op, err = config.clientContainer.Projects.Zones.Clusters.SetNetworkPolicy(
project, location, clusterName, req).Do()
case v1beta1:
reqV1Beta := &containerBeta.SetNetworkPolicyRequest{}
err = Convert(req, reqV1Beta)
if err != nil {
return err
}
name := containerClusterFullName(project, location, clusterName)
op, err = config.clientContainerBeta.Projects.Locations.Clusters.SetNetworkPolicy(name, reqV1Beta).Do()
}
if err != nil { if err != nil {
return err return err
} }
@ -1206,24 +1089,12 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
if d.HasChange("logging_service") { if d.HasChange("logging_service") {
logging := d.Get("logging_service").(string) logging := d.Get("logging_service").(string)
req := &container.SetLoggingServiceRequest{ req := &containerBeta.SetLoggingServiceRequest{
LoggingService: logging, LoggingService: logging,
} }
updateF := func() error { updateF := func() error {
var op interface{} name := containerClusterFullName(project, location, clusterName)
switch containerAPIVersion { op, err := config.clientContainerBeta.Projects.Locations.Clusters.SetLogging(name, req).Do()
case v1:
op, err = config.clientContainer.Projects.Zones.Clusters.Logging(
project, location, clusterName, req).Do()
case v1beta1:
reqV1Beta := &containerBeta.SetLoggingServiceRequest{}
err = Convert(req, reqV1Beta)
if err != nil {
return err
}
name := containerClusterFullName(project, location, clusterName)
op, err = config.clientContainerBeta.Projects.Locations.Clusters.SetLogging(name, reqV1Beta).Do()
}
if err != nil { if err != nil {
return err return err
} }
@ -1267,15 +1138,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
} }
if d.HasChange("remove_default_node_pool") && d.Get("remove_default_node_pool").(bool) { if d.HasChange("remove_default_node_pool") && d.Get("remove_default_node_pool").(bool) {
var op interface{} name := fmt.Sprintf("%s/nodePools/%s", containerClusterFullName(project, location, clusterName), "default-pool")
switch containerAPIVersion { op, err := config.clientContainerBeta.Projects.Locations.Clusters.NodePools.Delete(name).Do()
case v1:
op, err = config.clientContainer.Projects.Zones.Clusters.NodePools.Delete(
project, location, clusterName, "default-pool").Do()
case v1beta1:
name := fmt.Sprintf("%s/nodePools/%s", containerClusterFullName(project, location, clusterName), "default-pool")
op, err = config.clientContainerBeta.Projects.Locations.Clusters.NodePools.Delete(name).Do()
}
if err != nil { if err != nil {
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err) return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
} }

View File

@ -431,9 +431,6 @@ func TestAccContainerCluster_withPrivateCluster(t *testing.T) {
ImportStateIdPrefix: "us-central1-a/", ImportStateIdPrefix: "us-central1-a/",
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"private_cluster",
"master_ipv4_cidr_block"},
}, },
}, },
}) })
@ -642,17 +639,11 @@ func TestAccContainerCluster_withWorkloadMetadataConfig(t *testing.T) {
), ),
}, },
{ {
ResourceName: "google_container_cluster.with_workload_metadata_config", ResourceName: "google_container_cluster.with_workload_metadata_config",
ImportStateIdPrefix: "us-central1-a/", ImportStateIdPrefix: "us-central1-a/",
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,
// Import always uses the v1 API, so beta features don't get imported. ImportStateVerifyIgnore: []string{"min_master_version"},
ImportStateVerifyIgnore: []string{
"node_config.0.workload_metadata_config.#",
"node_config.0.workload_metadata_config.0.node_metadata",
"node_pool.0.node_config.0.workload_metadata_config.#",
"node_pool.0.node_config.0.workload_metadata_config.0.node_metadata",
"min_master_version"},
}, },
}, },
}) })
@ -1125,8 +1116,6 @@ func TestAccContainerCluster_withPodSecurityPolicy(t *testing.T) {
ImportStateIdPrefix: "us-central1-a/", ImportStateIdPrefix: "us-central1-a/",
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,
// Import always uses the v1 API, so beta features don't get imported.
ImportStateVerifyIgnore: []string{"pod_security_policy_config.#", "pod_security_policy_config.0.enabled"},
}, },
{ {
Config: testAccContainerCluster_withPodSecurityPolicy(clusterName, false), Config: testAccContainerCluster_withPodSecurityPolicy(clusterName, false),
@ -1140,8 +1129,6 @@ func TestAccContainerCluster_withPodSecurityPolicy(t *testing.T) {
ImportStateIdPrefix: "us-central1-a/", ImportStateIdPrefix: "us-central1-a/",
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,
// Import always uses the v1 API, so beta features don't get imported.
ImportStateVerifyIgnore: []string{"pod_security_policy_config.#", "pod_security_policy_config.0.enabled"},
}, },
}, },
}) })

41
google/test_utils.go Normal file
View File

@ -0,0 +1,41 @@
package google
type ResourceDataMock struct {
FieldsInSchema map[string]interface{}
FieldsWithHasChange []string
id string
}
func (d *ResourceDataMock) HasChange(key string) bool {
exists := false
for _, val := range d.FieldsWithHasChange {
if key == val {
exists = true
}
}
return exists
}
func (d *ResourceDataMock) GetOk(key string) (interface{}, bool) {
for k, v := range d.FieldsInSchema {
if key == k {
return v, true
}
}
return nil, false
}
func (d *ResourceDataMock) Set(key string, value interface{}) error {
d.FieldsInSchema[key] = value
return nil
}
func (d *ResourceDataMock) SetId(v string) {
d.id = v
}
func (d *ResourceDataMock) Id() string {
return d.id
}

View File

@ -17,6 +17,14 @@ import (
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
type TerraformResourceData interface {
HasChange(string) bool
GetOk(string) (interface{}, bool)
Set(string, interface{}) error
SetId(string)
Id() string
}
// getRegionFromZone returns the region from a zone for Google cloud. // getRegionFromZone returns the region from a zone for Google cloud.
func getRegionFromZone(zone string) string { func getRegionFromZone(zone string) string {
if zone != "" && len(zone) > 2 { if zone != "" && len(zone) > 2 {

View File

@ -112,5 +112,6 @@ 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 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. is publicly announced, and is when they generally become publicly available.
Resources will automatically be provisioned using Beta APIs when you specify a feature Terraform resources that support beta features will always use the Beta APIs to provision
marked Beta in your Terraform config file. 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.