Skip to content

Commit

Permalink
Add support for provider metadata to modules. (#22583)
Browse files Browse the repository at this point in the history
Implement a new provider_meta block in the terraform block of modules, allowing provider-keyed metadata to be communicated from HCL to provider binaries.

Bundled in this change for minimal protocol version bumping is the addition of markdown support for attribute descriptions and the ability to indicate when an attribute is deprecated, so this information can be shown in the schema dump.

Co-authored-by: Paul Tyng <paul@paultyng.net>
  • Loading branch information
paddycarver and paultyng authored Mar 6, 2020
1 parent 4654b45 commit e6592dc
Show file tree
Hide file tree
Showing 56 changed files with 2,457 additions and 246 deletions.
8 changes: 8 additions & 0 deletions builtin/providers/test/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ func Provider() terraform.ResourceProvider {
Optional: true,
},
},
ProviderMetaSchema: map[string]*schema.Schema{
// Optionally allow specifying information at a module-level
"foo": {
Type: schema.TypeString,
Optional: true,
},
},
ResourcesMap: map[string]*schema.Resource{
"test_resource": testResource(),
"test_resource_gh12183": testResourceGH12183(),
Expand All @@ -36,6 +43,7 @@ func Provider() terraform.ResourceProvider {
"test_resource_computed_set": testResourceComputedSet(),
"test_resource_config_mode": testResourceConfigMode(),
"test_resource_nested_id": testResourceNestedId(),
"test_resource_provider_meta": testResourceProviderMeta(),
"test_undeleteable": testResourceUndeleteable(),
"test_resource_required_min": testResourceRequiredMin(),
},
Expand Down
95 changes: 95 additions & 0 deletions builtin/providers/test/resource_provider_meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package test

import (
"fmt"

"github.com/hashicorp/terraform/helper/schema"
)

func testResourceProviderMeta() *schema.Resource {
return &schema.Resource{
Create: testResourceProviderMetaCreate,
Read: testResourceProviderMetaRead,
Update: testResourceProviderMetaUpdate,
Delete: testResourceProviderMetaDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"optional": {
Type: schema.TypeString,
Optional: true,
},
},
}
}

type providerMeta struct {
Foo string `cty:"foo"`
}

func testResourceProviderMetaCreate(d *schema.ResourceData, meta interface{}) error {
d.SetId("testId")
var m providerMeta

err := d.GetProviderMeta(&m)
if err != nil {
return err
}

if m.Foo != "bar" {
return fmt.Errorf("expected provider_meta.foo to be %q, was %q",
"bar", m.Foo)
}

return testResourceProviderMetaRead(d, meta)
}

func testResourceProviderMetaRead(d *schema.ResourceData, meta interface{}) error {
var m providerMeta

err := d.GetProviderMeta(&m)
if err != nil {
return err
}

if m.Foo != "bar" {
return fmt.Errorf("expected provider_meta.foo to be %q, was %q",
"bar", m.Foo)
}

return nil
}

func testResourceProviderMetaUpdate(d *schema.ResourceData, meta interface{}) error {
var m providerMeta

err := d.GetProviderMeta(&m)
if err != nil {
return err
}

if m.Foo != "bar" {
return fmt.Errorf("expected provider_meta.foo to be %q, was %q",
"bar", m.Foo)
}
return testResourceProviderMetaRead(d, meta)
}

func testResourceProviderMetaDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
var m providerMeta

err := d.GetProviderMeta(&m)
if err != nil {
return err
}

if m.Foo != "bar" {
return fmt.Errorf("expected provider_meta.foo to be %q, was %q",
"bar", m.Foo)
}
return nil
}
29 changes: 29 additions & 0 deletions builtin/providers/test/resource_provider_meta_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package test

import (
"strings"
"testing"

"github.com/hashicorp/terraform/helper/resource"
)

func TestResourceProviderMeta_basic(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
Providers: testAccProviders,
CheckDestroy: testAccCheckResourceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: strings.TrimSpace(`
terraform {
provider_meta "test" {
foo = "bar"
}
}
resource "test_resource_provider_meta" "foo" {
}
`),
},
},
})
}
37 changes: 25 additions & 12 deletions command/jsonprovider/attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,23 @@ import (
)

