pulumi/pkg/resource/provider/main.go
Luke Hoban af5298f4aa
Initial work on tracing support (#521)
Adds OpenTracing in the Pulumi engine and plugin + langhost subprocesses.

We currently create a single root span for any `Enging.plan` operation - which is a single `preview`, `update`, `destroy`, etc.

The only sub-spans we currently create are at gRPC boundaries, both on the client and server sides and on both the langhost and provider plugin interfaces.

We could extend this to include spans for any other semantically meaningful sections of compute inside the engine, though initial examples show we get pretty good granularity of coverage by focusing on the gRPC boundaries.

In the future, this should be easily extensible to HTTP boundaries and to track other bulky I/O like datastore read/writes once we hook up to the PPC and Pulumi Cloud.

We expose a `--trace <endpoint>` option to enable tracing on the CLI, which we will aim to thread through to subprocesses.

We currently support sending tracing data to a Zipkin-compatible endpoint.  This has been validated with both Zipkin and Jaeger UIs.

We do not yet have any tracing inside the TypeScript side of the JS langhost RPC interface.  There is not yet automatic gRPC OpenTracing instrumentation (though it looks like it's in progress now) - so we would need to manually create meaningful spans on that side of the interface.
2017-11-08 17:08:51 -08:00

66 lines
2 KiB
Go

// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
package provider
import (
"flag"
"fmt"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/util/cmdutil"
"github.com/pulumi/pulumi/pkg/util/rpcutil"
lumirpc "github.com/pulumi/pulumi/sdk/proto/go"
"google.golang.org/grpc"
)
// Tracing is the optional command line flag passed to this provider for configuring a Zipkin-compatible tracing
// endpoint
var tracing string
// Main is the typical entrypoint for a resource provider plugin. Using it isn't required but can cut down
// significantly on the amount of boilerplate necessary to fire up a new resource provider.
func Main(name string, provMaker func(*HostClient) (lumirpc.ResourceProviderServer, error)) error {
flag.StringVar(&tracing, "tracing", "", "Emit tracing to a Zipkin-compatible tracing endpoint")
flag.Parse()
// Initialize loggers before going any further.
cmdutil.InitLogging(false, 0, false)
cmdutil.InitTracing(name, tracing)
// Read the non-flags args and connect to the engine.
args := flag.Args()
if len(args) == 0 {
return errors.New("fatal: could not connect to host RPC; missing argument")
}
host, err := NewHostClient(args[0])
if err != nil {
return errors.Errorf("fatal: could not connect to host RPC: %v", err)
}
// Fire up a gRPC server, letting the kernel choose a free port for us.
port, done, err := rpcutil.Serve(0, nil, []func(*grpc.Server) error{
func(srv *grpc.Server) error {
prov, proverr := provMaker(host)
if proverr != nil {
return fmt.Errorf("failed to create resource provider: %v", proverr)
}
lumirpc.RegisterResourceProviderServer(srv, prov)
return nil
},
})
if err != nil {
return errors.Errorf("fatal: %v", err)
}
// The resource provider protocol requires that we now write out the port we have chosen to listen on.
fmt.Printf("%d\n", port)
// Finally, wait for the server to stop serving.
if err := <-done; err != nil {
return errors.Errorf("fatal: %v", err)
}
return nil
}