-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is another attempt at allowing providers to define schemas. It's born out of the ashes of #11, which gradually grew from baking an apple pie from scratch into creating the universe. Or however that saying goes. This is intentionally limited in scope to just setting up the types for declaring schemas and the types required by that, namely our attribute interfaces. Unlike #11, it makes no attempt to use these types for anything or prove they're the right types; the work done with #11 gives me confidence that they're a worthwhile direction to pursue. I'm submitting this as a separate PR to make review easier and to optimize for mergeability, letting us get some shared types established while still taking an appropriate amount of time to review the reflection code that is in our future.
- Loading branch information
1 parent
668dd44
commit 10602ce
Showing
9 changed files
with
330 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:feature | ||
Added support for defining schemas and attributes. | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package attr | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-go/tfprotov6" | ||
"github.com/hashicorp/terraform-plugin-go/tftypes" | ||
) | ||
|
||
// Type defines an interface for describing a kind of attribute. Types are | ||
// collections of constraints and behaviors such that they can be reused on | ||
// multiple attributes easily. | ||
type Type interface { | ||
// TerraformType returns the tftypes.Type that should be used to | ||
// represent this type. This constrains what user input will be | ||
// accepted and what kind of data can be set in state. The framework | ||
// will use this to translate the Type to something Terraform can | ||
// understand. | ||
TerraformType(context.Context) tftypes.Type | ||
|
||
// ValueFromTerraform returns a Value given a tftypes.Value. This is | ||
// meant to convert the tftypes.Value into a more convenient Go type | ||
// for the provider to consume the data with. | ||
ValueFromTerraform(context.Context, tftypes.Value) (Value, error) | ||
|
||
// Equal must return true if the Type is considered semantically equal | ||
// to the Type passed as an argument. | ||
Equal(Type) bool | ||
} | ||
|
||
// TypeWithValidate extends the Type interface to include a Validate method, | ||
// used to bundle consistent validation logic with the Type. | ||
type TypeWithValidate interface { | ||
Type | ||
|
||
// Validate returns any warnings or errors about the value that is | ||
// being used to populate the Type. It is generally used to check the | ||
// data format and ensure that it complies with the requirements of the | ||
// Type. | ||
// | ||
// TODO: don't use tfprotov6.Diagnostic, use our type | ||
Validate(context.Context, tftypes.Value) []*tfprotov6.Diagnostic | ||
} | ||
|
||
// TypeWithPlaintextDescription extends the Type interface to include a | ||
// Description method, used to bundle extra information to include in attribute | ||
// descriptions with the Type. It expects the description to be written as | ||
// plain text, with no special formatting. | ||
type TypeWithPlaintextDescription interface { | ||
Type | ||
|
||
// Description returns a practitioner-friendly explanation of the type | ||
// and the constraints of the data it accepts and returns. It will be | ||
// combined with the Description associated with the Attribute. | ||
Description(context.Context) string | ||
} | ||
|
||
// TypeWithMarkdownDescription extends the Type interface to include a | ||
// MarkdownDescription method, used to bundle extra information to include in | ||
// attribute descriptions with the Type. It expects the description to be | ||
// formatted for display with Markdown. | ||
type TypeWithMarkdownDescription interface { | ||
Type | ||
|
||
// MarkdownDescription returns a practitioner-friendly explanation of | ||
// the type and the constraints of the data it accepts and returns. It | ||
// will be combined with the MarkdownDescription associated with the | ||
// Attribute. | ||
MarkdownDescription(context.Context) string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package attr | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-go/tftypes" | ||
) | ||
|
||
// Value defines an interface for describing data associated with an attribute. | ||
// Values allow provider developers to specify data in a convenient format, and | ||
// have it transparently be converted to formats Terraform understands. | ||
type Value interface { | ||
// ToTerraformValue returns the data contained in the Value as | ||
// a Go type that tftypes.NewValue will accept. | ||
ToTerraformValue(context.Context) (interface{}, error) | ||
|
||
// SetTerraformValue updates the data in Value to match the | ||
// passed tftypes.Value. | ||
SetTerraformValue(context.Context, tftypes.Value) error | ||
|
||
// Equal must return true if the Value is considered semantically equal | ||
// to the Value passed as an argument. | ||
Equal(Value) bool | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package schema | ||
|
||
import "github.com/hashicorp/terraform-plugin-framework/attr" | ||
|
||
// Attribute defines the constraints and behaviors of a single field in a | ||
// schema. Attributes are the fields that show up in Terraform state files and | ||
// can be used in configuration files. | ||
type Attribute struct { | ||
// Type indicates what kind of attribute this is. You'll most likely | ||
// want to use one of the types in the types package. | ||
// | ||
// If Type is set, Attributes cannot be. | ||
Type attr.Type | ||
|
||
// Attributes can have their own, nested attributes. This nested map of | ||
// attributes behaves exactly like the map of attributes on the Schema | ||
// type. | ||
// | ||
// If Attributes is set, Type cannot be. | ||
Attributes NestedAttributes | ||
|
||
// Description is used in various tooling, like the language server, to | ||
// give practitioners more information about what this attribute is, | ||
// what it's for, and how it should be used. It should be written as | ||
// plain text, with no special formatting. | ||
Description string | ||
|
||
// MarkdownDescription is used in various tooling, like the | ||
// documentation generator, to give practitioners more information | ||
// about what this attribute is, what it's for, and how it should be | ||
// used. It should be formatted using Markdown. | ||
MarkdownDescription string | ||
|
||
// Required indicates whether the practitioner must enter a value for | ||
// this attribute or not. Required and Optional cannot both be true, | ||
// and Required and Computed cannot both be true. | ||
Required bool | ||
|
||
// Optional indicates whether the practitioner can choose not to enter | ||
// a value for this attribute or not. Optional and Required cannot both | ||
// be true. | ||
Optional bool | ||
|
||
// Computed indicates whether the provider may return its own value for | ||
// this attribute or not. Required and Computed cannot both be true. If | ||
// Required and Optional are both false, Computed must be true, and the | ||
// attribute will be considered "read only" for the practitioner, with | ||
// only the provider able to set its value. | ||
Computed bool | ||
|
||
// Sensitive indicates whether the value of this attribute should be | ||
// considered sensitive data. Setting it to true will obscure the value | ||
// in CLI output. Sensitive does not impact how values are stored, and | ||
// practitioners are encouraged to store their state as if the entire | ||
// file is sensitive. | ||
Sensitive bool | ||
|
||
// DeprecationMessage defines a message to display to practitioners | ||
// using this attribute, warning them that it is deprecated and | ||
// instructing them on what upgrade steps to take. | ||
DeprecationMessage string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package schema | ||
|
||
type nestingMode uint8 | ||
|
||
const ( | ||
nestingModeSingle nestingMode = 0 | ||
nestingModeList nestingMode = 1 | ||
nestingModeSet nestingMode = 2 | ||
nestingModeMap nestingMode = 3 | ||
) | ||
|
||
// NestedAttributes surfaces a group of attributes to nest beneath another | ||
// attribute, and how that nesting should behave. Nesting can have the | ||
// following modes: | ||
// | ||
// * SingleNestedAttributes are nested attributes that represent a struct or | ||
// object; there should only be one instance of them nested beneath that | ||
// specific attribute. | ||
// | ||
// * ListNestedAttributes are nested attributes that represent a list of | ||
// structs or objects; there can be multiple instances of them beneath that | ||
// specific attribute. | ||
// | ||
// * SetNestedAttributes are nested attributes that represent a set of structs | ||
// or objects; there can be multiple instances of them beneath that specific | ||
// attribute. Unlike ListNestedAttributes, these nested attributes must have | ||
// unique values. | ||
// | ||
// * MapNestedAttributes are nested attributes that represent a string-indexed | ||
// map of structs or objects; there can be multiple instances of them beneath | ||
// that specific attribute. Unlike ListNestedAttributes, these nested | ||
// attributes must be associated with a unique key. Unlike SetNestedAttributes, | ||
// the key must be explicitly set by the user. | ||
type NestedAttributes interface { | ||
getNestingMode() nestingMode | ||
getAttributes() map[string]Attribute | ||
} | ||
|
||
type nestedAttributes map[string]Attribute | ||
|
||
func (n nestedAttributes) getAttributes() map[string]Attribute { | ||
return map[string]Attribute(n) | ||
} | ||
|
||
// SingleNestedAttributes nests `attributes` under another attribute, only | ||
// allowing one instance of that group of attributes to appear in the | ||
// configuration. | ||
func SingleNestedAttributes(attributes map[string]Attribute) NestedAttributes { | ||
return singleNestedAttributes{ | ||
nestedAttributes(attributes), | ||
} | ||
} | ||
|
||
type singleNestedAttributes struct { | ||
nestedAttributes | ||
} | ||
|
||
func (s singleNestedAttributes) getNestingMode() nestingMode { | ||
return nestingModeSingle | ||
} | ||
|
||
// ListNestedAttributes nests `attributes` under another attribute, allowing | ||
// multiple instances of that group of attributes to appear in the | ||
// configuration. Minimum and maximum numbers of times the group can appear in | ||
// the configuration can be set using `opts`. | ||
func ListNestedAttributes(attributes map[string]Attribute, opts ListNestedAttributesOptions) NestedAttributes { | ||
return listNestedAttributes{ | ||
nestedAttributes: nestedAttributes(attributes), | ||
min: opts.MinItems, | ||
max: opts.MaxItems, | ||
} | ||
} | ||
|
||
type listNestedAttributes struct { | ||
nestedAttributes | ||
|
||
min, max int | ||
} | ||
|
||
// ListNestedAttributesOptions captures additional, optional parameters for | ||
// ListNestedAttributes. | ||
type ListNestedAttributesOptions struct { | ||
MinItems int | ||
MaxItems int | ||
} | ||
|
||
func (l listNestedAttributes) getNestingMode() nestingMode { | ||
return nestingModeList | ||
} | ||
|
||
// SetNestedAttributes nests `attributes` under another attribute, allowing | ||
// multiple instances of that group of attributes to appear in the | ||
// configuration, while requiring each group of values be unique. Minimum and | ||
// maximum numbers of times the group can appear in the configuration can be | ||
// set using `opts`. | ||
func SetNestedAttributes(attributes map[string]Attribute, opts SetNestedAttributesOptions) NestedAttributes { | ||
return setNestedAttributes{ | ||
nestedAttributes: nestedAttributes(attributes), | ||
min: opts.MinItems, | ||
max: opts.MaxItems, | ||
} | ||
} | ||
|
||
type setNestedAttributes struct { | ||
nestedAttributes | ||
|
||
min, max int | ||
} | ||
|
||
// SetNestedAttributesOptions captures additional, optional parameters for | ||
// SetNestedAttributes. | ||
type SetNestedAttributesOptions struct { | ||
MinItems int | ||
MaxItems int | ||
} | ||
|
||
func (s setNestedAttributes) getNestingMode() nestingMode { | ||
return nestingModeSet | ||
} | ||
|
||
// MapNestedAttributes nests `attributes` under another attribute, allowing | ||
// multiple instances of that group of attributes to appear in the | ||
// configuration. Each group will need to be associated with a unique string by | ||
// the user. Minimum and maximum numbers of times the group can appear in the | ||
// configuration can be set using `opts`. | ||
func MapNestedAttributes(attributes map[string]Attribute, opts MapNestedAttributesOptions) NestedAttributes { | ||
return mapNestedAttributes{ | ||
nestedAttributes: nestedAttributes(attributes), | ||
min: opts.MinItems, | ||
max: opts.MaxItems, | ||
} | ||
} | ||
|
||
type mapNestedAttributes struct { | ||
nestedAttributes | ||
|
||
min, max int | ||
} | ||
|
||
// MapNestedAttributesOptions captures additional, optional parameters for | ||
// MapNestedAttributes. | ||
type MapNestedAttributesOptions struct { | ||
MinItems int | ||
MaxItems int | ||
} | ||
|
||
func (m mapNestedAttributes) getNestingMode() nestingMode { | ||
return nestingModeMap | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package schema | ||
|
||
// Schema is used to define the shape of practitioner-provider information, | ||
// like resources, data sources, and providers. Think of it as a type | ||
// definition, but for Terraform. | ||
type Schema struct { | ||
// Attributes are the fields inside the resource, provider, or data | ||
// source that the schema is defining. The map key should be the name | ||
// of the attribute, and the body defines how it behaves. Names must | ||
// only contain lowercase letters, numbers, and underscores. | ||
Attributes map[string]Attribute | ||
|
||
// Version indicates the current version of the schema. Schemas are | ||
// versioned to help with automatic upgrade process. This is not | ||
// typically required unless there is a change in the schema, such as | ||
// changing an attribute type, that needs manual upgrade handling. | ||
// Versions should only be incremented by one each release. | ||
Version int64 | ||
} |