Skip to content

Commit

Permalink
Annotate panics with provider metadata and origin (#2871)
Browse files Browse the repository at this point in the history
Recover panics in the bridged providers and emit them as engine error
events with sufficient annotations about which provider, provider
version, resource and method was responsible for the panic.

Fixes #2844

While the bridged providers should never panic, some WIP semantic
discrepancy between TF and Pulumi still occasionally results in panics.
These changes accomplish two purposes. First, it is easier for
maintainers to immediately narrow down a panic to a resource or function
involved to attempt a repro. Second, it is easier to scan for panics in
internal logs and attribute them to providers unambiguously.

With these changes, an injected panic in pulumi-random looks as follows.
It is unfortunately double-stated but the new error message correctly
attributes it to the provider, resource and method:


```
pulumi up --yes                                 ~/code/pulumi-random/examples/simple/ts
Previewing update (anton-test-logging)

View in Browser (Ctrl+O): https://app.pulumi.com/anton-pulumi-corp/simple-random/anton-test-logging/previews/3f77e6b6-287b-4115-8439-8a3e5f328f24

     Type                           Name                              Plan       Info
 +   pulumi:pulumi:Stack            simple-random-anton-test-logging  create     4 errors; 3 warnings
 +   ├─ random:index:RandomInteger  integer                           create     
 +   ├─ random:index:RandomUuid     uuid                              create     
     ├─ random:index:RandomString   string                                       2 errors
 +   └─ random:index:RandomPet      pet                               create     

Diagnostics:
  pulumi:pulumi:Stack (simple-random-anton-test-logging):
    warning: using pulumi-resource-random from $PATH at /Users/anton/code/pulumi-random/bin/pulumi-resource-random
    warning: using pulumi-resource-random from $PATH at /Users/anton/code/pulumi-random/bin/pulumi-resource-random
    warning: resource plugin random is expected to have version >=4.16.8, but has 4.0.0-alpha.0+dev; the wrong version may be on your path, or this may be a bug in the plugin

    error: 
    
             Detected that /Users/anton/code/pulumi-random/bin/pulumi-resource-random exited prematurely.
             This is *always* a bug in the provider. Please report the issue to the provider author as appropriate.
    
    To assist with debugging we have dumped the STDOUT and STDERR streams of the plugin:
    
    panic: FAILUREZ [recovered]
        panic: FAILUREZ
    
    goroutine 27 [running]:
    github.com/pulumi/pulumi-terraform-bridge/v3/pkg/providerserver.(*PanicRecoveringProviderServer).Check.func1()
        /Users/anton/code/pulumi-terraform-bridge/pkg/providerserver/panic_recovering_provider.go:306 +0xc8
    panic({0x1023e15e0?, 0x102779e50?})
        /nix/store/lb8wx7xsk7vryjxbbf9wlj820pdfvnbj-go-1.23.3/share/go/src/runtime/panic.go:785 +0x124
    github.com/pulumi/pulumi-random/provider/v4.Provider.func1({0x14000bbf440?, 0x14000783e00?}, 0x140008bc0c0?, 0x7?)
        /Users/anton/code/pulumi-random/provider/resources.go:69 +0x2c
    github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfbridge.(*provider).CheckWithContext(0x14000a3a388, {0x102799b98?, 0x140007822a0?}, {0x14000720000, 0x5c}, 0x140007822d0, 0x14000782300, 0x0?, {0x140005000c0, 0x20, ...}, ...)
        /Users/anton/code/pulumi-terraform-bridge/pkg/pf/tfbridge/provider_check.go:60 +0x518
    github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/internal/plugin.(*providerServer).Check(0x14000a2bb00, {0x102799b98, 0x140007822a0}, 0x140007a0000)
        /Users/anton/code/pulumi-terraform-bridge/pkg/pf/internal/plugin/provider_server.go:366 +0x20c
    github.com/pulumi/pulumi-terraform-bridge/v3/pkg/providerserver.(*PanicRecoveringProviderServer).Check(0x1035943d0?, {0x102799b98?, 0x140007822a0?}, 0x1024133a0?)
        /Users/anton/code/pulumi-terraform-bridge/pkg/providerserver/panic_recovering_provider.go:309 +0x6c
    github.com/pulumi/pulumi/sdk/v3/proto/go._ResourceProvider_Check_Handler.func1({0x102799b98?, 0x140007822a0?}, {0x1026a33a0?, 0x140007a0000?})
        /Users/anton/go/pkg/mod/github.com/pulumi/pulumi/sdk/[email protected]/proto/go/provider_grpc.pb.go:855 +0xd0
    github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc.OpenTracingServerInterceptor.func1({0x102799b98, 0x140007821b0}, {0x1026a33a0, 0x140007a0000}, 0x1400017c0a0, 0x14000c0a0f0)
        /Users/anton/go/pkg/mod/github.com/grpc-ecosystem/[email protected]/go/otgrpc/server.go:57 +0x2d4
    github.com/pulumi/pulumi/sdk/v3/proto/go._ResourceProvider_Check_Handler({0x10272b000, 0x14000ba6780}, {0x102799b98, 0x140007821b0}, 0x14000284000, 0x14000a267c0)
        /Users/anton/go/pkg/mod/github.com/pulumi/pulumi/sdk/[email protected]/proto/go/provider_grpc.pb.go:857 +0x148
    google.golang.org/grpc.(*Server).processUnaryRPC(0x14000b9c000, {0x102799b98, 0x14000782120}, {0x1027aabe0, 0x140007b8820}, 0x1400078c000, 0x14000a2bb90, 0x1035c0960, 0x0)
        /Users/anton/go/pkg/mod/google.golang.org/[email protected]/server.go:1394 +0xb64
    google.golang.org/grpc.(*Server).handleStream(0x14000b9c000, {0x1027aabe0, 0x140007b8820}, 0x1400078c000)
        /Users/anton/go/pkg/mod/google.golang.org/[email protected]/server.go:1805 +0xb20
    google.golang.org/grpc.(*Server).serveStreams.func2.1()
        /Users/anton/go/pkg/mod/google.golang.org/[email protected]/server.go:1029 +0x84
    created by google.golang.org/grpc.(*Server).serveStreams.func2 in goroutine 51
        /Users/anton/go/pkg/mod/google.golang.org/[email protected]/server.go:1040 +0x13c

  random:index:RandomString (string):
    error: Bridged provider panic (provider=random v=4.0.0-alpha.0+dev resourceURN=urn:pulumi:anton-test-logging::simple-random::random:index/randomString:RandomString::string method=Check): FAILUREZ
    goroutine 27 [running]:
    runtime/debug.Stack()
        /nix/store/lb8wx7xsk7vryjxbbf9wlj820pdfvnbj-go-1.23.3/share/go/src/runtime/debug/stack.go:26 +0x64
    github.com/pulumi/pulumi-terraform-bridge/v3/pkg/providerserver.(*PanicRecoveringProviderServer).Check.func1()
        /Users/anton/code/pulumi-terraform-bridge/pkg/providerserver/panic_recovering_provider.go:303 +0x64
    panic({0x1023e15e0?, 0x102779e50?})
        /nix/store/lb8wx7xsk7vryjxbbf9wlj820pdfvnbj-go-1.23.3/share/go/src/runtime/panic.go:785 +0x124
    github.com/pulumi/pulumi-random/provider/v4.Provider.func1({0x14000bbf440?, 0x14000783e00?}, 0x140008bc0c0?, 0x7?)
        /Users/anton/code/pulumi-random/provider/resources.go:69 +0x2c
    github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfbridge.(*provider).CheckWithContext(0x14000a3a388, {0x102799b98?, 0x140007822a0?}, {0x14000720000, 0x5c}, 0x140007822d0, 0x14000782300, 0x0?, {0x140005000c0, 0x20, ...}, ...)
        /Users/anton/code/pulumi-terraform-bridge/pkg/pf/tfbridge/provider_check.go:60 +0x518
    github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/internal/plugin.(*providerServer).Check(0x14000a2bb00, {0x102799b98, 0x140007822a0}, 0x140007a0000)
        /Users/anton/code/pulumi-terraform-bridge/pkg/pf/internal/plugin/provider_server.go:366 +0x20c
    github.com/pulumi/pulumi-terraform-bridge/v3/pkg/providerserver.(*PanicRecoveringProviderServer).Check(0x1035943d0?, {0x102799b98?, 0x140007822a0?}, 0x1024133a0?)
        /Users/anton/code/pulumi-terraform-bridge/pkg/providerserver/panic_recovering_provider.go:309 +0x6c
    github.com/pulumi/pulumi/sdk/v3/proto/go._ResourceProvider_Check_Handler.func1({0x102799b98?, 0x140007822a0?}, {0x1026a33a0?, 0x140007a0000?})
        /Users/anton/go/pkg/mod/github.com/pulumi/pulumi/sdk/[email protected]/proto/go/provider_grpc.pb.go:855 +0xd0
    github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc.OpenTracingServerInterceptor.func1({0x102799b98, 0x140007821b0}, {0x1026a33a0, 0x140007a0000}, 0x1400017c0a0, 0x14000c0a0f0)
        /Users/anton/go/pkg/mod/github.com/grpc-ecosystem/[email protected]/go/otgrpc/server.go:57 +0x2d4
    github.com/pulumi/pulumi/sdk/v3/proto/go._ResourceProvider_Check_Handler({0x10272b000, 0x14000ba6780}, {0x102799b98, 0x140007821b0}, 0x14000284000, 0x14000a267c0)
        /Users/anton/go/pkg/mod/github.com/pulumi/pulumi/sdk/[email protected]/proto/go/provider_grpc.pb.go:857 +0x148
    google.golang.org/grpc.(*Server).processUnaryRPC(0x14000b9c000, {0x102799b98, 0x14000782120}, {0x1027aabe0, 0x140007b8820}, 0x1400078c000, 0x14000a2bb90, 0x1035c0960, 0x0)
        /Users/anton/go/pkg/mod/google.golang.org/[email protected]/server.go:1394 +0xb64
    google.golang.org/grpc.(*Server).handleStream(0x14000b9c000, {0x1027aabe0, 0x140007b8820}, 0x1400078c000)
        /Users/anton/go/pkg/mod/google.golang.org/[email protected]/server.go:1805 +0xb20
    google.golang.org/grpc.(*Server).serveStreams.func2.1()
        /Users/anton/go/pkg/mod/google.golang.org/[email protected]/server.go:1029 +0x84
    created by google.golang.org/grpc.(*Server).serveStreams.func2 in goroutine 51
        /Users/anton/go/pkg/mod/google.golang.org/[email protected]/server.go:1040 +0x13c
    error: error reading from server: EOF


```
  • Loading branch information
t0yv0 authored Jan 28, 2025
1 parent 93de27b commit a3a15d2
Show file tree
Hide file tree
Showing 11 changed files with 1,180 additions and 31 deletions.
3 changes: 2 additions & 1 deletion dynamic/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"google.golang.org/protobuf/types/known/structpb"

helper "github.com/pulumi/pulumi-terraform-bridge/v3/dynamic/internal/testing"
"github.com/pulumi/pulumi-terraform-bridge/v3/internal/logging"
pfbridge "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfbridge"
)

