Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tfprotov5/tf5server+tfprotov6/tf6server: Include private state data in protocol data output #221

Merged
merged 2 commits into from Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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