type attribute struct {
AttributeType json.RawMessage `json:"type,omitempty"`
Description string `json:"description,omitempty"`
Required bool `json:"required,omitempty"`
Optional bool `json:"optional,omitempty"`
Computed bool `json:"computed,omitempty"`
Sensitive bool `json:"sensitive,omitempty"`
AttributeType json.RawMessage `json:"type,omitempty"`
Description string `json:"description,omitempty"`
DescriptionKind string `json:"description_kind,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
Required bool `json:"required,omitempty"`
Optional bool `json:"optional,omitempty"`
Computed bool `json:"computed,omitempty"`
Sensitive bool `json:"sensitive,omitempty"`
}

func marshalStringKind(sk configschema.StringKind) string {
switch sk {
default:
return "plain"
case configschema.StringMarkdown:
return "markdown"
}
}

func marshalAttribute(attr *configschema.Attribute) *attribute {
Expand All @@ -21,11 +32,13 @@ func marshalAttribute(attr *configschema.Attribute) *attribute {
attrTy, _ := attr.Type.MarshalJSON()

return &attribute{
AttributeType: attrTy,
Description: attr.Description,
Required: attr.Required,
Optional: attr.Optional,
Computed: attr.Computed,
Sensitive: attr.Sensitive,
AttributeType: attrTy,
Description: attr.Description,
DescriptionKind: marshalStringKind(attr.DescriptionKind),
Required: attr.Required,
Optional: attr.Optional,
Computed: attr.Computed,
Sensitive: attr.Sensitive,
Deprecated: attr.Deprecated,
}
}
14 changes: 8 additions & 6 deletions command/jsonprovider/attribute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ func TestMarshalAttribute(t *testing.T) {
{
&configschema.Attribute{Type: cty.String, Optional: true, Computed: true},
&attribute{
AttributeType: json.RawMessage(`"string"`),
Optional: true,
Computed: true,
AttributeType: json.RawMessage(`"string"`),
Optional: true,
Computed: true,
DescriptionKind: "plain",
},
},
{ // collection types look a little odd.
&configschema.Attribute{Type: cty.Map(cty.String), Optional: true, Computed: true},
&attribute{
AttributeType: json.RawMessage(`["map","string"]`),
Optional: true,
Computed: true,
AttributeType: json.RawMessage(`["map","string"]`),
Optional: true,
Computed: true,
DescriptionKind: "plain",
},
},
}
Expand Down
14 changes: 11 additions & 3 deletions command/jsonprovider/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import (
)

type block struct {
Attributes map[string]*attribute `json:"attributes,omitempty"`
BlockTypes map[string]*blockType `json:"block_types,omitempty"`
Attributes map[string]*attribute `json:"attributes,omitempty"`
BlockTypes map[string]*blockType `json:"block_types,omitempty"`
Description string `json:"description,omitempty"`
DescriptionKind string `json:"description_kind,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
}

type blockType struct {
Expand Down Expand Up @@ -48,7 +51,12 @@ func marshalBlock(configBlock *configschema.Block) *block {
return &block{}
}

var ret block
ret := block{
Deprecated: configBlock.Deprecated,
Description: configBlock.Description,
DescriptionKind: marshalStringKind(configBlock.DescriptionKind),
}

if len(configBlock.Attributes) > 0 {
attrs := make(map[string]*attribute, len(configBlock.Attributes))
for k, attr := range configBlock.Attributes {
Expand Down
10 changes: 6 additions & 4 deletions command/jsonprovider/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,22 @@ func TestMarshalBlock(t *testing.T) {
},
Want: &block{
Attributes: map[string]*attribute{
"ami": {AttributeType: json.RawMessage(`"string"`), Optional: true},
"id": {AttributeType: json.RawMessage(`"string"`), Optional: true, Computed: true},
"ami": {AttributeType: json.RawMessage(`"string"`), Optional: true, DescriptionKind: "plain"},
"id": {AttributeType: json.RawMessage(`"string"`), Optional: true, Computed: true, DescriptionKind: "plain"},
},
BlockTypes: map[string]*blockType{
"network_interface": {
NestingMode: "list",
Block: &block{
Attributes: map[string]*attribute{
"description": {AttributeType: json.RawMessage(`"string"`), Optional: true},
"device_index": {AttributeType: json.RawMessage(`"string"`), Optional: true},
"description": {AttributeType: json.RawMessage(`"string"`), Optional: true, DescriptionKind: "plain"},
"device_index": {AttributeType: json.RawMessage(`"string"`), Optional: true, DescriptionKind: "plain"},
},
DescriptionKind: "plain",
},
},
},
DescriptionKind: "plain",
},
},
}
Expand Down
54 changes: 34 additions & 20 deletions command/jsonprovider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ func TestMarshalProvider(t *testing.T) {
Block: &block{
Attributes: map[string]*attribute{
"region": {
AttributeType: json.RawMessage(`"string"`),
Required: true,
AttributeType: json.RawMessage(`"string"`),
Required: true,
DescriptionKind: "plain",
},
},
DescriptionKind: "plain",
},
},
ResourceSchemas: map[string]*schema{
Expand All @@ -40,32 +42,38 @@ func TestMarshalProvider(t *testing.T) {
Block: &block{
Attributes: map[string]*attribute{
"id": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
Computed: true,
AttributeType: json.RawMessage(`"string"`),
Optional: true,
Computed: true,
DescriptionKind: "plain",
},
"ami": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
},
BlockTypes: map[string]*blockType{
"network_interface": {
Block: &block{
Attributes: map[string]*attribute{
"device_index": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
"description": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
},
DescriptionKind: "plain",
},
NestingMode: "list",
},
},
DescriptionKind: "plain",
},
},
},
Expand All @@ -75,32 +83,38 @@ func TestMarshalProvider(t *testing.T) {
Block: &block{
Attributes: map[string]*attribute{
"id": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
Computed: true,
AttributeType: json.RawMessage(`"string"`),
Optional: true,
Computed: true,
DescriptionKind: "plain",
},
"ami": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
},
BlockTypes: map[string]*blockType{
"network_interface": {
Block: &block{
Attributes: map[string]*attribute{
"device_index": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
"description": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
},
DescriptionKind: "plain",
},
NestingMode: "list",
},
},
DescriptionKind: "plain",
},
},
},
Expand Down
Loading

0 comments on commit e6592dc

Please sign in to comment.