Expand Down Expand Up @@ -375,7 +376,7 @@ var (
func grpcTestServer(ctx context.Context, t *testing.T) pulumirpc.ResourceProviderServer {
defaultInfo, metadata, close := initialSetup()
t.Cleanup(func() { assert.NoError(t, close()) })
s, err := pfbridge.NewProviderServer(ctx, nil, defaultInfo, metadata)
s, err := pfbridge.NewProviderServer(ctx, logging.NewTestingSink(t), defaultInfo, metadata)
require.NoError(t, err)
return s
}
Expand Down
44 changes: 44 additions & 0 deletions internal/logging/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,47 @@ type logLike interface {
Warn(msg string)
Error(msg string)
}

// Methods required by [NewTestingSink] from *testing.T.
type T interface {
Logf(string, ...any)
}

// Build a Sink that flushes to *testing.T or a similar context.
func NewTestingSink(t T) Sink {
return &testingLogSink{t: t}
}

type testingLogSink struct{ t T }

func (s testingLogSink) Log(_ context.Context, sev diag.Severity, urn resource.URN, msg string) error {
return s.log("LOG", sev, urn, msg)
}

func (s testingLogSink) LogStatus(_ context.Context, sev diag.Severity, urn resource.URN, msg string) error {
return s.log("STATUS", sev, urn, msg)
}

func (s testingLogSink) log(kind string, sev diag.Severity, urn resource.URN, msg string) error {
var urnMsg string
if urn != "" {
urnMsg = " (" + string(urn) + ")"
}
s.t.Logf("Provider[%s]: %s%s: %s", kind, sev, urnMsg, msg)
return nil
}

// This logging sink discards all messages.
func NewDiscardSink() Sink {
return &discardSink{}
}

type discardSink struct{}

func (s discardSink) Log(_ context.Context, sev diag.Severity, urn resource.URN, msg string) error {
return nil
}

func (s discardSink) LogStatus(_ context.Context, sev diag.Severity, urn resource.URN, msg string) error {
return nil
}
3 changes: 2 additions & 1 deletion pkg/pf/tests/internal/cross-tests/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/zclconf/go-cty/cty"
"gopkg.in/yaml.v3"

"github.com/pulumi/pulumi-terraform-bridge/v3/internal/logging"
crosstests "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/cross-tests"
crosstestsimpl "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/cross-tests/impl"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/cross-tests/impl/hclwrite"
Expand Down Expand Up @@ -174,7 +175,7 @@ resource "` + providerName + `_res" "res" {}
require.NoError(t, err)

makeProvider := func(providers.PulumiTest) (pulumirpc.ResourceProviderServer, error) {
ctx, sink := context.Background(), testLogSink{t}
ctx, sink := context.Background(), logging.NewTestingSink(t)

p := info.Provider{
Name: providerName,
Expand Down
23 changes: 0 additions & 23 deletions pkg/pf/tests/internal/cross-tests/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,15 @@
package crosstests

import (
"context"
"os"
"runtime"
"strings"

"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"

crosstestsimpl "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/cross-tests/impl"
)

type T = crosstestsimpl.T

type testLogSink struct{ t T }

func (s testLogSink) Log(_ context.Context, sev diag.Severity, urn resource.URN, msg string) error {
return s.log("LOG", sev, urn, msg)
}

func (s testLogSink) LogStatus(_ context.Context, sev diag.Severity, urn resource.URN, msg string) error {
return s.log("STATUS", sev, urn, msg)
}

func (s testLogSink) log(kind string, sev diag.Severity, urn resource.URN, msg string) error {
var urnMsg string
if urn != "" {
urnMsg = " (" + string(urn) + ")"
}
s.t.Logf("Provider[%s]: %s%s: %s", kind, sev, urnMsg, msg)
return nil
}

func skipUnlessLinux(t T) {
if ci, ok := os.LookupEnv("CI"); ok && ci == "true" && !strings.Contains(strings.ToLower(runtime.GOOS), "linux") {
t.Skip("Skipping on non-Linux platforms as our CI does not yet install Terraform CLI required for these tests")
Expand Down
3 changes: 2 additions & 1 deletion pkg/pf/tests/pulcheck/pulcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/grpc"

"github.com/pulumi/pulumi-terraform-bridge/v3/internal/logging"
crosstestsimpl "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/cross-tests/impl"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfbridge"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfgen"
Expand Down Expand Up @@ -64,7 +65,7 @@ func newProviderServer(t T, info tfbridge0.ProviderInfo) (pulumirpc.ResourceProv
if err != nil {
return nil, err
}
srv, err := tfbridge.NewProviderServer(ctx, nil, info, meta)
srv, err := tfbridge.NewProviderServer(ctx, logging.NewTestingSink(t), info, meta)
require.NoError(t, err)
return srv, nil
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/pf/tests/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
"github.com/stretchr/testify/require"

"github.com/pulumi/pulumi-terraform-bridge/v3/internal/logging"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfbridge"
tfbridge0 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
)
Expand All @@ -31,7 +32,7 @@ func newProviderServer(t *testing.T, info tfbridge0.ProviderInfo) (pulumirpc.Res
if err != nil {
return nil, err
}
srv, err := tfbridge.NewProviderServer(ctx, nil, info, meta)
srv, err := tfbridge.NewProviderServer(ctx, logging.NewTestingSink(t), info, meta)
require.NoError(t, err)
return srv, nil
}
Expand Down
10 changes: 9 additions & 1 deletion pkg/pf/tfbridge/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
pl "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/internal/plugin"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/internal/runtypes"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/internal/schemashim"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/providerserver"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
)
Expand Down Expand Up @@ -216,7 +217,14 @@ func NewProviderServer(
}

p.Unwrap().logSink = logSink
return pl.NewProviderServerWithContext(p), nil
srv := pl.NewProviderServerWithContext(p)

return providerserver.NewPanicRecoveringProviderServer(&providerserver.PanicRecoveringProviderServerOptions{
Logger: logSink,
ResourceProviderServer: srv,
ProviderName: info.Name,
ProviderVersion: info.Version,
}), nil
}

// Closer closes any underlying OS resources associated with this provider (like processes, RPC channels, etc).
Expand Down
Loading

0 comments on commit a3a15d2

Please sign in to comment.