Merge pull request #20957 from hashicorp/jbardin/shim-schemas
New methods for obtaining CoreConfigSchema for shimming
This commit is contained in:
commit
7df3275120
|
@ -0,0 +1,141 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDiffApply_set(t *testing.T) {
|
||||||
|
priorAttrs := map[string]string{
|
||||||
|
"id": "testID",
|
||||||
|
"egress.#": "1",
|
||||||
|
"egress.2129912301.cidr_blocks.#": "1",
|
||||||
|
"egress.2129912301.cidr_blocks.0": "10.0.0.0/8",
|
||||||
|
"egress.2129912301.description": "Egress description",
|
||||||
|
"egress.2129912301.from_port": "80",
|
||||||
|
"egress.2129912301.ipv6_cidr_blocks.#": "0",
|
||||||
|
"egress.2129912301.prefix_list_ids.#": "0",
|
||||||
|
"egress.2129912301.protocol": "tcp",
|
||||||
|
"egress.2129912301.security_groups.#": "0",
|
||||||
|
"egress.2129912301.self": "false",
|
||||||
|
"egress.2129912301.to_port": "8000",
|
||||||
|
}
|
||||||
|
|
||||||
|
diff := &terraform.InstanceDiff{
|
||||||
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
||||||
|
"egress.2129912301.cidr_blocks.#": {Old: "1", New: "0", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.2129912301.cidr_blocks.0": {Old: "10.0.0.0/8", New: "", NewComputed: false, NewRemoved: true},
|
||||||
|
"egress.2129912301.description": {Old: "Egress description", New: "", NewComputed: false, NewRemoved: true},
|
||||||
|
"egress.2129912301.from_port": {Old: "80", New: "0", NewComputed: false, NewRemoved: true},
|
||||||
|
"egress.2129912301.ipv6_cidr_blocks.#": {Old: "0", New: "0", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.2129912301.prefix_list_ids.#": {Old: "0", New: "0", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.2129912301.protocol": {Old: "tcp", New: "", NewComputed: false, NewRemoved: true},
|
||||||
|
"egress.2129912301.security_groups.#": {Old: "0", New: "0", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.2129912301.self": {Old: "false", New: "false", NewComputed: false, NewRemoved: true},
|
||||||
|
"egress.2129912301.to_port": {Old: "8000", New: "0", NewComputed: false, NewRemoved: true},
|
||||||
|
"egress.746197026.cidr_blocks.#": {Old: "", New: "1", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.746197026.cidr_blocks.0": {Old: "", New: "10.0.0.0/8", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.746197026.description": {Old: "", New: "New egress description", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.746197026.from_port": {Old: "", New: "80", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.746197026.ipv6_cidr_blocks.#": {Old: "", New: "0", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.746197026.prefix_list_ids.#": {Old: "", New: "0", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.746197026.protocol": {Old: "", New: "tcp", NewComputed: false, NewRemoved: false, NewExtra: "tcp"},
|
||||||
|
"egress.746197026.security_groups.#": {Old: "", New: "0", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.746197026.self": {Old: "", New: "false", NewComputed: false, NewRemoved: false},
|
||||||
|
"egress.746197026.to_port": {Old: "", New: "8000", NewComputed: false, NewRemoved: false},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resSchema := map[string]*schema.Schema{
|
||||||
|
"egress": {
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ConfigMode: schema.SchemaConfigModeAttr,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"from_port": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"to_port": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"protocol": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"cidr_blocks": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"ipv6_cidr_blocks": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefix_list_ids": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
|
"security_groups": {
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
|
||||||
|
"self": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"description": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := map[string]string{
|
||||||
|
"egress.#": "1",
|
||||||
|
"egress.746197026.cidr_blocks.#": "1",
|
||||||
|
"egress.746197026.cidr_blocks.0": "10.0.0.0/8",
|
||||||
|
"egress.746197026.description": "New egress description",
|
||||||
|
"egress.746197026.from_port": "80", "egress.746197026.ipv6_cidr_blocks.#": "0",
|
||||||
|
"egress.746197026.prefix_list_ids.#": "0",
|
||||||
|
"egress.746197026.protocol": "tcp",
|
||||||
|
"egress.746197026.security_groups.#": "0",
|
||||||
|
"egress.746197026.self": "false",
|
||||||
|
"egress.746197026.to_port": "8000",
|
||||||
|
"id": "testID",
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs, err := diff.Apply(priorAttrs, schema.LegacyResourceSchema(&schema.Resource{Schema: resSchema}).CoreConfigSchema())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(attrs, expected) {
|
||||||
|
t.Fatalf("\nexpected: %#v\ngot: %#v\n", expected, attrs)
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@ func Provider() terraform.ResourceProvider {
|
||||||
},
|
},
|
||||||
ResourcesMap: map[string]*schema.Resource{
|
ResourcesMap: map[string]*schema.Resource{
|
||||||
"test_resource": testResource(),
|
"test_resource": testResource(),
|
||||||
"test_resource_as_single": testResourceAsSingle(),
|
|
||||||
"test_resource_gh12183": testResourceGH12183(),
|
"test_resource_gh12183": testResourceGH12183(),
|
||||||
"test_resource_import_other": testResourceImportOther(),
|
"test_resource_import_other": testResourceImportOther(),
|
||||||
"test_resource_with_custom_diff": testResourceCustomDiff(),
|
"test_resource_with_custom_diff": testResourceCustomDiff(),
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testResourceAsSingle() *schema.Resource {
|
|
||||||
return &schema.Resource{
|
|
||||||
Create: testResourceAsSingleCreate,
|
|
||||||
Read: testResourceAsSingleRead,
|
|
||||||
Delete: testResourceAsSingleDelete,
|
|
||||||
Update: testResourceAsSingleUpdate,
|
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
|
||||||
"list_resource_as_block": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &schema.Resource{
|
|
||||||
Schema: map[string]*schema.Schema{
|
|
||||||
"foo": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"list_resource_as_attr": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
ConfigMode: schema.SchemaConfigModeAttr,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &schema.Resource{
|
|
||||||
Schema: map[string]*schema.Schema{
|
|
||||||
"foo": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"list_primitive": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"set_resource_as_block": {
|
|
||||||
Type: schema.TypeSet,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &schema.Resource{
|
|
||||||
Schema: map[string]*schema.Schema{
|
|
||||||
"foo": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"set_resource_as_attr": {
|
|
||||||
Type: schema.TypeSet,
|
|
||||||
ConfigMode: schema.SchemaConfigModeAttr,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &schema.Resource{
|
|
||||||
Schema: map[string]*schema.Schema{
|
|
||||||
"foo": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"set_primitive": {
|
|
||||||
Type: schema.TypeSet,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testResourceAsSingleCreate(d *schema.ResourceData, meta interface{}) error {
|
|
||||||
d.SetId("placeholder")
|
|
||||||
return testResourceAsSingleRead(d, meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testResourceAsSingleRead(d *schema.ResourceData, meta interface{}) error {
|
|
||||||
for _, k := range []string{"list_resource_as_block", "list_resource_as_attr", "list_primitive"} {
|
|
||||||
v := d.Get(k)
|
|
||||||
if v == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if l, ok := v.([]interface{}); !ok {
|
|
||||||
return fmt.Errorf("%s should appear as []interface {}, not %T", k, v)
|
|
||||||
} else {
|
|
||||||
for i, item := range l {
|
|
||||||
switch k {
|
|
||||||
case "list_primitive":
|
|
||||||
if _, ok := item.(string); item != nil && !ok {
|
|
||||||
return fmt.Errorf("%s[%d] should appear as string, not %T", k, i, item)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if _, ok := item.(map[string]interface{}); item != nil && !ok {
|
|
||||||
return fmt.Errorf("%s[%d] should appear as map[string]interface {}, not %T", k, i, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, k := range []string{"set_resource_as_block", "set_resource_as_attr", "set_primitive"} {
|
|
||||||
v := d.Get(k)
|
|
||||||
if v == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if s, ok := v.(*schema.Set); !ok {
|
|
||||||
return fmt.Errorf("%s should appear as *schema.Set, not %T", k, v)
|
|
||||||
} else {
|
|
||||||
for i, item := range s.List() {
|
|
||||||
switch k {
|
|
||||||
case "set_primitive":
|
|
||||||
if _, ok := item.(string); item != nil && !ok {
|
|
||||||
return fmt.Errorf("%s[%d] should appear as string, not %T", k, i, item)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if _, ok := item.(map[string]interface{}); item != nil && !ok {
|
|
||||||
return fmt.Errorf("%s[%d] should appear as map[string]interface {}, not %T", k, i, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func testResourceAsSingleUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
||||||
return testResourceAsSingleRead(d, meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testResourceAsSingleDelete(d *schema.ResourceData, meta interface{}) error {
|
|
||||||
d.SetId("")
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
|
||||||
"github.com/hashicorp/terraform/terraform"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestResourceAsSingle(t *testing.T) {
|
|
||||||
resource.UnitTest(t, resource.TestCase{
|
|
||||||
Providers: testAccProviders,
|
|
||||||
CheckDestroy: testAccCheckResourceDestroy,
|
|
||||||
Steps: []resource.TestStep{
|
|
||||||
resource.TestStep{
|
|
||||||
Config: strings.TrimSpace(`
|
|
||||||
resource "test_resource_as_single" "foo" {
|
|
||||||
list_resource_as_block {
|
|
||||||
foo = "as block a"
|
|
||||||
}
|
|
||||||
list_resource_as_attr = {
|
|
||||||
foo = "as attr a"
|
|
||||||
}
|
|
||||||
list_primitive = "primitive a"
|
|
||||||
|
|
||||||
set_resource_as_block {
|
|
||||||
foo = "as block a"
|
|
||||||
}
|
|
||||||
set_resource_as_attr = {
|
|
||||||
foo = "as attr a"
|
|
||||||
}
|
|
||||||
set_primitive = "primitive a"
|
|
||||||
}
|
|
||||||
`),
|
|
||||||
Check: resource.ComposeTestCheckFunc(
|
|
||||||
func(s *terraform.State) error {
|
|
||||||
t.Log("state after initial create:\n", s.String())
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_block.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_block.0.foo", "as block a"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_attr.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_attr.0.foo", "as attr a"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_primitive.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_primitive.0", "primitive a"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_block.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_block.1417230722.foo", "as block a"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_attr.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_attr.2549052262.foo", "as attr a"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_primitive.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_primitive.247272358", "primitive a"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
resource.TestStep{
|
|
||||||
Config: strings.TrimSpace(`
|
|
||||||
resource "test_resource_as_single" "foo" {
|
|
||||||
list_resource_as_block {
|
|
||||||
foo = "as block b"
|
|
||||||
}
|
|
||||||
list_resource_as_attr = {
|
|
||||||
foo = "as attr b"
|
|
||||||
}
|
|
||||||
list_primitive = "primitive b"
|
|
||||||
|
|
||||||
set_resource_as_block {
|
|
||||||
foo = "as block b"
|
|
||||||
}
|
|
||||||
set_resource_as_attr = {
|
|
||||||
foo = "as attr b"
|
|
||||||
}
|
|
||||||
set_primitive = "primitive b"
|
|
||||||
}
|
|
||||||
`),
|
|
||||||
Check: resource.ComposeTestCheckFunc(
|
|
||||||
func(s *terraform.State) error {
|
|
||||||
t.Log("state after update:\n", s.String())
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_block.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_block.0.foo", "as block b"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_attr.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_attr.0.foo", "as attr b"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_primitive.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_primitive.0", "primitive b"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_block.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_block.2136238657.foo", "as block b"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_attr.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_attr.3166838949.foo", "as attr b"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_primitive.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_primitive.630210661", "primitive b"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
resource.TestStep{
|
|
||||||
Config: strings.TrimSpace(`
|
|
||||||
resource "test_resource_as_single" "foo" {
|
|
||||||
}
|
|
||||||
`),
|
|
||||||
Check: resource.ComposeTestCheckFunc(
|
|
||||||
func(s *terraform.State) error {
|
|
||||||
t.Log("state after everything unset:\n", s.String())
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_block.#", "0"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_resource_as_attr.#", "0"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "list_primitive.#", "0"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_block.#", "0"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_resource_as_attr.#", "0"),
|
|
||||||
resource.TestCheckResourceAttr("test_resource_as_single.foo", "set_primitive.#", "0"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -87,17 +87,22 @@ func (s *GRPCProviderServer) getDatasourceSchemaBlockForCore(name string) *confi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCProviderServer) getProviderSchemaBlockForShimming() *configschema.Block {
|
func (s *GRPCProviderServer) getProviderSchemaBlockForShimming() *configschema.Block {
|
||||||
return schema.InternalMap(s.provider.Schema).CoreConfigSchemaForShimming()
|
newSchema := map[string]*schema.Schema{}
|
||||||
|
for attr, s := range s.provider.Schema {
|
||||||
|
newSchema[attr] = schema.LegacySchema(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema.InternalMap(newSchema).CoreConfigSchema()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCProviderServer) getResourceSchemaBlockForShimming(name string) *configschema.Block {
|
func (s *GRPCProviderServer) getResourceSchemaBlockForShimming(name string) *configschema.Block {
|
||||||
res := s.provider.ResourcesMap[name]
|
res := s.provider.ResourcesMap[name]
|
||||||
return res.CoreConfigSchemaForShimming()
|
return schema.LegacyResourceSchema(res).CoreConfigSchema()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCProviderServer) getDatasourceSchemaBlockForShimming(name string) *configschema.Block {
|
func (s *GRPCProviderServer) getDatasourceSchemaBlockForShimming(name string) *configschema.Block {
|
||||||
dat := s.provider.DataSourcesMap[name]
|
dat := s.provider.DataSourcesMap[name]
|
||||||
return dat.CoreConfigSchemaForShimming()
|
return schema.LegacyResourceSchema(dat).CoreConfigSchema()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto.PrepareProviderConfig_Request) (*proto.PrepareProviderConfig_Response, error) {
|
func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto.PrepareProviderConfig_Request) (*proto.PrepareProviderConfig_Response, error) {
|
||||||
|
@ -190,7 +195,6 @@ func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto
|
||||||
}
|
}
|
||||||
|
|
||||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.Schema)
|
|
||||||
|
|
||||||
warns, errs := s.provider.Validate(config)
|
warns, errs := s.provider.Validate(config)
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||||
|
@ -219,7 +223,6 @@ func (s *GRPCProviderServer) ValidateResourceTypeConfig(_ context.Context, req *
|
||||||
}
|
}
|
||||||
|
|
||||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.ResourcesMap[req.TypeName].Schema)
|
|
||||||
|
|
||||||
warns, errs := s.provider.ValidateResource(req.TypeName, config)
|
warns, errs := s.provider.ValidateResource(req.TypeName, config)
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||||
|
@ -246,7 +249,6 @@ func (s *GRPCProviderServer) ValidateDataSourceConfig(_ context.Context, req *pr
|
||||||
}
|
}
|
||||||
|
|
||||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.DataSourcesMap[req.TypeName].Schema)
|
|
||||||
|
|
||||||
warns, errs := s.provider.ValidateDataSource(req.TypeName, config)
|
warns, errs := s.provider.ValidateDataSource(req.TypeName, config)
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||||
|
@ -275,12 +277,6 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE WELL: The AsSingle mechanism cannot be automatically normalized here,
|
|
||||||
// so providers that use it must be ready to handle both normalized and
|
|
||||||
// unnormalized input in their upgrade codepaths. The _result_ of an upgrade
|
|
||||||
// should set a single-element list/set for any AsSingle element so that it
|
|
||||||
// can be normalized to a single value automatically on return.
|
|
||||||
|
|
||||||
// We first need to upgrade a flatmap state if it exists.
|
// We first need to upgrade a flatmap state if it exists.
|
||||||
// There should never be both a JSON and Flatmap state in the request.
|
// There should never be both a JSON and Flatmap state in the request.
|
||||||
if req.RawState.Flatmap != nil {
|
if req.RawState.Flatmap != nil {
|
||||||
|
@ -298,8 +294,6 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
schema.FixupAsSingleConfigValueOut(jsonMap, s.provider.ResourcesMap[req.TypeName].Schema)
|
|
||||||
|
|
||||||
// now we need to turn the state into the default json representation, so
|
// now we need to turn the state into the default json representation, so
|
||||||
// that it can be re-decoded using the actual schema.
|
// that it can be re-decoded using the actual schema.
|
||||||
val, err := schema.JSONMapToStateValue(jsonMap, blockForShimming)
|
val, err := schema.JSONMapToStateValue(jsonMap, blockForShimming)
|
||||||
|
@ -332,7 +326,7 @@ func (s *GRPCProviderServer) upgradeFlatmapState(version int, m map[string]strin
|
||||||
// first determine if we need to call the legacy MigrateState func
|
// first determine if we need to call the legacy MigrateState func
|
||||||
requiresMigrate := version < res.SchemaVersion
|
requiresMigrate := version < res.SchemaVersion
|
||||||
|
|
||||||
schemaType := res.CoreConfigSchemaForShimming().ImpliedType()
|
schemaType := schema.LegacyResourceSchema(res).CoreConfigSchema().ImpliedType()
|
||||||
|
|
||||||
// if there are any StateUpgraders, then we need to only compare
|
// if there are any StateUpgraders, then we need to only compare
|
||||||
// against the first version there
|
// against the first version there
|
||||||
|
@ -443,7 +437,6 @@ func (s *GRPCProviderServer) Configure(_ context.Context, req *proto.Configure_R
|
||||||
}
|
}
|
||||||
|
|
||||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.Schema)
|
|
||||||
err = s.provider.Configure(config)
|
err = s.provider.Configure(config)
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
|
|
||||||
|
@ -468,7 +461,6 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
// res.ShimInstanceStateFromValue result has already had FixupAsSingleInstanceStateIn applied
|
|
||||||
|
|
||||||
newInstanceState, err := res.RefreshWithoutUpgrade(instanceState, s.provider.Meta())
|
newInstanceState, err := res.RefreshWithoutUpgrade(instanceState, s.provider.Meta())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -493,7 +485,6 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
||||||
// helper/schema should always copy the ID over, but do it again just to be safe
|
// helper/schema should always copy the ID over, but do it again just to be safe
|
||||||
newInstanceState.Attributes["id"] = newInstanceState.ID
|
newInstanceState.Attributes["id"] = newInstanceState.ID
|
||||||
|
|
||||||
schema.FixupAsSingleInstanceStateOut(newInstanceState, s.provider.ResourcesMap[req.TypeName])
|
|
||||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, blockForShimming.ImpliedType())
|
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, blockForShimming.ImpliedType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
|
@ -560,7 +551,6 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
// res.ShimInstanceStateFromValue result has already had FixupAsSingleInstanceStateIn applied
|
|
||||||
priorPrivate := make(map[string]interface{})
|
priorPrivate := make(map[string]interface{})
|
||||||
if len(req.PriorPrivate) > 0 {
|
if len(req.PriorPrivate) > 0 {
|
||||||
if err := json.Unmarshal(req.PriorPrivate, &priorPrivate); err != nil {
|
if err := json.Unmarshal(req.PriorPrivate, &priorPrivate); err != nil {
|
||||||
|
@ -579,7 +569,6 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
||||||
|
|
||||||
// turn the proposed state into a legacy configuration
|
// turn the proposed state into a legacy configuration
|
||||||
cfg := terraform.NewResourceConfigShimmed(proposedNewStateVal, blockForShimming)
|
cfg := terraform.NewResourceConfigShimmed(proposedNewStateVal, blockForShimming)
|
||||||
schema.FixupAsSingleResourceConfigIn(cfg, s.provider.ResourcesMap[req.TypeName].Schema)
|
|
||||||
|
|
||||||
diff, err := s.provider.SimpleDiff(info, priorState, cfg)
|
diff, err := s.provider.SimpleDiff(info, priorState, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -612,17 +601,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we need to apply the diff to the prior state, so get the planned state
|
// now we need to apply the diff to the prior state, so get the planned state
|
||||||
plannedAttrs, err := diff.Apply(priorState.Attributes, res.CoreConfigSchemaWhenShimmed())
|
plannedAttrs, err := diff.Apply(priorState.Attributes, blockForShimming)
|
||||||
schema.FixupAsSingleInstanceStateOut(
|
|
||||||
&terraform.InstanceState{Attributes: plannedAttrs},
|
|
||||||
s.provider.ResourcesMap[req.TypeName],
|
|
||||||
)
|
|
||||||
|
|
||||||
// We also fix up the diff for AsSingle here, but note that we intentionally
|
|
||||||
// do it _after_ diff.Apply (so that the state can have its own fixup applied)
|
|
||||||
// but before we deal with requiresNew below so that fixing up the diff
|
|
||||||
// also fixes up the requiresNew keys to match.
|
|
||||||
schema.FixupAsSingleInstanceDiffOut(diff, s.provider.ResourcesMap[req.TypeName])
|
|
||||||
|
|
||||||
plannedStateVal, err := hcl2shim.HCL2ValueFromFlatmap(plannedAttrs, blockForShimming.ImpliedType())
|
plannedStateVal, err := hcl2shim.HCL2ValueFromFlatmap(plannedAttrs, blockForShimming.ImpliedType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -763,7 +742,6 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
// res.ShimInstanceStateFromValue result has already had FixupAsSingleInstanceStateIn applied
|
|
||||||
|
|
||||||
private := make(map[string]interface{})
|
private := make(map[string]interface{})
|
||||||
if len(req.PlannedPrivate) > 0 {
|
if len(req.PlannedPrivate) > 0 {
|
||||||
|
@ -785,10 +763,7 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
||||||
Destroy: true,
|
Destroy: true,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
diff, err = schema.DiffFromValues(
|
diff, err = schema.DiffFromValues(priorStateVal, plannedStateVal, stripResourceModifiers(res))
|
||||||
priorStateVal, plannedStateVal,
|
|
||||||
stripResourceModifiers(res),
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
@ -802,10 +777,6 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE WELL: schema.DiffFromValues has already effectively applied
|
|
||||||
// schema.FixupAsSingleInstanceDiffIn to the diff, so we need not (and must not)
|
|
||||||
// repeat that here.
|
|
||||||
|
|
||||||
// We need to fix any sets that may be using the "~" index prefix to
|
// We need to fix any sets that may be using the "~" index prefix to
|
||||||
// indicate partially computed. The special sigil isn't really used except
|
// indicate partially computed. The special sigil isn't really used except
|
||||||
// as a clue to visually indicate that the set isn't wholly known.
|
// as a clue to visually indicate that the set isn't wholly known.
|
||||||
|
@ -857,7 +828,6 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
}
|
}
|
||||||
schema.FixupAsSingleInstanceStateOut(newInstanceState, s.provider.ResourcesMap[req.TypeName])
|
|
||||||
newStateVal := cty.NullVal(blockForShimming.ImpliedType())
|
newStateVal := cty.NullVal(blockForShimming.ImpliedType())
|
||||||
|
|
||||||
// Always return a null value for destroy.
|
// Always return a null value for destroy.
|
||||||
|
@ -934,8 +904,6 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
|
||||||
// copy the ID again just to be sure it wasn't missed
|
// copy the ID again just to be sure it wasn't missed
|
||||||
is.Attributes["id"] = is.ID
|
is.Attributes["id"] = is.ID
|
||||||
|
|
||||||
schema.FixupAsSingleInstanceStateOut(is, s.provider.ResourcesMap[req.TypeName])
|
|
||||||
|
|
||||||
resourceType := is.Ephemeral.Type
|
resourceType := is.Ephemeral.Type
|
||||||
if resourceType == "" {
|
if resourceType == "" {
|
||||||
resourceType = req.TypeName
|
resourceType = req.TypeName
|
||||||
|
@ -998,7 +966,6 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
||||||
}
|
}
|
||||||
|
|
||||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.DataSourcesMap[req.TypeName].Schema)
|
|
||||||
|
|
||||||
// we need to still build the diff separately with the Read method to match
|
// we need to still build the diff separately with the Read method to match
|
||||||
// the old behavior
|
// the old behavior
|
||||||
|
@ -1014,7 +981,6 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
schema.FixupAsSingleInstanceStateOut(newInstanceState, s.provider.DataSourcesMap[req.TypeName])
|
|
||||||
|
|
||||||
newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, blockForShimming.ImpliedType())
|
newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, blockForShimming.ImpliedType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -4,11 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config/hcl2shim"
|
"github.com/hashicorp/terraform/config/hcl2shim"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/zclconf/go-cty/cty"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// shimState takes a new *states.State and reverts it to a legacy state for the provider ACC tests
|
// shimState takes a new *states.State and reverts it to a legacy state for the provider ACC tests
|
||||||
|
@ -153,7 +155,6 @@ func shimmedAttributes(instance *states.ResourceInstanceObjectSrc, res *schema.R
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceState, err := res.ShimInstanceStateFromValue(rio.Value)
|
instanceState, err := res.ShimInstanceStateFromValue(rio.Value)
|
||||||
// instanceState has already had the AsSingle fixup applied because ShimInstanceStateFromValue calls FixupAsSingleInstanceStateIn.
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,286 +0,0 @@
|
||||||
package schema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/terraform"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FixupAsSingleResourceConfigIn modifies the given ResourceConfig in-place if
|
|
||||||
// any attributes in the schema have the AsSingle flag set, wrapping the given
|
|
||||||
// values for these in an extra level of slice so that they can be understood
|
|
||||||
// by legacy SDK code that'll be expecting to decode into a list/set.
|
|
||||||
func FixupAsSingleResourceConfigIn(rc *terraform.ResourceConfig, s map[string]*Schema) {
|
|
||||||
if rc == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
FixupAsSingleConfigValueIn(rc.Config, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixupAsSingleInstanceStateIn modifies the given InstanceState in-place if
|
|
||||||
// any attributes in the schema have the AsSingle flag set, adding additional
|
|
||||||
// index steps to the flatmap keys for these so that they can be understood
|
|
||||||
// by legacy SDK code that'll be expecting to decode into a list/set.
|
|
||||||
func FixupAsSingleInstanceStateIn(is *terraform.InstanceState, r *Resource) {
|
|
||||||
fixupAsSingleInstanceState(is, r.Schema, "", fixupAsSingleFlatmapKeysIn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixupAsSingleInstanceStateOut modifies the given InstanceState in-place if
|
|
||||||
// any attributes in the schema have the AsSingle flag set, removing unneeded
|
|
||||||
// index steps from the flatmap keys for these so that they can be understood
|
|
||||||
// by the shim back to Terraform Core as a single nested value.
|
|
||||||
func FixupAsSingleInstanceStateOut(is *terraform.InstanceState, r *Resource) {
|
|
||||||
fixupAsSingleInstanceState(is, r.Schema, "", fixupAsSingleFlatmapKeysOut)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixupAsSingleInstanceDiffIn modifies the given InstanceDiff in-place if any
|
|
||||||
// attributes in the schema have the AsSingle flag set, adding additional index
|
|
||||||
// steps to the flatmap keys for these so that they can be understood by legacy
|
|
||||||
// SDK code that'll be expecting to decode into a list/set.
|
|
||||||
func FixupAsSingleInstanceDiffIn(id *terraform.InstanceDiff, r *Resource) {
|
|
||||||
fixupAsSingleInstanceDiff(id, r.Schema, "", fixupAsSingleAttrsMapKeysIn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixupAsSingleInstanceDiffOut modifies the given InstanceDiff in-place if any
|
|
||||||
// attributes in the schema have the AsSingle flag set, removing unneeded index
|
|
||||||
// steps from the flatmap keys for these so that they can be understood by the
|
|
||||||
// shim back to Terraform Core as a single nested value.
|
|
||||||
func FixupAsSingleInstanceDiffOut(id *terraform.InstanceDiff, r *Resource) {
|
|
||||||
fixupAsSingleInstanceDiff(id, r.Schema, "", fixupAsSingleAttrsMapKeysOut)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixupAsSingleConfigValueIn modifies the given "config value" in-place if
|
|
||||||
// any attributes in the schema have the AsSingle flag set, wrapping the given
|
|
||||||
// values for these in an extra level of slice so that they can be understood
|
|
||||||
// by legacy SDK code that'll be expecting to decode into a list/set.
|
|
||||||
//
|
|
||||||
// "Config value" for the purpose of this function has the same meaning as for
|
|
||||||
// the hcl2shims: a map[string]interface{} using the same subset of Go value
|
|
||||||
// types that would be generated by HCL/HIL when decoding a configuration in
|
|
||||||
// Terraform v0.11.
|
|
||||||
func FixupAsSingleConfigValueIn(c map[string]interface{}, s map[string]*Schema) {
|
|
||||||
for k, as := range s {
|
|
||||||
if !as.AsSingle {
|
|
||||||
continue // Don't touch non-AsSingle values at all. This is explicitly opt-in.
|
|
||||||
}
|
|
||||||
|
|
||||||
v, ok := c[k]
|
|
||||||
if ok {
|
|
||||||
c[k] = []interface{}{v}
|
|
||||||
}
|
|
||||||
|
|
||||||
if nr, ok := as.Elem.(*Resource); ok {
|
|
||||||
// Recursively fixup nested attributes too
|
|
||||||
nm, ok := v.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
// Weird for a nested resource to not be a map, but we'll tolerate it rather than crashing
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
FixupAsSingleConfigValueIn(nm, nr.Schema)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixupAsSingleConfigValueOut modifies the given "config value" in-place if
|
|
||||||
// any attributes in the schema have the AsSingle flag set, unwrapping the
|
|
||||||
// given values from their single-element slices so that they can be understood
|
|
||||||
// as a single object value by Terraform Core.
|
|
||||||
//
|
|
||||||
// This is the opposite of fixupAsSingleConfigValueIn.
|
|
||||||
func FixupAsSingleConfigValueOut(c map[string]interface{}, s map[string]*Schema) {
|
|
||||||
for k, as := range s {
|
|
||||||
if !as.AsSingle {
|
|
||||||
continue // Don't touch non-AsSingle values at all. This is explicitly opt-in.
|
|
||||||
}
|
|
||||||
|
|
||||||
sv, ok := c[k].([]interface{})
|
|
||||||
if ok && len(sv) != 0 { // Should always be a single-element slice, but if not we'll just leave it alone rather than crashing
|
|
||||||
c[k] = sv[0]
|
|
||||||
if nr, ok := as.Elem.(*Resource); ok {
|
|
||||||
// Recursively fixup nested attributes too
|
|
||||||
nm, ok := sv[0].(map[string]interface{})
|
|
||||||
if ok {
|
|
||||||
FixupAsSingleConfigValueOut(nm, nr.Schema)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fixupAsSingleInstanceState(is *terraform.InstanceState, s map[string]*Schema, prefix string, fn func(map[string]string, string) string) {
|
|
||||||
if is == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, as := range s {
|
|
||||||
if !as.AsSingle {
|
|
||||||
continue // Don't touch non-AsSingle values at all. This is explicitly opt-in.
|
|
||||||
}
|
|
||||||
|
|
||||||
nextPrefix := fn(is.Attributes, prefix+k+".")
|
|
||||||
if nr, ok := as.Elem.(*Resource); ok {
|
|
||||||
// Recursively fixup nested attributes too
|
|
||||||
fixupAsSingleInstanceState(is, nr.Schema, nextPrefix, fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fixupAsSingleInstanceDiff(id *terraform.InstanceDiff, s map[string]*Schema, prefix string, fn func(map[string]*terraform.ResourceAttrDiff, string) string) {
|
|
||||||
if id == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, as := range s {
|
|
||||||
if !as.AsSingle {
|
|
||||||
continue // Don't touch non-AsSingle values at all. This is explicitly opt-in.
|
|
||||||
}
|
|
||||||
|
|
||||||
nextPrefix := fn(id.Attributes, prefix+k+".")
|
|
||||||
if nr, ok := as.Elem.(*Resource); ok {
|
|
||||||
// Recursively fixup nested attributes too
|
|
||||||
fixupAsSingleInstanceDiff(id, nr.Schema, nextPrefix, fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixupAsSingleFlatmapKeysIn searches the given flatmap for all keys with
|
|
||||||
// the given prefix (which must end with a dot) and replaces them with keys
|
|
||||||
// where that prefix is followed by the dummy index "0." and, if any such
|
|
||||||
// keys are found, a ".#"-suffixed key is also added whose value is "1".
|
|
||||||
//
|
|
||||||
// This function will also replace an exact match of the given prefix with
|
|
||||||
// the trailing dot removed, to recognize values of primitive-typed attributes.
|
|
||||||
func fixupAsSingleFlatmapKeysIn(attrs map[string]string, prefix string) string {
|
|
||||||
ks := make([]string, 0, len(attrs))
|
|
||||||
for k := range attrs {
|
|
||||||
ks = append(ks, k)
|
|
||||||
}
|
|
||||||
sort.Strings(ks) // Makes no difference for valid input, but will ensure we handle invalid input deterministically
|
|
||||||
|
|
||||||
for _, k := range ks {
|
|
||||||
newK, countK := fixupAsSingleFlatmapKeyIn(k, prefix)
|
|
||||||
if _, exists := attrs[newK]; k != newK && !exists {
|
|
||||||
attrs[newK] = attrs[k]
|
|
||||||
delete(attrs, k)
|
|
||||||
}
|
|
||||||
if _, exists := attrs[countK]; countK != "" && !exists {
|
|
||||||
attrs[countK] = "1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prefix + "0."
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixupAsSingleAttrsMapKeysIn searches the given AttrDiff map for all keys with
|
|
||||||
// the given prefix (which must end with a dot) and replaces them with keys
|
|
||||||
// where that prefix is followed by the dummy index "0." and, if any such
|
|
||||||
// keys are found, a ".#"-suffixed key is also added whose value is "1".
|
|
||||||
//
|
|
||||||
// This function will also replace an exact match of the given prefix with
|
|
||||||
// the trailing dot removed, to recognize values of primitive-typed attributes.
|
|
||||||
func fixupAsSingleAttrsMapKeysIn(attrs map[string]*terraform.ResourceAttrDiff, prefix string) string {
|
|
||||||
ks := make([]string, 0, len(attrs))
|
|
||||||
for k := range attrs {
|
|
||||||
ks = append(ks, k)
|
|
||||||
}
|
|
||||||
sort.Strings(ks) // Makes no difference for valid input, but will ensure we handle invalid input deterministically
|
|
||||||
|
|
||||||
for _, k := range ks {
|
|
||||||
newK, countK := fixupAsSingleFlatmapKeyIn(k, prefix)
|
|
||||||
if _, exists := attrs[newK]; k != newK && !exists {
|
|
||||||
attrs[newK] = attrs[k]
|
|
||||||
delete(attrs, k)
|
|
||||||
}
|
|
||||||
if _, exists := attrs[countK]; countK != "" && !exists {
|
|
||||||
attrs[countK] = &terraform.ResourceAttrDiff{
|
|
||||||
Old: "1", // One should _always_ be present, so this seems okay?
|
|
||||||
New: "1",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prefix + "0."
|
|
||||||
}
|
|
||||||
|
|
||||||
func fixupAsSingleFlatmapKeyIn(k, prefix string) (string, string) {
|
|
||||||
exact := prefix[:len(prefix)-1]
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case k == exact:
|
|
||||||
return exact + ".0", exact + ".#"
|
|
||||||
case strings.HasPrefix(k, prefix):
|
|
||||||
return prefix + "0." + k[len(prefix):], prefix + "#"
|
|
||||||
default:
|
|
||||||
return k, ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixupAsSingleFlatmapKeysOut searches the given flatmap for all keys with
|
|
||||||
// the given prefix (which must end with a dot) and replaces them with keys
|
|
||||||
// where the following dot-separated label is removed, under the assumption that
|
|
||||||
// it's an index that is no longer needed and, if such a key is present, also
|
|
||||||
// remove the "count" key for the prefix, which is the prefix followed by "#".
|
|
||||||
func fixupAsSingleFlatmapKeysOut(attrs map[string]string, prefix string) string {
|
|
||||||
ks := make([]string, 0, len(attrs))
|
|
||||||
for k := range attrs {
|
|
||||||
ks = append(ks, k)
|
|
||||||
}
|
|
||||||
sort.Strings(ks) // Makes no difference for valid input, but will ensure we handle invalid input deterministically
|
|
||||||
|
|
||||||
for _, k := range ks {
|
|
||||||
newK := fixupAsSingleFlatmapKeyOut(k, prefix)
|
|
||||||
if newK != k && newK == "" {
|
|
||||||
delete(attrs, k)
|
|
||||||
} else if _, exists := attrs[newK]; newK != k && !exists {
|
|
||||||
attrs[newK] = attrs[k]
|
|
||||||
delete(attrs, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(attrs, prefix+"#") // drop the count key, if it's present
|
|
||||||
return prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixupAsSingleAttrsMapKeysOut searches the given AttrDiff map for all keys with
|
|
||||||
// the given prefix (which must end with a dot) and replaces them with keys
|
|
||||||
// where the following dot-separated label is removed, under the assumption that
|
|
||||||
// it's an index that is no longer needed and, if such a key is present, also
|
|
||||||
// remove the "count" key for the prefix, which is the prefix followed by "#".
|
|
||||||
func fixupAsSingleAttrsMapKeysOut(attrs map[string]*terraform.ResourceAttrDiff, prefix string) string {
|
|
||||||
ks := make([]string, 0, len(attrs))
|
|
||||||
for k := range attrs {
|
|
||||||
ks = append(ks, k)
|
|
||||||
}
|
|
||||||
sort.Strings(ks) // Makes no difference for valid input, but will ensure we handle invalid input deterministically
|
|
||||||
|
|
||||||
for _, k := range ks {
|
|
||||||
newK := fixupAsSingleFlatmapKeyOut(k, prefix)
|
|
||||||
if newK != k && newK == "" {
|
|
||||||
delete(attrs, k)
|
|
||||||
} else if _, exists := attrs[newK]; newK != k && !exists {
|
|
||||||
attrs[newK] = attrs[k]
|
|
||||||
delete(attrs, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(attrs, prefix+"#") // drop the count key, if it's present
|
|
||||||
return prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
func fixupAsSingleFlatmapKeyOut(k, prefix string) string {
|
|
||||||
if strings.HasPrefix(k, prefix) {
|
|
||||||
remain := k[len(prefix):]
|
|
||||||
if remain == "#" {
|
|
||||||
// Don't need the count element anymore
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
dotIdx := strings.Index(remain, ".")
|
|
||||||
if dotIdx == -1 {
|
|
||||||
return prefix[:len(prefix)-1] // no follow-on attributes then
|
|
||||||
} else {
|
|
||||||
return prefix + remain[dotIdx+1:] // everything after the next dot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return k
|
|
||||||
}
|
|
|
@ -1,239 +0,0 @@
|
||||||
package schema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/hashicorp/terraform/terraform"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFixupAsSingleInstanceStateInOut(t *testing.T) {
|
|
||||||
tests := map[string]struct {
|
|
||||||
AttrsIn map[string]string
|
|
||||||
AttrsOut map[string]string
|
|
||||||
Schema map[string]*Schema
|
|
||||||
}{
|
|
||||||
"empty": {
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
"simple": {
|
|
||||||
map[string]string{
|
|
||||||
"a": "a value",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"a": "a value",
|
|
||||||
},
|
|
||||||
map[string]*Schema{
|
|
||||||
"a": {Type: TypeString, Optional: true},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"normal list of primitive, empty": {
|
|
||||||
map[string]string{
|
|
||||||
"a.%": "0",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"a.%": "0",
|
|
||||||
},
|
|
||||||
map[string]*Schema{
|
|
||||||
"a": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
Elem: &Schema{Type: TypeString},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"normal list of primitive, single": {
|
|
||||||
map[string]string{
|
|
||||||
"a.%": "1",
|
|
||||||
"a.0": "hello",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"a.%": "1",
|
|
||||||
"a.0": "hello",
|
|
||||||
},
|
|
||||||
map[string]*Schema{
|
|
||||||
"a": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
Elem: &Schema{Type: TypeString},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"AsSingle list of primitive": {
|
|
||||||
map[string]string{
|
|
||||||
"a": "hello",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"a.#": "1",
|
|
||||||
"a.0": "hello",
|
|
||||||
},
|
|
||||||
map[string]*Schema{
|
|
||||||
"a": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Schema{Type: TypeString},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"AsSingle list of resource": {
|
|
||||||
map[string]string{
|
|
||||||
"a.b": "hello",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"a.#": "1",
|
|
||||||
"a.0.b": "hello",
|
|
||||||
},
|
|
||||||
map[string]*Schema{
|
|
||||||
"a": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Resource{
|
|
||||||
Schema: map[string]*Schema{
|
|
||||||
"b": {
|
|
||||||
Type: TypeString,
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"AsSingle list of resource with nested primitive list": {
|
|
||||||
map[string]string{
|
|
||||||
"a.b.#": "1",
|
|
||||||
"a.b.0": "hello",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"a.#": "1",
|
|
||||||
"a.0.b.#": "1",
|
|
||||||
"a.0.b.0": "hello",
|
|
||||||
},
|
|
||||||
map[string]*Schema{
|
|
||||||
"a": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Resource{
|
|
||||||
Schema: map[string]*Schema{
|
|
||||||
"b": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
Elem: &Schema{Type: TypeString},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"AsSingle list of resource with nested AsSingle primitive list": {
|
|
||||||
map[string]string{
|
|
||||||
"a.b": "hello",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"a.#": "1",
|
|
||||||
"a.0.b.#": "1",
|
|
||||||
"a.0.b.0": "hello",
|
|
||||||
},
|
|
||||||
map[string]*Schema{
|
|
||||||
"a": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Resource{
|
|
||||||
Schema: map[string]*Schema{
|
|
||||||
"b": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Schema{Type: TypeString},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"AsSingle list of resource with nested AsSingle resource list": {
|
|
||||||
map[string]string{
|
|
||||||
"a.b.c": "hello",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"a.#": "1",
|
|
||||||
"a.0.b.#": "1",
|
|
||||||
"a.0.b.0.c": "hello",
|
|
||||||
},
|
|
||||||
map[string]*Schema{
|
|
||||||
"a": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Resource{
|
|
||||||
Schema: map[string]*Schema{
|
|
||||||
"b": {
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Resource{
|
|
||||||
Schema: map[string]*Schema{
|
|
||||||
"c": {
|
|
||||||
Type: TypeString,
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
copyMap := func(m map[string]string) map[string]string {
|
|
||||||
if m == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ret := make(map[string]string, len(m))
|
|
||||||
for k, v := range m {
|
|
||||||
ret[k] = v
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, test := range tests {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Run("In", func(t *testing.T) {
|
|
||||||
input := copyMap(test.AttrsIn)
|
|
||||||
is := &terraform.InstanceState{
|
|
||||||
Attributes: input,
|
|
||||||
}
|
|
||||||
r := &Resource{Schema: test.Schema}
|
|
||||||
FixupAsSingleInstanceStateIn(is, r)
|
|
||||||
if !cmp.Equal(is.Attributes, test.AttrsOut) {
|
|
||||||
t.Errorf("wrong result\ninput: %#v\ngot: %#v\nwant: %#v\n\n%s", input, is.Attributes, test.AttrsOut, cmp.Diff(test.AttrsOut, is.Attributes))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("Out", func(t *testing.T) {
|
|
||||||
input := copyMap(test.AttrsOut)
|
|
||||||
is := &terraform.InstanceState{
|
|
||||||
Attributes: input,
|
|
||||||
}
|
|
||||||
r := &Resource{Schema: test.Schema}
|
|
||||||
FixupAsSingleInstanceStateOut(is, r)
|
|
||||||
if !cmp.Equal(is.Attributes, test.AttrsIn) {
|
|
||||||
t.Errorf("wrong result\ninput: %#v\ngot: %#v\nwant: %#v\n\n%s", input, is.Attributes, test.AttrsIn, cmp.Diff(test.AttrsIn, is.Attributes))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,35 +22,6 @@ import (
|
||||||
// This method presumes a schema that passes InternalValidate, and so may
|
// This method presumes a schema that passes InternalValidate, and so may
|
||||||
// panic or produce an invalid result if given an invalid schemaMap.
|
// panic or produce an invalid result if given an invalid schemaMap.
|
||||||
func (m schemaMap) CoreConfigSchema() *configschema.Block {
|
func (m schemaMap) CoreConfigSchema() *configschema.Block {
|
||||||
return m.coreConfigSchema(true, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoreConfigSchemaForShimming is a variant of CoreConfigSchema that returns
|
|
||||||
// the schema that should be used when applying our shimming behaviors.
|
|
||||||
//
|
|
||||||
// In particular, it ignores the SkipCoreTypeCheck flag on any legacy schemas,
|
|
||||||
// since the shims live on the SDK side and so they need to see the full
|
|
||||||
// type information that we'd normally hide from Terraform Core when skipping
|
|
||||||
// type checking over there.
|
|
||||||
func (m schemaMap) CoreConfigSchemaForShimming() *configschema.Block {
|
|
||||||
return m.coreConfigSchema(true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoreConfigSchemaWhenShimmed is a variant of CoreConfigSchema that returns
|
|
||||||
// the schema as it would appear when working with data structures that have
|
|
||||||
// already been shimmed to the legacy form.
|
|
||||||
//
|
|
||||||
// In particular, it ignores the AsSingle flag on any legacy schemas and behaves
|
|
||||||
// as if they were really lists/sets instead, thus giving a description of
|
|
||||||
// the shape of the data structure after the AsSingle fixup has been applied.
|
|
||||||
//
|
|
||||||
// This should be used with care only in unusual situations where we need to
|
|
||||||
// work with an already-shimmed value using a new-style schema.
|
|
||||||
func (m schemaMap) CoreConfigSchemaWhenShimmed() *configschema.Block {
|
|
||||||
return m.coreConfigSchema(false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m schemaMap) coreConfigSchema(asSingle, skipCoreCheck bool) *configschema.Block {
|
|
||||||
if len(m) == 0 {
|
if len(m) == 0 {
|
||||||
// We return an actual (empty) object here, rather than a nil,
|
// We return an actual (empty) object here, rather than a nil,
|
||||||
// because a nil result would mean that we don't have a schema at
|
// because a nil result would mean that we don't have a schema at
|
||||||
|
@ -65,7 +36,7 @@ func (m schemaMap) coreConfigSchema(asSingle, skipCoreCheck bool) *configschema.
|
||||||
|
|
||||||
for name, schema := range m {
|
for name, schema := range m {
|
||||||
if schema.Elem == nil {
|
if schema.Elem == nil {
|
||||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
ret.Attributes[name] = schema.coreConfigSchemaAttribute()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if schema.Type == TypeMap {
|
if schema.Type == TypeMap {
|
||||||
|
@ -79,27 +50,27 @@ func (m schemaMap) coreConfigSchema(asSingle, skipCoreCheck bool) *configschema.
|
||||||
sch.Elem = &Schema{
|
sch.Elem = &Schema{
|
||||||
Type: TypeString,
|
Type: TypeString,
|
||||||
}
|
}
|
||||||
ret.Attributes[name] = sch.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
ret.Attributes[name] = sch.coreConfigSchemaAttribute()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch schema.ConfigMode {
|
switch schema.ConfigMode {
|
||||||
case SchemaConfigModeAttr:
|
case SchemaConfigModeAttr:
|
||||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
ret.Attributes[name] = schema.coreConfigSchemaAttribute()
|
||||||
case SchemaConfigModeBlock:
|
case SchemaConfigModeBlock:
|
||||||
ret.BlockTypes[name] = schema.coreConfigSchemaBlock(asSingle, skipCoreCheck)
|
ret.BlockTypes[name] = schema.coreConfigSchemaBlock()
|
||||||
default: // SchemaConfigModeAuto, or any other invalid value
|
default: // SchemaConfigModeAuto, or any other invalid value
|
||||||
if schema.Computed && !schema.Optional {
|
if schema.Computed && !schema.Optional {
|
||||||
// Computed-only schemas are always handled as attributes,
|
// Computed-only schemas are always handled as attributes,
|
||||||
// because they never appear in configuration.
|
// because they never appear in configuration.
|
||||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
ret.Attributes[name] = schema.coreConfigSchemaAttribute()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch schema.Elem.(type) {
|
switch schema.Elem.(type) {
|
||||||
case *Schema, ValueType:
|
case *Schema, ValueType:
|
||||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
ret.Attributes[name] = schema.coreConfigSchemaAttribute()
|
||||||
case *Resource:
|
case *Resource:
|
||||||
ret.BlockTypes[name] = schema.coreConfigSchemaBlock(asSingle, skipCoreCheck)
|
ret.BlockTypes[name] = schema.coreConfigSchemaBlock()
|
||||||
default:
|
default:
|
||||||
// Should never happen for a valid schema
|
// Should never happen for a valid schema
|
||||||
panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem))
|
panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem))
|
||||||
|
@ -114,7 +85,7 @@ func (m schemaMap) coreConfigSchema(asSingle, skipCoreCheck bool) *configschema.
|
||||||
// of a schema. This is appropriate only for primitives or collections whose
|
// of a schema. This is appropriate only for primitives or collections whose
|
||||||
// Elem is an instance of Schema. Use coreConfigSchemaBlock for collections
|
// Elem is an instance of Schema. Use coreConfigSchemaBlock for collections
|
||||||
// whose elem is a whole resource.
|
// whose elem is a whole resource.
|
||||||
func (s *Schema) coreConfigSchemaAttribute(asSingle, skipCoreCheck bool) *configschema.Attribute {
|
func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute {
|
||||||
// The Schema.DefaultFunc capability adds some extra weirdness here since
|
// The Schema.DefaultFunc capability adds some extra weirdness here since
|
||||||
// it can be combined with "Required: true" to create a sitution where
|
// it can be combined with "Required: true" to create a sitution where
|
||||||
// required-ness is conditional. Terraform Core doesn't share this concept,
|
// required-ness is conditional. Terraform Core doesn't share this concept,
|
||||||
|
@ -145,7 +116,7 @@ func (s *Schema) coreConfigSchemaAttribute(asSingle, skipCoreCheck bool) *config
|
||||||
}
|
}
|
||||||
|
|
||||||
return &configschema.Attribute{
|
return &configschema.Attribute{
|
||||||
Type: s.coreConfigSchemaType(asSingle, skipCoreCheck),
|
Type: s.coreConfigSchemaType(),
|
||||||
Optional: opt,
|
Optional: opt,
|
||||||
Required: reqd,
|
Required: reqd,
|
||||||
Computed: s.Computed,
|
Computed: s.Computed,
|
||||||
|
@ -157,9 +128,9 @@ func (s *Schema) coreConfigSchemaAttribute(asSingle, skipCoreCheck bool) *config
|
||||||
// coreConfigSchemaBlock prepares a configschema.NestedBlock representation of
|
// coreConfigSchemaBlock prepares a configschema.NestedBlock representation of
|
||||||
// a schema. This is appropriate only for collections whose Elem is an instance
|
// a schema. This is appropriate only for collections whose Elem is an instance
|
||||||
// of Resource, and will panic otherwise.
|
// of Resource, and will panic otherwise.
|
||||||
func (s *Schema) coreConfigSchemaBlock(asSingle, skipCoreCheck bool) *configschema.NestedBlock {
|
func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock {
|
||||||
ret := &configschema.NestedBlock{}
|
ret := &configschema.NestedBlock{}
|
||||||
if nested := schemaMap(s.Elem.(*Resource).Schema).coreConfigSchema(asSingle, skipCoreCheck); nested != nil {
|
if nested := s.Elem.(*Resource).coreConfigSchema(); nested != nil {
|
||||||
ret.Block = *nested
|
ret.Block = *nested
|
||||||
}
|
}
|
||||||
switch s.Type {
|
switch s.Type {
|
||||||
|
@ -177,14 +148,6 @@ func (s *Schema) coreConfigSchemaBlock(asSingle, skipCoreCheck bool) *configsche
|
||||||
ret.MinItems = s.MinItems
|
ret.MinItems = s.MinItems
|
||||||
ret.MaxItems = s.MaxItems
|
ret.MaxItems = s.MaxItems
|
||||||
|
|
||||||
if s.AsSingle && asSingle {
|
|
||||||
// In AsSingle mode, we artifically force a TypeList or TypeSet
|
|
||||||
// attribute in the SDK to be treated as a single block by Terraform Core.
|
|
||||||
// This must then be fixed up in the shim code (in helper/plugin) so
|
|
||||||
// that the SDK still sees the lists or sets it's expecting.
|
|
||||||
ret.Nesting = configschema.NestingSingle
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Required && s.MinItems == 0 {
|
if s.Required && s.MinItems == 0 {
|
||||||
// configschema doesn't have a "required" representation for nested
|
// configschema doesn't have a "required" representation for nested
|
||||||
// blocks, but we can fake it by requiring at least one item.
|
// blocks, but we can fake it by requiring at least one item.
|
||||||
|
@ -210,8 +173,8 @@ func (s *Schema) coreConfigSchemaBlock(asSingle, skipCoreCheck bool) *configsche
|
||||||
|
|
||||||
// coreConfigSchemaType determines the core config schema type that corresponds
|
// coreConfigSchemaType determines the core config schema type that corresponds
|
||||||
// to a particular schema's type.
|
// to a particular schema's type.
|
||||||
func (s *Schema) coreConfigSchemaType(asSingle, skipCoreCheck bool) cty.Type {
|
func (s *Schema) coreConfigSchemaType() cty.Type {
|
||||||
if skipCoreCheck && s.SkipCoreTypeCheck {
|
if s.SkipCoreTypeCheck {
|
||||||
// If we're preparing a schema for Terraform Core and the schema is
|
// If we're preparing a schema for Terraform Core and the schema is
|
||||||
// asking us to skip the Core type-check then we'll tell core that this
|
// asking us to skip the Core type-check then we'll tell core that this
|
||||||
// attribute is dynamically-typed, so it'll just pass through anything
|
// attribute is dynamically-typed, so it'll just pass through anything
|
||||||
|
@ -233,17 +196,17 @@ func (s *Schema) coreConfigSchemaType(asSingle, skipCoreCheck bool) cty.Type {
|
||||||
var elemType cty.Type
|
var elemType cty.Type
|
||||||
switch set := s.Elem.(type) {
|
switch set := s.Elem.(type) {
|
||||||
case *Schema:
|
case *Schema:
|
||||||
elemType = set.coreConfigSchemaType(asSingle, skipCoreCheck)
|
elemType = set.coreConfigSchemaType()
|
||||||
case ValueType:
|
case ValueType:
|
||||||
// This represents a mistake in the provider code, but it's a
|
// This represents a mistake in the provider code, but it's a
|
||||||
// common one so we'll just shim it.
|
// common one so we'll just shim it.
|
||||||
elemType = (&Schema{Type: set}).coreConfigSchemaType(asSingle, skipCoreCheck)
|
elemType = (&Schema{Type: set}).coreConfigSchemaType()
|
||||||
case *Resource:
|
case *Resource:
|
||||||
// By default we construct a NestedBlock in this case, but this
|
// By default we construct a NestedBlock in this case, but this
|
||||||
// behavior is selected either for computed-only schemas or
|
// behavior is selected either for computed-only schemas or
|
||||||
// when ConfigMode is explicitly SchemaConfigModeBlock.
|
// when ConfigMode is explicitly SchemaConfigModeBlock.
|
||||||
// See schemaMap.CoreConfigSchema for the exact rules.
|
// See schemaMap.CoreConfigSchema for the exact rules.
|
||||||
elemType = schemaMap(set.Schema).coreConfigSchema(asSingle, skipCoreCheck).ImpliedType()
|
elemType = set.coreConfigSchema().ImpliedType()
|
||||||
default:
|
default:
|
||||||
if set != nil {
|
if set != nil {
|
||||||
// Should never happen for a valid schema
|
// Should never happen for a valid schema
|
||||||
|
@ -253,13 +216,6 @@ func (s *Schema) coreConfigSchemaType(asSingle, skipCoreCheck bool) cty.Type {
|
||||||
// to be compatible with them.
|
// to be compatible with them.
|
||||||
elemType = cty.String
|
elemType = cty.String
|
||||||
}
|
}
|
||||||
if s.AsSingle && asSingle {
|
|
||||||
// In AsSingle mode, we artifically force a TypeList or TypeSet
|
|
||||||
// attribute in the SDK to be treated as a single value by Terraform Core.
|
|
||||||
// This must then be fixed up in the shim code (in helper/plugin) so
|
|
||||||
// that the SDK still sees the lists or sets it's expecting.
|
|
||||||
return elemType
|
|
||||||
}
|
|
||||||
switch s.Type {
|
switch s.Type {
|
||||||
case TypeList:
|
case TypeList:
|
||||||
return cty.List(elemType)
|
return cty.List(elemType)
|
||||||
|
@ -281,34 +237,7 @@ func (s *Schema) coreConfigSchemaType(asSingle, skipCoreCheck bool) cty.Type {
|
||||||
// the resource's schema. CoreConfigSchema adds the implicitly required "id"
|
// the resource's schema. CoreConfigSchema adds the implicitly required "id"
|
||||||
// attribute for top level resources if it doesn't exist.
|
// attribute for top level resources if it doesn't exist.
|
||||||
func (r *Resource) CoreConfigSchema() *configschema.Block {
|
func (r *Resource) CoreConfigSchema() *configschema.Block {
|
||||||
return r.coreConfigSchema(true, true)
|
block := r.coreConfigSchema()
|
||||||
}
|
|
||||||
|
|
||||||
// CoreConfigSchemaForShimming is a variant of CoreConfigSchema that returns
|
|
||||||
// the schema that should be used to apply shims on the SDK side.
|
|
||||||
//
|
|
||||||
// In particular, it ignores the SkipCoreTypeCheck flag on any legacy schemas
|
|
||||||
// and uses the real type information instead.
|
|
||||||
func (r *Resource) CoreConfigSchemaForShimming() *configschema.Block {
|
|
||||||
return r.coreConfigSchema(true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoreConfigSchemaWhenShimmed is a variant of CoreConfigSchema that returns
|
|
||||||
// the schema as it would appear when working with data structures that have
|
|
||||||
// already been shimmed to the legacy form.
|
|
||||||
//
|
|
||||||
// In particular, it ignores the AsSingle flag on any legacy schemas and behaves
|
|
||||||
// as if they were really lists/sets instead, thus giving a description of
|
|
||||||
// the shape of the data structure after the AsSingle fixup has been applied.
|
|
||||||
//
|
|
||||||
// This should be used with care only in unusual situations where we need to
|
|
||||||
// work with an already-shimmed value using a new-style schema.
|
|
||||||
func (r *Resource) CoreConfigSchemaWhenShimmed() *configschema.Block {
|
|
||||||
return r.coreConfigSchema(false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) coreConfigSchema(asSingle, skipCoreCheck bool) *configschema.Block {
|
|
||||||
block := schemaMap(r.Schema).coreConfigSchema(asSingle, skipCoreCheck)
|
|
||||||
|
|
||||||
if block.Attributes == nil {
|
if block.Attributes == nil {
|
||||||
block.Attributes = map[string]*configschema.Attribute{}
|
block.Attributes = map[string]*configschema.Attribute{}
|
||||||
|
@ -377,6 +306,10 @@ func (r *Resource) coreConfigSchema(asSingle, skipCoreCheck bool) *configschema.
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Resource) coreConfigSchema() *configschema.Block {
|
||||||
|
return schemaMap(r.Schema).CoreConfigSchema()
|
||||||
|
}
|
||||||
|
|
||||||
// CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema
|
// CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema
|
||||||
// on the backends's schema.
|
// on the backends's schema.
|
||||||
func (r *Backend) CoreConfigSchema() *configschema.Block {
|
func (r *Backend) CoreConfigSchema() *configschema.Block {
|
||||||
|
|
|
@ -162,8 +162,6 @@ func (r *Resource) ShimInstanceStateFromValue(state cty.Value) (*terraform.Insta
|
||||||
// match those from the Schema.
|
// match those from the Schema.
|
||||||
s := terraform.NewInstanceStateShimmedFromValue(state, r.SchemaVersion)
|
s := terraform.NewInstanceStateShimmedFromValue(state, r.SchemaVersion)
|
||||||
|
|
||||||
FixupAsSingleInstanceStateIn(s, r)
|
|
||||||
|
|
||||||
// We now rebuild the state through the ResourceData, so that the set indexes
|
// We now rebuild the state through the ResourceData, so that the set indexes
|
||||||
// match what helper/schema expects.
|
// match what helper/schema expects.
|
||||||
data, err := schemaMap(r.Schema).Data(s, nil)
|
data, err := schemaMap(r.Schema).Data(s, nil)
|
||||||
|
|
|
@ -216,21 +216,8 @@ type Schema struct {
|
||||||
//
|
//
|
||||||
// If the field Optional is set to true then MinItems is ignored and thus
|
// If the field Optional is set to true then MinItems is ignored and thus
|
||||||
// effectively zero.
|
// effectively zero.
|
||||||
//
|
|
||||||
// If MaxItems is 1, you may optionally also set AsSingle in order to have
|
|
||||||
// Terraform v0.12 or later treat a TypeList or TypeSet as if it were a
|
|
||||||
// single value. It will remain a list or set in Terraform v0.10 and v0.11.
|
|
||||||
// Enabling this for an existing attribute after you've made at least one
|
|
||||||
// v0.12-compatible provider release is a breaking change. AsSingle is
|
|
||||||
// likely to misbehave when used with deeply-nested set structures due to
|
|
||||||
// the imprecision of set diffs, so be sure to test it thoroughly,
|
|
||||||
// including updates that change the set members at all levels. AsSingle
|
|
||||||
// exists primarily to be used in conjunction with ConfigMode when forcing
|
|
||||||
// a nested resource to be treated as an attribute, so it can be considered
|
|
||||||
// an attribute of object type rather than of list/set of object.
|
|
||||||
MaxItems int
|
MaxItems int
|
||||||
MinItems int
|
MinItems int
|
||||||
AsSingle bool
|
|
||||||
|
|
||||||
// PromoteSingle originally allowed for a single element to be assigned
|
// PromoteSingle originally allowed for a single element to be assigned
|
||||||
// where a primitive list was expected, but this no longer works from
|
// where a primitive list was expected, but this no longer works from
|
||||||
|
@ -863,15 +850,6 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.AsSingle {
|
|
||||||
if v.MaxItems != 1 {
|
|
||||||
return fmt.Errorf("%s: MaxItems must be 1 when AsSingle is set", k)
|
|
||||||
}
|
|
||||||
if v.Type != TypeList && v.Type != TypeSet {
|
|
||||||
return fmt.Errorf("%s: AsSingle can be used only with TypeList and TypeSet schemas", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Computed-only field
|
// Computed-only field
|
||||||
if v.Computed && !v.Optional {
|
if v.Computed && !v.Optional {
|
||||||
if v.ValidateFunc != nil {
|
if v.ValidateFunc != nil {
|
||||||
|
|
|
@ -3924,68 +3924,6 @@ func TestSchemaMap_InternalValidate(t *testing.T) {
|
||||||
},
|
},
|
||||||
true, // in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute
|
true, // in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute
|
||||||
},
|
},
|
||||||
|
|
||||||
"AsSingle okay": {
|
|
||||||
map[string]*Schema{
|
|
||||||
"block": &Schema{
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Resource{
|
|
||||||
Schema: map[string]*Schema{
|
|
||||||
"sub": &Schema{
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
Elem: &Resource{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
|
|
||||||
"AsSingle without MaxItems": {
|
|
||||||
map[string]*Schema{
|
|
||||||
"block": &Schema{
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Resource{
|
|
||||||
Schema: map[string]*Schema{
|
|
||||||
"sub": &Schema{
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
Elem: &Resource{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
true, // MaxItems must be 1 when AsSingle is set
|
|
||||||
},
|
|
||||||
|
|
||||||
"AsSingle on primitive type": {
|
|
||||||
map[string]*Schema{
|
|
||||||
"block": &Schema{
|
|
||||||
Type: TypeString,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
AsSingle: true,
|
|
||||||
Elem: &Resource{
|
|
||||||
Schema: map[string]*Schema{
|
|
||||||
"sub": &Schema{
|
|
||||||
Type: TypeList,
|
|
||||||
Optional: true,
|
|
||||||
Elem: &Resource{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
true, // Unexpected error occurred: block: MaxItems and MinItems are only supported on lists or sets
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range cases {
|
for tn, tc := range cases {
|
||||||
|
|
|
@ -14,10 +14,6 @@ import (
|
||||||
// derives a terraform.InstanceDiff to give to the legacy providers. This is
|
// derives a terraform.InstanceDiff to give to the legacy providers. This is
|
||||||
// used to take the states provided by the new ApplyResourceChange method and
|
// used to take the states provided by the new ApplyResourceChange method and
|
||||||
// convert them to a state+diff required for the legacy Apply method.
|
// convert them to a state+diff required for the legacy Apply method.
|
||||||
//
|
|
||||||
// If the fixup function is non-nil, it will be called with the constructed
|
|
||||||
// shimmed InstanceState and ResourceConfig values to do any necessary in-place
|
|
||||||
// mutations before producing the diff.
|
|
||||||
func DiffFromValues(prior, planned cty.Value, res *Resource) (*terraform.InstanceDiff, error) {
|
func DiffFromValues(prior, planned cty.Value, res *Resource) (*terraform.InstanceDiff, error) {
|
||||||
return diffFromValues(prior, planned, res, nil)
|
return diffFromValues(prior, planned, res, nil)
|
||||||
}
|
}
|
||||||
|
@ -28,7 +24,6 @@ func DiffFromValues(prior, planned cty.Value, res *Resource) (*terraform.Instanc
|
||||||
// have already been done.
|
// have already been done.
|
||||||
func diffFromValues(prior, planned cty.Value, res *Resource, cust CustomizeDiffFunc) (*terraform.InstanceDiff, error) {
|
func diffFromValues(prior, planned cty.Value, res *Resource, cust CustomizeDiffFunc) (*terraform.InstanceDiff, error) {
|
||||||
instanceState, err := res.ShimInstanceStateFromValue(prior)
|
instanceState, err := res.ShimInstanceStateFromValue(prior)
|
||||||
// The result of ShimInstanceStateFromValue already has FixupAsSingleInstanceStateIn applied
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -36,7 +31,6 @@ func diffFromValues(prior, planned cty.Value, res *Resource, cust CustomizeDiffF
|
||||||
configSchema := res.CoreConfigSchema()
|
configSchema := res.CoreConfigSchema()
|
||||||
|
|
||||||
cfg := terraform.NewResourceConfigShimmed(planned, configSchema)
|
cfg := terraform.NewResourceConfigShimmed(planned, configSchema)
|
||||||
FixupAsSingleResourceConfigIn(cfg, schemaMap(res.Schema))
|
|
||||||
|
|
||||||
diff, err := schemaMap(res.Schema).Diff(instanceState, cfg, cust, nil, false)
|
diff, err := schemaMap(res.Schema).Diff(instanceState, cfg, cust, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -94,3 +88,45 @@ func JSONMapToStateValue(m map[string]interface{}, block *configschema.Block) (c
|
||||||
func StateValueFromInstanceState(is *terraform.InstanceState, ty cty.Type) (cty.Value, error) {
|
func StateValueFromInstanceState(is *terraform.InstanceState, ty cty.Type) (cty.Value, error) {
|
||||||
return is.AttrsAsObjectValue(ty)
|
return is.AttrsAsObjectValue(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LegacyResourceSchema takes a *Resource and returns a deep copy with 0.12 specific
|
||||||
|
// features removed. This is used by the shims to get a configschema that
|
||||||
|
// directly matches the structure of the schema.Resource.
|
||||||
|
func LegacyResourceSchema(r *Resource) *Resource {
|
||||||
|
if r == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// start with a shallow copy
|
||||||
|
newResource := new(Resource)
|
||||||
|
*newResource = *r
|
||||||
|
newResource.Schema = map[string]*Schema{}
|
||||||
|
|
||||||
|
for k, s := range r.Schema {
|
||||||
|
newResource.Schema[k] = LegacySchema(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newResource
|
||||||
|
}
|
||||||
|
|
||||||
|
// LegacySchema takes a *Schema and returns a deep copy with 0.12 specific
|
||||||
|
// features removed. This is used by the shims to get a configschema that
|
||||||
|
// directly matches the structure of the schema.Resource.
|
||||||
|
func LegacySchema(s *Schema) *Schema {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// start with a shallow copy
|
||||||
|
newSchema := new(Schema)
|
||||||
|
*newSchema = *s
|
||||||
|
newSchema.ConfigMode = SchemaConfigModeAuto
|
||||||
|
newSchema.SkipCoreTypeCheck = false
|
||||||
|
|
||||||
|
switch e := newSchema.Elem.(type) {
|
||||||
|
case *Schema:
|
||||||
|
newSchema.Elem = LegacySchema(e)
|
||||||
|
case *Resource:
|
||||||
|
newSchema.Elem = LegacyResourceSchema(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newSchema
|
||||||
|
}
|
||||||
|
|
|
@ -191,7 +191,6 @@ func (p *GRPCProvider) PrepareProviderConfig(r providers.PrepareProviderConfigRe
|
||||||
|
|
||||||
func (p *GRPCProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) (resp providers.ValidateResourceTypeConfigResponse) {
|
func (p *GRPCProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) (resp providers.ValidateResourceTypeConfigResponse) {
|
||||||
log.Printf("[TRACE] GRPCProvider: ValidateResourceTypeConfig")
|
log.Printf("[TRACE] GRPCProvider: ValidateResourceTypeConfig")
|
||||||
|
|
||||||
resourceSchema := p.getResourceSchema(r.TypeName)
|
resourceSchema := p.getResourceSchema(r.TypeName)
|
||||||
|
|
||||||
mp, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType())
|
mp, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType())
|
||||||
|
|
Loading…
Reference in New Issue