Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8b42001
go mod tidy
Jun 7, 2021
0feb137
Make GetProviderSchema use our schema type.
Jun 7, 2021
9130b47
Stub out a config type.
Jun 7, 2021
19b9daa
Stub out support for getting the tftypes.Type from a schema.
Jun 7, 2021
04cdfca
Start implementing a provider server.
Jun 7, 2021
339c341
More todos
Jun 8, 2021
738689e
Flesh out serving more, add internal/proto6 package.
Jun 9, 2021
a70c606
Fill in rest of schema fields.
Jun 9, 2021
e1d8409
Fill out PlanResourceChange a bit more.
Jun 9, 2021
a3af286
Fill in TODOs.
Jun 11, 2021
7370bea
Populate ARC request values.
Jun 11, 2021
7300730
Debug why we're getting a null state on read resource.
Jun 11, 2021
1ea7759
Final fixes and tweaks to make it actually work.
Jun 11, 2021
72b60f5
Add some unit testing.
Jun 16, 2021
1c84d8d
Add changelog, fix lint and broken test.
Jun 16, 2021
972b552
Return error instead of panicking.
Jun 16, 2021
895395d
Populate response state's schema so it can be set.
Jun 16, 2021
508bc1a
Add State.RemoveResourceFromState.
Jun 16, 2021
475679c
Don't panic reflecting into large numbers.
Jun 16, 2021
715758a
Start testing schema endpoint.
Jun 18, 2021
cdb3072
Test our internal/proto6 package.
Jun 21, 2021
2a31650
Refactor request type tests.
Jun 21, 2021
919e3de
More testing of serve code.
Jun 22, 2021
289de7f
gofmt
Jun 22, 2021
56fb4f2
Test ConfigureProvider.
Jun 22, 2021
b0ead17
Test unknown values in ConfigureProvider.
Jun 22, 2021
f302b9d
Round out testing on read resource for test_one.
Jun 23, 2021
a4907f1
Test resource type two, test read resource diags.
Jun 23, 2021
90dd3f6
Add tests for PlanResourceChange.
Jun 23, 2021
0385632
Begin testing ApplyResourceChange.
Jun 23, 2021
e7bd27a
Test ApplyResourceChange.
Jun 23, 2021
d9ca589
Fix build after ResourceType interface change.
Jun 23, 2021
63d5e62
Finish testing read data source.
Jun 23, 2021
c8dd7af
Update internal/proto6/request_type.go
Jun 24, 2021
030bc75
Update internal/proto6/request_type.go
Jun 24, 2021
aedc8d6
Fix data source signatures.
Jun 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fill in TODOs.
This required updating some of the request/response types. It also
required modifying how things implement
ApplyTerraform5AttributePathStep, so we could get at the
schema.Attribute instead of just the attr.Type. All tests still pass.
  • Loading branch information
