Skip to content

Commit

Permalink
tfprotov5/tf5server+tfprotov6/tf6server: Include private state data i…
Browse files Browse the repository at this point in the history
…n protocol data output (#221)

Reference: #220

This data is "private" in the sense that it is provider-owned, rather than something managed by Terraform.
  • Loading branch information
bflad committed Aug 15, 2022
1 parent a0168ca commit 72b038d
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 13 deletions.
15 changes: 15 additions & 0 deletions .changelog/221.txt
@@ -0,0 +1,15 @@
```release-note:enhancement
tfprotov5/tf5server: Added resource private state when protocol data output is enabled
```

```release-note:enhancement
tfprotov6/tf6server: Added resource private state when protocol data output is enabled
```

```release-note:bug
tfprotov5/tf5server: Fixed `ApplyResourceChange` request RPC protocol data output to include `PriorState` and `ProviderMeta` fields
```

```release-note:bug
tfprotov6/tf6server: Fixed `ApplyResourceChange` request RPC protocol data output to include `PriorState` and `ProviderMeta` fields
```
6 changes: 6 additions & 0 deletions internal/logging/protocol.go
Expand Up @@ -25,3 +25,9 @@ func ProtocolWarn(ctx context.Context, msg string, additionalFields ...map[strin
func ProtocolTrace(ctx context.Context, msg string, additionalFields ...map[string]interface{}) {
tfsdklog.SubsystemTrace(ctx, SubsystemProto, msg, additionalFields...)
}

// ProtocolSetField returns a context with the additional protocol subsystem
// field set.
func ProtocolSetField(ctx context.Context, key string, value any) context.Context {
return tfsdklog.SubsystemSetField(ctx, SubsystemProto, key, value)
}
54 changes: 45 additions & 9 deletions internal/logging/protocol_data.go
Expand Up @@ -55,20 +55,34 @@ func ProtocolData(ctx context.Context, dataDir string, rpc string, message strin
return
}

fileName := fmt.Sprintf("%d_%s_%s_%s.%s", time.Now().Unix(), rpc, message, field, fileExtension)
filePath := path.Join(dataDir, fileName)
logFields := map[string]interface{}{KeyProtocolDataFile: filePath} // should not be persisted using With()

ProtocolTrace(ctx, "Writing protocol data file", logFields)
writeProtocolFile(ctx, dataDir, rpc, message, field, fileExtension, fileContents)
}

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

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

ProtocolTrace(ctx, "Wrote protocol data file", logFields)
var fileExtension string

if len(data) == 0 {
fileExtension = fileExtEmpty
}

writeProtocolFile(ctx, dataDir, rpc, message, field, fileExtension, data)
}

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

return fileExtEmpty, nil
}

