mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-07-03 08:42:39 +00:00
Remove switch statements between api versions for container; clean up dead version code (#1427)
* remove switch statements between api versions for container * remove dead api version code and move things around
This commit is contained in:
parent
9c5f5f63a3
commit
d59fcbbc59
|
@ -1,242 +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 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 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 maxContainerVersion(versionsInUse map[ApiVersion]struct{}) ApiVersion {
|
||||
return maxVersion(versionsInUse, OrderedContainerApiVersions)
|
||||
}
|
|
@ -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
77
google/convert.go
Normal 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
79
google/convert_test.go
Normal 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)
|
||||
}
|
||||
}
|
|
@ -8,9 +8,6 @@ import (
|
|||
"google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
var ProjectMetadataBaseApiVersion = v1
|
||||
var ProjectMetadataVersionedFeatures = []Feature{}
|
||||
|
||||
func resourceComputeProjectMetadata() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceComputeProjectMetadataCreate,
|
||||
|
|
|
@ -12,23 +12,12 @@ import (
|
|||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/helper/validation"
|
||||
"google.golang.org/api/container/v1"
|
||||
containerBeta "google.golang.org/api/container/v1beta1"
|
||||
)
|
||||
|
||||
var (
|
||||
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{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"cidr_blocks": {
|
||||
|
@ -475,7 +464,6 @@ func resourceContainerCluster() *schema.Resource {
|
|||
}
|
||||
|
||||
func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
|
||||
config := meta.(*Config)
|
||||
|
||||
project, err := getProject(d, config)
|
||||
|
@ -651,25 +639,8 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
|
|||
mutexKV.Lock(containerClusterMutexKey(project, location, clusterName))
|
||||
defer mutexKV.Unlock(containerClusterMutexKey(project, location, clusterName))
|
||||
|
||||
var op interface{}
|
||||
switch containerAPIVersion {
|
||||
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()
|
||||
}
|
||||
parent := fmt.Sprintf("projects/%s/locations/%s", project, location)
|
||||
op, err := config.clientContainerBeta.Projects.Locations.Clusters.Create(parent, req).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -687,15 +658,8 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
|
|||
log.Printf("[INFO] GKE cluster %s has been created", clusterName)
|
||||
|
||||
if d.Get("remove_default_node_pool").(bool) {
|
||||
var op interface{}
|
||||
switch containerAPIVersion {
|
||||
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()
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
|
||||
config := meta.(*Config)
|
||||
|
||||
project, err := getProject(d, config)
|
||||
|
@ -723,20 +686,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro
|
|||
}
|
||||
|
||||
cluster := &containerBeta.Cluster{}
|
||||
var clust interface{}
|
||||
err = resource.Retry(2*time.Minute, func() *resource.RetryError {
|
||||
switch containerAPIVersion {
|
||||
case v1:
|
||||
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)
|
||||
name := containerClusterFullName(project, location, d.Get("name").(string))
|
||||
cluster, err = config.clientContainerBeta.Projects.Locations.Clusters.Get(name).Do()
|
||||
if err != nil {
|
||||
return resource.NonRetryableError(err)
|
||||
}
|
||||
|
@ -834,7 +786,6 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro
|
|||
}
|
||||
|
||||
func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
|
||||
config := meta.(*Config)
|
||||
|
||||
project, err := getProject(d, config)
|
||||
|
@ -854,22 +805,10 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
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 {
|
||||
var err error
|
||||
var op interface{}
|
||||
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()
|
||||
}
|
||||
name := containerClusterFullName(project, location, clusterName)
|
||||
op, err := config.clientContainerBeta.Projects.Locations.Clusters.Update(name, req).Do()
|
||||
if err != nil {
|
||||
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 d.HasChange("master_authorized_networks_config") {
|
||||
c := d.Get("master_authorized_networks_config")
|
||||
conf := &container.MasterAuthorizedNetworksConfig{}
|
||||
err := Convert(expandMasterAuthorizedNetworksConfig(c), conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := &container.UpdateClusterRequest{
|
||||
Update: &container.ClusterUpdate{
|
||||
DesiredMasterAuthorizedNetworksConfig: conf,
|
||||
req := &containerBeta.UpdateClusterRequest{
|
||||
Update: &containerBeta.ClusterUpdate{
|
||||
DesiredMasterAuthorizedNetworksConfig: expandMasterAuthorizedNetworksConfig(c),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
if cur.LessThan(des) {
|
||||
req := &container.UpdateClusterRequest{
|
||||
Update: &container.ClusterUpdate{
|
||||
req := &containerBeta.UpdateClusterRequest{
|
||||
Update: &containerBeta.ClusterUpdate{
|
||||
DesiredMasterVersion: desiredMasterVersion,
|
||||
},
|
||||
}
|
||||
|
@ -936,8 +870,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
if d.HasChange("node_version") {
|
||||
desiredNodeVersion := d.Get("node_version").(string)
|
||||
req := &container.UpdateClusterRequest{
|
||||
Update: &container.ClusterUpdate{
|
||||
req := &containerBeta.UpdateClusterRequest{
|
||||
Update: &containerBeta.ClusterUpdate{
|
||||
DesiredNodeVersion: desiredNodeVersion,
|
||||
},
|
||||
}
|
||||
|
@ -955,14 +889,9 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
if d.HasChange("addons_config") {
|
||||
if ac, ok := d.GetOk("addons_config"); ok {
|
||||
conf := &container.AddonsConfig{}
|
||||
err := Convert(expandClusterAddonsConfig(ac), conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := &container.UpdateClusterRequest{
|
||||
Update: &container.ClusterUpdate{
|
||||
DesiredAddonsConfig: conf,
|
||||
req := &containerBeta.UpdateClusterRequest{
|
||||
Update: &containerBeta.ClusterUpdate{
|
||||
DesiredAddonsConfig: expandClusterAddonsConfig(ac),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -979,37 +908,20 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
}
|
||||
|
||||
if d.HasChange("maintenance_policy") {
|
||||
var req *container.SetMaintenancePolicyRequest
|
||||
var req *containerBeta.SetMaintenancePolicyRequest
|
||||
if mp, ok := d.GetOk("maintenance_policy"); ok {
|
||||
pol := &container.MaintenancePolicy{}
|
||||
err := Convert(expandMaintenancePolicy(mp), pol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req = &container.SetMaintenancePolicyRequest{
|
||||
MaintenancePolicy: pol,
|
||||
req = &containerBeta.SetMaintenancePolicyRequest{
|
||||
MaintenancePolicy: expandMaintenancePolicy(mp),
|
||||
}
|
||||
} else {
|
||||
req = &container.SetMaintenancePolicyRequest{
|
||||
req = &containerBeta.SetMaintenancePolicyRequest{
|
||||
NullFields: []string{"MaintenancePolicy"},
|
||||
}
|
||||
}
|
||||
|
||||
updateF := func() error {
|
||||
var op interface{}
|
||||
switch containerAPIVersion {
|
||||
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()
|
||||
}
|
||||
name := containerClusterFullName(project, location, clusterName)
|
||||
op, err := config.clientContainerBeta.Projects.Locations.Clusters.SetMaintenancePolicy(name, req).Do()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1044,8 +956,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
azSet.Add(location)
|
||||
}
|
||||
|
||||
req := &container.UpdateClusterRequest{
|
||||
Update: &container.ClusterUpdate{
|
||||
req := &containerBeta.UpdateClusterRequest{
|
||||
Update: &containerBeta.ClusterUpdate{
|
||||
DesiredLocations: convertStringSet(azSet),
|
||||
},
|
||||
}
|
||||
|
@ -1060,8 +972,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
azSetNew.Add(location)
|
||||
}
|
||||
if !azSet.Equal(azSetNew) {
|
||||
req = &container.UpdateClusterRequest{
|
||||
Update: &container.ClusterUpdate{
|
||||
req = &containerBeta.UpdateClusterRequest{
|
||||
Update: &containerBeta.ClusterUpdate{
|
||||
DesiredLocations: convertStringSet(azSetNew),
|
||||
},
|
||||
}
|
||||
|
@ -1080,26 +992,15 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
if d.HasChange("enable_legacy_abac") {
|
||||
enabled := d.Get("enable_legacy_abac").(bool)
|
||||
req := &container.SetLegacyAbacRequest{
|
||||
req := &containerBeta.SetLegacyAbacRequest{
|
||||
Enabled: enabled,
|
||||
ForceSendFields: []string{"Enabled"},
|
||||
}
|
||||
|
||||
updateF := func() error {
|
||||
log.Println("[DEBUG] updating enable_legacy_abac")
|
||||
var op interface{}
|
||||
switch containerAPIVersion {
|
||||
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()
|
||||
}
|
||||
name := containerClusterFullName(project, location, clusterName)
|
||||
op, err := config.clientContainerBeta.Projects.Locations.Clusters.SetLegacyAbac(name, req).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1123,8 +1024,8 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
if d.HasChange("monitoring_service") {
|
||||
desiredMonitoringService := d.Get("monitoring_service").(string)
|
||||
|
||||
req := &container.UpdateClusterRequest{
|
||||
Update: &container.ClusterUpdate{
|
||||
req := &containerBeta.UpdateClusterRequest{
|
||||
Update: &containerBeta.ClusterUpdate{
|
||||
DesiredMonitoringService: desiredMonitoringService,
|
||||
},
|
||||
}
|
||||
|
@ -1142,32 +1043,14 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
if d.HasChange("network_policy") {
|
||||
np := d.Get("network_policy")
|
||||
|
||||
pol := &container.NetworkPolicy{}
|
||||
err := Convert(expandNetworkPolicy(np), pol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := &container.SetNetworkPolicyRequest{
|
||||
NetworkPolicy: pol,
|
||||
req := &containerBeta.SetNetworkPolicyRequest{
|
||||
NetworkPolicy: expandNetworkPolicy(np),
|
||||
}
|
||||
|
||||
updateF := func() error {
|
||||
log.Println("[DEBUG] updating network_policy")
|
||||
var op interface{}
|
||||
switch containerAPIVersion {
|
||||
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()
|
||||
}
|
||||
name := containerClusterFullName(project, location, clusterName)
|
||||
op, err := config.clientContainerBeta.Projects.Locations.Clusters.SetNetworkPolicy(name, req).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1206,24 +1089,12 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
if d.HasChange("logging_service") {
|
||||
logging := d.Get("logging_service").(string)
|
||||
|
||||
req := &container.SetLoggingServiceRequest{
|
||||
req := &containerBeta.SetLoggingServiceRequest{
|
||||
LoggingService: logging,
|
||||
}
|
||||
updateF := func() error {
|
||||
var op interface{}
|
||||
switch containerAPIVersion {
|
||||
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()
|
||||
}
|
||||
name := containerClusterFullName(project, location, clusterName)
|
||||
op, err := config.clientContainerBeta.Projects.Locations.Clusters.SetLogging(name, req).Do()
|
||||
if err != nil {
|
||||
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) {
|
||||
var op interface{}
|
||||
switch containerAPIVersion {
|
||||
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()
|
||||
}
|
||||
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 {
|
||||
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
|
||||
}
|
||||
|
|
|
@ -431,9 +431,6 @@ func TestAccContainerCluster_withPrivateCluster(t *testing.T) {
|
|||
ImportStateIdPrefix: "us-central1-a/",
|
||||
ImportState: 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",
|
||||
ImportStateIdPrefix: "us-central1-a/",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
// Import always uses the v1 API, so beta features don't get imported.
|
||||
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"},
|
||||
ResourceName: "google_container_cluster.with_workload_metadata_config",
|
||||
ImportStateIdPrefix: "us-central1-a/",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"min_master_version"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -1125,8 +1116,6 @@ func TestAccContainerCluster_withPodSecurityPolicy(t *testing.T) {
|
|||
ImportStateIdPrefix: "us-central1-a/",
|
||||
ImportState: 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),
|
||||
|
@ -1140,8 +1129,6 @@ func TestAccContainerCluster_withPodSecurityPolicy(t *testing.T) {
|
|||
ImportStateIdPrefix: "us-central1-a/",
|
||||
ImportState: 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
41
google/test_utils.go
Normal 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
|
||||
}
|
|
@ -17,6 +17,14 @@ import (
|
|||
"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.
|
||||
func getRegionFromZone(zone string) string {
|
||||
if zone != "" && len(zone) > 2 {
|
||||
|
|
Loading…
Reference in New Issue
Block a user