Paddy Carver committed Jun 24, 2021
commit a3af2868e7dc0606df10a5eadd27155da21fd8ce
8 changes: 4 additions & 4 deletions data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ type DataSourceType interface {
// DataSource implements a data source instance.
type DataSource interface {
// Read is called when the provider must read data source values in
// order to update state. Planned state values should be read from the
// ReadResourceRequest and new state values set on the
// ReadResourceResponse.
Read(context.Context, *ReadResourceRequest, *ReadResourceResponse)
// order to update state. Config values should be read from the
// ReadDataSourceRequest and new state values set on the
// ReadDataSourceResponse.
Read(context.Context, ReadDataSourceRequest, *ReadDataSourceResponse)
}
13 changes: 13 additions & 0 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,16 @@ type DeleteResourceRequest struct {
// from knowing the value at request time.
Config Config
}

// ReadDataSourceRequest represents a request for the provider to read a data
// source, i.e., update values in state according to the real state of the
// data source. An instance of this request struct is supplied as an argument
// to the data source's Read function.
type ReadDataSourceRequest struct {
// Config is the configuration the user supplied for the data source.
//
// This configuration may contain unknown values if a user uses
// interpolation or other functionality that would prevent Terraform
// from knowing the value at request time.
Config Config
}
8 changes: 4 additions & 4 deletions resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,21 @@ type Resource interface {
// and planned state values should be read from the
// CreateResourceRequest and new state values set on the
// CreateResourceResponse.
Create(context.Context, *CreateResourceRequest, *CreateResourceResponse)
Create(context.Context, CreateResourceRequest, *CreateResourceResponse)

// Read is called when the provider must read resource values in order
// to update state. Planned state values should be read from the
// ReadResourceRequest and new state values set on the
// ReadResourceResponse.
Read(context.Context, *ReadResourceRequest, *ReadResourceResponse)
Read(context.Context, ReadResourceRequest, *ReadResourceResponse)

// Update is called to update the state of the resource. Config, planned
// state, and prior state values should be read from the
// UpdateResourceRequest and new state values set on the
// UpdateResourceResponse.
Update(context.Context, *UpdateResourceRequest, *UpdateResourceResponse)
Update(context.Context, UpdateResourceRequest, *UpdateResourceResponse)

// Delete is called when the provider must delete the resource. Config
// values may be read from the DeleteResourceRequest.
Delete(context.Context, *DeleteResourceRequest, *DeleteResourceResponse)
Delete(context.Context, DeleteResourceRequest, *DeleteResourceResponse)
}
20 changes: 20 additions & 0 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@ func (r *UpdateResourceResponse) AddAttributeError(attributePath *tftypes.Attrib
// an argument to the resource's Delete function, in which the provider
// should set values on the DeleteResourceResponse as appropriate.
type DeleteResourceResponse struct {
// State is the state of the resource following the Delete operation.
// This field is pre-populated from UpdateResourceRequest.Plan and
// should be set during the resource's Update operation.
State State

// Diagnostics report errors or warnings related to deleting the
// resource. An empty slice indicates a successful operation with no
// warnings or errors generated.
Expand Down Expand Up @@ -284,3 +289,18 @@ func (r *DeleteResourceResponse) AddAttributeError(attributePath *tftypes.Attrib
Severity: tfprotov6.DiagnosticSeverityError,
})
}

// ReadDataSourceResponse represents a response to a ReadDataSourceRequest. An
// instance of this response struct is supplied as an argument to the data
// source's Read function, in which the provider should set values on the
// ReadDataSourceResponse as appropriate.
type ReadDataSourceResponse struct {
// State is the state of the data source following the Read operation.
// This field should be set during the resource's Read operation.
State State

// Diagnostics report errors or warnings related to reading the data
// source. An empty slice indicates a successful operation with no
// warnings or errors generated.
Diagnostics []*tfprotov6.Diagnostic
}
13 changes: 13 additions & 0 deletions schema/attribute.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package schema

import (
"errors"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// Attribute defines the constraints and behaviors of a single field in a
Expand Down Expand Up @@ -62,3 +65,13 @@ type Attribute struct {
// instructing them on what upgrade steps to take.
DeprecationMessage string
}

func (a Attribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) {
if a.Type != nil {
return a.Type.ApplyTerraform5AttributePathStep(step)
}
if a.Attributes != nil {
return a.Attributes.ApplyTerraform5AttributePathStep(step)
}
return nil, errors.New("Attribute has no type or nested attributes")
}
85 changes: 57 additions & 28 deletions schema/nested_attributes.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package schema

import (
"fmt"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

type NestingMode uint8
Expand Down Expand Up @@ -38,6 +41,7 @@ const (
// attributes must be associated with a unique key. Unlike SetNestedAttributes,
// the key must be explicitly set by the user.
type NestedAttributes interface {
tftypes.AttributePathStepper
AttributeType() attr.Type
GetNestingMode() NestingMode
GetAttributes() map[string]Attribute
Expand All @@ -54,6 +58,34 @@ func (n nestedAttributes) GetAttributes() map[string]Attribute {

func (n nestedAttributes) unimplementable() {}

func (n nestedAttributes) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) {
a, ok := step.(tftypes.AttributeName)
if !ok {
return nil, fmt.Errorf("can't apply %T to Attributes", step)
}
res, ok := n[string(a)]
if !ok {
return nil, fmt.Errorf("no attribute %q on Attributes", a)
}
return res, nil
}

// AttributeType returns an attr.Type corresponding to the nested attributes.
func (n nestedAttributes) AttributeType() attr.Type {
attrTypes := map[string]attr.Type{}
for name, attr := range n.GetAttributes() {
if attr.Type != nil {
attrTypes[name] = attr.Type
}
if attr.Attributes != nil {
attrTypes[name] = attr.Attributes.AttributeType()
}
}
return types.ObjectType{
AttrTypes: attrTypes,
}
}

// SingleNestedAttributes nests `attributes` under another attribute, only
// allowing one instance of that group of attributes to appear in the
// configuration.
Expand All @@ -79,22 +111,6 @@ func (s singleNestedAttributes) GetMaxItems() int64 {
return 0
}

// AttributeType returns an attr.Type corresponding to the nested attributes.
func (s singleNestedAttributes) AttributeType() attr.Type {
attrTypes := map[string]attr.Type{}
for name, attr := range s.GetAttributes() {
if attr.Type != nil {
attrTypes[name] = attr.Type
}
if attr.Attributes != nil {
attrTypes[name] = attr.Attributes.AttributeType()
}
}
return types.ObjectType{
AttrTypes: attrTypes,
}
}

// 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
Expand Down Expand Up @@ -134,20 +150,17 @@ func (l listNestedAttributes) GetMaxItems() int64 {

// AttributeType returns an attr.Type corresponding to the nested attributes.
func (l listNestedAttributes) AttributeType() attr.Type {
attrTypes := map[string]attr.Type{}
for name, attr := range l.GetAttributes() {
if attr.Type != nil {
attrTypes[name] = attr.Type
}
if attr.Attributes != nil {
attrTypes[name] = attr.Attributes.AttributeType()
}
}
return types.ListType{
ElemType: types.ObjectType{
AttrTypes: attrTypes,
},
ElemType: l.nestedAttributes.AttributeType(),
}
}

func (l listNestedAttributes) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) {
_, ok := step.(tftypes.ElementKeyInt)
if !ok {
return nil, fmt.Errorf("can't apply %T to ListNestedAttributes", step)
}
return l.nestedAttributes, nil
}

// SetNestedAttributes nests `attributes` under another attribute, allowing
Expand Down Expand Up @@ -194,6 +207,14 @@ func (s setNestedAttributes) AttributeType() attr.Type {
return nil
}

func (s setNestedAttributes) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) {
_, ok := step.(tftypes.ElementKeyValue)
if !ok {
return nil, fmt.Errorf("can't use %T on sets", step)
}
return s.nestedAttributes, nil
}

// 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
Expand Down Expand Up @@ -237,3 +258,11 @@ func (m mapNestedAttributes) AttributeType() attr.Type {
// TODO fill in implementation when types.MapType is available
return nil
}

func (m mapNestedAttributes) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) {
_, ok := step.(tftypes.ElementKeyString)
if !ok {
return nil, fmt.Errorf("can't use %T on maps", step)
}
return m.nestedAttributes, nil
}
36 changes: 26 additions & 10 deletions schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,7 @@ type Schema struct {
func (s Schema) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) {
if v, ok := step.(tftypes.AttributeName); ok {
if attr, ok := s.Attributes[string(v)]; ok {
if attr.Type != nil {
return attr.Type, nil
}
if attr.Attributes != nil {
return attr.Attributes.AttributeType(), nil
}
return attr, nil
}
return nil, fmt.Errorf("could not find attribute %q in schema", v)
}
Expand Down Expand Up @@ -69,12 +64,20 @@ func (s Schema) AttributeTypeAtPath(path *tftypes.AttributePath) (attr.Type, err
return nil, fmt.Errorf("%v still remains in the path: %w", remaining, err)
}

attrType, ok := rawType.(attr.Type)
typ, ok := rawType.(attr.Type)
if ok {
return typ, nil
}

a, ok := rawType.(Attribute)
if !ok {
return nil, fmt.Errorf("got non-attr.Type result %v with type %T", rawType, rawType)
return nil, fmt.Errorf("got unexpected type %T", rawType)
}
if a.Type != nil {
return a.Type, nil
}

return attrType, nil
return a.Attributes.AttributeType(), nil
}
func (s Schema) TerraformType(ctx context.Context) tftypes.Type {
attrTypes := map[string]tftypes.Type{}
Expand All @@ -83,8 +86,21 @@ func (s Schema) TerraformType(ctx context.Context) tftypes.Type {
attrTypes[name] = attr.Type.TerraformType(ctx)
}
if attr.Attributes != nil {
// TODO: handle nested attributes
attrTypes[name] = attr.Attributes.AttributeType().TerraformType(ctx)
}
}
return tftypes.Object{AttributeTypes: attrTypes}
}

func (s Schema) AttributeAtPath(path *tftypes.AttributePath) (Attribute, error) {
res, remaining, err := tftypes.WalkAttributePath(s, path)
if err != nil {
return Attribute{}, fmt.Errorf("%v still remains in the path: %w", remaining, err)
}

a, ok := res.(Attribute)
if !ok {
return Attribute{}, fmt.Errorf("got unexpected type %T", res)
}
return a, nil
}
Loading