Skip to content

Commit e91cd2e

Browse files
Paddy Carverpaddycarver
authored andcommitted
Prototype exposing cty.Values from ResourceData.
Fixes #793.
1 parent b147a9f commit e91cd2e

File tree

9 files changed

+272
-6
lines changed

9 files changed

+272
-6
lines changed

helper/schema/grpc_provider.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ func (s *GRPCProviderServer) ReadResource(ctx context.Context, req *tfprotov5.Re
552552
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
553553
return resp, nil
554554
}
555+
instanceState.RawState = stateVal
555556

556557
private := make(map[string]interface{})
557558
if len(req.Private) > 0 {
@@ -656,11 +657,20 @@ func (s *GRPCProviderServer) PlanResourceChange(ctx context.Context, req *tfprot
656657
return resp, nil
657658
}
658659

660+
configVal, err := msgpack.Unmarshal(req.Config.MsgPack, schemaBlock.ImpliedType())
661+
if err != nil {
662+
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
663+
return resp, nil
664+
}
665+
659666
priorState, err := res.ShimInstanceStateFromValue(priorStateVal)
660667
if err != nil {
661668
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
662669
return resp, nil
663670
}
671+
priorState.RawState = priorStateVal
672+
priorState.RawPlan = proposedNewStateVal
673+
priorState.RawConfig = configVal
664674
priorPrivate := make(map[string]interface{})
665675
if len(req.PriorPrivate) > 0 {
666676
if err := json.Unmarshal(req.PriorPrivate, &priorPrivate); err != nil {
@@ -869,6 +879,12 @@ func (s *GRPCProviderServer) ApplyResourceChange(ctx context.Context, req *tfpro
869879
return resp, nil
870880
}
871881

882+
configVal, err := msgpack.Unmarshal(req.Config.MsgPack, schemaBlock.ImpliedType())
883+
if err != nil {
884+
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
885+
return resp, nil
886+
}
887+
872888
priorState, err := res.ShimInstanceStateFromValue(priorStateVal)
873889
if err != nil {
874890
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
@@ -893,9 +909,12 @@ func (s *GRPCProviderServer) ApplyResourceChange(ctx context.Context, req *tfpro
893909
Attributes: make(map[string]*terraform.ResourceAttrDiff),
894910
Meta: make(map[string]interface{}),
895911
Destroy: true,
912+
RawPlan: plannedStateVal,
913+
RawState: priorStateVal,
914+
RawConfig: configVal,
896915
}
897916
} else {
898-
diff, err = DiffFromValues(ctx, priorStateVal, plannedStateVal, stripResourceModifiers(res))
917+
diff, err = DiffFromValues(ctx, priorStateVal, plannedStateVal, configVal, stripResourceModifiers(res))
899918
if err != nil {
900919
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
901920
return resp, nil
@@ -906,6 +925,9 @@ func (s *GRPCProviderServer) ApplyResourceChange(ctx context.Context, req *tfpro
906925
diff = &terraform.InstanceDiff{
907926
Attributes: make(map[string]*terraform.ResourceAttrDiff),
908927
Meta: make(map[string]interface{}),
928+
RawPlan: plannedStateVal,
929+
RawState: priorStateVal,
930+
RawConfig: configVal,
909931
}
910932
}
911933

@@ -1101,6 +1123,8 @@ func (s *GRPCProviderServer) ReadDataSource(ctx context.Context, req *tfprotov5.
11011123
return resp, nil
11021124
}
11031125

1126+
diff.RawConfig = configVal
1127+
11041128
// now we can get the new complete data source
11051129
newInstanceState, diags := res.ReadDataApply(ctx, diff, s.provider.Meta())
11061130
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, diags)

helper/schema/grpc_provider_test.go

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

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"math/big"
78
"strconv"
@@ -606,6 +607,17 @@ func TestPlanResourceChange(t *testing.T) {
606607
t.Fatal(err)
607608
}
608609

610+
config, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
611+
"id": cty.NullVal(cty.String),
612+
}))
613+
if err != nil {
614+
t.Fatal(err)
615+
}
616+
configBytes, err := msgpack.Marshal(config, schema.ImpliedType())
617+
if err != nil {
618+
t.Fatal(err)
619+
}
620+
609621
testReq := &tfprotov5.PlanResourceChangeRequest{
610622
TypeName: "test",
611623
PriorState: &tfprotov5.DynamicValue{
@@ -614,6 +626,9 @@ func TestPlanResourceChange(t *testing.T) {
614626
ProposedNewState: &tfprotov5.DynamicValue{
615627
MsgPack: proposedState,
616628
},
629+
Config: &tfprotov5.DynamicValue{
630+
MsgPack: configBytes,
631+
},
617632
}
618633

619634
resp, err := server.PlanResourceChange(context.Background(), testReq)
@@ -663,6 +678,18 @@ func TestPlanResourceChange_bigint(t *testing.T) {
663678
t.Fatal(err)
664679
}
665680

681+
config, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
682+
"id": cty.NullVal(cty.String),
683+
"foo": cty.MustParseNumberVal("7227701560655103598"),
684+
}))
685+
if err != nil {
686+
t.Fatal(err)
687+
}
688+
configBytes, err := msgpack.Marshal(config, schema.ImpliedType())
689+
if err != nil {
690+
t.Fatal(err)
691+
}
692+
666693
testReq := &tfprotov5.PlanResourceChangeRequest{
667694
TypeName: "test",
668695
PriorState: &tfprotov5.DynamicValue{
@@ -671,6 +698,9 @@ func TestPlanResourceChange_bigint(t *testing.T) {
671698
ProposedNewState: &tfprotov5.DynamicValue{
672699
MsgPack: proposedState,
673700
},
701+
Config: &tfprotov5.DynamicValue{
702+
MsgPack: configBytes,
703+
},
674704
}
675705

676706
resp, err := server.PlanResourceChange(context.Background(), testReq)
@@ -749,6 +779,31 @@ func TestApplyResourceChange(t *testing.T) {
749779
},
750780
},
751781
},
782+
{
783+
Description: "Create_cty",
784+
TestResource: &Resource{
785+
SchemaVersion: 4,
786+
Schema: map[string]*Schema{
787+
"foo": {
788+
Type: TypeInt,
789+
Optional: true,
790+
},
791+
},
792+
CreateWithoutTimeout: func(_ context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics {
793+
if rd.GetRawConfig().IsNull() {
794+
return diag.FromErr(errors.New("null raw config"))
795+
}
796+
if !rd.GetRawState().IsNull() {
797+
return diag.FromErr(fmt.Errorf("non-null raw state: %s", rd.GetRawState().GoString()))
798+
}
799+
if rd.GetRawPlan().IsNull() {
800+
return diag.FromErr(errors.New("null raw plan"))
801+
}
802+
rd.SetId("bar")
803+
return nil
804+
},
805+
},
806+
},
752807
}
753808

