Skip to content

Commit 7f56ec3

Browse files
committed
add changes based on upstream import support
1 parent a966135 commit 7f56ec3

File tree

7 files changed

+49
-294
lines changed

7 files changed

+49
-294
lines changed

internal/generic/diagnostic.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ func DesiredStateErrorDiag(source string, err error) diag.Diagnostic {
2020
)
2121
}
2222

23+
func ResourceAttributeNotSetDiag(err error) diag.Diagnostic {
24+
return diag.NewErrorDiagnostic(
25+
"Terraform Resource Attribute Not Set",
26+
fmt.Sprintf("Terraform resource attribute not set in State. This is typically an error with the Terraform provider implementation. Original Error: %s", err.Error()),
27+
)
28+
}
29+
2330
func ResourceIdentifierNotFoundDiag(err error) diag.Diagnostic {
2431
return diag.NewErrorDiagnostic(
2532
"Terraform Resource Identifier Not Found",

internal/generic/resource.go

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7-
"reflect"
87
"strings"
98
"time"
109

@@ -724,24 +723,19 @@ func (r *resource) ImportState(ctx context.Context, request tfsdk.ImportResource
724723

725724
tflog.Debug(ctx, "Request.ID", "value", hclog.Fmt("%v", request.ID))
726725

727-
translator := toStruct{Schema: r.resourceType.tfSchema}
728-
typ, err := translator.FromSchemaAttributes(ctx)
729-
if err != nil {
730-
response.Diagnostics.AddError(
731-
"Import Of Terraform State Unsuccessful",
732-
fmt.Sprintf("Unable to create a Terraform State value from a tfsdk.Schema for import. This is typically an error with the Terraform provider implementation. Original Error: %s", err.Error()),
733-
)
734-
735-
return
736-
}
726+
state := &response.State
737727

738-
state := reflect.New(typ)
728+
for name, attr := range state.Schema.Attributes {
729+
err := r.setEmptyAttribute(ctx, attr, name, state)
739730

740-
state.Elem().FieldByName("Id").SetString(request.ID)
731+
if err != nil {
732+
response.Diagnostics = append(response.Diagnostics, ResourceAttributeNotSetDiag(err))
741733

742-
diags := response.State.Set(ctx, state.Elem().Interface())
734+
return
735+
}
736+
}
743737

744-
response.Diagnostics.Append(diags...)
738+
tfsdk.ResourceImportStatePassthroughID(ctx, idAttributePath, request, response)
745739

746740
tflog.Trace(ctx, "Resource.ImportState exit", "cfTypeName", r.resourceType.cfTypeName, "tfTypeName", r.resourceType.tfTypeName)
747741
}
@@ -788,6 +782,39 @@ func (r *resource) setId(ctx context.Context, val string, state *tfsdk.State) er
788782
return nil
789783
}
790784

785+
func (r *resource) setEmptyAttribute(ctx context.Context, attr tfsdk.Attribute, name string, state *tfsdk.State) error {
786+
path := tftypes.NewAttributePath().WithAttributeName(name)
787+
788+
var diags diag.Diagnostics
789+
790+
if t := attr.Type; t != nil {
791+
if t.TerraformType(ctx).Is(tftypes.String) {
792+
diags = state.SetAttribute(ctx, path, "")
793+
} else if t.TerraformType(ctx).Is(tftypes.Number) {
794+
diags = state.SetAttribute(ctx, path, float64(0))
795+
} else if t.TerraformType(ctx).Is(tftypes.Bool) {
796+
diags = state.SetAttribute(ctx, path, false)
797+
} else if t.TerraformType(ctx).Is(tftypes.Set{}) || t.TerraformType(ctx).Is(tftypes.List{}) || t.TerraformType(ctx).Is(tftypes.Tuple{}) {
798+
diags = state.SetAttribute(ctx, path, []interface{}{})
799+
} else if t.TerraformType(ctx).Is(tftypes.Map{}) || t.TerraformType(ctx).Is(tftypes.Object{}) {
800+
diags = state.SetAttribute(ctx, path, map[string]interface{}{})
801+
}
802+
} else if attr.Attributes != nil { // attribute is a not a "tftype" e.g. providertypes.SetNestedAttributes
803+
diags = state.SetAttribute(ctx, path, []interface{}{})
804+
} else {
805+
diags.Append(diag.NewErrorDiagnostic(
806+
"Unknown Terraform Type for Attribute",
807+
fmt.Sprintf("unknown terraform type for attribute (%s): %T", name, t),
808+
))
809+
}
810+
811+
if diags.HasError() {
812+
return tfresource.DiagsError(diags)
813+
}
814+
815+
return nil
816+
}
817+
791818
// patchDocument returns a JSON Patch document describing the difference between `old` and `new`.
792819
func patchDocument(old, new string) (string, error) {
793820
patch, err := jsonpatch.CreatePatch([]byte(old), []byte(new))

internal/generic/state_test.go

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,6 @@ var testSimpleSchemaWithList = tfsdk.Schema{
5959
},
6060
}
6161

62-
var testSimpleSchemaWithUnsupportedType = tfsdk.Schema{
63-
Attributes: map[string]tfsdk.Attribute{
64-
"arn": {
65-
Computed: true,
66-
},
67-
"identifier": {
68-
Type: types.StringType,
69-
Computed: true,
70-
},
71-
"name": {
72-
Type: types.StringType,
73-
Required: true,
74-
},
75-
"number": {
76-
Type: types.NumberType,
77-
Optional: true,
78-
},
79-
},
80-
}
81-
8262
var simpleCfToTfNameMap = map[string]string{
8363
"Arn": "arn",
8464
"Identifier": "identifier",

internal/generic/translate.go

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ import (
55
"encoding/json"
66
"fmt"
77
"math/big"
8-
"reflect"
98

109
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
1110
"github.com/hashicorp/terraform-plugin-go/tftypes"
1211
tflog "github.com/hashicorp/terraform-plugin-log"
13-
"github.com/hashicorp/terraform-provider-awscc/internal/naming"
1412
)
1513

1614
// Translates a Terraform Value to Cloud Control DesiredState.
@@ -271,44 +269,3 @@ func (t toTerraform) valueFromRaw(ctx context.Context, schema *tfsdk.Schema, pat
271269
return tftypes.Value{}, fmt.Errorf("unsupported raw type: %T", v)
272270
}
273271
}
274-
275-
type toStruct struct {
276-
Schema tfsdk.Schema
277-
}
278-
279-
// FromSchemaAttributes returns a reflect.Type representing the
280-
// specified (resource) schema's attributes.
281-
func (s toStruct) FromSchemaAttributes(ctx context.Context) (reflect.Type, error) {
282-
var fields []reflect.StructField
283-
284-
for k, v := range s.Schema.Attributes {
285-
name := naming.TerraformAttributeToPascalCase(k)
286-
287-
field := reflect.StructField{
288-
Name: name,
289-
Tag: reflect.StructTag(fmt.Sprintf(`tfsdk:"%s"`, k)),
290-
}
291-
292-
if v.Type != nil {
293-
if v.Type.TerraformType(ctx).Is(tftypes.String) {
294-
field.Type = reflect.TypeOf("")
295-
} else if v.Type.TerraformType(ctx).Is(tftypes.Number) {
296-
field.Type = reflect.TypeOf(float64(0))
297-
} else if v.Type.TerraformType(ctx).Is(tftypes.Bool) {
298-
field.Type = reflect.TypeOf(false)
299-
} else if v.Type.TerraformType(ctx).Is(tftypes.Set{}) || v.Type.TerraformType(ctx).Is(tftypes.List{}) || v.Type.TerraformType(ctx).Is(tftypes.Tuple{}) {
300-
field.Type = reflect.TypeOf([]interface{}{})
301-
} else if v.Type.TerraformType(ctx).Is(tftypes.Map{}) || v.Type.TerraformType(ctx).Is(tftypes.Object{}) {
302-
field.Type = reflect.TypeOf(map[string]interface{}{})
303-
}
304-
} else if v.Attributes != nil { // attribute is a not a "tftype" e.g. providertypes.SetNestedAttributes
305-
field.Type = reflect.TypeOf([]interface{}{})
306-
} else {
307-
return nil, fmt.Errorf("unknown type for attribute: %T", v)
308-
}
309-
310-
fields = append(fields, field)
311-
}
312-
313-
return reflect.StructOf(fields), nil
314-
}

internal/generic/translate_test.go

Lines changed: 0 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package generic
22

33
import (
44
"context"
5-
"reflect"
65
"testing"
76

87
"github.com/google/go-cmp/cmp"
@@ -393,169 +392,3 @@ func TestTranslateToTerraform(t *testing.T) {
393392
})
394393
}
395394
}
396-
397-
func TestTranslateToStruct(t *testing.T) {
398-
testCases := []struct {
399-
TestName string
400-
Schema tfsdk.Schema
401-
ExpectedType reflect.Type
402-
ExpectedError bool
403-
}{
404-
{
405-
TestName: "simple struct with unsupported attribute",
406-
Schema: testSimpleSchemaWithUnsupportedType,
407-
ExpectedError: true,
408-
},
409-
{
410-
TestName: "simple Struct",
411-
Schema: testSimpleSchema,
412-
ExpectedType: reflect.StructOf([]reflect.StructField{
413-
{
414-
Name: "Arn",
415-
Tag: `tfsdk:"arn"`,
416-
Type: reflect.TypeOf(""),
417-
},
418-
{
419-
Name: "Identifier",
420-
Tag: `tfsdk:"identifier"`,
421-
Type: reflect.TypeOf(""),
422-
},
423-
{
424-
Name: "Name",
425-
Tag: `tfsdk:"name"`,
426-
Type: reflect.TypeOf(""),
427-
},
428-
{
429-
Name: "Number",
430-
Tag: `tfsdk:"number"`,
431-
Type: reflect.TypeOf(0.0),
432-
},
433-
}),
434-
},
435-
{
436-
TestName: "simple struct with list",
437-
Schema: testSimpleSchemaWithList,
438-
ExpectedType: reflect.StructOf([]reflect.StructField{
439-
{
440-
Name: "Arn",
441-
Tag: `tfsdk:"arn"`,
442-
Type: reflect.TypeOf(""),
443-
},
444-
{
445-
Name: "Identifier",
446-
Tag: `tfsdk:"identifier"`,
447-
Type: reflect.TypeOf(""),
448-
},
449-
{
450-
Name: "Name",
451-
Tag: `tfsdk:"name"`,
452-
Type: reflect.TypeOf(""),
453-
},
454-
{
455-
Name: "Number",
456-
Tag: `tfsdk:"number"`,
457-
Type: reflect.TypeOf(0.0),
458-
},
459-
{
460-
Name: "Ports",
461-
Tag: `tfsdk:"ports"`,
462-
Type: reflect.TypeOf([]interface{}{}),
463-
},
464-
}),
465-
},
466-
{
467-
TestName: "complex struct",
468-
Schema: testComplexSchema,
469-
ExpectedType: reflect.StructOf([]reflect.StructField{
470-
{
471-
Name: "Name",
472-
Tag: `tfsdk:"name"`,
473-
Type: reflect.TypeOf(""),
474-
},
475-
{
476-
Name: "MachineType",
477-
Tag: `tfsdk:"machine_type"`,
478-
Type: reflect.TypeOf(""),
479-
},
480-
{
481-
Name: "Ports",
482-
Tag: `tfsdk:"ports"`,
483-
Type: reflect.TypeOf([]interface{}{}),
484-
},
485-
{
486-
Name: "Tags",
487-
Tag: `tfsdk:"tags"`,
488-
Type: reflect.TypeOf([]interface{}{}),
489-
},
490-
{
491-
Name: "Disks",
492-
Tag: `tfsdk:"disks"`,
493-
Type: reflect.TypeOf([]interface{}{}),
494-
},
495-
{
496-
Name: "BootDisk",
497-
Tag: `tfsdk:"boot_disk"`,
498-
Type: reflect.TypeOf([]interface{}{}),
499-
},
500-
{
501-
Name: "ScratchDisk",
502-
Tag: `tfsdk:"scratch_disk"`,
503-
Type: reflect.TypeOf(map[string]interface{}{}),
504-
},
505-
{
506-
Name: "VideoPorts",
507-
Tag: `tfsdk:"video_ports"`,
508-
Type: reflect.TypeOf([]interface{}{}),
509-
},
510-
{
511-
Name: "Identifier",
512-
Tag: `tfsdk:"identifier"`,
513-
Type: reflect.TypeOf(""),
514-
},
515-
}),
516-
},
517-
}
518-
519-
for _, testCase := range testCases {
520-
t.Run(testCase.TestName, func(t *testing.T) {
521-
translator := toStruct{Schema: testCase.Schema}
522-
got, err := translator.FromSchemaAttributes(context.TODO())
523-
524-
if err == nil && testCase.ExpectedError {
525-
t.Fatalf("expected error")
526-
}
527-
528-
if err != nil && !testCase.ExpectedError {
529-
t.Fatalf("unexpected error: %s", err)
530-
}
531-
532-
if err == nil {
533-
if !typesEqual(testCase.ExpectedType, got) {
534-
t.Errorf("expected: %v, got: %v", testCase.ExpectedType, got)
535-
}
536-
}
537-
})
538-
}
539-
}
540-
541-
func typesEqual(expected, got reflect.Type) bool {
542-
if got.NumField() != expected.NumField() {
543-
return false
544-
}
545-
546-
for i := 0; i < expected.NumField(); i++ {
547-
expectedField := expected.Field(i)
548-
549-
gotField, ok := got.FieldByName(expectedField.Name)
550-
551-
if !ok {
552-
return false
553-
}
554-
555-
if gotField.Name != expectedField.Name || gotField.Tag != expectedField.Tag || gotField.Type != expectedField.Type {
556-
return false
557-
}
558-
}
559-
560-
return true
561-
}

internal/naming/naming.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,6 @@ func Pluralize(name string) string {
106106
return pluralName
107107
}
108108

109-
// TerraformAttributeToPascalCase converts a Terraform Attribute name to PascalCase where applicable.
110-
// For example `global_replication_group_description` -> `GlobalReplicationGroupDescription`.
111-
func TerraformAttributeToPascalCase(name string) string {
112-
if !strings.Contains(name, "_") {
113-
return strings.Title(name)
114-
}
115-
n := strings.Replace(name, "_", " ", -1)
116-
return strings.Replace(strings.Title(n), " ", "", -1)
117-
}
118-
119109
// PluralizeWithCustomNameSuffix converts a name to its plural form similar to Pluralize,
120110
// with the exception that a suffix can be passed in as an argument to be used
121111
// only for names that are considered "custom" i.e. return true for isCustomName.

0 commit comments

Comments
 (0)