func writeProtocolFile(ctx context.Context, dataDir string, rpc string, message string, field string, fileExtension string, fileContents []byte) {
fileName := fmt.Sprintf("%d_%s_%s_%s", time.Now().Unix(), rpc, message, field)

if fileExtension != "" {
fileName += "." + fileExtension
}

filePath := path.Join(dataDir, fileName)
ctx = ProtocolSetField(ctx, KeyProtocolDataFile, filePath)

ProtocolTrace(ctx, "Writing protocol data file")

err := os.WriteFile(filePath, fileContents, 0644)

if err != nil {
ProtocolError(ctx, "Unable to write protocol data file", map[string]any{KeyError: err.Error()})
return
}

ProtocolTrace(ctx, "Wrote protocol data file")
}
11 changes: 9 additions & 2 deletions tfprotov5/tf5server/server.go
Expand Up @@ -743,6 +743,7 @@ func (s *server) ReadResource(ctx context.Context, req *tfplugin5.ReadResource_R
}
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "CurrentState", r.CurrentState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "Private", r.Private)
ctx = tf5serverlogging.DownstreamRequest(ctx)
resp, err := s.downstream.ReadResource(ctx, r)
if err != nil {
Expand All @@ -751,6 +752,7 @@ func (s *server) ReadResource(ctx context.Context, req *tfplugin5.ReadResource_R
}
tf5serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "NewState", resp.NewState)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "Private", resp.Private)
ret, err := toproto.ReadResource_Response(resp)
if err != nil {
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
Expand All @@ -776,6 +778,7 @@ func (s *server) PlanResourceChange(ctx context.Context, req *tfplugin5.PlanReso
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PriorState", r.PriorState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProposedNewState", r.ProposedNewState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "PriorPrivate", r.PriorPrivate)
ctx = tf5serverlogging.DownstreamRequest(ctx)
resp, err := s.downstream.PlanResourceChange(ctx, r)
if err != nil {
Expand All @@ -784,6 +787,7 @@ func (s *server) PlanResourceChange(ctx context.Context, req *tfplugin5.PlanReso
}
tf5serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "PlannedState", resp.PlannedState)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "PlannedPrivate", resp.PlannedPrivate)
ret, err := toproto.PlanResourceChange_Response(resp)
if err != nil {
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
Expand All @@ -807,8 +811,9 @@ func (s *server) ApplyResourceChange(ctx context.Context, req *tfplugin5.ApplyRe
}
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PlannedState", r.PlannedState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PriorState", r.PriorState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "PlannedPrivate", r.PlannedPrivate)
ctx = tf5serverlogging.DownstreamRequest(ctx)
resp, err := s.downstream.ApplyResourceChange(ctx, r)
if err != nil {
Expand All @@ -817,6 +822,7 @@ func (s *server) ApplyResourceChange(ctx context.Context, req *tfplugin5.ApplyRe
}
tf5serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "NewState", resp.NewState)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "Private", resp.Private)
ret, err := toproto.ApplyResourceChange_Response(resp)
if err != nil {
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
Expand Down Expand Up @@ -847,6 +853,7 @@ func (s *server) ImportResourceState(ctx context.Context, req *tfplugin5.ImportR
tf5serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
for _, importedResource := range resp.ImportedResources {
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response_ImportedResource", "State", importedResource.State)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response_ImportedResource", "Private", importedResource.Private)
}
ret, err := toproto.ImportResourceState_Response(resp)
if err != nil {
Expand Down
11 changes: 9 additions & 2 deletions tfprotov6/tf6server/server.go
Expand Up @@ -741,6 +741,7 @@ func (s *server) ReadResource(ctx context.Context, req *tfplugin6.ReadResource_R
}
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "CurrentState", r.CurrentState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "Private", r.Private)
ctx = tf6serverlogging.DownstreamRequest(ctx)
resp, err := s.downstream.ReadResource(ctx, r)
if err != nil {
Expand All @@ -749,6 +750,7 @@ func (s *server) ReadResource(ctx context.Context, req *tfplugin6.ReadResource_R
}
tf6serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "NewState", resp.NewState)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "Private", resp.Private)
ret, err := toproto.ReadResource_Response(resp)
if err != nil {
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
Expand All @@ -774,6 +776,7 @@ func (s *server) PlanResourceChange(ctx context.Context, req *tfplugin6.PlanReso
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PriorState", r.PriorState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProposedNewState", r.ProposedNewState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "PriorPrivate", r.PriorPrivate)
ctx = tf6serverlogging.DownstreamRequest(ctx)
resp, err := s.downstream.PlanResourceChange(ctx, r)
if err != nil {
Expand All @@ -782,6 +785,7 @@ func (s *server) PlanResourceChange(ctx context.Context, req *tfplugin6.PlanReso
}
tf6serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "PlannedState", resp.PlannedState)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "PlannedPrivate", resp.PlannedPrivate)
ret, err := toproto.PlanResourceChange_Response(resp)
if err != nil {
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
Expand All @@ -805,8 +809,9 @@ func (s *server) ApplyResourceChange(ctx context.Context, req *tfplugin6.ApplyRe
}
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PlannedState", r.PlannedState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "Config", r.Config)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "PriorState", r.PriorState)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Request", "ProviderMeta", r.ProviderMeta)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Request", "PlannedPrivate", r.PlannedPrivate)
ctx = tf6serverlogging.DownstreamRequest(ctx)
resp, err := s.downstream.ApplyResourceChange(ctx, r)
if err != nil {
Expand All @@ -815,6 +820,7 @@ func (s *server) ApplyResourceChange(ctx context.Context, req *tfplugin6.ApplyRe
}
tf6serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response", "NewState", resp.NewState)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response", "Private", resp.Private)
ret, err := toproto.ApplyResourceChange_Response(resp)
if err != nil {
logging.ProtocolError(ctx, "Error converting response to protobuf", map[string]interface{}{logging.KeyError: err})
Expand Down Expand Up @@ -845,6 +851,7 @@ func (s *server) ImportResourceState(ctx context.Context, req *tfplugin6.ImportR
tf6serverlogging.DownstreamResponse(ctx, resp.Diagnostics)
for _, importedResource := range resp.ImportedResources {
logging.ProtocolData(ctx, s.protocolDataDir, rpc, "Response_ImportedResource", "State", importedResource.State)
logging.ProtocolPrivateData(ctx, s.protocolDataDir, rpc, "Response_ImportedResource", "Private", importedResource.Private)
}
ret, err := toproto.ImportResourceState_Response(resp)
if err != nil {
Expand Down

0 comments on commit 72b038d

Please sign in to comment.