Skip to content

Commit 72b038d

Browse files
authored
tfprotov5/tf5server+tfprotov6/tf6server: Include private state data in protocol data output (#221)
Reference: #220 This data is "private" in the sense that it is provider-owned, rather than something managed by Terraform.
1 parent a0168ca commit 72b038d

File tree

5 files changed

+84
-13
lines changed

5 files changed

+84
-13
lines changed

.changelog/221.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
```release-note:enhancement
2+
tfprotov5/tf5server: Added resource private state when protocol data output is enabled
3+
```
4+
5+
```release-note:enhancement
6+
tfprotov6/tf6server: Added resource private state when protocol data output is enabled
7+
```
8+
9+
```release-note:bug
10+
tfprotov5/tf5server: Fixed `ApplyResourceChange` request RPC protocol data output to include `PriorState` and `ProviderMeta` fields
11+
```
12+
13+
```release-note:bug
14+
tfprotov6/tf6server: Fixed `ApplyResourceChange` request RPC protocol data output to include `PriorState` and `ProviderMeta` fields
15+
```

internal/logging/protocol.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@ func ProtocolWarn(ctx context.Context, msg string, additionalFields ...map[strin
2525
func ProtocolTrace(ctx context.Context, msg string, additionalFields ...map[string]interface{}) {
2626
tfsdklog.SubsystemTrace(ctx, SubsystemProto, msg, additionalFields...)
2727
}
28+
29+
// ProtocolSetField returns a context with the additional protocol subsystem
30+
// field set.
31+
func ProtocolSetField(ctx context.Context, key string, value any) context.Context {
32+
return tfsdklog.SubsystemSetField(ctx, SubsystemProto, key, value)
33+
}

internal/logging/protocol_data.go

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,34 @@ func ProtocolData(ctx context.Context, dataDir string, rpc string, message strin
5555
return
5656
}
5757

58-
fileName := fmt.Sprintf("%d_%s_%s_%s.%s", time.Now().Unix(), rpc, message, field, fileExtension)
59-
filePath := path.Join(dataDir, fileName)
60-
logFields := map[string]interface{}{KeyProtocolDataFile: filePath} // should not be persisted using With()
61-
62-
ProtocolTrace(ctx, "Writing protocol data file", logFields)
58+
writeProtocolFile(ctx, dataDir, rpc, message, field, fileExtension, fileContents)
59+
}
6360

64-
err := os.WriteFile(filePath, fileContents, 0644)
61+
// ProtocolPrivateData emits raw protocol private data to a file, if given a
62+
// directory. This data is "private" in the sense that it is provider-owned,
63+
// rather than something managed by Terraform.
64+
//
65+
// The directory must exist and be writable, prior to invoking this function.
66+
//
67+
// File names are in the format: {TIME}_{RPC}_{MESSAGE}_{FIELD}(.empty)
68+
func ProtocolPrivateData(ctx context.Context, dataDir string, rpc string, message string, field string, data []byte) {
69+
if dataDir == "" {
70+
// Write a log, only once, that explains how to enable this functionality.
71+
protocolDataSkippedLog.Do(func() {
72+
ProtocolTrace(ctx, "Skipping protocol data file writing because no data directory is set. "+
73+
fmt.Sprintf("Use the %s environment variable to enable this functionality.", EnvTfLogSdkProtoDataDir))
74+
})
6575

66-
if err != nil {
67-
ProtocolError(ctx, fmt.Sprintf("Unable to write protocol data file: %s", err), logFields)
6876
return
6977
}
7078

71-
ProtocolTrace(ctx, "Wrote protocol data file", logFields)
79+
var fileExtension string
80+
81+
if len(data) == 0 {
82+
fileExtension = fileExtEmpty
83+
}
84+
85+
writeProtocolFile(ctx, dataDir, rpc, message, field, fileExtension, data)
7286
}
7387

7488
func protocolDataDynamicValue5(_ context.Context, value *tfprotov5.DynamicValue) (string, []byte) {
@@ -106,3 +120,25 @@ func protocolDataDynamicValue6(_ context.Context, value *tfprotov6.DynamicValue)
106120

107121
return fileExtEmpty, nil
108122
}
123+
124+
func writeProtocolFile(ctx context.Context, dataDir string, rpc string, message string, field string, fileExtension string, fileContents []byte) {
125+
fileName := fmt.Sprintf("%d_%s_%s_%s", time.Now().Unix(), rpc, message, field)
126+
127+
if fileExtension != "" {
128+
fileName += "." + fileExtension
129+
}
130+
131+
filePath := path.Join(dataDir, fileName)
132+
ctx = ProtocolSetField(ctx, KeyProtocolDataFile, filePath)
133+
134+
ProtocolTrace(ctx, "Writing protocol data file")
135+
136+
err := os.WriteFile(filePath, fileContents, 0644)
137+
138+
if err != nil {
139+
ProtocolError(ctx, "Unable to write protocol data file", map[string]any{KeyError: err.Error()})
140+
return
141+
}
142+
143+
ProtocolTrace(ctx, "Wrote protocol data file")
144+
}

tfprotov5/tf5server/server.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ func (s *server) ReadResource(ctx context.Context, req *tfplugin5.ReadResource_R
743743
}
744744
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "CurrentState", r.CurrentState)
745745
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
746+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "Private", r.Private)
746747
ctx = tf5serverlogging.DownstreamRequest(ctx)
747748
resp, err := s.downstream.ReadResource(ctx, r)
748749
if err != nil {
@@ -751,6 +752,7 @@ func (s *server) ReadResource(ctx context.Context, req *tfplugin5.ReadResource_R
751752
}
752753
tf5serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
753754
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "NewState", resp.NewState)
755+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "Private", resp.Private)
754756
ret, err := toproto.ReadResource_Response(resp)
755757
if err != nil {
756758
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
@@ -776,6 +778,7 @@ func (s *server) PlanResourceChange(ctx context.Context, req *tfplugin5.PlanReso
776778
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PriorState", r.PriorState)
777779
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProposedNewState", r.ProposedNewState)
778780
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
781+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "PriorPrivate", r.PriorPrivate)
779782
ctx = tf5serverlogging.DownstreamRequest(ctx)
780783
resp, err := s.downstream.PlanResourceChange(ctx, r)
781784
if err != nil {
@@ -784,6 +787,7 @@ func (s *server) PlanResourceChange(ctx context.Context, req *tfplugin5.PlanReso
784787
}
785788
tf5serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
786789
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "PlannedState", resp.PlannedState)
790+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "PlannedPrivate", resp.PlannedPrivate)
787791
ret, err := toproto.PlanResourceChange_Response(resp)
788792
if err != nil {
789793
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
@@ -807,8 +811,9 @@ func (s *server) ApplyResourceChange(ctx context.Context, req *tfplugin5.ApplyRe
807811
}
808812
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
809813
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PlannedState", r.PlannedState)
810-
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
811-
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
814+
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PriorState", r.PriorState)
815+
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
816+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "PlannedPrivate", r.PlannedPrivate)
812817
ctx = tf5serverlogging.DownstreamRequest(ctx)
813818
resp, err := s.downstream.ApplyResourceChange(ctx, r)
814819
if err != nil {
@@ -817,6 +822,7 @@ func (s *server) ApplyResourceChange(ctx context.Context, req *tfplugin5.ApplyRe
817822
}
818823
tf5serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
819824
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "NewState", resp.NewState)
825+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "Private", resp.Private)
820826
ret, err := toproto.ApplyResourceChange_Response(resp)
821827
if err != nil {
822828
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
@@ -847,6 +853,7 @@ func (s *server) ImportResourceState(ctx context.Context, req *tfplugin5.ImportR
847853
tf5serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
848854
for _, importedResource := range resp.ImportedResources {
849855
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response_ImportedResource", "State", importedResource.State)
856+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response_ImportedResource", "Private", importedResource.Private)
850857
}
851858
ret, err := toproto.ImportResourceState_Response(resp)
852859
if err != nil {

tfprotov6/tf6server/server.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ func (s *server) ReadResource(ctx context.Context, req *tfplugin6.ReadResource_R
741741
}
742742
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "CurrentState", r.CurrentState)
743743
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
744+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "Private", r.Private)
744745
ctx = tf6serverlogging.DownstreamRequest(ctx)
745746
resp, err := s.downstream.ReadResource(ctx, r)
746747
if err != nil {
@@ -749,6 +750,7 @@ func (s *server) ReadResource(ctx context.Context, req *tfplugin6.ReadResource_R
749750
}
750751
tf6serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
751752
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "NewState", resp.NewState)
753+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "Private", resp.Private)
752754
ret, err := toproto.ReadResource_Response(resp)
753755
if err != nil {
754756
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
@@ -774,6 +776,7 @@ func (s *server) PlanResourceChange(ctx context.Context, req *tfplugin6.PlanReso
774776
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PriorState", r.PriorState)
775777
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProposedNewState", r.ProposedNewState)
776778
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
779+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "PriorPrivate", r.PriorPrivate)
777780
ctx = tf6serverlogging.DownstreamRequest(ctx)
778781
resp, err := s.downstream.PlanResourceChange(ctx, r)
779782
if err != nil {
@@ -782,6 +785,7 @@ func (s *server) PlanResourceChange(ctx context.Context, req *tfplugin6.PlanReso
782785
}
783786
tf6serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
784787
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "PlannedState", resp.PlannedState)
788+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "PlannedPrivate", resp.PlannedPrivate)
785789
ret, err := toproto.PlanResourceChange_Response(resp)
786790
if err != nil {
787791
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
@@ -805,8 +809,9 @@ func (s *server) ApplyResourceChange(ctx context.Context, req *tfplugin6.ApplyRe
805809
}
806810
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
807811
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PlannedState", r.PlannedState)
808-
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
809-
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
812+
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PriorState", r.PriorState)
813+
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
814+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "PlannedPrivate", r.PlannedPrivate)
810815
ctx = tf6serverlogging.DownstreamRequest(ctx)
811816
resp, err := s.downstream.ApplyResourceChange(ctx, r)
812817
if err != nil {
@@ -815,6 +820,7 @@ func (s *server) ApplyResourceChange(ctx context.Context, req *tfplugin6.ApplyRe
815820
}
816821
tf6serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
817822
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "NewState", resp.NewState)
823+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "Private", resp.Private)
818824
ret, err := toproto.ApplyResourceChange_Response(resp)
819825
if err != nil {
820826
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
@@ -845,6 +851,7 @@ func (s *server) ImportResourceState(ctx context.Context, req *tfplugin6.ImportR
845851
tf6serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
846852
for _, importedResource := range resp.ImportedResources {
847853
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response_ImportedResource", "State", importedResource.State)
854+
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response_ImportedResource", "Private", importedResource.Private)
848855
}
849856
ret, err := toproto.ImportResourceState_Response(resp)
850857
if err != nil {

0 commit comments

Comments
 (0)