diff --git a/example_test.go b/example_test.go index e913a65..d178832 100644 --- a/example_test.go +++ b/example_test.go @@ -20,11 +20,13 @@ import ( "log" "time" + "google.golang.org/grpc/credentials" + "contrib.go.opencensus.io/exporter/ocagent" "go.opencensus.io/trace" ) -func Example() { +func Example_insecure() { exp, err := ocagent.NewExporter(ocagent.WithInsecure(), ocagent.WithServiceName("engine")) if err != nil { log.Fatalf("Failed to create the agent exporter: %v", err) @@ -44,3 +46,31 @@ func Example() { iSpan.End() } } + +func Example_withTLS() { + // Please take at look at https://godoc.org/google.golang.org/grpc/credentials#TransportCredentials + // for ways on how to initialize gRPC TransportCredentials. + creds, err := credentials.NewClientTLSFromFile("my-cert.pem", "") + if err != nil { + log.Fatalf("Failed to create gRPC client TLS credentials: %v", err) + } + + exp, err := ocagent.NewExporter(ocagent.WithTLSCredentials(creds), ocagent.WithServiceName("engine")) + if err != nil { + log.Fatalf("Failed to create the agent exporter: %v", err) + } + defer exp.Stop() + + // Now register it as a trace exporter. + trace.RegisterExporter(exp) + + // Then use the OpenCensus tracing library, like we normally would. + ctx, span := trace.StartSpan(context.Background(), "Securely-Talking-To-Agent-Span") + defer span.End() + + for i := 0; i < 10; i++ { + _, iSpan := trace.StartSpan(ctx, fmt.Sprintf("Sample-%d", i)) + <-time.After(6 * time.Millisecond) + iSpan.End() + } +} diff --git a/ocagent.go b/ocagent.go index 294d9b2..75eb50b 100644 --- a/ocagent.go +++ b/ocagent.go @@ -23,6 +23,7 @@ import ( "google.golang.org/api/support/bundler" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" "go.opencensus.io/resource" @@ -82,6 +83,8 @@ type Exporter struct { // from OpenCensus-Go view.Data to metricspb.Metric. // Please do not confuse it with metricsBundler! viewDataBundler *bundler.Bundler + + clientTransportCredentials credentials.TransportCredentials } func NewExporter(opts ...ExporterOption) (*Exporter, error) { @@ -257,7 +260,9 @@ func (ae *Exporter) createMetricsServiceConnection(cc *grpc.ClientConn, node *co func (ae *Exporter) dialToAgent() (*grpc.ClientConn, error) { addr := ae.prepareAgentAddress() var dialOpts []grpc.DialOption - if ae.canDialInsecure { + if ae.clientTransportCredentials != nil { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(ae.clientTransportCredentials)) + } else if ae.canDialInsecure { dialOpts = append(dialOpts, grpc.WithInsecure()) } if ae.compressor != "" { diff --git a/options.go b/options.go index 507ad96..3e05ae8 100644 --- a/options.go +++ b/options.go @@ -14,7 +14,11 @@ package ocagent -import "time" +import ( + "time" + + "google.golang.org/grpc/credentials" +) const ( DefaultAgentPort uint16 = 55678 @@ -103,3 +107,22 @@ func (h headerSetter) withExporter(e *Exporter) { func WithHeaders(headers map[string]string) ExporterOption { return headerSetter(headers) } + +type clientCredentials struct { + credentials.TransportCredentials +} + +var _ ExporterOption = (*clientCredentials)(nil) + +// WithTLSCredentials allows the connection to use TLS credentials +// when talking to the server. It takes in grpc.TransportCredentials instead +// of say a Certificate file or a tls.Certificate, because the retrieving +// these credentials can be done in many ways e.g. plain file, in code tls.Config +// or by certificate rotation, so it is up to the caller to decide what to use. +func WithTLSCredentials(creds credentials.TransportCredentials) ExporterOption { + return &clientCredentials{TransportCredentials: creds} +} + +func (cc *clientCredentials) withExporter(e *Exporter) { + e.clientTransportCredentials = cc.TransportCredentials +}