754809
for _, testCase := range testCases {
@@ -779,6 +834,17 @@ func TestApplyResourceChange(t *testing.T) {
779834
t.Fatal(err)
780835
}
781836

837+
config, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
838+
"id": cty.NullVal(cty.String),
839+
}))
840+
if err != nil {
841+
t.Fatal(err)
842+
}
843+
configBytes, err := msgpack.Marshal(config, schema.ImpliedType())
844+
if err != nil {
845+
t.Fatal(err)
846+
}
847+
782848
testReq := &tfprotov5.ApplyResourceChangeRequest{
783849
TypeName: "test",
784850
PriorState: &tfprotov5.DynamicValue{
@@ -787,6 +853,9 @@ func TestApplyResourceChange(t *testing.T) {
787853
PlannedState: &tfprotov5.DynamicValue{
788854
MsgPack: plannedState,
789855
},
856+
Config: &tfprotov5.DynamicValue{
857+
MsgPack: configBytes,
858+
},
790859
}
791860

792861
resp, err := server.ApplyResourceChange(context.Background(), testReq)
@@ -886,6 +955,18 @@ func TestApplyResourceChange_bigint(t *testing.T) {
886955
t.Fatal(err)
887956
}
888957

958+
config, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
959+
"id": cty.NullVal(cty.String),
960+
"foo": cty.MustParseNumberVal("7227701560655103598"),
961+
}))
962+
if err != nil {
963+
t.Fatal(err)
964+
}
965+
configBytes, err := msgpack.Marshal(config, schema.ImpliedType())
966+
if err != nil {
967+
t.Fatal(err)
968+
}
969+
889970
testReq := &tfprotov5.ApplyResourceChangeRequest{
890971
TypeName: "test",
891972
PriorState: &tfprotov5.DynamicValue{
@@ -894,6 +975,9 @@ func TestApplyResourceChange_bigint(t *testing.T) {
894975
PlannedState: &tfprotov5.DynamicValue{
895976
MsgPack: plannedState,
896977
},
978+
Config: &tfprotov5.DynamicValue{
979+
MsgPack: configBytes,
980+
},
897981
}
898982

899983
resp, err := server.ApplyResourceChange(context.Background(), testReq)
@@ -1692,6 +1776,17 @@ func TestStopContext_grpc(t *testing.T) {
16921776
t.Fatal(err)
16931777
}
16941778

1779+
config, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
1780+
"id": cty.NullVal(cty.String),
1781+
}))
1782+
if err != nil {
1783+
t.Fatal(err)
1784+
}
1785+
configBytes, err := msgpack.Marshal(config, schema.ImpliedType())
1786+
if err != nil {
1787+
t.Fatal(err)
1788+
}
1789+
16951790
testReq := &tfprotov5.ApplyResourceChangeRequest{
16961791
TypeName: "test",
16971792
PriorState: &tfprotov5.DynamicValue{
@@ -1700,6 +1795,9 @@ func TestStopContext_grpc(t *testing.T) {
17001795
PlannedState: &tfprotov5.DynamicValue{
17011796
MsgPack: plannedState,
17021797
},
1798+
Config: &tfprotov5.DynamicValue{
1799+
MsgPack: configBytes,
1800+
},
17031801
}
17041802
ctx, cancel := context.WithCancel(context.Background())
17051803
ctx = server.StopContext(ctx)
@@ -1793,6 +1891,17 @@ func TestStopContext_stop(t *testing.T) {
17931891
t.Fatal(err)
17941892
}
17951893

1894+
config, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
1895+
"id": cty.NullVal(cty.String),
1896+
}))
1897+
if err != nil {
1898+
t.Fatal(err)
1899+
}
1900+
configBytes, err := msgpack.Marshal(config, schema.ImpliedType())
1901+
if err != nil {
1902+
t.Fatal(err)
1903+
}
1904+
17961905
testReq := &tfprotov5.ApplyResourceChangeRequest{
17971906
TypeName: "test",
17981907
PriorState: &tfprotov5.DynamicValue{
@@ -1801,6 +1910,9 @@ func TestStopContext_stop(t *testing.T) {
18011910
PlannedState: &tfprotov5.DynamicValue{
18021911
MsgPack: plannedState,
18031912
},
1913+
Config: &tfprotov5.DynamicValue{
1914+
MsgPack: configBytes,
1915+
},
18041916
}
18051917

18061918
ctx := server.StopContext(context.Background())
@@ -1893,6 +2005,17 @@ func TestStopContext_stopReset(t *testing.T) {
18932005
t.Fatal(err)
18942006
}
18952007

2008+
config, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
2009+
"id": cty.NullVal(cty.String),
2010+
}))
2011+
if err != nil {
2012+
t.Fatal(err)
2013+
}
2014+
configBytes, err := msgpack.Marshal(config, schema.ImpliedType())
2015+
if err != nil {
2016+
t.Fatal(err)
2017+
}
2018+
18962019
testReq := &tfprotov5.ApplyResourceChangeRequest{
18972020
TypeName: "test",
18982021
PriorState: &tfprotov5.DynamicValue{
@@ -1901,6 +2024,9 @@ func TestStopContext_stopReset(t *testing.T) {
19012024
PlannedState: &tfprotov5.DynamicValue{
19022025
MsgPack: plannedState,
19032026
},
2027+
Config: &tfprotov5.DynamicValue{
2028+
MsgPack: configBytes,
2029+
},
19042030
}
19052031

19062032
// test first stop

helper/schema/resource_data.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,3 +580,51 @@ func (d *ResourceData) GetProviderMeta(dst interface{}) error {
580580
}
581581
return gocty.FromCtyValue(d.providerMeta, &dst)
582582
}
583+
584+
// GetRawConfig returns the cty.Value that Terraform sent the SDK for the
585+
// config. If no value was sent, or if a null value was sent, the value will be
586+
// a null value of the resource's type.
587+
//
588+
// GetRawConfig is considered experimental and advanced functionality, and
589+
// familiarity with the Terraform protocol is suggested when using it.
590+
func (d *ResourceData) GetRawConfig() cty.Value {
591+
if d.diff != nil && !d.diff.RawConfig.IsNull() {
592+
return d.diff.RawConfig
593+
}
594+
if d.state != nil && !d.state.RawConfig.IsNull() {
595+
return d.state.RawConfig
596+
}
597+
return cty.NullVal(schemaMap(d.schema).CoreConfigSchema().ImpliedType())
598+
}
599+
600+
// GetRawState returns the cty.Value that Terraform sent the SDK for the state.
601+
// If no value was sent, or if a null value was sent, the value will be a null
602+
// value of the resource's type.
603+
//
604+
// GetRawState is considered experimental and advanced functionality, and
605+
// familiarity with the Terraform protocol is suggested when using it.
606+
func (d *ResourceData) GetRawState() cty.Value {
607+
if d.diff != nil && !d.diff.RawState.IsNull() {
608+
return d.diff.RawState
609+
}
610+
if d.state != nil && !d.state.RawState.IsNull() {
611+
return d.state.RawState
612+
}
613+
return cty.NullVal(schemaMap(d.schema).CoreConfigSchema().ImpliedType())
614+
}
615+
616+
// GetRawPlan returns the cty.Value that Terraform sent the SDK for the plan.
617+
// If no value was sent, or if a null value was sent, the value will be a null
618+
// value of the resource's type.
619+
//
620+
// GetRawPlan is considered experimental and advanced functionality, and
621+
// familiarity with the Terraform protocol is suggested when using it.
622+
func (d *ResourceData) GetRawPlan() cty.Value {
623+
if d.diff != nil && !d.diff.RawPlan.IsNull() {
624+
return d.diff.RawPlan
625+
}
626+
if d.state != nil && !d.state.RawPlan.IsNull() {
627+
return d.state.RawPlan
628+
}
629+
return cty.NullVal(schemaMap(d.schema).CoreConfigSchema().ImpliedType())
630+
}

0 commit comments

Comments
 (0)