From c6c841fc7c11dcda5929bdeafa867af0b20c1892 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Fri, 8 May 2020 14:25:20 +0200 Subject: [PATCH 001/108] add simple demo for connecting to collector --- example/otel-collector/main.go | 54 +++++++++++ example/otel-collector/otel-coll.yaml | 125 ++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 example/otel-collector/main.go create mode 100644 example/otel-collector/otel-coll.yaml diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go new file mode 100644 index 000000000..f65489430 --- /dev/null +++ b/example/otel-collector/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "context" + "fmt" + "log" + "time" + + "go.opentelemetry.io/otel/api/global" + "go.opentelemetry.io/otel/exporters/otlp" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "google.golang.org/grpc" +) + +func main() { + exp, err := otlp.NewExporter(otlp.WithInsecure(), + otlp.WithGRPCDialOption(grpc.WithBlock())) + if err != nil { + log.Fatalf("Failed to create the collector exporter: %v", err) + } + if err = exp.GetLastConnectError(); err != nil { + log.Fatalf("Failed to create the collector exporter: %v", err) + } + + defer func() { + + _ = exp.Stop() + }() + + tp, _ := sdktrace.NewProvider( + sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), + sdktrace.WithBatcher(exp, // add following two options to ensure flush + sdktrace.WithScheduleDelayMillis(5), + sdktrace.WithMaxExportBatchSize(1), + )) + if err != nil { + log.Fatalf("error creating trace provider: %v\n", err) + } + + global.SetTraceProvider(tp) + tracer := global.Tracer("test-tracer") + + // Then use the OpenTelemetry tracing library, like we normally would. + ctx, span := tracer.Start(context.Background(), "CollectorExporter-Example") + defer span.End() + + for i := 0; i < 10; i++ { + _, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i)) + <-time.After(6 * time.Second) + iSpan.End() + } + + // exp.ExportSpan(ctx, span) +} diff --git a/example/otel-collector/otel-coll.yaml b/example/otel-collector/otel-coll.yaml new file mode 100644 index 000000000..1708042de --- /dev/null +++ b/example/otel-collector/otel-coll.yaml @@ -0,0 +1,125 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: otel-collector-conf + labels: + app: opentelemetry + component: otel-collector-conf +data: + otel-collector-config: | + receivers: + otlp: {} + processors: + queued_retry: + num_workers: 16 + queue_size: 10000 + retry_on_failure: true + batch: + send_batch_size: 1024 + timeout: 5s + extensions: + health_check: {} + exporters: + jaeger: + endpoint: "simplest-collector.default.svc.cluster.local:14250" # Replace with a real endpoint. + service: + extensions: [health_check] + pipelines: + traces/2: + receivers: [otlp] + processors: [] + exporters: [jaeger] +--- +apiVersion: v1 +kind: Service +metadata: + name: otel-collector + labels: + app: opencensus + component: otel-collector +spec: + ports: + - name: otlp # Default endpoint for Opencensus receiver. + port: 55680 + protocol: TCP + targetPort: 55680 + - name: metrics # Default endpoint for querying metrics. + port: 8888 + selector: + component: otel-collector + type: + NodePort +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: otel-collector + labels: + app: opentelemetry + component: otel-collector +spec: + selector: + matchLabels: + app: opentelemetry + component: otel-collector + minReadySeconds: 5 + progressDeadlineSeconds: 120 + replicas: 1 #TODO - adjust this to your own requirements + template: + metadata: + annotations: + prometheus.io/path: "/metrics" + prometheus.io/port: "8888" + prometheus.io/scrape: "true" + labels: + app: opentelemetry + component: otel-collector + spec: + containers: + - command: + - "/otelcol" + - "--config=/conf/otel-collector-config.yaml" +# Memory Ballast size should be max 1/3 to 1/2 of memory. + - "--mem-ballast-size-mib=683" + env: + - name: GOGC + value: "80" + image: otel/opentelemetry-collector-dev:latest + name: otel-collector + resources: + limits: + cpu: 1 + memory: 2Gi + requests: + cpu: 200m + memory: 400Mi + ports: + - containerPort: 55680 # Default endpoint for otlp receiver. + - containerPort: 8888 # Default endpoint for querying metrics. + volumeMounts: + - name: otel-collector-config-vol + mountPath: /conf +# - name: otel-collector-secrets +# mountPath: /secrets + livenessProbe: + httpGet: + path: / + port: 13133 # Health Check extension default port. + readinessProbe: + httpGet: + path: / + port: 13133 # Health Check extension default port. + volumes: + - configMap: + name: otel-collector-conf + items: + - key: otel-collector-config + path: otel-collector-config.yaml + name: otel-collector-config-vol +# - secret: +# name: otel-collector-secrets +# items: +# - key: cert.pem +# path: cert.pem +# - key: key.pem +# path: key.pem From a27a37ee34674e50375e6420450a7b3f2bc62fb6 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Fri, 8 May 2020 14:34:29 +0200 Subject: [PATCH 002/108] Update main.go --- example/otel-collector/main.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index f65489430..5a1b1558c 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -18,12 +18,7 @@ func main() { if err != nil { log.Fatalf("Failed to create the collector exporter: %v", err) } - if err = exp.GetLastConnectError(); err != nil { - log.Fatalf("Failed to create the collector exporter: %v", err) - } - defer func() { - _ = exp.Stop() }() @@ -49,6 +44,4 @@ func main() { <-time.After(6 * time.Second) iSpan.End() } - - // exp.ExportSpan(ctx, span) } From 6bfa16ecef9e41bcafbe4c4bd2f822bd7c2337df Mon Sep 17 00:00:00 2001 From: Ahmed Mujtaba Date: Sat, 2 May 2020 23:56:08 +0200 Subject: [PATCH 003/108] Added test case for grpc UrinaryInterceptorClient --- plugin/grpctrace/interceptor_test.go | 141 ++++++++++++--------------- 1 file changed, 65 insertions(+), 76 deletions(-) diff --git a/plugin/grpctrace/interceptor_test.go b/plugin/grpctrace/interceptor_test.go index d9360341d..8c5ec6f69 100644 --- a/plugin/grpctrace/interceptor_test.go +++ b/plugin/grpctrace/interceptor_test.go @@ -20,6 +20,8 @@ import ( "google.golang.org/grpc" + "go.opentelemetry.io/otel/api/core" + "go.opentelemetry.io/otel/api/global" export "go.opentelemetry.io/otel/sdk/export/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) @@ -32,110 +34,97 @@ func (t *testExporter) ExportSpan(ctx context.Context, s *export.SpanData) { t.spanMap[s.Name] = append(t.spanMap[s.Name], s) } -type mockCCInvoker struct { +type mockUICInvoker struct { ctx context.Context } -func (mcci *mockCCInvoker) invoke(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error { - mcci.ctx = ctx +func (mcuici *mockUICInvoker) invoker(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error { + mcuici.ctx = ctx return nil } -type mockProtoMessage struct { +type mockProtoMessage struct{} + +func (mm *mockProtoMessage) Reset() { } -func (mm *mockProtoMessage) Reset() {} -func (mm *mockProtoMessage) String() string { return "mock" } -func (mm *mockProtoMessage) ProtoMessage() {} - -type nameAttributeTestCase struct { - testName string - expectedName string - fullNameFmt string +func (mm *mockProtoMessage) String() string { + return "mock" } -func (tc nameAttributeTestCase) fullName() string { - return fmt.Sprintf(tc.fullNameFmt, tc.expectedName) +func (mm *mockProtoMessage) ProtoMessage() { } -func TestUCISetsExpectedServiceNameAttribute(t *testing.T) { - testCases := []nameAttributeTestCase{ - { - "FullyQualifiedMethodName", - "serviceName", - "/github.com.foo.%s/bar", - }, - { - "SimpleMethodName", - "serviceName", - "/%s/bar", - }, - { - "MethodNameWithoutFullPath", - "serviceName", - "%s/bar", - }, - { - "InvalidMethodName", - "", - "invalidName", - }, - { - "NonAlphanumericMethodName", - "serviceName_123", - "/github.com.foo.%s/method", - }, - } - - for _, tc := range testCases { - t.Run(tc.testName, tc.testUCISetsExpectedNameAttribute) - } -} - -func (tc nameAttributeTestCase) testUCISetsExpectedNameAttribute(t *testing.T) { +func TestUnaryClientInterceptor(t *testing.T) { exp := &testExporter{make(map[string][]*export.SpanData)} tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), - sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()})) - - tr := tp.Tracer("grpctrace/client") - ctx, span := tr.Start(context.Background(), tc.testName) - defer span.End() + sdktrace.WithConfig(sdktrace.Config{ + DefaultSampler: sdktrace.AlwaysSample(), + }, + )) + global.SetTraceProvider(tp) clientConn, err := grpc.Dial("fake:connection", grpc.WithInsecure()) - if err != nil { t.Fatalf("failed to create client connection: %v", err) } - unaryInt := UnaryClientInterceptor(tr) + tracer := tp.Tracer("grpctrace/client") + unaryInterceptor := UnaryClientInterceptor(tracer) req := &mockProtoMessage{} reply := &mockProtoMessage{} - ccInvoker := &mockCCInvoker{} + uniInterceptorInvoker := &mockUICInvoker{} - err = unaryInt(ctx, tc.fullName(), req, reply, clientConn, ccInvoker.invoke) - if err != nil { - t.Fatalf("failed to run unary interceptor: %v", err) + checks := []struct { + name string + expectedAttr map[core.Key]core.Value + eventsAttr [][]core.KeyValue + }{ + { + name: fmt.Sprintf("/foo.%s/bar", "serviceName"), + expectedAttr: map[core.Key]core.Value{ + rpcServiceKey: core.String("serviceName"), + netPeerIPKey: core.String("fake"), + netPeerPortKey: core.String("connection"), + }, + eventsAttr: [][]core.KeyValue{ + { + core.KeyValue{Key: messageTypeKey, Value: core.String("SENT")}, + core.KeyValue{Key: messageIDKey, Value: core.Int(1)}, + }, + { + core.KeyValue{Key: messageTypeKey, Value: core.String("RECEIVED")}, + core.KeyValue{Key: messageIDKey, Value: core.Int(1)}, + }, + }, + }, } - spanData, hasSpanData := exp.spanMap[tc.fullName()] + for _, check := range checks { + err = unaryInterceptor(context.Background(), check.name, req, reply, clientConn, uniInterceptorInvoker.invoker) + if err != nil { + t.Fatalf("failed to run unary interceptor: %v", err) + } - if !hasSpanData || len(spanData) == 0 { - t.Fatalf("no span data found for name < %s >", tc.fullName()) - } + attrs := exp.spanMap[check.name][0].Attributes + for _, attr := range attrs { + expectedAttr, ok := check.expectedAttr[attr.Key] + if ok { + if expectedAttr != attr.Value { + t.Fatalf("invalid %s found. expected %s, actual %s", string(attr.Key), + expectedAttr.AsString(), attr.Value.AsString()) + } + } + } - attributes := spanData[0].Attributes - - var actualServiceName string - for _, attr := range attributes { - if attr.Key == rpcServiceKey { - actualServiceName = attr.Value.AsString() - break + events := exp.spanMap[check.name][0].MessageEvents + for event := 0; event < len(check.eventsAttr); event++ { + for attr := 0; attr < len(check.eventsAttr[event]); attr++ { + if events[event].Attributes[attr] != check.eventsAttr[event][attr] { + t.Fatalf("invalid attribute in events") + } + } } } - - if tc.expectedName != actualServiceName { - t.Fatalf("invalid service name found. expected %s, actual %s", - tc.expectedName, actualServiceName) - } } From 02ff1be72ccc4447c17b88816c20d71d43536419 Mon Sep 17 00:00:00 2001 From: Ahmed Mujtaba Date: Tue, 5 May 2020 22:50:01 +0200 Subject: [PATCH 004/108] Minor fixes and improvment in GRPC urinary interceptor test --- plugin/grpctrace/interceptor_test.go | 82 ++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/plugin/grpctrace/interceptor_test.go b/plugin/grpctrace/interceptor_test.go index 8c5ec6f69..fac424933 100644 --- a/plugin/grpctrace/interceptor_test.go +++ b/plugin/grpctrace/interceptor_test.go @@ -21,7 +21,6 @@ import ( "google.golang.org/grpc" "go.opentelemetry.io/otel/api/core" - "go.opentelemetry.io/otel/api/global" export "go.opentelemetry.io/otel/sdk/export/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) @@ -62,7 +61,6 @@ func TestUnaryClientInterceptor(t *testing.T) { DefaultSampler: sdktrace.AlwaysSample(), }, )) - global.SetTraceProvider(tp) clientConn, err := grpc.Dial("fake:connection", grpc.WithInsecure()) if err != nil { @@ -79,50 +77,102 @@ func TestUnaryClientInterceptor(t *testing.T) { checks := []struct { name string expectedAttr map[core.Key]core.Value - eventsAttr [][]core.KeyValue + eventsAttr []map[core.Key]core.Value }{ { - name: fmt.Sprintf("/foo.%s/bar", "serviceName"), + name: "/github.com.serviceName/bar", expectedAttr: map[core.Key]core.Value{ rpcServiceKey: core.String("serviceName"), netPeerIPKey: core.String("fake"), netPeerPortKey: core.String("connection"), }, - eventsAttr: [][]core.KeyValue{ + eventsAttr: []map[core.Key]core.Value{ { - core.KeyValue{Key: messageTypeKey, Value: core.String("SENT")}, - core.KeyValue{Key: messageIDKey, Value: core.Int(1)}, + messageTypeKey: core.String("SENT"), + messageIDKey: core.Int(1), }, { - core.KeyValue{Key: messageTypeKey, Value: core.String("RECEIVED")}, - core.KeyValue{Key: messageIDKey, Value: core.Int(1)}, + messageTypeKey: core.String("RECEIVED"), + messageIDKey: core.Int(1), }, }, }, + { + name: "/serviceName/bar", + expectedAttr: map[core.Key]core.Value{ + rpcServiceKey: core.String("serviceName"), + }, + eventsAttr: []map[core.Key]core.Value{ + { + messageTypeKey: core.String("SENT"), + messageIDKey: core.Int(1), + }, + { + messageTypeKey: core.String("RECEIVED"), + messageIDKey: core.Int(1), + }, + }, + }, + { + name: "serviceName/bar", + expectedAttr: map[core.Key]core.Value{rpcServiceKey: core.String("serviceName")}, + }, + { + name: "invalidName", + expectedAttr: map[core.Key]core.Value{rpcServiceKey: core.String("")}, + }, + { + name: "/github.com.foo.serviceName_123/method", + expectedAttr: map[core.Key]core.Value{rpcServiceKey: core.String("serviceName_123")}, + }, } - for _, check := range checks { + for idx, check := range checks { + fmt.Println("================", idx, "==================") err = unaryInterceptor(context.Background(), check.name, req, reply, clientConn, uniInterceptorInvoker.invoker) if err != nil { t.Fatalf("failed to run unary interceptor: %v", err) } - attrs := exp.spanMap[check.name][0].Attributes + spanData, ok := exp.spanMap[check.name] + if !ok || len(spanData) == 0 { + t.Fatalf("no span data found for name < %s >", check.name) + } + + attrs := spanData[0].Attributes for _, attr := range attrs { expectedAttr, ok := check.expectedAttr[attr.Key] if ok { if expectedAttr != attr.Value { - t.Fatalf("invalid %s found. expected %s, actual %s", string(attr.Key), + t.Errorf("name: %s invalid %s found. expected %s, actual %s", check.name, string(attr.Key), expectedAttr.AsString(), attr.Value.AsString()) } + delete(check.expectedAttr, attr.Key) } } - events := exp.spanMap[check.name][0].MessageEvents + // Check if any expected attr not seen + if len(check.expectedAttr) > 0 { + for attr := range check.expectedAttr { + t.Errorf("missing attribute %s in span", string(attr)) + } + } + + events := spanData[0].MessageEvents for event := 0; event < len(check.eventsAttr); event++ { - for attr := 0; attr < len(check.eventsAttr[event]); attr++ { - if events[event].Attributes[attr] != check.eventsAttr[event][attr] { - t.Fatalf("invalid attribute in events") + for _, attr := range events[event].Attributes { + expectedAttr, ok := check.eventsAttr[event][attr.Key] + if ok { + if attr.Value != expectedAttr { + t.Errorf("invalid value for attribute %s in events, expected %s actual %s", + string(attr.Key), attr.Value.AsString(), expectedAttr.AsString()) + } + delete(check.eventsAttr[event], attr.Key) + } + } + if len(check.eventsAttr[event]) > 0 { + for attr := range check.eventsAttr[event] { + t.Errorf("missing attribute %s in span event", string(attr)) } } } From 8921ddfe2529c31bac38c75cb9dfc43492361edd Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Mon, 11 May 2020 12:10:12 +0200 Subject: [PATCH 005/108] Add readme to the example --- example/otel-collector/README.md | 80 +++++++++++ example/otel-collector/go.mod | 9 ++ example/otel-collector/go.sum | 95 +++++++++++++ example/otel-collector/main.go | 24 +++- example/otel-collector/otel-coll.yaml | 125 ------------------ .../otel-collector/otel-collector-config.yaml | 27 ++++ 6 files changed, 229 insertions(+), 131 deletions(-) create mode 100644 example/otel-collector/README.md create mode 100644 example/otel-collector/go.mod create mode 100644 example/otel-collector/go.sum delete mode 100644 example/otel-collector/otel-coll.yaml create mode 100644 example/otel-collector/otel-collector-config.yaml diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md new file mode 100644 index 000000000..18f7095a7 --- /dev/null +++ b/example/otel-collector/README.md @@ -0,0 +1,80 @@ +# OpenTelemetry Collector Traces Example + +This example illustrates how to export traces from the otel-go sdk to the Open Telemetry Collector, and from there to a Jaeger instance. +The complete flow is: + +`otel-collector-demo -> otel-collector -> Jaeger` + +# Prerequisites + +The demo assumes you already have both OpenTelemetry Collector and Jaeger up and running. For setting these up, please follow the corresponding documentations: +* Jaeger: https://www.jaegertracing.io/docs/1.17/getting-started/ +* OpenTelemetry Collector: https://opentelemetry.io/docs/collector/about/ + +Moreover, this demo is build against a microk8s cluster runnning both the OpenTelemetry Collector, and Jaeger. Therefor, the OpenTelemetry Collector configuration illustrated is a K8S ConfgurationMap. But the gist of it is there, so it shouldn't matter too much. + +# Configuring the OTEL Collector + +In order to enable our application to send traces to the OpenTelemetry Collector, we need to first open up the OTLP receiver: + +```yml +receivers: + otlp: {} +``` + +This will create the receiver on the collector side, and open up port `55680` for receiving traces. +The rest of the configuration is quite standard, with the only mention that we need to create the Jaeger exporter: +```yml +exporters: + jaeger: + # Replace with a real endpoint. + endpoint: "jaeger-service-url:14250" +``` + +After this, apply the configuration to your OpenTelemetry Collector instance (with `k apply -f otel-controller-config.yaml` for k8s users). You should see that the collector creates the otlp receiver: +```json +{"level":"info","ts":1589184143.206609,"caller":"builder/receivers_builder.go:79","msg":"Receiver started.","component_kind":"receiver","component_type":"otlp","component_name":"otlp"} +``` +and the Jaeger exporter: +```json +{"level":"info","ts":1589184143.1535392,"caller":"builder/exporters_builder.go:94","msg":"Exporter started.","component_kind":"exporter","component_type":"jaeger","component_name":"jaeger"} +``` + +# Writing the demo + +Having the OpenTelemetry Collector started with the OTLP port open for traces, and connected to Jaeger, let's look at the go app that will send traces to the Collector. + +First, we need to create an exporter using the otlp package: +```go +exp, _ := otlp.NewExporter(otlp.WithInsecure(), + // For debug purposes + otlp.WithGRPCDialOption(grpc.WithBlock())) +defer func() { + _ = exp.Stop() + }() +``` +Feel free to remove the blocking operation, but it might come in handy when testing the connection. Also, make sure to close the exporter before the app exits. + +The next steps are the same as for all other otel-go sdk uses: +1) Create a trace provider from the `otlp` exporter: +```go +tp, _ := sdktrace.NewProvider( + sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), + sdktrace.WithBatcher(exp, // add following two options to ensure flush + sdktrace.WithScheduleDelayMillis(5), + sdktrace.WithMaxExportBatchSize(2), + )) +``` + +2) Start sending traces: +```go +tracer := tp.Tracer("test-tracer") +ctx, span := tracer.Start(context.Background(), "CollectorExporter-Example") + defer span.End() +``` + +The traces should now be visible from the Jaeger UI (if you have it installed). + +# Notes + +* There is an issue with the exporter/collector which causes Jaeger to throw errors when receiving spans from the OpenTelemetry Collector: https://github.com/open-telemetry/opentelemetry-collector/issues/815 diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod new file mode 100644 index 000000000..0a9509eab --- /dev/null +++ b/example/otel-collector/go.mod @@ -0,0 +1,9 @@ +module go.opentelemetry.io/otel/example/otel-collector + +go 1.14 + +require ( + go.opentelemetry.io/otel v0.4.3 + go.opentelemetry.io/otel/exporters/otlp v0.4.3 + google.golang.org/grpc v1.29.1 +) diff --git a/example/otel-collector/go.sum b/example/otel-collector/go.sum new file mode 100644 index 000000000..58faba930 --- /dev/null +++ b/example/otel-collector/go.sum @@ -0,0 +1,95 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= +github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= +github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= +github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/open-telemetry/opentelemetry-proto v0.3.0 h1:+ASAtcayvoELyCF40+rdCMlBOhZIn5TPDez85zSYc30= +github.com/open-telemetry/opentelemetry-proto v0.3.0/go.mod h1:PMR5GI0F7BSpio+rBGFxNm6SLzg3FypDTcFuQZnO+F8= +github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +go.opentelemetry.io v0.1.0 h1:EANZoRCOP+A3faIlw/iN6YEWoYb1vleZRKm1EvH8T48= +go.opentelemetry.io/otel v0.4.3 h1:CroUX/0O1ZDcF0iWOO8gwYFWb5EbdSF0/C1yosO+Vhs= +go.opentelemetry.io/otel v0.4.3/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek= +go.opentelemetry.io/otel/exporters/otlp v0.4.3 h1:n0zV9impmvdavDnr5uBiza+P9D1AfkcfUvuTWogMY2w= +go.opentelemetry.io/otel/exporters/otlp v0.4.3/go.mod h1:h51N+tR0tmfiF05zFB13vaiROHSIUm7AuFetkY8T4GY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0 h1:2mqDk8w/o6UmeUCu5Qiq2y7iMf6anbx+YA8d1JFoFrs= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8= +google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index 5a1b1558c..cbf2a6be2 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -1,3 +1,18 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Example from otlp package: https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp?tab=doc#example-package-Insecure package main import ( @@ -6,7 +21,6 @@ import ( "log" "time" - "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/exporters/otlp" sdktrace "go.opentelemetry.io/otel/sdk/trace" "google.golang.org/grpc" @@ -26,14 +40,12 @@ func main() { sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), sdktrace.WithBatcher(exp, // add following two options to ensure flush sdktrace.WithScheduleDelayMillis(5), - sdktrace.WithMaxExportBatchSize(1), + sdktrace.WithMaxExportBatchSize(2), )) if err != nil { log.Fatalf("error creating trace provider: %v\n", err) } - - global.SetTraceProvider(tp) - tracer := global.Tracer("test-tracer") + tracer := tp.Tracer("test-tracer") // Then use the OpenTelemetry tracing library, like we normally would. ctx, span := tracer.Start(context.Background(), "CollectorExporter-Example") @@ -41,7 +53,7 @@ func main() { for i := 0; i < 10; i++ { _, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i)) - <-time.After(6 * time.Second) + <-time.After(time.Second) iSpan.End() } } diff --git a/example/otel-collector/otel-coll.yaml b/example/otel-collector/otel-coll.yaml deleted file mode 100644 index 1708042de..000000000 --- a/example/otel-collector/otel-coll.yaml +++ /dev/null @@ -1,125 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: otel-collector-conf - labels: - app: opentelemetry - component: otel-collector-conf -data: - otel-collector-config: | - receivers: - otlp: {} - processors: - queued_retry: - num_workers: 16 - queue_size: 10000 - retry_on_failure: true - batch: - send_batch_size: 1024 - timeout: 5s - extensions: - health_check: {} - exporters: - jaeger: - endpoint: "simplest-collector.default.svc.cluster.local:14250" # Replace with a real endpoint. - service: - extensions: [health_check] - pipelines: - traces/2: - receivers: [otlp] - processors: [] - exporters: [jaeger] ---- -apiVersion: v1 -kind: Service -metadata: - name: otel-collector - labels: - app: opencensus - component: otel-collector -spec: - ports: - - name: otlp # Default endpoint for Opencensus receiver. - port: 55680 - protocol: TCP - targetPort: 55680 - - name: metrics # Default endpoint for querying metrics. - port: 8888 - selector: - component: otel-collector - type: - NodePort ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: otel-collector - labels: - app: opentelemetry - component: otel-collector -spec: - selector: - matchLabels: - app: opentelemetry - component: otel-collector - minReadySeconds: 5 - progressDeadlineSeconds: 120 - replicas: 1 #TODO - adjust this to your own requirements - template: - metadata: - annotations: - prometheus.io/path: "/metrics" - prometheus.io/port: "8888" - prometheus.io/scrape: "true" - labels: - app: opentelemetry - component: otel-collector - spec: - containers: - - command: - - "/otelcol" - - "--config=/conf/otel-collector-config.yaml" -# Memory Ballast size should be max 1/3 to 1/2 of memory. - - "--mem-ballast-size-mib=683" - env: - - name: GOGC - value: "80" - image: otel/opentelemetry-collector-dev:latest - name: otel-collector - resources: - limits: - cpu: 1 - memory: 2Gi - requests: - cpu: 200m - memory: 400Mi - ports: - - containerPort: 55680 # Default endpoint for otlp receiver. - - containerPort: 8888 # Default endpoint for querying metrics. - volumeMounts: - - name: otel-collector-config-vol - mountPath: /conf -# - name: otel-collector-secrets -# mountPath: /secrets - livenessProbe: - httpGet: - path: / - port: 13133 # Health Check extension default port. - readinessProbe: - httpGet: - path: / - port: 13133 # Health Check extension default port. - volumes: - - configMap: - name: otel-collector-conf - items: - - key: otel-collector-config - path: otel-collector-config.yaml - name: otel-collector-config-vol -# - secret: -# name: otel-collector-secrets -# items: -# - key: cert.pem -# path: cert.pem -# - key: key.pem -# path: key.pem diff --git a/example/otel-collector/otel-collector-config.yaml b/example/otel-collector/otel-collector-config.yaml new file mode 100644 index 000000000..278fc32c3 --- /dev/null +++ b/example/otel-collector/otel-collector-config.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: otel-collector-conf + labels: + app: opentelemetry + component: otel-collector-conf +data: + otel-collector-config: | + receivers: + # Make sure to add the otlp receiver. + # This will use the default configuration, and open up the receiver on port 55680 + otlp: {} + processors: + extensions: + health_check: {} + exporters: + jaeger: + # Replace with a real endpoint. + endpoint: "simplest-collector.default.svc.cluster.local:14250" + service: + extensions: [health_check] + pipelines: + traces/2: + receivers: [otlp] + processors: [] + exporters: [jaeger] \ No newline at end of file From 74347efaaa241867f8b47a2f75f9425a66a86f46 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Mon, 11 May 2020 12:17:41 +0200 Subject: [PATCH 006/108] Mention default config --- example/otel-collector/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 18f7095a7..23eabf320 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -53,6 +53,8 @@ defer func() { _ = exp.Stop() }() ``` +This will initialize the exporter with the default configuration. In this configuration, it will try to connect to an otlp receiver at the address `localhost:55680`. If your OpenTelemetry Collector is running at a different address, use the [otlp.WithAddress](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp?tab=doc#WithAddress) function to change the default address. + Feel free to remove the blocking operation, but it might come in handy when testing the connection. Also, make sure to close the exporter before the app exits. The next steps are the same as for all other otel-go sdk uses: From be01af5ea391781e751d25ec2e27145e76b8b31c Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Mon, 11 May 2020 12:21:40 +0200 Subject: [PATCH 007/108] small rephrasing --- example/otel-collector/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 23eabf320..749a9d144 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -22,16 +22,16 @@ receivers: otlp: {} ``` -This will create the receiver on the collector side, and open up port `55680` for receiving traces. +This will create the receiver on the Collector side, and open up port `55680` for receiving traces. The rest of the configuration is quite standard, with the only mention that we need to create the Jaeger exporter: ```yml exporters: jaeger: # Replace with a real endpoint. - endpoint: "jaeger-service-url:14250" + endpoint: ":14250" ``` -After this, apply the configuration to your OpenTelemetry Collector instance (with `k apply -f otel-controller-config.yaml` for k8s users). You should see that the collector creates the otlp receiver: +After this, apply the configuration to your OpenTelemetry Collector instance (with `k apply -f otel-controller-config.yaml` for k8s users). You should see that the Collector creates the otlp receiver: ```json {"level":"info","ts":1589184143.206609,"caller":"builder/receivers_builder.go:79","msg":"Receiver started.","component_kind":"receiver","component_type":"otlp","component_name":"otlp"} ``` @@ -79,4 +79,4 @@ The traces should now be visible from the Jaeger UI (if you have it installed). # Notes -* There is an issue with the exporter/collector which causes Jaeger to throw errors when receiving spans from the OpenTelemetry Collector: https://github.com/open-telemetry/opentelemetry-collector/issues/815 +* There is an issue with the exporter/Collector which causes Jaeger to throw errors when receiving spans from the OpenTelemetry Collector: https://github.com/open-telemetry/opentelemetry-collector/issues/815 From 97f88560df6d113c04e9a68454a63e2feb50303e Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Mon, 11 May 2020 12:35:11 +0200 Subject: [PATCH 008/108] fix readme --- example/otel-collector/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 749a9d144..64e3d84c7 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -11,7 +11,7 @@ The demo assumes you already have both OpenTelemetry Collector and Jaeger up and * Jaeger: https://www.jaegertracing.io/docs/1.17/getting-started/ * OpenTelemetry Collector: https://opentelemetry.io/docs/collector/about/ -Moreover, this demo is build against a microk8s cluster runnning both the OpenTelemetry Collector, and Jaeger. Therefor, the OpenTelemetry Collector configuration illustrated is a K8S ConfgurationMap. But the gist of it is there, so it shouldn't matter too much. +Moreover, this demo is build against a microk8s cluster running both the OpenTelemetry Collector, and Jaeger. Therefor, the OpenTelemetry Collector configuration illustrated is a K8S ConfigurationMap. But the gist of it is there, so it shouldn't matter too much. # Configuring the OTEL Collector From 105e0df893b802cd24f4a073d4006c7c52d6c65d Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Mon, 11 May 2020 12:42:36 +0200 Subject: [PATCH 009/108] precommit --- .gitignore | 1 + example/otel-collector/go.sum | 14 +++++++++++++- example/otel-collector/main.go | 3 ++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index b0a66130d..1f4d25f3c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ coverage.* /example/namedtracer/namedtracer /example/prometheus/prometheus /example/zipkin/zipkin +/example/otel-collector/otel-collector diff --git a/example/otel-collector/go.sum b/example/otel-collector/go.sum index 58faba930..c425ab081 100644 --- a/example/otel-collector/go.sum +++ b/example/otel-collector/go.sum @@ -1,18 +1,22 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= +github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -22,24 +26,29 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/open-telemetry/opentelemetry-proto v0.3.0 h1:+ASAtcayvoELyCF40+rdCMlBOhZIn5TPDez85zSYc30= github.com/open-telemetry/opentelemetry-proto v0.3.0/go.mod h1:PMR5GI0F7BSpio+rBGFxNm6SLzg3FypDTcFuQZnO+F8= github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -go.opentelemetry.io v0.1.0 h1:EANZoRCOP+A3faIlw/iN6YEWoYb1vleZRKm1EvH8T48= go.opentelemetry.io/otel v0.4.3 h1:CroUX/0O1ZDcF0iWOO8gwYFWb5EbdSF0/C1yosO+Vhs= go.opentelemetry.io/otel v0.4.3/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek= go.opentelemetry.io/otel/exporters/otlp v0.4.3 h1:n0zV9impmvdavDnr5uBiza+P9D1AfkcfUvuTWogMY2w= @@ -71,6 +80,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -87,9 +97,11 @@ google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index cbf2a6be2..0b83796a8 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -21,9 +21,10 @@ import ( "log" "time" + "google.golang.org/grpc" + "go.opentelemetry.io/otel/exporters/otlp" sdktrace "go.opentelemetry.io/otel/sdk/trace" - "google.golang.org/grpc" ) func main() { From cffc57c907a4d002057e688ad4f62097eabc876d Mon Sep 17 00:00:00 2001 From: Ahmed Mujtaba Date: Sun, 10 May 2020 23:19:20 +0200 Subject: [PATCH 010/108] Added grpc stream interceptor client --- plugin/grpctrace/interceptor_test.go | 133 ++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 4 deletions(-) diff --git a/plugin/grpctrace/interceptor_test.go b/plugin/grpctrace/interceptor_test.go index fac424933..2dce41797 100644 --- a/plugin/grpctrace/interceptor_test.go +++ b/plugin/grpctrace/interceptor_test.go @@ -15,10 +15,12 @@ package grpctrace import ( "context" - "fmt" + "sync" "testing" + "time" "google.golang.org/grpc" + "google.golang.org/grpc/metadata" "go.opentelemetry.io/otel/api/core" export "go.opentelemetry.io/otel/sdk/export/trace" @@ -26,10 +28,13 @@ import ( ) type testExporter struct { + mu sync.Mutex spanMap map[string][]*export.SpanData } func (t *testExporter) ExportSpan(ctx context.Context, s *export.SpanData) { + t.mu.Lock() + defer t.mu.Unlock() t.spanMap[s.Name] = append(t.spanMap[s.Name], s) } @@ -55,7 +60,7 @@ func (mm *mockProtoMessage) ProtoMessage() { } func TestUnaryClientInterceptor(t *testing.T) { - exp := &testExporter{make(map[string][]*export.SpanData)} + exp := &testExporter{spanMap: make(map[string][]*export.SpanData)} tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{ DefaultSampler: sdktrace.AlwaysSample(), @@ -127,8 +132,7 @@ func TestUnaryClientInterceptor(t *testing.T) { }, } - for idx, check := range checks { - fmt.Println("================", idx, "==================") + for _, check := range checks { err = unaryInterceptor(context.Background(), check.name, req, reply, clientConn, uniInterceptorInvoker.invoker) if err != nil { t.Fatalf("failed to run unary interceptor: %v", err) @@ -178,3 +182,124 @@ func TestUnaryClientInterceptor(t *testing.T) { } } } + +type mockClientStream struct { + Desc *grpc.StreamDesc + Ctx context.Context +} + +func (mockClientStream) SendMsg(m interface{}) error { return nil } +func (mockClientStream) RecvMsg(m interface{}) error { return nil } +func (mockClientStream) CloseSend() error { return nil } +func (c mockClientStream) Context() context.Context { return c.Ctx } +func (mockClientStream) Header() (metadata.MD, error) { return nil, nil } +func (mockClientStream) Trailer() metadata.MD { return nil } + +func TestStreamClientInterceptor(t *testing.T) { + exp := &testExporter{spanMap: make(map[string][]*export.SpanData)} + tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), + sdktrace.WithConfig(sdktrace.Config{ + DefaultSampler: sdktrace.AlwaysSample(), + }, + )) + clientConn, err := grpc.Dial("fake:connection", grpc.WithInsecure()) + if err != nil { + t.Fatalf("failed to create client connection: %v", err) + } + + // tracer + tracer := tp.Tracer("grpctrace/Server") + streamCI := StreamClientInterceptor(tracer) + + var mockClStr mockClientStream + methodName := "/github.com.serviceName/bar" + + streamClient, err := streamCI(context.Background(), + &grpc.StreamDesc{ServerStreams: true}, + clientConn, + methodName, + func(ctx context.Context, + desc *grpc.StreamDesc, + cc *grpc.ClientConn, + method string, + opts ...grpc.CallOption) (grpc.ClientStream, error) { + mockClStr = mockClientStream{Desc: desc, Ctx: ctx} + return mockClStr, nil + }) + + if err != nil { + t.Fatalf("failed to initialize grpc stream client: %v", err) + } + + // no span exported while stream is open + if _, ok := exp.spanMap[methodName]; ok { + t.Fatalf("span shouldn't end while stream is open") + } + + req := &mockProtoMessage{} + reply := &mockProtoMessage{} + + // send and receive fake data + for i := 0; i < 10; i++ { + _ = streamClient.SendMsg(req) + _ = streamClient.RecvMsg(reply) + } + + // close client and server stream + _ = streamClient.CloseSend() + mockClStr.Desc.ServerStreams = false + _ = streamClient.RecvMsg(reply) + + // added retry because span end is called in separate go routine + var spanData []*export.SpanData + for retry := 0; retry < 5; retry++ { + ok := false + exp.mu.Lock() + spanData, ok = exp.spanMap[methodName] + exp.mu.Unlock() + if ok { + break + } + time.Sleep(time.Second * 1) + } + if len(spanData) == 0 { + t.Fatalf("no span data found for name < %s >", methodName) + } + + attrs := spanData[0].Attributes + expectedAttr := map[core.Key]string{ + rpcServiceKey: "serviceName", + netPeerIPKey: "fake", + netPeerPortKey: "connection", + } + + for _, attr := range attrs { + expected, ok := expectedAttr[attr.Key] + if ok { + if expected != attr.Value.AsString() { + t.Errorf("name: %s invalid %s found. expected %s, actual %s", methodName, string(attr.Key), + expected, attr.Value.AsString()) + } + } + } + + events := spanData[0].MessageEvents + if len(events) != 20 { + t.Fatalf("incorrect number of events expected 20 got %d", len(events)) + } + for i := 0; i < 20; i += 2 { + msgID := i/2 + 1 + validate := func(eventName string, attrs []core.KeyValue) { + for _, attr := range attrs { + if attr.Key == messageTypeKey && attr.Value.AsString() != eventName { + t.Errorf("invalid event on index: %d expecting %s event, receive %s event", i, eventName, attr.Value.AsString()) + } + if attr.Key == messageIDKey && attr.Value != core.Int(msgID) { + t.Errorf("invalid id for message event expected %d received %d", msgID, attr.Value.AsInt32()) + } + } + } + validate("SENT", events[i].Attributes) + validate("RECEIVED", events[i+1].Attributes) + } +} From 6ba581865f1eb01bceb72a31a05003c22a323680 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:03:03 +0200 Subject: [PATCH 011/108] Update example/otel-collector/README.md Co-authored-by: Tyler Yahn --- example/otel-collector/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 64e3d84c7..9d76fd046 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -1,6 +1,6 @@ # OpenTelemetry Collector Traces Example -This example illustrates how to export traces from the otel-go sdk to the Open Telemetry Collector, and from there to a Jaeger instance. +This example illustrates how to export traces from the OpenTelemetry-Go SDK to the OpenTelemetry Collector, and from there to a Jaeger instance. The complete flow is: `otel-collector-demo -> otel-collector -> Jaeger` From 4c7262b1672c0e1bf36faee8f6001c442f920636 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:03:12 +0200 Subject: [PATCH 012/108] Update example/otel-collector/README.md Co-authored-by: Tyler Yahn --- example/otel-collector/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 9d76fd046..565a8a228 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -7,7 +7,9 @@ The complete flow is: # Prerequisites -The demo assumes you already have both OpenTelemetry Collector and Jaeger up and running. For setting these up, please follow the corresponding documentations: +The demo assumes you already have both an instance of the OpenTelemetry Collector and Jaeger up and running. +For information about how to set these up, please follow the corresponding documentation: + * Jaeger: https://www.jaegertracing.io/docs/1.17/getting-started/ * OpenTelemetry Collector: https://opentelemetry.io/docs/collector/about/ From e2e4e7762465820c8c4f779fb5f7690e82b7c14e Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:03:19 +0200 Subject: [PATCH 013/108] Update example/otel-collector/README.md Co-authored-by: Tyler Yahn --- example/otel-collector/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 565a8a228..ac30c5e5c 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -13,7 +13,9 @@ For information about how to set these up, please follow the corresponding docum * Jaeger: https://www.jaegertracing.io/docs/1.17/getting-started/ * OpenTelemetry Collector: https://opentelemetry.io/docs/collector/about/ -Moreover, this demo is build against a microk8s cluster running both the OpenTelemetry Collector, and Jaeger. Therefor, the OpenTelemetry Collector configuration illustrated is a K8S ConfigurationMap. But the gist of it is there, so it shouldn't matter too much. +Moreover, this demo is built on a Kubernetes cluster running both the OpenTelemetry Collector, and Jaeger. +Therefore, the included OpenTelemetry Collector configuration is a Kubernetes `ConfigMap`. +However, the main concepts should be transferable to any other platform you want to run this demo on. # Configuring the OTEL Collector From 7db551e39e92cdf815f0fe3497a49088594a5a26 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:03:26 +0200 Subject: [PATCH 014/108] Update example/otel-collector/README.md Co-authored-by: Tyler Yahn --- example/otel-collector/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index ac30c5e5c..6bf4055c4 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -19,7 +19,7 @@ However, the main concepts should be transferable to any other platform you want # Configuring the OTEL Collector -In order to enable our application to send traces to the OpenTelemetry Collector, we need to first open up the OTLP receiver: +In order to enable our application to send traces to the OpenTelemetry Collector, we need to first configure the OTLP receiver: ```yml receivers: From 9c442a097205aa9b3bce2ce7ff661a4d35d93aba Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:03:34 +0200 Subject: [PATCH 015/108] Update example/otel-collector/README.md Co-authored-by: Tyler Yahn --- example/otel-collector/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 6bf4055c4..ecd02778e 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -28,6 +28,7 @@ receivers: This will create the receiver on the Collector side, and open up port `55680` for receiving traces. The rest of the configuration is quite standard, with the only mention that we need to create the Jaeger exporter: + ```yml exporters: jaeger: From 95888a770076001158623e2b22b355009a7075d4 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:03:44 +0200 Subject: [PATCH 016/108] Update example/otel-collector/README.md Co-authored-by: Tyler Yahn --- example/otel-collector/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index ecd02778e..4e5436435 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -36,7 +36,9 @@ exporters: endpoint: ":14250" ``` -After this, apply the configuration to your OpenTelemetry Collector instance (with `k apply -f otel-controller-config.yaml` for k8s users). You should see that the Collector creates the otlp receiver: +After this, apply the configuration to your OpenTelemetry Collector instance (with `kubectl apply -f otel-controller-config.yaml` for k8s users). +You should see that the Collector creates the otlp receiver: + ```json {"level":"info","ts":1589184143.206609,"caller":"builder/receivers_builder.go:79","msg":"Receiver started.","component_kind":"receiver","component_type":"otlp","component_name":"otlp"} ``` From a81fb66b044c9587b6d8b33052677a563c21bb4c Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:03:53 +0200 Subject: [PATCH 017/108] Update example/otel-collector/README.md Co-authored-by: Tyler Yahn --- example/otel-collector/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 4e5436435..627940cf9 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -60,7 +60,9 @@ defer func() { _ = exp.Stop() }() ``` -This will initialize the exporter with the default configuration. In this configuration, it will try to connect to an otlp receiver at the address `localhost:55680`. If your OpenTelemetry Collector is running at a different address, use the [otlp.WithAddress](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp?tab=doc#WithAddress) function to change the default address. +This will initialize the exporter with the default configuration. +In this configuration, it will try to connect to an OTLP receiver at the address `localhost:55680`. +If your OpenTelemetry Collector is running at a different address, use the [`otlp.WithAddress`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp?tab=doc#WithAddress) function to change the default address. Feel free to remove the blocking operation, but it might come in handy when testing the connection. Also, make sure to close the exporter before the app exits. From 1e878b95640575f4ec56db25f62f37ea0fa5ec0e Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:04:02 +0200 Subject: [PATCH 018/108] Update example/otel-collector/README.md Co-authored-by: Tyler Yahn --- example/otel-collector/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 627940cf9..004cd3ea0 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -66,7 +66,7 @@ If your OpenTelemetry Collector is running at a different address, use the [`otl Feel free to remove the blocking operation, but it might come in handy when testing the connection. Also, make sure to close the exporter before the app exits. -The next steps are the same as for all other otel-go sdk uses: +The next steps are the same as for all other OpenTelemetry-Go SDK uses: 1) Create a trace provider from the `otlp` exporter: ```go tp, _ := sdktrace.NewProvider( From 4c1109bee8388362185a1633f1fff8472d6838af Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:04:09 +0200 Subject: [PATCH 019/108] Update example/otel-collector/README.md Co-authored-by: Tyler Yahn --- example/otel-collector/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 004cd3ea0..6636def30 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -64,7 +64,8 @@ This will initialize the exporter with the default configuration. In this configuration, it will try to connect to an OTLP receiver at the address `localhost:55680`. If your OpenTelemetry Collector is running at a different address, use the [`otlp.WithAddress`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp?tab=doc#WithAddress) function to change the default address. -Feel free to remove the blocking operation, but it might come in handy when testing the connection. Also, make sure to close the exporter before the app exits. +Feel free to remove the blocking operation, but it might come in handy when testing the connection. +Also, make sure to close the exporter before the app exits. The next steps are the same as for all other OpenTelemetry-Go SDK uses: 1) Create a trace provider from the `otlp` exporter: From 41ca31f31a038f0033b61354f4b94ea2a7b2c806 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:04:15 +0200 Subject: [PATCH 020/108] Update example/otel-collector/main.go Co-authored-by: Tyler Yahn --- example/otel-collector/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index 0b83796a8..d5e4ad0f9 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -37,7 +37,7 @@ func main() { _ = exp.Stop() }() - tp, _ := sdktrace.NewProvider( + tp, err := sdktrace.NewProvider( sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), sdktrace.WithBatcher(exp, // add following two options to ensure flush sdktrace.WithScheduleDelayMillis(5), From a09fd990c2f14ca0e4cde7b2568b5bcb85daa478 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 12 May 2020 13:04:22 +0200 Subject: [PATCH 021/108] Update example/otel-collector/main.go Co-authored-by: Tyler Yahn --- example/otel-collector/main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index d5e4ad0f9..0f85ae4a6 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -34,7 +34,10 @@ func main() { log.Fatalf("Failed to create the collector exporter: %v", err) } defer func() { - _ = exp.Stop() + err := exp.Stop() + if err != nil { + log.Fatalf("Failed to stop the exporter: %v", err) + } }() tp, err := sdktrace.NewProvider( From bbb7763a848c8cd4b6465019e54504b6fc946b1f Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Wed, 13 May 2020 11:10:19 +0200 Subject: [PATCH 022/108] add full collector config --- .../otel-collector/otel-collector-config.yaml | 27 ---- example/otel-collector/otel-collector.yaml | 117 ++++++++++++++++++ 2 files changed, 117 insertions(+), 27 deletions(-) delete mode 100644 example/otel-collector/otel-collector-config.yaml create mode 100644 example/otel-collector/otel-collector.yaml diff --git a/example/otel-collector/otel-collector-config.yaml b/example/otel-collector/otel-collector-config.yaml deleted file mode 100644 index 278fc32c3..000000000 --- a/example/otel-collector/otel-collector-config.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: otel-collector-conf - labels: - app: opentelemetry - component: otel-collector-conf -data: - otel-collector-config: | - receivers: - # Make sure to add the otlp receiver. - # This will use the default configuration, and open up the receiver on port 55680 - otlp: {} - processors: - extensions: - health_check: {} - exporters: - jaeger: - # Replace with a real endpoint. - endpoint: "simplest-collector.default.svc.cluster.local:14250" - service: - extensions: [health_check] - pipelines: - traces/2: - receivers: [otlp] - processors: [] - exporters: [jaeger] \ No newline at end of file diff --git a/example/otel-collector/otel-collector.yaml b/example/otel-collector/otel-collector.yaml new file mode 100644 index 000000000..005a4477a --- /dev/null +++ b/example/otel-collector/otel-collector.yaml @@ -0,0 +1,117 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: otel-collector-conf + labels: + app: opentelemetry + component: otel-collector-conf +data: + otel-collector-config: | + receivers: + # Make sure to add the otlp receiver. + # This will use the default configuration, and open up the receiver on port 55680 + otlp: {} + processors: + extensions: + health_check: {} + exporters: + jaeger: + # Replace with a real endpoint. + endpoint: "simplest-collector.default.svc.cluster.local:14250" + service: + extensions: [health_check] + pipelines: + traces/2: + receivers: [otlp] + processors: [] + exporters: [jaeger] +--- +apiVersion: v1 +kind: Service +metadata: + name: otel-collector + labels: + app: opencensus + component: otel-collector +spec: + ports: + - name: otlp # Default endpoint for Opencensus receiver. + port: 55680 + protocol: TCP + targetPort: 55680 + selector: + component: otel-collector +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: otel-collector + labels: + app: opentelemetry + component: otel-collector +spec: + selector: + matchLabels: + app: opentelemetry + component: otel-collector + minReadySeconds: 5 + progressDeadlineSeconds: 120 + replicas: 1 #TODO - adjust this to your own requirements + template: + metadata: + annotations: + prometheus.io/path: "/metrics" + prometheus.io/port: "8888" + prometheus.io/scrape: "true" + labels: + app: opentelemetry + component: otel-collector + spec: + containers: + - command: + - "/otelcol" + - "--config=/conf/otel-collector-config.yaml" +# Memory Ballast size should be max 1/3 to 1/2 of memory. + - "--mem-ballast-size-mib=683" + env: + - name: GOGC + value: "80" + image: otel/opentelemetry-collector-dev:latest + name: otel-collector + resources: + limits: + cpu: 1 + memory: 2Gi + requests: + cpu: 200m + memory: 400Mi + ports: + - containerPort: 55680 # Default endpoint for otlp receiver. + - containerPort: 8888 # Default endpoint for querying metrics. + volumeMounts: + - name: otel-collector-config-vol + mountPath: /conf +# - name: otel-collector-secrets +# mountPath: /secrets + livenessProbe: + httpGet: + path: / + port: 13133 # Health Check extension default port. + readinessProbe: + httpGet: + path: / + port: 13133 # Health Check extension default port. + volumes: + - configMap: + name: otel-collector-conf + items: + - key: otel-collector-config + path: otel-collector-config.yaml + name: otel-collector-config-vol +# - secret: +# name: otel-collector-secrets +# items: +# - key: cert.pem +# path: cert.pem +# - key: key.pem +# path: key.pem \ No newline at end of file From d69386f56cbdb87498d62a5bf4832cdf22050010 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Wed, 13 May 2020 12:38:23 +0200 Subject: [PATCH 023/108] add resource attributes to provider --- example/otel-collector/go.mod | 1 + example/otel-collector/go.sum | 1175 ++++++++++++++++++++++++++++++++ example/otel-collector/main.go | 7 + 3 files changed, 1183 insertions(+) diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod index 0a9509eab..b429c620a 100644 --- a/example/otel-collector/go.mod +++ b/example/otel-collector/go.mod @@ -3,6 +3,7 @@ module go.opentelemetry.io/otel/example/otel-collector go 1.14 require ( + github.com/open-telemetry/opentelemetry-collector v0.3.0 go.opentelemetry.io/otel v0.4.3 go.opentelemetry.io/otel/exporters/otlp v0.4.3 google.golang.org/grpc v1.29.1 diff --git a/example/otel-collector/go.sum b/example/otel-collector/go.sum index c425ab081..f2bdcc4d6 100644 --- a/example/otel-collector/go.sum +++ b/example/otel-collector/go.sum @@ -1,107 +1,1282 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +contrib.go.opencensus.io/exporter/jaeger v0.1.1-0.20190430175949-e8b55949d948/go.mod h1:ukdzwIYYHgZ7QYtwVFQUjiT28BJHiMhTERo32s6qVgM= +contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs= +contrib.go.opencensus.io/exporter/prometheus v0.1.0/go.mod h1:cGFniUXGZlKRjzOyuZJ6mgB+PgBcCIa79kEKR8YCW+A= +contrib.go.opencensus.io/resource v0.1.2/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= +github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v11.2.8+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/Songmu/retry v0.1.0/go.mod h1:7sXIW7eseB9fq0FUvigRcQMVLR9tuHI0Scok+rkpAuA= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= +github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.23.12/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04= github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= +github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTKY95VwV8U= +github.com/cenkalti/backoff v0.0.0-20181003080854-62661b46c409/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v0.0.0-20181017004759-096ff4a8a059/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= +github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= +github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v0.0.0-20180713052910-9f541cc9db5d/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20181003060214-f58a169a71a5/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= +github.com/go-critic/go-critic v0.4.1/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E68facVDK23g= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.17.2/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.17.2/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.18.0/go.mod h1:uI6pHuxWYTy94zZxgcwJkUWa9wbIlhteGfloI10GD4U= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.3/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.17.2/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.4/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.17.2/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= +github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= +github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= +github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= +github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= +github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= +github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= +github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= +github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= +github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= +github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= +github.com/gobuffalo/buffalo v0.12.8-0.20181004233540-fac9bb505aa8/go.mod h1:sLyT7/dceRXJUxSsE813JTQtA3Eb1vjxWfo/N//vXIY= +github.com/gobuffalo/buffalo v0.13.0/go.mod h1:Mjn1Ba9wpIbpbrD+lIDMy99pQ0H0LiddMIIDGse7qT4= +github.com/gobuffalo/buffalo-plugins v1.0.2/go.mod h1:pOp/uF7X3IShFHyobahTkTLZaeUXwb0GrUTb9ngJWTs= +github.com/gobuffalo/buffalo-plugins v1.0.4/go.mod h1:pWS1vjtQ6uD17MVFWf7i3zfThrEKWlI5+PYLw/NaDB4= +github.com/gobuffalo/buffalo-plugins v1.4.3/go.mod h1:uCzTY0woez4nDMdQjkcOYKanngeUVRO2HZi7ezmAjWY= +github.com/gobuffalo/buffalo-plugins v1.5.1/go.mod h1:jbmwSZK5+PiAP9cC09VQOrGMZFCa/P0UMlIS3O12r5w= +github.com/gobuffalo/buffalo-plugins v1.6.4/go.mod h1:/+N1aophkA2jZ1ifB2O3Y9yGwu6gKOVMtUmJnbg+OZI= +github.com/gobuffalo/buffalo-plugins v1.6.5/go.mod h1:0HVkbgrVs/MnPZ/FOseDMVanCTm2RNcdM0PuXcL1NNI= +github.com/gobuffalo/buffalo-plugins v1.6.7/go.mod h1:ZGZRkzz2PiKWHs0z7QsPBOTo2EpcGRArMEym6ghKYgk= +github.com/gobuffalo/buffalo-plugins v1.6.9/go.mod h1:yYlYTrPdMCz+6/+UaXg5Jm4gN3xhsvsQ2ygVatZV5vw= +github.com/gobuffalo/buffalo-plugins v1.6.11/go.mod h1:eAA6xJIL8OuynJZ8amXjRmHND6YiusVAaJdHDN1Lu8Q= +github.com/gobuffalo/buffalo-plugins v1.8.2/go.mod h1:9te6/VjEQ7pKp7lXlDIMqzxgGpjlKoAcAANdCgoR960= +github.com/gobuffalo/buffalo-plugins v1.8.3/go.mod h1:IAWq6vjZJVXebIq2qGTLOdlXzmpyTZ5iJG5b59fza5U= +github.com/gobuffalo/buffalo-plugins v1.9.4/go.mod h1:grCV6DGsQlVzQwk6XdgcL3ZPgLm9BVxlBmXPMF8oBHI= +github.com/gobuffalo/buffalo-plugins v1.10.0/go.mod h1:4osg8d9s60txLuGwXnqH+RCjPHj9K466cDFRl3PErHI= +github.com/gobuffalo/buffalo-plugins v1.11.0/go.mod h1:rtIvAYRjYibgmWhnjKmo7OadtnxuMG5ZQLr25ozAzjg= +github.com/gobuffalo/buffalo-pop v1.0.5/go.mod h1:Fw/LfFDnSmB/vvQXPvcXEjzP98Tc+AudyNWUBWKCwQ8= +github.com/gobuffalo/envy v1.6.4/go.mod h1:Abh+Jfw475/NWtYMEt+hnJWRiC8INKWibIMyNt1w2Mc= +github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= +github.com/gobuffalo/envy v1.6.6/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= +github.com/gobuffalo/envy v1.6.7/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= +github.com/gobuffalo/envy v1.6.8/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= +github.com/gobuffalo/envy v1.6.9/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= +github.com/gobuffalo/envy v1.6.10/go.mod h1:X0CFllQjTV5ogsnUrg+Oks2yTI+PU2dGYBJOEI2D1Uo= +github.com/gobuffalo/envy v1.6.11/go.mod h1:Fiq52W7nrHGDggFPhn2ZCcHw4u/rqXkqo+i7FB6EAcg= +github.com/gobuffalo/envy v1.6.12/go.mod h1:qJNrJhKkZpEW0glh5xP2syQHH5kgdmgsKss2Kk8PTP0= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/events v1.0.3/go.mod h1:Txo8WmqScapa7zimEQIwgiJBvMECMe9gJjsKNPN3uZw= +github.com/gobuffalo/events v1.0.7/go.mod h1:z8txf6H9jWhQ5Scr7YPLWg/cgXBRj8Q4uYI+rsVCCSQ= +github.com/gobuffalo/events v1.0.8/go.mod h1:A5KyqT1sA+3GJiBE4QKZibse9mtOcI9nw8gGrDdqYGs= +github.com/gobuffalo/events v1.1.3/go.mod h1:9yPGWYv11GENtzrIRApwQRMYSbUgCsZ1w6R503fCfrk= +github.com/gobuffalo/events v1.1.4/go.mod h1:09/YRRgZHEOts5Isov+g9X2xajxdvOAcUuAHIX/O//A= +github.com/gobuffalo/events v1.1.5/go.mod h1:3YUSzgHfYctSjEjLCWbkXP6djH2M+MLaVRzb4ymbAK0= +github.com/gobuffalo/events v1.1.7/go.mod h1:6fGqxH2ing5XMb3EYRq9LEkVlyPGs4oO/eLzh+S8CxY= +github.com/gobuffalo/events v1.1.8/go.mod h1:UFy+W6X6VbCWS8k2iT81HYX65dMtiuVycMy04cplt/8= +github.com/gobuffalo/events v1.1.9/go.mod h1:/0nf8lMtP5TkgNbzYxR6Bl4GzBy5s5TebgNTdRfRbPM= +github.com/gobuffalo/fizz v1.0.12/go.mod h1:C0sltPxpYK8Ftvf64kbsQa2yiCZY4RZviurNxXdAKwc= +github.com/gobuffalo/flect v0.0.0-20180907193754-dc14d8acaf9f/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= +github.com/gobuffalo/flect v0.0.0-20181002182613-4571df4b1daf/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= +github.com/gobuffalo/flect v0.0.0-20181007231023-ae7ed6bfe683/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= +github.com/gobuffalo/flect v0.0.0-20181018182602-fd24a256709f/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= +github.com/gobuffalo/flect v0.0.0-20181019110701-3d6f0b585514/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= +github.com/gobuffalo/flect v0.0.0-20181024204909-8f6be1a8c6c2/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= +github.com/gobuffalo/flect v0.0.0-20181104133451-1f6e9779237a/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= +github.com/gobuffalo/flect v0.0.0-20181114183036-47375f6d8328/go.mod h1:0HvNbHdfh+WOvDSIASqJOSxTOWSxCCUF++k/Y53v9rI= +github.com/gobuffalo/flect v0.0.0-20181210151238-24a2b68e0316/go.mod h1:en58vff74S9b99Eg42Dr+/9yPu437QjlNsO/hBYPuOk= +github.com/gobuffalo/flect v0.0.0-20190104192022-4af577e09bf2/go.mod h1:en58vff74S9b99Eg42Dr+/9yPu437QjlNsO/hBYPuOk= +github.com/gobuffalo/flect v0.0.0-20190117212819-a62e61d96794/go.mod h1:397QT6v05LkZkn07oJXXT6y9FCfwC8Pug0WA2/2mE9k= +github.com/gobuffalo/genny v0.0.0-20180924032338-7af3a40f2252/go.mod h1:tUTQOogrr7tAQnhajMSH6rv1BVev34H2sa1xNHMy94g= +github.com/gobuffalo/genny v0.0.0-20181003150629-3786a0744c5d/go.mod h1:WAd8HmjMVrnkAZbmfgH5dLBUchsZfqzp/WS5sQz+uTM= +github.com/gobuffalo/genny v0.0.0-20181005145118-318a41a134cc/go.mod h1:WAd8HmjMVrnkAZbmfgH5dLBUchsZfqzp/WS5sQz+uTM= +github.com/gobuffalo/genny v0.0.0-20181007153042-b8de7d566757/go.mod h1:+oG5Ljrw04czAHbPXREwaFojJbpUvcIy4DiOnbEJFTA= +github.com/gobuffalo/genny v0.0.0-20181012161047-33e5f43d83a6/go.mod h1:+oG5Ljrw04czAHbPXREwaFojJbpUvcIy4DiOnbEJFTA= +github.com/gobuffalo/genny v0.0.0-20181017160347-90a774534246/go.mod h1:+oG5Ljrw04czAHbPXREwaFojJbpUvcIy4DiOnbEJFTA= +github.com/gobuffalo/genny v0.0.0-20181024195656-51392254bf53/go.mod h1:o9GEH5gn5sCKLVB5rHFC4tq40rQ3VRUzmx6WwmaqISE= +github.com/gobuffalo/genny v0.0.0-20181025145300-af3f81d526b8/go.mod h1:uZ1fFYvdcP8mu0B/Ynarf6dsGvp7QFIpk/QACUuFUVI= +github.com/gobuffalo/genny v0.0.0-20181027191429-94d6cfb5c7fc/go.mod h1:x7SkrQQBx204Y+O9EwRXeszLJDTaWN0GnEasxgLrQTA= +github.com/gobuffalo/genny v0.0.0-20181027195209-3887b7171c4f/go.mod h1:JbKx8HSWICu5zyqWOa0dVV1pbbXOHusrSzQUprW6g+w= +github.com/gobuffalo/genny v0.0.0-20181106193839-7dcb0924caf1/go.mod h1:x61yHxvbDCgQ/7cOAbJCacZQuHgB0KMSzoYcw5debjU= +github.com/gobuffalo/genny v0.0.0-20181107223128-f18346459dbe/go.mod h1:utQD3aKKEsdb03oR+Vi/6ztQb1j7pO10N3OBoowRcSU= +github.com/gobuffalo/genny v0.0.0-20181114215459-0a4decd77f5d/go.mod h1:kN2KZ8VgXF9VIIOj/GM0Eo7YK+un4Q3tTreKOf0q1ng= +github.com/gobuffalo/genny v0.0.0-20181119162812-e8ff4adce8bb/go.mod h1:BA9htSe4bZwBDJLe8CUkoqkypq3hn3+CkoHqVOW718E= +github.com/gobuffalo/genny v0.0.0-20181127225641-2d959acc795b/go.mod h1:l54xLXNkteX/PdZ+HlgPk1qtcrgeOr3XUBBPDbH+7CQ= +github.com/gobuffalo/genny v0.0.0-20181128191930-77e34f71ba2a/go.mod h1:FW/D9p7cEEOqxYA71/hnrkOWm62JZ5ZNxcNIVJEaWBU= +github.com/gobuffalo/genny v0.0.0-20181203165245-fda8bcce96b1/go.mod h1:wpNSANu9UErftfiaAlz1pDZclrYzLtO5lALifODyjuM= +github.com/gobuffalo/genny v0.0.0-20181203201232-849d2c9534ea/go.mod h1:wpNSANu9UErftfiaAlz1pDZclrYzLtO5lALifODyjuM= +github.com/gobuffalo/genny v0.0.0-20181206121324-d6fb8a0dbe36/go.mod h1:wpNSANu9UErftfiaAlz1pDZclrYzLtO5lALifODyjuM= +github.com/gobuffalo/genny v0.0.0-20181207164119-84844398a37d/go.mod h1:y0ysCHGGQf2T3vOhCrGHheYN54Y/REj0ayd0Suf4C/8= +github.com/gobuffalo/genny v0.0.0-20181211165820-e26c8466f14d/go.mod h1:sHnK+ZSU4e2feXP3PA29ouij6PUEiN+RCwECjCTB3yM= +github.com/gobuffalo/genny v0.0.0-20190104222617-a71664fc38e7/go.mod h1:QPsQ1FnhEsiU8f+O0qKWXz2RE4TiDqLVChWkBuh1WaY= +github.com/gobuffalo/genny v0.0.0-20190112155932-f31a84fcacf5/go.mod h1:CIaHCrSIuJ4il6ka3Hub4DR4adDrGoXGEEt2FbBxoIo= +github.com/gobuffalo/github_flavored_markdown v1.0.4/go.mod h1:uRowCdK+q8d/RF0Kt3/DSalaIXbb0De/dmTqMQdkQ4I= +github.com/gobuffalo/github_flavored_markdown v1.0.5/go.mod h1:U0643QShPF+OF2tJvYNiYDLDGDuQmJZXsf/bHOJPsMY= +github.com/gobuffalo/github_flavored_markdown v1.0.7/go.mod h1:w93Pd9Lz6LvyQXEG6DktTPHkOtCbr+arAD5mkwMzXLI= +github.com/gobuffalo/httptest v1.0.2/go.mod h1:7T1IbSrg60ankme0aDLVnEY0h056g9M1/ZvpVThtB7E= +github.com/gobuffalo/licenser v0.0.0-20180924033006-eae28e638a42/go.mod h1:Ubo90Np8gpsSZqNScZZkVXXAo5DGhTb+WYFIjlnog8w= +github.com/gobuffalo/licenser v0.0.0-20181025145548-437d89de4f75/go.mod h1:x3lEpYxkRG/XtGCUNkio+6RZ/dlOvLzTI9M1auIwFcw= +github.com/gobuffalo/licenser v0.0.0-20181027200154-58051a75da95/go.mod h1:BzhaaxGd1tq1+OLKObzgdCV9kqVhbTulxOpYbvMQWS0= +github.com/gobuffalo/licenser v0.0.0-20181109171355-91a2a7aac9a7/go.mod h1:m+Ygox92pi9bdg+gVaycvqE8RVSjZp7mWw75+K5NPHk= +github.com/gobuffalo/licenser v0.0.0-20181128165715-cc7305f8abed/go.mod h1:oU9F9UCE+AzI/MueCKZamsezGOOHfSirltllOVeRTAE= +github.com/gobuffalo/licenser v0.0.0-20181203160806-fe900bbede07/go.mod h1:ph6VDNvOzt1CdfaWC+9XwcBnlSTBz2j49PBwum6RFaU= +github.com/gobuffalo/licenser v0.0.0-20181211173111-f8a311c51159/go.mod h1:ve/Ue99DRuvnTaLq2zKa6F4KtHiYf7W046tDjuGYPfM= +github.com/gobuffalo/logger v0.0.0-20181022175615-46cfb361fc27/go.mod h1:8sQkgyhWipz1mIctHF4jTxmJh1Vxhp7mP8IqbljgJZo= +github.com/gobuffalo/logger v0.0.0-20181027144941-73d08d2bb969/go.mod h1:7uGg2duHKpWnN4+YmyKBdLXfhopkAdVM6H3nKbyFbz8= +github.com/gobuffalo/logger v0.0.0-20181027193913-9cf4dd0efe46/go.mod h1:7uGg2duHKpWnN4+YmyKBdLXfhopkAdVM6H3nKbyFbz8= +github.com/gobuffalo/logger v0.0.0-20181109185836-3feeab578c17/go.mod h1:oNErH0xLe+utO+OW8ptXMSA5DkiSEDW1u3zGIt8F9Ew= +github.com/gobuffalo/logger v0.0.0-20181117211126-8e9b89b7c264/go.mod h1:5etB91IE0uBlw9k756fVKZJdS+7M7ejVhmpXXiSFj0I= +github.com/gobuffalo/logger v0.0.0-20181127160119-5b956e21995c/go.mod h1:+HxKANrR9VGw9yN3aOAppJKvhO05ctDi63w4mDnKv2U= +github.com/gobuffalo/makr v1.1.5/go.mod h1:Y+o0btAH1kYAMDJW/TX3+oAXEu0bmSLLoC9mIFxtzOw= +github.com/gobuffalo/mapi v1.0.0/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/meta v0.0.0-20181018155829-df62557efcd3/go.mod h1:XTTOhwMNryif3x9LkTTBO/Llrveezd71u3quLd0u7CM= +github.com/gobuffalo/meta v0.0.0-20181018192820-8c6cef77dab3/go.mod h1:E94EPzx9NERGCY69UWlcj6Hipf2uK/vnfrF4QD0plVE= +github.com/gobuffalo/meta v0.0.0-20181025145500-3a985a084b0a/go.mod h1:YDAKBud2FP7NZdruCSlmTmDOZbVSa6bpK7LJ/A/nlKg= +github.com/gobuffalo/meta v0.0.0-20181114191255-b130ebedd2f7/go.mod h1:K6cRZ29ozr4Btvsqkjvg5nDFTLOgTqf03KA70Ks0ypE= +github.com/gobuffalo/meta v0.0.0-20181127070345-0d7e59dd540b/go.mod h1:RLO7tMvE0IAKAM8wny1aN12pvEKn7EtkBLkUZR00Qf8= +github.com/gobuffalo/meta v0.0.0-20190120163247-50bbb1fa260d/go.mod h1:KKsH44nIK2gA8p0PJmRT9GvWJUdphkDUA8AJEvFWiqM= +github.com/gobuffalo/mw-basicauth v1.0.3/go.mod h1:dg7+ilMZOKnQFHDefUzUHufNyTswVUviCBgF244C1+0= +github.com/gobuffalo/mw-contenttype v0.0.0-20180802152300-74f5a47f4d56/go.mod h1:7EvcmzBbeCvFtQm5GqF9ys6QnCxz2UM1x0moiWLq1No= +github.com/gobuffalo/mw-csrf v0.0.0-20180802151833-446ff26e108b/go.mod h1:sbGtb8DmDZuDUQoxjr8hG1ZbLtZboD9xsn6p77ppcHo= +github.com/gobuffalo/mw-forcessl v0.0.0-20180802152810-73921ae7a130/go.mod h1:JvNHRj7bYNAMUr/5XMkZaDcw3jZhUZpsmzhd//FFWmQ= +github.com/gobuffalo/mw-i18n v0.0.0-20180802152014-e3060b7e13d6/go.mod h1:91AQfukc52A6hdfIfkxzyr+kpVYDodgAeT5cjX1UIj4= +github.com/gobuffalo/mw-paramlogger v0.0.0-20181005191442-d6ee392ec72e/go.mod h1:6OJr6VwSzgJMqWMj7TYmRUqzNe2LXu/W1rRW4MAz/ME= +github.com/gobuffalo/mw-tokenauth v0.0.0-20181001105134-8545f626c189/go.mod h1:UqBF00IfKvd39ni5+yI5MLMjAf4gX7cDKN/26zDOD6c= +github.com/gobuffalo/packd v0.0.0-20181027182251-01ad393492c8/go.mod h1:SmdBdhj6uhOsg1Ui4SFAyrhuc7U4VCildosO5IDJ3lc= +github.com/gobuffalo/packd v0.0.0-20181027190505-aafc0d02c411/go.mod h1:SmdBdhj6uhOsg1Ui4SFAyrhuc7U4VCildosO5IDJ3lc= +github.com/gobuffalo/packd v0.0.0-20181027194105-7ae579e6d213/go.mod h1:SmdBdhj6uhOsg1Ui4SFAyrhuc7U4VCildosO5IDJ3lc= +github.com/gobuffalo/packd v0.0.0-20181031195726-c82734870264/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= +github.com/gobuffalo/packd v0.0.0-20181104210303-d376b15f8e96/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= +github.com/gobuffalo/packd v0.0.0-20181111195323-b2e760a5f0ff/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= +github.com/gobuffalo/packd v0.0.0-20181114190715-f25c5d2471d7/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= +github.com/gobuffalo/packd v0.0.0-20181124090624-311c6248e5fb/go.mod h1:Foenia9ZvITEvG05ab6XpiD5EfBHPL8A6hush8SJ0o8= +github.com/gobuffalo/packd v0.0.0-20181207120301-c49825f8f6f4/go.mod h1:LYc0TGKFBBFTRC9dg2pcRcMqGCTMD7T2BIMP7OBuQAA= +github.com/gobuffalo/packd v0.0.0-20181212173646-eca3b8fd6687/go.mod h1:LYc0TGKFBBFTRC9dg2pcRcMqGCTMD7T2BIMP7OBuQAA= +github.com/gobuffalo/packr v1.13.7/go.mod h1:KkinLIn/n6+3tVXMwg6KkNvWwVsrRAz4ph+jgpk3Z24= +github.com/gobuffalo/packr v1.15.0/go.mod h1:t5gXzEhIviQwVlNx/+3SfS07GS+cZ2hn76WLzPp6MGI= +github.com/gobuffalo/packr v1.15.1/go.mod h1:IeqicJ7jm8182yrVmNbM6PR4g79SjN9tZLH8KduZZwE= +github.com/gobuffalo/packr v1.19.0/go.mod h1:MstrNkfCQhd5o+Ct4IJ0skWlxN8emOq8DsoT1G98VIU= +github.com/gobuffalo/packr v1.20.0/go.mod h1:JDytk1t2gP+my1ig7iI4NcVaXr886+N0ecUga6884zw= +github.com/gobuffalo/packr v1.21.0/go.mod h1:H00jGfj1qFKxscFJSw8wcL4hpQtPe1PfU2wa6sg/SR0= +github.com/gobuffalo/packr v1.22.0/go.mod h1:Qr3Wtxr3+HuQEwWqlLnNW4t1oTvK+7Gc/Rnoi/lDFvA= +github.com/gobuffalo/packr/v2 v2.0.0-rc.8/go.mod h1:y60QCdzwuMwO2R49fdQhsjCPv7tLQFR0ayzxxla9zes= +github.com/gobuffalo/packr/v2 v2.0.0-rc.9/go.mod h1:fQqADRfZpEsgkc7c/K7aMew3n4aF1Kji7+lIZeR98Fc= +github.com/gobuffalo/packr/v2 v2.0.0-rc.10/go.mod h1:4CWWn4I5T3v4c1OsJ55HbHlUEKNWMITG5iIkdr4Px4w= +github.com/gobuffalo/packr/v2 v2.0.0-rc.11/go.mod h1:JoieH/3h3U4UmatmV93QmqyPUdf4wVM9HELaHEu+3fk= +github.com/gobuffalo/packr/v2 v2.0.0-rc.12/go.mod h1:FV1zZTsVFi1DSCboO36Xgs4pzCZBjB/tDV9Cz/lSaR8= +github.com/gobuffalo/packr/v2 v2.0.0-rc.13/go.mod h1:2Mp7GhBFMdJlOK8vGfl7SYtfMP3+5roE39ejlfjw0rA= +github.com/gobuffalo/packr/v2 v2.0.0-rc.14/go.mod h1:06otbrNvDKO1eNQ3b8hst+1010UooI2MFg+B2Ze4MV8= +github.com/gobuffalo/packr/v2 v2.0.0-rc.15/go.mod h1:IMe7H2nJvcKXSF90y4X1rjYIRlNMJYCxEhssBXNZwWs= +github.com/gobuffalo/plush v3.7.16+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plush v3.7.20+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plush v3.7.21+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plush v3.7.22+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plush v3.7.23+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plush v3.7.30+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plush v3.7.31+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plush v3.7.32+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plushgen v0.0.0-20181128164830-d29dcb966cb2/go.mod h1:r9QwptTFnuvSaSRjpSp4S2/4e2D3tJhARYbvEBcKSb4= +github.com/gobuffalo/plushgen v0.0.0-20181203163832-9fc4964505c2/go.mod h1:opEdT33AA2HdrIwK1aibqnTJDVVKXC02Bar/GT1YRVs= +github.com/gobuffalo/plushgen v0.0.0-20181207152837-eedb135bd51b/go.mod h1:Lcw7HQbEVm09sAQrCLzIxuhFbB3nAgp4c55E+UlynR0= +github.com/gobuffalo/plushgen v0.0.0-20190104222512-177cd2b872b3/go.mod h1:tYxCozi8X62bpZyKXYHw1ncx2ZtT2nFvG42kuLwYjoc= +github.com/gobuffalo/pop v4.8.2+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= +github.com/gobuffalo/pop v4.8.3+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= +github.com/gobuffalo/pop v4.8.4+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= +github.com/gobuffalo/release v1.0.35/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4= +github.com/gobuffalo/release v1.0.38/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4= +github.com/gobuffalo/release v1.0.42/go.mod h1:RPs7EtafH4oylgetOJpGP0yCZZUiO4vqHfTHJjSdpug= +github.com/gobuffalo/release v1.0.52/go.mod h1:RPs7EtafH4oylgetOJpGP0yCZZUiO4vqHfTHJjSdpug= +github.com/gobuffalo/release v1.0.53/go.mod h1:FdF257nd8rqhNaqtDWFGhxdJ/Ig4J7VcS3KL7n/a+aA= +github.com/gobuffalo/release v1.0.54/go.mod h1:Pe5/RxRa/BE8whDpGfRqSI7D1a0evGK1T4JDm339tJc= +github.com/gobuffalo/release v1.0.61/go.mod h1:mfIO38ujUNVDlBziIYqXquYfBF+8FDHUjKZgYC1Hj24= +github.com/gobuffalo/release v1.0.72/go.mod h1:NP5NXgg/IX3M5XmHmWR99D687/3Dt9qZtTK/Lbwc1hU= +github.com/gobuffalo/release v1.1.1/go.mod h1:Sluak1Xd6kcp6snkluR1jeXAogdJZpFFRzTYRs/2uwg= +github.com/gobuffalo/release v1.1.3/go.mod h1:CuXc5/m+4zuq8idoDt1l4va0AXAn/OSs08uHOfMVr8E= +github.com/gobuffalo/release v1.1.6/go.mod h1:18naWa3kBsqO0cItXZNJuefCKOENpbbUIqRL1g+p6z0= +github.com/gobuffalo/shoulders v1.0.1/go.mod h1:V33CcVmaQ4gRUmHKwq1fiTXuf8Gp/qjQBUL5tHPmvbA= +github.com/gobuffalo/syncx v0.0.0-20181120191700-98333ab04150/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobuffalo/syncx v0.0.0-20181120194010-558ac7de985f/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobuffalo/tags v2.0.11+incompatible/go.mod h1:9XmhOkyaB7UzvuY4UoZO4s67q8/xRMVJEaakauVQYeY= +github.com/gobuffalo/tags v2.0.14+incompatible/go.mod h1:9XmhOkyaB7UzvuY4UoZO4s67q8/xRMVJEaakauVQYeY= +github.com/gobuffalo/tags v2.0.15+incompatible/go.mod h1:9XmhOkyaB7UzvuY4UoZO4s67q8/xRMVJEaakauVQYeY= +github.com/gobuffalo/uuid v2.0.3+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= +github.com/gobuffalo/uuid v2.0.4+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= +github.com/gobuffalo/uuid v2.0.5+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= +github.com/gobuffalo/validate v2.0.3+incompatible/go.mod h1:N+EtDe0J8252BgfzQUChBgfd6L93m9weay53EWFVsMM= +github.com/gobuffalo/x v0.0.0-20181003152136-452098b06085/go.mod h1:WevpGD+5YOreDJznWevcn8NTmQEW5STSBgIkpkjzqXc= +github.com/gobuffalo/x v0.0.0-20181007152206-913e47c59ca7/go.mod h1:9rDPXaB3kXdKWzMc4odGQQdG2e2DIEmANy5aSJ9yesY= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.3.0/go.mod h1:d+q1s/xVJxZGKWwC/6UfPIF33J+G1Tq4GYv9Y+Tg/EU= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/gddo v0.0.0-20180828051604-96d2a289f41e/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= +github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= +github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= +github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= +github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= +github.com/golangci/golangci-lint v1.24.0/go.mod h1:yIqiAZ2SSQqg+1JeFlAdvEWjGVz4uu5jr4lrciqA1gE= +github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= +github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= +github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= +github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/google/addlicense v0.0.0-20200301095109-7c013a14f2e2/go.mod h1:EMjYTRimagHs1FwlIqKyX3wAM0u3rA+McvlIIWmSamA= +github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.1.2/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= +github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/gotestyourself/gotestyourself v1.3.0/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.11.1/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.2.0/go.mod h1:1SIkFYi2ZTXUE5Kgt179+4hH33djo11+0Eo2XgTAtkw= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.2.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb v1.7.7/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.3.2/go.mod h1:LvCquS3HbBKwgl7KbX9KyqEIumJAbm1UMcTvGaIf3bM= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= +github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.4.1/go.mod h1:6iSW+JznC0YT+SgBn7rNxoEBsBgSmnC5FwyCekOGUiE= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jaegertracing/jaeger v1.17.0/go.mod h1:LUWPSnzNPGRubM8pk0inANGitpiMOOxihXx0+53llXI= +github.com/jessevdk/go-flags v0.0.0-20180331124232-1c38ed7ad0cc/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s= +github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= +github.com/karrick/godirwalk v1.7.7/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= +github.com/karrick/godirwalk v1.7.8/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/luna-duclos/instrumentedsql v0.0.0-20181127104832-b7d587d28109/go.mod h1:PWUIzhtavmOR965zfawVsHXbEuU1G29BPZ/CB3C7jXk= +github.com/luna-duclos/instrumentedsql v1.1.2/go.mod h1:4LGbEqDnopzNAiyxPPDXhLspyunZxgPTMJBKtC6U0BQ= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/markbates/deplist v1.0.4/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= +github.com/markbates/deplist v1.0.5/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= +github.com/markbates/going v1.0.2/go.mod h1:UWCk3zm0UKefHZ7l8BNqi26UyiEMniznk8naLdTcy6c= +github.com/markbates/grift v1.0.4/go.mod h1:wbmtW74veyx+cgfwFhlnnMWqhoz55rnHR47oMXzsyVs= +github.com/markbates/hmax v1.0.0/go.mod h1:cOkR9dktiESxIMu+65oc/r/bdY4bE8zZw3OLhLx0X2c= +github.com/markbates/inflect v1.0.0/go.mod h1:oTeZL2KHA7CUX6X+fovmK9OvIOFuqu0TwdQrZjLTh88= +github.com/markbates/inflect v1.0.1/go.mod h1:uv3UVNBe5qBIfCm8O8Q+DW+S1EopeyINj+Ikhc7rnCk= +github.com/markbates/inflect v1.0.3/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= +github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= +github.com/markbates/oncer v0.0.0-20180924031910-e862a676800b/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/oncer v0.0.0-20180924034138-723ad0170a46/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/oncer v0.0.0-20181014194634-05fccaae8fc4/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/refresh v1.4.10/go.mod h1:NDPHvotuZmTmesXxr95C9bjlw1/0frJwtME2dzcVKhc= +github.com/markbates/safe v1.0.0/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/markbates/sigtx v1.0.0/go.mod h1:QF1Hv6Ic6Ca6W+T+DL0Y/ypborFKyvUY9HmuCD4VeTc= +github.com/markbates/willie v1.0.9/go.mod h1:fsrFVWl91+gXpx/6dv715j7i11fYPfZ9ZGfH0DQzY7w= +github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q= +github.com/moul/http2curl v0.0.0-20170919181001-9ac6cf4d929b/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +github.com/mozilla/tls-observatory v0.0.0-20200220173314-aae45faa4006/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v0.0.0-20170117200651-66bb6560562f/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/oleiade/reflections v1.0.0/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/open-telemetry/opentelemetry-collector v0.3.0 h1:/i106g9t6xNQ4hOAuczEwW10UX+S8ZnkdWDGawo9fyg= +github.com/open-telemetry/opentelemetry-collector v0.3.0/go.mod h1:c5EgyLBK6FoGCaJpOEQ0j+sHqmfuoRzOm29tdVA/wDg= github.com/open-telemetry/opentelemetry-proto v0.3.0 h1:+ASAtcayvoELyCF40+rdCMlBOhZIn5TPDez85zSYc30= github.com/open-telemetry/opentelemetry-proto v0.3.0/go.mod h1:PMR5GI0F7BSpio+rBGFxNm6SLzg3FypDTcFuQZnO+F8= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/orijtech/prometheus-go-metrics-exporter v0.0.4/go.mod h1:BiTx/ugZex8LheBk3j53tktWaRdFjV5FCfT2o0P7msE= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/ory/dockertest/v3 v3.5.4/go.mod h1:J8ZUbNB2FOhm1cFZW9xBpDsODqsSWcyYgtJYVPcnF70= +github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0= +github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4= +github.com/ory/go-acc v0.2.1/go.mod h1:0omgy2aa3nDBJ45VAKeLHH8ccPBudxLeic4xiDRtug0= +github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs= +github.com/ory/gojsonreference v0.0.0-20190720135523-6b606c2d8ee8/go.mod h1:wsH1C4nIeeQClDtD5AH7kF1uTS6zWyqfjVDTmB0Em7A= +github.com/ory/gojsonschema v1.1.1-0.20190919112458-f254ca73d5e9/go.mod h1:BNZpdJgB74KOLSsWFvzw6roXg1I6O51WO8roMmW+T7Y= +github.com/ory/herodot v0.6.2/go.mod h1:3BOneqcyBsVybCPAJoi92KN2BpJHcmDqAMcAAaJiJow= +github.com/ory/herodot v0.7.0/go.mod h1:YXKOfAXYdQojDP5sD8m0ajowq3+QXNdtxA+QiUXBwn0= +github.com/ory/jsonschema/v3 v3.0.1/go.mod h1:jgLHekkFk0uiGdEWGleC+tOm6JSSP8cbf17PnBuGXlw= +github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28= +github.com/ory/viper v1.7.4/go.mod h1:T6sodNZKNGPpashUOk7EtXz2isovz8oCd57GNVkkNmE= +github.com/ory/x v0.0.84/go.mod h1:RXLPBG7B+hAViONVg0sHwK+U/ie1Y/NeXrq1JcARfoE= +github.com/ory/x v0.0.85/go.mod h1:s44V8t3xyjWZREcU+mWlp4h302rTuM4aLXcW+y5FbQ8= +github.com/ory/x v0.0.93/go.mod h1:lfcTaGXpTZs7IEQAW00r9EtTCOxD//SiP5uWtNiz31g= +github.com/ory/x v0.0.109/go.mod h1:tStpZsifohWoQk609GQoc2yNS2gRBDt5abkfx9pEPJg= +github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pavius/impi v0.0.0-20180302134524-c1cbdcb8df2b/go.mod h1:x/hU0bfdWIhuOT1SKwiJg++yvkk6EuOtJk8WtDZqgr8= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a/go.mod h1:lzZQ3Noex5pfAy7mkAeCjcBDteYU85uWWnJ/y6gKU8k= +github.com/prometheus/alertmanager v0.18.0/go.mod h1:WcxHBl40VSPuOaqWae6l6HpnEOVRIycEJ7i9iYkadEE= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/prometheus v0.0.0-20180315085919-58e2a31db8de/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= +github.com/prometheus/prometheus v1.8.2-0.20190924101040-52e0504f83ea/go.mod h1:elNqjVbwD3sCZJqKzyN7uEuwGcCpeJvv67D6BrHsDbw= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.0.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= +github.com/santhosh-tekuri/jsonschema/v2 v2.1.0/go.mod h1:yzJzKUGV4RbWqWIBBP4wSOBqavX5saE02yirLS0OTyg= +github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/securego/gosec v0.0.0-20200103095621-79fbf3af8d83/go.mod h1:vvbZ2Ae7AzSq3/kywjUDxSNq2SJ27RxCz2un0H3ePqE= +github.com/securego/gosec v0.0.0-20200316084457-7da9f46445fd/go.mod h1:NurAFZsWJAEZjogSwdVPlHkOZB3DOAU7gsPP8VFZCHc= +github.com/segmentio/analytics-go v3.0.1+incompatible/go.mod h1:C7CYBtQWk4vRk2RyLu0qOcbHJ18E3F1HV2C/8JvKN48= +github.com/segmentio/analytics-go v3.1.0+incompatible/go.mod h1:C7CYBtQWk4vRk2RyLu0qOcbHJ18E3F1HV2C/8JvKN48= +github.com/segmentio/backo-go v0.0.0-20160424052352-204274ad699c/go.mod h1:kJ9mm9YmoWSkk+oQ+5Cj8DEoRCX2JT6As4kEtIIOp1M= +github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20170515013102-78fb10f4a5f8/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/octicon v0.0.0-20180602230221-c42b0e3b24d9/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20180825020608-02ddb050ef6b/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= +github.com/sirupsen/logrus v1.1.1/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= +github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= +github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= +github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tcnksm/ghr v0.13.0/go.mod h1:tcp6tzbRYE0LqFSG7ykXP/BVG1/2BkX6aIn9FFV1mIQ= +github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE= +github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e/go.mod h1:d7u6HkTYKSv5m6MCKkOQlHwaShTMl3HjqSGW3XtVhXM= +github.com/tidwall/gjson v1.3.2/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y= +github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v1.5.0/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/uber/tchannel-go v1.10.0/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= +github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= +github.com/unrolled/secure v0.0.0-20180918153822-f340ee86eb8b/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= +github.com/unrolled/secure v0.0.0-20181005190816-ff9db2ff917f/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= +github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.0.4/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/otel v0.4.3 h1:CroUX/0O1ZDcF0iWOO8gwYFWb5EbdSF0/C1yosO+Vhs= go.opentelemetry.io/otel v0.4.3/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek= go.opentelemetry.io/otel/exporters/otlp v0.4.3 h1:n0zV9impmvdavDnr5uBiza+P9D1AfkcfUvuTWogMY2w= go.opentelemetry.io/otel/exporters/otlp v0.4.3/go.mod h1:h51N+tR0tmfiF05zFB13vaiROHSIUm7AuFetkY8T4GY= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180830192347-182538f80094/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181024171144-74cb1d3d52f4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181025113841-85e1b3f9139a/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190102171810-8d7daa0c54b3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181207154023-610586996380/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0 h1:2mqDk8w/o6UmeUCu5Qiq2y7iMf6anbx+YA8d1JFoFrs= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181003184128-c57b0facaced/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180921163948-d47a0f339242/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180927150500-dad3d9fb7b6e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181005133103-4497e2df6f9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181022134430-8a28ead16f52/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181024145615-5cd93ef61a7c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181025063200-d989b31c8746/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026064943-731415f00dce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181106135930-3a76605856fd/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200121082415-34d275377bf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE= +golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180805044716-cb6730876b98/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181003024731-2f84ea8ef872/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181006002542-f60d9635b16a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181013182035-5e66757b835f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181017214349-06f26fdaaa28/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181024171208-a2dc47679d30/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181026183834-f60e5f99f081/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181105230042-78dc5bac0cac/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181107215632-34b416bd17b3/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181114190951-94339b83286c/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181119130350-139d099f6620/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181127195227-b4e97c0ed882/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181127232545-e782529d0ddd/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181203210056-e5f3ab76ea4b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181205224935-3576414c54a4/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181206194817-bcd4e47d0288/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181207183836-8bc39b988060/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181212172921-837e80568c09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190102213336-ca9055ed7d04/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190104182027-498d95493402/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190111214448-fc1d57b08d7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190118193359-16909d206f00/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190813034749-528a2984e271/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113232020-e2727e816f5a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200102140908-9497f49d5709/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200203215610-ab391d50b528/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204192400-7124308813f3/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200228224639-71482053b885/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20191229114700-bbb4dff026f8/go.mod h1:2IgXn/sJaRbePPBA1wRj8OE+QLvVaH0q8SK6TSTKlnk= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.0.0-20200111075622-4abb28f724d5/go.mod h1:+HbaZVpsa73UwN7kXGCECULRHovLRJjH+t5cFPgxErs= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8= google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= +gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.1.9/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= +k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN7Vh59Bw0vh9jhoX+V58= +k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= +k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8= +k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= +k8s.io/client-go v12.0.0+incompatible/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= +k8s.io/kube-openapi v0.0.0-20190722073852-5e22f3d471e6/go.mod h1:RZvgC8MSN6DjiMV6oIfEE9pDL9CYXokkfaCKZeHm3nc= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +k8s.io/utils v0.0.0-20190809000727-6c36bc71fc4a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= +mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index 0b83796a8..b9397f975 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -23,8 +23,11 @@ import ( "google.golang.org/grpc" + "go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/exporters/otlp" sdktrace "go.opentelemetry.io/otel/sdk/trace" + + "github.com/open-telemetry/opentelemetry-collector/translator/conventions" ) func main() { @@ -39,6 +42,9 @@ func main() { tp, _ := sdktrace.NewProvider( sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), + sdktrace.WithResourceAttributes( + core.Key(conventions.AttributeServiceName).String("test-service"), + ), sdktrace.WithBatcher(exp, // add following two options to ensure flush sdktrace.WithScheduleDelayMillis(5), sdktrace.WithMaxExportBatchSize(2), @@ -46,6 +52,7 @@ func main() { if err != nil { log.Fatalf("error creating trace provider: %v\n", err) } + tracer := tp.Tracer("test-tracer") // Then use the OpenTelemetry tracing library, like we normally would. From 46477e2fa41d005ffa96668269d4c6cb0bbcb397 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Wed, 13 May 2020 12:55:33 +0200 Subject: [PATCH 024/108] fix precommit --- example/otel-collector/go.sum | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example/otel-collector/go.sum b/example/otel-collector/go.sum index f2bdcc4d6..1d84a900a 100644 --- a/example/otel-collector/go.sum +++ b/example/otel-collector/go.sum @@ -593,6 +593,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1244,6 +1245,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From c40b3d47cf4aa0f024140013041607a41f184325 Mon Sep 17 00:00:00 2001 From: Ahmed Mujtaba Date: Wed, 13 May 2020 19:43:20 +0200 Subject: [PATCH 025/108] minor improvements in grpc interceptor test --- plugin/grpctrace/interceptor_test.go | 36 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/plugin/grpctrace/interceptor_test.go b/plugin/grpctrace/interceptor_test.go index 2dce41797..3667b9dbe 100644 --- a/plugin/grpctrace/interceptor_test.go +++ b/plugin/grpctrace/interceptor_test.go @@ -29,13 +29,13 @@ import ( type testExporter struct { mu sync.Mutex - spanMap map[string][]*export.SpanData + spanMap map[string]*export.SpanData } func (t *testExporter) ExportSpan(ctx context.Context, s *export.SpanData) { t.mu.Lock() defer t.mu.Unlock() - t.spanMap[s.Name] = append(t.spanMap[s.Name], s) + t.spanMap[s.Name] = s } type mockUICInvoker struct { @@ -60,7 +60,7 @@ func (mm *mockProtoMessage) ProtoMessage() { } func TestUnaryClientInterceptor(t *testing.T) { - exp := &testExporter{spanMap: make(map[string][]*export.SpanData)} + exp := &testExporter{spanMap: make(map[string]*export.SpanData)} tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{ DefaultSampler: sdktrace.AlwaysSample(), @@ -135,15 +135,21 @@ func TestUnaryClientInterceptor(t *testing.T) { for _, check := range checks { err = unaryInterceptor(context.Background(), check.name, req, reply, clientConn, uniInterceptorInvoker.invoker) if err != nil { - t.Fatalf("failed to run unary interceptor: %v", err) + t.Errorf("failed to run unary interceptor: %v", err) + continue } spanData, ok := exp.spanMap[check.name] - if !ok || len(spanData) == 0 { - t.Fatalf("no span data found for name < %s >", check.name) + if !ok { + t.Errorf("no span data found for name < %s >", check.name) + continue } - attrs := spanData[0].Attributes + attrs := spanData.Attributes + if len(check.expectedAttr) > len(attrs) { + t.Errorf("attributes received are less than expected attributes, received %d, expected %d", + len(attrs), len(check.expectedAttr)) + } for _, attr := range attrs { expectedAttr, ok := check.expectedAttr[attr.Key] if ok { @@ -162,7 +168,11 @@ func TestUnaryClientInterceptor(t *testing.T) { } } - events := spanData[0].MessageEvents + events := spanData.MessageEvents + if len(check.eventsAttr) > len(events) { + t.Errorf("events received are less than expected events, received %d, expected %d", + len(events), len(check.eventsAttr)) + } for event := 0; event < len(check.eventsAttr); event++ { for _, attr := range events[event].Attributes { expectedAttr, ok := check.eventsAttr[event][attr.Key] @@ -196,7 +206,7 @@ func (mockClientStream) Header() (metadata.MD, error) { return nil, nil } func (mockClientStream) Trailer() metadata.MD { return nil } func TestStreamClientInterceptor(t *testing.T) { - exp := &testExporter{spanMap: make(map[string][]*export.SpanData)} + exp := &testExporter{spanMap: make(map[string]*export.SpanData)} tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{ DefaultSampler: sdktrace.AlwaysSample(), @@ -251,7 +261,7 @@ func TestStreamClientInterceptor(t *testing.T) { _ = streamClient.RecvMsg(reply) // added retry because span end is called in separate go routine - var spanData []*export.SpanData + var spanData *export.SpanData for retry := 0; retry < 5; retry++ { ok := false exp.mu.Lock() @@ -262,11 +272,11 @@ func TestStreamClientInterceptor(t *testing.T) { } time.Sleep(time.Second * 1) } - if len(spanData) == 0 { + if spanData == nil { t.Fatalf("no span data found for name < %s >", methodName) } - attrs := spanData[0].Attributes + attrs := spanData.Attributes expectedAttr := map[core.Key]string{ rpcServiceKey: "serviceName", netPeerIPKey: "fake", @@ -283,7 +293,7 @@ func TestStreamClientInterceptor(t *testing.T) { } } - events := spanData[0].MessageEvents + events := spanData.MessageEvents if len(events) != 20 { t.Fatalf("incorrect number of events expected 20 got %d", len(events)) } From e9f185c396fb1c46aec1a4ce4fb03e78daab31ae Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Thu, 14 May 2020 11:11:51 +0200 Subject: [PATCH 026/108] add k8s files --- example/otel-collector/Makefile | 34 + example/otel-collector/k8s/jaeger/jaeger.yaml | 5 + .../k8s/jaeger/jaeger_cluster_role.yaml | 189 + .../jaeger/jaeger_cluster_role_binding.yaml | 12 + .../k8s/jaeger/jaeger_operator.yaml | 39 + .../k8s/jaeger/jaeger_role.yaml | 159 + .../k8s/jaeger/jaeger_role_binding.yaml | 12 + .../k8s/jaeger/jaeger_service_account.yaml | 5 + .../jaeger/jaegertracing.io_jaegers_crd.yaml | 9835 +++++++++++++++++ example/otel-collector/k8s/namespace.yaml | 4 + .../{ => k8s}/otel-collector.yaml | 15 +- 11 files changed, 10305 insertions(+), 4 deletions(-) create mode 100644 example/otel-collector/Makefile create mode 100644 example/otel-collector/k8s/jaeger/jaeger.yaml create mode 100644 example/otel-collector/k8s/jaeger/jaeger_cluster_role.yaml create mode 100644 example/otel-collector/k8s/jaeger/jaeger_cluster_role_binding.yaml create mode 100644 example/otel-collector/k8s/jaeger/jaeger_operator.yaml create mode 100644 example/otel-collector/k8s/jaeger/jaeger_role.yaml create mode 100644 example/otel-collector/k8s/jaeger/jaeger_role_binding.yaml create mode 100644 example/otel-collector/k8s/jaeger/jaeger_service_account.yaml create mode 100644 example/otel-collector/k8s/jaeger/jaegertracing.io_jaegers_crd.yaml create mode 100644 example/otel-collector/k8s/namespace.yaml rename example/otel-collector/{ => k8s}/otel-collector.yaml (89%) diff --git a/example/otel-collector/Makefile b/example/otel-collector/Makefile new file mode 100644 index 000000000..699bf649f --- /dev/null +++ b/example/otel-collector/Makefile @@ -0,0 +1,34 @@ +create-k8s-namespace: + k apply -f k8s/namespace.yaml + +jaeger-operator-k8s: + # Create the jaeger operator and necessary artifacts in ns observability + kubectl apply -f k8s/jaeger/jaegertracing.io_jaegers_crd.yaml + kubectl apply -f k8s/jaeger/jaeger_service_account.yaml + kubectl apply -f k8s/jaeger/jaeger_role.yaml + kubectl apply -f k8s/jaeger/jaeger_role_binding.yaml + kubectl apply -f k8s/jaeger/jaeger_operator.yaml + + # Create the cluster role & bindings + kubectl apply -f k8s/jaeger/jaeger_cluster_role.yaml + kubectl apply -f k8s/jaeger/jaeger_cluster_role_binding.yaml + +jaeger-k8s: + kubectl apply -f k8s/jaeger/jaeger.yaml + +otel-collector-k8s: + kubectl apply -f k8s/otel-collector.yaml + +clean-k8s: + kubectl delete -f k8s/otel-collector.yaml + + kubectl delete -f k8s/jaeger/jaeger.yaml + + kubectl delete -f k8s/jaeger/jaeger_cluster_role.yaml + kubectl delete -f k8s/jaeger/jaeger_cluster_role_binding.yaml + + kubectl delete -f k8s/jaeger/jaegertracing.io_jaegers_crd.yaml + kubectl delete -f k8s/jaeger/jaeger_service_account.yaml + kubectl delete -f k8s/jaeger/jaeger_role.yaml + kubectl delete -f k8s/jaeger/jaeger_role_binding.yaml + kubectl delete -f k8s/jaeger/jaeger_operator.yaml \ No newline at end of file diff --git a/example/otel-collector/k8s/jaeger/jaeger.yaml b/example/otel-collector/k8s/jaeger/jaeger.yaml new file mode 100644 index 000000000..98291ac90 --- /dev/null +++ b/example/otel-collector/k8s/jaeger/jaeger.yaml @@ -0,0 +1,5 @@ +apiVersion: jaegertracing.io/v1 +kind: Jaeger +metadata: + name: jaeger + namespace: observability \ No newline at end of file diff --git a/example/otel-collector/k8s/jaeger/jaeger_cluster_role.yaml b/example/otel-collector/k8s/jaeger/jaeger_cluster_role.yaml new file mode 100644 index 000000000..660d48fc6 --- /dev/null +++ b/example/otel-collector/k8s/jaeger/jaeger_cluster_role.yaml @@ -0,0 +1,189 @@ +## When using the operator in cluster-wide mode, this ClusterRole has to be created and bound to the jaeger-operator service account, +## so that the operator can watch and create resources in every namespace in the cluster. +## An alternative to this cluster role is to create one role binding for each namespace that the operator should watch +## in that case, don't forget to add a comma-separated list of namespaces as WATCH_NAMESPACE in the operator's deployment. +## Further down in this file there's another set of rules, with extra optional permissions +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: jaeger-operator +rules: + +## our own custom resources +- apiGroups: + - jaegertracing.io + resources: + - '*' + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' + +## for the operator's own deployment +- apiGroups: + - apps + resourceNames: + - jaeger-operator + resources: + - deployments/finalizers + verbs: + - update + +## regular things the operator manages for an instance, as the result of processing CRs +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + - services + - services/finalizers + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - apps + resources: + - deployments + - daemonsets + - replicasets + - statefulsets + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - batch + resources: + - jobs + - cronjobs + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' + +## needed if you want the operator to create service monitors for the Jaeger instances +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' + +## for the Elasticsearch auto-provisioning +- apiGroups: + - logging.openshift.io + resources: + - elasticsearches + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' + +## for the Kafka auto-provisioning +- apiGroups: + - kafka.strimzi.io + resources: + - kafkas + - kafkausers + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' + +## Extra permissions +## This is an extra set of permissions that the Jaeger Operator might make use of if granted + +## needed if support for injecting sidecars based on namespace annotation is required +- apiGroups: + - "" + resources: + - namespaces + verbs: + - 'get' + - 'list' + - 'watch' + +## needed if support for injecting sidecars based on deployment annotation is required, across all namespaces +- apiGroups: + - apps + resources: + - deployments + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'watch' + +## needed only when .Spec.Ingress.Openshift.DelegateUrls is used +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' diff --git a/example/otel-collector/k8s/jaeger/jaeger_cluster_role_binding.yaml b/example/otel-collector/k8s/jaeger/jaeger_cluster_role_binding.yaml new file mode 100644 index 000000000..ab474d086 --- /dev/null +++ b/example/otel-collector/k8s/jaeger/jaeger_cluster_role_binding.yaml @@ -0,0 +1,12 @@ +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: jaeger-operator +subjects: +- kind: ServiceAccount + name: jaeger-operator + namespace: "observability" # change to point to the namespace you installed your operator +roleRef: + kind: ClusterRole + name: jaeger-operator + apiGroup: rbac.authorization.k8s.io diff --git a/example/otel-collector/k8s/jaeger/jaeger_operator.yaml b/example/otel-collector/k8s/jaeger/jaeger_operator.yaml new file mode 100644 index 000000000..5dedda43f --- /dev/null +++ b/example/otel-collector/k8s/jaeger/jaeger_operator.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: jaeger-operator + namespace: observability +spec: + replicas: 1 + selector: + matchLabels: + name: jaeger-operator + template: + metadata: + labels: + name: jaeger-operator + spec: + serviceAccountName: jaeger-operator + containers: + - name: jaeger-operator + image: jaegertracing/jaeger-operator:1.17.1 + ports: + - containerPort: 8383 + name: metrics + args: ["start"] + imagePullPolicy: Always + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: OPERATOR_NAME + value: "jaeger-operator" diff --git a/example/otel-collector/k8s/jaeger/jaeger_role.yaml b/example/otel-collector/k8s/jaeger/jaeger_role.yaml new file mode 100644 index 000000000..a92604d75 --- /dev/null +++ b/example/otel-collector/k8s/jaeger/jaeger_role.yaml @@ -0,0 +1,159 @@ +## this is a set of basic permissions the Jaeger Operator needs when restricted to work in specific namespaces +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: jaeger-operator + namespace: observability +rules: + +## our own custom resources +- apiGroups: + - jaegertracing.io + resources: + - '*' + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' + +## for the operator's own deployment +- apiGroups: + - apps + resourceNames: + - jaeger-operator + resources: + - deployments/finalizers + verbs: + - update + +## regular things the operator manages for an instance, as the result of processing CRs +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + - services + - services/finalizers + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - apps + resources: + - deployments + - daemonsets + - replicasets + - statefulsets + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - batch + resources: + - jobs + - cronjobs + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - image.openshift.io + resources: + - imagestreams + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' +- apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' + +## needed if you want the operator to create service monitors for the Jaeger instances +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' + +## for the Elasticsearch auto-provisioning +- apiGroups: + - logging.openshift.io + resources: + - elasticsearches + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' + +## for the Kafka auto-provisioning +- apiGroups: + - kafka.strimzi.io + resources: + - kafkas + - kafkausers + verbs: + - 'get' + - 'list' + - 'create' + - 'update' + - 'delete' + - 'watch' diff --git a/example/otel-collector/k8s/jaeger/jaeger_role_binding.yaml b/example/otel-collector/k8s/jaeger/jaeger_role_binding.yaml new file mode 100644 index 000000000..2741fbbd0 --- /dev/null +++ b/example/otel-collector/k8s/jaeger/jaeger_role_binding.yaml @@ -0,0 +1,12 @@ +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: jaeger-operator + namespace: observability +subjects: +- kind: ServiceAccount + name: jaeger-operator +roleRef: + kind: Role + name: jaeger-operator + apiGroup: rbac.authorization.k8s.io diff --git a/example/otel-collector/k8s/jaeger/jaeger_service_account.yaml b/example/otel-collector/k8s/jaeger/jaeger_service_account.yaml new file mode 100644 index 000000000..03d499e49 --- /dev/null +++ b/example/otel-collector/k8s/jaeger/jaeger_service_account.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + namespace: observability + name: jaeger-operator diff --git a/example/otel-collector/k8s/jaeger/jaegertracing.io_jaegers_crd.yaml b/example/otel-collector/k8s/jaeger/jaegertracing.io_jaegers_crd.yaml new file mode 100644 index 000000000..7db755177 --- /dev/null +++ b/example/otel-collector/k8s/jaeger/jaegertracing.io_jaegers_crd.yaml @@ -0,0 +1,9835 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.4 + creationTimestamp: null + name: jaegers.jaegertracing.io +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Jaeger instance's status + name: Status + type: string + - JSONPath: .status.version + description: Jaeger Version + name: Version + type: string + - JSONPath: .spec.strategy + description: Jaeger deployment strategy + name: Strategy + type: string + - JSONPath: .spec.storage.type + description: Jaeger storage type + name: Storage + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: jaegertracing.io + names: + kind: Jaeger + listKind: JaegerList + plural: jaegers + singular: jaeger + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + agent: + nullable: true + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + config: + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + options: + type: object + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + strategy: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + allInOne: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + options: + type: object + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + collector: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + autoscale: + type: boolean + config: + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + maxReplicas: + format: int32 + type: integer + minReplicas: + format: int32 + type: integer + options: + type: object + replicas: + format: int32 + type: integer + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + ingester: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + autoscale: + type: boolean + config: + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + maxReplicas: + format: int32 + type: integer + minReplicas: + format: int32 + type: integer + options: + type: object + replicas: + format: int32 + type: integer + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + ingress: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + enabled: + type: boolean + hosts: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + openshift: + properties: + delegateUrls: + type: string + htpasswdFile: + type: string + sar: + type: string + skipLogout: + type: boolean + type: object + options: + type: object + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + secretName: + type: string + security: + type: string + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + tls: + items: + properties: + hosts: + items: + type: string + type: array + secretName: + type: string + type: object + type: array + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + labels: + additionalProperties: + type: string + type: object + query: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + options: + type: object + replicas: + format: int32 + type: integer + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + sampling: + properties: + options: + type: object + type: object + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + storage: + properties: + cassandraCreateSchema: + properties: + datacenter: + type: string + enabled: + type: boolean + image: + type: string + mode: + type: string + timeout: + type: string + ttlSecondsAfterFinished: + format: int32 + type: integer + type: object + dependencies: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + cassandraClientAuthEnabled: + type: boolean + elasticsearchClientNodeOnly: + type: boolean + elasticsearchNodesWanOnly: + type: boolean + enabled: + type: boolean + image: + type: string + javaOpts: + type: string + labels: + additionalProperties: + type: string + type: object + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + schedule: + type: string + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + sparkMaster: + type: string + successfulJobsHistoryLimit: + format: int32 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + ttlSecondsAfterFinished: + format: int32 + type: integer + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + elasticsearch: + properties: + image: + type: string + nodeCount: + format: int32 + type: integer + nodeSelector: + additionalProperties: + type: string + type: object + redundancyPolicy: + type: string + resources: + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + storage: + properties: + size: + type: string + storageClassName: + type: string + type: object + type: object + esIndexCleaner: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + enabled: + type: boolean + image: + type: string + labels: + additionalProperties: + type: string + type: object + numberOfDays: + type: integer + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + schedule: + type: string + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + successfulJobsHistoryLimit: + format: int32 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + ttlSecondsAfterFinished: + format: int32 + type: integer + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + esRollover: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + nullable: true + type: object + conditions: + type: string + image: + type: string + labels: + additionalProperties: + type: string + type: object + readTTL: + type: string + resources: + nullable: true + properties: + limits: + additionalProperties: + type: string + type: object + requests: + additionalProperties: + type: string + type: object + type: object + schedule: + type: string + securityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + successfulJobsHistoryLimit: + format: int32 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + ttlSecondsAfterFinished: + format: int32 + type: integer + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + options: + type: object + secretName: + type: string + type: + type: string + type: object + strategy: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + ui: + properties: + options: + type: object + type: object + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + type: string + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + properties: + phase: + type: string + version: + type: string + required: + - phase + - version + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/example/otel-collector/k8s/namespace.yaml b/example/otel-collector/k8s/namespace.yaml new file mode 100644 index 000000000..4872021cb --- /dev/null +++ b/example/otel-collector/k8s/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: observability \ No newline at end of file diff --git a/example/otel-collector/otel-collector.yaml b/example/otel-collector/k8s/otel-collector.yaml similarity index 89% rename from example/otel-collector/otel-collector.yaml rename to example/otel-collector/k8s/otel-collector.yaml index 005a4477a..f6111b93d 100644 --- a/example/otel-collector/otel-collector.yaml +++ b/example/otel-collector/k8s/otel-collector.yaml @@ -2,6 +2,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: otel-collector-conf + namespace: observability labels: app: opentelemetry component: otel-collector-conf @@ -17,7 +18,7 @@ data: exporters: jaeger: # Replace with a real endpoint. - endpoint: "simplest-collector.default.svc.cluster.local:14250" + endpoint: "jaeger-collector.observability.svc.cluster.local:14250" service: extensions: [health_check] pipelines: @@ -30,15 +31,20 @@ apiVersion: v1 kind: Service metadata: name: otel-collector + namespace: observability labels: - app: opencensus + app: opentelemetry component: otel-collector spec: ports: - - name: otlp # Default endpoint for Opencensus receiver. - port: 55680 + - name: otlp # Default endpoint for otlp receiver. + port: 8889 protocol: TCP targetPort: 55680 + - name: metrics # Default endpoint for metrics. + port: 8888 + protocol: TCP + targetPort: 8888 selector: component: otel-collector --- @@ -46,6 +52,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: otel-collector + namespace: observability labels: app: opentelemetry component: otel-collector From 2e6011db7adb148c353c8dad597cbcd3f1694981 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Thu, 14 May 2020 18:18:22 +0200 Subject: [PATCH 027/108] change collector image --- example/otel-collector/k8s/otel-collector.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/otel-collector/k8s/otel-collector.yaml b/example/otel-collector/k8s/otel-collector.yaml index f6111b93d..c0f0b11f3 100644 --- a/example/otel-collector/k8s/otel-collector.yaml +++ b/example/otel-collector/k8s/otel-collector.yaml @@ -16,16 +16,16 @@ data: extensions: health_check: {} exporters: - jaeger: + jaeger_grpc: # Replace with a real endpoint. endpoint: "jaeger-collector.observability.svc.cluster.local:14250" service: extensions: [health_check] pipelines: - traces/2: + traces: receivers: [otlp] processors: [] - exporters: [jaeger] + exporters: [jaeger_grpc] --- apiVersion: v1 kind: Service @@ -38,7 +38,7 @@ metadata: spec: ports: - name: otlp # Default endpoint for otlp receiver. - port: 8889 + port: 55680 protocol: TCP targetPort: 55680 - name: metrics # Default endpoint for metrics. @@ -83,7 +83,7 @@ spec: env: - name: GOGC value: "80" - image: otel/opentelemetry-collector-dev:latest + image: otel/opentelemetry-collector:0.3.0 name: otel-collector resources: limits: From 612e894247ee3a7d37877102b39640987fdab89e Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Thu, 14 May 2020 18:52:38 +0200 Subject: [PATCH 028/108] fix otlp receiver endpoint --- example/otel-collector/k8s/otel-collector.yaml | 3 ++- example/otel-collector/main.go | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/k8s/otel-collector.yaml b/example/otel-collector/k8s/otel-collector.yaml index c0f0b11f3..3ad0ce2d9 100644 --- a/example/otel-collector/k8s/otel-collector.yaml +++ b/example/otel-collector/k8s/otel-collector.yaml @@ -11,7 +11,8 @@ data: receivers: # Make sure to add the otlp receiver. # This will use the default configuration, and open up the receiver on port 55680 - otlp: {} + otlp: + endpoint: 0.0.0.0:55680 processors: extensions: health_check: {} diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index e1220615a..9ee007b0e 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -32,6 +32,8 @@ import ( func main() { exp, err := otlp.NewExporter(otlp.WithInsecure(), + // Replace the address with the actual address of the collector service + otlp.WithAddress("10.152.183.133:55680"), otlp.WithGRPCDialOption(grpc.WithBlock())) if err != nil { log.Fatalf("Failed to create the collector exporter: %v", err) From 2719c0ac167553b9152c1bf2e99a07a1356c2c8c Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Wed, 6 May 2020 17:46:54 +0300 Subject: [PATCH 029/108] Rewrite processQueue for better batching --- sdk/trace/batch_span_processor.go | 83 ++++++++++++++++++------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index a66d2d65e..2b06dac86 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -103,23 +103,10 @@ func NewBatchSpanProcessor(e export.SpanBatcher, opts ...BatchSpanProcessorOptio bsp.stopCh = make(chan struct{}) - // Start timer to export spans. - ticker := time.NewTicker(bsp.o.ScheduledDelayMillis) bsp.stopWait.Add(1) go func() { - defer ticker.Stop() - batch := make([]*export.SpanData, 0, bsp.o.MaxExportBatchSize) - for { - select { - case <-bsp.stopCh: - bsp.processQueue(&batch) - close(bsp.queue) - bsp.stopWait.Done() - return - case <-ticker.C: - bsp.processQueue(&batch) - } - } + defer bsp.stopWait.Done() + bsp.processQueue() }() return bsp, nil @@ -167,32 +154,58 @@ func WithBlocking() BatchSpanProcessorOption { } } -// processQueue removes spans from the `queue` channel until there is -// no more data. It calls the exporter in batches of up to -// MaxExportBatchSize until all the available data have been processed. -func (bsp *BatchSpanProcessor) processQueue(batch *[]*export.SpanData) { +// processQueue removes spans from the `queue` channel until processor +// is shut down. It calls the exporter in batches of up to MaxExportBatchSize +// waiting up to ScheduledDelayMillis to form a batch. +func (bsp *BatchSpanProcessor) processQueue() { + ticker := time.NewTicker(bsp.o.ScheduledDelayMillis) + defer ticker.Stop() + + batch := make([]*export.SpanData, 0, bsp.o.MaxExportBatchSize) + + exportSpans := func() { + if len(batch) > 0 { + bsp.e.ExportSpans(context.Background(), batch) + batch = batch[:0] + } + } + +loop: for { - // Read spans until either the buffer fills or the - // queue is empty. - for ok := true; ok && len(*batch) < bsp.o.MaxExportBatchSize; { - select { - case sd := <-bsp.queue: - if sd != nil && sd.SpanContext.IsSampled() { - *batch = append(*batch, sd) + select { + case <-bsp.stopCh: + break loop + case <-ticker.C: + exportSpans() + case sd := <-bsp.queue: + if sd.SpanContext.IsSampled() { + batch = append(batch, sd) + if len(batch) == bsp.o.MaxExportBatchSize { + ticker.Reset(bsp.o.ScheduledDelayMillis) + exportSpans() } - default: - ok = false } } + } - if len(*batch) == 0 { - return + // Consume queue before close to unblock enqueue and prevent + // "panic: send on closed channel". + for { + select { + case sd := <-bsp.queue: + if sd == nil { + exportSpans() + return + } + if sd.SpanContext.IsSampled() { + batch = append(batch, sd) + if len(batch) == bsp.o.MaxExportBatchSize { + exportSpans() + } + } + default: + close(bsp.queue) } - - // Send one batch, then continue reading until the - // buffer is empty. - bsp.e.ExportSpans(context.Background(), *batch) - *batch = (*batch)[:0] } } From 891d16dd15e1a47dfa927fb7d1cb9d8d37759c2c Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Thu, 14 May 2020 14:02:04 +0300 Subject: [PATCH 030/108] Replace Ticker with Timer since Ticker does not Reset yet --- sdk/trace/batch_span_processor.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index 2b06dac86..f3845b1dc 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -158,12 +158,17 @@ func WithBlocking() BatchSpanProcessorOption { // is shut down. It calls the exporter in batches of up to MaxExportBatchSize // waiting up to ScheduledDelayMillis to form a batch. func (bsp *BatchSpanProcessor) processQueue() { - ticker := time.NewTicker(bsp.o.ScheduledDelayMillis) - defer ticker.Stop() + timer := time.NewTimer(bsp.o.ScheduledDelayMillis) + defer timer.Stop() batch := make([]*export.SpanData, 0, bsp.o.MaxExportBatchSize) exportSpans := func() { + if !timer.Stop() { + <-timer.C + } + timer.Reset(bsp.o.ScheduledDelayMillis) + if len(batch) > 0 { bsp.e.ExportSpans(context.Background(), batch) batch = batch[:0] @@ -175,13 +180,12 @@ loop: select { case <-bsp.stopCh: break loop - case <-ticker.C: + case <-timer.C: exportSpans() case sd := <-bsp.queue: if sd.SpanContext.IsSampled() { batch = append(batch, sd) if len(batch) == bsp.o.MaxExportBatchSize { - ticker.Reset(bsp.o.ScheduledDelayMillis) exportSpans() } } From ab19dddd0fc09e1122e60b0042c7a793083980e7 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Thu, 14 May 2020 14:02:22 +0300 Subject: [PATCH 031/108] Update tests --- sdk/trace/batch_span_processor_test.go | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/sdk/trace/batch_span_processor_test.go b/sdk/trace/batch_span_processor_test.go index 9dcd086aa..b6eb164ee 100644 --- a/sdk/trace/batch_span_processor_test.go +++ b/sdk/trace/batch_span_processor_test.go @@ -69,30 +69,26 @@ type testOption struct { wantNumSpans int wantBatchCount int genNumSpans int - waitTime time.Duration parallel bool } func TestNewBatchSpanProcessorWithOptions(t *testing.T) { schDelay := 200 * time.Millisecond - waitTime := schDelay + 100*time.Millisecond options := []testOption{ { name: "default BatchSpanProcessorOptions", - wantNumSpans: 2048, + wantNumSpans: 2053, wantBatchCount: 4, genNumSpans: 2053, - waitTime: 5100 * time.Millisecond, }, { name: "non-default ScheduledDelayMillis", o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithScheduleDelayMillis(schDelay), }, - wantNumSpans: 2048, + wantNumSpans: 2053, wantBatchCount: 4, genNumSpans: 2053, - waitTime: waitTime, }, { name: "non-default MaxQueueSize and ScheduledDelayMillis", @@ -100,10 +96,9 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { sdktrace.WithScheduleDelayMillis(schDelay), sdktrace.WithMaxQueueSize(200), }, - wantNumSpans: 200, + wantNumSpans: 205, wantBatchCount: 1, genNumSpans: 205, - waitTime: waitTime, }, { name: "non-default MaxQueueSize, ScheduledDelayMillis and MaxExportBatchSize", @@ -112,10 +107,9 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { sdktrace.WithMaxQueueSize(205), sdktrace.WithMaxExportBatchSize(20), }, - wantNumSpans: 205, + wantNumSpans: 210, wantBatchCount: 11, genNumSpans: 210, - waitTime: waitTime, }, { name: "blocking option", @@ -128,7 +122,6 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { wantNumSpans: 205, wantBatchCount: 11, genNumSpans: 205, - waitTime: waitTime, }, { name: "parallel span generation", @@ -136,10 +129,9 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { sdktrace.WithScheduleDelayMillis(schDelay), sdktrace.WithMaxQueueSize(200), }, - wantNumSpans: 200, + wantNumSpans: 205, wantBatchCount: 1, genNumSpans: 205, - waitTime: waitTime, parallel: true, }, { @@ -152,7 +144,6 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { wantNumSpans: 2000, wantBatchCount: 10, genNumSpans: 2000, - waitTime: waitTime, parallel: true, }, } @@ -168,8 +159,6 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { generateSpan(t, option.parallel, tr, option) - time.Sleep(option.waitTime) - tp.UnregisterSpanProcessor(ssp) gotNumOfSpans := te.len() @@ -182,8 +171,6 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { t.Errorf("%s: number batches: got %+v, want >= %+v\n", option.name, gotBatchCount, option.wantBatchCount) t.Errorf("Batches %v\n", te.sizes) } - - tp.UnregisterSpanProcessor(ssp) } } From 774889cbfa8f55f57f82431eee2d71e99684dd60 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Thu, 14 May 2020 14:25:41 +0300 Subject: [PATCH 032/108] Add proper enqueue sync --- sdk/trace/batch_span_processor.go | 39 +++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index f3845b1dc..4b320eb4d 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -17,6 +17,7 @@ package trace import ( "context" "errors" + "log" "sync" "sync/atomic" "time" @@ -70,9 +71,10 @@ type BatchSpanProcessor struct { queue chan *export.SpanData dropped uint32 - stopWait sync.WaitGroup - stopOnce sync.Once - stopCh chan struct{} + enqueueWait sync.WaitGroup + stopWait sync.WaitGroup + stopOnce sync.Once + stopCh chan struct{} } var _ SpanProcessor = (*BatchSpanProcessor)(nil) @@ -192,45 +194,58 @@ loop: } } - // Consume queue before close to unblock enqueue and prevent - // "panic: send on closed channel". + go func() { + bsp.enqueueWait.Wait() + close(bsp.queue) + }() + for { + if !timer.Stop() { + <-timer.C + } + const waitTimeout = 30 * time.Second + timer.Reset(waitTimeout) + select { case sd := <-bsp.queue: if sd == nil { exportSpans() return } + if sd.SpanContext.IsSampled() { batch = append(batch, sd) if len(batch) == bsp.o.MaxExportBatchSize { exportSpans() } } - default: - close(bsp.queue) + case <-timer.C: + log.Println("bsp.enqueueWait timeout") + exportSpans() + return } } } func (bsp *BatchSpanProcessor) enqueue(sd *export.SpanData) { + bsp.enqueueWait.Add(1) + select { case <-bsp.stopCh: + bsp.enqueueWait.Done() return default: } + if bsp.o.BlockOnQueueFull { bsp.queue <- sd } else { - var ok bool select { case bsp.queue <- sd: - ok = true default: - ok = false - } - if !ok { atomic.AddUint32(&bsp.dropped, 1) } } + + bsp.enqueueWait.Done() } From 8885bc404d4752340785dbe60cc3bc2883f84554 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Thu, 14 May 2020 14:26:28 +0300 Subject: [PATCH 033/108] Move IsSampled check --- sdk/trace/batch_span_processor.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index 4b320eb4d..bde8aef2b 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -185,11 +185,9 @@ loop: case <-timer.C: exportSpans() case sd := <-bsp.queue: - if sd.SpanContext.IsSampled() { - batch = append(batch, sd) - if len(batch) == bsp.o.MaxExportBatchSize { - exportSpans() - } + batch = append(batch, sd) + if len(batch) == bsp.o.MaxExportBatchSize { + exportSpans() } } } @@ -213,11 +211,9 @@ loop: return } - if sd.SpanContext.IsSampled() { - batch = append(batch, sd) - if len(batch) == bsp.o.MaxExportBatchSize { - exportSpans() - } + batch = append(batch, sd) + if len(batch) == bsp.o.MaxExportBatchSize { + exportSpans() } case <-timer.C: log.Println("bsp.enqueueWait timeout") @@ -228,6 +224,10 @@ loop: } func (bsp *BatchSpanProcessor) enqueue(sd *export.SpanData) { + if !sd.SpanContext.IsSampled() { + return + } + bsp.enqueueWait.Add(1) select { From 88d9ad0ba8789066a8c1fe225beb0a429206aef7 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Fri, 15 May 2020 09:32:33 +0300 Subject: [PATCH 034/108] Add ref to #174 --- sdk/trace/batch_span_processor.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index bde8aef2b..12ad7282b 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -216,6 +216,7 @@ loop: exportSpans() } case <-timer.C: + //TODO: use error callback - see issue #174 log.Println("bsp.enqueueWait timeout") exportSpans() return From 28571207b7ff60b26fbb579285d5d11f1062d937 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Fri, 15 May 2020 09:36:04 +0300 Subject: [PATCH 035/108] Add a comment --- sdk/trace/batch_span_processor.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index 12ad7282b..568491025 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -201,6 +201,8 @@ loop: if !timer.Stop() { <-timer.C } + // This is not needed normally, but use some timeout so we are not stuck + // waiting for enqueueWait forever. const waitTimeout = 30 * time.Second timer.Reset(waitTimeout) From d623815dfce812c1ef74795ba0292043f1009e57 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Fri, 15 May 2020 11:46:53 +0200 Subject: [PATCH 036/108] Update README with latest changes --- example/otel-collector/Makefile | 20 +- example/otel-collector/README.md | 181 +++++++++++++----- .../otel-collector/k8s/otel-collector.yaml | 6 +- example/otel-collector/main.go | 8 +- 4 files changed, 156 insertions(+), 59 deletions(-) diff --git a/example/otel-collector/Makefile b/example/otel-collector/Makefile index 699bf649f..72e659b09 100644 --- a/example/otel-collector/Makefile +++ b/example/otel-collector/Makefile @@ -1,4 +1,4 @@ -create-k8s-namespace: +namespace-k8s: k apply -f k8s/namespace.yaml jaeger-operator-k8s: @@ -20,15 +20,15 @@ otel-collector-k8s: kubectl apply -f k8s/otel-collector.yaml clean-k8s: - kubectl delete -f k8s/otel-collector.yaml + - kubectl delete -f k8s/otel-collector.yaml - kubectl delete -f k8s/jaeger/jaeger.yaml + - kubectl delete -f k8s/jaeger/jaeger.yaml - kubectl delete -f k8s/jaeger/jaeger_cluster_role.yaml - kubectl delete -f k8s/jaeger/jaeger_cluster_role_binding.yaml + - kubectl delete -f k8s/jaeger/jaeger_cluster_role.yaml + - kubectl delete -f k8s/jaeger/jaeger_cluster_role_binding.yaml - kubectl delete -f k8s/jaeger/jaegertracing.io_jaegers_crd.yaml - kubectl delete -f k8s/jaeger/jaeger_service_account.yaml - kubectl delete -f k8s/jaeger/jaeger_role.yaml - kubectl delete -f k8s/jaeger/jaeger_role_binding.yaml - kubectl delete -f k8s/jaeger/jaeger_operator.yaml \ No newline at end of file + - kubectl delete -f k8s/jaeger/jaegertracing.io_jaegers_crd.yaml + - kubectl delete -f k8s/jaeger/jaeger_service_account.yaml + - kubectl delete -f k8s/jaeger/jaeger_role.yaml + - kubectl delete -f k8s/jaeger/jaeger_role_binding.yaml + - kubectl delete -f k8s/jaeger/jaeger_operator.yaml \ No newline at end of file diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 6636def30..ec9d1e562 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -3,90 +3,181 @@ This example illustrates how to export traces from the OpenTelemetry-Go SDK to the OpenTelemetry Collector, and from there to a Jaeger instance. The complete flow is: -`otel-collector-demo -> otel-collector -> Jaeger` +`Demo App -> OpenTelemetry Collector -> Jaeger` # Prerequisites -The demo assumes you already have both an instance of the OpenTelemetry Collector and Jaeger up and running. -For information about how to set these up, please follow the corresponding documentation: +The demo is built on Kubernetes, and uses a local instance of [microk8s](https://microk8s.io/). You will need access to a cluster in order to deploy the OpenTelemetry Collector and Jaeger components from this demo. -* Jaeger: https://www.jaegertracing.io/docs/1.17/getting-started/ -* OpenTelemetry Collector: https://opentelemetry.io/docs/collector/about/ +For simplicity, the demo application is not part of the k8s cluster, and will access the OpenTelemetry Collector through a NodePort on the cluster. Note that the NodePort opened by this demo is not secured. -Moreover, this demo is built on a Kubernetes cluster running both the OpenTelemetry Collector, and Jaeger. -Therefore, the included OpenTelemetry Collector configuration is a Kubernetes `ConfigMap`. -However, the main concepts should be transferable to any other platform you want to run this demo on. +Ideally you'd want to either have your application running as part of the kubernetes cluster, or use a secured connection (NodePort/LoadBalancer with TLS or an ingress extension). -# Configuring the OTEL Collector +# Deploying Jaeger and OpenTelemetry Collector +The first step of this demo is to deploy a Jaeger instance and a Collector to your cluster. All the necessary Kubernetes deployment files are available in this demo, in the [k8s](./k8s) folder. +There are two ways to create the necessary deployments for this demo: using the [makefile](./Makefile) or manually applying the k8s files. -In order to enable our application to send traces to the OpenTelemetry Collector, we need to first configure the OTLP receiver: +## Using the makefile + +For using the [makefile](./Makefile), run the following commands in order: +```bash +# Create the namespace +make namespace-k8s + +# Deploy Jaeger operator +make jaeger-operator-k8s + +# After the operator is deployed, create the Jaeger instance +make jaeger-k8s + +# Finally, deploy the OpenTelemetry Collector +make otel-collector-k8s +``` + +If you want to clean up after this, you can use the `make clean-k8s` to delete all the resources created above. Note that this will not remove the namespace. Because Kubernetes sometimes gets stuck when removing namespaces, please remove this namespace manually after all the resources inside have been deleted. + +## Manual deployment + +For manual deployments, follow the same steps as above, but instead run the `kubectl apply` yourself. + +First, the namespace needs to be created: +```bash +k apply -f k8s/namespace.yaml +``` + +Jaeger is then deployed via the operator, and the demo follows [these steps](https://github.com/jaegertracing/jaeger-operator#getting-started) to create it: +```bash +# Create the jaeger operator and necessary artifacts in ns observability +kubectl apply -f k8s/jaeger/jaegertracing.io_jaegers_crd.yaml +kubectl apply -f k8s/jaeger/jaeger_service_account.yaml +kubectl apply -f k8s/jaeger/jaeger_role.yaml +kubectl apply -f k8s/jaeger/jaeger_role_binding.yaml +kubectl apply -f k8s/jaeger/jaeger_operator.yaml + +# Create the cluster role & bindings +kubectl apply -f k8s/jaeger/jaeger_cluster_role.yaml +kubectl apply -f k8s/jaeger/jaeger_cluster_role_binding.yaml + +# Create the Jaeger instance itself: +kubectl apply -f k8s/jaeger/jaeger.yaml +``` + +The OpenTelemetry Collector is contained in a single k8s file, which can be deployed with one command: +```bash +k8s apply -f k8s/otel-collector.yaml +``` + + +# Configuring the OpenTelemetry Collector + +Although the above steps should deploy and configure both Jaeger and the OpenTelemetry Collector, it might be worth spending some time on the [configuration](./k8s/otel-collector.yaml) of the Collector. + +One important part here is that, in order to enable our application to send traces to the OpenTelemetry Collector, we need to first configure the otlp receiver: ```yml -receivers: - otlp: {} +... + otel-collector-config: | + receivers: + # Make sure to add the otlp receiver. + # This will open up the receiver on port 55680. + otlp: + endpoint: 0.0.0.0:55680 + processors: +... ``` This will create the receiver on the Collector side, and open up port `55680` for receiving traces. + The rest of the configuration is quite standard, with the only mention that we need to create the Jaeger exporter: ```yml -exporters: - jaeger: - # Replace with a real endpoint. - endpoint: ":14250" +... + exporters: + jaeger_grpc: + endpoint: "jaeger-collector.observability.svc.cluster.local:14250" +... ``` -After this, apply the configuration to your OpenTelemetry Collector instance (with `kubectl apply -f otel-controller-config.yaml` for k8s users). -You should see that the Collector creates the otlp receiver: +## OpenTelemetry Collector service -```json -{"level":"info","ts":1589184143.206609,"caller":"builder/receivers_builder.go:79","msg":"Receiver started.","component_kind":"receiver","component_type":"otlp","component_name":"otlp"} -``` -and the Jaeger exporter: -```json -{"level":"info","ts":1589184143.1535392,"caller":"builder/exporters_builder.go:94","msg":"Exporter started.","component_kind":"exporter","component_type":"jaeger","component_name":"jaeger"} +One more aspect in the OpenTelemetry Collector [configuration](./k8s/otel-collector.yaml) worth looking at is the NodePort service used for accessing it: +```yaml +apiVersion: v1 +kind: Service +metadata: + ... +spec: + ports: + - name: otlp # Default endpoint for otlp receiver. + port: 55680 + protocol: TCP + targetPort: 55680 + nodePort: 30080 + - name: metrics # Default endpoint for metrics. + port: 8888 + protocol: TCP + targetPort: 8888 + selector: + component: otel-collector + type: + NodePort ``` +This service will bind the `55680` port used to access the otlp receiver to port `30080` on your cluster's node. By doing so, it makes it possible for us to access the Collector by using the static address `:30080`. In case you are running a local cluster, this will be `localhost:30080`. Note that you can also change this to a LoadBalancer or have an ingress extension for accessing the service. + + # Writing the demo -Having the OpenTelemetry Collector started with the OTLP port open for traces, and connected to Jaeger, let's look at the go app that will send traces to the Collector. +Having the OpenTelemetry Collector started with the otlp port open for traces, and connected to Jaeger, let's look at the go app that will send traces to the Collector. -First, we need to create an exporter using the otlp package: +First, we need to create an exporter using the [otlp](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp?tab=doc) package: ```go -exp, _ := otlp.NewExporter(otlp.WithInsecure(), - // For debug purposes +exp, err := otlp.NewExporter(otlp.WithInsecure(), + // use the address of the NodePort service created above + // :30080 + otlp.WithAddress("localhost:30080"), otlp.WithGRPCDialOption(grpc.WithBlock())) +if err != nil { + log.Fatalf("Failed to create the collector exporter: %v", err) +} defer func() { - _ = exp.Stop() - }() + err := exp.Stop() + if err != nil { + log.Fatalf("Failed to stop the exporter: %v", err) + } +}() ``` -This will initialize the exporter with the default configuration. -In this configuration, it will try to connect to an OTLP receiver at the address `localhost:55680`. -If your OpenTelemetry Collector is running at a different address, use the [`otlp.WithAddress`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp?tab=doc#WithAddress) function to change the default address. +This will initialize the exporter and connect to the otlp receiver at the address that we set for the [NodePort](#opentelemetry-collector-service): `localhost:30080`. Feel free to remove the blocking operation, but it might come in handy when testing the connection. Also, make sure to close the exporter before the app exits. -The next steps are the same as for all other OpenTelemetry-Go SDK uses: -1) Create a trace provider from the `otlp` exporter: +The next step is to create the TraceProvider: ```go -tp, _ := sdktrace.NewProvider( - sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), - sdktrace.WithBatcher(exp, // add following two options to ensure flush - sdktrace.WithScheduleDelayMillis(5), - sdktrace.WithMaxExportBatchSize(2), +tp, err := sdktrace.NewProvider( + sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), + sdktrace.WithResourceAttributes( + // the service name used to display traces in Jaeger + core.Key(conventions.AttributeServiceName).String("test-service"), + ), + sdktrace.WithBatcher(exp, // add following two options to ensure flush + sdktrace.WithScheduleDelayMillis(5), + sdktrace.WithMaxExportBatchSize(2), )) +if err != nil { + log.Fatalf("error creating trace provider: %v\n", err) +} ``` -2) Start sending traces: +It is important here to set the [AttributeServiceName](https://github.com/open-telemetry/opentelemetry-collector/blob/master/translator/conventions/opentelemetry.go#L20) from the `github.com/open-telemetry/opentelemetry-collector/translator/conventions` package on the resource level. This will be passed to the OpenTelemetry Collector, and used as ServiceName when exporting the traces to Jaeger. + +After this, you can simply start sending traces: ```go tracer := tp.Tracer("test-tracer") ctx, span := tracer.Start(context.Background(), "CollectorExporter-Example") defer span.End() ``` -The traces should now be visible from the Jaeger UI (if you have it installed). +The traces should now be visible from the Jaeger UI (if you have it installed), or thorough the jaeger-query service, under the name `test-service`. -# Notes - -* There is an issue with the exporter/Collector which causes Jaeger to throw errors when receiving spans from the OpenTelemetry Collector: https://github.com/open-telemetry/opentelemetry-collector/issues/815 +You can find the complete code for this example in the [main.go](./main.go) file. \ No newline at end of file diff --git a/example/otel-collector/k8s/otel-collector.yaml b/example/otel-collector/k8s/otel-collector.yaml index 3ad0ce2d9..da491f09f 100644 --- a/example/otel-collector/k8s/otel-collector.yaml +++ b/example/otel-collector/k8s/otel-collector.yaml @@ -10,7 +10,7 @@ data: otel-collector-config: | receivers: # Make sure to add the otlp receiver. - # This will use the default configuration, and open up the receiver on port 55680 + # This will open up the receiver on port 55680 otlp: endpoint: 0.0.0.0:55680 processors: @@ -18,7 +18,6 @@ data: health_check: {} exporters: jaeger_grpc: - # Replace with a real endpoint. endpoint: "jaeger-collector.observability.svc.cluster.local:14250" service: extensions: [health_check] @@ -42,12 +41,15 @@ spec: port: 55680 protocol: TCP targetPort: 55680 + nodePort: 30080 - name: metrics # Default endpoint for metrics. port: 8888 protocol: TCP targetPort: 8888 selector: component: otel-collector + type: + NodePort --- apiVersion: apps/v1 kind: Deployment diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index 9ee007b0e..c109c172e 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -31,9 +31,12 @@ import ( ) func main() { + // If the OpenTelemetry Collector is running on a local cluster (minikube or microk8s), + // it should be accessible through the NodePort service at the `localhost:30080` address. + // Otherwise, replace `localhost` with the address of your cluster. + // If you run the app inside k8s, then you can probably connect directly to the service through dns exp, err := otlp.NewExporter(otlp.WithInsecure(), - // Replace the address with the actual address of the collector service - otlp.WithAddress("10.152.183.133:55680"), + otlp.WithAddress("localhost:30080"), otlp.WithGRPCDialOption(grpc.WithBlock())) if err != nil { log.Fatalf("Failed to create the collector exporter: %v", err) @@ -48,6 +51,7 @@ func main() { tp, err := sdktrace.NewProvider( sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), sdktrace.WithResourceAttributes( + // the service name used to display traces in Jaeger core.Key(conventions.AttributeServiceName).String("test-service"), ), sdktrace.WithBatcher(exp, // add following two options to ensure flush From e31b09388a6568d41ce7c000ef0f71312b105226 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Fri, 15 May 2020 12:57:21 +0200 Subject: [PATCH 037/108] change api/core package to api/kv --- example/otel-collector/go.mod | 4 ++-- example/otel-collector/go.sum | 9 +++++---- example/otel-collector/main.go | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod index b429c620a..0ceb645ec 100644 --- a/example/otel-collector/go.mod +++ b/example/otel-collector/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( github.com/open-telemetry/opentelemetry-collector v0.3.0 - go.opentelemetry.io/otel v0.4.3 - go.opentelemetry.io/otel/exporters/otlp v0.4.3 + go.opentelemetry.io/otel v0.5.0 + go.opentelemetry.io/otel/exporters/otlp v0.5.0 google.golang.org/grpc v1.29.1 ) diff --git a/example/otel-collector/go.sum b/example/otel-collector/go.sum index 1d84a900a..d28718445 100644 --- a/example/otel-collector/go.sum +++ b/example/otel-collector/go.sum @@ -929,10 +929,10 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v0.4.3 h1:CroUX/0O1ZDcF0iWOO8gwYFWb5EbdSF0/C1yosO+Vhs= -go.opentelemetry.io/otel v0.4.3/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek= -go.opentelemetry.io/otel/exporters/otlp v0.4.3 h1:n0zV9impmvdavDnr5uBiza+P9D1AfkcfUvuTWogMY2w= -go.opentelemetry.io/otel/exporters/otlp v0.4.3/go.mod h1:h51N+tR0tmfiF05zFB13vaiROHSIUm7AuFetkY8T4GY= +go.opentelemetry.io/otel v0.5.0 h1:tdIR1veg/z+VRJaw/6SIxz+QX3l+m+BDleYLTs+GC1g= +go.opentelemetry.io/otel v0.5.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek= +go.opentelemetry.io/otel/exporters/otlp v0.5.0 h1:dfS89YmU0e6HmmULuJQ9s3xnfz2uu1LHz29wseFt0Jc= +go.opentelemetry.io/otel/exporters/otlp v0.5.0/go.mod h1:uQseOXa3qUrjJRaRl8At4ISGr55GgKPkILaovWY5EI4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1197,6 +1197,7 @@ google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dT google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8= diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index c109c172e..7c4855895 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -23,7 +23,7 @@ import ( "google.golang.org/grpc" - "go.opentelemetry.io/otel/api/core" + "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/exporters/otlp" sdktrace "go.opentelemetry.io/otel/sdk/trace" @@ -52,7 +52,7 @@ func main() { sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), sdktrace.WithResourceAttributes( // the service name used to display traces in Jaeger - core.Key(conventions.AttributeServiceName).String("test-service"), + kv.Key(conventions.AttributeServiceName).String("test-service"), ), sdktrace.WithBatcher(exp, // add following two options to ensure flush sdktrace.WithScheduleDelayMillis(5), From b2285e0c71d22cc2d9636fc0cdef8e8143f735e8 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Fri, 15 May 2020 14:01:43 +0300 Subject: [PATCH 038/108] Fix timer.Stop --- sdk/trace/batch_span_processor.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index 568491025..b35885eed 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -166,9 +166,6 @@ func (bsp *BatchSpanProcessor) processQueue() { batch := make([]*export.SpanData, 0, bsp.o.MaxExportBatchSize) exportSpans := func() { - if !timer.Stop() { - <-timer.C - } timer.Reset(bsp.o.ScheduledDelayMillis) if len(batch) > 0 { @@ -187,6 +184,9 @@ loop: case sd := <-bsp.queue: batch = append(batch, sd) if len(batch) == bsp.o.MaxExportBatchSize { + if !timer.Stop() { + <-timer.C + } exportSpans() } } @@ -201,6 +201,7 @@ loop: if !timer.Stop() { <-timer.C } + // This is not needed normally, but use some timeout so we are not stuck // waiting for enqueueWait forever. const waitTimeout = 30 * time.Second @@ -208,7 +209,7 @@ loop: select { case sd := <-bsp.queue: - if sd == nil { + if sd == nil { // queue is closed exportSpans() return } From 55905e58c5eb5f2e7cf56f7ba3aaec666569fb6e Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 15 May 2020 18:16:38 +0200 Subject: [PATCH 039/108] Remove krnowak from approvers --- CODEOWNERS | 2 +- CONTRIBUTING.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 9797c512b..868eb2405 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -12,6 +12,6 @@ # https://help.github.com/en/articles/about-code-owners # -* @jmacd @paivagustavo @krnowak @lizthegrey @MrAlias @Aneurysm9 @evantorrie +* @jmacd @paivagustavo @lizthegrey @MrAlias @Aneurysm9 @evantorrie CODEOWNERS @MrAlias @jmacd diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d2e59e639..67a2e08bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -139,7 +139,6 @@ https://github.com/open-telemetry/opentelemetry-specification/issues/165 Approvers: -- [Krzesimir Nowak](https://github.com/krnowak), Kinvolk - [Liz Fong-Jones](https://github.com/lizthegrey), Honeycomb - [Gustavo Silva Paiva](https://github.com/paivagustavo), Stilingue - [Anthony Mirabella](https://github.com/Aneurysm9), Centene From 80a59c227540984bc8ac80fd134311a8c38fa86b Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 09:33:06 -0700 Subject: [PATCH 040/108] Update correlation context header name --- api/correlation/correlation_context_propagator.go | 4 +++- .../correlation_context_propagator_test.go | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/api/correlation/correlation_context_propagator.go b/api/correlation/correlation_context_propagator.go index 20e1cd6af..172a3b902 100644 --- a/api/correlation/correlation_context_propagator.go +++ b/api/correlation/correlation_context_propagator.go @@ -23,7 +23,9 @@ import ( "go.opentelemetry.io/otel/api/propagation" ) -const correlationContextHeader = "Correlation-Context" +// Temporary header name until W3C finalizes format. +// https://github.com/open-telemetry/opentelemetry-specification/blob/18b2752ebe6c7f0cdd8c7b2bcbdceb0ae3f5ad95/specification/correlationcontext/api.md#header-name +const correlationContextHeader = "otcorrelations" // CorrelationContext propagates Key:Values in W3C CorrelationContext // format. diff --git a/api/correlation/correlation_context_propagator_test.go b/api/correlation/correlation_context_propagator_test.go index 3fe832fa6..5a88133eb 100644 --- a/api/correlation/correlation_context_propagator_test.go +++ b/api/correlation/correlation_context_propagator_test.go @@ -89,7 +89,7 @@ func TestExtractValidDistributedContextFromHTTPReq(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req, _ := http.NewRequest("GET", "http://example.com", nil) - req.Header.Set("Correlation-Context", tt.header) + req.Header.Set("otcorrelations", tt.header) ctx := context.Background() ctx = propagation.ExtractHTTP(ctx, props, req.Header) @@ -133,7 +133,7 @@ func TestExtractInvalidDistributedContextFromHTTPReq(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req, _ := http.NewRequest("GET", "http://example.com", nil) - req.Header.Set("Correlation-Context", tt.header) + req.Header.Set("otcorrelations", tt.header) ctx := context.Background() ctx = propagation.ExtractHTTP(ctx, props, req.Header) @@ -202,17 +202,17 @@ func TestInjectCorrelationContextToHTTPReq(t *testing.T) { ctx := correlation.ContextWithMap(context.Background(), correlation.NewMap(correlation.MapUpdate{MultiKV: tt.kvs})) propagation.InjectHTTP(ctx, props, req.Header) - gotHeader := req.Header.Get("Correlation-Context") + gotHeader := req.Header.Get("otcorrelations") wantedLen := len(strings.Join(tt.wantInHeader, ",")) if wantedLen != len(gotHeader) { t.Errorf( - "%s: Inject Correlation-Context incorrect length %d != %d.", tt.name, tt.wantedLen, len(gotHeader), + "%s: Inject otcorrelations incorrect length %d != %d.", tt.name, tt.wantedLen, len(gotHeader), ) } for _, inHeader := range tt.wantInHeader { if !strings.Contains(gotHeader, inHeader) { t.Errorf( - "%s: Inject Correlation-Context missing part of header: %s in %s", tt.name, inHeader, gotHeader, + "%s: Inject otcorrelations missing part of header: %s in %s", tt.name, inHeader, gotHeader, ) } } @@ -222,7 +222,7 @@ func TestInjectCorrelationContextToHTTPReq(t *testing.T) { func TestTraceContextPropagator_GetAllKeys(t *testing.T) { var propagator correlation.CorrelationContext - want := []string{"Correlation-Context"} + want := []string{"otcorrelations"} got := propagator.GetAllKeys() if diff := cmp.Diff(got, want); diff != "" { t.Errorf("GetAllKeys: -got +want %s", diff) From 7c209b5c8c14b4d837060962fa10cdf335b69d29 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 11:32:03 -0700 Subject: [PATCH 041/108] Rename resourcekeys to singular resourcekey --- sdk/resource/{resourcekeys => resourcekey}/const.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename sdk/resource/{resourcekeys => resourcekey}/const.go (93%) diff --git a/sdk/resource/resourcekeys/const.go b/sdk/resource/resourcekey/const.go similarity index 93% rename from sdk/resource/resourcekeys/const.go rename to sdk/resource/resourcekey/const.go index 5adc993dd..ad266d165 100644 --- a/sdk/resource/resourcekeys/const.go +++ b/sdk/resource/resourcekey/const.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package resourcekeys contains well known type and label keys for resources. -package resourcekeys // import "go.opentelemetry.io/otel/sdk/resource/resourcekeys" +// Package resourcekey contains well known type and label keys for resources. +package resourcekey // import "go.opentelemetry.io/otel/sdk/resource/resourcekey" // Constants for Service resources. const ( From 4eecaf53912e3695d13f98ea1620fe12d7e32082 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 11:35:44 -0700 Subject: [PATCH 042/108] Update resourcekey package doc --- sdk/resource/resourcekey/const.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/resource/resourcekey/const.go b/sdk/resource/resourcekey/const.go index ad266d165..08769e233 100644 --- a/sdk/resource/resourcekey/const.go +++ b/sdk/resource/resourcekey/const.go @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package resourcekey contains well known type and label keys for resources. +// Package resourcekey contains standard resource attribute keys as defined +// by the OpenTelemetry specification +// (https://github.com/open-telemetry/opentelemetry-specification/tree/v0.4.0/specification/resource/semantic_conventions). package resourcekey // import "go.opentelemetry.io/otel/sdk/resource/resourcekey" // Constants for Service resources. From f7f3fc39181f6326752f0b889774010d02e08a85 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 11:38:02 -0700 Subject: [PATCH 043/108] Remove redundant "Key" from const names --- sdk/resource/resourcekey/const.go | 50 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/sdk/resource/resourcekey/const.go b/sdk/resource/resourcekey/const.go index 08769e233..b2ca3b6cf 100644 --- a/sdk/resource/resourcekey/const.go +++ b/sdk/resource/resourcekey/const.go @@ -20,18 +20,18 @@ package resourcekey // import "go.opentelemetry.io/otel/sdk/resource/resourcekey // Constants for Service resources. const ( // A uniquely identifying name for a Service. - ServiceKeyName = "service.name" - ServiceKeyNamespace = "service.namespace" - ServiceKeyInstanceID = "service.instance.id" - ServiceKeyVersion = "service.version" + ServiceName = "service.name" + ServiceNamespace = "service.namespace" + ServiceInstanceID = "service.instance.id" + ServiceVersion = "service.version" ) // Constants for Library resources. const ( // A uniquely identifying name for a Library. - LibraryKeyName = "library.name" - LibraryKeyLanguage = "library.language" - LibraryKeyVersion = "library.version" + LibraryName = "library.name" + LibraryLanguage = "library.language" + LibraryVersion = "library.version" ) // Constants for Kubernetes resources. @@ -40,26 +40,26 @@ const ( // does not have cluster names as an internal concept so this may be // set to any meaningful value within the environment. For example, // GKE clusters have a name which can be used for this label. - K8SKeyClusterName = "k8s.cluster.name" - K8SKeyNamespaceName = "k8s.namespace.name" - K8SKeyPodName = "k8s.pod.name" - K8SKeyDeploymentName = "k8s.deployment.name" + K8SClusterName = "k8s.cluster.name" + K8SNamespaceName = "k8s.namespace.name" + K8SPodName = "k8s.pod.name" + K8SDeploymentName = "k8s.deployment.name" ) // Constants for Container resources. const ( // A uniquely identifying name for the Container. - ContainerKeyName = "container.name" - ContainerKeyImageName = "container.image.name" - ContainerKeyImageTag = "container.image.tag" + ContainerName = "container.name" + ContainerImageName = "container.image.name" + ContainerImageTag = "container.image.tag" ) // Constants for Cloud resources. const ( - CloudKeyProvider = "cloud.provider" - CloudKeyAccountID = "cloud.account.id" - CloudKeyRegion = "cloud.region" - CloudKeyZone = "cloud.zone" + CloudProvider = "cloud.provider" + CloudAccountID = "cloud.account.id" + CloudRegion = "cloud.region" + CloudZone = "cloud.zone" // Cloud Providers CloudProviderAWS = "aws" @@ -70,13 +70,13 @@ const ( // Constants for Host resources. const ( // A uniquely identifying name for the host. - HostKeyName = "host.name" + HostName = "host.name" // A hostname as returned by the 'hostname' command on host machine. - HostKeyHostName = "host.hostname" - HostKeyID = "host.id" - HostKeyType = "host.type" - HostKeyImageName = "host.image.name" - HostKeyImageID = "host.image.id" - HostKeyImageVersion = "host.image.version" + HostHostName = "host.hostname" + HostID = "host.id" + HostType = "host.type" + HostImageName = "host.image.name" + HostImageID = "host.image.id" + HostImageVersion = "host.image.version" ) From 55bbf514599c0f1830481cd58c0b8672054d1dc9 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 11:44:17 -0700 Subject: [PATCH 044/108] Switch to kv.Key types --- sdk/resource/resourcekey/const.go | 57 +++++++++++++++---------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/sdk/resource/resourcekey/const.go b/sdk/resource/resourcekey/const.go index b2ca3b6cf..3c5e1d8a5 100644 --- a/sdk/resource/resourcekey/const.go +++ b/sdk/resource/resourcekey/const.go @@ -17,21 +17,23 @@ // (https://github.com/open-telemetry/opentelemetry-specification/tree/v0.4.0/specification/resource/semantic_conventions). package resourcekey // import "go.opentelemetry.io/otel/sdk/resource/resourcekey" +import "go.opentelemetry.io/otel/api/kv" + // Constants for Service resources. const ( // A uniquely identifying name for a Service. - ServiceName = "service.name" - ServiceNamespace = "service.namespace" - ServiceInstanceID = "service.instance.id" - ServiceVersion = "service.version" + ServiceName = kv.Key("service.name") + ServiceNamespace = kv.Key("service.namespace") + ServiceInstanceID = kv.Key("service.instance.id") + ServiceVersion = kv.Key("service.version") ) // Constants for Library resources. const ( // A uniquely identifying name for a Library. - LibraryName = "library.name" - LibraryLanguage = "library.language" - LibraryVersion = "library.version" + LibraryName = kv.Key("library.name") + LibraryLanguage = kv.Key("library.language") + LibraryVersion = kv.Key("library.version") ) // Constants for Kubernetes resources. @@ -40,43 +42,38 @@ const ( // does not have cluster names as an internal concept so this may be // set to any meaningful value within the environment. For example, // GKE clusters have a name which can be used for this label. - K8SClusterName = "k8s.cluster.name" - K8SNamespaceName = "k8s.namespace.name" - K8SPodName = "k8s.pod.name" - K8SDeploymentName = "k8s.deployment.name" + K8SClusterName = kv.Key("k8s.cluster.name") + K8SNamespaceName = kv.Key("k8s.namespace.name") + K8SPodName = kv.Key("k8s.pod.name") + K8SDeploymentName = kv.Key("k8s.deployment.name") ) // Constants for Container resources. const ( // A uniquely identifying name for the Container. - ContainerName = "container.name" - ContainerImageName = "container.image.name" - ContainerImageTag = "container.image.tag" + ContainerName = kv.Key("container.name") + ContainerImageName = kv.Key("container.image.name") + ContainerImageTag = kv.Key("container.image.tag") ) // Constants for Cloud resources. const ( - CloudProvider = "cloud.provider" - CloudAccountID = "cloud.account.id" - CloudRegion = "cloud.region" - CloudZone = "cloud.zone" - - // Cloud Providers - CloudProviderAWS = "aws" - CloudProviderGCP = "gcp" - CloudProviderAZURE = "azure" + CloudProvider = kv.Key("cloud.provider") + CloudAccountID = kv.Key("cloud.account.id") + CloudRegion = kv.Key("cloud.region") + CloudZone = kv.Key("cloud.zone") ) // Constants for Host resources. const ( // A uniquely identifying name for the host. - HostName = "host.name" + HostName = kv.Key("host.name") // A hostname as returned by the 'hostname' command on host machine. - HostHostName = "host.hostname" - HostID = "host.id" - HostType = "host.type" - HostImageName = "host.image.name" - HostImageID = "host.image.id" - HostImageVersion = "host.image.version" + HostHostName = kv.Key("host.hostname") + HostID = kv.Key("host.id") + HostType = kv.Key("host.type") + HostImageName = kv.Key("host.image.name") + HostImageID = kv.Key("host.image.id") + HostImageVersion = kv.Key("host.image.version") ) From 1d554f34c1e8335393cea231578c3d45acddb29f Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 12:42:13 -0700 Subject: [PATCH 045/108] Add standard package contain all semantic conventions --- sdk/resource/resourcekey/const.go => api/standard/resource.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename sdk/resource/resourcekey/const.go => api/standard/resource.go (94%) diff --git a/sdk/resource/resourcekey/const.go b/api/standard/resource.go similarity index 94% rename from sdk/resource/resourcekey/const.go rename to api/standard/resource.go index 3c5e1d8a5..83a8b2160 100644 --- a/sdk/resource/resourcekey/const.go +++ b/api/standard/resource.go @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package resourcekey contains standard resource attribute keys as defined +// Package standard contains standard resource attribute keys as defined // by the OpenTelemetry specification // (https://github.com/open-telemetry/opentelemetry-specification/tree/v0.4.0/specification/resource/semantic_conventions). -package resourcekey // import "go.opentelemetry.io/otel/sdk/resource/resourcekey" +package standard // import "go.opentelemetry.io/otel/api/standard" import "go.opentelemetry.io/otel/api/kv" From 721628d402629d47610a5acb97c495e618a82edc Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 12:46:53 -0700 Subject: [PATCH 046/108] Update standard package docs --- api/standard/doc.go | 22 ++++++++++++++++++++++ api/standard/resource.go | 3 --- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 api/standard/doc.go diff --git a/api/standard/doc.go b/api/standard/doc.go new file mode 100644 index 000000000..b681eccf8 --- /dev/null +++ b/api/standard/doc.go @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package standard contains keys and values that have been standardized for +// use in OpenTelemetry. These standardizations are specified in the +// OpenTelemetry specification: +// +// - https://github.com/open-telemetry/opentelemetry-specification/tree/v0.4.0/specification/resource/semantic_conventions +// - https://github.com/open-telemetry/opentelemetry-specification/tree/v0.4.0/specification/trace/semantic_conventions +// - https://github.com/open-telemetry/opentelemetry-specification/tree/v0.4.0/specification/metrics/semantic_conventions +package standard diff --git a/api/standard/resource.go b/api/standard/resource.go index 83a8b2160..082827457 100644 --- a/api/standard/resource.go +++ b/api/standard/resource.go @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package standard contains standard resource attribute keys as defined -// by the OpenTelemetry specification -// (https://github.com/open-telemetry/opentelemetry-specification/tree/v0.4.0/specification/resource/semantic_conventions). package standard // import "go.opentelemetry.io/otel/api/standard" import "go.opentelemetry.io/otel/api/kv" From 5c160d31e58502ee6b914be677738e946d86a342 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 13:21:52 -0700 Subject: [PATCH 047/108] Update to v0.4.0 specification Change `library.*` to `telemetry.sdk.*`. Add FaaS. Add comments to all constants. --- api/standard/resource.go | 156 +++++++++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 40 deletions(-) diff --git a/api/standard/resource.go b/api/standard/resource.go index 082827457..5bb6beb2d 100644 --- a/api/standard/resource.go +++ b/api/standard/resource.go @@ -16,61 +16,137 @@ package standard // import "go.opentelemetry.io/otel/api/standard" import "go.opentelemetry.io/otel/api/kv" -// Constants for Service resources. +// Standard service resource attribute keys. const ( - // A uniquely identifying name for a Service. - ServiceName = kv.Key("service.name") - ServiceNamespace = kv.Key("service.namespace") - ServiceInstanceID = kv.Key("service.instance.id") - ServiceVersion = kv.Key("service.version") + // Name of the service. + ServiceNameKey = kv.Key("service.name") + + // A namespace for `service.name`. This needs to have meaning that helps + // to distinguish a group of services. For example, the team name that + // owns a group of services. `service.name` is expected to be unique + // within the same namespace. + ServiceNamespaceKey = kv.Key("service.namespace") + + // A unique identifier of the service instance. In conjunction with the + // `service.name` and `service.namespace` this must be unique. + ServiceInstanceIDKey = kv.Key("service.instance.id") + + // The version of the service API. + ServiceVersionKey = kv.Key("service.version") ) -// Constants for Library resources. +// Standard telemetry SDK resource attribute keys. const ( - // A uniquely identifying name for a Library. - LibraryName = kv.Key("library.name") - LibraryLanguage = kv.Key("library.language") - LibraryVersion = kv.Key("library.version") + // The name of the telemetry SDK. + // + // The default OpenTelemetry SDK provided by the OpenTelemetry project + // MUST set telemetry.sdk.name to the value `opentelemetry`. + // + // If another SDK is used, this attribute MUST be set to the import path + // of that SDK's package. + // + // The value `opentelemetry` is reserved and MUST NOT be used by + // non-OpenTelemetry SDKs. + TelemetrySDKNameKey = kv.Key("telemetry.sdk.name") + + // The language of the telemetry SDK. + TelemetrySDKLanguageKey = kv.Key("telemetry.sdk.language") + + // The version string of the telemetry SDK. + TelemetrySDKVersionKey = kv.Key("telemetry.sdk.version") ) -// Constants for Kubernetes resources. +// Standard telemetry SDK resource attributes. +var ( + TelemetrySDKLanguageGo = TelemetrySDKLanguageKey.String("go") +) + +// Standard container resource attribute keys. +const ( + // A uniquely identifying name for the Container. + ContainerNameKey = kv.Key("container.name") + + // Name of the image the container was built on. + ContainerImageNameKey = kv.Key("container.image.name") + + // Container image tag. + ContainerImageTagKey = kv.Key("container.image.tag") +) + +// Standard Function-as-a-Service resource attribute keys. +const ( + // A uniquely identifying name for the FaaS. + FaaSName = kv.Key("faas.name") + + // The unique name of the function being executed. + FaaSID = kv.Key("faas.id") + + // The version of the function being executed. + FaaSVersion = kv.Key("faas.version") + + // The execution environment identifier. + FaaSInstance = kv.Key("faas.instance") +) + +// Standard Kubernetes resource attribute keys. const ( // A uniquely identifying name for the Kubernetes cluster. Kubernetes // does not have cluster names as an internal concept so this may be // set to any meaningful value within the environment. For example, // GKE clusters have a name which can be used for this label. - K8SClusterName = kv.Key("k8s.cluster.name") - K8SNamespaceName = kv.Key("k8s.namespace.name") - K8SPodName = kv.Key("k8s.pod.name") - K8SDeploymentName = kv.Key("k8s.deployment.name") + K8SClusterNameKey = kv.Key("k8s.cluster.name") + + // The name of the namespace that the pod is running in. + K8SNamespaceNameKey = kv.Key("k8s.namespace.name") + + // The name of the pod. + K8SPodNameKey = kv.Key("k8s.pod.name") + + // The name of the deployment. + K8SDeploymentNameKey = kv.Key("k8s.deployment.name") ) -// Constants for Container resources. -const ( - // A uniquely identifying name for the Container. - ContainerName = kv.Key("container.name") - ContainerImageName = kv.Key("container.image.name") - ContainerImageTag = kv.Key("container.image.tag") -) - -// Constants for Cloud resources. -const ( - CloudProvider = kv.Key("cloud.provider") - CloudAccountID = kv.Key("cloud.account.id") - CloudRegion = kv.Key("cloud.region") - CloudZone = kv.Key("cloud.zone") -) - -// Constants for Host resources. +// Standard host resource attribute keys. const ( // A uniquely identifying name for the host. - HostName = kv.Key("host.name") + HostNameKey = kv.Key("host.name") // A hostname as returned by the 'hostname' command on host machine. - HostHostName = kv.Key("host.hostname") - HostID = kv.Key("host.id") - HostType = kv.Key("host.type") - HostImageName = kv.Key("host.image.name") - HostImageID = kv.Key("host.image.id") - HostImageVersion = kv.Key("host.image.version") + HostHostNameKey = kv.Key("host.hostname") + + // Unique host ID. For cloud environments this will be the instance ID. + HostIDKey = kv.Key("host.id") + + // Type of host. For cloud environments this will be the machine type. + HostTypeKey = kv.Key("host.type") + + // Name of the OS or VM image the host is running. + HostImageNameKey = kv.Key("host.image.name") + + // Identifier of the image the host is running. + HostImageIDKey = kv.Key("host.image.id") + + // Version of the image the host is running. + HostImageVersionKey = kv.Key("host.image.version") +) + +// Standard cloud environment resource attribute keys. +const ( + // Name of the cloud provider. + CloudProviderKey = kv.Key("cloud.provider") + + // The account ID from the cloud provider used for authorization. + CloudAccountIDKey = kv.Key("cloud.account.id") + + // Geographical region where this resource is. + CloudRegionKey = kv.Key("cloud.region") + + // Zone of the region where this resource is. + CloudZoneKey = kv.Key("cloud.zone") +) + +var ( + CloudProviderAWS = CloudProviderKey.String("aws") + CloudProviderAzure = CloudProviderKey.String("azure") + CloudProviderGCP = CloudProviderKey.String("gcp") ) From f4a25cf745a6d9fc24f5e9fdd8f47c80b03c4464 Mon Sep 17 00:00:00 2001 From: Ahmed Mujtaba Date: Fri, 15 May 2020 22:43:07 +0200 Subject: [PATCH 048/108] Added condition for missing attr received in attr expected --- plugin/grpctrace/interceptor_test.go | 89 +++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 15 deletions(-) diff --git a/plugin/grpctrace/interceptor_test.go b/plugin/grpctrace/interceptor_test.go index 3667b9dbe..243bf776a 100644 --- a/plugin/grpctrace/interceptor_test.go +++ b/plugin/grpctrace/interceptor_test.go @@ -19,6 +19,7 @@ import ( "testing" "time" + "github.com/golang/protobuf/proto" "google.golang.org/grpc" "google.golang.org/grpc/metadata" @@ -93,42 +94,96 @@ func TestUnaryClientInterceptor(t *testing.T) { }, eventsAttr: []map[core.Key]core.Value{ { - messageTypeKey: core.String("SENT"), - messageIDKey: core.Int(1), + messageTypeKey: core.String("SENT"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: core.String("RECEIVED"), - messageIDKey: core.Int(1), + messageTypeKey: core.String("RECEIVED"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), }, }, }, { name: "/serviceName/bar", expectedAttr: map[core.Key]core.Value{ - rpcServiceKey: core.String("serviceName"), + rpcServiceKey: core.String("serviceName"), + netPeerIPKey: core.String("fake"), + netPeerPortKey: core.String("connection"), }, eventsAttr: []map[core.Key]core.Value{ { - messageTypeKey: core.String("SENT"), - messageIDKey: core.Int(1), + messageTypeKey: core.String("SENT"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: core.String("RECEIVED"), - messageIDKey: core.Int(1), + messageTypeKey: core.String("RECEIVED"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), }, }, }, { - name: "serviceName/bar", - expectedAttr: map[core.Key]core.Value{rpcServiceKey: core.String("serviceName")}, + name: "serviceName/bar", + expectedAttr: map[core.Key]core.Value{ + rpcServiceKey: core.String("serviceName"), + netPeerIPKey: core.String("fake"), + netPeerPortKey: core.String("connection"), + }, + eventsAttr: []map[core.Key]core.Value{ + { + messageTypeKey: core.String("SENT"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), + }, + { + messageTypeKey: core.String("RECEIVED"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), + }, + }, }, { - name: "invalidName", - expectedAttr: map[core.Key]core.Value{rpcServiceKey: core.String("")}, + name: "invalidName", + expectedAttr: map[core.Key]core.Value{ + rpcServiceKey: core.String(""), + netPeerIPKey: core.String("fake"), + netPeerPortKey: core.String("connection"), + }, + eventsAttr: []map[core.Key]core.Value{ + { + messageTypeKey: core.String("SENT"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), + }, + { + messageTypeKey: core.String("RECEIVED"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), + }, + }, }, { - name: "/github.com.foo.serviceName_123/method", - expectedAttr: map[core.Key]core.Value{rpcServiceKey: core.String("serviceName_123")}, + name: "/github.com.foo.serviceName_123/method", + expectedAttr: map[core.Key]core.Value{ + rpcServiceKey: core.String("serviceName_123"), + netPeerIPKey: core.String("fake"), + netPeerPortKey: core.String("connection"), + }, + eventsAttr: []map[core.Key]core.Value{ + { + messageTypeKey: core.String("SENT"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), + }, + { + messageTypeKey: core.String("RECEIVED"), + messageIDKey: core.Int(1), + messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), + }, + }, }, } @@ -158,6 +213,8 @@ func TestUnaryClientInterceptor(t *testing.T) { expectedAttr.AsString(), attr.Value.AsString()) } delete(check.expectedAttr, attr.Key) + } else { + t.Errorf("attribute %s not found in expected attributes map", string(attr.Key)) } } @@ -182,6 +239,8 @@ func TestUnaryClientInterceptor(t *testing.T) { string(attr.Key), attr.Value.AsString(), expectedAttr.AsString()) } delete(check.eventsAttr[event], attr.Key) + } else { + t.Errorf("attribute in event %s not found in expected attributes map", string(attr.Key)) } } if len(check.eventsAttr[event]) > 0 { From 5abfeb02a98a233509e813f49ccd0c26cd219308 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 15:20:30 -0700 Subject: [PATCH 049/108] Add tracing standards --- api/standard/trace.go | 262 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 api/standard/trace.go diff --git a/api/standard/trace.go b/api/standard/trace.go new file mode 100644 index 000000000..b3e2f4b8b --- /dev/null +++ b/api/standard/trace.go @@ -0,0 +1,262 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package standard + +import "go.opentelemetry.io/otel/api/kv" + +// Standard attribute keys used for network related operations. +const ( + // Transport protocol used. + NetTransportKey = kv.Key("net.transport") + + // Remote address of the peer. + NetPeerIPKey = kv.Key("net.peer.ip") + + // Remote port number. + NetPeerPortKey = kv.Key("net.peer.port") + + // Remote hostname or similar. + NetPeerNameKey = kv.Key("net.peer.name") + + // Local host IP. Useful in case of a multi-IP host. + NetHostIPKey = kv.Key("net.host.ip") + + // Local host port. + NetHostPortKey = kv.Key("net.host.port") + + // Local hostname or similar. + NetHostNameKey = kv.Key("net.host.name") +) + +var ( + NetTransportTCP = NetTransportKey.String("IP.TCP") + NetTransportUDP = NetTransportKey.String("IP.UDP") + NetTransportIP = NetTransportKey.String("IP") + NetTransportUnix = NetTransportKey.String("Unix") + NetTransportPipe = NetTransportKey.String("pipe") + NetTransportInProc = NetTransportKey.String("inproc") + NetTransportOther = NetTransportKey.String("other") +) + +// Standard attribute keys used to identify an authorized enduser. +const ( + // Username or the client identifier extracted from the access token or + // authorization header in the inbound request from outside the system. + EnduserIDKey = kv.Key("enduser.id") + + // Actual or assumed role the client is making the request with. + EnduserRoleKey = kv.Key("enduser.role") + + // Scopes or granted authorities the client currently possesses. + EnduserScopeKey = kv.Key("enduser.scope") +) + +// Standard attribute keys for HTTP. +const ( + // HTTP request method. + HTTPMethodKey = kv.Key("http.method") + + // Full HTTP request URL in the form: + // scheme://host[:port]/path?query[#fragment]. + HTTPUrlKey = kv.Key("http.url") + + // The full request target as passed in a HTTP request line or + // equivalent, e.g. "/path/12314/?q=ddds#123". + HTTPTargetKey = kv.Key("http.target") + + // The value of the HTTP host header. + HTTPHostKey = kv.Key("http.host") + + // The URI scheme identifying the used protocol. + HTTPSchemeKey = kv.Key("http.scheme") + + // HTTP response status code. + HTTPStatusCodeKey = kv.Key("http.status_code") + + // HTTP reason phrase. + HTTPStatusTextKey = kv.Key("http.status_text") + + // Kind of HTTP protocol used. + HTTPFlavorKey = kv.Key("http.flavor") + + // Value of the HTTP User-Agent header sent by the client. + HTTPUserAgentKey = kv.Key("http.user_agent") + + // The primary server name of the matched virtual host. + HTTPServerNameKey = kv.Key("http.server_name") + + // The matched route served (path template). For example, + // "/users/:userID?". + HTTPRouteKey = kv.Key("http.route") + + // The IP address of the original client behind all proxies, if known + // (e.g. from X-Forwarded-For). + HTTPClientIPKey = kv.Key("http.client_ip") +) + +var ( + HTTPSchemeHTTP = HTTPSchemeKey.String("http") + HTTPSchemeHTTPS = HTTPSchemeKey.String("https") + + HTTPFlavor1_0 = HTTPFlavorKey.String("1.0") + HTTPFlavor1_1 = HTTPFlavorKey.String("1.1") + HTTPFlavor2 = HTTPFlavorKey.String("2") + HTTPFlavorSPDY = HTTPFlavorKey.String("SPDY") + HTTPFlavorQUIC = HTTPFlavorKey.String("QUIC") +) + +// Standard attribute keys for database clients. +const ( + // Database type. For any SQL database, "sql". For others, the + // lower-case database category, e.g. "cassandra", "hbase", or "redis". + DBTypeKey = kv.Key("db.type") + + // Database instance name. + DBInstanceKey = kv.Key("db.instance") + + // A database statement for the given database type. + DBStatementKey = kv.Key("db.statement") + + // Username for accessing database. + DBUserKey = kv.Key("db.user") + + // Database URL. + DBUrlKey = kv.Key("db.url") +) + +// Standard attribute keys for RPC. +const ( + // The RPC service name. + RPCServiceKey = kv.Key("rpc.service") + + // Name of message transmitted or received. + RPCNameKey = kv.Key("name") + + // Type of message transmitted or received. + RPCMessageTypeKey = kv.Key("message.type") + + // Identifier of message transmitted or received. + RPCMessageIDKey = kv.Key("message.id") + + // The compressed size of the message transmitted or received in bytes. + RPCMessageCompressedSizeKey = kv.Key("message.compressed_size") + + // The uncompressed size of the message transmitted or received in + // bytes. + RPCMessageUncompressedSizeKey = kv.Key("message.uncompressed_size") +) + +var ( + RPCNameMessage = RPCNameKey.String("message") + + RPCMessageTypeSent = RPCMessageTypeKey.String("SENT") + RPCMessageTypeReceived = RPCMessageTypeKey.String("RECEIVED") +) + +// Standard attribute keys for messaging systems. +const ( + // A unique identifier describing the messaging system. For example, + // kafka, rabbitmq or activemq. + MessagingSystemKey = kv.Key("messaging.system") + + // The message destination name, e.g. MyQueue or MyTopic. + MessagingDestinationKey = kv.Key("messaging.destination") + + // The kind of message destination. + MessagingDestinationKindKey = kv.Key("messaging.destination_kind") + + // Describes if the destination is temporary or not. + MessagingTempDestinationKey = kv.Key("messaging.temp_destination") + + // The name of the transport protocol. + MessagingProtocolKey = kv.Key("messaging.protocol") + + // The version of the transport protocol. + MessagingProtocolVersionKey = kv.Key("messaging.protocol_version") + + // Messaging service URL. + MessagingURLKey = kv.Key("messaging.url") + + // Identifier used by the messaging system for a message. + MessagingMessageIDKey = kv.Key("messaging.message_id") + + // Identifier used by the messaging system for a conversation. + MessagingConversationIDKey = kv.Key("messaging.conversation_id") + + // The (uncompressed) size of the message payload in bytes. + MessagingMessagePayloadSizeBytesKey = kv.Key("messaging.message_payload_size_bytes") + + // The compressed size of the message payload in bytes. + MessagingMessagePayloadCompressedSizeBytesKey = kv.Key("messaging.message_payload_compressed_size_bytes") + + // Identifies which part and kind of message consumption is being + // preformed. + MessagingOperationKey = kv.Key("messaging.operation") + + // RabbitMQ specific attribute describing the destination routing key. + MessagingRabbitMQRoutingKeyKey = kv.Key("messaging.rabbitmq.routing_key") +) + +var ( + MessagingDestinationKindKeyQueue = MessagingDestinationKindKey.String("queue") + MessagingDestinationKindKeyTopic = MessagingDestinationKindKey.String("topic") + + MessagingTempDestination = MessagingTempDestinationKey.Bool(true) + + MessagingOperationReceive = MessagingOperationKey.String("receive") + MessagingOperationProcess = MessagingOperationKey.String("process") +) + +// Standard attribute keys for FaaS systems. +const ( + + // Type of the trigger on which the function is executed. + FaaSTriggerKey = kv.Key("faas.trigger") + + // String containing the execution identifier of the function. + FaaSExecutionKey = kv.Key("faas.execution") + + // The name of the source on which the operation was performed. + // For example, in Cloud Storage or S3 corresponds to the bucket name, + // and in Cosmos DB to the database name. + FaaSDocumentCollectionKey = kv.Key("faas.document.collection") + + // The type of the operation that was performed on the data. + FaaSDocumentOperationKey = kv.Key("faas.document.operation") + + // A string containing the time when the data was accessed. + FaaSDocumentTimeKey = kv.Key("faas.document.time") + + // The document name/table subjected to the operation. + FaaSDocumentNameKey = kv.Key("faas.document.name") + + // The function invocation time. + FaaSTimeKey = kv.Key("faas.time") + + // The schedule period as Cron Expression. + FaaSCronKey = kv.Key("faas.cron") +) + +var ( + FaasTriggerDatasource = FaaSTriggerKey.String("datasource") + FaasTriggerHTTP = FaaSTriggerKey.String("http") + FaasTriggerPubSub = FaaSTriggerKey.String("pubsub") + FaasTriggerTimer = FaaSTriggerKey.String("timer") + FaasTriggerOther = FaaSTriggerKey.String("other") + + FaaSDocumentOperationInsert = FaaSDocumentOperationKey.String("insert") + FaaSDocumentOperationEdit = FaaSDocumentOperationKey.String("edit") + FaaSDocumentOperationDelete = FaaSDocumentOperationKey.String("delete") +) From 55d4f7c31f38758446f5c1171dfa272c549a33d8 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 15 May 2020 16:27:40 -0700 Subject: [PATCH 050/108] Upgrade to v0.5.0 --- plugin/grpctrace/interceptor_test.go | 123 ++++++++++++++------------- 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/plugin/grpctrace/interceptor_test.go b/plugin/grpctrace/interceptor_test.go index 243bf776a..a92c7177f 100644 --- a/plugin/grpctrace/interceptor_test.go +++ b/plugin/grpctrace/interceptor_test.go @@ -23,7 +23,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/metadata" - "go.opentelemetry.io/otel/api/core" + "go.opentelemetry.io/otel/api/kv" + "go.opentelemetry.io/otel/api/kv/value" export "go.opentelemetry.io/otel/sdk/export/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) @@ -82,106 +83,106 @@ func TestUnaryClientInterceptor(t *testing.T) { checks := []struct { name string - expectedAttr map[core.Key]core.Value - eventsAttr []map[core.Key]core.Value + expectedAttr map[kv.Key]value.Value + eventsAttr []map[kv.Key]value.Value }{ { name: "/github.com.serviceName/bar", - expectedAttr: map[core.Key]core.Value{ - rpcServiceKey: core.String("serviceName"), - netPeerIPKey: core.String("fake"), - netPeerPortKey: core.String("connection"), + expectedAttr: map[kv.Key]value.Value{ + rpcServiceKey: value.String("serviceName"), + netPeerIPKey: value.String("fake"), + netPeerPortKey: value.String("connection"), }, - eventsAttr: []map[core.Key]core.Value{ + eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: core.String("SENT"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), + messageTypeKey: value.String("SENT"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: core.String("RECEIVED"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), + messageTypeKey: value.String("RECEIVED"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, { name: "/serviceName/bar", - expectedAttr: map[core.Key]core.Value{ - rpcServiceKey: core.String("serviceName"), - netPeerIPKey: core.String("fake"), - netPeerPortKey: core.String("connection"), + expectedAttr: map[kv.Key]value.Value{ + rpcServiceKey: value.String("serviceName"), + netPeerIPKey: value.String("fake"), + netPeerPortKey: value.String("connection"), }, - eventsAttr: []map[core.Key]core.Value{ + eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: core.String("SENT"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), + messageTypeKey: value.String("SENT"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: core.String("RECEIVED"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), + messageTypeKey: value.String("RECEIVED"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, { name: "serviceName/bar", - expectedAttr: map[core.Key]core.Value{ - rpcServiceKey: core.String("serviceName"), - netPeerIPKey: core.String("fake"), - netPeerPortKey: core.String("connection"), + expectedAttr: map[kv.Key]value.Value{ + rpcServiceKey: value.String("serviceName"), + netPeerIPKey: value.String("fake"), + netPeerPortKey: value.String("connection"), }, - eventsAttr: []map[core.Key]core.Value{ + eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: core.String("SENT"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), + messageTypeKey: value.String("SENT"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: core.String("RECEIVED"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), + messageTypeKey: value.String("RECEIVED"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, { name: "invalidName", - expectedAttr: map[core.Key]core.Value{ - rpcServiceKey: core.String(""), - netPeerIPKey: core.String("fake"), - netPeerPortKey: core.String("connection"), + expectedAttr: map[kv.Key]value.Value{ + rpcServiceKey: value.String(""), + netPeerIPKey: value.String("fake"), + netPeerPortKey: value.String("connection"), }, - eventsAttr: []map[core.Key]core.Value{ + eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: core.String("SENT"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), + messageTypeKey: value.String("SENT"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: core.String("RECEIVED"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), + messageTypeKey: value.String("RECEIVED"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, { name: "/github.com.foo.serviceName_123/method", - expectedAttr: map[core.Key]core.Value{ - rpcServiceKey: core.String("serviceName_123"), - netPeerIPKey: core.String("fake"), - netPeerPortKey: core.String("connection"), + expectedAttr: map[kv.Key]value.Value{ + rpcServiceKey: value.String("serviceName_123"), + netPeerIPKey: value.String("fake"), + netPeerPortKey: value.String("connection"), }, - eventsAttr: []map[core.Key]core.Value{ + eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: core.String("SENT"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(req))), + messageTypeKey: value.String("SENT"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: core.String("RECEIVED"), - messageIDKey: core.Int(1), - messageUncompressedSizeKey: core.Int(proto.Size(proto.Message(reply))), + messageTypeKey: value.String("RECEIVED"), + messageIDKey: value.Int(1), + messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, @@ -336,7 +337,7 @@ func TestStreamClientInterceptor(t *testing.T) { } attrs := spanData.Attributes - expectedAttr := map[core.Key]string{ + expectedAttr := map[kv.Key]string{ rpcServiceKey: "serviceName", netPeerIPKey: "fake", netPeerPortKey: "connection", @@ -358,12 +359,12 @@ func TestStreamClientInterceptor(t *testing.T) { } for i := 0; i < 20; i += 2 { msgID := i/2 + 1 - validate := func(eventName string, attrs []core.KeyValue) { + validate := func(eventName string, attrs []kv.KeyValue) { for _, attr := range attrs { if attr.Key == messageTypeKey && attr.Value.AsString() != eventName { t.Errorf("invalid event on index: %d expecting %s event, receive %s event", i, eventName, attr.Value.AsString()) } - if attr.Key == messageIDKey && attr.Value != core.Int(msgID) { + if attr.Key == messageIDKey && attr.Value != value.Int(msgID) { t.Errorf("invalid id for message event expected %d received %d", msgID, attr.Value.AsInt32()) } } From 0122b586b7759042ba1f8d8e0efe2319824ec311 Mon Sep 17 00:00:00 2001 From: ET Date: Fri, 15 May 2020 21:53:05 -0700 Subject: [PATCH 051/108] Ensure golang alpine image is running golang-1.14 (#733) Older versions of go (even only as recently as 1.12.7) have problems building outside of $GOPATH --- example/http/Dockerfile | 2 +- example/zipkin/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/http/Dockerfile b/example/http/Dockerfile index 01493194c..23f88befd 100644 --- a/example/http/Dockerfile +++ b/example/http/Dockerfile @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:alpine AS base +FROM golang:1.14-alpine AS base COPY . /go/src/github.com/open-telemetry/opentelemetry-go/ WORKDIR /go/src/github.com/open-telemetry/opentelemetry-go/example/http/ diff --git a/example/zipkin/Dockerfile b/example/zipkin/Dockerfile index 6efef34b5..46421442b 100644 --- a/example/zipkin/Dockerfile +++ b/example/zipkin/Dockerfile @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:alpine +FROM golang:1.14-alpine COPY . /go/src/github.com/open-telemetry/opentelemetry-go/ WORKDIR /go/src/github.com/open-telemetry/opentelemetry-go/example/zipkin/ RUN go install ./main.go From 6bc14ffd2ccc1f3090c8a58f0eb9143245576f06 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 15 May 2020 22:11:12 -0700 Subject: [PATCH 052/108] Replace Measure instrument by ValueRecorder instrument (#732) * Measure->Value recorder and cleanups re: measure * More edits * More edits * Feedback --- api/global/internal/benchmark_test.go | 2 +- api/global/internal/meter_test.go | 22 +++--- api/global/internal/registry_test.go | 8 +- api/metric/api_test.go | 14 ++-- api/metric/doc.go | 70 ++++++---------- api/metric/kind.go | 4 +- api/metric/kind_string.go | 6 +- api/metric/meter.go | 16 ++-- api/metric/must.go | 12 +-- api/metric/registry/registry_test.go | 8 +- api/metric/sdkapi.go | 2 +- api/metric/sync.go | 12 +-- api/metric/{measure.go => valuerecorder.go} | 48 +++++------ example/basic/main.go | 10 +-- example/prometheus/main.go | 18 ++--- exporters/metric/prometheus/prometheus.go | 4 +- .../metric/prometheus/prometheus_test.go | 74 ++++++++--------- exporters/metric/stdout/stdout.go | 6 +- exporters/metric/stdout/stdout_test.go | 8 +- exporters/metric/test/test.go | 4 +- .../otlp/internal/transform/metric_test.go | 12 +-- exporters/otlp/otlp_integration_test.go | 22 +++--- exporters/otlp/otlp_metric_test.go | 8 +- sdk/export/metric/aggregator/aggregator.go | 2 +- .../metric/aggregator/aggregator_test.go | 2 +- sdk/export/metric/metric.go | 20 ++--- sdk/metric/aggregator/array/array.go | 2 + sdk/metric/aggregator/array/array_test.go | 8 +- sdk/metric/aggregator/ddsketch/ddsketch.go | 2 +- .../aggregator/ddsketch/ddsketch_test.go | 4 +- sdk/metric/aggregator/histogram/histogram.go | 2 +- .../aggregator/histogram/histogram_test.go | 6 +- sdk/metric/aggregator/minmaxsumcount/mmsc.go | 9 ++- .../aggregator/minmaxsumcount/mmsc_test.go | 6 +- sdk/metric/aggregator/sum/sum_test.go | 4 +- sdk/metric/benchmark_test.go | 50 ++++++------ sdk/metric/correct_test.go | 30 +++---- sdk/metric/doc.go | 79 ++++++------------- sdk/metric/histogram_stress_test.go | 2 +- sdk/metric/minmaxsumcount_stress_test.go | 2 +- sdk/metric/selector/simple/simple.go | 32 ++++---- sdk/metric/selector/simple/simple_test.go | 30 +++---- sdk/metric/stress_test.go | 14 ++-- 43 files changed, 321 insertions(+), 375 deletions(-) rename api/metric/{measure.go => valuerecorder.go} (51%) diff --git a/api/global/internal/benchmark_test.go b/api/global/internal/benchmark_test.go index 66a3728d1..017afbd95 100644 --- a/api/global/internal/benchmark_test.go +++ b/api/global/internal/benchmark_test.go @@ -59,7 +59,7 @@ func (*benchFixture) AggregatorFor(descriptor *metric.Descriptor) export.Aggrega switch descriptor.MetricKind() { case metric.CounterKind: return sum.New() - case metric.MeasureKind: + case metric.ValueRecorderKind: if strings.HasSuffix(descriptor.Name(), "minmaxsumcount") { return minmaxsumcount.New(descriptor) } else if strings.HasSuffix(descriptor.Name(), "ddsketch") { diff --git a/api/global/internal/meter_test.go b/api/global/internal/meter_test.go index 070e65221..17485b745 100644 --- a/api/global/internal/meter_test.go +++ b/api/global/internal/meter_test.go @@ -82,9 +82,9 @@ func TestDirect(t *testing.T) { counter.Add(ctx, 1, labels1...) counter.Add(ctx, 1, labels1...) - measure := Must(meter1).NewFloat64Measure("test.measure") - measure.Record(ctx, 1, labels1...) - measure.Record(ctx, 2, labels1...) + valuerecorder := Must(meter1).NewFloat64ValueRecorder("test.valuerecorder") + valuerecorder.Record(ctx, 1, labels1...) + valuerecorder.Record(ctx, 2, labels1...) _ = Must(meter1).RegisterFloat64Observer("test.observer.float", func(result metric.Float64ObserverResult) { result.Observe(1., labels1...) @@ -96,7 +96,7 @@ func TestDirect(t *testing.T) { result.Observe(2, labels2...) }) - second := Must(meter2).NewFloat64Measure("test.second") + second := Must(meter2).NewFloat64ValueRecorder("test.second") second.Record(ctx, 1, labels3...) second.Record(ctx, 2, labels3...) @@ -104,7 +104,7 @@ func TestDirect(t *testing.T) { global.SetMeterProvider(provider) counter.Add(ctx, 1, labels1...) - measure.Record(ctx, 3, labels1...) + valuerecorder.Record(ctx, 3, labels1...) second.Record(ctx, 3, labels3...) mock.RunAsyncInstruments() @@ -120,7 +120,7 @@ func TestDirect(t *testing.T) { Number: asInt(1), }, { - Name: "test.measure", + Name: "test.valuerecorder", LibraryName: "test1", Labels: asMap(labels1...), Number: asFloat(3), @@ -174,8 +174,8 @@ func TestBound(t *testing.T) { boundC.Add(ctx, 1) boundC.Add(ctx, 1) - measure := Must(glob).NewInt64Measure("test.measure") - boundM := measure.Bind(labels1...) + valuerecorder := Must(glob).NewInt64ValueRecorder("test.valuerecorder") + boundM := valuerecorder.Bind(labels1...) boundM.Record(ctx, 1) boundM.Record(ctx, 2) @@ -194,7 +194,7 @@ func TestBound(t *testing.T) { Number: asFloat(1), }, { - Name: "test.measure", + Name: "test.valuerecorder", LibraryName: "test", Labels: asMap(labels1...), Number: asInt(3), @@ -216,8 +216,8 @@ func TestUnbind(t *testing.T) { counter := Must(glob).NewFloat64Counter("test.counter") boundC := counter.Bind(labels1...) - measure := Must(glob).NewInt64Measure("test.measure") - boundM := measure.Bind(labels1...) + valuerecorder := Must(glob).NewInt64ValueRecorder("test.valuerecorder") + boundM := valuerecorder.Bind(labels1...) boundC.Unbind() boundM.Unbind() diff --git a/api/global/internal/registry_test.go b/api/global/internal/registry_test.go index 6e6af1407..14ae04dfd 100644 --- a/api/global/internal/registry_test.go +++ b/api/global/internal/registry_test.go @@ -36,11 +36,11 @@ var ( "counter.float64": func(name, libraryName string) (metric.InstrumentImpl, error) { return unwrap(MeterProvider().Meter(libraryName).NewFloat64Counter(name)) }, - "measure.int64": func(name, libraryName string) (metric.InstrumentImpl, error) { - return unwrap(MeterProvider().Meter(libraryName).NewInt64Measure(name)) + "valuerecorder.int64": func(name, libraryName string) (metric.InstrumentImpl, error) { + return unwrap(MeterProvider().Meter(libraryName).NewInt64ValueRecorder(name)) }, - "measure.float64": func(name, libraryName string) (metric.InstrumentImpl, error) { - return unwrap(MeterProvider().Meter(libraryName).NewFloat64Measure(name)) + "valuerecorder.float64": func(name, libraryName string) (metric.InstrumentImpl, error) { + return unwrap(MeterProvider().Meter(libraryName).NewFloat64ValueRecorder(name)) }, "observer.int64": func(name, libraryName string) (metric.InstrumentImpl, error) { return unwrap(MeterProvider().Meter(libraryName).RegisterInt64Observer(name, func(metric.Int64ObserverResult) {})) diff --git a/api/metric/api_test.go b/api/metric/api_test.go index 1395b8687..9a370a8d4 100644 --- a/api/metric/api_test.go +++ b/api/metric/api_test.go @@ -119,29 +119,29 @@ func TestCounter(t *testing.T) { } } -func TestMeasure(t *testing.T) { +func TestValueRecorder(t *testing.T) { { mockSDK, meter := mockTest.NewMeter() - m := Must(meter).NewFloat64Measure("test.measure.float") + m := Must(meter).NewFloat64ValueRecorder("test.valuerecorder.float") ctx := context.Background() labels := []kv.KeyValue{} m.Record(ctx, 42, labels...) boundInstrument := m.Bind(labels...) boundInstrument.Record(ctx, 42) meter.RecordBatch(ctx, labels, m.Measurement(42)) - t.Log("Testing float measure") + t.Log("Testing float valuerecorder") checkBatches(t, ctx, labels, mockSDK, metric.Float64NumberKind, m.SyncImpl()) } { mockSDK, meter := mockTest.NewMeter() - m := Must(meter).NewInt64Measure("test.measure.int") + m := Must(meter).NewInt64ValueRecorder("test.valuerecorder.int") ctx := context.Background() labels := []kv.KeyValue{kv.Int("I", 1)} m.Record(ctx, 42, labels...) boundInstrument := m.Bind(labels...) boundInstrument.Record(ctx, 42) meter.RecordBatch(ctx, labels, m.Measurement(42)) - t.Log("Testing int measure") + t.Log("Testing int valuerecorder") checkBatches(t, ctx, labels, mockSDK, metric.Int64NumberKind, m.SyncImpl()) } } @@ -309,10 +309,10 @@ func TestWrappedInstrumentError(t *testing.T) { impl := &testWrappedMeter{} meter := metric.WrapMeterImpl(impl, "test") - measure, err := meter.NewInt64Measure("test.measure") + valuerecorder, err := meter.NewInt64ValueRecorder("test.valuerecorder") require.Equal(t, err, metric.ErrSDKReturnedNilImpl) - require.NotNil(t, measure.SyncImpl()) + require.NotNil(t, valuerecorder.SyncImpl()) observer, err := meter.RegisterInt64Observer("test.observer", func(result metric.Int64ObserverResult) {}) diff --git a/api/metric/doc.go b/api/metric/doc.go index 127b33bd4..8249b6f6d 100644 --- a/api/metric/doc.go +++ b/api/metric/doc.go @@ -13,57 +13,37 @@ // limitations under the License. // metric package provides an API for reporting diagnostic -// measurements using four basic kinds of instruments. +// measurements using instruments categorized as follows: // -// The three basic kinds are: +// Synchronous instruments are called by the user with a Context. +// Asynchronous instruments are called by the SDK during collection. // -// - counters -// - measures -// - observers +// Additive instruments are semantically intended for capturing a sum. +// Non-additive instruments are intended for capturing a distribution. // -// All instruments report either float64 or int64 values. +// Additive instruments may be monotonic, in which case they are +// non-descreasing and naturally define a rate. // -// The primary object that handles metrics is Meter. Meter can be -// obtained from Provider. The implementations of the Meter and -// Provider are provided by SDK. Normally, the Meter is used directly -// only for the instrument creation and batch recording. +// The synchronous instrument names are: // -// Counters are instruments that are reporting a quantity or a sum. An -// example could be bank account balance or bytes downloaded. Counters -// can be created with either NewFloat64Counter or -// NewInt64Counter. Counters expect non-negative values by default to -// be reported. This can be changed with the WithMonotonic option -// (passing false as a parameter) passed to the Meter.New*Counter -// function - this allows reporting negative values. To report the new -// value, use an Add function. +// Counter: additive, monotonic +// UpDownCounter: additive +// ValueRecorder: non-additive // -// Measures are instruments that are reporting values that are -// recorded separately to figure out some statistical properties from -// those values (like average). An example could be temperature over -// time or lines of code in the project over time. Measures can be -// created with either NewFloat64Measure or NewInt64Measure. Measures -// by default take only non-negative values. This can be changed with -// the WithAbsolute option (passing false as a parameter) passed to -// the New*Measure function - this allows reporting negative values -// too. To report a new value, use the Record function. +// and the asynchronous instruments are: // -// Observers are instruments that are reporting a current state of a -// set of values. An example could be voltage or -// temperature. Observers can be created with either -// RegisterFloat64Observer or RegisterInt64Observer. Observers by -// default have no limitations about reported values - they can be -// less or greater than the last reported value. This can be changed -// with the WithMonotonic option passed to the Register*Observer -// function - this permits the reported values only to go -// up. Reporting of the new values happens asynchronously, with the -// use of a callback passed to the Register*Observer function. The -// callback can report multiple values. There is no unregister function. +// SumObserver: additive, monotonic +// UpDownSumOnserver: additive +// ValueObserver: non-additive // -// Counters and measures support creating bound instruments for a -// potentially more efficient reporting. The bound instruments have -// the same function names as the instruments (so a Counter bound -// instrument has Add, and a Measure bound instrument has Record). -// Bound Instruments can be created with the Bind function of the -// respective instrument. When done with the bound instrument, call -// Unbind on it. +// All instruments are provided with support for either float64 or +// int64 input values. +// +// The Meter interface supports allocating new instruments as well as +// interfaces for recording batches of synchronous measurements or +// asynchronous observations. To obtain a Meter, use a Provider. +// +// The Provider interface supports obtaining a named Meter interface. +// To obtain a Provider implementation, initialize and configure any +// compatible SDK. package metric // import "go.opentelemetry.io/otel/api/metric" diff --git a/api/metric/kind.go b/api/metric/kind.go index 63799ca96..38001e918 100644 --- a/api/metric/kind.go +++ b/api/metric/kind.go @@ -20,8 +20,8 @@ package metric type Kind int8 const ( - // MeasureKind indicates a Measure instrument. - MeasureKind Kind = iota + // ValueRecorderKind indicates a ValueRecorder instrument. + ValueRecorderKind Kind = iota // ObserverKind indicates an Observer instrument. ObserverKind // CounterKind indicates a Counter instrument. diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index f46c1463f..67113b120 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -8,14 +8,14 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[MeasureKind-0] + _ = x[ValueRecorderKind-0] _ = x[ObserverKind-1] _ = x[CounterKind-2] } -const _Kind_name = "MeasureKindObserverKindCounterKind" +const _Kind_name = "ValueRecorderKindObserverKindCounterKind" -var _Kind_index = [...]uint8{0, 11, 23, 34} +var _Kind_index = [...]uint8{0, 17, 29, 40} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { diff --git a/api/metric/meter.go b/api/metric/meter.go index e20b02b1e..5e95e2812 100644 --- a/api/metric/meter.go +++ b/api/metric/meter.go @@ -82,22 +82,22 @@ func (m Meter) NewFloat64Counter(name string, options ...Option) (Float64Counter m.newSync(name, CounterKind, Float64NumberKind, options)) } -// NewInt64Measure creates a new integer Measure instrument with the +// NewInt64ValueRecorder creates a new integer ValueRecorder instrument with the // given name, customized with options. May return an error if the // name is invalid (e.g., empty) or improperly registered (e.g., // duplicate registration). -func (m Meter) NewInt64Measure(name string, opts ...Option) (Int64Measure, error) { - return wrapInt64MeasureInstrument( - m.newSync(name, MeasureKind, Int64NumberKind, opts)) +func (m Meter) NewInt64ValueRecorder(name string, opts ...Option) (Int64ValueRecorder, error) { + return wrapInt64ValueRecorderInstrument( + m.newSync(name, ValueRecorderKind, Int64NumberKind, opts)) } -// NewFloat64Measure creates a new floating point Measure with the +// NewFloat64ValueRecorder creates a new floating point ValueRecorder with the // given name, customized with options. May return an error if the // name is invalid (e.g., empty) or improperly registered (e.g., // duplicate registration). -func (m Meter) NewFloat64Measure(name string, opts ...Option) (Float64Measure, error) { - return wrapFloat64MeasureInstrument( - m.newSync(name, MeasureKind, Float64NumberKind, opts)) +func (m Meter) NewFloat64ValueRecorder(name string, opts ...Option) (Float64ValueRecorder, error) { + return wrapFloat64ValueRecorderInstrument( + m.newSync(name, ValueRecorderKind, Float64NumberKind, opts)) } // RegisterInt64Observer creates a new integer Observer instrument diff --git a/api/metric/must.go b/api/metric/must.go index ecd47b0e0..b747932f3 100644 --- a/api/metric/must.go +++ b/api/metric/must.go @@ -53,20 +53,20 @@ func (mm MeterMust) NewFloat64Counter(name string, cos ...Option) Float64Counter } } -// NewInt64Measure calls `Meter.NewInt64Measure` and returns the +// NewInt64ValueRecorder calls `Meter.NewInt64ValueRecorder` and returns the // instrument, panicking if it encounters an error. -func (mm MeterMust) NewInt64Measure(name string, mos ...Option) Int64Measure { - if inst, err := mm.meter.NewInt64Measure(name, mos...); err != nil { +func (mm MeterMust) NewInt64ValueRecorder(name string, mos ...Option) Int64ValueRecorder { + if inst, err := mm.meter.NewInt64ValueRecorder(name, mos...); err != nil { panic(err) } else { return inst } } -// NewFloat64Measure calls `Meter.NewFloat64Measure` and returns the +// NewFloat64ValueRecorder calls `Meter.NewFloat64ValueRecorder` and returns the // instrument, panicking if it encounters an error. -func (mm MeterMust) NewFloat64Measure(name string, mos ...Option) Float64Measure { - if inst, err := mm.meter.NewFloat64Measure(name, mos...); err != nil { +func (mm MeterMust) NewFloat64ValueRecorder(name string, mos ...Option) Float64ValueRecorder { + if inst, err := mm.meter.NewFloat64ValueRecorder(name, mos...); err != nil { panic(err) } else { return inst diff --git a/api/metric/registry/registry_test.go b/api/metric/registry/registry_test.go index 55eea312f..51f8392a1 100644 --- a/api/metric/registry/registry_test.go +++ b/api/metric/registry/registry_test.go @@ -37,11 +37,11 @@ var ( "counter.float64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { return unwrap(m.NewFloat64Counter(name)) }, - "measure.int64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { - return unwrap(m.NewInt64Measure(name)) + "valuerecorder.int64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { + return unwrap(m.NewInt64ValueRecorder(name)) }, - "measure.float64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { - return unwrap(m.NewFloat64Measure(name)) + "valuerecorder.float64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { + return unwrap(m.NewFloat64ValueRecorder(name)) }, "observer.int64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { return unwrap(m.RegisterInt64Observer(name, func(metric.Int64ObserverResult) {})) diff --git a/api/metric/sdkapi.go b/api/metric/sdkapi.go index c9b902dc8..c7a6fe4c5 100644 --- a/api/metric/sdkapi.go +++ b/api/metric/sdkapi.go @@ -53,7 +53,7 @@ type InstrumentImpl interface { } // SyncImpl is the implementation-level interface to a generic -// synchronous instrument (e.g., Measure and Counter instruments). +// synchronous instrument (e.g., ValueRecorder and Counter instruments). type SyncImpl interface { InstrumentImpl diff --git a/api/metric/sync.go b/api/metric/sync.go index 6b8d3a2a3..66e99c285 100644 --- a/api/metric/sync.go +++ b/api/metric/sync.go @@ -174,22 +174,22 @@ func wrapFloat64CounterInstrument(syncInst SyncImpl, err error) (Float64Counter, return Float64Counter{syncInstrument: common}, err } -// wrapInt64MeasureInstrument returns an `Int64Measure` from a +// wrapInt64ValueRecorderInstrument returns an `Int64ValueRecorder` from a // `SyncImpl`. An error will be generated if the // `SyncImpl` is nil (in which case a No-op is substituted), // otherwise the error passes through. -func wrapInt64MeasureInstrument(syncInst SyncImpl, err error) (Int64Measure, error) { +func wrapInt64ValueRecorderInstrument(syncInst SyncImpl, err error) (Int64ValueRecorder, error) { common, err := checkNewSync(syncInst, err) - return Int64Measure{syncInstrument: common}, err + return Int64ValueRecorder{syncInstrument: common}, err } -// wrapFloat64MeasureInstrument returns an `Float64Measure` from a +// wrapFloat64ValueRecorderInstrument returns an `Float64ValueRecorder` from a // `SyncImpl`. An error will be generated if the // `SyncImpl` is nil (in which case a No-op is substituted), // otherwise the error passes through. -func wrapFloat64MeasureInstrument(syncInst SyncImpl, err error) (Float64Measure, error) { +func wrapFloat64ValueRecorderInstrument(syncInst SyncImpl, err error) (Float64ValueRecorder, error) { common, err := checkNewSync(syncInst, err) - return Float64Measure{syncInstrument: common}, err + return Float64ValueRecorder{syncInstrument: common}, err } // wrapInt64ObserverInstrument returns an `Int64Observer` from a diff --git a/api/metric/measure.go b/api/metric/valuerecorder.go similarity index 51% rename from api/metric/measure.go rename to api/metric/valuerecorder.go index 11dd215ee..f4723fb98 100644 --- a/api/metric/measure.go +++ b/api/metric/valuerecorder.go @@ -20,78 +20,78 @@ import ( "go.opentelemetry.io/otel/api/kv" ) -// Float64Measure is a metric that records float64 values. -type Float64Measure struct { +// Float64ValueRecorder is a metric that records float64 values. +type Float64ValueRecorder struct { syncInstrument } -// Int64Measure is a metric that records int64 values. -type Int64Measure struct { +// Int64ValueRecorder is a metric that records int64 values. +type Int64ValueRecorder struct { syncInstrument } -// BoundFloat64Measure is a bound instrument for Float64Measure. +// BoundFloat64ValueRecorder is a bound instrument for Float64ValueRecorder. // // It inherits the Unbind function from syncBoundInstrument. -type BoundFloat64Measure struct { +type BoundFloat64ValueRecorder struct { syncBoundInstrument } -// BoundInt64Measure is a bound instrument for Int64Measure. +// BoundInt64ValueRecorder is a bound instrument for Int64ValueRecorder. // // It inherits the Unbind function from syncBoundInstrument. -type BoundInt64Measure struct { +type BoundInt64ValueRecorder struct { syncBoundInstrument } -// Bind creates a bound instrument for this measure. The labels are +// Bind creates a bound instrument for this ValueRecorder. The labels are // associated with values recorded via subsequent calls to Record. -func (c Float64Measure) Bind(labels ...kv.KeyValue) (h BoundFloat64Measure) { +func (c Float64ValueRecorder) Bind(labels ...kv.KeyValue) (h BoundFloat64ValueRecorder) { h.syncBoundInstrument = c.bind(labels) return } -// Bind creates a bound instrument for this measure. The labels are +// Bind creates a bound instrument for this ValueRecorder. The labels are // associated with values recorded via subsequent calls to Record. -func (c Int64Measure) Bind(labels ...kv.KeyValue) (h BoundInt64Measure) { +func (c Int64ValueRecorder) Bind(labels ...kv.KeyValue) (h BoundInt64ValueRecorder) { h.syncBoundInstrument = c.bind(labels) return } // Measurement creates a Measurement object to use with batch // recording. -func (c Float64Measure) Measurement(value float64) Measurement { +func (c Float64ValueRecorder) Measurement(value float64) Measurement { return c.float64Measurement(value) } // Measurement creates a Measurement object to use with batch // recording. -func (c Int64Measure) Measurement(value int64) Measurement { +func (c Int64ValueRecorder) Measurement(value int64) Measurement { return c.int64Measurement(value) } -// Record adds a new value to the list of measure's records. The +// Record adds a new value to the list of ValueRecorder's records. The // labels should contain the keys and values to be associated with // this value. -func (c Float64Measure) Record(ctx context.Context, value float64, labels ...kv.KeyValue) { +func (c Float64ValueRecorder) Record(ctx context.Context, value float64, labels ...kv.KeyValue) { c.directRecord(ctx, NewFloat64Number(value), labels) } -// Record adds a new value to the list of measure's records. The +// Record adds a new value to the ValueRecorder's distribution. The // labels should contain the keys and values to be associated with // this value. -func (c Int64Measure) Record(ctx context.Context, value int64, labels ...kv.KeyValue) { +func (c Int64ValueRecorder) Record(ctx context.Context, value int64, labels ...kv.KeyValue) { c.directRecord(ctx, NewInt64Number(value), labels) } -// Record adds a new value to the list of measure's records using the labels -// previously bound to the measure via Bind() -func (b BoundFloat64Measure) Record(ctx context.Context, value float64) { +// Record adds a new value to the ValueRecorder's distribution using the labels +// previously bound to the ValueRecorder via Bind(). +func (b BoundFloat64ValueRecorder) Record(ctx context.Context, value float64) { b.directRecord(ctx, NewFloat64Number(value)) } -// Record adds a new value to the list of measure's records using the labels -// previously bound to the measure via Bind() -func (b BoundInt64Measure) Record(ctx context.Context, value int64) { +// Record adds a new value to the ValueRecorder's distribution using the labels +// previously bound to the ValueRecorder via Bind(). +func (b BoundInt64ValueRecorder) Record(ctx context.Context, value int64) { b.directRecord(ctx, NewInt64Number(value)) } diff --git a/example/basic/main.go b/example/basic/main.go index 9a5b05983..84470d10b 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -80,7 +80,7 @@ func main() { metric.WithDescription("An observer set to 1.0"), ) - measureTwo := metric.Must(meter).NewFloat64Measure("ex.com.two") + valuerecorderTwo := metric.Must(meter).NewFloat64ValueRecorder("ex.com.two") ctx := context.Background() @@ -89,8 +89,8 @@ func main() { barKey.String("bar1"), ) - measure := measureTwo.Bind(commonLabels...) - defer measure.Unbind() + valuerecorder := valuerecorderTwo.Bind(commonLabels...) + defer valuerecorder.Unbind() err := tracer.WithSpan(ctx, "operation", func(ctx context.Context) error { @@ -103,7 +103,7 @@ func main() { correlation.NewContext(ctx, anotherKey.String("xyz")), commonLabels, - measureTwo.Measurement(2.0), + valuerecorderTwo.Measurement(2.0), ) return tracer.WithSpan( @@ -114,7 +114,7 @@ func main() { trace.SpanFromContext(ctx).AddEvent(ctx, "Sub span event") - measure.Record(ctx, 1.3) + valuerecorder.Record(ctx, 1.3) return nil }, diff --git a/example/prometheus/main.go b/example/prometheus/main.go index 6c0282801..f9a5cf702 100644 --- a/example/prometheus/main.go +++ b/example/prometheus/main.go @@ -60,11 +60,11 @@ func main() { result.Observe(value, labels...) } _ = metric.Must(meter).RegisterFloat64Observer("ex.com.one", cb, - metric.WithDescription("A measure set to 1.0"), + metric.WithDescription("An observer set to 1.0"), ) - measureTwo := metric.Must(meter).NewFloat64Measure("ex.com.two") - measureThree := metric.Must(meter).NewFloat64Counter("ex.com.three") + valuerecorder := metric.Must(meter).NewFloat64ValueRecorder("ex.com.two") + counter := metric.Must(meter).NewFloat64Counter("ex.com.three") commonLabels := []kv.KeyValue{lemonsKey.Int(10), kv.String("A", "1"), kv.String("B", "2"), kv.String("C", "3")} notSoCommonLabels := []kv.KeyValue{lemonsKey.Int(13)} @@ -78,8 +78,8 @@ func main() { meter.RecordBatch( ctx, commonLabels, - measureTwo.Measurement(2.0), - measureThree.Measurement(12.0), + valuerecorder.Measurement(2.0), + counter.Measurement(12.0), ) time.Sleep(5 * time.Second) @@ -91,8 +91,8 @@ func main() { meter.RecordBatch( ctx, notSoCommonLabels, - measureTwo.Measurement(2.0), - measureThree.Measurement(22.0), + valuerecorder.Measurement(2.0), + counter.Measurement(22.0), ) time.Sleep(5 * time.Second) @@ -104,8 +104,8 @@ func main() { meter.RecordBatch( ctx, commonLabels, - measureTwo.Measurement(12.0), - measureThree.Measurement(13.0), + valuerecorder.Measurement(12.0), + counter.Measurement(13.0), ) time.Sleep(100 * time.Second) diff --git a/exporters/metric/prometheus/prometheus.go b/exporters/metric/prometheus/prometheus.go index 2d8ec76de..5af88a6b7 100644 --- a/exporters/metric/prometheus/prometheus.go +++ b/exporters/metric/prometheus/prometheus.go @@ -147,7 +147,7 @@ func InstallNewPipeline(config Config) (*push.Controller, http.HandlerFunc, erro // NewExportPipeline sets up a complete export pipeline with the recommended setup, // chaining a NewRawExporter into the recommended selectors and integrators. func NewExportPipeline(config Config, period time.Duration) (*push.Controller, http.HandlerFunc, error) { - selector := simple.NewWithHistogramMeasure(config.DefaultHistogramBoundaries) + selector := simple.NewWithHistogramDistribution(config.DefaultHistogramBoundaries) exporter, err := NewRawExporter(config) if err != nil { return nil, nil, err @@ -220,7 +220,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) { } } else if dist, ok := agg.(aggregator.Distribution); ok { // TODO: summaries values are never being resetted. - // As measures are recorded, new records starts to have less impact on these summaries. + // As measurements are recorded, new records starts to have less impact on these summaries. // We should implement an solution that is similar to the Prometheus Clients // using a rolling window for summaries could be a solution. // diff --git a/exporters/metric/prometheus/prometheus_test.go b/exporters/metric/prometheus/prometheus_test.go index 362df65a6..0505281a4 100644 --- a/exporters/metric/prometheus/prometheus_test.go +++ b/exporters/metric/prometheus/prometheus_test.go @@ -45,10 +45,10 @@ func TestPrometheusExporter(t *testing.T) { "counter", metric.CounterKind, metric.Float64NumberKind) lastValue := metric.NewDescriptor( "lastvalue", metric.ObserverKind, metric.Float64NumberKind) - measure := metric.NewDescriptor( - "measure", metric.MeasureKind, metric.Float64NumberKind) - histogramMeasure := metric.NewDescriptor( - "histogram_measure", metric.MeasureKind, metric.Float64NumberKind) + valuerecorder := metric.NewDescriptor( + "valuerecorder", metric.ValueRecorderKind, metric.Float64NumberKind) + histogramValueRecorder := metric.NewDescriptor( + "histogram_valuerecorder", metric.ValueRecorderKind, metric.Float64NumberKind) labels := []kv.KeyValue{ kv.Key("A").String("B"), @@ -61,26 +61,26 @@ func TestPrometheusExporter(t *testing.T) { checkpointSet.AddLastValue(&lastValue, 13.2, labels...) expected = append(expected, `lastvalue{A="B",C="D"} 13.2`) - checkpointSet.AddMeasure(&measure, 13, labels...) - checkpointSet.AddMeasure(&measure, 15, labels...) - checkpointSet.AddMeasure(&measure, 17, labels...) - expected = append(expected, `measure{A="B",C="D",quantile="0.5"} 15`) - expected = append(expected, `measure{A="B",C="D",quantile="0.9"} 17`) - expected = append(expected, `measure{A="B",C="D",quantile="0.99"} 17`) - expected = append(expected, `measure_sum{A="B",C="D"} 45`) - expected = append(expected, `measure_count{A="B",C="D"} 3`) + checkpointSet.AddValueRecorder(&valuerecorder, 13, labels...) + checkpointSet.AddValueRecorder(&valuerecorder, 15, labels...) + checkpointSet.AddValueRecorder(&valuerecorder, 17, labels...) + expected = append(expected, `valuerecorder{A="B",C="D",quantile="0.5"} 15`) + expected = append(expected, `valuerecorder{A="B",C="D",quantile="0.9"} 17`) + expected = append(expected, `valuerecorder{A="B",C="D",quantile="0.99"} 17`) + expected = append(expected, `valuerecorder_sum{A="B",C="D"} 45`) + expected = append(expected, `valuerecorder_count{A="B",C="D"} 3`) boundaries := []metric.Number{metric.NewFloat64Number(-0.5), metric.NewFloat64Number(1)} - checkpointSet.AddHistogramMeasure(&histogramMeasure, boundaries, -0.6, labels...) - checkpointSet.AddHistogramMeasure(&histogramMeasure, boundaries, -0.4, labels...) - checkpointSet.AddHistogramMeasure(&histogramMeasure, boundaries, 0.6, labels...) - checkpointSet.AddHistogramMeasure(&histogramMeasure, boundaries, 20, labels...) + checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.6, labels...) + checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.4, labels...) + checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, 0.6, labels...) + checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, 20, labels...) - expected = append(expected, `histogram_measure_bucket{A="B",C="D",le="+Inf"} 4`) - expected = append(expected, `histogram_measure_bucket{A="B",C="D",le="-0.5"} 1`) - expected = append(expected, `histogram_measure_bucket{A="B",C="D",le="1"} 3`) - expected = append(expected, `histogram_measure_count{A="B",C="D"} 4`) - expected = append(expected, `histogram_measure_sum{A="B",C="D"} 19.6`) + expected = append(expected, `histogram_valuerecorder_bucket{A="B",C="D",le="+Inf"} 4`) + expected = append(expected, `histogram_valuerecorder_bucket{A="B",C="D",le="-0.5"} 1`) + expected = append(expected, `histogram_valuerecorder_bucket{A="B",C="D",le="1"} 3`) + expected = append(expected, `histogram_valuerecorder_count{A="B",C="D"} 4`) + expected = append(expected, `histogram_valuerecorder_sum{A="B",C="D"} 19.6`) missingLabels := []kv.KeyValue{ kv.Key("A").String("E"), @@ -93,25 +93,25 @@ func TestPrometheusExporter(t *testing.T) { checkpointSet.AddLastValue(&lastValue, 32, missingLabels...) expected = append(expected, `lastvalue{A="E",C=""} 32`) - checkpointSet.AddMeasure(&measure, 19, missingLabels...) - expected = append(expected, `measure{A="E",C="",quantile="0.5"} 19`) - expected = append(expected, `measure{A="E",C="",quantile="0.9"} 19`) - expected = append(expected, `measure{A="E",C="",quantile="0.99"} 19`) - expected = append(expected, `measure_count{A="E",C=""} 1`) - expected = append(expected, `measure_sum{A="E",C=""} 19`) + checkpointSet.AddValueRecorder(&valuerecorder, 19, missingLabels...) + expected = append(expected, `valuerecorder{A="E",C="",quantile="0.5"} 19`) + expected = append(expected, `valuerecorder{A="E",C="",quantile="0.9"} 19`) + expected = append(expected, `valuerecorder{A="E",C="",quantile="0.99"} 19`) + expected = append(expected, `valuerecorder_count{A="E",C=""} 1`) + expected = append(expected, `valuerecorder_sum{A="E",C=""} 19`) boundaries = []metric.Number{metric.NewFloat64Number(0), metric.NewFloat64Number(1)} - checkpointSet.AddHistogramMeasure(&histogramMeasure, boundaries, -0.6, missingLabels...) - checkpointSet.AddHistogramMeasure(&histogramMeasure, boundaries, -0.4, missingLabels...) - checkpointSet.AddHistogramMeasure(&histogramMeasure, boundaries, -0.1, missingLabels...) - checkpointSet.AddHistogramMeasure(&histogramMeasure, boundaries, 15, missingLabels...) - checkpointSet.AddHistogramMeasure(&histogramMeasure, boundaries, 15, missingLabels...) + checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.6, missingLabels...) + checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.4, missingLabels...) + checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.1, missingLabels...) + checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, 15, missingLabels...) + checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, 15, missingLabels...) - expected = append(expected, `histogram_measure_bucket{A="E",C="",le="+Inf"} 5`) - expected = append(expected, `histogram_measure_bucket{A="E",C="",le="0"} 3`) - expected = append(expected, `histogram_measure_bucket{A="E",C="",le="1"} 3`) - expected = append(expected, `histogram_measure_count{A="E",C=""} 5`) - expected = append(expected, `histogram_measure_sum{A="E",C=""} 28.9`) + expected = append(expected, `histogram_valuerecorder_bucket{A="E",C="",le="+Inf"} 5`) + expected = append(expected, `histogram_valuerecorder_bucket{A="E",C="",le="0"} 3`) + expected = append(expected, `histogram_valuerecorder_bucket{A="E",C="",le="1"} 3`) + expected = append(expected, `histogram_valuerecorder_count{A="E",C=""} 5`) + expected = append(expected, `histogram_valuerecorder_sum{A="E",C=""} 28.9`) compareExport(t, exporter, checkpointSet, expected) } diff --git a/exporters/metric/stdout/stdout.go b/exporters/metric/stdout/stdout.go index 04e39e6b1..d22b5f070 100644 --- a/exporters/metric/stdout/stdout.go +++ b/exporters/metric/stdout/stdout.go @@ -53,8 +53,8 @@ type Config struct { // useful to create deterministic test conditions. DoNotPrintTime bool - // Quantiles are the desired aggregation quantiles for measure - // metric data, used when the configured aggregator supports + // Quantiles are the desired aggregation quantiles for distribution + // summaries, used when the configured aggregator supports // quantiles. // // Note: this exporter is meant as a demonstration; a real @@ -133,7 +133,7 @@ func InstallNewPipeline(config Config, opts ...push.Option) (*push.Controller, e // NewExportPipeline sets up a complete export pipeline with the recommended setup, // chaining a NewRawExporter into the recommended selectors and integrators. func NewExportPipeline(config Config, period time.Duration, opts ...push.Option) (*push.Controller, error) { - selector := simple.NewWithExactMeasure() + selector := simple.NewWithExactDistribution() exporter, err := NewRawExporter(config) if err != nil { return nil, err diff --git a/exporters/metric/stdout/stdout_test.go b/exporters/metric/stdout/stdout_test.go index a5f94df6c..918c47b8b 100644 --- a/exporters/metric/stdout/stdout_test.go +++ b/exporters/metric/stdout/stdout_test.go @@ -177,7 +177,7 @@ func TestStdoutMinMaxSumCount(t *testing.T) { checkpointSet := test.NewCheckpointSet() - desc := metric.NewDescriptor("test.name", metric.MeasureKind, metric.Float64NumberKind) + desc := metric.NewDescriptor("test.name", metric.ValueRecorderKind, metric.Float64NumberKind) magg := minmaxsumcount.New(&desc) aggtest.CheckedUpdate(fix.t, magg, metric.NewFloat64Number(123.456), &desc) aggtest.CheckedUpdate(fix.t, magg, metric.NewFloat64Number(876.543), &desc) @@ -190,14 +190,14 @@ func TestStdoutMinMaxSumCount(t *testing.T) { require.Equal(t, `{"updates":[{"name":"test.name{A=B,C=D}","min":123.456,"max":876.543,"sum":999.999,"count":2}]}`, fix.Output()) } -func TestStdoutMeasureFormat(t *testing.T) { +func TestStdoutValueRecorderFormat(t *testing.T) { fix := newFixture(t, nil, stdout.Config{ PrettyPrint: true, }) checkpointSet := test.NewCheckpointSet() - desc := metric.NewDescriptor("test.name", metric.MeasureKind, metric.Float64NumberKind) + desc := metric.NewDescriptor("test.name", metric.ValueRecorderKind, metric.Float64NumberKind) magg := array.New() for i := 0; i < 1000; i++ { @@ -238,7 +238,7 @@ func TestStdoutMeasureFormat(t *testing.T) { } func TestStdoutNoData(t *testing.T) { - desc := metric.NewDescriptor("test.name", metric.MeasureKind, metric.Float64NumberKind) + desc := metric.NewDescriptor("test.name", metric.ValueRecorderKind, metric.Float64NumberKind) for name, tc := range map[string]export.Aggregator{ "ddsketch": ddsketch.New(ddsketch.NewDefaultConfig(), &desc), "minmaxsumcount": minmaxsumcount.New(&desc), diff --git a/exporters/metric/test/test.go b/exporters/metric/test/test.go index a7dca90d1..bc49cd9c9 100644 --- a/exporters/metric/test/test.go +++ b/exporters/metric/test/test.go @@ -88,11 +88,11 @@ func (p *CheckpointSet) AddCounter(desc *metric.Descriptor, v float64, labels .. p.updateAggregator(desc, sum.New(), v, labels...) } -func (p *CheckpointSet) AddMeasure(desc *metric.Descriptor, v float64, labels ...kv.KeyValue) { +func (p *CheckpointSet) AddValueRecorder(desc *metric.Descriptor, v float64, labels ...kv.KeyValue) { p.updateAggregator(desc, array.New(), v, labels...) } -func (p *CheckpointSet) AddHistogramMeasure(desc *metric.Descriptor, boundaries []metric.Number, v float64, labels ...kv.KeyValue) { +func (p *CheckpointSet) AddHistogramValueRecorder(desc *metric.Descriptor, boundaries []metric.Number, v float64, labels ...kv.KeyValue) { p.updateAggregator(desc, histogram.New(desc, boundaries), v, labels...) } diff --git a/exporters/otlp/internal/transform/metric_test.go b/exporters/otlp/internal/transform/metric_test.go index 57e0388a9..79f71e187 100644 --- a/exporters/otlp/internal/transform/metric_test.go +++ b/exporters/otlp/internal/transform/metric_test.go @@ -111,7 +111,7 @@ func TestMinMaxSumCountMetricDescriptor(t *testing.T) { }{ { "mmsc-test-a", - metric.MeasureKind, + metric.ValueRecorderKind, "test-a-description", unit.Dimensionless, metric.Int64NumberKind, @@ -160,7 +160,7 @@ func TestMinMaxSumCountMetricDescriptor(t *testing.T) { } func TestMinMaxSumCountDatapoints(t *testing.T) { - desc := metric.NewDescriptor("", metric.MeasureKind, metric.Int64NumberKind) + desc := metric.NewDescriptor("", metric.ValueRecorderKind, metric.Int64NumberKind) labels := label.NewSet() mmsc := minmaxsumcount.New(&desc) assert.NoError(t, mmsc.Update(context.Background(), 1, &desc)) @@ -228,7 +228,7 @@ func TestSumMetricDescriptor(t *testing.T) { }, { "sum-test-b", - metric.MeasureKind, // This shouldn't change anything. + metric.ValueRecorderKind, // This shouldn't change anything. "test-b-description", unit.Milliseconds, metric.Float64NumberKind, @@ -257,7 +257,7 @@ func TestSumMetricDescriptor(t *testing.T) { } func TestSumInt64DataPoints(t *testing.T) { - desc := metric.NewDescriptor("", metric.MeasureKind, metric.Int64NumberKind) + desc := metric.NewDescriptor("", metric.ValueRecorderKind, metric.Int64NumberKind) labels := label.NewSet() s := sumAgg.New() assert.NoError(t, s.Update(context.Background(), metric.Number(1), &desc)) @@ -271,7 +271,7 @@ func TestSumInt64DataPoints(t *testing.T) { } func TestSumFloat64DataPoints(t *testing.T) { - desc := metric.NewDescriptor("", metric.MeasureKind, metric.Float64NumberKind) + desc := metric.NewDescriptor("", metric.ValueRecorderKind, metric.Float64NumberKind) labels := label.NewSet() s := sumAgg.New() assert.NoError(t, s.Update(context.Background(), metric.NewFloat64Number(1), &desc)) @@ -285,7 +285,7 @@ func TestSumFloat64DataPoints(t *testing.T) { } func TestSumErrUnknownValueType(t *testing.T) { - desc := metric.NewDescriptor("", metric.MeasureKind, metric.NumberKind(-1)) + desc := metric.NewDescriptor("", metric.ValueRecorderKind, metric.NumberKind(-1)) labels := label.NewSet() s := sumAgg.New() _, err := sum(&desc, &labels, s) diff --git a/exporters/otlp/otlp_integration_test.go b/exporters/otlp/otlp_integration_test.go index 79121a292..624bd4a07 100644 --- a/exporters/otlp/otlp_integration_test.go +++ b/exporters/otlp/otlp_integration_test.go @@ -109,7 +109,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) span.End() } - selector := simple.NewWithExactMeasure() + selector := simple.NewWithExactDistribution() integrator := integrator.New(selector, true) pusher := push.New(integrator, exp, 60*time.Second) pusher.Start() @@ -124,12 +124,12 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) val int64 } instruments := map[string]data{ - "test-int64-counter": {metric.CounterKind, metricapi.Int64NumberKind, 1}, - "test-float64-counter": {metric.CounterKind, metricapi.Float64NumberKind, 1}, - "test-int64-measure": {metric.MeasureKind, metricapi.Int64NumberKind, 2}, - "test-float64-measure": {metric.MeasureKind, metricapi.Float64NumberKind, 2}, - "test-int64-observer": {metric.ObserverKind, metricapi.Int64NumberKind, 3}, - "test-float64-observer": {metric.ObserverKind, metricapi.Float64NumberKind, 3}, + "test-int64-counter": {metric.CounterKind, metricapi.Int64NumberKind, 1}, + "test-float64-counter": {metric.CounterKind, metricapi.Float64NumberKind, 1}, + "test-int64-valuerecorder": {metric.ValueRecorderKind, metricapi.Int64NumberKind, 2}, + "test-float64-valuerecorder": {metric.ValueRecorderKind, metricapi.Float64NumberKind, 2}, + "test-int64-observer": {metric.ObserverKind, metricapi.Int64NumberKind, 3}, + "test-float64-observer": {metric.ObserverKind, metricapi.Float64NumberKind, 3}, } for name, data := range instruments { switch data.iKind { @@ -142,12 +142,12 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) default: assert.Failf(t, "unsupported number testing kind", data.nKind.String()) } - case metric.MeasureKind: + case metric.ValueRecorderKind: switch data.nKind { case metricapi.Int64NumberKind: - metricapi.Must(meter).NewInt64Measure(name).Record(ctx, data.val, labels...) + metricapi.Must(meter).NewInt64ValueRecorder(name).Record(ctx, data.val, labels...) case metricapi.Float64NumberKind: - metricapi.Must(meter).NewFloat64Measure(name).Record(ctx, float64(data.val), labels...) + metricapi.Must(meter).NewFloat64ValueRecorder(name).Record(ctx, float64(data.val), labels...) default: assert.Failf(t, "unsupported number testing kind", data.nKind.String()) } @@ -246,7 +246,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) default: assert.Failf(t, "invalid number kind", data.nKind.String()) } - case metric.MeasureKind, metric.ObserverKind: + case metric.ValueRecorderKind, metric.ObserverKind: assert.Equal(t, metricpb.MetricDescriptor_SUMMARY.String(), desc.GetType().String()) m.GetSummaryDataPoints() if dp := m.GetSummaryDataPoints(); assert.Len(t, dp, 1) { diff --git a/exporters/otlp/otlp_metric_test.go b/exporters/otlp/otlp_metric_test.go index 0471d0add..4d72d541a 100644 --- a/exporters/otlp/otlp_metric_test.go +++ b/exporters/otlp/otlp_metric_test.go @@ -188,10 +188,10 @@ func TestNoGroupingExport(t *testing.T) { ) } -func TestMeasureMetricGroupingExport(t *testing.T) { +func TestValuerecorderMetricGroupingExport(t *testing.T) { r := record{ - "measure", - metric.MeasureKind, + "valuerecorder", + metric.ValueRecorderKind, metric.Int64NumberKind, nil, nil, @@ -205,7 +205,7 @@ func TestMeasureMetricGroupingExport(t *testing.T) { Metrics: []*metricpb.Metric{ { MetricDescriptor: &metricpb.MetricDescriptor{ - Name: "measure", + Name: "valuerecorder", Type: metricpb.MetricDescriptor_SUMMARY, Labels: []*commonpb.StringKeyValue{ { diff --git a/sdk/export/metric/aggregator/aggregator.go b/sdk/export/metric/aggregator/aggregator.go index df1285108..660e83ef3 100644 --- a/sdk/export/metric/aggregator/aggregator.go +++ b/sdk/export/metric/aggregator/aggregator.go @@ -116,7 +116,7 @@ func NewInconsistentMergeError(a1, a2 export.Aggregator) error { // RangeTest is a commmon routine for testing for valid input values. // This rejects NaN values. This rejects negative values when the // metric instrument does not support negative values, including -// monotonic counter metrics and absolute measure metrics. +// monotonic counter metrics and absolute ValueRecorder metrics. func RangeTest(number metric.Number, descriptor *metric.Descriptor) error { numberKind := descriptor.NumberKind() diff --git a/sdk/export/metric/aggregator/aggregator_test.go b/sdk/export/metric/aggregator/aggregator_test.go index 1907b92b7..0083a71a2 100644 --- a/sdk/export/metric/aggregator/aggregator_test.go +++ b/sdk/export/metric/aggregator/aggregator_test.go @@ -86,7 +86,7 @@ func TestNaNTest(t *testing.T) { t.Run(nkind.String(), func(t *testing.T) { for _, mkind := range []metric.Kind{ metric.CounterKind, - metric.MeasureKind, + metric.ValueRecorderKind, metric.ObserverKind, } { desc := metric.NewDescriptor( diff --git a/sdk/export/metric/metric.go b/sdk/export/metric/metric.go index 5b3a19a8f..c2d583173 100644 --- a/sdk/export/metric/metric.go +++ b/sdk/export/metric/metric.go @@ -100,22 +100,16 @@ type AggregationSelector interface { } // Aggregator implements a specific aggregation behavior, e.g., a -// behavior to track a sequence of updates to a counter, a measure, or -// an observer instrument. For the most part, counter semantics are -// fixed and the provided implementation should be used. Measure and -// observer metrics offer a wide range of potential tradeoffs and -// several implementations are provided. -// -// Aggregators are meant to compute the change (i.e., delta) in state -// from one checkpoint to the next, with the exception of LastValue -// aggregators. LastValue aggregators are required to maintain the last -// value across checkpoints. +// behavior to track a sequence of updates to an instrument. Sum-only +// instruments commonly use a simple Sum aggregator, but for the +// distribution instruments (ValueRecorder, ValueObserver) there are a +// number of possible aggregators with different cost and accuracy +// tradeoffs. // // Note that any Aggregator may be attached to any instrument--this is // the result of the OpenTelemetry API/SDK separation. It is possible -// to attach a counter aggregator to a Measure instrument (to compute -// a simple sum) or a LastValue aggregator to a measure instrument (to -// compute the last value). +// to attach a Sum aggregator to a ValueRecorder instrument or a +// MinMaxSumCount aggregator to a Counter instrument. type Aggregator interface { // Update receives a new measured value and incorporates it // into the aggregation. Update() calls may arrive diff --git a/sdk/metric/aggregator/array/array.go b/sdk/metric/aggregator/array/array.go index 38168bcc2..2d92a54fe 100644 --- a/sdk/metric/aggregator/array/array.go +++ b/sdk/metric/aggregator/array/array.go @@ -27,6 +27,8 @@ import ( ) type ( + // Aggregator aggregates events that form a distribution, keeping + // an array with the exact set of values. Aggregator struct { // ckptSum needs to be aligned for 64-bit atomic operations. ckptSum metric.Number diff --git a/sdk/metric/aggregator/array/array_test.go b/sdk/metric/aggregator/array/array_test.go index 87ea6d98f..2c3efb706 100644 --- a/sdk/metric/aggregator/array/array_test.go +++ b/sdk/metric/aggregator/array/array_test.go @@ -50,7 +50,7 @@ type updateTest struct { } func (ut *updateTest) run(t *testing.T, profile test.Profile) { - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg := New() @@ -118,7 +118,7 @@ type mergeTest struct { func (mt *mergeTest) run(t *testing.T, profile test.Profile) { ctx := context.Background() - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg1 := New() agg2 := New() @@ -215,7 +215,7 @@ func TestArrayErrors(t *testing.T) { ctx := context.Background() - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) test.CheckedUpdate(t, agg, metric.Number(0), descriptor) @@ -243,7 +243,7 @@ func TestArrayErrors(t *testing.T) { } func TestArrayFloat64(t *testing.T) { - descriptor := test.NewAggregatorTest(metric.MeasureKind, metric.Float64NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, metric.Float64NumberKind) fpsf := func(sign int) []float64 { // Check behavior of a bunch of odd floating diff --git a/sdk/metric/aggregator/ddsketch/ddsketch.go b/sdk/metric/aggregator/ddsketch/ddsketch.go index a6b95da15..197d95e4d 100644 --- a/sdk/metric/aggregator/ddsketch/ddsketch.go +++ b/sdk/metric/aggregator/ddsketch/ddsketch.go @@ -29,7 +29,7 @@ import ( // Config is an alias for the underlying DDSketch config object. type Config = sdk.Config -// Aggregator aggregates measure events. +// Aggregator aggregates events into a distribution. type Aggregator struct { lock sync.Mutex cfg *Config diff --git a/sdk/metric/aggregator/ddsketch/ddsketch_test.go b/sdk/metric/aggregator/ddsketch/ddsketch_test.go index 22a39b568..cc68359de 100644 --- a/sdk/metric/aggregator/ddsketch/ddsketch_test.go +++ b/sdk/metric/aggregator/ddsketch/ddsketch_test.go @@ -33,7 +33,7 @@ type updateTest struct { func (ut *updateTest) run(t *testing.T, profile test.Profile) { ctx := context.Background() - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg := New(NewDefaultConfig(), descriptor) all := test.NewNumbers(profile.NumberKind) @@ -92,7 +92,7 @@ type mergeTest struct { func (mt *mergeTest) run(t *testing.T, profile test.Profile) { ctx := context.Background() - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg1 := New(NewDefaultConfig(), descriptor) agg2 := New(NewDefaultConfig(), descriptor) diff --git a/sdk/metric/aggregator/histogram/histogram.go b/sdk/metric/aggregator/histogram/histogram.go index f861d9651..6566dab91 100644 --- a/sdk/metric/aggregator/histogram/histogram.go +++ b/sdk/metric/aggregator/histogram/histogram.go @@ -51,7 +51,7 @@ var _ aggregator.Sum = &Aggregator{} var _ aggregator.Count = &Aggregator{} var _ aggregator.Histogram = &Aggregator{} -// New returns a new measure aggregator for computing Histograms. +// New returns a new aggregator for computing Histograms. // // A Histogram observe events and counts them in pre-defined buckets. // And also provides the total sum and count of all observations. diff --git a/sdk/metric/aggregator/histogram/histogram_test.go b/sdk/metric/aggregator/histogram/histogram_test.go index 53aa1250b..6a559cec3 100644 --- a/sdk/metric/aggregator/histogram/histogram_test.go +++ b/sdk/metric/aggregator/histogram/histogram_test.go @@ -84,7 +84,7 @@ func TestHistogramPositiveAndNegative(t *testing.T) { // Validates count, sum and buckets for a given profile and policy func histogram(t *testing.T, profile test.Profile, policy policy) { ctx := context.Background() - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg := New(descriptor, boundaries[profile.NumberKind]) @@ -126,7 +126,7 @@ func TestHistogramMerge(t *testing.T) { ctx := context.Background() test.RunProfiles(t, func(t *testing.T, profile test.Profile) { - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg1 := New(descriptor, boundaries[profile.NumberKind]) agg2 := New(descriptor, boundaries[profile.NumberKind]) @@ -178,7 +178,7 @@ func TestHistogramNotSet(t *testing.T) { ctx := context.Background() test.RunProfiles(t, func(t *testing.T, profile test.Profile) { - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg := New(descriptor, boundaries[profile.NumberKind]) agg.Checkpoint(ctx, descriptor) diff --git a/sdk/metric/aggregator/minmaxsumcount/mmsc.go b/sdk/metric/aggregator/minmaxsumcount/mmsc.go index 1c840a158..a66bec496 100644 --- a/sdk/metric/aggregator/minmaxsumcount/mmsc.go +++ b/sdk/metric/aggregator/minmaxsumcount/mmsc.go @@ -24,8 +24,8 @@ import ( ) type ( - // Aggregator aggregates measure events, keeping only the min, max, - // sum, and count. + // Aggregator aggregates events that form a distribution, + // keeping only the min, max, sum, and count. Aggregator struct { lock sync.Mutex current state @@ -44,8 +44,9 @@ type ( var _ export.Aggregator = &Aggregator{} var _ aggregator.MinMaxSumCount = &Aggregator{} -// New returns a new measure aggregator for computing min, max, sum, and -// count. It does not compute quantile information other than Min and Max. +// New returns a new aggregator for computing the min, max, sum, and +// count. It does not compute quantile information other than Min and +// Max. // // This type uses a mutex for Update() and Checkpoint() concurrency. func New(desc *metric.Descriptor) *Aggregator { diff --git a/sdk/metric/aggregator/minmaxsumcount/mmsc_test.go b/sdk/metric/aggregator/minmaxsumcount/mmsc_test.go index 50c2fa5fc..d01916b8e 100644 --- a/sdk/metric/aggregator/minmaxsumcount/mmsc_test.go +++ b/sdk/metric/aggregator/minmaxsumcount/mmsc_test.go @@ -79,7 +79,7 @@ func TestMinMaxSumCountPositiveAndNegative(t *testing.T) { // Validates min, max, sum and count for a given profile and policy func minMaxSumCount(t *testing.T, profile test.Profile, policy policy) { ctx := context.Background() - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg := New(descriptor) @@ -127,7 +127,7 @@ func TestMinMaxSumCountMerge(t *testing.T) { ctx := context.Background() test.RunProfiles(t, func(t *testing.T, profile test.Profile) { - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg1 := New(descriptor) agg2 := New(descriptor) @@ -185,7 +185,7 @@ func TestMaxSumCountNotSet(t *testing.T) { ctx := context.Background() test.RunProfiles(t, func(t *testing.T, profile test.Profile) { - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) agg := New(descriptor) agg.Checkpoint(ctx, descriptor) diff --git a/sdk/metric/aggregator/sum/sum_test.go b/sdk/metric/aggregator/sum/sum_test.go index acd37e9ab..617254d2c 100644 --- a/sdk/metric/aggregator/sum/sum_test.go +++ b/sdk/metric/aggregator/sum/sum_test.go @@ -71,13 +71,13 @@ func TestCounterSum(t *testing.T) { }) } -func TestMeasureSum(t *testing.T) { +func TestValueRecorderSum(t *testing.T) { ctx := context.Background() test.RunProfiles(t, func(t *testing.T, profile test.Profile) { agg := New() - descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) sum := metric.Number(0) diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index 9cbc1a578..06c8a980b 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -311,7 +311,7 @@ func BenchmarkInt64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewInt64Measure("int64.lastvalue") + mea := fix.meter.NewInt64ValueRecorder("int64.lastvalue") b.ResetTimer() @@ -324,7 +324,7 @@ func BenchmarkInt64LastValueHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewInt64Measure("int64.lastvalue") + mea := fix.meter.NewInt64ValueRecorder("int64.lastvalue") handle := mea.Bind(labs...) b.ResetTimer() @@ -338,7 +338,7 @@ func BenchmarkFloat64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewFloat64Measure("float64.lastvalue") + mea := fix.meter.NewFloat64ValueRecorder("float64.lastvalue") b.ResetTimer() @@ -351,7 +351,7 @@ func BenchmarkFloat64LastValueHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewFloat64Measure("float64.lastvalue") + mea := fix.meter.NewFloat64ValueRecorder("float64.lastvalue") handle := mea.Bind(labs...) b.ResetTimer() @@ -361,13 +361,13 @@ func BenchmarkFloat64LastValueHandleAdd(b *testing.B) { } } -// Measures +// ValueRecorders -func benchmarkInt64MeasureAdd(b *testing.B, name string) { +func benchmarkInt64ValueRecorderAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewInt64Measure(name) + mea := fix.meter.NewInt64ValueRecorder(name) b.ResetTimer() @@ -376,11 +376,11 @@ func benchmarkInt64MeasureAdd(b *testing.B, name string) { } } -func benchmarkInt64MeasureHandleAdd(b *testing.B, name string) { +func benchmarkInt64ValueRecorderHandleAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewInt64Measure(name) + mea := fix.meter.NewInt64ValueRecorder(name) handle := mea.Bind(labs...) b.ResetTimer() @@ -390,11 +390,11 @@ func benchmarkInt64MeasureHandleAdd(b *testing.B, name string) { } } -func benchmarkFloat64MeasureAdd(b *testing.B, name string) { +func benchmarkFloat64ValueRecorderAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewFloat64Measure(name) + mea := fix.meter.NewFloat64ValueRecorder(name) b.ResetTimer() @@ -403,11 +403,11 @@ func benchmarkFloat64MeasureAdd(b *testing.B, name string) { } } -func benchmarkFloat64MeasureHandleAdd(b *testing.B, name string) { +func benchmarkFloat64ValueRecorderHandleAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewFloat64Measure(name) + mea := fix.meter.NewFloat64ValueRecorder(name) handle := mea.Bind(labs...) b.ResetTimer() @@ -467,55 +467,55 @@ func BenchmarkObserverObservationFloat64(b *testing.B) { // MaxSumCount func BenchmarkInt64MaxSumCountAdd(b *testing.B) { - benchmarkInt64MeasureAdd(b, "int64.minmaxsumcount") + benchmarkInt64ValueRecorderAdd(b, "int64.minmaxsumcount") } func BenchmarkInt64MaxSumCountHandleAdd(b *testing.B) { - benchmarkInt64MeasureHandleAdd(b, "int64.minmaxsumcount") + benchmarkInt64ValueRecorderHandleAdd(b, "int64.minmaxsumcount") } func BenchmarkFloat64MaxSumCountAdd(b *testing.B) { - benchmarkFloat64MeasureAdd(b, "float64.minmaxsumcount") + benchmarkFloat64ValueRecorderAdd(b, "float64.minmaxsumcount") } func BenchmarkFloat64MaxSumCountHandleAdd(b *testing.B) { - benchmarkFloat64MeasureHandleAdd(b, "float64.minmaxsumcount") + benchmarkFloat64ValueRecorderHandleAdd(b, "float64.minmaxsumcount") } // DDSketch func BenchmarkInt64DDSketchAdd(b *testing.B) { - benchmarkInt64MeasureAdd(b, "int64.ddsketch") + benchmarkInt64ValueRecorderAdd(b, "int64.ddsketch") } func BenchmarkInt64DDSketchHandleAdd(b *testing.B) { - benchmarkInt64MeasureHandleAdd(b, "int64.ddsketch") + benchmarkInt64ValueRecorderHandleAdd(b, "int64.ddsketch") } func BenchmarkFloat64DDSketchAdd(b *testing.B) { - benchmarkFloat64MeasureAdd(b, "float64.ddsketch") + benchmarkFloat64ValueRecorderAdd(b, "float64.ddsketch") } func BenchmarkFloat64DDSketchHandleAdd(b *testing.B) { - benchmarkFloat64MeasureHandleAdd(b, "float64.ddsketch") + benchmarkFloat64ValueRecorderHandleAdd(b, "float64.ddsketch") } // Array func BenchmarkInt64ArrayAdd(b *testing.B) { - benchmarkInt64MeasureAdd(b, "int64.array") + benchmarkInt64ValueRecorderAdd(b, "int64.array") } func BenchmarkInt64ArrayHandleAdd(b *testing.B) { - benchmarkInt64MeasureHandleAdd(b, "int64.array") + benchmarkInt64ValueRecorderHandleAdd(b, "int64.array") } func BenchmarkFloat64ArrayAdd(b *testing.B) { - benchmarkFloat64MeasureAdd(b, "float64.array") + benchmarkFloat64ValueRecorderAdd(b, "float64.array") } func BenchmarkFloat64ArrayHandleAdd(b *testing.B) { - benchmarkFloat64MeasureHandleAdd(b, "float64.array") + benchmarkFloat64ValueRecorderHandleAdd(b, "float64.array") } // BatchRecord diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index 02174b877..e26aa630a 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -107,7 +107,7 @@ func TestInputRangeTestCounter(t *testing.T) { require.Nil(t, sdkErr) } -func TestInputRangeTestMeasure(t *testing.T) { +func TestInputRangeTestValueRecorder(t *testing.T) { ctx := context.Background() integrator := &correctnessIntegrator{ t: t, @@ -120,17 +120,17 @@ func TestInputRangeTestMeasure(t *testing.T) { sdkErr = handleErr }) - measure := Must(meter).NewFloat64Measure("name.measure") + valuerecorder := Must(meter).NewFloat64ValueRecorder("name.valuerecorder") - measure.Record(ctx, math.NaN()) + valuerecorder.Record(ctx, math.NaN()) require.Equal(t, aggregator.ErrNaNInput, sdkErr) sdkErr = nil checkpointed := sdk.Collect(ctx) require.Equal(t, 0, checkpointed) - measure.Record(ctx, 1) - measure.Record(ctx, 2) + valuerecorder.Record(ctx, 1) + valuerecorder.Record(ctx, 2) integrator.records = nil checkpointed = sdk.Collect(ctx) @@ -150,9 +150,9 @@ func TestDisabledInstrument(t *testing.T) { sdk := metricsdk.NewAccumulator(integrator) meter := metric.WrapMeterImpl(sdk, "test") - measure := Must(meter).NewFloat64Measure("name.disabled") + valuerecorder := Must(meter).NewFloat64ValueRecorder("name.disabled") - measure.Record(ctx, -1) + valuerecorder.Record(ctx, -1) checkpointed := sdk.Collect(ctx) require.Equal(t, 0, checkpointed) @@ -389,8 +389,8 @@ func TestRecordBatch(t *testing.T) { counter1 := Must(meter).NewInt64Counter("int64.counter") counter2 := Must(meter).NewFloat64Counter("float64.counter") - measure1 := Must(meter).NewInt64Measure("int64.measure") - measure2 := Must(meter).NewFloat64Measure("float64.measure") + valuerecorder1 := Must(meter).NewInt64ValueRecorder("int64.valuerecorder") + valuerecorder2 := Must(meter).NewFloat64ValueRecorder("float64.valuerecorder") sdk.RecordBatch( ctx, @@ -400,8 +400,8 @@ func TestRecordBatch(t *testing.T) { }, counter1.Measurement(1), counter2.Measurement(2), - measure1.Measurement(3), - measure2.Measurement(4), + valuerecorder1.Measurement(3), + valuerecorder2.Measurement(4), ) sdk.Collect(ctx) @@ -411,10 +411,10 @@ func TestRecordBatch(t *testing.T) { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ - "int64.counter/A=B,C=D": 1, - "float64.counter/A=B,C=D": 2, - "int64.measure/A=B,C=D": 3, - "float64.measure/A=B,C=D": 4, + "int64.counter/A=B,C=D": 1, + "float64.counter/A=B,C=D": 2, + "int64.valuerecorder/A=B,C=D": 3, + "float64.valuerecorder/A=B,C=D": 4, }, out.Map) } diff --git a/sdk/metric/doc.go b/sdk/metric/doc.go index cee67d00b..1ad5edc7d 100644 --- a/sdk/metric/doc.go +++ b/sdk/metric/doc.go @@ -13,57 +13,34 @@ // limitations under the License. /* -Package metric implements the OpenTelemetry metric.Meter API. The SDK -supports configurable metrics export behavior through a collection of -export interfaces that support various export strategies, described below. +Package metric implements the OpenTelemetry metric.MeterImpl +interface. The Accumulator type supports configurable metrics export +behavior through a collection of export interfaces that support +various export strategies, described below. -The metric.Meter API consists of methods for constructing each of the basic -kinds of metric instrument. There are six types of instrument available to -the end user, comprised of three basic kinds of metric instrument (Counter, -Measure, Observer) crossed with two kinds of number (int64, float64). +The metric.MeterImpl API consists of methods for constructing +synchronous and asynchronous instruments. There are two constructors +per instrument for the two kinds of number (int64, float64). -The API assists the SDK by consolidating the variety of metric instruments -into a narrower interface, allowing the SDK to avoid repetition of -boilerplate. The API and SDK are separated such that an event reaching -the SDK has a uniform structure: an instrument, a label set, and a -numerical value. +Synchronous instruments are managed by a sync.Map containing a *record +with the current state for each synchronous instrument. A bound +instrument encapsulates a direct pointer to the record, allowing +bound metric events to bypass a sync.Map lookup. A lock-free +algorithm is used to protect against races when adding and removing +items from the sync.Map. -To this end, the API uses a kv.Number type to represent either an int64 -or a float64, depending on the instrument's definition. A single -implementation interface is used for counter and measure instruments, -metric.InstrumentImpl, and a single implementation interface is used for -their handles, metric.HandleImpl. For observers, the API defines -interfaces, for which the SDK provides an implementation. - -There are four entry points for events in the Metrics API - three for -synchronous instruments (counters and measures) and one for asynchronous -instruments (observers). The entry points for synchronous instruments are: -via instrument handles, via direct instrument calls, and via BatchRecord. -The SDK is designed with handles as the primary entry point, the other two -entry points are implemented in terms of short-lived handles. For example, -the implementation of a direct call allocates a handle, operates on the -handle, and releases the handle. Similarly, the implementation of -RecordBatch uses a short-lived handle for each measurement in the batch. -The entry point for asynchronous instruments is via observer callbacks. -Observer callbacks behave like a set of instrument handles - one for each -observation for a distinct label set. The observer handles are alive as -long as they are used. If the callback stops reporting values for a -certain label set, the associated handle is dropped. +Asynchronous instruments are managed by an internal +AsyncInstrumentState, which coordinates calling batch and single +instrument callbacks. Internal Structure -The SDK is designed with minimal use of locking, to avoid adding -contention for user-level code. For each handle, whether it is held by -user-level code or a short-lived device, there exists an internal record -managed by the SDK. Each internal record corresponds to a specific -instrument and label set combination. - Each observer also has its own kind of record stored in the SDK. This record contains a set of recorders for every specific label set used in the callback. A sync.Map maintains the mapping of current instruments and label sets to -internal records. To create a new handle, the SDK consults the Map to +internal records. To create a new bound instrument, the SDK consults the Map to locate an existing record, otherwise it constructs a new record. The SDK maintains a count of the number of references to each record, ensuring that records are not reclaimed from the Map while they are still active @@ -74,12 +51,7 @@ sweeps through all records in the SDK, checkpointing their state. When a record is discovered that has no references and has not been updated since the prior collection pass, it is removed from the Map. -The SDK maintains a current epoch number, corresponding to the number of -completed collections. Each recorder of an observer record contains the -last epoch during which it was updated. This variable allows the collection -code path to detect stale recorders and remove them. - -Each record of a handle and recorder of an observer has an associated +Both synchronous and asynchronous instruments have an associated aggregator, which maintains the current state resulting from all metric events since its last checkpoint. Aggregators may be lock-free or they may use locking, but they should expect to be called concurrently. Aggregators @@ -97,21 +69,18 @@ enters the SDK resulting in a new record, and collection context, where a system-level thread performs a collection pass through the SDK. -Descriptor is a struct that describes the metric instrument to the export -pipeline, containing the name, recommended aggregation keys, units, -description, metric kind (counter or measure), number kind (int64 or -float64), and whether the instrument has alternate semantics or not (i.e., -monotonic=false counter, absolute=false measure). A Descriptor accompanies -metric data as it passes through the export pipeline. +Descriptor is a struct that describes the metric instrument to the +export pipeline, containing the name, units, description, metric kind, +number kind (int64 or float64). A Descriptor accompanies metric data +as it passes through the export pipeline. The AggregationSelector interface supports choosing the method of aggregation to apply to a particular instrument. Given the Descriptor, this AggregatorFor method returns an implementation of Aggregator. If this interface returns nil, the metric will be disabled. The aggregator should be matched to the capabilities of the exporter. Selecting the aggregator -for counter instruments is relatively straightforward, but for measure and -observer instruments there are numerous choices with different cost and -quality tradeoffs. +for sum-only instruments is relatively straightforward, but many options +are available for aggregating distributions from ValueRecorder instruments. Aggregator is an interface which implements a concrete strategy for aggregating metric updates. Several Aggregator implementations are diff --git a/sdk/metric/histogram_stress_test.go b/sdk/metric/histogram_stress_test.go index 4a40823ef..d05536622 100644 --- a/sdk/metric/histogram_stress_test.go +++ b/sdk/metric/histogram_stress_test.go @@ -25,7 +25,7 @@ import ( ) func TestStressInt64Histogram(t *testing.T) { - desc := metric.NewDescriptor("some_metric", metric.MeasureKind, metric.Int64NumberKind) + desc := metric.NewDescriptor("some_metric", metric.ValueRecorderKind, metric.Int64NumberKind) h := histogram.New(&desc, []metric.Number{metric.NewInt64Number(25), metric.NewInt64Number(50), metric.NewInt64Number(75)}) ctx, cancelFunc := context.WithCancel(context.Background()) diff --git a/sdk/metric/minmaxsumcount_stress_test.go b/sdk/metric/minmaxsumcount_stress_test.go index ecec40564..0b51f66a6 100644 --- a/sdk/metric/minmaxsumcount_stress_test.go +++ b/sdk/metric/minmaxsumcount_stress_test.go @@ -25,7 +25,7 @@ import ( ) func TestStressInt64MinMaxSumCount(t *testing.T) { - desc := metric.NewDescriptor("some_metric", metric.MeasureKind, metric.Int64NumberKind) + desc := metric.NewDescriptor("some_metric", metric.ValueRecorderKind, metric.Int64NumberKind) mmsc := minmaxsumcount.New(&desc) ctx, cancel := context.WithCancel(context.Background()) diff --git a/sdk/metric/selector/simple/simple.go b/sdk/metric/selector/simple/simple.go index e8929f093..3f7517585 100644 --- a/sdk/metric/selector/simple/simple.go +++ b/sdk/metric/selector/simple/simple.go @@ -42,40 +42,40 @@ var ( _ export.AggregationSelector = selectorHistogram{} ) -// NewWithInexpensiveMeasure returns a simple aggregation selector +// NewWithInexpensiveDistribution returns a simple aggregation selector // that uses counter, minmaxsumcount and minmaxsumcount aggregators // for the three kinds of metric. This selector is faster and uses // less memory than the others because minmaxsumcount does not // aggregate quantile information. -func NewWithInexpensiveMeasure() export.AggregationSelector { +func NewWithInexpensiveDistribution() export.AggregationSelector { return selectorInexpensive{} } -// NewWithSketchMeasure returns a simple aggregation selector that +// NewWithSketchDistribution returns a simple aggregation selector that // uses counter, ddsketch, and ddsketch aggregators for the three // kinds of metric. This selector uses more cpu and memory than the -// NewWithInexpensiveMeasure because it uses one DDSketch per distinct -// measure/observer and labelset. -func NewWithSketchMeasure(config *ddsketch.Config) export.AggregationSelector { +// NewWithInexpensiveDistribution because it uses one DDSketch per distinct +// instrument and label set. +func NewWithSketchDistribution(config *ddsketch.Config) export.AggregationSelector { return selectorSketch{ config: config, } } -// NewWithExactMeasure returns a simple aggregation selector that uses +// NewWithExactDistribution returns a simple aggregation selector that uses // counter, array, and array aggregators for the three kinds of metric. -// This selector uses more memory than the NewWithSketchMeasure +// This selector uses more memory than the NewWithSketchDistribution // because it aggregates an array of all values, therefore is able to // compute exact quantiles. -func NewWithExactMeasure() export.AggregationSelector { +func NewWithExactDistribution() export.AggregationSelector { return selectorExact{} } -// NewWithHistogramMeasure returns a simple aggregation selector that uses counter, +// NewWithHistogramDistribution returns a simple aggregation selector that uses counter, // histogram, and histogram aggregators for the three kinds of metric. This -// selector uses more memory than the NewWithInexpensiveMeasure because it +// selector uses more memory than the NewWithInexpensiveDistribution because it // uses a counter per bucket. -func NewWithHistogramMeasure(boundaries []metric.Number) export.AggregationSelector { +func NewWithHistogramDistribution(boundaries []metric.Number) export.AggregationSelector { return selectorHistogram{boundaries: boundaries} } @@ -83,7 +83,7 @@ func (selectorInexpensive) AggregatorFor(descriptor *metric.Descriptor) export.A switch descriptor.MetricKind() { case metric.ObserverKind: fallthrough - case metric.MeasureKind: + case metric.ValueRecorderKind: return minmaxsumcount.New(descriptor) default: return sum.New() @@ -94,7 +94,7 @@ func (s selectorSketch) AggregatorFor(descriptor *metric.Descriptor) export.Aggr switch descriptor.MetricKind() { case metric.ObserverKind: fallthrough - case metric.MeasureKind: + case metric.ValueRecorderKind: return ddsketch.New(s.config, descriptor) default: return sum.New() @@ -105,7 +105,7 @@ func (selectorExact) AggregatorFor(descriptor *metric.Descriptor) export.Aggrega switch descriptor.MetricKind() { case metric.ObserverKind: fallthrough - case metric.MeasureKind: + case metric.ValueRecorderKind: return array.New() default: return sum.New() @@ -116,7 +116,7 @@ func (s selectorHistogram) AggregatorFor(descriptor *metric.Descriptor) export.A switch descriptor.MetricKind() { case metric.ObserverKind: fallthrough - case metric.MeasureKind: + case metric.ValueRecorderKind: return histogram.New(descriptor, s.boundaries) default: return sum.New() diff --git a/sdk/metric/selector/simple/simple_test.go b/sdk/metric/selector/simple/simple_test.go index 0f79df535..a80c62ac2 100644 --- a/sdk/metric/selector/simple/simple_test.go +++ b/sdk/metric/selector/simple/simple_test.go @@ -29,35 +29,35 @@ import ( ) var ( - testCounterDesc = metric.NewDescriptor("counter", metric.CounterKind, metric.Int64NumberKind) - testMeasureDesc = metric.NewDescriptor("measure", metric.MeasureKind, metric.Int64NumberKind) - testObserverDesc = metric.NewDescriptor("observer", metric.ObserverKind, metric.Int64NumberKind) + testCounterDesc = metric.NewDescriptor("counter", metric.CounterKind, metric.Int64NumberKind) + testValueRecorderDesc = metric.NewDescriptor("valuerecorder", metric.ValueRecorderKind, metric.Int64NumberKind) + testObserverDesc = metric.NewDescriptor("observer", metric.ObserverKind, metric.Int64NumberKind) ) -func TestInexpensiveMeasure(t *testing.T) { - inex := simple.NewWithInexpensiveMeasure() +func TestInexpensiveDistribution(t *testing.T) { + inex := simple.NewWithInexpensiveDistribution() require.NotPanics(t, func() { _ = inex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) }) - require.NotPanics(t, func() { _ = inex.AggregatorFor(&testMeasureDesc).(*minmaxsumcount.Aggregator) }) + require.NotPanics(t, func() { _ = inex.AggregatorFor(&testValueRecorderDesc).(*minmaxsumcount.Aggregator) }) require.NotPanics(t, func() { _ = inex.AggregatorFor(&testObserverDesc).(*minmaxsumcount.Aggregator) }) } -func TestSketchMeasure(t *testing.T) { - sk := simple.NewWithSketchMeasure(ddsketch.NewDefaultConfig()) +func TestSketchDistribution(t *testing.T) { + sk := simple.NewWithSketchDistribution(ddsketch.NewDefaultConfig()) require.NotPanics(t, func() { _ = sk.AggregatorFor(&testCounterDesc).(*sum.Aggregator) }) - require.NotPanics(t, func() { _ = sk.AggregatorFor(&testMeasureDesc).(*ddsketch.Aggregator) }) + require.NotPanics(t, func() { _ = sk.AggregatorFor(&testValueRecorderDesc).(*ddsketch.Aggregator) }) require.NotPanics(t, func() { _ = sk.AggregatorFor(&testObserverDesc).(*ddsketch.Aggregator) }) } -func TestExactMeasure(t *testing.T) { - ex := simple.NewWithExactMeasure() +func TestExactDistribution(t *testing.T) { + ex := simple.NewWithExactDistribution() require.NotPanics(t, func() { _ = ex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) }) - require.NotPanics(t, func() { _ = ex.AggregatorFor(&testMeasureDesc).(*array.Aggregator) }) + require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueRecorderDesc).(*array.Aggregator) }) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testObserverDesc).(*array.Aggregator) }) } -func TestHistogramMeasure(t *testing.T) { - ex := simple.NewWithHistogramMeasure([]metric.Number{}) +func TestHistogramDistribution(t *testing.T) { + ex := simple.NewWithHistogramDistribution([]metric.Number{}) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) }) - require.NotPanics(t, func() { _ = ex.AggregatorFor(&testMeasureDesc).(*histogram.Aggregator) }) + require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueRecorderDesc).(*histogram.Aggregator) }) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testObserverDesc).(*histogram.Aggregator) }) } diff --git a/sdk/metric/stress_test.go b/sdk/metric/stress_test.go index 7e15e1594..7bdd72432 100644 --- a/sdk/metric/stress_test.go +++ b/sdk/metric/stress_test.go @@ -285,7 +285,7 @@ func (f *testFixture) Process(_ context.Context, record export.Record) error { f.T.Fatal("Sum error: ", err) } f.impl.storeCollect(actual, sum, time.Time{}) - case metric.MeasureKind: + case metric.ValueRecorderKind: lv, ts, err := agg.(aggregator.LastValue).LastValue() if err != nil && err != aggregator.ErrNoData { f.T.Fatal("Last value error: ", err) @@ -431,15 +431,15 @@ func TestStressFloat64Counter(t *testing.T) { func intLastValueTestImpl() testImpl { return testImpl{ newInstrument: func(meter api.Meter, name string) SyncImpler { - return Must(meter).NewInt64Measure(name + ".lastvalue") + return Must(meter).NewInt64ValueRecorder(name + ".lastvalue") }, getUpdateValue: func() api.Number { r1 := rand.Int63() return api.NewInt64Number(rand.Int63() - r1) }, operate: func(inst interface{}, ctx context.Context, value api.Number, labels []kv.KeyValue) { - measure := inst.(api.Int64Measure) - measure.Record(ctx, value.AsInt64(), labels...) + valuerecorder := inst.(api.Int64ValueRecorder) + valuerecorder.Record(ctx, value.AsInt64(), labels...) }, newStore: func() interface{} { return &lastValueState{ @@ -473,14 +473,14 @@ func TestStressInt64LastValue(t *testing.T) { func floatLastValueTestImpl() testImpl { return testImpl{ newInstrument: func(meter api.Meter, name string) SyncImpler { - return Must(meter).NewFloat64Measure(name + ".lastvalue") + return Must(meter).NewFloat64ValueRecorder(name + ".lastvalue") }, getUpdateValue: func() api.Number { return api.NewFloat64Number((-0.5 + rand.Float64()) * 100000) }, operate: func(inst interface{}, ctx context.Context, value api.Number, labels []kv.KeyValue) { - measure := inst.(api.Float64Measure) - measure.Record(ctx, value.AsFloat64(), labels...) + valuerecorder := inst.(api.Float64ValueRecorder) + valuerecorder.Record(ctx, value.AsFloat64(), labels...) }, newStore: func() interface{} { return &lastValueState{ From 4408b6e328ebae1b16b848d7fa12d209c7ef268d Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Sat, 16 May 2020 10:19:46 +0300 Subject: [PATCH 053/108] Remove buggy enqueueWait --- sdk/trace/batch_span_processor.go | 44 +++++++++++-------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index b35885eed..45d3ef85e 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -17,7 +17,6 @@ package trace import ( "context" "errors" - "log" "sync" "sync/atomic" "time" @@ -71,10 +70,9 @@ type BatchSpanProcessor struct { queue chan *export.SpanData dropped uint32 - enqueueWait sync.WaitGroup - stopWait sync.WaitGroup - stopOnce sync.Once - stopCh chan struct{} + stopWait sync.WaitGroup + stopOnce sync.Once + stopCh chan struct{} } var _ SpanProcessor = (*BatchSpanProcessor)(nil) @@ -192,24 +190,11 @@ loop: } } - go func() { - bsp.enqueueWait.Wait() - close(bsp.queue) - }() - for { - if !timer.Stop() { - <-timer.C - } - - // This is not needed normally, but use some timeout so we are not stuck - // waiting for enqueueWait forever. - const waitTimeout = 30 * time.Second - timer.Reset(waitTimeout) - select { case sd := <-bsp.queue: if sd == nil { // queue is closed + go throwAwayFutureSends(bsp.queue) exportSpans() return } @@ -218,10 +203,18 @@ loop: if len(batch) == bsp.o.MaxExportBatchSize { exportSpans() } - case <-timer.C: - //TODO: use error callback - see issue #174 - log.Println("bsp.enqueueWait timeout") - exportSpans() + default: + // Send nil instead of closing to prevent "send on closed channel". + bsp.queue <- nil + } + } +} + +func throwAwayFutureSends(ch <-chan *export.SpanData) { + for { + select { + case <-ch: + case <-time.After(time.Minute): return } } @@ -232,11 +225,8 @@ func (bsp *BatchSpanProcessor) enqueue(sd *export.SpanData) { return } - bsp.enqueueWait.Add(1) - select { case <-bsp.stopCh: - bsp.enqueueWait.Done() return default: } @@ -250,6 +240,4 @@ func (bsp *BatchSpanProcessor) enqueue(sd *export.SpanData) { atomic.AddUint32(&bsp.dropped, 1) } } - - bsp.enqueueWait.Done() } From 2a000381750093cbe07105614dcbf6b609ad31c2 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Sat, 16 May 2020 12:15:53 +0200 Subject: [PATCH 054/108] Update example/otel-collector/k8s/namespace.yaml Co-authored-by: Tyler Yahn --- example/otel-collector/k8s/namespace.yaml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/k8s/namespace.yaml b/example/otel-collector/k8s/namespace.yaml index 4872021cb..c31ad1c9d 100644 --- a/example/otel-collector/k8s/namespace.yaml +++ b/example/otel-collector/k8s/namespace.yaml @@ -1,4 +1,18 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + apiVersion: v1 kind: Namespace metadata: - name: observability \ No newline at end of file + name: observability From c80ed298db5a7c571bf0e7f00e74f2a6afce3deb Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Sat, 16 May 2020 12:16:04 +0200 Subject: [PATCH 055/108] Update example/otel-collector/k8s/otel-collector.yaml Co-authored-by: Tyler Yahn --- example/otel-collector/k8s/otel-collector.yaml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/k8s/otel-collector.yaml b/example/otel-collector/k8s/otel-collector.yaml index da491f09f..88f4f87bc 100644 --- a/example/otel-collector/k8s/otel-collector.yaml +++ b/example/otel-collector/k8s/otel-collector.yaml @@ -1,3 +1,17 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + apiVersion: v1 kind: ConfigMap metadata: @@ -124,4 +138,4 @@ spec: # - key: cert.pem # path: cert.pem # - key: key.pem -# path: key.pem \ No newline at end of file +# path: key.pem From 8bcefd434eb9be78b4e32871bc3869734b9e1225 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Sat, 16 May 2020 12:37:47 +0200 Subject: [PATCH 056/108] remove unnecessary jaeger files & add licence header --- example/otel-collector/Makefile | 32 +- example/otel-collector/README.md | 14 +- example/otel-collector/k8s/jaeger.yaml | 19 + example/otel-collector/k8s/jaeger/jaeger.yaml | 5 - .../k8s/jaeger/jaeger_cluster_role.yaml | 189 - .../jaeger/jaeger_cluster_role_binding.yaml | 12 - .../k8s/jaeger/jaeger_operator.yaml | 39 - .../k8s/jaeger/jaeger_role.yaml | 159 - .../k8s/jaeger/jaeger_role_binding.yaml | 12 - .../k8s/jaeger/jaeger_service_account.yaml | 5 - .../jaeger/jaegertracing.io_jaegers_crd.yaml | 9835 ----------------- .../otel-collector/k8s/otel-collector.yaml | 3 + 12 files changed, 45 insertions(+), 10279 deletions(-) create mode 100644 example/otel-collector/k8s/jaeger.yaml delete mode 100644 example/otel-collector/k8s/jaeger/jaeger.yaml delete mode 100644 example/otel-collector/k8s/jaeger/jaeger_cluster_role.yaml delete mode 100644 example/otel-collector/k8s/jaeger/jaeger_cluster_role_binding.yaml delete mode 100644 example/otel-collector/k8s/jaeger/jaeger_operator.yaml delete mode 100644 example/otel-collector/k8s/jaeger/jaeger_role.yaml delete mode 100644 example/otel-collector/k8s/jaeger/jaeger_role_binding.yaml delete mode 100644 example/otel-collector/k8s/jaeger/jaeger_service_account.yaml delete mode 100644 example/otel-collector/k8s/jaeger/jaegertracing.io_jaegers_crd.yaml diff --git a/example/otel-collector/Makefile b/example/otel-collector/Makefile index 72e659b09..2305afa68 100644 --- a/example/otel-collector/Makefile +++ b/example/otel-collector/Makefile @@ -3,18 +3,18 @@ namespace-k8s: jaeger-operator-k8s: # Create the jaeger operator and necessary artifacts in ns observability - kubectl apply -f k8s/jaeger/jaegertracing.io_jaegers_crd.yaml - kubectl apply -f k8s/jaeger/jaeger_service_account.yaml - kubectl apply -f k8s/jaeger/jaeger_role.yaml - kubectl apply -f k8s/jaeger/jaeger_role_binding.yaml - kubectl apply -f k8s/jaeger/jaeger_operator.yaml + kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml + kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml + kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml + kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml + kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml # Create the cluster role & bindings - kubectl apply -f k8s/jaeger/jaeger_cluster_role.yaml - kubectl apply -f k8s/jaeger/jaeger_cluster_role_binding.yaml + kubectl create -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role.yaml + kubectl create -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role_binding.yaml jaeger-k8s: - kubectl apply -f k8s/jaeger/jaeger.yaml + kubectl apply -f k8s/jaeger.yaml otel-collector-k8s: kubectl apply -f k8s/otel-collector.yaml @@ -22,13 +22,13 @@ otel-collector-k8s: clean-k8s: - kubectl delete -f k8s/otel-collector.yaml - - kubectl delete -f k8s/jaeger/jaeger.yaml + - kubectl delete -f k8s/jaeger.yaml - - kubectl delete -f k8s/jaeger/jaeger_cluster_role.yaml - - kubectl delete -f k8s/jaeger/jaeger_cluster_role_binding.yaml + - kubectl delete -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role.yaml + - kubectl delete -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role_binding.yaml - - kubectl delete -f k8s/jaeger/jaegertracing.io_jaegers_crd.yaml - - kubectl delete -f k8s/jaeger/jaeger_service_account.yaml - - kubectl delete -f k8s/jaeger/jaeger_role.yaml - - kubectl delete -f k8s/jaeger/jaeger_role_binding.yaml - - kubectl delete -f k8s/jaeger/jaeger_operator.yaml \ No newline at end of file + - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml + - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml + - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml + - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml + - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml \ No newline at end of file diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index ec9d1e562..67c4857ce 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -48,15 +48,15 @@ k apply -f k8s/namespace.yaml Jaeger is then deployed via the operator, and the demo follows [these steps](https://github.com/jaegertracing/jaeger-operator#getting-started) to create it: ```bash # Create the jaeger operator and necessary artifacts in ns observability -kubectl apply -f k8s/jaeger/jaegertracing.io_jaegers_crd.yaml -kubectl apply -f k8s/jaeger/jaeger_service_account.yaml -kubectl apply -f k8s/jaeger/jaeger_role.yaml -kubectl apply -f k8s/jaeger/jaeger_role_binding.yaml -kubectl apply -f k8s/jaeger/jaeger_operator.yaml +kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml +kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml +kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml +kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml +kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml # Create the cluster role & bindings -kubectl apply -f k8s/jaeger/jaeger_cluster_role.yaml -kubectl apply -f k8s/jaeger/jaeger_cluster_role_binding.yaml +kubectl create -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role.yaml +kubectl create -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role_binding.yaml # Create the Jaeger instance itself: kubectl apply -f k8s/jaeger/jaeger.yaml diff --git a/example/otel-collector/k8s/jaeger.yaml b/example/otel-collector/k8s/jaeger.yaml new file mode 100644 index 000000000..56558aee2 --- /dev/null +++ b/example/otel-collector/k8s/jaeger.yaml @@ -0,0 +1,19 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: jaegertracing.io/v1 +kind: Jaeger +metadata: + name: jaeger + namespace: observability \ No newline at end of file diff --git a/example/otel-collector/k8s/jaeger/jaeger.yaml b/example/otel-collector/k8s/jaeger/jaeger.yaml deleted file mode 100644 index 98291ac90..000000000 --- a/example/otel-collector/k8s/jaeger/jaeger.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: jaegertracing.io/v1 -kind: Jaeger -metadata: - name: jaeger - namespace: observability \ No newline at end of file diff --git a/example/otel-collector/k8s/jaeger/jaeger_cluster_role.yaml b/example/otel-collector/k8s/jaeger/jaeger_cluster_role.yaml deleted file mode 100644 index 660d48fc6..000000000 --- a/example/otel-collector/k8s/jaeger/jaeger_cluster_role.yaml +++ /dev/null @@ -1,189 +0,0 @@ -## When using the operator in cluster-wide mode, this ClusterRole has to be created and bound to the jaeger-operator service account, -## so that the operator can watch and create resources in every namespace in the cluster. -## An alternative to this cluster role is to create one role binding for each namespace that the operator should watch -## in that case, don't forget to add a comma-separated list of namespaces as WATCH_NAMESPACE in the operator's deployment. -## Further down in this file there's another set of rules, with extra optional permissions -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: jaeger-operator -rules: - -## our own custom resources -- apiGroups: - - jaegertracing.io - resources: - - '*' - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' - -## for the operator's own deployment -- apiGroups: - - apps - resourceNames: - - jaeger-operator - resources: - - deployments/finalizers - verbs: - - update - -## regular things the operator manages for an instance, as the result of processing CRs -- apiGroups: - - "" - resources: - - configmaps - - persistentvolumeclaims - - pods - - secrets - - serviceaccounts - - services - - services/finalizers - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - extensions - resources: - - ingresses - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - batch - resources: - - jobs - - cronjobs - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - route.openshift.io - resources: - - routes - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' - -## needed if you want the operator to create service monitors for the Jaeger instances -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' - -## for the Elasticsearch auto-provisioning -- apiGroups: - - logging.openshift.io - resources: - - elasticsearches - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' - -## for the Kafka auto-provisioning -- apiGroups: - - kafka.strimzi.io - resources: - - kafkas - - kafkausers - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' - -## Extra permissions -## This is an extra set of permissions that the Jaeger Operator might make use of if granted - -## needed if support for injecting sidecars based on namespace annotation is required -- apiGroups: - - "" - resources: - - namespaces - verbs: - - 'get' - - 'list' - - 'watch' - -## needed if support for injecting sidecars based on deployment annotation is required, across all namespaces -- apiGroups: - - apps - resources: - - deployments - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'watch' - -## needed only when .Spec.Ingress.Openshift.DelegateUrls is used -- apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' diff --git a/example/otel-collector/k8s/jaeger/jaeger_cluster_role_binding.yaml b/example/otel-collector/k8s/jaeger/jaeger_cluster_role_binding.yaml deleted file mode 100644 index ab474d086..000000000 --- a/example/otel-collector/k8s/jaeger/jaeger_cluster_role_binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: jaeger-operator -subjects: -- kind: ServiceAccount - name: jaeger-operator - namespace: "observability" # change to point to the namespace you installed your operator -roleRef: - kind: ClusterRole - name: jaeger-operator - apiGroup: rbac.authorization.k8s.io diff --git a/example/otel-collector/k8s/jaeger/jaeger_operator.yaml b/example/otel-collector/k8s/jaeger/jaeger_operator.yaml deleted file mode 100644 index 5dedda43f..000000000 --- a/example/otel-collector/k8s/jaeger/jaeger_operator.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: jaeger-operator - namespace: observability -spec: - replicas: 1 - selector: - matchLabels: - name: jaeger-operator - template: - metadata: - labels: - name: jaeger-operator - spec: - serviceAccountName: jaeger-operator - containers: - - name: jaeger-operator - image: jaegertracing/jaeger-operator:1.17.1 - ports: - - containerPort: 8383 - name: metrics - args: ["start"] - imagePullPolicy: Always - env: - - name: WATCH_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: OPERATOR_NAME - value: "jaeger-operator" diff --git a/example/otel-collector/k8s/jaeger/jaeger_role.yaml b/example/otel-collector/k8s/jaeger/jaeger_role.yaml deleted file mode 100644 index a92604d75..000000000 --- a/example/otel-collector/k8s/jaeger/jaeger_role.yaml +++ /dev/null @@ -1,159 +0,0 @@ -## this is a set of basic permissions the Jaeger Operator needs when restricted to work in specific namespaces -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: jaeger-operator - namespace: observability -rules: - -## our own custom resources -- apiGroups: - - jaegertracing.io - resources: - - '*' - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' - -## for the operator's own deployment -- apiGroups: - - apps - resourceNames: - - jaeger-operator - resources: - - deployments/finalizers - verbs: - - update - -## regular things the operator manages for an instance, as the result of processing CRs -- apiGroups: - - "" - resources: - - configmaps - - persistentvolumeclaims - - pods - - secrets - - serviceaccounts - - services - - services/finalizers - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - extensions - resources: - - ingresses - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - batch - resources: - - jobs - - cronjobs - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - route.openshift.io - resources: - - routes - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - image.openshift.io - resources: - - imagestreams - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' - -## needed if you want the operator to create service monitors for the Jaeger instances -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' - -## for the Elasticsearch auto-provisioning -- apiGroups: - - logging.openshift.io - resources: - - elasticsearches - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' - -## for the Kafka auto-provisioning -- apiGroups: - - kafka.strimzi.io - resources: - - kafkas - - kafkausers - verbs: - - 'get' - - 'list' - - 'create' - - 'update' - - 'delete' - - 'watch' diff --git a/example/otel-collector/k8s/jaeger/jaeger_role_binding.yaml b/example/otel-collector/k8s/jaeger/jaeger_role_binding.yaml deleted file mode 100644 index 2741fbbd0..000000000 --- a/example/otel-collector/k8s/jaeger/jaeger_role_binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: jaeger-operator - namespace: observability -subjects: -- kind: ServiceAccount - name: jaeger-operator -roleRef: - kind: Role - name: jaeger-operator - apiGroup: rbac.authorization.k8s.io diff --git a/example/otel-collector/k8s/jaeger/jaeger_service_account.yaml b/example/otel-collector/k8s/jaeger/jaeger_service_account.yaml deleted file mode 100644 index 03d499e49..000000000 --- a/example/otel-collector/k8s/jaeger/jaeger_service_account.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - namespace: observability - name: jaeger-operator diff --git a/example/otel-collector/k8s/jaeger/jaegertracing.io_jaegers_crd.yaml b/example/otel-collector/k8s/jaeger/jaegertracing.io_jaegers_crd.yaml deleted file mode 100644 index 7db755177..000000000 --- a/example/otel-collector/k8s/jaeger/jaegertracing.io_jaegers_crd.yaml +++ /dev/null @@ -1,9835 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: jaegers.jaegertracing.io -spec: - additionalPrinterColumns: - - JSONPath: .status.phase - description: Jaeger instance's status - name: Status - type: string - - JSONPath: .status.version - description: Jaeger Version - name: Version - type: string - - JSONPath: .spec.strategy - description: Jaeger deployment strategy - name: Strategy - type: string - - JSONPath: .spec.storage.type - description: Jaeger storage type - name: Storage - type: string - - JSONPath: .metadata.creationTimestamp - name: Age - type: date - group: jaegertracing.io - names: - kind: Jaeger - listKind: JaegerList - plural: jaegers - singular: jaeger - scope: Namespaced - subresources: - status: {} - validation: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - agent: - nullable: true - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - config: - type: object - image: - type: string - labels: - additionalProperties: - type: string - type: object - options: - type: object - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - strategy: - type: string - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - allInOne: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - image: - type: string - labels: - additionalProperties: - type: string - type: object - options: - type: object - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - collector: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - autoscale: - type: boolean - config: - type: object - image: - type: string - labels: - additionalProperties: - type: string - type: object - maxReplicas: - format: int32 - type: integer - minReplicas: - format: int32 - type: integer - options: - type: object - replicas: - format: int32 - type: integer - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - ingester: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - autoscale: - type: boolean - config: - type: object - image: - type: string - labels: - additionalProperties: - type: string - type: object - maxReplicas: - format: int32 - type: integer - minReplicas: - format: int32 - type: integer - options: - type: object - replicas: - format: int32 - type: integer - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - ingress: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - enabled: - type: boolean - hosts: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - openshift: - properties: - delegateUrls: - type: string - htpasswdFile: - type: string - sar: - type: string - skipLogout: - type: boolean - type: object - options: - type: object - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - secretName: - type: string - security: - type: string - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - tls: - items: - properties: - hosts: - items: - type: string - type: array - secretName: - type: string - type: object - type: array - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - labels: - additionalProperties: - type: string - type: object - query: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - image: - type: string - labels: - additionalProperties: - type: string - type: object - options: - type: object - replicas: - format: int32 - type: integer - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - sampling: - properties: - options: - type: object - type: object - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - storage: - properties: - cassandraCreateSchema: - properties: - datacenter: - type: string - enabled: - type: boolean - image: - type: string - mode: - type: string - timeout: - type: string - ttlSecondsAfterFinished: - format: int32 - type: integer - type: object - dependencies: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - cassandraClientAuthEnabled: - type: boolean - elasticsearchClientNodeOnly: - type: boolean - elasticsearchNodesWanOnly: - type: boolean - enabled: - type: boolean - image: - type: string - javaOpts: - type: string - labels: - additionalProperties: - type: string - type: object - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - schedule: - type: string - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - sparkMaster: - type: string - successfulJobsHistoryLimit: - format: int32 - type: integer - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - ttlSecondsAfterFinished: - format: int32 - type: integer - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - elasticsearch: - properties: - image: - type: string - nodeCount: - format: int32 - type: integer - nodeSelector: - additionalProperties: - type: string - type: object - redundancyPolicy: - type: string - resources: - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - storage: - properties: - size: - type: string - storageClassName: - type: string - type: object - type: object - esIndexCleaner: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - enabled: - type: boolean - image: - type: string - labels: - additionalProperties: - type: string - type: object - numberOfDays: - type: integer - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - schedule: - type: string - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - successfulJobsHistoryLimit: - format: int32 - type: integer - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - ttlSecondsAfterFinished: - format: int32 - type: integer - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - esRollover: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - nullable: true - type: object - conditions: - type: string - image: - type: string - labels: - additionalProperties: - type: string - type: object - readTTL: - type: string - resources: - nullable: true - properties: - limits: - additionalProperties: - type: string - type: object - requests: - additionalProperties: - type: string - type: object - type: object - schedule: - type: string - securityContext: - properties: - fsGroup: - format: int64 - type: integer - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - successfulJobsHistoryLimit: - format: int32 - type: integer - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - ttlSecondsAfterFinished: - format: int32 - type: integer - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - options: - type: object - secretName: - type: string - type: - type: string - type: object - strategy: - type: string - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - ui: - properties: - options: - type: object - type: object - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - type: string - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - type: string - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - status: - properties: - phase: - type: string - version: - type: string - required: - - phase - - version - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/example/otel-collector/k8s/otel-collector.yaml b/example/otel-collector/k8s/otel-collector.yaml index 88f4f87bc..c1d0ec803 100644 --- a/example/otel-collector/k8s/otel-collector.yaml +++ b/example/otel-collector/k8s/otel-collector.yaml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Configuration based on otel-collector k8s demo: +# https://github.com/open-telemetry/opentelemetry-collector/blob/master/examples/k8s.yaml apiVersion: v1 kind: ConfigMap From 2dee67652aec812903a6db53945f5c8a54d6cc47 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 18 May 2020 09:44:33 -0700 Subject: [PATCH 057/108] Histogram aggregator initial state (fix #735) (#736) * Add a test * Add comments and description options * Another test * Undo buffer re-use * Mod tidy * Precommit * Again * Copyright * Undo rename --- exporters/metric/prometheus/example_test.go | 97 +++++++++++++++++++ sdk/metric/aggregator/histogram/histogram.go | 43 ++++---- .../aggregator/histogram/histogram_test.go | 29 ++++-- 3 files changed, 139 insertions(+), 30 deletions(-) create mode 100644 exporters/metric/prometheus/example_test.go diff --git a/exporters/metric/prometheus/example_test.go b/exporters/metric/prometheus/example_test.go new file mode 100644 index 000000000..81e741a38 --- /dev/null +++ b/exporters/metric/prometheus/example_test.go @@ -0,0 +1,97 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus_test + +import ( + "bytes" + "context" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + + "go.opentelemetry.io/otel/api/kv" + "go.opentelemetry.io/otel/api/metric" + "go.opentelemetry.io/otel/exporters/metric/prometheus" + sdk "go.opentelemetry.io/otel/sdk/metric" + integrator "go.opentelemetry.io/otel/sdk/metric/integrator/simple" + "go.opentelemetry.io/otel/sdk/metric/selector/simple" +) + +// This test demonstrates that it is relatively difficult to setup a +// Prometheus export pipeline: +// +// 1. The default boundaries are difficult to pass, should be []float instead of []metric.Number +// 2. The push controller doesn't make sense b/c Prometheus is pull-bsaed +// +// TODO: Address these issues; add Resources to the test. + +func ExampleNewExportPipeline() { + // Create a meter + selector := simple.NewWithHistogramDistribution(nil) + exporter, err := prometheus.NewRawExporter(prometheus.Config{}) + if err != nil { + panic(err) + } + integrator := integrator.New(selector, true) + meterImpl := sdk.NewAccumulator(integrator) + meter := metric.WrapMeterImpl(meterImpl, "example") + + ctx := context.Background() + + // Use two instruments + counter := metric.Must(meter).NewInt64Counter( + "a.counter", + metric.WithDescription("Counts things"), + ) + recorder := metric.Must(meter).NewInt64ValueRecorder( + "a.valuerecorder", + metric.WithDescription("Records values"), + ) + + counter.Add(ctx, 100, kv.String("key", "value")) + recorder.Record(ctx, 100, kv.String("key", "value")) + + // Simulate a push + meterImpl.Collect(ctx) + err = exporter.Export(ctx, nil, integrator.CheckpointSet()) + if err != nil { + panic(err) + } + + // GET the HTTP endpoint + var input bytes.Buffer + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", &input) + if err != nil { + panic(err) + } + exporter.ServeHTTP(resp, req) + data, err := ioutil.ReadAll(resp.Result().Body) + if err != nil { + panic(err) + } + fmt.Print(string(data)) + + // Output: + // # HELP a_counter Counts things + // # TYPE a_counter counter + // a_counter{key="value"} 100 + // # HELP a_valuerecorder Records values + // # TYPE a_valuerecorder histogram + // a_valuerecorder_bucket{key="value",le="+Inf"} 1 + // a_valuerecorder_sum{key="value"} 100 + // a_valuerecorder_count{key="value"} 1 +} diff --git a/sdk/metric/aggregator/histogram/histogram.go b/sdk/metric/aggregator/histogram/histogram.go index 6566dab91..ccb0c2d3c 100644 --- a/sdk/metric/aggregator/histogram/histogram.go +++ b/sdk/metric/aggregator/histogram/histogram.go @@ -24,6 +24,11 @@ import ( "go.opentelemetry.io/otel/sdk/export/metric/aggregator" ) +// Note: This code uses a Mutex to govern access to the exclusive +// aggregator state. This is in contrast to a lock-free approach +// (as in the Go prometheus client) that was reverted here: +// https://github.com/open-telemetry/opentelemetry-go/pull/669 + type ( // Aggregator observe events and counts them in pre-determined buckets. // It also calculates the sum and count of all events. @@ -39,10 +44,9 @@ type ( // the sum and counts for all observed values and // the less than equal bucket count for the pre-determined boundaries. state struct { - // all fields have to be aligned for 64-bit atomic operations. - buckets aggregator.Buckets - count metric.Number - sum metric.Number + bucketCounts []metric.Number + count metric.Number + sum metric.Number } ) @@ -71,17 +75,12 @@ func New(desc *metric.Descriptor, boundaries []metric.Number) *Aggregator { sort.Sort(&sortedBoundaries) boundaries = sortedBoundaries.numbers - agg := Aggregator{ + return &Aggregator{ kind: desc.NumberKind(), boundaries: boundaries, - current: state{ - buckets: aggregator.Buckets{ - Boundaries: boundaries, - Counts: make([]metric.Number, len(boundaries)+1), - }, - }, + current: emptyState(boundaries), + checkpoint: emptyState(boundaries), } - return &agg } // Sum returns the sum of all values in the checkpoint. @@ -102,7 +101,10 @@ func (c *Aggregator) Count() (int64, error) { func (c *Aggregator) Histogram() (aggregator.Buckets, error) { c.lock.Lock() defer c.lock.Unlock() - return c.checkpoint.buckets, nil + return aggregator.Buckets{ + Boundaries: c.boundaries, + Counts: c.checkpoint.bucketCounts, + }, nil } // Checkpoint saves the current state and resets the current state to @@ -111,16 +113,13 @@ func (c *Aggregator) Histogram() (aggregator.Buckets, error) { // other. func (c *Aggregator) Checkpoint(ctx context.Context, desc *metric.Descriptor) { c.lock.Lock() - c.checkpoint, c.current = c.current, c.emptyState() + c.checkpoint, c.current = c.current, emptyState(c.boundaries) c.lock.Unlock() } -func (c *Aggregator) emptyState() state { +func emptyState(boundaries []metric.Number) state { return state{ - buckets: aggregator.Buckets{ - Boundaries: c.boundaries, - Counts: make([]metric.Number, len(c.boundaries)+1), - }, + bucketCounts: make([]metric.Number, len(boundaries)+1), } } @@ -141,7 +140,7 @@ func (c *Aggregator) Update(_ context.Context, number metric.Number, desc *metri c.current.count.AddInt64(1) c.current.sum.AddNumber(kind, number) - c.current.buckets.Counts[bucketID].AddUint64(1) + c.current.bucketCounts[bucketID].AddUint64(1) return nil } @@ -156,8 +155,8 @@ func (c *Aggregator) Merge(oa export.Aggregator, desc *metric.Descriptor) error c.checkpoint.sum.AddNumber(desc.NumberKind(), o.checkpoint.sum) c.checkpoint.count.AddNumber(metric.Uint64NumberKind, o.checkpoint.count) - for i := 0; i < len(c.checkpoint.buckets.Counts); i++ { - c.checkpoint.buckets.Counts[i].AddNumber(metric.Uint64NumberKind, o.checkpoint.buckets.Counts[i]) + for i := 0; i < len(c.checkpoint.bucketCounts); i++ { + c.checkpoint.bucketCounts[i].AddNumber(metric.Uint64NumberKind, o.checkpoint.bucketCounts[i]) } return nil } diff --git a/sdk/metric/aggregator/histogram/histogram_test.go b/sdk/metric/aggregator/histogram/histogram_test.go index 6a559cec3..c1541ee2b 100644 --- a/sdk/metric/aggregator/histogram/histogram_test.go +++ b/sdk/metric/aggregator/histogram/histogram_test.go @@ -113,15 +113,28 @@ func histogram(t *testing.T, profile test.Profile, policy policy) { require.Equal(t, all.Count(), count, "Same count -"+policy.name) require.Nil(t, err) - require.Equal(t, len(agg.checkpoint.buckets.Counts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries") + require.Equal(t, len(agg.checkpoint.bucketCounts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries") counts := calcBuckets(all.Points(), profile) for i, v := range counts { - bCount := agg.checkpoint.buckets.Counts[i].AsUint64() - require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, agg.checkpoint.buckets.Counts) + bCount := agg.checkpoint.bucketCounts[i].AsUint64() + require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, agg.checkpoint.bucketCounts) } } +func TestHistogramInitial(t *testing.T) { + test.RunProfiles(t, func(t *testing.T, profile test.Profile) { + descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) + + agg := New(descriptor, boundaries[profile.NumberKind]) + buckets, err := agg.Histogram() + + require.NoError(t, err) + require.Equal(t, len(buckets.Counts), len(boundaries[profile.NumberKind])+1) + require.Equal(t, len(buckets.Boundaries), len(boundaries[profile.NumberKind])) + }) +} + func TestHistogramMerge(t *testing.T) { ctx := context.Background() @@ -164,12 +177,12 @@ func TestHistogramMerge(t *testing.T) { require.Equal(t, all.Count(), count, "Same count - absolute") require.Nil(t, err) - require.Equal(t, len(agg1.checkpoint.buckets.Counts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries") + require.Equal(t, len(agg1.checkpoint.bucketCounts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries") counts := calcBuckets(all.Points(), profile) for i, v := range counts { - bCount := agg1.checkpoint.buckets.Counts[i].AsUint64() - require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, agg1.checkpoint.buckets.Counts) + bCount := agg1.checkpoint.bucketCounts[i].AsUint64() + require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, agg1.checkpoint.bucketCounts) } }) } @@ -191,8 +204,8 @@ func TestHistogramNotSet(t *testing.T) { require.Equal(t, int64(0), count, "Empty checkpoint count = 0") require.Nil(t, err) - require.Equal(t, len(agg.checkpoint.buckets.Counts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries") - for i, bCount := range agg.checkpoint.buckets.Counts { + require.Equal(t, len(agg.checkpoint.bucketCounts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries") + for i, bCount := range agg.checkpoint.bucketCounts { require.Equal(t, uint64(0), bCount.AsUint64(), "Bucket #%d must have 0 observed values", i) } }) From 76baa9cc7a46280f0bbc3721adc19556bda053fe Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 18 May 2020 10:48:58 -0700 Subject: [PATCH 058/108] Remove the push controller named Meter map (#738) * Remove the push controller named Meter map * Checkpoint * Remove Provider impls * Add a test * Expose Provider() getter instead of implementing the interface --- api/global/internal/meter_test.go | 2 +- api/metric/registry/registry.go | 20 +++++++++++++++++ api/metric/registry/registry_test.go | 11 ++++++++++ exporters/metric/prometheus/prometheus.go | 2 +- exporters/metric/stdout/example_test.go | 2 +- exporters/metric/stdout/stdout.go | 2 +- exporters/otlp/otlp_integration_test.go | 2 +- internal/metric/mock.go | 26 +---------------------- sdk/metric/controller/push/push.go | 26 ++++++----------------- sdk/metric/controller/push/push_test.go | 4 ++-- sdk/metric/example_test.go | 2 +- 11 files changed, 47 insertions(+), 52 deletions(-) diff --git a/api/global/internal/meter_test.go b/api/global/internal/meter_test.go index 17485b745..5c436791d 100644 --- a/api/global/internal/meter_test.go +++ b/api/global/internal/meter_test.go @@ -407,7 +407,7 @@ func TestRecordBatchRealSDK(t *testing.T) { if err != nil { t.Fatal(err) } - global.SetMeterProvider(pusher) + global.SetMeterProvider(pusher.Provider()) meter.RecordBatch(context.Background(), nil, counter.Measurement(1)) pusher.Stop() diff --git a/api/metric/registry/registry.go b/api/metric/registry/registry.go index 3a66b6903..56b187861 100644 --- a/api/metric/registry/registry.go +++ b/api/metric/registry/registry.go @@ -23,6 +23,13 @@ import ( "go.opentelemetry.io/otel/api/metric" ) +// Provider is a standard metric.Provider for wrapping `MeterImpl` +type Provider struct { + impl metric.MeterImpl +} + +var _ metric.Provider = (*Provider)(nil) + // uniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding // uniqueness checking for instrument descriptors. Use NewUniqueInstrumentMeter // to wrap an implementation with uniqueness checking. @@ -39,6 +46,19 @@ type key struct { libraryName string } +// NewProvider returns a new provider that implements instrument +// name-uniqueness checking. +func NewProvider(impl metric.MeterImpl) *Provider { + return &Provider{ + impl: NewUniqueInstrumentMeterImpl(impl), + } +} + +// Meter implements metric.Provider. +func (p *Provider) Meter(name string) metric.Meter { + return metric.WrapMeterImpl(p.impl, name) +} + // ErrMetricKindMismatch is the standard error for mismatched metric // instrument definitions. var ErrMetricKindMismatch = fmt.Errorf( diff --git a/api/metric/registry/registry_test.go b/api/metric/registry/registry_test.go index 51f8392a1..3d5991ca8 100644 --- a/api/metric/registry/registry_test.go +++ b/api/metric/registry/registry_test.go @@ -118,3 +118,14 @@ func TestRegistryDiffInstruments(t *testing.T) { } } } + +func TestProvider(t *testing.T) { + impl, _ := mockTest.NewMeter() + p := registry.NewProvider(impl) + m1 := p.Meter("m1") + m1p := p.Meter("m1") + m2 := p.Meter("m2") + + require.Equal(t, m1, m1p) + require.NotEqual(t, m1, m2) +} diff --git a/exporters/metric/prometheus/prometheus.go b/exporters/metric/prometheus/prometheus.go index 5af88a6b7..4d615fdf4 100644 --- a/exporters/metric/prometheus/prometheus.go +++ b/exporters/metric/prometheus/prometheus.go @@ -140,7 +140,7 @@ func InstallNewPipeline(config Config) (*push.Controller, http.HandlerFunc, erro if err != nil { return controller, hf, err } - global.SetMeterProvider(controller) + global.SetMeterProvider(controller.Provider()) return controller, hf, err } diff --git a/exporters/metric/stdout/example_test.go b/exporters/metric/stdout/example_test.go index 9cb3b45ad..0952b41c9 100644 --- a/exporters/metric/stdout/example_test.go +++ b/exporters/metric/stdout/example_test.go @@ -38,7 +38,7 @@ func ExampleNewExportPipeline() { ctx := context.Background() key := kv.Key("key") - meter := pusher.Meter("example") + meter := pusher.Provider().Meter("example") // Create and update a single counter: counter := metric.Must(meter).NewInt64Counter("a.counter") diff --git a/exporters/metric/stdout/stdout.go b/exporters/metric/stdout/stdout.go index d22b5f070..5e8b513a5 100644 --- a/exporters/metric/stdout/stdout.go +++ b/exporters/metric/stdout/stdout.go @@ -126,7 +126,7 @@ func InstallNewPipeline(config Config, opts ...push.Option) (*push.Controller, e if err != nil { return controller, err } - global.SetMeterProvider(controller) + global.SetMeterProvider(controller.Provider()) return controller, err } diff --git a/exporters/otlp/otlp_integration_test.go b/exporters/otlp/otlp_integration_test.go index 624bd4a07..54aae4f0c 100644 --- a/exporters/otlp/otlp_integration_test.go +++ b/exporters/otlp/otlp_integration_test.go @@ -115,7 +115,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) pusher.Start() ctx := context.Background() - meter := pusher.Meter("test-meter") + meter := pusher.Provider().Meter("test-meter") labels := []kv.KeyValue{kv.Bool("test", true)} type data struct { diff --git a/internal/metric/mock.go b/internal/metric/mock.go index 4ec328585..985ea7fc0 100644 --- a/internal/metric/mock.go +++ b/internal/metric/mock.go @@ -38,13 +38,6 @@ type ( LibraryName string } - MeterProvider struct { - lock sync.Mutex - impl *MeterImpl - unique metric.MeterImpl - registered map[string]apimetric.Meter - } - MeterImpl struct { lock sync.Mutex @@ -123,24 +116,7 @@ func NewProvider() (*MeterImpl, apimetric.Provider) { impl := &MeterImpl{ asyncInstruments: NewAsyncInstrumentState(nil), } - p := &MeterProvider{ - impl: impl, - unique: registry.NewUniqueInstrumentMeterImpl(impl), - registered: map[string]apimetric.Meter{}, - } - return impl, p -} - -func (p *MeterProvider) Meter(name string) apimetric.Meter { - p.lock.Lock() - defer p.lock.Unlock() - - if lookup, ok := p.registered[name]; ok { - return lookup - } - m := apimetric.WrapMeterImpl(p.unique, name) - p.registered[name] = m - return m + return impl, registry.NewProvider(impl) } func NewMeter() (*MeterImpl, apimetric.Meter) { diff --git a/sdk/metric/controller/push/push.go b/sdk/metric/controller/push/push.go index d93d032de..0e00ce5fd 100644 --- a/sdk/metric/controller/push/push.go +++ b/sdk/metric/controller/push/push.go @@ -32,8 +32,6 @@ type Controller struct { collectLock sync.Mutex accumulator *sdk.Accumulator resource *resource.Resource - uniq metric.MeterImpl - named map[string]metric.Meter errorHandler sdk.ErrorHandler integrator export.Integrator exporter export.Exporter @@ -42,10 +40,9 @@ type Controller struct { period time.Duration ticker Ticker clock Clock + provider *registry.Provider } -var _ metric.Provider = &Controller{} - // Several types below are created to match "github.com/benbjohnson/clock" // so that it remains a test-only dependency. @@ -83,8 +80,7 @@ func New(integrator export.Integrator, exporter export.Exporter, period time.Dur return &Controller{ accumulator: impl, resource: c.Resource, - uniq: registry.NewUniqueInstrumentMeterImpl(impl), - named: map[string]metric.Meter{}, + provider: registry.NewProvider(impl), errorHandler: c.ErrorHandler, integrator: integrator, exporter: exporter, @@ -102,6 +98,8 @@ func (c *Controller) SetClock(clock Clock) { c.clock = clock } +// SetErrorHandler sets the handler for errors. If none has been set, the +// SDK default error handler is used. func (c *Controller) SetErrorHandler(errorHandler sdk.ErrorHandler) { c.lock.Lock() defer c.lock.Unlock() @@ -109,19 +107,9 @@ func (c *Controller) SetErrorHandler(errorHandler sdk.ErrorHandler) { c.accumulator.SetErrorHandler(errorHandler) } -// Meter returns a named Meter, satisifying the metric.Provider -// interface. -func (c *Controller) Meter(name string) metric.Meter { - c.lock.Lock() - defer c.lock.Unlock() - - if meter, ok := c.named[name]; ok { - return meter - } - - meter := metric.WrapMeterImpl(c.uniq, name) - c.named[name] = meter - return meter +// Provider returns a metric.Provider instance for this controller. +func (c *Controller) Provider() metric.Provider { + return c.provider } // Start begins a ticker that periodically collects and exports diff --git a/sdk/metric/controller/push/push_test.go b/sdk/metric/controller/push/push_test.go index 11dbf4a52..fd200f4d0 100644 --- a/sdk/metric/controller/push/push_test.go +++ b/sdk/metric/controller/push/push_test.go @@ -183,7 +183,7 @@ func TestPushTicker(t *testing.T) { fix := newFixture(t) p := push.New(fix.integrator, fix.exporter, time.Second) - meter := p.Meter("name") + meter := p.Provider().Meter("name") mock := mockClock{clock.NewMock()} p.SetClock(mock) @@ -280,7 +280,7 @@ func TestPushExportError(t *testing.T) { ctx := context.Background() - meter := p.Meter("name") + meter := p.Provider().Meter("name") counter1 := metric.Must(meter).NewInt64Counter("counter1") counter2 := metric.Must(meter).NewInt64Counter("counter2") diff --git a/sdk/metric/example_test.go b/sdk/metric/example_test.go index 4f39441b5..c4910b9bb 100644 --- a/sdk/metric/example_test.go +++ b/sdk/metric/example_test.go @@ -38,7 +38,7 @@ func ExampleNew() { ctx := context.Background() key := kv.Key("key") - meter := pusher.Meter("example") + meter := pusher.Provider().Meter("example") counter := metric.Must(meter).NewInt64Counter("a.counter") From ee3c9ed1a5a662240050fcefc90c3e50b0331c46 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 18 May 2020 11:03:43 -0700 Subject: [PATCH 059/108] Rename Observer to ValueObserver (#734) * Observer -> ValueObserver * Move wrappers into async.go --- api/global/internal/meter_test.go | 20 +++++----- api/global/internal/registry_test.go | 8 ++-- api/metric/api_test.go | 20 +++++----- api/metric/async.go | 20 +++++++++- api/metric/kind.go | 4 +- api/metric/kind_string.go | 6 +-- api/metric/meter.go | 40 +++++++++---------- api/metric/must.go | 24 +++++------ api/metric/observer.go | 12 +++--- api/metric/registry/registry_test.go | 8 ++-- api/metric/sync.go | 18 --------- example/basic/main.go | 4 +- example/prometheus/main.go | 4 +- .../metric/prometheus/prometheus_test.go | 2 +- exporters/metric/stdout/stdout_test.go | 8 ++-- exporters/otlp/otlp_integration_test.go | 12 +++--- .../metric/aggregator/aggregator_test.go | 2 +- .../aggregator/lastvalue/lastvalue_test.go | 6 +-- sdk/metric/benchmark_test.go | 12 +++--- sdk/metric/correct_test.go | 30 +++++++------- sdk/metric/integrator/test/test.go | 6 +-- sdk/metric/selector/simple/simple.go | 8 ++-- sdk/metric/selector/simple/simple_test.go | 10 ++--- 23 files changed, 142 insertions(+), 142 deletions(-) diff --git a/api/global/internal/meter_test.go b/api/global/internal/meter_test.go index 5c436791d..5d9188c96 100644 --- a/api/global/internal/meter_test.go +++ b/api/global/internal/meter_test.go @@ -86,12 +86,12 @@ func TestDirect(t *testing.T) { valuerecorder.Record(ctx, 1, labels1...) valuerecorder.Record(ctx, 2, labels1...) - _ = Must(meter1).RegisterFloat64Observer("test.observer.float", func(result metric.Float64ObserverResult) { + _ = Must(meter1).RegisterFloat64ValueObserver("test.valueobserver.float", func(result metric.Float64ObserverResult) { result.Observe(1., labels1...) result.Observe(2., labels2...) }) - _ = Must(meter1).RegisterInt64Observer("test.observer.int", func(result metric.Int64ObserverResult) { + _ = Must(meter1).RegisterInt64ValueObserver("test.valueobserver.int", func(result metric.Int64ObserverResult) { result.Observe(1, labels1...) result.Observe(2, labels2...) }) @@ -132,25 +132,25 @@ func TestDirect(t *testing.T) { Number: asFloat(3), }, { - Name: "test.observer.float", + Name: "test.valueobserver.float", LibraryName: "test1", Labels: asMap(labels1...), Number: asFloat(1), }, { - Name: "test.observer.float", + Name: "test.valueobserver.float", LibraryName: "test1", Labels: asMap(labels2...), Number: asFloat(2), }, { - Name: "test.observer.int", + Name: "test.valueobserver.int", LibraryName: "test1", Labels: asMap(labels1...), Number: asInt(1), }, { - Name: "test.observer.int", + Name: "test.valueobserver.int", LibraryName: "test1", Labels: asMap(labels2...), Number: asInt(2), @@ -331,12 +331,12 @@ func TestImplementationIndirection(t *testing.T) { require.False(t, ok) // Async: no SDK yet - observer := Must(meter1).RegisterFloat64Observer( - "interface.observer", + valueobserver := Must(meter1).RegisterFloat64ValueObserver( + "interface.valueobserver", func(result metric.Float64ObserverResult) {}, ) - ival = observer.AsyncImpl().Implementation() + ival = valueobserver.AsyncImpl().Implementation() require.NotNil(t, ival) _, ok = ival.(*metrictest.Async) @@ -356,7 +356,7 @@ func TestImplementationIndirection(t *testing.T) { require.True(t, ok) // Async - ival = observer.AsyncImpl().Implementation() + ival = valueobserver.AsyncImpl().Implementation() require.NotNil(t, ival) _, ok = ival.(*metrictest.Async) diff --git a/api/global/internal/registry_test.go b/api/global/internal/registry_test.go index 14ae04dfd..76144bf5b 100644 --- a/api/global/internal/registry_test.go +++ b/api/global/internal/registry_test.go @@ -42,11 +42,11 @@ var ( "valuerecorder.float64": func(name, libraryName string) (metric.InstrumentImpl, error) { return unwrap(MeterProvider().Meter(libraryName).NewFloat64ValueRecorder(name)) }, - "observer.int64": func(name, libraryName string) (metric.InstrumentImpl, error) { - return unwrap(MeterProvider().Meter(libraryName).RegisterInt64Observer(name, func(metric.Int64ObserverResult) {})) + "valueobserver.int64": func(name, libraryName string) (metric.InstrumentImpl, error) { + return unwrap(MeterProvider().Meter(libraryName).RegisterInt64ValueObserver(name, func(metric.Int64ObserverResult) {})) }, - "observer.float64": func(name, libraryName string) (metric.InstrumentImpl, error) { - return unwrap(MeterProvider().Meter(libraryName).RegisterFloat64Observer(name, func(metric.Float64ObserverResult) {})) + "valueobserver.float64": func(name, libraryName string) (metric.InstrumentImpl, error) { + return unwrap(MeterProvider().Meter(libraryName).RegisterFloat64ValueObserver(name, func(metric.Float64ObserverResult) {})) }, } ) diff --git a/api/metric/api_test.go b/api/metric/api_test.go index 9a370a8d4..cfcd1a7a4 100644 --- a/api/metric/api_test.go +++ b/api/metric/api_test.go @@ -146,11 +146,11 @@ func TestValueRecorder(t *testing.T) { } } -func TestObserver(t *testing.T) { +func TestObserverInstruments(t *testing.T) { { labels := []kv.KeyValue{kv.String("O", "P")} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterFloat64Observer("test.observer.float", func(result metric.Float64ObserverResult) { + o := Must(meter).RegisterFloat64ValueObserver("test.observer.float", func(result metric.Float64ObserverResult) { result.Observe(42, labels...) }) t.Log("Testing float observer") @@ -161,7 +161,7 @@ func TestObserver(t *testing.T) { { labels := []kv.KeyValue{} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterInt64Observer("test.observer.int", func(result metric.Int64ObserverResult) { + o := Must(meter).RegisterInt64ValueObserver("test.observer.int", func(result metric.Int64ObserverResult) { result.Observe(42, labels...) }) t.Log("Testing int observer") @@ -210,11 +210,11 @@ func checkBatches(t *testing.T, ctx context.Context, labels []kv.KeyValue, mock } } -func TestBatchObserver(t *testing.T) { +func TestBatchObserverInstruments(t *testing.T) { mockSDK, meter := mockTest.NewMeter() - var obs1 metric.Int64Observer - var obs2 metric.Float64Observer + var obs1 metric.Int64ValueObserver + var obs2 metric.Float64ValueObserver labels := []kv.KeyValue{ kv.String("A", "B"), @@ -229,8 +229,8 @@ func TestBatchObserver(t *testing.T) { ) }, ) - obs1 = cb.RegisterInt64Observer("test.observer.int") - obs2 = cb.RegisterFloat64Observer("test.observer.float") + obs1 = cb.RegisterInt64ValueObserver("test.observer.int") + obs2 = cb.RegisterFloat64ValueObserver("test.observer.float") mockSDK.RunAsyncInstruments() @@ -314,7 +314,7 @@ func TestWrappedInstrumentError(t *testing.T) { require.Equal(t, err, metric.ErrSDKReturnedNilImpl) require.NotNil(t, valuerecorder.SyncImpl()) - observer, err := meter.RegisterInt64Observer("test.observer", func(result metric.Int64ObserverResult) {}) + observer, err := meter.RegisterInt64ValueObserver("test.observer", func(result metric.Int64ObserverResult) {}) require.NotNil(t, err) require.NotNil(t, observer.AsyncImpl()) @@ -324,7 +324,7 @@ func TestNilCallbackObserverNoop(t *testing.T) { // Tests that a nil callback yields a no-op observer without error. _, meter := mockTest.NewMeter() - observer := Must(meter).RegisterInt64Observer("test.observer", nil) + observer := Must(meter).RegisterInt64ValueObserver("test.observer", nil) _, ok := observer.AsyncImpl().(metric.NoopAsync) require.True(t, ok) diff --git a/api/metric/async.go b/api/metric/async.go index bd22f714e..7f766e1ed 100644 --- a/api/metric/async.go +++ b/api/metric/async.go @@ -29,7 +29,7 @@ import "go.opentelemetry.io/otel/api/kv" // Observation is used for reporting an asynchronous batch of metric // values. Instances of this type should be created by asynchronous -// instruments (e.g., Int64Observer.Observation()). +// instruments (e.g., Int64ValueObserver.Observation()). type Observation struct { // number needs to be aligned for 64-bit atomic operations. number Number @@ -175,3 +175,21 @@ func (b *BatchObserverCallback) Run(function func([]kv.KeyValue, ...Observation) function: function, }) } + +// wrapInt64ValueObserverInstrument returns an `Int64ValueObserver` from a +// `AsyncImpl`. An error will be generated if the +// `AsyncImpl` is nil (in which case a No-op is substituted), +// otherwise the error passes through. +func wrapInt64ValueObserverInstrument(asyncInst AsyncImpl, err error) (Int64ValueObserver, error) { + common, err := checkNewAsync(asyncInst, err) + return Int64ValueObserver{asyncInstrument: common}, err +} + +// wrapFloat64ValueObserverInstrument returns an `Float64ValueObserver` from a +// `AsyncImpl`. An error will be generated if the +// `AsyncImpl` is nil (in which case a No-op is substituted), +// otherwise the error passes through. +func wrapFloat64ValueObserverInstrument(asyncInst AsyncImpl, err error) (Float64ValueObserver, error) { + common, err := checkNewAsync(asyncInst, err) + return Float64ValueObserver{asyncInstrument: common}, err +} diff --git a/api/metric/kind.go b/api/metric/kind.go index 38001e918..cd847a242 100644 --- a/api/metric/kind.go +++ b/api/metric/kind.go @@ -22,8 +22,8 @@ type Kind int8 const ( // ValueRecorderKind indicates a ValueRecorder instrument. ValueRecorderKind Kind = iota - // ObserverKind indicates an Observer instrument. - ObserverKind + // ValueObserverKind indicates an ValueObserver instrument. + ValueObserverKind // CounterKind indicates a Counter instrument. CounterKind ) diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index 67113b120..a05d5f307 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -9,13 +9,13 @@ func _() { // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[ValueRecorderKind-0] - _ = x[ObserverKind-1] + _ = x[ValueObserverKind-1] _ = x[CounterKind-2] } -const _Kind_name = "ValueRecorderKindObserverKindCounterKind" +const _Kind_name = "ValueRecorderKindValueObserverKindCounterKind" -var _Kind_index = [...]uint8{0, 17, 29, 40} +var _Kind_index = [...]uint8{0, 17, 34, 45} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { diff --git a/api/metric/meter.go b/api/metric/meter.go index 5e95e2812..9cec69ec6 100644 --- a/api/metric/meter.go +++ b/api/metric/meter.go @@ -100,54 +100,54 @@ func (m Meter) NewFloat64ValueRecorder(name string, opts ...Option) (Float64Valu m.newSync(name, ValueRecorderKind, Float64NumberKind, opts)) } -// RegisterInt64Observer creates a new integer Observer instrument +// RegisterInt64ValueObserver creates a new integer ValueObserver instrument // with the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (m Meter) RegisterInt64Observer(name string, callback Int64ObserverCallback, opts ...Option) (Int64Observer, error) { +func (m Meter) RegisterInt64ValueObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64ValueObserver, error) { if callback == nil { - return wrapInt64ObserverInstrument(NoopAsync{}, nil) + return wrapInt64ValueObserverInstrument(NoopAsync{}, nil) } - return wrapInt64ObserverInstrument( - m.newAsync(name, ObserverKind, Int64NumberKind, opts, + return wrapInt64ValueObserverInstrument( + m.newAsync(name, ValueObserverKind, Int64NumberKind, opts, newInt64AsyncRunner(callback))) } -// RegisterFloat64Observer creates a new floating point Observer with +// RegisterFloat64ValueObserver creates a new floating point ValueObserver with // the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (m Meter) RegisterFloat64Observer(name string, callback Float64ObserverCallback, opts ...Option) (Float64Observer, error) { +func (m Meter) RegisterFloat64ValueObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64ValueObserver, error) { if callback == nil { - return wrapFloat64ObserverInstrument(NoopAsync{}, nil) + return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil) } - return wrapFloat64ObserverInstrument( - m.newAsync(name, ObserverKind, Float64NumberKind, opts, + return wrapFloat64ValueObserverInstrument( + m.newAsync(name, ValueObserverKind, Float64NumberKind, opts, newFloat64AsyncRunner(callback))) } -// RegisterInt64Observer creates a new integer Observer instrument +// RegisterInt64ValueObserver creates a new integer ValueObserver instrument // with the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (b BatchObserver) RegisterInt64Observer(name string, opts ...Option) (Int64Observer, error) { +func (b BatchObserver) RegisterInt64ValueObserver(name string, opts ...Option) (Int64ValueObserver, error) { if b.runner == nil { - return wrapInt64ObserverInstrument(NoopAsync{}, nil) + return wrapInt64ValueObserverInstrument(NoopAsync{}, nil) } - return wrapInt64ObserverInstrument( - b.meter.newAsync(name, ObserverKind, Int64NumberKind, opts, b.runner)) + return wrapInt64ValueObserverInstrument( + b.meter.newAsync(name, ValueObserverKind, Int64NumberKind, opts, b.runner)) } -// RegisterFloat64Observer creates a new floating point Observer with +// RegisterFloat64ValueObserver creates a new floating point ValueObserver with // the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (b BatchObserver) RegisterFloat64Observer(name string, opts ...Option) (Float64Observer, error) { +func (b BatchObserver) RegisterFloat64ValueObserver(name string, opts ...Option) (Float64ValueObserver, error) { if b.runner == nil { - return wrapFloat64ObserverInstrument(NoopAsync{}, nil) + return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil) } - return wrapFloat64ObserverInstrument( - b.meter.newAsync(name, ObserverKind, Float64NumberKind, opts, + return wrapFloat64ValueObserverInstrument( + b.meter.newAsync(name, ValueObserverKind, Float64NumberKind, opts, b.runner)) } diff --git a/api/metric/must.go b/api/metric/must.go index b747932f3..2bfd03310 100644 --- a/api/metric/must.go +++ b/api/metric/must.go @@ -73,20 +73,20 @@ func (mm MeterMust) NewFloat64ValueRecorder(name string, mos ...Option) Float64V } } -// RegisterInt64Observer calls `Meter.RegisterInt64Observer` and +// RegisterInt64ValueObserver calls `Meter.RegisterInt64ValueObserver` and // returns the instrument, panicking if it encounters an error. -func (mm MeterMust) RegisterInt64Observer(name string, callback Int64ObserverCallback, oos ...Option) Int64Observer { - if inst, err := mm.meter.RegisterInt64Observer(name, callback, oos...); err != nil { +func (mm MeterMust) RegisterInt64ValueObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64ValueObserver { + if inst, err := mm.meter.RegisterInt64ValueObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } -// RegisterFloat64Observer calls `Meter.RegisterFloat64Observer` and +// RegisterFloat64ValueObserver calls `Meter.RegisterFloat64ValueObserver` and // returns the instrument, panicking if it encounters an error. -func (mm MeterMust) RegisterFloat64Observer(name string, callback Float64ObserverCallback, oos ...Option) Float64Observer { - if inst, err := mm.meter.RegisterFloat64Observer(name, callback, oos...); err != nil { +func (mm MeterMust) RegisterFloat64ValueObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64ValueObserver { + if inst, err := mm.meter.RegisterFloat64ValueObserver(name, callback, oos...); err != nil { panic(err) } else { return inst @@ -101,20 +101,20 @@ func (mm MeterMust) NewBatchObserver(callback BatchObserverCallback) BatchObserv } } -// RegisterInt64Observer calls `BatchObserver.RegisterInt64Observer` and +// RegisterInt64ValueObserver calls `BatchObserver.RegisterInt64ValueObserver` and // returns the instrument, panicking if it encounters an error. -func (bm BatchObserverMust) RegisterInt64Observer(name string, oos ...Option) Int64Observer { - if inst, err := bm.batch.RegisterInt64Observer(name, oos...); err != nil { +func (bm BatchObserverMust) RegisterInt64ValueObserver(name string, oos ...Option) Int64ValueObserver { + if inst, err := bm.batch.RegisterInt64ValueObserver(name, oos...); err != nil { panic(err) } else { return inst } } -// RegisterFloat64Observer calls `BatchObserver.RegisterFloat64Observer` and +// RegisterFloat64ValueObserver calls `BatchObserver.RegisterFloat64ValueObserver` and // returns the instrument, panicking if it encounters an error. -func (bm BatchObserverMust) RegisterFloat64Observer(name string, oos ...Option) Float64Observer { - if inst, err := bm.batch.RegisterFloat64Observer(name, oos...); err != nil { +func (bm BatchObserverMust) RegisterFloat64ValueObserver(name string, oos ...Option) Float64ValueObserver { + if inst, err := bm.batch.RegisterFloat64ValueObserver(name, oos...); err != nil { panic(err) } else { return inst diff --git a/api/metric/observer.go b/api/metric/observer.go index c5b173ff1..9d1a0582c 100644 --- a/api/metric/observer.go +++ b/api/metric/observer.go @@ -21,15 +21,15 @@ type BatchObserver struct { runner AsyncBatchRunner } -// Int64Observer is a metric that captures a set of int64 values at a +// Int64ValueObserver is a metric that captures a set of int64 values at a // point in time. -type Int64Observer struct { +type Int64ValueObserver struct { asyncInstrument } -// Float64Observer is a metric that captures a set of float64 values +// Float64ValueObserver is a metric that captures a set of float64 values // at a point in time. -type Float64Observer struct { +type Float64ValueObserver struct { asyncInstrument } @@ -37,7 +37,7 @@ type Float64Observer struct { // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, // users should not refer to this. -func (i Int64Observer) Observation(v int64) Observation { +func (i Int64ValueObserver) Observation(v int64) Observation { return Observation{ number: NewInt64Number(v), instrument: i.instrument, @@ -48,7 +48,7 @@ func (i Int64Observer) Observation(v int64) Observation { // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, // users should not refer to this. -func (f Float64Observer) Observation(v float64) Observation { +func (f Float64ValueObserver) Observation(v float64) Observation { return Observation{ number: NewFloat64Number(v), instrument: f.instrument, diff --git a/api/metric/registry/registry_test.go b/api/metric/registry/registry_test.go index 3d5991ca8..4f5c10a33 100644 --- a/api/metric/registry/registry_test.go +++ b/api/metric/registry/registry_test.go @@ -43,11 +43,11 @@ var ( "valuerecorder.float64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { return unwrap(m.NewFloat64ValueRecorder(name)) }, - "observer.int64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { - return unwrap(m.RegisterInt64Observer(name, func(metric.Int64ObserverResult) {})) + "valueobserver.int64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { + return unwrap(m.RegisterInt64ValueObserver(name, func(metric.Int64ObserverResult) {})) }, - "observer.float64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { - return unwrap(m.RegisterFloat64Observer(name, func(metric.Float64ObserverResult) {})) + "valueobserver.float64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { + return unwrap(m.RegisterFloat64ValueObserver(name, func(metric.Float64ObserverResult) {})) }, } ) diff --git a/api/metric/sync.go b/api/metric/sync.go index 66e99c285..2001ff197 100644 --- a/api/metric/sync.go +++ b/api/metric/sync.go @@ -191,21 +191,3 @@ func wrapFloat64ValueRecorderInstrument(syncInst SyncImpl, err error) (Float64Va common, err := checkNewSync(syncInst, err) return Float64ValueRecorder{syncInstrument: common}, err } - -// wrapInt64ObserverInstrument returns an `Int64Observer` from a -// `AsyncImpl`. An error will be generated if the -// `AsyncImpl` is nil (in which case a No-op is substituted), -// otherwise the error passes through. -func wrapInt64ObserverInstrument(asyncInst AsyncImpl, err error) (Int64Observer, error) { - common, err := checkNewAsync(asyncInst, err) - return Int64Observer{asyncInstrument: common}, err -} - -// wrapFloat64ObserverInstrument returns an `Float64Observer` from a -// `AsyncImpl`. An error will be generated if the -// `AsyncImpl` is nil (in which case a No-op is substituted), -// otherwise the error passes through. -func wrapFloat64ObserverInstrument(asyncInst AsyncImpl, err error) (Float64Observer, error) { - common, err := checkNewAsync(asyncInst, err) - return Float64Observer{asyncInstrument: common}, err -} diff --git a/example/basic/main.go b/example/basic/main.go index 84470d10b..04c4f8e49 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -76,8 +76,8 @@ func main() { oneMetricCB := func(result metric.Float64ObserverResult) { result.Observe(1, commonLabels...) } - _ = metric.Must(meter).RegisterFloat64Observer("ex.com.one", oneMetricCB, - metric.WithDescription("An observer set to 1.0"), + _ = metric.Must(meter).RegisterFloat64ValueObserver("ex.com.one", oneMetricCB, + metric.WithDescription("A ValueObserver set to 1.0"), ) valuerecorderTwo := metric.Must(meter).NewFloat64ValueRecorder("ex.com.two") diff --git a/example/prometheus/main.go b/example/prometheus/main.go index f9a5cf702..4fbf94baa 100644 --- a/example/prometheus/main.go +++ b/example/prometheus/main.go @@ -59,8 +59,8 @@ func main() { (*observerLock).RUnlock() result.Observe(value, labels...) } - _ = metric.Must(meter).RegisterFloat64Observer("ex.com.one", cb, - metric.WithDescription("An observer set to 1.0"), + _ = metric.Must(meter).RegisterFloat64ValueObserver("ex.com.one", cb, + metric.WithDescription("A ValueObserver set to 1.0"), ) valuerecorder := metric.Must(meter).NewFloat64ValueRecorder("ex.com.two") diff --git a/exporters/metric/prometheus/prometheus_test.go b/exporters/metric/prometheus/prometheus_test.go index 0505281a4..f30813b06 100644 --- a/exporters/metric/prometheus/prometheus_test.go +++ b/exporters/metric/prometheus/prometheus_test.go @@ -44,7 +44,7 @@ func TestPrometheusExporter(t *testing.T) { counter := metric.NewDescriptor( "counter", metric.CounterKind, metric.Float64NumberKind) lastValue := metric.NewDescriptor( - "lastvalue", metric.ObserverKind, metric.Float64NumberKind) + "lastvalue", metric.ValueObserverKind, metric.Float64NumberKind) valuerecorder := metric.NewDescriptor( "valuerecorder", metric.ValueRecorderKind, metric.Float64NumberKind) histogramValueRecorder := metric.NewDescriptor( diff --git a/exporters/metric/stdout/stdout_test.go b/exporters/metric/stdout/stdout_test.go index 918c47b8b..2dee68e55 100644 --- a/exporters/metric/stdout/stdout_test.go +++ b/exporters/metric/stdout/stdout_test.go @@ -98,7 +98,7 @@ func TestStdoutTimestamp(t *testing.T) { checkpointSet := test.NewCheckpointSet() ctx := context.Background() - desc := metric.NewDescriptor("test.name", metric.ObserverKind, metric.Int64NumberKind) + desc := metric.NewDescriptor("test.name", metric.ValueObserverKind, metric.Int64NumberKind) lvagg := lastvalue.New() aggtest.CheckedUpdate(t, lvagg, metric.NewInt64Number(321), &desc) lvagg.Checkpoint(ctx, &desc) @@ -160,7 +160,7 @@ func TestStdoutLastValueFormat(t *testing.T) { checkpointSet := test.NewCheckpointSet() - desc := metric.NewDescriptor("test.name", metric.ObserverKind, metric.Float64NumberKind) + desc := metric.NewDescriptor("test.name", metric.ValueObserverKind, metric.Float64NumberKind) lvagg := lastvalue.New() aggtest.CheckedUpdate(fix.t, lvagg, metric.NewFloat64Number(123.456), &desc) lvagg.Checkpoint(fix.ctx, &desc) @@ -268,7 +268,7 @@ func TestStdoutLastValueNotSet(t *testing.T) { checkpointSet := test.NewCheckpointSet() - desc := metric.NewDescriptor("test.name", metric.ObserverKind, metric.Float64NumberKind) + desc := metric.NewDescriptor("test.name", metric.ValueObserverKind, metric.Float64NumberKind) lvagg := lastvalue.New() lvagg.Checkpoint(fix.ctx, &desc) @@ -318,7 +318,7 @@ func TestStdoutResource(t *testing.T) { checkpointSet := test.NewCheckpointSet() - desc := metric.NewDescriptor("test.name", metric.ObserverKind, metric.Float64NumberKind) + desc := metric.NewDescriptor("test.name", metric.ValueObserverKind, metric.Float64NumberKind) lvagg := lastvalue.New() aggtest.CheckedUpdate(fix.t, lvagg, metric.NewFloat64Number(123.456), &desc) lvagg.Checkpoint(fix.ctx, &desc) diff --git a/exporters/otlp/otlp_integration_test.go b/exporters/otlp/otlp_integration_test.go index 54aae4f0c..a7a764a49 100644 --- a/exporters/otlp/otlp_integration_test.go +++ b/exporters/otlp/otlp_integration_test.go @@ -128,8 +128,8 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) "test-float64-counter": {metric.CounterKind, metricapi.Float64NumberKind, 1}, "test-int64-valuerecorder": {metric.ValueRecorderKind, metricapi.Int64NumberKind, 2}, "test-float64-valuerecorder": {metric.ValueRecorderKind, metricapi.Float64NumberKind, 2}, - "test-int64-observer": {metric.ObserverKind, metricapi.Int64NumberKind, 3}, - "test-float64-observer": {metric.ObserverKind, metricapi.Float64NumberKind, 3}, + "test-int64-valueobserver": {metric.ValueObserverKind, metricapi.Int64NumberKind, 3}, + "test-float64-valueobserver": {metric.ValueObserverKind, metricapi.Float64NumberKind, 3}, } for name, data := range instruments { switch data.iKind { @@ -151,18 +151,18 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) default: assert.Failf(t, "unsupported number testing kind", data.nKind.String()) } - case metric.ObserverKind: + case metric.ValueObserverKind: switch data.nKind { case metricapi.Int64NumberKind: callback := func(v int64) metricapi.Int64ObserverCallback { return metricapi.Int64ObserverCallback(func(result metricapi.Int64ObserverResult) { result.Observe(v, labels...) }) }(data.val) - metricapi.Must(meter).RegisterInt64Observer(name, callback) + metricapi.Must(meter).RegisterInt64ValueObserver(name, callback) case metricapi.Float64NumberKind: callback := func(v float64) metricapi.Float64ObserverCallback { return metricapi.Float64ObserverCallback(func(result metricapi.Float64ObserverResult) { result.Observe(v, labels...) }) }(float64(data.val)) - metricapi.Must(meter).RegisterFloat64Observer(name, callback) + metricapi.Must(meter).RegisterFloat64ValueObserver(name, callback) default: assert.Failf(t, "unsupported number testing kind", data.nKind.String()) } @@ -246,7 +246,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) default: assert.Failf(t, "invalid number kind", data.nKind.String()) } - case metric.ValueRecorderKind, metric.ObserverKind: + case metric.ValueRecorderKind, metric.ValueObserverKind: assert.Equal(t, metricpb.MetricDescriptor_SUMMARY.String(), desc.GetType().String()) m.GetSummaryDataPoints() if dp := m.GetSummaryDataPoints(); assert.Len(t, dp, 1) { diff --git a/sdk/export/metric/aggregator/aggregator_test.go b/sdk/export/metric/aggregator/aggregator_test.go index 0083a71a2..ce7624949 100644 --- a/sdk/export/metric/aggregator/aggregator_test.go +++ b/sdk/export/metric/aggregator/aggregator_test.go @@ -87,7 +87,7 @@ func TestNaNTest(t *testing.T) { for _, mkind := range []metric.Kind{ metric.CounterKind, metric.ValueRecorderKind, - metric.ObserverKind, + metric.ValueObserverKind, } { desc := metric.NewDescriptor( "name", diff --git a/sdk/metric/aggregator/lastvalue/lastvalue_test.go b/sdk/metric/aggregator/lastvalue/lastvalue_test.go index 49b9e6970..1b4da094f 100644 --- a/sdk/metric/aggregator/lastvalue/lastvalue_test.go +++ b/sdk/metric/aggregator/lastvalue/lastvalue_test.go @@ -55,7 +55,7 @@ func TestLastValueUpdate(t *testing.T) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) { agg := New() - record := test.NewAggregatorTest(metric.ObserverKind, profile.NumberKind) + record := test.NewAggregatorTest(metric.ValueObserverKind, profile.NumberKind) var last metric.Number for i := 0; i < count; i++ { @@ -79,7 +79,7 @@ func TestLastValueMerge(t *testing.T) { agg1 := New() agg2 := New() - descriptor := test.NewAggregatorTest(metric.ObserverKind, profile.NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueObserverKind, profile.NumberKind) first1 := profile.Random(+1) first2 := profile.Random(+1) @@ -107,7 +107,7 @@ func TestLastValueMerge(t *testing.T) { } func TestLastValueNotSet(t *testing.T) { - descriptor := test.NewAggregatorTest(metric.ObserverKind, metric.Int64NumberKind) + descriptor := test.NewAggregatorTest(metric.ValueObserverKind, metric.Int64NumberKind) g := New() g.Checkpoint(context.Background(), descriptor) diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index 06c8a980b..3a6b9888d 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -423,22 +423,22 @@ func BenchmarkObserverRegistration(b *testing.B) { fix := newFixture(b) names := make([]string, 0, b.N) for i := 0; i < b.N; i++ { - names = append(names, fmt.Sprintf("test.observer.%d", i)) + names = append(names, fmt.Sprintf("test.valueobserver.%d", i)) } cb := func(result metric.Int64ObserverResult) {} b.ResetTimer() for i := 0; i < b.N; i++ { - fix.meter.RegisterInt64Observer(names[i], cb) + fix.meter.RegisterInt64ValueObserver(names[i], cb) } } -func BenchmarkObserverObservationInt64(b *testing.B) { +func BenchmarkValueObserverObservationInt64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - _ = fix.meter.RegisterInt64Observer("test.observer", func(result metric.Int64ObserverResult) { + _ = fix.meter.RegisterInt64ValueObserver("test.valueobserver", func(result metric.Int64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((int64)(i), labs...) } @@ -449,11 +449,11 @@ func BenchmarkObserverObservationInt64(b *testing.B) { fix.accumulator.Collect(ctx) } -func BenchmarkObserverObservationFloat64(b *testing.B) { +func BenchmarkValueObserverObservationFloat64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - _ = fix.meter.RegisterFloat64Observer("test.observer", func(result metric.Float64ObserverResult) { + _ = fix.meter.RegisterFloat64ValueObserver("test.valueobserver", func(result metric.Float64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((float64)(i), labs...) } diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index e26aa630a..686e168a5 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -291,20 +291,20 @@ func TestObserverCollection(t *testing.T) { sdk := metricsdk.NewAccumulator(integrator) meter := metric.WrapMeterImpl(sdk, "test") - _ = Must(meter).RegisterFloat64Observer("float.observer", func(result metric.Float64ObserverResult) { + _ = Must(meter).RegisterFloat64ValueObserver("float.valueobserver", func(result metric.Float64ObserverResult) { result.Observe(1, kv.String("A", "B")) // last value wins result.Observe(-1, kv.String("A", "B")) result.Observe(-1, kv.String("C", "D")) }) - _ = Must(meter).RegisterInt64Observer("int.observer", func(result metric.Int64ObserverResult) { + _ = Must(meter).RegisterInt64ValueObserver("int.valueobserver", func(result metric.Int64ObserverResult) { result.Observe(-1, kv.String("A", "B")) result.Observe(1) // last value wins result.Observe(1, kv.String("A", "B")) result.Observe(1) }) - _ = Must(meter).RegisterInt64Observer("empty.observer", func(result metric.Int64ObserverResult) { + _ = Must(meter).RegisterInt64ValueObserver("empty.valueobserver", func(result metric.Int64ObserverResult) { }) collected := sdk.Collect(ctx) @@ -317,10 +317,10 @@ func TestObserverCollection(t *testing.T) { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ - "float.observer/A=B": -1, - "float.observer/C=D": -1, - "int.observer/": 1, - "int.observer/A=B": 1, + "float.valueobserver/A=B": -1, + "float.valueobserver/C=D": -1, + "int.valueobserver/": 1, + "int.valueobserver/A=B": 1, }, out.Map) } @@ -333,8 +333,8 @@ func TestObserverBatch(t *testing.T) { sdk := metricsdk.NewAccumulator(integrator) meter := metric.WrapMeterImpl(sdk, "test") - var floatObs metric.Float64Observer - var intObs metric.Int64Observer + var floatObs metric.Float64ValueObserver + var intObs metric.Int64ValueObserver var batch = Must(meter).NewBatchObserver( func(result metric.BatchObserverResult) { result.Observe( @@ -358,8 +358,8 @@ func TestObserverBatch(t *testing.T) { intObs.Observation(1), ) }) - floatObs = batch.RegisterFloat64Observer("float.observer") - intObs = batch.RegisterInt64Observer("int.observer") + floatObs = batch.RegisterFloat64ValueObserver("float.valueobserver") + intObs = batch.RegisterInt64ValueObserver("int.valueobserver") collected := sdk.Collect(ctx) @@ -371,10 +371,10 @@ func TestObserverBatch(t *testing.T) { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ - "float.observer/A=B": -1, - "float.observer/C=D": -1, - "int.observer/": 1, - "int.observer/A=B": 1, + "float.valueobserver/A=B": -1, + "float.valueobserver/C=D": -1, + "int.valueobserver/": 1, + "int.valueobserver/A=B": 1, }, out.Map) } diff --git a/sdk/metric/integrator/test/test.go b/sdk/metric/integrator/test/test.go index 383c77645..5f18425a8 100644 --- a/sdk/metric/integrator/test/test.go +++ b/sdk/metric/integrator/test/test.go @@ -47,9 +47,9 @@ type ( var ( // LastValueADesc and LastValueBDesc group by "G" LastValueADesc = metric.NewDescriptor( - "lastvalue.a", metric.ObserverKind, metric.Int64NumberKind) + "lastvalue.a", metric.ValueObserverKind, metric.Int64NumberKind) LastValueBDesc = metric.NewDescriptor( - "lastvalue.b", metric.ObserverKind, metric.Int64NumberKind) + "lastvalue.b", metric.ValueObserverKind, metric.Int64NumberKind) // CounterADesc and CounterBDesc group by "C" CounterADesc = metric.NewDescriptor( "sum.a", metric.CounterKind, metric.Int64NumberKind) @@ -92,7 +92,7 @@ func (*testAggregationSelector) AggregatorFor(desc *metric.Descriptor) export.Ag switch desc.MetricKind() { case metric.CounterKind: return sum.New() - case metric.ObserverKind: + case metric.ValueObserverKind: return lastvalue.New() default: panic("Invalid descriptor MetricKind for this test") diff --git a/sdk/metric/selector/simple/simple.go b/sdk/metric/selector/simple/simple.go index 3f7517585..b27d0af47 100644 --- a/sdk/metric/selector/simple/simple.go +++ b/sdk/metric/selector/simple/simple.go @@ -81,7 +81,7 @@ func NewWithHistogramDistribution(boundaries []metric.Number) export.Aggregation func (selectorInexpensive) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { switch descriptor.MetricKind() { - case metric.ObserverKind: + case metric.ValueObserverKind: fallthrough case metric.ValueRecorderKind: return minmaxsumcount.New(descriptor) @@ -92,7 +92,7 @@ func (selectorInexpensive) AggregatorFor(descriptor *metric.Descriptor) export.A func (s selectorSketch) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { switch descriptor.MetricKind() { - case metric.ObserverKind: + case metric.ValueObserverKind: fallthrough case metric.ValueRecorderKind: return ddsketch.New(s.config, descriptor) @@ -103,7 +103,7 @@ func (s selectorSketch) AggregatorFor(descriptor *metric.Descriptor) export.Aggr func (selectorExact) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { switch descriptor.MetricKind() { - case metric.ObserverKind: + case metric.ValueObserverKind: fallthrough case metric.ValueRecorderKind: return array.New() @@ -114,7 +114,7 @@ func (selectorExact) AggregatorFor(descriptor *metric.Descriptor) export.Aggrega func (s selectorHistogram) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { switch descriptor.MetricKind() { - case metric.ObserverKind: + case metric.ValueObserverKind: fallthrough case metric.ValueRecorderKind: return histogram.New(descriptor, s.boundaries) diff --git a/sdk/metric/selector/simple/simple_test.go b/sdk/metric/selector/simple/simple_test.go index a80c62ac2..018d49efa 100644 --- a/sdk/metric/selector/simple/simple_test.go +++ b/sdk/metric/selector/simple/simple_test.go @@ -31,33 +31,33 @@ import ( var ( testCounterDesc = metric.NewDescriptor("counter", metric.CounterKind, metric.Int64NumberKind) testValueRecorderDesc = metric.NewDescriptor("valuerecorder", metric.ValueRecorderKind, metric.Int64NumberKind) - testObserverDesc = metric.NewDescriptor("observer", metric.ObserverKind, metric.Int64NumberKind) + testValueObserverDesc = metric.NewDescriptor("valueobserver", metric.ValueObserverKind, metric.Int64NumberKind) ) func TestInexpensiveDistribution(t *testing.T) { inex := simple.NewWithInexpensiveDistribution() require.NotPanics(t, func() { _ = inex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) }) require.NotPanics(t, func() { _ = inex.AggregatorFor(&testValueRecorderDesc).(*minmaxsumcount.Aggregator) }) - require.NotPanics(t, func() { _ = inex.AggregatorFor(&testObserverDesc).(*minmaxsumcount.Aggregator) }) + require.NotPanics(t, func() { _ = inex.AggregatorFor(&testValueObserverDesc).(*minmaxsumcount.Aggregator) }) } func TestSketchDistribution(t *testing.T) { sk := simple.NewWithSketchDistribution(ddsketch.NewDefaultConfig()) require.NotPanics(t, func() { _ = sk.AggregatorFor(&testCounterDesc).(*sum.Aggregator) }) require.NotPanics(t, func() { _ = sk.AggregatorFor(&testValueRecorderDesc).(*ddsketch.Aggregator) }) - require.NotPanics(t, func() { _ = sk.AggregatorFor(&testObserverDesc).(*ddsketch.Aggregator) }) + require.NotPanics(t, func() { _ = sk.AggregatorFor(&testValueObserverDesc).(*ddsketch.Aggregator) }) } func TestExactDistribution(t *testing.T) { ex := simple.NewWithExactDistribution() require.NotPanics(t, func() { _ = ex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) }) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueRecorderDesc).(*array.Aggregator) }) - require.NotPanics(t, func() { _ = ex.AggregatorFor(&testObserverDesc).(*array.Aggregator) }) + require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueObserverDesc).(*array.Aggregator) }) } func TestHistogramDistribution(t *testing.T) { ex := simple.NewWithHistogramDistribution([]metric.Number{}) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) }) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueRecorderDesc).(*histogram.Aggregator) }) - require.NotPanics(t, func() { _ = ex.AggregatorFor(&testObserverDesc).(*histogram.Aggregator) }) + require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueObserverDesc).(*histogram.Aggregator) }) } From 69da3056f24170ccbfcd81031819fa36025e02a4 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 18 May 2020 17:44:28 -0700 Subject: [PATCH 060/108] Move Resource into the metric export Record (#739) * Checkpoint * Tests pass --- exporters/metric/prometheus/example_test.go | 2 +- exporters/metric/prometheus/prometheus.go | 5 +- .../metric/prometheus/prometheus_test.go | 4 +- exporters/metric/stdout/stdout.go | 5 +- exporters/metric/stdout/stdout_test.go | 48 +++++----- exporters/metric/test/test.go | 13 ++- exporters/otlp/internal/transform/metric.go | 8 +- exporters/otlp/otlp.go | 5 +- exporters/otlp/otlp_metric_test.go | 11 +-- sdk/export/metric/metric.go | 14 ++- sdk/metric/config.go | 19 ++++ sdk/metric/controller/push/push.go | 13 +-- sdk/metric/controller/push/push_test.go | 25 +++-- sdk/metric/correct_test.go | 94 +++++++------------ sdk/metric/integrator/simple/simple.go | 6 ++ sdk/metric/integrator/simple/simple_test.go | 36 +++---- sdk/metric/integrator/test/test.go | 11 ++- sdk/metric/sdk.go | 7 +- 18 files changed, 172 insertions(+), 154 deletions(-) diff --git a/exporters/metric/prometheus/example_test.go b/exporters/metric/prometheus/example_test.go index 81e741a38..1a15e38de 100644 --- a/exporters/metric/prometheus/example_test.go +++ b/exporters/metric/prometheus/example_test.go @@ -66,7 +66,7 @@ func ExampleNewExportPipeline() { // Simulate a push meterImpl.Collect(ctx) - err = exporter.Export(ctx, nil, integrator.CheckpointSet()) + err = exporter.Export(ctx, integrator.CheckpointSet()) if err != nil { panic(err) } diff --git a/exporters/metric/prometheus/prometheus.go b/exporters/metric/prometheus/prometheus.go index 4d615fdf4..86bedf02a 100644 --- a/exporters/metric/prometheus/prometheus.go +++ b/exporters/metric/prometheus/prometheus.go @@ -32,7 +32,6 @@ import ( "go.opentelemetry.io/otel/sdk/metric/controller/push" integrator "go.opentelemetry.io/otel/sdk/metric/integrator/simple" "go.opentelemetry.io/otel/sdk/metric/selector/simple" - "go.opentelemetry.io/otel/sdk/resource" ) // Exporter is an implementation of metric.Exporter that sends metrics to @@ -169,8 +168,7 @@ func NewExportPipeline(config Config, period time.Duration) (*push.Controller, h } // Export exports the provide metric record to prometheus. -func (e *Exporter) Export(_ context.Context, _ *resource.Resource, checkpointSet export.CheckpointSet) error { - // TODO: Use the resource value in this exporter. +func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error { e.snapshot = checkpointSet return nil } @@ -211,6 +209,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) { err := c.exp.snapshot.ForEach(func(record export.Record) error { agg := record.Aggregator() numberKind := record.Descriptor().NumberKind() + // TODO: Use the resource value in this record. labels := labelValues(record.Labels()) desc := c.toDesc(&record) diff --git a/exporters/metric/prometheus/prometheus_test.go b/exporters/metric/prometheus/prometheus_test.go index f30813b06..a95d09ad3 100644 --- a/exporters/metric/prometheus/prometheus_test.go +++ b/exporters/metric/prometheus/prometheus_test.go @@ -39,7 +39,7 @@ func TestPrometheusExporter(t *testing.T) { } var expected []string - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(nil) counter := metric.NewDescriptor( "counter", metric.CounterKind, metric.Float64NumberKind) @@ -117,7 +117,7 @@ func TestPrometheusExporter(t *testing.T) { } func compareExport(t *testing.T, exporter *prometheus.Exporter, checkpointSet *test.CheckpointSet, expected []string) { - err := exporter.Export(context.Background(), nil, checkpointSet) + err := exporter.Export(context.Background(), checkpointSet) require.Nil(t, err) rec := httptest.NewRecorder() diff --git a/exporters/metric/stdout/stdout.go b/exporters/metric/stdout/stdout.go index 5e8b513a5..433288503 100644 --- a/exporters/metric/stdout/stdout.go +++ b/exporters/metric/stdout/stdout.go @@ -25,7 +25,6 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/label" - "go.opentelemetry.io/otel/sdk/resource" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregator" @@ -145,18 +144,18 @@ func NewExportPipeline(config Config, period time.Duration, opts ...push.Option) return pusher, nil } -func (e *Exporter) Export(_ context.Context, resource *resource.Resource, checkpointSet export.CheckpointSet) error { +func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error { var aggError error var batch expoBatch if !e.config.DoNotPrintTime { ts := time.Now() batch.Timestamp = &ts } - encodedResource := resource.Encoded(e.config.LabelEncoder) aggError = checkpointSet.ForEach(func(record export.Record) error { desc := record.Descriptor() agg := record.Aggregator() kind := desc.NumberKind() + encodedResource := record.Resource().Encoded(e.config.LabelEncoder) var expose expoLine diff --git a/exporters/metric/stdout/stdout_test.go b/exporters/metric/stdout/stdout_test.go index 2dee68e55..1d5805ba2 100644 --- a/exporters/metric/stdout/stdout_test.go +++ b/exporters/metric/stdout/stdout_test.go @@ -44,10 +44,11 @@ type testFixture struct { ctx context.Context exporter *stdout.Exporter output *bytes.Buffer - resource *resource.Resource } -func newFixture(t *testing.T, resource *resource.Resource, config stdout.Config) testFixture { +var testResource = resource.New(kv.String("R", "V")) + +func newFixture(t *testing.T, config stdout.Config) testFixture { buf := &bytes.Buffer{} config.Writer = buf config.DoNotPrintTime = true @@ -60,7 +61,6 @@ func newFixture(t *testing.T, resource *resource.Resource, config stdout.Config) ctx: context.Background(), exporter: exp, output: buf, - resource: resource, } } @@ -69,7 +69,7 @@ func (fix testFixture) Output() string { } func (fix testFixture) Export(checkpointSet export.CheckpointSet) { - err := fix.exporter.Export(fix.ctx, fix.resource, checkpointSet) + err := fix.exporter.Export(fix.ctx, checkpointSet) if err != nil { fix.t.Error("export failed: ", err) } @@ -95,7 +95,7 @@ func TestStdoutTimestamp(t *testing.T) { before := time.Now() - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(testResource) ctx := context.Background() desc := metric.NewDescriptor("test.name", metric.ValueObserverKind, metric.Int64NumberKind) @@ -105,7 +105,7 @@ func TestStdoutTimestamp(t *testing.T) { checkpointSet.Add(&desc, lvagg) - if err := exporter.Export(ctx, nil, checkpointSet); err != nil { + if err := exporter.Export(ctx, checkpointSet); err != nil { t.Fatal("Unexpected export error: ", err) } @@ -139,9 +139,9 @@ func TestStdoutTimestamp(t *testing.T) { } func TestStdoutCounterFormat(t *testing.T) { - fix := newFixture(t, nil, stdout.Config{}) + fix := newFixture(t, stdout.Config{}) - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(testResource) desc := metric.NewDescriptor("test.name", metric.CounterKind, metric.Int64NumberKind) cagg := sum.New() @@ -152,13 +152,13 @@ func TestStdoutCounterFormat(t *testing.T) { fix.Export(checkpointSet) - require.Equal(t, `{"updates":[{"name":"test.name{A=B,C=D}","sum":123}]}`, fix.Output()) + require.Equal(t, `{"updates":[{"name":"test.name{R=V,A=B,C=D}","sum":123}]}`, fix.Output()) } func TestStdoutLastValueFormat(t *testing.T) { - fix := newFixture(t, nil, stdout.Config{}) + fix := newFixture(t, stdout.Config{}) - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(testResource) desc := metric.NewDescriptor("test.name", metric.ValueObserverKind, metric.Float64NumberKind) lvagg := lastvalue.New() @@ -169,13 +169,13 @@ func TestStdoutLastValueFormat(t *testing.T) { fix.Export(checkpointSet) - require.Equal(t, `{"updates":[{"name":"test.name{A=B,C=D}","last":123.456}]}`, fix.Output()) + require.Equal(t, `{"updates":[{"name":"test.name{R=V,A=B,C=D}","last":123.456}]}`, fix.Output()) } func TestStdoutMinMaxSumCount(t *testing.T) { - fix := newFixture(t, nil, stdout.Config{}) + fix := newFixture(t, stdout.Config{}) - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(testResource) desc := metric.NewDescriptor("test.name", metric.ValueRecorderKind, metric.Float64NumberKind) magg := minmaxsumcount.New(&desc) @@ -187,15 +187,15 @@ func TestStdoutMinMaxSumCount(t *testing.T) { fix.Export(checkpointSet) - require.Equal(t, `{"updates":[{"name":"test.name{A=B,C=D}","min":123.456,"max":876.543,"sum":999.999,"count":2}]}`, fix.Output()) + require.Equal(t, `{"updates":[{"name":"test.name{R=V,A=B,C=D}","min":123.456,"max":876.543,"sum":999.999,"count":2}]}`, fix.Output()) } func TestStdoutValueRecorderFormat(t *testing.T) { - fix := newFixture(t, nil, stdout.Config{ + fix := newFixture(t, stdout.Config{ PrettyPrint: true, }) - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(testResource) desc := metric.NewDescriptor("test.name", metric.ValueRecorderKind, metric.Float64NumberKind) magg := array.New() @@ -213,7 +213,7 @@ func TestStdoutValueRecorderFormat(t *testing.T) { require.Equal(t, `{ "updates": [ { - "name": "test.name{A=B,C=D}", + "name": "test.name{R=V,A=B,C=D}", "min": 0.5, "max": 999.5, "sum": 500000, @@ -247,9 +247,9 @@ func TestStdoutNoData(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - fix := newFixture(t, nil, stdout.Config{}) + fix := newFixture(t, stdout.Config{}) - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(testResource) magg := tc magg.Checkpoint(fix.ctx, &desc) @@ -264,9 +264,9 @@ func TestStdoutNoData(t *testing.T) { } func TestStdoutLastValueNotSet(t *testing.T) { - fix := newFixture(t, nil, stdout.Config{}) + fix := newFixture(t, stdout.Config{}) - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(testResource) desc := metric.NewDescriptor("test.name", metric.ValueObserverKind, metric.Float64NumberKind) lvagg := lastvalue.New() @@ -314,9 +314,9 @@ func TestStdoutResource(t *testing.T) { } for _, tc := range testCases { - fix := newFixture(t, tc.res, stdout.Config{}) + fix := newFixture(t, stdout.Config{}) - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(tc.res) desc := metric.NewDescriptor("test.name", metric.ValueObserverKind, metric.Float64NumberKind) lvagg := lastvalue.New() diff --git a/exporters/metric/test/test.go b/exporters/metric/test/test.go index bc49cd9c9..cb99b6489 100644 --- a/exporters/metric/test/test.go +++ b/exporters/metric/test/test.go @@ -27,6 +27,7 @@ import ( "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" + "go.opentelemetry.io/otel/sdk/resource" ) type mapkey struct { @@ -35,15 +36,17 @@ type mapkey struct { } type CheckpointSet struct { - records map[mapkey]export.Record - updates []export.Record + records map[mapkey]export.Record + resource *resource.Resource + updates []export.Record } // NewCheckpointSet returns a test CheckpointSet that new records could be added. // Records are grouped by their encoded labels. -func NewCheckpointSet() *CheckpointSet { +func NewCheckpointSet(resource *resource.Resource) *CheckpointSet { return &CheckpointSet{ - records: make(map[mapkey]export.Record), + records: make(map[mapkey]export.Record), + resource: resource, } } @@ -67,7 +70,7 @@ func (p *CheckpointSet) Add(desc *metric.Descriptor, newAgg export.Aggregator, l return record.Aggregator(), false } - rec := export.NewRecord(desc, &elabels, newAgg) + rec := export.NewRecord(desc, &elabels, p.resource, newAgg) p.updates = append(p.updates, rec) p.records[key] = rec return newAgg, true diff --git a/exporters/otlp/internal/transform/metric.go b/exporters/otlp/internal/transform/metric.go index f9dd696bd..318378961 100644 --- a/exporters/otlp/internal/transform/metric.go +++ b/exporters/otlp/internal/transform/metric.go @@ -61,7 +61,7 @@ type result struct { // CheckpointSet transforms all records contained in a checkpoint into // batched OTLP ResourceMetrics. -func CheckpointSet(ctx context.Context, resource *resource.Resource, cps export.CheckpointSet, numWorkers uint) ([]*metricpb.ResourceMetrics, error) { +func CheckpointSet(ctx context.Context, cps export.CheckpointSet, numWorkers uint) ([]*metricpb.ResourceMetrics, error) { records, errc := source(ctx, cps) // Start a fixed number of goroutines to transform records. @@ -71,7 +71,7 @@ func CheckpointSet(ctx context.Context, resource *resource.Resource, cps export. for i := uint(0); i < numWorkers; i++ { go func() { defer wg.Done() - transformer(ctx, resource, records, transformed) + transformer(ctx, records, transformed) }() } go func() { @@ -116,7 +116,7 @@ func source(ctx context.Context, cps export.CheckpointSet) (<-chan export.Record // transformer transforms records read from the passed in chan into // OTLP Metrics which are sent on the out chan. -func transformer(ctx context.Context, resource *resource.Resource, in <-chan export.Record, out chan<- result) { +func transformer(ctx context.Context, in <-chan export.Record, out chan<- result) { for r := range in { m, err := Record(r) // Propagate errors, but do not send empty results. @@ -124,7 +124,7 @@ func transformer(ctx context.Context, resource *resource.Resource, in <-chan exp continue } res := result{ - Resource: resource, + Resource: r.Resource(), Library: r.Descriptor().LibraryName(), Metric: m, Err: err, diff --git a/exporters/otlp/otlp.go b/exporters/otlp/otlp.go index 0c06676be..d0e83f944 100644 --- a/exporters/otlp/otlp.go +++ b/exporters/otlp/otlp.go @@ -31,7 +31,6 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/internal/transform" metricsdk "go.opentelemetry.io/otel/sdk/export/metric" tracesdk "go.opentelemetry.io/otel/sdk/export/trace" - "go.opentelemetry.io/otel/sdk/resource" ) type Exporter struct { @@ -212,7 +211,7 @@ func (e *Exporter) Stop() error { // Export implements the "go.opentelemetry.io/otel/sdk/export/metric".Exporter // interface. It transforms and batches metric Records into OTLP Metrics and // transmits them to the configured collector. -func (e *Exporter) Export(parent context.Context, resource *resource.Resource, cps metricsdk.CheckpointSet) error { +func (e *Exporter) Export(parent context.Context, cps metricsdk.CheckpointSet) error { // Unify the parent context Done signal with the exporter stopCh. ctx, cancel := context.WithCancel(parent) defer cancel() @@ -224,7 +223,7 @@ func (e *Exporter) Export(parent context.Context, resource *resource.Resource, c } }(ctx, cancel) - rms, err := transform.CheckpointSet(ctx, resource, cps, e.c.numWorkers) + rms, err := transform.CheckpointSet(ctx, cps, e.c.numWorkers) if err != nil { return err } diff --git a/exporters/otlp/otlp_metric_test.go b/exporters/otlp/otlp_metric_test.go index 4d72d541a..db47cb157 100644 --- a/exporters/otlp/otlp_metric_test.go +++ b/exporters/otlp/otlp_metric_test.go @@ -659,11 +659,10 @@ func runMetricExportTest(t *testing.T, exp *Exporter, rs []record, expected []me equiv := r.resource.Equivalent() resources[equiv] = r.resource - recs[equiv] = append(recs[equiv], metricsdk.NewRecord(&desc, &labs, agg)) + recs[equiv] = append(recs[equiv], metricsdk.NewRecord(&desc, &labs, r.resource, agg)) } - for equiv, records := range recs { - resource := resources[equiv] - assert.NoError(t, exp.Export(context.Background(), resource, checkpointSet{records: records})) + for _, records := range recs { + assert.NoError(t, exp.Export(context.Background(), checkpointSet{records: records})) } // assert.ElementsMatch does not equate nested slices of different order, @@ -713,8 +712,6 @@ func TestEmptyMetricExport(t *testing.T) { exp.metricExporter = msc exp.started = true - resource := resource.New(kv.String("R", "S")) - for _, test := range []struct { records []metricsdk.Record want []metricpb.ResourceMetrics @@ -729,7 +726,7 @@ func TestEmptyMetricExport(t *testing.T) { }, } { msc.Reset() - require.NoError(t, exp.Export(context.Background(), resource, checkpointSet{records: test.records})) + require.NoError(t, exp.Export(context.Background(), checkpointSet{records: test.records})) assert.Equal(t, test.want, msc.ResourceMetrics()) } } diff --git a/sdk/export/metric/metric.go b/sdk/export/metric/metric.go index c2d583173..86f195aa1 100644 --- a/sdk/export/metric/metric.go +++ b/sdk/export/metric/metric.go @@ -154,12 +154,9 @@ type Exporter interface { // The Context comes from the controller that initiated // collection. // - // The Resource contains common attributes that apply to all - // metric events in the SDK. - // // The CheckpointSet interface refers to the Integrator that just // completed collection. - Export(context.Context, *resource.Resource, CheckpointSet) error + Export(context.Context, CheckpointSet) error } // CheckpointSet allows a controller to access a complete checkpoint of @@ -183,16 +180,18 @@ type CheckpointSet interface { type Record struct { descriptor *metric.Descriptor labels *label.Set + resource *resource.Resource aggregator Aggregator } // NewRecord allows Integrator implementations to construct export // records. The Descriptor, Labels, and Aggregator represent // aggregate metric events received over a single collection period. -func NewRecord(descriptor *metric.Descriptor, labels *label.Set, aggregator Aggregator) Record { +func NewRecord(descriptor *metric.Descriptor, labels *label.Set, resource *resource.Resource, aggregator Aggregator) Record { return Record{ descriptor: descriptor, labels: labels, + resource: resource, aggregator: aggregator, } } @@ -213,3 +212,8 @@ func (r Record) Descriptor() *metric.Descriptor { func (r Record) Labels() *label.Set { return r.labels } + +// Resource contains common attributes that apply to this metric event. +func (r Record) Resource() *resource.Resource { + return r.resource +} diff --git a/sdk/metric/config.go b/sdk/metric/config.go index 44f06fbe3..dbdbc57f5 100644 --- a/sdk/metric/config.go +++ b/sdk/metric/config.go @@ -14,6 +14,8 @@ package metric +import "go.opentelemetry.io/otel/sdk/resource" + // Config contains configuration for an SDK. type Config struct { // ErrorHandler is the function called when the SDK encounters an error. @@ -21,6 +23,10 @@ type Config struct { // This option can be overridden after instantiation of the SDK // with the `SetErrorHandler` method. ErrorHandler ErrorHandler + + // Resource describes all the metric records processed by the + // Accumulator. + Resource *resource.Resource } // Option is the interface that applies the value to a configuration option. @@ -39,3 +45,16 @@ type errorHandlerOption ErrorHandler func (o errorHandlerOption) Apply(config *Config) { config.ErrorHandler = ErrorHandler(o) } + +// WithResource sets the Resource configuration option of a Config. +func WithResource(res *resource.Resource) Option { + return resourceOption{res} +} + +type resourceOption struct { + *resource.Resource +} + +func (o resourceOption) Apply(config *Config) { + config.Resource = o.Resource +} diff --git a/sdk/metric/controller/push/push.go b/sdk/metric/controller/push/push.go index 0e00ce5fd..ea347d3fa 100644 --- a/sdk/metric/controller/push/push.go +++ b/sdk/metric/controller/push/push.go @@ -31,7 +31,7 @@ type Controller struct { lock sync.Mutex collectLock sync.Mutex accumulator *sdk.Accumulator - resource *resource.Resource + provider *registry.Provider errorHandler sdk.ErrorHandler integrator export.Integrator exporter export.Exporter @@ -40,7 +40,6 @@ type Controller struct { period time.Duration ticker Ticker clock Clock - provider *registry.Provider } // Several types below are created to match "github.com/benbjohnson/clock" @@ -71,15 +70,17 @@ var _ Ticker = realTicker{} // configuration options to configure an SDK with periodic collection. // The integrator itself is configured with the aggregation selector policy. func New(integrator export.Integrator, exporter export.Exporter, period time.Duration, opts ...Option) *Controller { - c := &Config{ErrorHandler: sdk.DefaultErrorHandler} + c := &Config{ + ErrorHandler: sdk.DefaultErrorHandler, + Resource: resource.Empty(), + } for _, opt := range opts { opt.Apply(c) } - impl := sdk.NewAccumulator(integrator, sdk.WithErrorHandler(c.ErrorHandler)) + impl := sdk.NewAccumulator(integrator, sdk.WithErrorHandler(c.ErrorHandler), sdk.WithResource(c.Resource)) return &Controller{ accumulator: impl, - resource: c.Resource, provider: registry.NewProvider(impl), errorHandler: c.ErrorHandler, integrator: integrator, @@ -166,7 +167,7 @@ func (c *Controller) tick() { mtx: &c.collectLock, delegate: c.integrator.CheckpointSet(), } - err := c.exporter.Export(ctx, c.resource, checkpointSet) + err := c.exporter.Export(ctx, checkpointSet) c.integrator.FinishedCollection() if err != nil { diff --git a/sdk/metric/controller/push/push_test.go b/sdk/metric/controller/push/push_test.go index fd200f4d0..dc4ae94c6 100644 --- a/sdk/metric/controller/push/push_test.go +++ b/sdk/metric/controller/push/push_test.go @@ -25,6 +25,8 @@ import ( "github.com/benbjohnson/clock" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/api/kv" + "go.opentelemetry.io/otel/api/label" "go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/exporters/metric/test" export "go.opentelemetry.io/otel/sdk/export/metric" @@ -42,6 +44,8 @@ type testIntegrator struct { finishes int } +var testResource = resource.New(kv.String("R", "V")) + type testExporter struct { t *testing.T lock sync.Mutex @@ -68,7 +72,7 @@ var _ push.Clock = mockClock{} var _ push.Ticker = mockTicker{} func newFixture(t *testing.T) testFixture { - checkpointSet := test.NewCheckpointSet() + checkpointSet := test.NewCheckpointSet(testResource) integrator := &testIntegrator{ t: t, @@ -115,7 +119,7 @@ func (b *testIntegrator) getCounts() (checkpoints, finishes int) { return b.checkpoints, b.finishes } -func (e *testExporter) Export(_ context.Context, _ *resource.Resource, checkpointSet export.CheckpointSet) error { +func (e *testExporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error { e.lock.Lock() defer e.lock.Unlock() e.exports++ @@ -213,6 +217,7 @@ func TestPushTicker(t *testing.T) { require.Equal(t, 1, exports) require.Equal(t, 1, len(records)) require.Equal(t, "counter", records[0].Descriptor().Name()) + require.Equal(t, "R=V", records[0].Resource().Encoded(label.DefaultEncoder())) sum, err := records[0].Aggregator().(aggregator.Sum).Sum() require.Equal(t, int64(3), sum.AsInt64()) @@ -232,6 +237,7 @@ func TestPushTicker(t *testing.T) { require.Equal(t, 2, exports) require.Equal(t, 1, len(records)) require.Equal(t, "counter", records[0].Descriptor().Name()) + require.Equal(t, "R=V", records[0].Resource().Encoded(label.DefaultEncoder())) sum, err = records[0].Aggregator().(aggregator.Sum).Sum() require.Equal(t, int64(7), sum.AsInt64()) @@ -256,8 +262,8 @@ func TestPushExportError(t *testing.T) { expectedDescriptors []string expectedError error }{ - {"errNone", nil, []string{"counter1", "counter2"}, nil}, - {"errNoData", aggregator.ErrNoData, []string{"counter2"}, nil}, + {"errNone", nil, []string{"counter1{R=V,X=Y}", "counter2{R=V,}"}, nil}, + {"errNoData", aggregator.ErrNoData, []string{"counter2{R=V,}"}, nil}, {"errUnexpected", errAggregator, []string{}, errAggregator}, } for _, tt := range tests { @@ -287,7 +293,7 @@ func TestPushExportError(t *testing.T) { p.Start() runtime.Gosched() - counter1.Add(ctx, 3) + counter1.Add(ctx, 3, kv.String("X", "Y")) counter2.Add(ctx, 5) require.Equal(t, 0, fix.exporter.exports) @@ -311,11 +317,16 @@ func TestPushExportError(t *testing.T) { lock.Unlock() require.Equal(t, len(tt.expectedDescriptors), len(records)) for _, r := range records { - require.Contains(t, tt.expectedDescriptors, r.Descriptor().Name()) + require.Contains(t, tt.expectedDescriptors, + fmt.Sprintf("%s{%s,%s}", + r.Descriptor().Name(), + r.Resource().Encoded(label.DefaultEncoder()), + r.Labels().Encoded(label.DefaultEncoder()), + ), + ) } p.Stop() - }) } } diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index 686e168a5..519bf342b 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -33,9 +33,11 @@ import ( "go.opentelemetry.io/otel/sdk/metric/aggregator/array" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" batchTest "go.opentelemetry.io/otel/sdk/metric/integrator/test" + "go.opentelemetry.io/otel/sdk/resource" ) var Must = metric.Must +var testResource = resource.New(kv.String("R", "V")) type correctnessIntegrator struct { newAggCount int64 @@ -45,6 +47,15 @@ type correctnessIntegrator struct { records []export.Record } +func newSDK(t *testing.T) (metric.Meter, *metricsdk.Accumulator, *correctnessIntegrator) { + integrator := &correctnessIntegrator{ + t: t, + } + accum := metricsdk.NewAccumulator(integrator, metricsdk.WithResource(testResource)) + meter := metric.WrapMeterImpl(accum, "test") + return meter, accum, integrator +} + func (cb *correctnessIntegrator) AggregatorFor(descriptor *metric.Descriptor) (agg export.Aggregator) { name := descriptor.Name() @@ -77,11 +88,7 @@ func (cb *correctnessIntegrator) Process(_ context.Context, record export.Record func TestInputRangeTestCounter(t *testing.T) { ctx := context.Background() - integrator := &correctnessIntegrator{ - t: t, - } - sdk := metricsdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(sdk, "test") + meter, sdk, integrator := newSDK(t) var sdkErr error sdk.SetErrorHandler(func(handleErr error) { @@ -109,11 +116,7 @@ func TestInputRangeTestCounter(t *testing.T) { func TestInputRangeTestValueRecorder(t *testing.T) { ctx := context.Background() - integrator := &correctnessIntegrator{ - t: t, - } - sdk := metricsdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(sdk, "test") + meter, sdk, integrator := newSDK(t) var sdkErr error sdk.SetErrorHandler(func(handleErr error) { @@ -144,11 +147,7 @@ func TestInputRangeTestValueRecorder(t *testing.T) { func TestDisabledInstrument(t *testing.T) { ctx := context.Background() - integrator := &correctnessIntegrator{ - t: t, - } - sdk := metricsdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(sdk, "test") + meter, sdk, integrator := newSDK(t) valuerecorder := Must(meter).NewFloat64ValueRecorder("name.disabled") @@ -161,12 +160,7 @@ func TestDisabledInstrument(t *testing.T) { func TestRecordNaN(t *testing.T) { ctx := context.Background() - integrator := &correctnessIntegrator{ - t: t, - } - - sdk := metricsdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(sdk, "test") + meter, sdk, _ := newSDK(t) var sdkErr error sdk.SetErrorHandler(func(handleErr error) { @@ -181,11 +175,7 @@ func TestRecordNaN(t *testing.T) { func TestSDKLabelsDeduplication(t *testing.T) { ctx := context.Background() - integrator := &correctnessIntegrator{ - t: t, - } - sdk := metricsdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(sdk, "test") + meter, sdk, integrator := newSDK(t) counter := Must(meter).NewInt64Counter("counter") @@ -284,12 +274,7 @@ func TestDefaultLabelEncoder(t *testing.T) { func TestObserverCollection(t *testing.T) { ctx := context.Background() - integrator := &correctnessIntegrator{ - t: t, - } - - sdk := metricsdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(sdk, "test") + meter, sdk, integrator := newSDK(t) _ = Must(meter).RegisterFloat64ValueObserver("float.valueobserver", func(result metric.Float64ObserverResult) { result.Observe(1, kv.String("A", "B")) @@ -317,21 +302,16 @@ func TestObserverCollection(t *testing.T) { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ - "float.valueobserver/A=B": -1, - "float.valueobserver/C=D": -1, - "int.valueobserver/": 1, - "int.valueobserver/A=B": 1, + "float.valueobserver/A=B/R=V": -1, + "float.valueobserver/C=D/R=V": -1, + "int.valueobserver//R=V": 1, + "int.valueobserver/A=B/R=V": 1, }, out.Map) } func TestObserverBatch(t *testing.T) { ctx := context.Background() - integrator := &correctnessIntegrator{ - t: t, - } - - sdk := metricsdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(sdk, "test") + meter, sdk, integrator := newSDK(t) var floatObs metric.Float64ValueObserver var intObs metric.Int64ValueObserver @@ -371,21 +351,16 @@ func TestObserverBatch(t *testing.T) { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ - "float.valueobserver/A=B": -1, - "float.valueobserver/C=D": -1, - "int.valueobserver/": 1, - "int.valueobserver/A=B": 1, + "float.valueobserver/A=B/R=V": -1, + "float.valueobserver/C=D/R=V": -1, + "int.valueobserver//R=V": 1, + "int.valueobserver/A=B/R=V": 1, }, out.Map) } func TestRecordBatch(t *testing.T) { ctx := context.Background() - integrator := &correctnessIntegrator{ - t: t, - } - - sdk := metricsdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(sdk, "test") + meter, sdk, integrator := newSDK(t) counter1 := Must(meter).NewInt64Counter("int64.counter") counter2 := Must(meter).NewFloat64Counter("float64.counter") @@ -411,10 +386,10 @@ func TestRecordBatch(t *testing.T) { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ - "int64.counter/A=B,C=D": 1, - "float64.counter/A=B,C=D": 2, - "int64.valuerecorder/A=B,C=D": 3, - "float64.valuerecorder/A=B,C=D": 4, + "int64.counter/A=B,C=D/R=V": 1, + "float64.counter/A=B,C=D/R=V": 2, + "int64.valuerecorder/A=B,C=D/R=V": 3, + "float64.valuerecorder/A=B,C=D/R=V": 4, }, out.Map) } @@ -423,12 +398,7 @@ func TestRecordBatch(t *testing.T) { // that its encoded labels will be cached across collection intervals. func TestRecordPersistence(t *testing.T) { ctx := context.Background() - integrator := &correctnessIntegrator{ - t: t, - } - - sdk := metricsdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(sdk, "test") + meter, sdk, integrator := newSDK(t) c := Must(meter).NewFloat64Counter("sum.name") b := c.Bind(kv.String("bound", "true")) diff --git a/sdk/metric/integrator/simple/simple.go b/sdk/metric/integrator/simple/simple.go index 9a379c9fc..123361ff0 100644 --- a/sdk/metric/integrator/simple/simple.go +++ b/sdk/metric/integrator/simple/simple.go @@ -22,6 +22,7 @@ import ( "go.opentelemetry.io/otel/api/metric" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregator" + "go.opentelemetry.io/otel/sdk/resource" ) type ( @@ -34,11 +35,13 @@ type ( batchKey struct { descriptor *metric.Descriptor distinct label.Distinct + resource label.Distinct } batchValue struct { aggregator export.Aggregator labels *label.Set + resource *resource.Resource } batchMap map[batchKey]batchValue @@ -64,6 +67,7 @@ func (b *Integrator) Process(_ context.Context, record export.Record) error { key := batchKey{ descriptor: desc, distinct: record.Labels().Equivalent(), + resource: record.Resource().Equivalent(), } agg := record.Aggregator() value, ok := b.batchMap[key] @@ -91,6 +95,7 @@ func (b *Integrator) Process(_ context.Context, record export.Record) error { b.batchMap[key] = batchValue{ aggregator: agg, labels: record.Labels(), + resource: record.Resource(), } return nil } @@ -110,6 +115,7 @@ func (c batchMap) ForEach(f func(export.Record) error) error { if err := f(export.NewRecord( key.descriptor, value.labels, + value.resource, value.aggregator, )); err != nil && !errors.Is(err, aggregator.ErrNoData) { return err diff --git a/sdk/metric/integrator/simple/simple_test.go b/sdk/metric/integrator/simple/simple_test.go index 75a9b7a42..2b43fc8a8 100644 --- a/sdk/metric/integrator/simple/simple_test.go +++ b/sdk/metric/integrator/simple/simple_test.go @@ -68,18 +68,18 @@ func TestUngroupedStateless(t *testing.T) { // Output lastvalue should have only the "G=H" and "G=" keys. // Output counter should have only the "C=D" and "C=" keys. require.EqualValues(t, map[string]float64{ - "sum.a/C~D&G~H": 60, // labels1 - "sum.a/C~D&E~F": 20, // labels2 - "sum.a/": 40, // labels3 - "sum.b/C~D&G~H": 60, // labels1 - "sum.b/C~D&E~F": 20, // labels2 - "sum.b/": 40, // labels3 - "lastvalue.a/C~D&G~H": 50, // labels1 - "lastvalue.a/C~D&E~F": 20, // labels2 - "lastvalue.a/": 30, // labels3 - "lastvalue.b/C~D&G~H": 50, // labels1 - "lastvalue.b/C~D&E~F": 20, // labels2 - "lastvalue.b/": 30, // labels3 + "sum.a/C~D&G~H/R~V": 60, // labels1 + "sum.a/C~D&E~F/R~V": 20, // labels2 + "sum.a//R~V": 40, // labels3 + "sum.b/C~D&G~H/R~V": 60, // labels1 + "sum.b/C~D&E~F/R~V": 20, // labels2 + "sum.b//R~V": 40, // labels3 + "lastvalue.a/C~D&G~H/R~V": 50, // labels1 + "lastvalue.a/C~D&E~F/R~V": 20, // labels2 + "lastvalue.a//R~V": 30, // labels3 + "lastvalue.b/C~D&G~H/R~V": 50, // labels1 + "lastvalue.b/C~D&E~F/R~V": 20, // labels2 + "lastvalue.b//R~V": 30, // labels3 }, records.Map) // Verify that state was reset @@ -110,8 +110,8 @@ func TestUngroupedStateful(t *testing.T) { _ = checkpointSet.ForEach(records1.AddTo) require.EqualValues(t, map[string]float64{ - "sum.a/C~D&G~H": 10, // labels1 - "sum.b/C~D&G~H": 10, // labels1 + "sum.a/C~D&G~H/R~V": 10, // labels1 + "sum.b/C~D&G~H/R~V": 10, // labels1 }, records1.Map) // Test that state was NOT reset @@ -140,8 +140,8 @@ func TestUngroupedStateful(t *testing.T) { require.EqualValues(t, records1.Map, records3.Map) // Now process the second update - _ = b.Process(ctx, export.NewRecord(&test.CounterADesc, test.Labels1, caggA)) - _ = b.Process(ctx, export.NewRecord(&test.CounterBDesc, test.Labels1, caggB)) + _ = b.Process(ctx, export.NewRecord(&test.CounterADesc, test.Labels1, test.Resource, caggA)) + _ = b.Process(ctx, export.NewRecord(&test.CounterBDesc, test.Labels1, test.Resource, caggB)) checkpointSet = b.CheckpointSet() b.FinishedCollection() @@ -150,7 +150,7 @@ func TestUngroupedStateful(t *testing.T) { _ = checkpointSet.ForEach(records4.AddTo) require.EqualValues(t, map[string]float64{ - "sum.a/C~D&G~H": 30, - "sum.b/C~D&G~H": 30, + "sum.a/C~D&G~H/R~V": 30, + "sum.b/C~D&G~H/R~V": 30, }, records4.Map) } diff --git a/sdk/metric/integrator/test/test.go b/sdk/metric/integrator/test/test.go index 5f18425a8..7cc6288ab 100644 --- a/sdk/metric/integrator/test/test.go +++ b/sdk/metric/integrator/test/test.go @@ -26,6 +26,7 @@ import ( "go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" + "go.opentelemetry.io/otel/sdk/resource" ) type ( @@ -45,6 +46,9 @@ type ( ) var ( + // Resource is applied to all test records built in this package. + Resource = resource.New(kv.String("R", "V")) + // LastValueADesc and LastValueBDesc group by "G" LastValueADesc = metric.NewDescriptor( "lastvalue.a", metric.ValueObserverKind, metric.Int64NumberKind) @@ -133,12 +137,12 @@ func LastValueAgg(desc *metric.Descriptor, v int64) export.Aggregator { // Convenience method for building a test exported lastValue record. func NewLastValueRecord(desc *metric.Descriptor, labels *label.Set, value int64) export.Record { - return export.NewRecord(desc, labels, LastValueAgg(desc, value)) + return export.NewRecord(desc, labels, Resource, LastValueAgg(desc, value)) } // Convenience method for building a test exported counter record. func NewCounterRecord(desc *metric.Descriptor, labels *label.Set, value int64) export.Record { - return export.NewRecord(desc, labels, CounterAgg(desc, value)) + return export.NewRecord(desc, labels, Resource, CounterAgg(desc, value)) } // CounterAgg returns a checkpointed counter aggregator w/ the specified descriptor and value. @@ -154,7 +158,8 @@ func CounterAgg(desc *metric.Descriptor, v int64) export.Aggregator { // value to the output map. func (o Output) AddTo(rec export.Record) error { encoded := rec.Labels().Encoded(o.labelEncoder) - key := fmt.Sprint(rec.Descriptor().Name(), "/", encoded) + rencoded := rec.Resource().Encoded(o.labelEncoder) + key := fmt.Sprint(rec.Descriptor().Name(), "/", encoded, "/", rencoded) var value float64 if s, ok := rec.Aggregator().(aggregator.Sum); ok { diff --git a/sdk/metric/sdk.go b/sdk/metric/sdk.go index 9be8b17ca..8de0953e3 100644 --- a/sdk/metric/sdk.go +++ b/sdk/metric/sdk.go @@ -29,6 +29,7 @@ import ( internal "go.opentelemetry.io/otel/internal/metric" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregator" + "go.opentelemetry.io/otel/sdk/resource" ) type ( @@ -68,6 +69,9 @@ type ( // place for sorting during labels creation to avoid // allocation. It is cleared after use. asyncSortSlice label.Sortable + + // resource is applied to all records in this Accumulator. + resource *resource.Resource } syncInstrument struct { @@ -317,6 +321,7 @@ func NewAccumulator(integrator export.Integrator, opts ...Option) *Accumulator { integrator: integrator, errorHandler: c.ErrorHandler, asyncInstruments: internal.NewAsyncInstrumentState(c.ErrorHandler), + resource: c.Resource, } } @@ -472,7 +477,7 @@ func (m *Accumulator) checkpoint(ctx context.Context, descriptor *metric.Descrip } recorder.Checkpoint(ctx, descriptor) - exportRecord := export.NewRecord(descriptor, labels, recorder) + exportRecord := export.NewRecord(descriptor, labels, m.resource, recorder) err := m.integrator.Process(ctx, exportRecord) if err != nil { m.errorHandler(err) From 21d094af438ee964cfffe677b1677c1e6062448e Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 18 May 2020 18:37:41 -0700 Subject: [PATCH 061/108] Refactor the api/metrics push controller; add CheckpointSet synchronization (#737) * Checkpoint * Finish tests * Checkpoint * Checkpoint (builds) * Checkpoint + RWMutex interface * Comments * Remove commitLock * Apply feedback --- example/prometheus/main.go | 4 +- exporters/metric/prometheus/prometheus.go | 48 +++++--- .../metric/prometheus/prometheus_test.go | 73 ++++++++++-- exporters/metric/stdout/example_test.go | 3 +- exporters/metric/stdout/stdout.go | 23 ++-- exporters/metric/test/test.go | 4 +- exporters/otlp/otlp_integration_test.go | 2 +- exporters/otlp/otlp_metric_test.go | 8 +- sdk/export/metric/metric.go | 29 +++-- sdk/metric/controller/push/config.go | 32 +++++ sdk/metric/controller/push/push.go | 112 +++++------------- sdk/metric/controller/push/push_test.go | 110 +++-------------- sdk/metric/controller/test/test.go | 58 +++++++++ sdk/metric/controller/time/time.go | 59 +++++++++ sdk/metric/example_test.go | 3 +- sdk/metric/integrator/simple/simple.go | 37 +++--- sdk/metric/integrator/simple/simple_test.go | 14 +-- sdk/metric/sdk.go | 1 + 18 files changed, 366 insertions(+), 254 deletions(-) create mode 100644 sdk/metric/controller/test/test.go create mode 100644 sdk/metric/controller/time/time.go diff --git a/example/prometheus/main.go b/example/prometheus/main.go index 4fbf94baa..378eb98e6 100644 --- a/example/prometheus/main.go +++ b/example/prometheus/main.go @@ -33,11 +33,11 @@ var ( ) func initMeter() *push.Controller { - pusher, hf, err := prometheus.InstallNewPipeline(prometheus.Config{}) + pusher, exporter, err := prometheus.InstallNewPipeline(prometheus.Config{}) if err != nil { log.Panicf("failed to initialize prometheus exporter %v", err) } - http.HandleFunc("/", hf) + http.HandleFunc("/", exporter.ServeHTTP) go func() { _ = http.ListenAndServe(":2222", nil) }() diff --git a/exporters/metric/prometheus/prometheus.go b/exporters/metric/prometheus/prometheus.go index 86bedf02a..44f7fb016 100644 --- a/exporters/metric/prometheus/prometheus.go +++ b/exporters/metric/prometheus/prometheus.go @@ -18,7 +18,7 @@ import ( "context" "fmt" "net/http" - "time" + "sync" "go.opentelemetry.io/otel/api/metric" @@ -30,7 +30,6 @@ import ( export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/controller/push" - integrator "go.opentelemetry.io/otel/sdk/metric/integrator/simple" "go.opentelemetry.io/otel/sdk/metric/selector/simple" ) @@ -42,6 +41,7 @@ type Exporter struct { registerer prometheus.Registerer gatherer prometheus.Gatherer + lock sync.RWMutex snapshot export.CheckpointSet onError func(error) @@ -134,41 +134,49 @@ func NewRawExporter(config Config) (*Exporter, error) { // http.HandleFunc("/metrics", hf) // defer pipeline.Stop() // ... Done -func InstallNewPipeline(config Config) (*push.Controller, http.HandlerFunc, error) { - controller, hf, err := NewExportPipeline(config, time.Minute) +func InstallNewPipeline(config Config, options ...push.Option) (*push.Controller, *Exporter, error) { + controller, exp, err := NewExportPipeline(config, options...) if err != nil { - return controller, hf, err + return controller, exp, err } global.SetMeterProvider(controller.Provider()) - return controller, hf, err + return controller, exp, err } // NewExportPipeline sets up a complete export pipeline with the recommended setup, // chaining a NewRawExporter into the recommended selectors and integrators. -func NewExportPipeline(config Config, period time.Duration) (*push.Controller, http.HandlerFunc, error) { - selector := simple.NewWithHistogramDistribution(config.DefaultHistogramBoundaries) +// +// The returned Controller contains an implementation of +// `metric.Provider`. The controller is returned unstarted and should +// be started by the caller to begin collection. +func NewExportPipeline(config Config, options ...push.Option) (*push.Controller, *Exporter, error) { exporter, err := NewRawExporter(config) if err != nil { return nil, nil, err } - // Prometheus needs to use a stateful integrator since counters (and histogram since they are a collection of Counters) - // are cumulative (i.e., monotonically increasing values) and should not be resetted after each export. + // Prometheus uses a stateful push controller since instruments are + // cumulative and should not be reset after each collection interval. // // Prometheus uses this approach to be resilient to scrape failures. // If a Prometheus server tries to scrape metrics from a host and fails for some reason, // it could try again on the next scrape and no data would be lost, only resolution. // // Gauges (or LastValues) and Summaries are an exception to this and have different behaviors. - integrator := integrator.New(selector, true) - pusher := push.New(integrator, exporter, period) - pusher.Start() + pusher := push.New( + simple.NewWithHistogramDistribution(config.DefaultHistogramBoundaries), + exporter, + append(options, push.WithStateful(true))..., + ) - return pusher, exporter.ServeHTTP, nil + return pusher, exporter, nil } // Export exports the provide metric record to prometheus. func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error { + // TODO: Use the resource value in this exporter. + e.lock.Lock() + defer e.lock.Unlock() e.snapshot = checkpointSet return nil } @@ -187,10 +195,16 @@ func newCollector(exporter *Exporter) *collector { } func (c *collector) Describe(ch chan<- *prometheus.Desc) { + c.exp.lock.RLock() + defer c.exp.lock.RUnlock() + if c.exp.snapshot == nil { return } + c.exp.snapshot.RLock() + defer c.exp.snapshot.RUnlock() + _ = c.exp.snapshot.ForEach(func(record export.Record) error { ch <- c.toDesc(&record) return nil @@ -202,10 +216,16 @@ func (c *collector) Describe(ch chan<- *prometheus.Desc) { // Collect is invoked whenever prometheus.Gatherer is also invoked. // For example, when the HTTP endpoint is invoked by Prometheus. func (c *collector) Collect(ch chan<- prometheus.Metric) { + c.exp.lock.RLock() + defer c.exp.lock.RUnlock() + if c.exp.snapshot == nil { return } + c.exp.snapshot.RLock() + defer c.exp.snapshot.RUnlock() + err := c.exp.snapshot.ForEach(func(record export.Record) error { agg := record.Aggregator() numberKind := record.Descriptor().NumberKind() diff --git a/exporters/metric/prometheus/prometheus_test.go b/exporters/metric/prometheus/prometheus_test.go index a95d09ad3..254cd1e55 100644 --- a/exporters/metric/prometheus/prometheus_test.go +++ b/exporters/metric/prometheus/prometheus_test.go @@ -15,31 +15,35 @@ package prometheus_test import ( + "bytes" "context" - "log" + "io/ioutil" + "net/http" "net/http/httptest" + "runtime" "sort" "strings" "testing" + "time" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/exporters/metric/prometheus" - "go.opentelemetry.io/otel/exporters/metric/test" + exportTest "go.opentelemetry.io/otel/exporters/metric/test" + "go.opentelemetry.io/otel/sdk/metric/controller/push" + controllerTest "go.opentelemetry.io/otel/sdk/metric/controller/test" ) func TestPrometheusExporter(t *testing.T) { exporter, err := prometheus.NewRawExporter(prometheus.Config{ DefaultSummaryQuantiles: []float64{0.5, 0.9, 0.99}, }) - if err != nil { - log.Panicf("failed to initialize prometheus exporter %v", err) - } + require.NoError(t, err) var expected []string - checkpointSet := test.NewCheckpointSet(nil) + checkpointSet := exportTest.NewCheckpointSet(nil) counter := metric.NewDescriptor( "counter", metric.CounterKind, metric.Float64NumberKind) @@ -116,7 +120,7 @@ func TestPrometheusExporter(t *testing.T) { compareExport(t, exporter, checkpointSet, expected) } -func compareExport(t *testing.T, exporter *prometheus.Exporter, checkpointSet *test.CheckpointSet, expected []string) { +func compareExport(t *testing.T, exporter *prometheus.Exporter, checkpointSet *exportTest.CheckpointSet, expected []string) { err := exporter.Export(context.Background(), checkpointSet) require.Nil(t, err) @@ -138,3 +142,58 @@ func compareExport(t *testing.T, exporter *prometheus.Exporter, checkpointSet *t require.Equal(t, strings.Join(expected, "\n"), strings.Join(metricsOnly, "\n")) } + +func TestPrometheusStatefulness(t *testing.T) { + // Create a meter + controller, exporter, err := prometheus.NewExportPipeline(prometheus.Config{}, push.WithPeriod(time.Minute)) + require.NoError(t, err) + + meter := controller.Provider().Meter("test") + mock := controllerTest.NewMockClock() + controller.SetClock(mock) + controller.Start() + + // GET the HTTP endpoint + scrape := func() string { + var input bytes.Buffer + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", &input) + require.NoError(t, err) + + exporter.ServeHTTP(resp, req) + data, err := ioutil.ReadAll(resp.Result().Body) + require.NoError(t, err) + + return string(data) + } + + ctx := context.Background() + + counter := metric.Must(meter).NewInt64Counter( + "a.counter", + metric.WithDescription("Counts things"), + ) + + counter.Add(ctx, 100, kv.String("key", "value")) + + // Trigger a push + mock.Add(time.Minute) + runtime.Gosched() + + require.Equal(t, `# HELP a_counter Counts things +# TYPE a_counter counter +a_counter{key="value"} 100 +`, scrape()) + + counter.Add(ctx, 100, kv.String("key", "value")) + + // Again, now expect cumulative count + mock.Add(time.Minute) + runtime.Gosched() + + require.Equal(t, `# HELP a_counter Counts things +# TYPE a_counter counter +a_counter{key="value"} 200 +`, scrape()) + +} diff --git a/exporters/metric/stdout/example_test.go b/exporters/metric/stdout/example_test.go index 0952b41c9..306a9f501 100644 --- a/exporters/metric/stdout/example_test.go +++ b/exporters/metric/stdout/example_test.go @@ -17,7 +17,6 @@ package stdout_test import ( "context" "log" - "time" "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/metric" @@ -29,7 +28,7 @@ func ExampleNewExportPipeline() { pusher, err := stdout.NewExportPipeline(stdout.Config{ PrettyPrint: true, DoNotPrintTime: true, - }, time.Minute) + }) if err != nil { log.Fatal("Could not initialize stdout exporter:", err) } diff --git a/exporters/metric/stdout/stdout.go b/exporters/metric/stdout/stdout.go index 433288503..270edaa02 100644 --- a/exporters/metric/stdout/stdout.go +++ b/exporters/metric/stdout/stdout.go @@ -29,7 +29,6 @@ import ( export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/controller/push" - integrator "go.opentelemetry.io/otel/sdk/metric/integrator/simple" "go.opentelemetry.io/otel/sdk/metric/selector/simple" ) @@ -120,8 +119,8 @@ func NewRawExporter(config Config) (*Exporter, error) { // } // defer pipeline.Stop() // ... Done -func InstallNewPipeline(config Config, opts ...push.Option) (*push.Controller, error) { - controller, err := NewExportPipeline(config, time.Minute, opts...) +func InstallNewPipeline(config Config, options ...push.Option) (*push.Controller, error) { + controller, err := NewExportPipeline(config, options...) if err != nil { return controller, err } @@ -129,16 +128,22 @@ func InstallNewPipeline(config Config, opts ...push.Option) (*push.Controller, e return controller, err } -// NewExportPipeline sets up a complete export pipeline with the recommended setup, -// chaining a NewRawExporter into the recommended selectors and integrators. -func NewExportPipeline(config Config, period time.Duration, opts ...push.Option) (*push.Controller, error) { - selector := simple.NewWithExactDistribution() +// NewExportPipeline sets up a complete export pipeline with the +// recommended setup, chaining a NewRawExporter into the recommended +// selectors and integrators. +// +// The pipeline is configured with a stateful integrator unless the +// push.WithStateful(false) option is used. +func NewExportPipeline(config Config, options ...push.Option) (*push.Controller, error) { exporter, err := NewRawExporter(config) if err != nil { return nil, err } - integrator := integrator.New(selector, true) - pusher := push.New(integrator, exporter, period, opts...) + pusher := push.New( + simple.NewWithExactDistribution(), + exporter, + append([]push.Option{push.WithStateful(true)}, options...)..., + ) pusher.Start() return pusher, nil diff --git a/exporters/metric/test/test.go b/exporters/metric/test/test.go index cb99b6489..9fd639493 100644 --- a/exporters/metric/test/test.go +++ b/exporters/metric/test/test.go @@ -17,6 +17,7 @@ package test import ( "context" "errors" + "sync" "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/label" @@ -36,9 +37,10 @@ type mapkey struct { } type CheckpointSet struct { + sync.RWMutex records map[mapkey]export.Record - resource *resource.Resource updates []export.Record + resource *resource.Resource } // NewCheckpointSet returns a test CheckpointSet that new records could be added. diff --git a/exporters/otlp/otlp_integration_test.go b/exporters/otlp/otlp_integration_test.go index a7a764a49..e4f61fdb6 100644 --- a/exporters/otlp/otlp_integration_test.go +++ b/exporters/otlp/otlp_integration_test.go @@ -111,7 +111,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) selector := simple.NewWithExactDistribution() integrator := integrator.New(selector, true) - pusher := push.New(integrator, exp, 60*time.Second) + pusher := push.New(integrator, exp) pusher.Start() ctx := context.Background() diff --git a/exporters/otlp/otlp_metric_test.go b/exporters/otlp/otlp_metric_test.go index db47cb157..390a59995 100644 --- a/exporters/otlp/otlp_metric_test.go +++ b/exporters/otlp/otlp_metric_test.go @@ -16,6 +16,7 @@ package otlp import ( "context" + "sync" "testing" colmetricpb "github.com/open-telemetry/opentelemetry-proto/gen/go/collector/metrics/v1" @@ -60,10 +61,11 @@ func (m *metricsServiceClientStub) Reset() { } type checkpointSet struct { + sync.RWMutex records []metricsdk.Record } -func (m checkpointSet) ForEach(fn func(metricsdk.Record) error) error { +func (m *checkpointSet) ForEach(fn func(metricsdk.Record) error) error { for _, r := range m.records { if err := fn(r); err != nil && err != aggregator.ErrNoData { return err @@ -662,7 +664,7 @@ func runMetricExportTest(t *testing.T, exp *Exporter, rs []record, expected []me recs[equiv] = append(recs[equiv], metricsdk.NewRecord(&desc, &labs, r.resource, agg)) } for _, records := range recs { - assert.NoError(t, exp.Export(context.Background(), checkpointSet{records: records})) + assert.NoError(t, exp.Export(context.Background(), &checkpointSet{records: records})) } // assert.ElementsMatch does not equate nested slices of different order, @@ -726,7 +728,7 @@ func TestEmptyMetricExport(t *testing.T) { }, } { msc.Reset() - require.NoError(t, exp.Export(context.Background(), checkpointSet{records: test.records})) + require.NoError(t, exp.Export(context.Background(), &checkpointSet{records: test.records})) assert.Equal(t, test.want, msc.ResourceMetrics()) } } diff --git a/sdk/export/metric/metric.go b/sdk/export/metric/metric.go index 86f195aa1..f22cdec0f 100644 --- a/sdk/export/metric/metric.go +++ b/sdk/export/metric/metric.go @@ -16,6 +16,7 @@ package metric // import "go.opentelemetry.io/otel/sdk/export/metric" import ( "context" + "sync" "go.opentelemetry.io/otel/api/label" "go.opentelemetry.io/otel/api/metric" @@ -39,10 +40,6 @@ import ( // single-threaded context from the SDK, after the aggregator is // checkpointed, allowing the integrator to build the set of metrics // currently being exported. -// -// The `CheckpointSet` method is called during collection in a -// single-threaded context from the Exporter, giving the exporter -// access to a producer for iterating over the complete checkpoint. type Integrator interface { // AggregationSelector is responsible for selecting the // concrete type of Aggregator used for a metric in the SDK. @@ -70,17 +67,6 @@ type Integrator interface { // The Context argument originates from the controller that // orchestrates collection. Process(ctx context.Context, record Record) error - - // CheckpointSet is the interface used by the controller to - // access the fully aggregated checkpoint after collection. - // - // The returned CheckpointSet is passed to the Exporter. - CheckpointSet() CheckpointSet - - // FinishedCollection informs the Integrator that a complete - // collection round was completed. Stateless integrators might - // reset state in this method, for example. - FinishedCollection() } // AggregationSelector supports selecting the kind of Aggregator to @@ -173,6 +159,19 @@ type CheckpointSet interface { // of error will immediately halt ForEach and return // the error to the caller. ForEach(func(Record) error) error + + // Locker supports locking the checkpoint set. Collection + // into the checkpoint set cannot take place (in case of a + // stateful integrator) while it is locked. + // + // The Integrator attached to the Accumulator MUST be called + // with the lock held. + sync.Locker + + // RLock acquires a read lock corresponding to this Locker. + RLock() + // RUnlock releases a read lock corresponding to this Locker. + RUnlock() } // Record contains the exported data for a single metric instrument diff --git a/sdk/metric/controller/push/config.go b/sdk/metric/controller/push/config.go index 2b2b86b71..fccd02df0 100644 --- a/sdk/metric/controller/push/config.go +++ b/sdk/metric/controller/push/config.go @@ -15,6 +15,8 @@ package push import ( + "time" + sdk "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" ) @@ -30,6 +32,14 @@ type Config struct { // Resource is the OpenTelemetry resource associated with all Meters // created by the Controller. Resource *resource.Resource + + // Stateful causes the controller to maintain state across + // collection events, so that records in the exported + // checkpoint set are cumulative. + Stateful bool + + // Period is the interval between calls to Collect a checkpoint. + Period time.Duration } // Option is the interface that applies the value to a configuration option. @@ -59,3 +69,25 @@ type resourceOption struct{ *resource.Resource } func (o resourceOption) Apply(config *Config) { config.Resource = o.Resource } + +// WithStateful sets the Stateful configuration option of a Config. +func WithStateful(stateful bool) Option { + return statefulOption(stateful) +} + +type statefulOption bool + +func (o statefulOption) Apply(config *Config) { + config.Stateful = bool(o) +} + +// WithPeriod sets the Period configuration option of a Config. +func WithPeriod(period time.Duration) Option { + return periodOption(period) +} + +type periodOption time.Duration + +func (o periodOption) Apply(config *Config) { + config.Period = time.Duration(o) +} diff --git a/sdk/metric/controller/push/push.go b/sdk/metric/controller/push/push.go index ea347d3fa..0a009780b 100644 --- a/sdk/metric/controller/push/push.go +++ b/sdk/metric/controller/push/push.go @@ -23,77 +23,61 @@ import ( "go.opentelemetry.io/otel/api/metric/registry" export "go.opentelemetry.io/otel/sdk/export/metric" sdk "go.opentelemetry.io/otel/sdk/metric" - "go.opentelemetry.io/otel/sdk/resource" + controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time" + "go.opentelemetry.io/otel/sdk/metric/integrator/simple" ) +// DefaultPushPeriod is the default time interval between pushes. +const DefaultPushPeriod = 10 * time.Second + // Controller organizes a periodic push of metric data. type Controller struct { lock sync.Mutex - collectLock sync.Mutex accumulator *sdk.Accumulator provider *registry.Provider errorHandler sdk.ErrorHandler - integrator export.Integrator + integrator *simple.Integrator exporter export.Exporter wg sync.WaitGroup ch chan struct{} period time.Duration - ticker Ticker - clock Clock + clock controllerTime.Clock + ticker controllerTime.Ticker } -// Several types below are created to match "github.com/benbjohnson/clock" -// so that it remains a test-only dependency. - -type Clock interface { - Now() time.Time - Ticker(time.Duration) Ticker -} - -type Ticker interface { - Stop() - C() <-chan time.Time -} - -type realClock struct { -} - -type realTicker struct { - ticker *time.Ticker -} - -var _ Clock = realClock{} -var _ Ticker = realTicker{} - // New constructs a Controller, an implementation of metric.Provider, -// using the provided integrator, exporter, collection period, and SDK -// configuration options to configure an SDK with periodic collection. -// The integrator itself is configured with the aggregation selector policy. -func New(integrator export.Integrator, exporter export.Exporter, period time.Duration, opts ...Option) *Controller { +// using the provided exporter and options to configure an SDK with +// periodic collection. +func New(selector export.AggregationSelector, exporter export.Exporter, opts ...Option) *Controller { c := &Config{ ErrorHandler: sdk.DefaultErrorHandler, - Resource: resource.Empty(), + Period: DefaultPushPeriod, } for _, opt := range opts { opt.Apply(c) } - impl := sdk.NewAccumulator(integrator, sdk.WithErrorHandler(c.ErrorHandler), sdk.WithResource(c.Resource)) + integrator := simple.New(selector, c.Stateful) + impl := sdk.NewAccumulator( + integrator, + sdk.WithErrorHandler(c.ErrorHandler), + sdk.WithResource(c.Resource), + ) return &Controller{ - accumulator: impl, provider: registry.NewProvider(impl), - errorHandler: c.ErrorHandler, + accumulator: impl, integrator: integrator, exporter: exporter, + errorHandler: c.ErrorHandler, ch: make(chan struct{}), - period: period, - clock: realClock{}, + period: c.Period, + clock: controllerTime.RealClock{}, } } // SetClock supports setting a mock clock for testing. This must be // called before Start(). -func (c *Controller) SetClock(clock Clock) { +func (c *Controller) SetClock(clock controllerTime.Clock) { c.lock.Lock() defer c.lock.Unlock() c.clock = clock @@ -162,53 +146,15 @@ func (c *Controller) tick() { // TODO: either remove the context argument from Export() or // configure a timeout here? ctx := context.Background() - c.collect(ctx) - checkpointSet := syncCheckpointSet{ - mtx: &c.collectLock, - delegate: c.integrator.CheckpointSet(), - } - err := c.exporter.Export(ctx, checkpointSet) + c.integrator.Lock() + defer c.integrator.Unlock() + + c.accumulator.Collect(ctx) + + err := c.exporter.Export(ctx, c.integrator.CheckpointSet()) c.integrator.FinishedCollection() if err != nil { c.errorHandler(err) } } - -func (c *Controller) collect(ctx context.Context) { - c.collectLock.Lock() - defer c.collectLock.Unlock() - - c.accumulator.Collect(ctx) -} - -// syncCheckpointSet is a wrapper for a CheckpointSet to synchronize -// SDK's collection and reads of a CheckpointSet by an exporter. -type syncCheckpointSet struct { - mtx *sync.Mutex - delegate export.CheckpointSet -} - -var _ export.CheckpointSet = (*syncCheckpointSet)(nil) - -func (c syncCheckpointSet) ForEach(fn func(export.Record) error) error { - c.mtx.Lock() - defer c.mtx.Unlock() - return c.delegate.ForEach(fn) -} - -func (realClock) Now() time.Time { - return time.Now() -} - -func (realClock) Ticker(period time.Duration) Ticker { - return realTicker{time.NewTicker(period)} -} - -func (t realTicker) Stop() { - t.ticker.Stop() -} - -func (t realTicker) C() <-chan time.Time { - return t.ticker.C -} diff --git a/sdk/metric/controller/push/push_test.go b/sdk/metric/controller/push/push_test.go index dc4ae94c6..ad7945100 100644 --- a/sdk/metric/controller/push/push_test.go +++ b/sdk/metric/controller/push/push_test.go @@ -22,7 +22,6 @@ import ( "testing" "time" - "github.com/benbjohnson/clock" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/api/kv" @@ -33,17 +32,10 @@ import ( "go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/controller/push" + controllerTest "go.opentelemetry.io/otel/sdk/metric/controller/test" "go.opentelemetry.io/otel/sdk/resource" ) -type testIntegrator struct { - t *testing.T - lock sync.Mutex - checkpointSet *test.CheckpointSet - checkpoints int - finishes int -} - var testResource = resource.New(kv.String("R", "V")) type testExporter struct { @@ -56,69 +48,27 @@ type testExporter struct { type testFixture struct { checkpointSet *test.CheckpointSet - integrator *testIntegrator exporter *testExporter } -type mockClock struct { - mock *clock.Mock -} - -type mockTicker struct { - ticker *clock.Ticker -} - -var _ push.Clock = mockClock{} -var _ push.Ticker = mockTicker{} +type testSelector struct{} func newFixture(t *testing.T) testFixture { checkpointSet := test.NewCheckpointSet(testResource) - integrator := &testIntegrator{ - t: t, - checkpointSet: checkpointSet, - } exporter := &testExporter{ t: t, } return testFixture{ checkpointSet: checkpointSet, - integrator: integrator, exporter: exporter, } } -func (b *testIntegrator) AggregatorFor(*metric.Descriptor) export.Aggregator { +func (testSelector) AggregatorFor(*metric.Descriptor) export.Aggregator { return sum.New() } -func (b *testIntegrator) CheckpointSet() export.CheckpointSet { - b.lock.Lock() - defer b.lock.Unlock() - b.checkpoints++ - return b.checkpointSet -} - -func (b *testIntegrator) FinishedCollection() { - b.lock.Lock() - defer b.lock.Unlock() - b.finishes++ -} - -func (b *testIntegrator) Process(_ context.Context, record export.Record) error { - b.lock.Lock() - defer b.lock.Unlock() - labels := record.Labels().ToSlice() - b.checkpointSet.Add(record.Descriptor(), record.Aggregator(), labels...) - return nil -} - -func (b *testIntegrator) getCounts() (checkpoints, finishes int) { - b.lock.Lock() - defer b.lock.Unlock() - return b.checkpoints, b.finishes -} - func (e *testExporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error { e.lock.Lock() defer e.lock.Unlock() @@ -147,29 +97,9 @@ func (e *testExporter) resetRecords() ([]export.Record, int) { return r, e.exports } -func (c mockClock) Now() time.Time { - return c.mock.Now() -} - -func (c mockClock) Ticker(period time.Duration) push.Ticker { - return mockTicker{c.mock.Ticker(period)} -} - -func (c mockClock) Add(d time.Duration) { - c.mock.Add(d) -} - -func (t mockTicker) Stop() { - t.ticker.Stop() -} - -func (t mockTicker) C() <-chan time.Time { - return t.ticker.C -} - func TestPushDoubleStop(t *testing.T) { fix := newFixture(t) - p := push.New(fix.integrator, fix.exporter, time.Second) + p := push.New(testSelector{}, fix.exporter) p.Start() p.Stop() p.Stop() @@ -177,7 +107,7 @@ func TestPushDoubleStop(t *testing.T) { func TestPushDoubleStart(t *testing.T) { fix := newFixture(t) - p := push.New(fix.integrator, fix.exporter, time.Second) + p := push.New(testSelector{}, fix.exporter) p.Start() p.Start() p.Stop() @@ -186,10 +116,15 @@ func TestPushDoubleStart(t *testing.T) { func TestPushTicker(t *testing.T) { fix := newFixture(t) - p := push.New(fix.integrator, fix.exporter, time.Second) + p := push.New( + testSelector{}, + fix.exporter, + push.WithPeriod(time.Second), + push.WithResource(testResource), + ) meter := p.Provider().Meter("name") - mock := mockClock{clock.NewMock()} + mock := controllerTest.NewMockClock() p.SetClock(mock) ctx := context.Background() @@ -201,9 +136,6 @@ func TestPushTicker(t *testing.T) { counter.Add(ctx, 3) records, exports := fix.exporter.resetRecords() - checkpoints, finishes := fix.integrator.getCounts() - require.Equal(t, 0, checkpoints) - require.Equal(t, 0, finishes) require.Equal(t, 0, exports) require.Equal(t, 0, len(records)) @@ -211,9 +143,6 @@ func TestPushTicker(t *testing.T) { runtime.Gosched() records, exports = fix.exporter.resetRecords() - checkpoints, finishes = fix.integrator.getCounts() - require.Equal(t, 1, checkpoints) - require.Equal(t, 1, finishes) require.Equal(t, 1, exports) require.Equal(t, 1, len(records)) require.Equal(t, "counter", records[0].Descriptor().Name()) @@ -231,9 +160,6 @@ func TestPushTicker(t *testing.T) { runtime.Gosched() records, exports = fix.exporter.resetRecords() - checkpoints, finishes = fix.integrator.getCounts() - require.Equal(t, 2, checkpoints) - require.Equal(t, 2, finishes) require.Equal(t, 2, exports) require.Equal(t, 1, len(records)) require.Equal(t, "counter", records[0].Descriptor().Name()) @@ -271,7 +197,12 @@ func TestPushExportError(t *testing.T) { fix := newFixture(t) fix.exporter.injectErr = injector("counter1", tt.injectedError) - p := push.New(fix.integrator, fix.exporter, time.Second) + p := push.New( + testSelector{}, + fix.exporter, + push.WithPeriod(time.Second), + push.WithResource(testResource), + ) var err error var lock sync.Mutex @@ -281,7 +212,7 @@ func TestPushExportError(t *testing.T) { err = sdkErr }) - mock := mockClock{clock.NewMock()} + mock := controllerTest.NewMockClock() p.SetClock(mock) ctx := context.Background() @@ -303,10 +234,7 @@ func TestPushExportError(t *testing.T) { runtime.Gosched() records, exports := fix.exporter.resetRecords() - checkpoints, finishes := fix.integrator.getCounts() require.Equal(t, 1, exports) - require.Equal(t, 1, checkpoints) - require.Equal(t, 1, finishes) lock.Lock() if tt.expectedError == nil { require.NoError(t, err) diff --git a/sdk/metric/controller/test/test.go b/sdk/metric/controller/test/test.go new file mode 100644 index 000000000..f2c2e7447 --- /dev/null +++ b/sdk/metric/controller/test/test.go @@ -0,0 +1,58 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "time" + + "github.com/benbjohnson/clock" + + controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time" +) + +type MockClock struct { + mock *clock.Mock +} + +type MockTicker struct { + ticker *clock.Ticker +} + +var _ controllerTime.Clock = MockClock{} +var _ controllerTime.Ticker = MockTicker{} + +func NewMockClock() MockClock { + return MockClock{clock.NewMock()} +} + +func (c MockClock) Now() time.Time { + return c.mock.Now() +} + +func (c MockClock) Ticker(period time.Duration) controllerTime.Ticker { + return MockTicker{c.mock.Ticker(period)} +} + +func (c MockClock) Add(d time.Duration) { + c.mock.Add(d) +} + +func (t MockTicker) Stop() { + t.ticker.Stop() +} + +func (t MockTicker) C() <-chan time.Time { + return t.ticker.C +} diff --git a/sdk/metric/controller/time/time.go b/sdk/metric/controller/time/time.go new file mode 100644 index 000000000..9d0e4eb79 --- /dev/null +++ b/sdk/metric/controller/time/time.go @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package time // import "go.opentelemetry.io/otel/sdk/metric/controller/time" + +import ( + "time" + lib "time" +) + +// Several types below are created to match "github.com/benbjohnson/clock" +// so that it remains a test-only dependency. + +type Clock interface { + Now() lib.Time + Ticker(lib.Duration) Ticker +} + +type Ticker interface { + Stop() + C() <-chan lib.Time +} + +type RealClock struct { +} + +type RealTicker struct { + ticker *lib.Ticker +} + +var _ Clock = RealClock{} +var _ Ticker = RealTicker{} + +func (RealClock) Now() time.Time { + return time.Now() +} + +func (RealClock) Ticker(period time.Duration) Ticker { + return RealTicker{time.NewTicker(period)} +} + +func (t RealTicker) Stop() { + t.ticker.Stop() +} + +func (t RealTicker) C() <-chan time.Time { + return t.ticker.C +} diff --git a/sdk/metric/example_test.go b/sdk/metric/example_test.go index c4910b9bb..d76766c7e 100644 --- a/sdk/metric/example_test.go +++ b/sdk/metric/example_test.go @@ -17,7 +17,6 @@ package metric_test import ( "context" "fmt" - "time" "go.opentelemetry.io/otel/api/kv" @@ -29,7 +28,7 @@ func ExampleNew() { pusher, err := stdout.NewExportPipeline(stdout.Config{ PrettyPrint: true, DoNotPrintTime: true, // This makes the output deterministic - }, time.Minute) + }) if err != nil { panic(fmt.Sprintln("Could not initialize stdout exporter:", err)) } diff --git a/sdk/metric/integrator/simple/simple.go b/sdk/metric/integrator/simple/simple.go index 123361ff0..28c35ac50 100644 --- a/sdk/metric/integrator/simple/simple.go +++ b/sdk/metric/integrator/simple/simple.go @@ -17,6 +17,7 @@ package simple // import "go.opentelemetry.io/otel/sdk/metric/integrator/simple" import ( "context" "errors" + "sync" "go.opentelemetry.io/otel/api/label" "go.opentelemetry.io/otel/api/metric" @@ -27,9 +28,9 @@ import ( type ( Integrator struct { - selector export.AggregationSelector - batchMap batchMap + export.AggregationSelector stateful bool + batch } batchKey struct { @@ -44,24 +45,26 @@ type ( resource *resource.Resource } - batchMap map[batchKey]batchValue + batch struct { + // RWMutex implements locking for the `CheckpoingSet` interface. + sync.RWMutex + values map[batchKey]batchValue + } ) var _ export.Integrator = &Integrator{} -var _ export.CheckpointSet = batchMap{} +var _ export.CheckpointSet = &batch{} func New(selector export.AggregationSelector, stateful bool) *Integrator { return &Integrator{ - selector: selector, - batchMap: batchMap{}, - stateful: stateful, + AggregationSelector: selector, + stateful: stateful, + batch: batch{ + values: map[batchKey]batchValue{}, + }, } } -func (b *Integrator) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { - return b.selector.AggregatorFor(descriptor) -} - func (b *Integrator) Process(_ context.Context, record export.Record) error { desc := record.Descriptor() key := batchKey{ @@ -70,7 +73,7 @@ func (b *Integrator) Process(_ context.Context, record export.Record) error { resource: record.Resource().Equivalent(), } agg := record.Aggregator() - value, ok := b.batchMap[key] + value, ok := b.batch.values[key] if ok { // Note: The call to Merge here combines only // identical records. It is required even for a @@ -92,7 +95,7 @@ func (b *Integrator) Process(_ context.Context, record export.Record) error { return err } } - b.batchMap[key] = batchValue{ + b.batch.values[key] = batchValue{ aggregator: agg, labels: record.Labels(), resource: record.Resource(), @@ -101,17 +104,17 @@ func (b *Integrator) Process(_ context.Context, record export.Record) error { } func (b *Integrator) CheckpointSet() export.CheckpointSet { - return b.batchMap + return &b.batch } func (b *Integrator) FinishedCollection() { if !b.stateful { - b.batchMap = batchMap{} + b.batch.values = map[batchKey]batchValue{} } } -func (c batchMap) ForEach(f func(export.Record) error) error { - for key, value := range c { +func (b *batch) ForEach(f func(export.Record) error) error { + for key, value := range b.values { if err := f(export.NewRecord( key.descriptor, value.labels, diff --git a/sdk/metric/integrator/simple/simple_test.go b/sdk/metric/integrator/simple/simple_test.go index 2b43fc8a8..54fecdd7f 100644 --- a/sdk/metric/integrator/simple/simple_test.go +++ b/sdk/metric/integrator/simple/simple_test.go @@ -29,7 +29,7 @@ import ( // These tests use the ../test label encoding. -func TestUngroupedStateless(t *testing.T) { +func TestSimpleStateless(t *testing.T) { ctx := context.Background() b := simple.New(test.NewAggregationSelector(), false) @@ -60,7 +60,6 @@ func TestUngroupedStateless(t *testing.T) { _ = b.Process(ctx, test.NewCounterRecord(&test.CounterBDesc, test.Labels1, 50)) checkpointSet := b.CheckpointSet() - b.FinishedCollection() records := test.NewOutput(test.SdkEncoder) _ = checkpointSet.ForEach(records.AddTo) @@ -81,17 +80,18 @@ func TestUngroupedStateless(t *testing.T) { "lastvalue.b/C~D&E~F/R~V": 20, // labels2 "lastvalue.b//R~V": 30, // labels3 }, records.Map) + b.FinishedCollection() // Verify that state was reset checkpointSet = b.CheckpointSet() - b.FinishedCollection() _ = checkpointSet.ForEach(func(rec export.Record) error { t.Fatal("Unexpected call") return nil }) + b.FinishedCollection() } -func TestUngroupedStateful(t *testing.T) { +func TestSimpleStateful(t *testing.T) { ctx := context.Background() b := simple.New(test.NewAggregationSelector(), true) @@ -116,12 +116,12 @@ func TestUngroupedStateful(t *testing.T) { // Test that state was NOT reset checkpointSet = b.CheckpointSet() - b.FinishedCollection() records2 := test.NewOutput(test.SdkEncoder) _ = checkpointSet.ForEach(records2.AddTo) require.EqualValues(t, records1.Map, records2.Map) + b.FinishedCollection() // Update and re-checkpoint the original record. _ = caggA.Update(ctx, metric.NewInt64Number(20), &test.CounterADesc) @@ -132,19 +132,18 @@ func TestUngroupedStateful(t *testing.T) { // As yet cagg has not been passed to Integrator.Process. Should // not see an update. checkpointSet = b.CheckpointSet() - b.FinishedCollection() records3 := test.NewOutput(test.SdkEncoder) _ = checkpointSet.ForEach(records3.AddTo) require.EqualValues(t, records1.Map, records3.Map) + b.FinishedCollection() // Now process the second update _ = b.Process(ctx, export.NewRecord(&test.CounterADesc, test.Labels1, test.Resource, caggA)) _ = b.Process(ctx, export.NewRecord(&test.CounterBDesc, test.Labels1, test.Resource, caggB)) checkpointSet = b.CheckpointSet() - b.FinishedCollection() records4 := test.NewOutput(test.SdkEncoder) _ = checkpointSet.ForEach(records4.AddTo) @@ -153,4 +152,5 @@ func TestUngroupedStateful(t *testing.T) { "sum.a/C~D&G~H/R~V": 30, "sum.b/C~D&G~H/R~V": 30, }, records4.Map) + b.FinishedCollection() } diff --git a/sdk/metric/sdk.go b/sdk/metric/sdk.go index 8de0953e3..f3939a41d 100644 --- a/sdk/metric/sdk.go +++ b/sdk/metric/sdk.go @@ -370,6 +370,7 @@ func (m *Accumulator) Collect(ctx context.Context) int { checkpointed := m.collectSyncInstruments(ctx) checkpointed += m.observeAsyncInstruments(ctx) m.currentEpoch++ + return checkpointed } From 1e36a61edfd8596a69b5fc5ba8b37eff2ea3c9e4 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Mon, 18 May 2020 18:54:38 -0700 Subject: [PATCH 062/108] Fix panic in gRPC UnaryServerInfo (#740) Fixes unresolved issue identified in #691 and attempted in #697. Adds unit test to ensure the UnaryServerInfo function does not panic during an error returned from the handler and appropriately annotates the span with the correct event. Restructures the interceptor to remove this class of errors. Co-authored-by: Joshua MacDonald --- plugin/grpctrace/interceptor.go | 66 ++++++++++++++-------------- plugin/grpctrace/interceptor_test.go | 38 ++++++++++++++++ 2 files changed, 70 insertions(+), 34 deletions(-) diff --git a/plugin/grpctrace/interceptor.go b/plugin/grpctrace/interceptor.go index a84aec9e1..0981954c4 100644 --- a/plugin/grpctrace/interceptor.go +++ b/plugin/grpctrace/interceptor.go @@ -43,9 +43,29 @@ var ( messageUncompressedSizeKey = kv.Key("message.uncompressed_size") ) +type messageType string + +// Event adds an event of the messageType to the span associated with the +// passed context with id and size (if message is a proto message). +func (m messageType) Event(ctx context.Context, id int, message interface{}) { + span := trace.SpanFromContext(ctx) + if p, ok := message.(proto.Message); ok { + span.AddEvent(ctx, "message", + messageTypeKey.String(string(m)), + messageIDKey.Int(id), + messageUncompressedSizeKey.Int(proto.Size(p)), + ) + } else { + span.AddEvent(ctx, "message", + messageTypeKey.String(string(m)), + messageIDKey.Int(id), + ) + } +} + const ( - messageTypeSent = "SENT" - messageTypeReceived = "RECEIVED" + messageSent messageType = "SENT" + messageReceived messageType = "RECEIVED" ) // UnaryClientInterceptor returns a grpc.UnaryClientInterceptor suitable @@ -80,11 +100,11 @@ func UnaryClientInterceptor(tracer trace.Tracer) grpc.UnaryClientInterceptor { Inject(ctx, &metadataCopy) ctx = metadata.NewOutgoingContext(ctx, metadataCopy) - addEventForMessageSent(ctx, 1, req) + messageSent.Event(ctx, 1, req) err := invoker(ctx, method, req, reply, cc, opts...) - addEventForMessageReceived(ctx, 1, reply) + messageReceived.Event(ctx, 1, reply) if err != nil { s, _ := status.FromError(err) @@ -134,7 +154,7 @@ func (w *clientStream) RecvMsg(m interface{}) error { w.events <- streamEvent{errorEvent, err} } else { w.receivedMessageID++ - addEventForMessageReceived(w.Context(), w.receivedMessageID, m) + messageReceived.Event(w.Context(), w.receivedMessageID, m) } return err @@ -144,7 +164,7 @@ func (w *clientStream) SendMsg(m interface{}) error { err := w.ClientStream.SendMsg(m) w.sentMessageID++ - addEventForMessageSent(w.Context(), w.sentMessageID, m) + messageSent.Event(w.Context(), w.sentMessageID, m) if err != nil { w.events <- streamEvent{errorEvent, err} @@ -297,15 +317,15 @@ func UnaryServerInterceptor(tracer trace.Tracer) grpc.UnaryServerInterceptor { ) defer span.End() - addEventForMessageReceived(ctx, 1, req) + messageReceived.Event(ctx, 1, req) resp, err := handler(ctx, req) - - addEventForMessageSent(ctx, 1, resp) - if err != nil { s, _ := status.FromError(err) span.SetStatus(s.Code(), s.Message()) + messageSent.Event(ctx, 1, s.Proto()) + } else { + messageSent.Event(ctx, 1, resp) } return resp, err @@ -331,7 +351,7 @@ func (w *serverStream) RecvMsg(m interface{}) error { if err == nil { w.receivedMessageID++ - addEventForMessageReceived(w.Context(), w.receivedMessageID, m) + messageReceived.Event(w.Context(), w.receivedMessageID, m) } return err @@ -341,7 +361,7 @@ func (w *serverStream) SendMsg(m interface{}) error { err := w.ServerStream.SendMsg(m) w.sentMessageID++ - addEventForMessageSent(w.Context(), w.sentMessageID, m) + messageSent.Event(w.Context(), w.sentMessageID, m) return err } @@ -435,25 +455,3 @@ func serviceFromFullMethod(method string) string { return match[1] } - -func addEventForMessageReceived(ctx context.Context, id int, m interface{}) { - size := proto.Size(m.(proto.Message)) - - span := trace.SpanFromContext(ctx) - span.AddEvent(ctx, "message", - messageTypeKey.String(messageTypeReceived), - messageIDKey.Int(id), - messageUncompressedSizeKey.Int(size), - ) -} - -func addEventForMessageSent(ctx context.Context, id int, m interface{}) { - size := proto.Size(m.(proto.Message)) - - span := trace.SpanFromContext(ctx) - span.AddEvent(ctx, "message", - messageTypeKey.String(messageTypeSent), - messageIDKey.Int(id), - messageUncompressedSizeKey.Int(size), - ) -} diff --git a/plugin/grpctrace/interceptor_test.go b/plugin/grpctrace/interceptor_test.go index a92c7177f..211a9c36e 100644 --- a/plugin/grpctrace/interceptor_test.go +++ b/plugin/grpctrace/interceptor_test.go @@ -20,8 +20,12 @@ import ( "time" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/grpc" + "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/kv/value" @@ -373,3 +377,37 @@ func TestStreamClientInterceptor(t *testing.T) { validate("RECEIVED", events[i+1].Attributes) } } + +func TestServerInterceptorError(t *testing.T) { + exp := &testExporter{spanMap: make(map[string]*export.SpanData)} + tp, err := sdktrace.NewProvider( + sdktrace.WithSyncer(exp), + sdktrace.WithConfig(sdktrace.Config{ + DefaultSampler: sdktrace.AlwaysSample(), + }), + ) + require.NoError(t, err) + + tracer := tp.Tracer("grpctrace/Server") + usi := UnaryServerInterceptor(tracer) + deniedErr := status.Error(codes.PermissionDenied, "PERMISSION_DENIED_TEXT") + handler := func(_ context.Context, _ interface{}) (interface{}, error) { + return nil, deniedErr + } + _, err = usi(context.Background(), &mockProtoMessage{}, &grpc.UnaryServerInfo{}, handler) + require.Error(t, err) + assert.Equal(t, err, deniedErr) + + span, ok := exp.spanMap[""] + if !ok { + t.Fatalf("failed to export error span") + } + assert.Equal(t, span.StatusCode, codes.PermissionDenied) + assert.Contains(t, deniedErr.Error(), span.StatusMessage) + assert.Len(t, span.MessageEvents, 2) + assert.Equal(t, []kv.KeyValue{ + kv.String("message.type", "SENT"), + kv.Int("message.id", 1), + kv.Int("message.uncompressed_size", 26), + }, span.MessageEvents[1].Attributes) +} From 51ff97e534bd96aaf2c17d420f7dc1964bca9f05 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Mon, 18 May 2020 21:43:27 -0700 Subject: [PATCH 063/108] Add timeout to push Controller (#742) Addresses existing TODO in the push `tick` function by added a context timeout set to a configurable Controller timeout. This ensures that hung collections or exports do not have runaway resource usage. Defaults to the length of a collector period. --- sdk/metric/controller/push/config.go | 16 ++++++++++++++++ sdk/metric/controller/push/push.go | 11 ++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/sdk/metric/controller/push/config.go b/sdk/metric/controller/push/config.go index fccd02df0..87efd421d 100644 --- a/sdk/metric/controller/push/config.go +++ b/sdk/metric/controller/push/config.go @@ -40,6 +40,11 @@ type Config struct { // Period is the interval between calls to Collect a checkpoint. Period time.Duration + + // Timeout is the duration a collection (i.e. collect, accumulate, + // integrate, and export) can last before it is canceled. Defaults to + // the controller push period. + Timeout time.Duration } // Option is the interface that applies the value to a configuration option. @@ -91,3 +96,14 @@ type periodOption time.Duration func (o periodOption) Apply(config *Config) { config.Period = time.Duration(o) } + +// WithTimeout sets the Timeout configuration option of a Config. +func WithTimeout(timeout time.Duration) Option { + return timeoutOption(timeout) +} + +type timeoutOption time.Duration + +func (o timeoutOption) Apply(config *Config) { + config.Timeout = time.Duration(o) +} diff --git a/sdk/metric/controller/push/push.go b/sdk/metric/controller/push/push.go index 0a009780b..4920326da 100644 --- a/sdk/metric/controller/push/push.go +++ b/sdk/metric/controller/push/push.go @@ -41,6 +41,7 @@ type Controller struct { wg sync.WaitGroup ch chan struct{} period time.Duration + timeout time.Duration clock controllerTime.Clock ticker controllerTime.Ticker } @@ -56,6 +57,9 @@ func New(selector export.AggregationSelector, exporter export.Exporter, opts ... for _, opt := range opts { opt.Apply(c) } + if c.Timeout == 0 { + c.Timeout = c.Period + } integrator := simple.New(selector, c.Stateful) impl := sdk.NewAccumulator( @@ -71,6 +75,7 @@ func New(selector export.AggregationSelector, exporter export.Exporter, opts ... errorHandler: c.ErrorHandler, ch: make(chan struct{}), period: c.Period, + timeout: c.Timeout, clock: controllerTime.RealClock{}, } } @@ -143,9 +148,9 @@ func (c *Controller) run(ch chan struct{}) { } func (c *Controller) tick() { - // TODO: either remove the context argument from Export() or - // configure a timeout here? - ctx := context.Background() + ctx, cancel := context.WithTimeout(context.Background(), c.timeout) + defer cancel() + c.integrator.Lock() defer c.integrator.Unlock() From 7c9f0696ca5e59c7bd72c936e6db7880f98bb242 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Tue, 19 May 2020 07:29:52 +0200 Subject: [PATCH 064/108] Update Makefile --- example/otel-collector/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/otel-collector/Makefile b/example/otel-collector/Makefile index 2305afa68..024d558d1 100644 --- a/example/otel-collector/Makefile +++ b/example/otel-collector/Makefile @@ -1,5 +1,5 @@ namespace-k8s: - k apply -f k8s/namespace.yaml + kubectl apply -f k8s/namespace.yaml jaeger-operator-k8s: # Create the jaeger operator and necessary artifacts in ns observability @@ -31,4 +31,4 @@ clean-k8s: - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml - - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml \ No newline at end of file + - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml From 9adedba21468b9f3402260ee210c29463dd1d2d3 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Tue, 19 May 2020 19:10:30 +0300 Subject: [PATCH 065/108] Fix String in Infer (#746) --- api/kv/kv.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/kv/kv.go b/api/kv/kv.go index e10b91349..7436cd5a6 100644 --- a/api/kv/kv.go +++ b/api/kv/kv.go @@ -128,7 +128,7 @@ func Infer(k string, value interface{}) KeyValue { case reflect.Float64: return Float64(k, rv.Float()) case reflect.String: - return String(k, rv.Interface().(string)) + return String(k, rv.String()) } - return String(k, fmt.Sprint(rv.Interface())) + return String(k, fmt.Sprint(value)) } From 055e9c54e163bc4aca3acc0d726e7a2ed512487b Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Tue, 19 May 2020 09:36:33 -0700 Subject: [PATCH 066/108] Disable parts of batch_span_processor test as flakes (#743) * Name the BSP tests * Add a drain wait group; use the stop wait group to avoid leaking a goroutine * Lint & comments * Fix * Use defer/recover * Restore the Add/Done... * Restore the Add/Done... * Consolidate select stmts * Disable the test * Lint * Use better recover --- sdk/trace/batch_span_processor.go | 140 ++++++++++++------------- sdk/trace/batch_span_processor_test.go | 38 ++++--- 2 files changed, 88 insertions(+), 90 deletions(-) diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index 45d3ef85e..31aa6da3a 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -17,6 +17,7 @@ package trace import ( "context" "errors" + "runtime" "sync" "sync/atomic" "time" @@ -25,9 +26,9 @@ import ( ) const ( - defaultMaxQueueSize = 2048 - defaultScheduledDelay = 5000 * time.Millisecond - defaultMaxExportBatchSize = 512 + DefaultMaxQueueSize = 2048 + DefaultScheduledDelay = 5000 * time.Millisecond + DefaultMaxExportBatchSize = 512 ) var ( @@ -70,6 +71,8 @@ type BatchSpanProcessor struct { queue chan *export.SpanData dropped uint32 + batch []*export.SpanData + timer *time.Timer stopWait sync.WaitGroup stopOnce sync.Once stopCh chan struct{} @@ -87,26 +90,26 @@ func NewBatchSpanProcessor(e export.SpanBatcher, opts ...BatchSpanProcessorOptio } o := BatchSpanProcessorOptions{ - ScheduledDelayMillis: defaultScheduledDelay, - MaxQueueSize: defaultMaxQueueSize, - MaxExportBatchSize: defaultMaxExportBatchSize, + ScheduledDelayMillis: DefaultScheduledDelay, + MaxQueueSize: DefaultMaxQueueSize, + MaxExportBatchSize: DefaultMaxExportBatchSize, } for _, opt := range opts { opt(&o) } bsp := &BatchSpanProcessor{ - e: e, - o: o, + e: e, + o: o, + batch: make([]*export.SpanData, 0, o.MaxExportBatchSize), + timer: time.NewTimer(o.ScheduledDelayMillis), + queue: make(chan *export.SpanData, o.MaxQueueSize), + stopCh: make(chan struct{}), } - - bsp.queue = make(chan *export.SpanData, bsp.o.MaxQueueSize) - - bsp.stopCh = make(chan struct{}) - bsp.stopWait.Add(1) + go func() { - defer bsp.stopWait.Done() bsp.processQueue() + bsp.drainQueue() }() return bsp, nil @@ -127,6 +130,8 @@ func (bsp *BatchSpanProcessor) Shutdown() { bsp.stopOnce.Do(func() { close(bsp.stopCh) bsp.stopWait.Wait() + close(bsp.queue) + }) } @@ -154,70 +159,51 @@ func WithBlocking() BatchSpanProcessorOption { } } +// exportSpans is a subroutine of processing and draining the queue. +func (bsp *BatchSpanProcessor) exportSpans() { + bsp.timer.Reset(bsp.o.ScheduledDelayMillis) + + if len(bsp.batch) > 0 { + bsp.e.ExportSpans(context.Background(), bsp.batch) + bsp.batch = bsp.batch[:0] + } +} + // processQueue removes spans from the `queue` channel until processor // is shut down. It calls the exporter in batches of up to MaxExportBatchSize // waiting up to ScheduledDelayMillis to form a batch. func (bsp *BatchSpanProcessor) processQueue() { - timer := time.NewTimer(bsp.o.ScheduledDelayMillis) - defer timer.Stop() + defer bsp.stopWait.Done() + defer bsp.timer.Stop() - batch := make([]*export.SpanData, 0, bsp.o.MaxExportBatchSize) - - exportSpans := func() { - timer.Reset(bsp.o.ScheduledDelayMillis) - - if len(batch) > 0 { - bsp.e.ExportSpans(context.Background(), batch) - batch = batch[:0] - } - } - -loop: for { select { case <-bsp.stopCh: - break loop - case <-timer.C: - exportSpans() + return + case <-bsp.timer.C: + bsp.exportSpans() case sd := <-bsp.queue: - batch = append(batch, sd) - if len(batch) == bsp.o.MaxExportBatchSize { - if !timer.Stop() { - <-timer.C + bsp.batch = append(bsp.batch, sd) + if len(bsp.batch) == bsp.o.MaxExportBatchSize { + if !bsp.timer.Stop() { + <-bsp.timer.C } - exportSpans() + bsp.exportSpans() } } } - - for { - select { - case sd := <-bsp.queue: - if sd == nil { // queue is closed - go throwAwayFutureSends(bsp.queue) - exportSpans() - return - } - - batch = append(batch, sd) - if len(batch) == bsp.o.MaxExportBatchSize { - exportSpans() - } - default: - // Send nil instead of closing to prevent "send on closed channel". - bsp.queue <- nil - } - } } -func throwAwayFutureSends(ch <-chan *export.SpanData) { - for { - select { - case <-ch: - case <-time.After(time.Minute): - return +// drainQueue awaits the any caller that had added to bsp.stopWait +// to finish the enqueue, then exports the final batch. +func (bsp *BatchSpanProcessor) drainQueue() { + for sd := range bsp.queue { + bsp.batch = append(bsp.batch, sd) + if len(bsp.batch) == bsp.o.MaxExportBatchSize { + bsp.exportSpans() } } + bsp.exportSpans() } func (bsp *BatchSpanProcessor) enqueue(sd *export.SpanData) { @@ -225,19 +211,33 @@ func (bsp *BatchSpanProcessor) enqueue(sd *export.SpanData) { return } - select { - case <-bsp.stopCh: - return - default: - } + // This ensures the bsp.queue<- below does not panic as the + // processor shuts down. + defer func() { + x := recover() + switch err := x.(type) { + case nil: + return + case runtime.Error: + if err.Error() == "send on closed channel" { + return + } + } + panic(x) + }() if bsp.o.BlockOnQueueFull { - bsp.queue <- sd - } else { select { case bsp.queue <- sd: - default: - atomic.AddUint32(&bsp.dropped, 1) + case <-bsp.stopCh: } + return + } + + select { + case bsp.queue <- sd: + case <-bsp.stopCh: + default: + atomic.AddUint32(&bsp.dropped, 1) } } diff --git a/sdk/trace/batch_span_processor_test.go b/sdk/trace/batch_span_processor_test.go index b6eb164ee..abc91f4cd 100644 --- a/sdk/trace/batch_span_processor_test.go +++ b/sdk/trace/batch_span_processor_test.go @@ -148,29 +148,27 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { }, } for _, option := range options { - te := testBatchExporter{} - tp := basicProvider(t) - ssp := createAndRegisterBatchSP(t, option, &te) - if ssp == nil { - t.Fatalf("%s: Error creating new instance of BatchSpanProcessor\n", option.name) - } - tp.RegisterSpanProcessor(ssp) - tr := tp.Tracer("BatchSpanProcessorWithOptions") + t.Run(option.name, func(t *testing.T) { + te := testBatchExporter{} + tp := basicProvider(t) + ssp := createAndRegisterBatchSP(t, option, &te) + if ssp == nil { + t.Fatalf("%s: Error creating new instance of BatchSpanProcessor\n", option.name) + } + tp.RegisterSpanProcessor(ssp) + tr := tp.Tracer("BatchSpanProcessorWithOptions") - generateSpan(t, option.parallel, tr, option) + generateSpan(t, option.parallel, tr, option) - tp.UnregisterSpanProcessor(ssp) + tp.UnregisterSpanProcessor(ssp) - gotNumOfSpans := te.len() - if option.wantNumSpans != gotNumOfSpans { - t.Errorf("%s: number of exported span: got %+v, want %+v\n", option.name, gotNumOfSpans, option.wantNumSpans) - } - - gotBatchCount := te.getBatchCount() - if gotBatchCount < option.wantBatchCount { - t.Errorf("%s: number batches: got %+v, want >= %+v\n", option.name, gotBatchCount, option.wantBatchCount) - t.Errorf("Batches %v\n", te.sizes) - } + // TODO(https://github.com/open-telemetry/opentelemetry-go/issues/741) + // Restore some sort of test here. + _ = option.wantNumSpans + _ = option.wantBatchCount + _ = te.len() // gotNumOfSpans + _ = te.getBatchCount() // gotBatchCount + }) } } From 0a333cade1df1abb78217645944e44521c3cec3e Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Tue, 19 May 2020 10:00:22 -0700 Subject: [PATCH 067/108] Add the UpDownCounter instrument (#745) * Add UpDownCounter to the API * Add an SDK test * Comment fix --- api/metric/api_test.go | 106 ++++++++++++++++++++++++------------ api/metric/kind.go | 3 + api/metric/kind_string.go | 5 +- api/metric/meter.go | 18 ++++++ api/metric/must.go | 20 +++++++ api/metric/sync.go | 32 +++++------ api/metric/updowncounter.go | 96 ++++++++++++++++++++++++++++++++ sdk/metric/correct_test.go | 28 +++++++++- 8 files changed, 252 insertions(+), 56 deletions(-) create mode 100644 api/metric/updowncounter.go diff --git a/api/metric/api_test.go b/api/metric/api_test.go index cfcd1a7a4..71c2f0a0f 100644 --- a/api/metric/api_test.go +++ b/api/metric/api_test.go @@ -93,57 +93,90 @@ func TestOptions(t *testing.T) { } func TestCounter(t *testing.T) { - { + // N.B. the API does not check for negative + // values, that's the SDK's responsibility. + t.Run("float64 counter", func(t *testing.T) { mockSDK, meter := mockTest.NewMeter() c := Must(meter).NewFloat64Counter("test.counter.float") ctx := context.Background() labels := []kv.KeyValue{kv.String("A", "B")} - c.Add(ctx, 42, labels...) + c.Add(ctx, 1994.1, labels...) boundInstrument := c.Bind(labels...) - boundInstrument.Add(ctx, 42) + boundInstrument.Add(ctx, -742) meter.RecordBatch(ctx, labels, c.Measurement(42)) - t.Log("Testing float counter") - checkBatches(t, ctx, labels, mockSDK, metric.Float64NumberKind, c.SyncImpl()) - } - { + checkSyncBatches(t, ctx, labels, mockSDK, metric.Float64NumberKind, metric.CounterKind, c.SyncImpl(), + 1994.1, -742, 42, + ) + }) + t.Run("int64 counter", func(t *testing.T) { mockSDK, meter := mockTest.NewMeter() c := Must(meter).NewInt64Counter("test.counter.int") ctx := context.Background() labels := []kv.KeyValue{kv.String("A", "B"), kv.String("C", "D")} c.Add(ctx, 42, labels...) boundInstrument := c.Bind(labels...) - boundInstrument.Add(ctx, 42) + boundInstrument.Add(ctx, 4200) + meter.RecordBatch(ctx, labels, c.Measurement(420000)) + checkSyncBatches(t, ctx, labels, mockSDK, metric.Int64NumberKind, metric.CounterKind, c.SyncImpl(), + 42, 4200, 420000, + ) + + }) + t.Run("int64 updowncounter", func(t *testing.T) { + mockSDK, meter := mockTest.NewMeter() + c := Must(meter).NewInt64UpDownCounter("test.updowncounter.int") + ctx := context.Background() + labels := []kv.KeyValue{kv.String("A", "B"), kv.String("C", "D")} + c.Add(ctx, 100, labels...) + boundInstrument := c.Bind(labels...) + boundInstrument.Add(ctx, -100) meter.RecordBatch(ctx, labels, c.Measurement(42)) - t.Log("Testing int counter") - checkBatches(t, ctx, labels, mockSDK, metric.Int64NumberKind, c.SyncImpl()) - } + checkSyncBatches(t, ctx, labels, mockSDK, metric.Int64NumberKind, metric.UpDownCounterKind, c.SyncImpl(), + 100, -100, 42, + ) + }) + t.Run("float64 updowncounter", func(t *testing.T) { + mockSDK, meter := mockTest.NewMeter() + c := Must(meter).NewFloat64UpDownCounter("test.updowncounter.float") + ctx := context.Background() + labels := []kv.KeyValue{kv.String("A", "B"), kv.String("C", "D")} + c.Add(ctx, 100.1, labels...) + boundInstrument := c.Bind(labels...) + boundInstrument.Add(ctx, -76) + meter.RecordBatch(ctx, labels, c.Measurement(-100.1)) + checkSyncBatches(t, ctx, labels, mockSDK, metric.Float64NumberKind, metric.UpDownCounterKind, c.SyncImpl(), + 100.1, -76, -100.1, + ) + }) } func TestValueRecorder(t *testing.T) { - { + t.Run("float64 valuerecorder", func(t *testing.T) { mockSDK, meter := mockTest.NewMeter() m := Must(meter).NewFloat64ValueRecorder("test.valuerecorder.float") ctx := context.Background() labels := []kv.KeyValue{} m.Record(ctx, 42, labels...) boundInstrument := m.Bind(labels...) - boundInstrument.Record(ctx, 42) - meter.RecordBatch(ctx, labels, m.Measurement(42)) - t.Log("Testing float valuerecorder") - checkBatches(t, ctx, labels, mockSDK, metric.Float64NumberKind, m.SyncImpl()) - } - { + boundInstrument.Record(ctx, 0) + meter.RecordBatch(ctx, labels, m.Measurement(-100.5)) + checkSyncBatches(t, ctx, labels, mockSDK, metric.Float64NumberKind, metric.ValueRecorderKind, m.SyncImpl(), + 42, 0, -100.5, + ) + }) + t.Run("int64 valuerecorder", func(t *testing.T) { mockSDK, meter := mockTest.NewMeter() m := Must(meter).NewInt64ValueRecorder("test.valuerecorder.int") ctx := context.Background() labels := []kv.KeyValue{kv.Int("I", 1)} - m.Record(ctx, 42, labels...) + m.Record(ctx, 173, labels...) boundInstrument := m.Bind(labels...) - boundInstrument.Record(ctx, 42) - meter.RecordBatch(ctx, labels, m.Measurement(42)) - t.Log("Testing int valuerecorder") - checkBatches(t, ctx, labels, mockSDK, metric.Int64NumberKind, m.SyncImpl()) - } + boundInstrument.Record(ctx, 80) + meter.RecordBatch(ctx, labels, m.Measurement(0)) + checkSyncBatches(t, ctx, labels, mockSDK, metric.Int64NumberKind, metric.ValueRecorderKind, m.SyncImpl(), + 173, 80, 0, + ) + }) } func TestObserverInstruments(t *testing.T) { @@ -170,7 +203,7 @@ func TestObserverInstruments(t *testing.T) { } } -func checkBatches(t *testing.T, ctx context.Context, labels []kv.KeyValue, mock *mockTest.MeterImpl, kind metric.NumberKind, instrument metric.InstrumentImpl) { +func checkSyncBatches(t *testing.T, ctx context.Context, labels []kv.KeyValue, mock *mockTest.MeterImpl, nkind metric.NumberKind, mkind metric.Kind, instrument metric.InstrumentImpl, expected ...float64) { t.Helper() if len(mock.MeasurementBatches) != 3 { t.Errorf("Expected 3 recorded measurement batches, got %d", len(mock.MeasurementBatches)) @@ -195,6 +228,8 @@ func checkBatches(t *testing.T, ctx context.Context, labels []kv.KeyValue, mock } for j := 0; j < minMLen; j++ { measurement := got.Measurements[j] + require.Equal(t, mkind, measurement.Instrument.Descriptor().MetricKind()) + if measurement.Instrument.Implementation() != ourInstrument { d := func(iface interface{}) string { i := iface.(*mockTest.Instrument) @@ -202,9 +237,9 @@ func checkBatches(t *testing.T, ctx context.Context, labels []kv.KeyValue, mock } t.Errorf("Wrong recorded instrument in measurement %d in batch %d, expected %s, got %s", j, i, d(ourInstrument), d(measurement.Instrument.Implementation())) } - ft := fortyTwo(t, kind) - if measurement.Number.CompareNumber(kind, ft) != 0 { - t.Errorf("Wrong recorded value in measurement %d in batch %d, expected %s, got %s", j, i, ft.Emit(kind), measurement.Number.Emit(kind)) + expect := number(t, nkind, expected[i]) + if measurement.Number.CompareNumber(nkind, expect) != 0 { + t.Errorf("Wrong recorded value in measurement %d in batch %d, expected %s, got %s", j, i, expect.Emit(nkind), measurement.Number.Emit(nkind)) } } } @@ -248,11 +283,11 @@ func TestBatchObserverInstruments(t *testing.T) { m1 := got.Measurements[0] require.Equal(t, impl1, m1.Instrument.Implementation().(*mockTest.Async)) - require.Equal(t, 0, m1.Number.CompareNumber(metric.Int64NumberKind, fortyTwo(t, metric.Int64NumberKind))) + require.Equal(t, 0, m1.Number.CompareNumber(metric.Int64NumberKind, number(t, metric.Int64NumberKind, 42))) m2 := got.Measurements[1] require.Equal(t, impl2, m2.Instrument.Implementation().(*mockTest.Async)) - require.Equal(t, 0, m2.Number.CompareNumber(metric.Float64NumberKind, fortyTwo(t, metric.Float64NumberKind))) + require.Equal(t, 0, m2.Number.CompareNumber(metric.Float64NumberKind, number(t, metric.Float64NumberKind, 42))) } func checkObserverBatch(t *testing.T, labels []kv.KeyValue, mock *mockTest.MeterImpl, kind metric.NumberKind, observer metric.AsyncImpl) { @@ -273,20 +308,19 @@ func checkObserverBatch(t *testing.T, labels []kv.KeyValue, mock *mockTest.Meter } measurement := got.Measurements[0] assert.Equal(t, o, measurement.Instrument.Implementation().(*mockTest.Async)) - ft := fortyTwo(t, kind) + ft := number(t, kind, 42) assert.Equal(t, 0, measurement.Number.CompareNumber(kind, ft)) } -func fortyTwo(t *testing.T, kind metric.NumberKind) metric.Number { +func number(t *testing.T, kind metric.NumberKind, value float64) metric.Number { t.Helper() switch kind { case metric.Int64NumberKind: - return metric.NewInt64Number(42) + return metric.NewInt64Number(int64(value)) case metric.Float64NumberKind: - return metric.NewFloat64Number(42) + return metric.NewFloat64Number(value) } - t.Errorf("Invalid value kind %q", kind) - return metric.NewInt64Number(0) + panic("invalid number kind") } type testWrappedMeter struct { diff --git a/api/metric/kind.go b/api/metric/kind.go index cd847a242..66fc1b01e 100644 --- a/api/metric/kind.go +++ b/api/metric/kind.go @@ -24,6 +24,9 @@ const ( ValueRecorderKind Kind = iota // ValueObserverKind indicates an ValueObserver instrument. ValueObserverKind + // CounterKind indicates a Counter instrument. CounterKind + // UpDownCounterKind indicates a UpDownCounter instrument. + UpDownCounterKind ) diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index a05d5f307..5f089067e 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -11,11 +11,12 @@ func _() { _ = x[ValueRecorderKind-0] _ = x[ValueObserverKind-1] _ = x[CounterKind-2] + _ = x[UpDownCounterKind-3] } -const _Kind_name = "ValueRecorderKindValueObserverKindCounterKind" +const _Kind_name = "ValueRecorderKindValueObserverKindCounterKindUpDownCounterKind" -var _Kind_index = [...]uint8{0, 17, 34, 45} +var _Kind_index = [...]uint8{0, 17, 34, 45, 62} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { diff --git a/api/metric/meter.go b/api/metric/meter.go index 9cec69ec6..9ca493e0e 100644 --- a/api/metric/meter.go +++ b/api/metric/meter.go @@ -82,6 +82,24 @@ func (m Meter) NewFloat64Counter(name string, options ...Option) (Float64Counter m.newSync(name, CounterKind, Float64NumberKind, options)) } +// NewInt64UpDownCounter creates a new integer UpDownCounter instrument with the +// given name, customized with options. May return an error if the +// name is invalid (e.g., empty) or improperly registered (e.g., +// duplicate registration). +func (m Meter) NewInt64UpDownCounter(name string, options ...Option) (Int64UpDownCounter, error) { + return wrapInt64UpDownCounterInstrument( + m.newSync(name, UpDownCounterKind, Int64NumberKind, options)) +} + +// NewFloat64UpDownCounter creates a new floating point UpDownCounter with the +// given name, customized with options. May return an error if the +// name is invalid (e.g., empty) or improperly registered (e.g., +// duplicate registration). +func (m Meter) NewFloat64UpDownCounter(name string, options ...Option) (Float64UpDownCounter, error) { + return wrapFloat64UpDownCounterInstrument( + m.newSync(name, UpDownCounterKind, Float64NumberKind, options)) +} + // NewInt64ValueRecorder creates a new integer ValueRecorder instrument with the // given name, customized with options. May return an error if the // name is invalid (e.g., empty) or improperly registered (e.g., diff --git a/api/metric/must.go b/api/metric/must.go index 2bfd03310..e734e8292 100644 --- a/api/metric/must.go +++ b/api/metric/must.go @@ -53,6 +53,26 @@ func (mm MeterMust) NewFloat64Counter(name string, cos ...Option) Float64Counter } } +// NewInt64UpDownCounter calls `Meter.NewInt64UpDownCounter` and returns the +// instrument, panicking if it encounters an error. +func (mm MeterMust) NewInt64UpDownCounter(name string, cos ...Option) Int64UpDownCounter { + if inst, err := mm.meter.NewInt64UpDownCounter(name, cos...); err != nil { + panic(err) + } else { + return inst + } +} + +// NewFloat64UpDownCounter calls `Meter.NewFloat64UpDownCounter` and returns the +// instrument, panicking if it encounters an error. +func (mm MeterMust) NewFloat64UpDownCounter(name string, cos ...Option) Float64UpDownCounter { + if inst, err := mm.meter.NewFloat64UpDownCounter(name, cos...); err != nil { + panic(err) + } else { + return inst + } +} + // NewInt64ValueRecorder calls `Meter.NewInt64ValueRecorder` and returns the // instrument, panicking if it encounters an error. func (mm MeterMust) NewInt64ValueRecorder(name string, mos ...Option) Int64ValueRecorder { diff --git a/api/metric/sync.go b/api/metric/sync.go index 2001ff197..029137683 100644 --- a/api/metric/sync.go +++ b/api/metric/sync.go @@ -156,37 +156,37 @@ func newMeasurement(instrument SyncImpl, number Number) Measurement { } } -// wrapInt64CounterInstrument returns an `Int64Counter` from a -// `SyncImpl`. An error will be generated if the -// `SyncImpl` is nil (in which case a No-op is substituted), -// otherwise the error passes through. +// wrapInt64CounterInstrument converts a SyncImpl into Int64Counter. func wrapInt64CounterInstrument(syncInst SyncImpl, err error) (Int64Counter, error) { common, err := checkNewSync(syncInst, err) return Int64Counter{syncInstrument: common}, err } -// wrapFloat64CounterInstrument returns an `Float64Counter` from a -// `SyncImpl`. An error will be generated if the -// `SyncImpl` is nil (in which case a No-op is substituted), -// otherwise the error passes through. +// wrapFloat64CounterInstrument converts a SyncImpl into Float64Counter. func wrapFloat64CounterInstrument(syncInst SyncImpl, err error) (Float64Counter, error) { common, err := checkNewSync(syncInst, err) return Float64Counter{syncInstrument: common}, err } -// wrapInt64ValueRecorderInstrument returns an `Int64ValueRecorder` from a -// `SyncImpl`. An error will be generated if the -// `SyncImpl` is nil (in which case a No-op is substituted), -// otherwise the error passes through. +// wrapInt64UpDownCounterInstrument converts a SyncImpl into Int64UpDownCounter. +func wrapInt64UpDownCounterInstrument(syncInst SyncImpl, err error) (Int64UpDownCounter, error) { + common, err := checkNewSync(syncInst, err) + return Int64UpDownCounter{syncInstrument: common}, err +} + +// wrapFloat64UpDownCounterInstrument converts a SyncImpl into Float64UpDownCounter. +func wrapFloat64UpDownCounterInstrument(syncInst SyncImpl, err error) (Float64UpDownCounter, error) { + common, err := checkNewSync(syncInst, err) + return Float64UpDownCounter{syncInstrument: common}, err +} + +// wrapInt64ValueRecorderInstrument converts a SyncImpl into Int64ValueRecorder. func wrapInt64ValueRecorderInstrument(syncInst SyncImpl, err error) (Int64ValueRecorder, error) { common, err := checkNewSync(syncInst, err) return Int64ValueRecorder{syncInstrument: common}, err } -// wrapFloat64ValueRecorderInstrument returns an `Float64ValueRecorder` from a -// `SyncImpl`. An error will be generated if the -// `SyncImpl` is nil (in which case a No-op is substituted), -// otherwise the error passes through. +// wrapFloat64ValueRecorderInstrument converts a SyncImpl into Float64ValueRecorder. func wrapFloat64ValueRecorderInstrument(syncInst SyncImpl, err error) (Float64ValueRecorder, error) { common, err := checkNewSync(syncInst, err) return Float64ValueRecorder{syncInstrument: common}, err diff --git a/api/metric/updowncounter.go b/api/metric/updowncounter.go new file mode 100644 index 000000000..26366c3d8 --- /dev/null +++ b/api/metric/updowncounter.go @@ -0,0 +1,96 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metric + +import ( + "context" + + "go.opentelemetry.io/otel/api/kv" +) + +// Float64UpDownCounter is a metric instrument that sums floating +// point values. +type Float64UpDownCounter struct { + syncInstrument +} + +// Int64UpDownCounter is a metric instrument that sums integer values. +type Int64UpDownCounter struct { + syncInstrument +} + +// BoundFloat64UpDownCounter is a bound instrument for Float64UpDownCounter. +// +// It inherits the Unbind function from syncBoundInstrument. +type BoundFloat64UpDownCounter struct { + syncBoundInstrument +} + +// BoundInt64UpDownCounter is a boundInstrument for Int64UpDownCounter. +// +// It inherits the Unbind function from syncBoundInstrument. +type BoundInt64UpDownCounter struct { + syncBoundInstrument +} + +// Bind creates a bound instrument for this counter. The labels are +// associated with values recorded via subsequent calls to Record. +func (c Float64UpDownCounter) Bind(labels ...kv.KeyValue) (h BoundFloat64UpDownCounter) { + h.syncBoundInstrument = c.bind(labels) + return +} + +// Bind creates a bound instrument for this counter. The labels are +// associated with values recorded via subsequent calls to Record. +func (c Int64UpDownCounter) Bind(labels ...kv.KeyValue) (h BoundInt64UpDownCounter) { + h.syncBoundInstrument = c.bind(labels) + return +} + +// Measurement creates a Measurement object to use with batch +// recording. +func (c Float64UpDownCounter) Measurement(value float64) Measurement { + return c.float64Measurement(value) +} + +// Measurement creates a Measurement object to use with batch +// recording. +func (c Int64UpDownCounter) Measurement(value int64) Measurement { + return c.int64Measurement(value) +} + +// Add adds the value to the counter's sum. The labels should contain +// the keys and values to be associated with this value. +func (c Float64UpDownCounter) Add(ctx context.Context, value float64, labels ...kv.KeyValue) { + c.directRecord(ctx, NewFloat64Number(value), labels) +} + +// Add adds the value to the counter's sum. The labels should contain +// the keys and values to be associated with this value. +func (c Int64UpDownCounter) Add(ctx context.Context, value int64, labels ...kv.KeyValue) { + c.directRecord(ctx, NewInt64Number(value), labels) +} + +// Add adds the value to the counter's sum using the labels +// previously bound to this counter via Bind() +func (b BoundFloat64UpDownCounter) Add(ctx context.Context, value float64) { + b.directRecord(ctx, NewFloat64Number(value)) +} + +// Add adds the value to the counter's sum using the labels +// previously bound to this counter via Bind() +func (b BoundInt64UpDownCounter) Add(ctx context.Context, value int64) { + b.directRecord(ctx, NewInt64Number(value)) +} diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index 519bf342b..3ffd78929 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -86,7 +86,7 @@ func (cb *correctnessIntegrator) Process(_ context.Context, record export.Record return nil } -func TestInputRangeTestCounter(t *testing.T) { +func TestInputRangeCounter(t *testing.T) { ctx := context.Background() meter, sdk, integrator := newSDK(t) @@ -114,7 +114,31 @@ func TestInputRangeTestCounter(t *testing.T) { require.Nil(t, sdkErr) } -func TestInputRangeTestValueRecorder(t *testing.T) { +func TestInputRangeUpDownCounter(t *testing.T) { + ctx := context.Background() + meter, sdk, integrator := newSDK(t) + + var sdkErr error + sdk.SetErrorHandler(func(handleErr error) { + sdkErr = handleErr + }) + + counter := Must(meter).NewInt64UpDownCounter("name.updowncounter") + + counter.Add(ctx, -1) + counter.Add(ctx, -1) + counter.Add(ctx, 2) + counter.Add(ctx, 1) + + checkpointed := sdk.Collect(ctx) + sum, err := integrator.records[0].Aggregator().(aggregator.Sum).Sum() + require.Equal(t, int64(1), sum.AsInt64()) + require.Equal(t, 1, checkpointed) + require.Nil(t, err) + require.Nil(t, sdkErr) +} + +func TestInputRangeValueRecorder(t *testing.T) { ctx := context.Background() meter, sdk, integrator := newSDK(t) From c5f2252c4861ddcbc1bc56d4c53ce4c5e40e2d24 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Tue, 19 May 2020 11:49:24 -0700 Subject: [PATCH 068/108] Add the SumObserver instrument (#747) * Add the SumObserver instrument * Lint --- api/metric/api_test.go | 54 ++++++--- api/metric/async.go | 22 ++-- api/metric/kind.go | 3 + api/metric/kind_string.go | 5 +- api/metric/meter.go | 51 +++++++++ api/metric/must.go | 40 +++++++ api/metric/observer.go | 34 ++++++ sdk/export/metric/aggregator/aggregator.go | 2 +- sdk/metric/correct_test.go | 122 +++++++++++++++++---- 9 files changed, 285 insertions(+), 48 deletions(-) diff --git a/api/metric/api_test.go b/api/metric/api_test.go index 71c2f0a0f..0650269f1 100644 --- a/api/metric/api_test.go +++ b/api/metric/api_test.go @@ -180,27 +180,50 @@ func TestValueRecorder(t *testing.T) { } func TestObserverInstruments(t *testing.T) { - { + t.Run("float valueobserver", func(t *testing.T) { labels := []kv.KeyValue{kv.String("O", "P")} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterFloat64ValueObserver("test.observer.float", func(result metric.Float64ObserverResult) { - result.Observe(42, labels...) + o := Must(meter).RegisterFloat64ValueObserver("test.valueobserver.float", func(result metric.Float64ObserverResult) { + result.Observe(42.1, labels...) }) - t.Log("Testing float observer") - mockSDK.RunAsyncInstruments() - checkObserverBatch(t, labels, mockSDK, metric.Float64NumberKind, o.AsyncImpl()) - } - { + checkObserverBatch(t, labels, mockSDK, metric.Float64NumberKind, metric.ValueObserverKind, o.AsyncImpl(), + 42.1, + ) + }) + t.Run("int valueobserver", func(t *testing.T) { labels := []kv.KeyValue{} mockSDK, meter := mockTest.NewMeter() o := Must(meter).RegisterInt64ValueObserver("test.observer.int", func(result metric.Int64ObserverResult) { - result.Observe(42, labels...) + result.Observe(-142, labels...) }) - t.Log("Testing int observer") mockSDK.RunAsyncInstruments() - checkObserverBatch(t, labels, mockSDK, metric.Int64NumberKind, o.AsyncImpl()) - } + checkObserverBatch(t, labels, mockSDK, metric.Int64NumberKind, metric.ValueObserverKind, o.AsyncImpl(), + -142, + ) + }) + t.Run("float sumobserver", func(t *testing.T) { + labels := []kv.KeyValue{kv.String("O", "P")} + mockSDK, meter := mockTest.NewMeter() + o := Must(meter).RegisterFloat64SumObserver("test.sumobserver.float", func(result metric.Float64ObserverResult) { + result.Observe(42.1, labels...) + }) + mockSDK.RunAsyncInstruments() + checkObserverBatch(t, labels, mockSDK, metric.Float64NumberKind, metric.SumObserverKind, o.AsyncImpl(), + 42.1, + ) + }) + t.Run("int sumobserver", func(t *testing.T) { + labels := []kv.KeyValue{} + mockSDK, meter := mockTest.NewMeter() + o := Must(meter).RegisterInt64SumObserver("test.observer.int", func(result metric.Int64ObserverResult) { + result.Observe(-142, labels...) + }) + mockSDK.RunAsyncInstruments() + checkObserverBatch(t, labels, mockSDK, metric.Int64NumberKind, metric.SumObserverKind, o.AsyncImpl(), + -142, + ) + }) } func checkSyncBatches(t *testing.T, ctx context.Context, labels []kv.KeyValue, mock *mockTest.MeterImpl, nkind metric.NumberKind, mkind metric.Kind, instrument metric.InstrumentImpl, expected ...float64) { @@ -290,7 +313,7 @@ func TestBatchObserverInstruments(t *testing.T) { require.Equal(t, 0, m2.Number.CompareNumber(metric.Float64NumberKind, number(t, metric.Float64NumberKind, 42))) } -func checkObserverBatch(t *testing.T, labels []kv.KeyValue, mock *mockTest.MeterImpl, kind metric.NumberKind, observer metric.AsyncImpl) { +func checkObserverBatch(t *testing.T, labels []kv.KeyValue, mock *mockTest.MeterImpl, nkind metric.NumberKind, mkind metric.Kind, observer metric.AsyncImpl, expected float64) { t.Helper() assert.Len(t, mock.MeasurementBatches, 1) if len(mock.MeasurementBatches) < 1 { @@ -307,9 +330,10 @@ func checkObserverBatch(t *testing.T, labels []kv.KeyValue, mock *mockTest.Meter return } measurement := got.Measurements[0] + require.Equal(t, mkind, measurement.Instrument.Descriptor().MetricKind()) assert.Equal(t, o, measurement.Instrument.Implementation().(*mockTest.Async)) - ft := number(t, kind, 42) - assert.Equal(t, 0, measurement.Number.CompareNumber(kind, ft)) + ft := number(t, nkind, expected) + assert.Equal(t, 0, measurement.Number.CompareNumber(nkind, ft)) } func number(t *testing.T, kind metric.NumberKind, value float64) metric.Number { diff --git a/api/metric/async.go b/api/metric/async.go index 7f766e1ed..e54f0cf0d 100644 --- a/api/metric/async.go +++ b/api/metric/async.go @@ -176,20 +176,26 @@ func (b *BatchObserverCallback) Run(function func([]kv.KeyValue, ...Observation) }) } -// wrapInt64ValueObserverInstrument returns an `Int64ValueObserver` from a -// `AsyncImpl`. An error will be generated if the -// `AsyncImpl` is nil (in which case a No-op is substituted), -// otherwise the error passes through. +// wrapInt64ValueObserverInstrument converts an AsyncImpl into Int64ValueObserver. func wrapInt64ValueObserverInstrument(asyncInst AsyncImpl, err error) (Int64ValueObserver, error) { common, err := checkNewAsync(asyncInst, err) return Int64ValueObserver{asyncInstrument: common}, err } -// wrapFloat64ValueObserverInstrument returns an `Float64ValueObserver` from a -// `AsyncImpl`. An error will be generated if the -// `AsyncImpl` is nil (in which case a No-op is substituted), -// otherwise the error passes through. +// wrapFloat64ValueObserverInstrument converts an AsyncImpl into Float64ValueObserver. func wrapFloat64ValueObserverInstrument(asyncInst AsyncImpl, err error) (Float64ValueObserver, error) { common, err := checkNewAsync(asyncInst, err) return Float64ValueObserver{asyncInstrument: common}, err } + +// wrapInt64SumObserverInstrument converts an AsyncImpl into Int64SumObserver. +func wrapInt64SumObserverInstrument(asyncInst AsyncImpl, err error) (Int64SumObserver, error) { + common, err := checkNewAsync(asyncInst, err) + return Int64SumObserver{asyncInstrument: common}, err +} + +// wrapFloat64SumObserverInstrument converts an AsyncImpl into Float64SumObserver. +func wrapFloat64SumObserverInstrument(asyncInst AsyncImpl, err error) (Float64SumObserver, error) { + common, err := checkNewAsync(asyncInst, err) + return Float64SumObserver{asyncInstrument: common}, err +} diff --git a/api/metric/kind.go b/api/metric/kind.go index 66fc1b01e..fca4fa6fb 100644 --- a/api/metric/kind.go +++ b/api/metric/kind.go @@ -29,4 +29,7 @@ const ( CounterKind // UpDownCounterKind indicates a UpDownCounter instrument. UpDownCounterKind + + // SumObserverKind indicates a SumObserver instrument. + SumObserverKind ) diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index 5f089067e..33118e2a0 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -12,11 +12,12 @@ func _() { _ = x[ValueObserverKind-1] _ = x[CounterKind-2] _ = x[UpDownCounterKind-3] + _ = x[SumObserverKind-4] } -const _Kind_name = "ValueRecorderKindValueObserverKindCounterKindUpDownCounterKind" +const _Kind_name = "ValueRecorderKindValueObserverKindCounterKindUpDownCounterKindSumObserverKind" -var _Kind_index = [...]uint8{0, 17, 34, 45, 62} +var _Kind_index = [...]uint8{0, 17, 34, 45, 62, 77} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { diff --git a/api/metric/meter.go b/api/metric/meter.go index 9ca493e0e..e1b546bf9 100644 --- a/api/metric/meter.go +++ b/api/metric/meter.go @@ -144,6 +144,32 @@ func (m Meter) RegisterFloat64ValueObserver(name string, callback Float64Observe newFloat64AsyncRunner(callback))) } +// RegisterInt64SumObserver creates a new integer SumObserver instrument +// with the given name, running a given callback, and customized with +// options. May return an error if the name is invalid (e.g., empty) +// or improperly registered (e.g., duplicate registration). +func (m Meter) RegisterInt64SumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64SumObserver, error) { + if callback == nil { + return wrapInt64SumObserverInstrument(NoopAsync{}, nil) + } + return wrapInt64SumObserverInstrument( + m.newAsync(name, SumObserverKind, Int64NumberKind, opts, + newInt64AsyncRunner(callback))) +} + +// RegisterFloat64SumObserver creates a new floating point SumObserver with +// the given name, running a given callback, and customized with +// options. May return an error if the name is invalid (e.g., empty) +// or improperly registered (e.g., duplicate registration). +func (m Meter) RegisterFloat64SumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64SumObserver, error) { + if callback == nil { + return wrapFloat64SumObserverInstrument(NoopAsync{}, nil) + } + return wrapFloat64SumObserverInstrument( + m.newAsync(name, SumObserverKind, Float64NumberKind, opts, + newFloat64AsyncRunner(callback))) +} + // RegisterInt64ValueObserver creates a new integer ValueObserver instrument // with the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) @@ -169,6 +195,31 @@ func (b BatchObserver) RegisterFloat64ValueObserver(name string, opts ...Option) b.runner)) } +// RegisterInt64SumObserver creates a new integer SumObserver instrument +// with the given name, running in a batch callback, and customized with +// options. May return an error if the name is invalid (e.g., empty) +// or improperly registered (e.g., duplicate registration). +func (b BatchObserver) RegisterInt64SumObserver(name string, opts ...Option) (Int64SumObserver, error) { + if b.runner == nil { + return wrapInt64SumObserverInstrument(NoopAsync{}, nil) + } + return wrapInt64SumObserverInstrument( + b.meter.newAsync(name, SumObserverKind, Int64NumberKind, opts, b.runner)) +} + +// RegisterFloat64SumObserver creates a new floating point SumObserver with +// the given name, running in a batch callback, and customized with +// options. May return an error if the name is invalid (e.g., empty) +// or improperly registered (e.g., duplicate registration). +func (b BatchObserver) RegisterFloat64SumObserver(name string, opts ...Option) (Float64SumObserver, error) { + if b.runner == nil { + return wrapFloat64SumObserverInstrument(NoopAsync{}, nil) + } + return wrapFloat64SumObserverInstrument( + b.meter.newAsync(name, SumObserverKind, Float64NumberKind, opts, + b.runner)) +} + // MeterImpl returns the underlying MeterImpl of this Meter. func (m Meter) MeterImpl() MeterImpl { return m.impl diff --git a/api/metric/must.go b/api/metric/must.go index e734e8292..bf4b60284 100644 --- a/api/metric/must.go +++ b/api/metric/must.go @@ -113,6 +113,26 @@ func (mm MeterMust) RegisterFloat64ValueObserver(name string, callback Float64Ob } } +// RegisterInt64SumObserver calls `Meter.RegisterInt64SumObserver` and +// returns the instrument, panicking if it encounters an error. +func (mm MeterMust) RegisterInt64SumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64SumObserver { + if inst, err := mm.meter.RegisterInt64SumObserver(name, callback, oos...); err != nil { + panic(err) + } else { + return inst + } +} + +// RegisterFloat64SumObserver calls `Meter.RegisterFloat64SumObserver` and +// returns the instrument, panicking if it encounters an error. +func (mm MeterMust) RegisterFloat64SumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64SumObserver { + if inst, err := mm.meter.RegisterFloat64SumObserver(name, callback, oos...); err != nil { + panic(err) + } else { + return inst + } +} + // NewBatchObserver returns a wrapper around BatchObserver that panics // when any instrument constructor returns an error. func (mm MeterMust) NewBatchObserver(callback BatchObserverCallback) BatchObserverMust { @@ -140,3 +160,23 @@ func (bm BatchObserverMust) RegisterFloat64ValueObserver(name string, oos ...Opt return inst } } + +// RegisterInt64SumObserver calls `BatchObserver.RegisterInt64SumObserver` and +// returns the instrument, panicking if it encounters an error. +func (bm BatchObserverMust) RegisterInt64SumObserver(name string, oos ...Option) Int64SumObserver { + if inst, err := bm.batch.RegisterInt64SumObserver(name, oos...); err != nil { + panic(err) + } else { + return inst + } +} + +// RegisterFloat64SumObserver calls `BatchObserver.RegisterFloat64SumObserver` and +// returns the instrument, panicking if it encounters an error. +func (bm BatchObserverMust) RegisterFloat64SumObserver(name string, oos ...Option) Float64SumObserver { + if inst, err := bm.batch.RegisterFloat64SumObserver(name, oos...); err != nil { + panic(err) + } else { + return inst + } +} diff --git a/api/metric/observer.go b/api/metric/observer.go index 9d1a0582c..f9100f310 100644 --- a/api/metric/observer.go +++ b/api/metric/observer.go @@ -33,6 +33,18 @@ type Float64ValueObserver struct { asyncInstrument } +// Int64SumObserver is a metric that captures a precomputed sum of +// int64 values at a point in time. +type Int64SumObserver struct { + asyncInstrument +} + +// Float64SumObserver is a metric that captures a precomputed sum of +// float64 values at a point in time. +type Float64SumObserver struct { + asyncInstrument +} + // Observation returns an Observation, a BatchObserverCallback // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, @@ -54,3 +66,25 @@ func (f Float64ValueObserver) Observation(v float64) Observation { instrument: f.instrument, } } + +// Observation returns an Observation, a BatchObserverCallback +// argument, for an asynchronous integer instrument. +// This returns an implementation-level object for use by the SDK, +// users should not refer to this. +func (i Int64SumObserver) Observation(v int64) Observation { + return Observation{ + number: NewInt64Number(v), + instrument: i.instrument, + } +} + +// Observation returns an Observation, a BatchObserverCallback +// argument, for an asynchronous integer instrument. +// This returns an implementation-level object for use by the SDK, +// users should not refer to this. +func (f Float64SumObserver) Observation(v float64) Observation { + return Observation{ + number: NewFloat64Number(v), + instrument: f.instrument, + } +} diff --git a/sdk/export/metric/aggregator/aggregator.go b/sdk/export/metric/aggregator/aggregator.go index 660e83ef3..f0b6409e6 100644 --- a/sdk/export/metric/aggregator/aggregator.go +++ b/sdk/export/metric/aggregator/aggregator.go @@ -125,7 +125,7 @@ func RangeTest(number metric.Number, descriptor *metric.Descriptor) error { } switch descriptor.MetricKind() { - case metric.CounterKind: + case metric.CounterKind, metric.SumObserverKind: if number.IsNegative(numberKind) { return ErrNegativeInput } diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index 3ffd78929..8eccc0fa0 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -19,6 +19,7 @@ import ( "fmt" "math" "strings" + "sync" "sync/atomic" "testing" @@ -45,18 +46,37 @@ type correctnessIntegrator struct { t *testing.T records []export.Record + + sync.Mutex + err error } func newSDK(t *testing.T) (metric.Meter, *metricsdk.Accumulator, *correctnessIntegrator) { integrator := &correctnessIntegrator{ t: t, } - accum := metricsdk.NewAccumulator(integrator, metricsdk.WithResource(testResource)) + accum := metricsdk.NewAccumulator( + integrator, + metricsdk.WithResource(testResource), + metricsdk.WithErrorHandler(func(err error) { + integrator.Lock() + defer integrator.Unlock() + integrator.err = err + }), + ) meter := metric.WrapMeterImpl(accum, "test") return meter, accum, integrator } -func (cb *correctnessIntegrator) AggregatorFor(descriptor *metric.Descriptor) (agg export.Aggregator) { +func (ci *correctnessIntegrator) sdkErr() error { + ci.Lock() + defer ci.Unlock() + t := ci.err + ci.err = nil + return t +} + +func (ci *correctnessIntegrator) AggregatorFor(descriptor *metric.Descriptor) (agg export.Aggregator) { name := descriptor.Name() switch { @@ -68,21 +88,21 @@ func (cb *correctnessIntegrator) AggregatorFor(descriptor *metric.Descriptor) (a agg = array.New() } if agg != nil { - atomic.AddInt64(&cb.newAggCount, 1) + atomic.AddInt64(&ci.newAggCount, 1) } return } -func (cb *correctnessIntegrator) CheckpointSet() export.CheckpointSet { - cb.t.Fatal("Should not be called") +func (ci *correctnessIntegrator) CheckpointSet() export.CheckpointSet { + ci.t.Fatal("Should not be called") return nil } func (*correctnessIntegrator) FinishedCollection() { } -func (cb *correctnessIntegrator) Process(_ context.Context, record export.Record) error { - cb.records = append(cb.records, record) +func (ci *correctnessIntegrator) Process(_ context.Context, record export.Record) error { + ci.records = append(ci.records, record) return nil } @@ -313,19 +333,37 @@ func TestObserverCollection(t *testing.T) { result.Observe(1, kv.String("A", "B")) result.Observe(1) }) + + _ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(result metric.Float64ObserverResult) { + result.Observe(1, kv.String("A", "B")) + result.Observe(2, kv.String("A", "B")) + result.Observe(1, kv.String("C", "D")) + }) + _ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(result metric.Int64ObserverResult) { + result.Observe(2, kv.String("A", "B")) + result.Observe(1) + // last value wins + result.Observe(1, kv.String("A", "B")) + result.Observe(1) + }) + _ = Must(meter).RegisterInt64ValueObserver("empty.valueobserver", func(result metric.Int64ObserverResult) { }) collected := sdk.Collect(ctx) - require.Equal(t, 4, collected) - require.Equal(t, 4, len(integrator.records)) + require.Equal(t, 8, collected) + require.Equal(t, 8, len(integrator.records)) out := batchTest.NewOutput(label.DefaultEncoder()) for _, rec := range integrator.records { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ + "float.sumobserver/A=B/R=V": 2, + "float.sumobserver/C=D/R=V": 1, + "int.sumobserver//R=V": 1, + "int.sumobserver/A=B/R=V": 1, "float.valueobserver/A=B/R=V": -1, "float.valueobserver/C=D/R=V": -1, "int.valueobserver//R=V": 1, @@ -333,48 +371,88 @@ func TestObserverCollection(t *testing.T) { }, out.Map) } +func TestSumObserverInputRange(t *testing.T) { + ctx := context.Background() + meter, sdk, integrator := newSDK(t) + + _ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(result metric.Float64ObserverResult) { + result.Observe(-2, kv.String("A", "B")) + require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) + result.Observe(-1, kv.String("C", "D")) + require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) + }) + _ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(result metric.Int64ObserverResult) { + result.Observe(-1, kv.String("A", "B")) + require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) + result.Observe(-1) + require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) + }) + + collected := sdk.Collect(ctx) + + require.Equal(t, 0, collected) + require.Equal(t, 0, len(integrator.records)) + + // check that the error condition was reset + require.NoError(t, integrator.sdkErr()) +} + func TestObserverBatch(t *testing.T) { ctx := context.Background() meter, sdk, integrator := newSDK(t) - var floatObs metric.Float64ValueObserver - var intObs metric.Int64ValueObserver + var floatValueObs metric.Float64ValueObserver + var intValueObs metric.Int64ValueObserver + var floatSumObs metric.Float64SumObserver + var intSumObs metric.Int64SumObserver + var batch = Must(meter).NewBatchObserver( func(result metric.BatchObserverResult) { result.Observe( []kv.KeyValue{ kv.String("A", "B"), }, - floatObs.Observation(1), - floatObs.Observation(-1), - intObs.Observation(-1), - intObs.Observation(1), + floatValueObs.Observation(1), + floatValueObs.Observation(-1), + intValueObs.Observation(-1), + intValueObs.Observation(1), + floatSumObs.Observation(1000), + intSumObs.Observation(100), ) result.Observe( []kv.KeyValue{ kv.String("C", "D"), }, - floatObs.Observation(-1), + floatValueObs.Observation(-1), + floatSumObs.Observation(-1), ) result.Observe( nil, - intObs.Observation(1), - intObs.Observation(1), + intValueObs.Observation(1), + intValueObs.Observation(1), + intSumObs.Observation(10), + floatSumObs.Observation(1.1), ) }) - floatObs = batch.RegisterFloat64ValueObserver("float.valueobserver") - intObs = batch.RegisterInt64ValueObserver("int.valueobserver") + floatValueObs = batch.RegisterFloat64ValueObserver("float.valueobserver") + intValueObs = batch.RegisterInt64ValueObserver("int.valueobserver") + floatSumObs = batch.RegisterFloat64SumObserver("float.sumobserver") + intSumObs = batch.RegisterInt64SumObserver("int.sumobserver") collected := sdk.Collect(ctx) - require.Equal(t, 4, collected) - require.Equal(t, 4, len(integrator.records)) + require.Equal(t, 8, collected) + require.Equal(t, 8, len(integrator.records)) out := batchTest.NewOutput(label.DefaultEncoder()) for _, rec := range integrator.records { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ + "float.sumobserver//R=V": 1.1, + "float.sumobserver/A=B/R=V": 1000, + "int.sumobserver//R=V": 10, + "int.sumobserver/A=B/R=V": 100, "float.valueobserver/A=B/R=V": -1, "float.valueobserver/C=D/R=V": -1, "int.valueobserver//R=V": 1, From 1fab21ddbf29b4aa9729db035bb49718b2a6364c Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Tue, 19 May 2020 21:33:10 -0700 Subject: [PATCH 069/108] Support use of synchronous instruments in async callbacks (#725) * Support use of synchronous instruments in async callbacks * Add a test --- api/global/internal/meter_test.go | 6 ++-- api/global/internal/registry_test.go | 5 ++-- api/metric/api_test.go | 12 ++++---- api/metric/async.go | 28 +++++++++-------- api/metric/registry/registry_test.go | 5 ++-- example/basic/main.go | 2 +- example/prometheus/main.go | 2 +- exporters/otlp/otlp_integration_test.go | 4 +-- internal/metric/async.go | 7 +++-- internal/metric/mock.go | 2 +- sdk/metric/benchmark_test.go | 6 ++-- sdk/metric/correct_test.go | 40 ++++++++++++++++++++----- sdk/metric/sdk.go | 6 ++-- 13 files changed, 78 insertions(+), 47 deletions(-) diff --git a/api/global/internal/meter_test.go b/api/global/internal/meter_test.go index 5d9188c96..95438259c 100644 --- a/api/global/internal/meter_test.go +++ b/api/global/internal/meter_test.go @@ -86,12 +86,12 @@ func TestDirect(t *testing.T) { valuerecorder.Record(ctx, 1, labels1...) valuerecorder.Record(ctx, 2, labels1...) - _ = Must(meter1).RegisterFloat64ValueObserver("test.valueobserver.float", func(result metric.Float64ObserverResult) { + _ = Must(meter1).RegisterFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1., labels1...) result.Observe(2., labels2...) }) - _ = Must(meter1).RegisterInt64ValueObserver("test.valueobserver.int", func(result metric.Int64ObserverResult) { + _ = Must(meter1).RegisterInt64ValueObserver("test.valueobserver.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(1, labels1...) result.Observe(2, labels2...) }) @@ -333,7 +333,7 @@ func TestImplementationIndirection(t *testing.T) { // Async: no SDK yet valueobserver := Must(meter1).RegisterFloat64ValueObserver( "interface.valueobserver", - func(result metric.Float64ObserverResult) {}, + func(_ context.Context, result metric.Float64ObserverResult) {}, ) ival = valueobserver.AsyncImpl().Implementation() diff --git a/api/global/internal/registry_test.go b/api/global/internal/registry_test.go index 76144bf5b..a37ec22fd 100644 --- a/api/global/internal/registry_test.go +++ b/api/global/internal/registry_test.go @@ -15,6 +15,7 @@ package internal import ( + "context" "errors" "testing" @@ -43,10 +44,10 @@ var ( return unwrap(MeterProvider().Meter(libraryName).NewFloat64ValueRecorder(name)) }, "valueobserver.int64": func(name, libraryName string) (metric.InstrumentImpl, error) { - return unwrap(MeterProvider().Meter(libraryName).RegisterInt64ValueObserver(name, func(metric.Int64ObserverResult) {})) + return unwrap(MeterProvider().Meter(libraryName).RegisterInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {})) }, "valueobserver.float64": func(name, libraryName string) (metric.InstrumentImpl, error) { - return unwrap(MeterProvider().Meter(libraryName).RegisterFloat64ValueObserver(name, func(metric.Float64ObserverResult) {})) + return unwrap(MeterProvider().Meter(libraryName).RegisterFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {})) }, } ) diff --git a/api/metric/api_test.go b/api/metric/api_test.go index 0650269f1..369baa661 100644 --- a/api/metric/api_test.go +++ b/api/metric/api_test.go @@ -183,7 +183,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("float valueobserver", func(t *testing.T) { labels := []kv.KeyValue{kv.String("O", "P")} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterFloat64ValueObserver("test.valueobserver.float", func(result metric.Float64ObserverResult) { + o := Must(meter).RegisterFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(42.1, labels...) }) mockSDK.RunAsyncInstruments() @@ -194,7 +194,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("int valueobserver", func(t *testing.T) { labels := []kv.KeyValue{} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterInt64ValueObserver("test.observer.int", func(result metric.Int64ObserverResult) { + o := Must(meter).RegisterInt64ValueObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-142, labels...) }) mockSDK.RunAsyncInstruments() @@ -205,7 +205,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("float sumobserver", func(t *testing.T) { labels := []kv.KeyValue{kv.String("O", "P")} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterFloat64SumObserver("test.sumobserver.float", func(result metric.Float64ObserverResult) { + o := Must(meter).RegisterFloat64SumObserver("test.sumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(42.1, labels...) }) mockSDK.RunAsyncInstruments() @@ -216,7 +216,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("int sumobserver", func(t *testing.T) { labels := []kv.KeyValue{} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterInt64SumObserver("test.observer.int", func(result metric.Int64ObserverResult) { + o := Must(meter).RegisterInt64SumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-142, labels...) }) mockSDK.RunAsyncInstruments() @@ -280,7 +280,7 @@ func TestBatchObserverInstruments(t *testing.T) { } cb := Must(meter).NewBatchObserver( - func(result metric.BatchObserverResult) { + func(_ context.Context, result metric.BatchObserverResult) { result.Observe(labels, obs1.Observation(42), obs2.Observation(42.0), @@ -372,7 +372,7 @@ func TestWrappedInstrumentError(t *testing.T) { require.Equal(t, err, metric.ErrSDKReturnedNilImpl) require.NotNil(t, valuerecorder.SyncImpl()) - observer, err := meter.RegisterInt64ValueObserver("test.observer", func(result metric.Int64ObserverResult) {}) + observer, err := meter.RegisterInt64ValueObserver("test.observer", func(_ context.Context, result metric.Int64ObserverResult) {}) require.NotNil(t, err) require.NotNil(t, observer.AsyncImpl()) diff --git a/api/metric/async.go b/api/metric/async.go index e54f0cf0d..c82fdc409 100644 --- a/api/metric/async.go +++ b/api/metric/async.go @@ -14,7 +14,11 @@ package metric -import "go.opentelemetry.io/otel/api/kv" +import ( + "context" + + "go.opentelemetry.io/otel/api/kv" +) // The file is organized as follows: // @@ -38,16 +42,16 @@ type Observation struct { // Int64ObserverCallback is a type of callback that integral // observers run. -type Int64ObserverCallback func(Int64ObserverResult) +type Int64ObserverCallback func(context.Context, Int64ObserverResult) // Float64ObserverCallback is a type of callback that floating point // observers run. -type Float64ObserverCallback func(Float64ObserverResult) +type Float64ObserverCallback func(context.Context, Float64ObserverResult) // BatchObserverCallback is a callback argument for use with any // Observer instrument that will be reported as a batch of // observations. -type BatchObserverCallback func(BatchObserverResult) +type BatchObserverCallback func(context.Context, BatchObserverResult) // Int64ObserverResult is passed to an observer callback to capture // observations for one asynchronous integer metric instrument. @@ -110,7 +114,7 @@ type AsyncSingleRunner interface { // receives one captured observation. (The function accepts // multiple observations so the same implementation can be // used for batch runners.) - Run(single AsyncImpl, capture func([]kv.KeyValue, ...Observation)) + Run(ctx context.Context, single AsyncImpl, capture func([]kv.KeyValue, ...Observation)) AsyncRunner } @@ -120,7 +124,7 @@ type AsyncSingleRunner interface { type AsyncBatchRunner interface { // Run accepts a function for capturing observations of // multiple instruments. - Run(capture func([]kv.KeyValue, ...Observation)) + Run(ctx context.Context, capture func([]kv.KeyValue, ...Observation)) AsyncRunner } @@ -154,24 +158,24 @@ func (*Float64ObserverCallback) AnyRunner() {} func (*BatchObserverCallback) AnyRunner() {} // Run implements AsyncSingleRunner. -func (i *Int64ObserverCallback) Run(impl AsyncImpl, function func([]kv.KeyValue, ...Observation)) { - (*i)(Int64ObserverResult{ +func (i *Int64ObserverCallback) Run(ctx context.Context, impl AsyncImpl, function func([]kv.KeyValue, ...Observation)) { + (*i)(ctx, Int64ObserverResult{ instrument: impl, function: function, }) } // Run implements AsyncSingleRunner. -func (f *Float64ObserverCallback) Run(impl AsyncImpl, function func([]kv.KeyValue, ...Observation)) { - (*f)(Float64ObserverResult{ +func (f *Float64ObserverCallback) Run(ctx context.Context, impl AsyncImpl, function func([]kv.KeyValue, ...Observation)) { + (*f)(ctx, Float64ObserverResult{ instrument: impl, function: function, }) } // Run implements AsyncBatchRunner. -func (b *BatchObserverCallback) Run(function func([]kv.KeyValue, ...Observation)) { - (*b)(BatchObserverResult{ +func (b *BatchObserverCallback) Run(ctx context.Context, function func([]kv.KeyValue, ...Observation)) { + (*b)(ctx, BatchObserverResult{ function: function, }) } diff --git a/api/metric/registry/registry_test.go b/api/metric/registry/registry_test.go index 4f5c10a33..e80e23f39 100644 --- a/api/metric/registry/registry_test.go +++ b/api/metric/registry/registry_test.go @@ -15,6 +15,7 @@ package registry_test import ( + "context" "errors" "testing" @@ -44,10 +45,10 @@ var ( return unwrap(m.NewFloat64ValueRecorder(name)) }, "valueobserver.int64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { - return unwrap(m.RegisterInt64ValueObserver(name, func(metric.Int64ObserverResult) {})) + return unwrap(m.RegisterInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {})) }, "valueobserver.float64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { - return unwrap(m.RegisterFloat64ValueObserver(name, func(metric.Float64ObserverResult) {})) + return unwrap(m.RegisterFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {})) }, } ) diff --git a/example/basic/main.go b/example/basic/main.go index 04c4f8e49..fee62818a 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -73,7 +73,7 @@ func main() { commonLabels := []kv.KeyValue{lemonsKey.Int(10), kv.String("A", "1"), kv.String("B", "2"), kv.String("C", "3")} - oneMetricCB := func(result metric.Float64ObserverResult) { + oneMetricCB := func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1, commonLabels...) } _ = metric.Must(meter).RegisterFloat64ValueObserver("ex.com.one", oneMetricCB, diff --git a/example/prometheus/main.go b/example/prometheus/main.go index 378eb98e6..117a2b13f 100644 --- a/example/prometheus/main.go +++ b/example/prometheus/main.go @@ -52,7 +52,7 @@ func main() { observerLock := new(sync.RWMutex) observerValueToReport := new(float64) observerLabelsToReport := new([]kv.KeyValue) - cb := func(result metric.Float64ObserverResult) { + cb := func(_ context.Context, result metric.Float64ObserverResult) { (*observerLock).RLock() value := *observerValueToReport labels := *observerLabelsToReport diff --git a/exporters/otlp/otlp_integration_test.go b/exporters/otlp/otlp_integration_test.go index e4f61fdb6..04d17dbd8 100644 --- a/exporters/otlp/otlp_integration_test.go +++ b/exporters/otlp/otlp_integration_test.go @@ -155,12 +155,12 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) switch data.nKind { case metricapi.Int64NumberKind: callback := func(v int64) metricapi.Int64ObserverCallback { - return metricapi.Int64ObserverCallback(func(result metricapi.Int64ObserverResult) { result.Observe(v, labels...) }) + return metricapi.Int64ObserverCallback(func(_ context.Context, result metricapi.Int64ObserverResult) { result.Observe(v, labels...) }) }(data.val) metricapi.Must(meter).RegisterInt64ValueObserver(name, callback) case metricapi.Float64NumberKind: callback := func(v float64) metricapi.Float64ObserverCallback { - return metricapi.Float64ObserverCallback(func(result metricapi.Float64ObserverResult) { result.Observe(v, labels...) }) + return metricapi.Float64ObserverCallback(func(_ context.Context, result metricapi.Float64ObserverResult) { result.Observe(v, labels...) }) }(float64(data.val)) metricapi.Must(meter).RegisterFloat64ValueObserver(name, callback) default: diff --git a/internal/metric/async.go b/internal/metric/async.go index 07b7e01df..1be4ea361 100644 --- a/internal/metric/async.go +++ b/internal/metric/async.go @@ -15,6 +15,7 @@ package metric import ( + "context" "errors" "fmt" "os" @@ -133,7 +134,7 @@ func (a *AsyncInstrumentState) Register(inst metric.AsyncImpl, runner metric.Asy } // Run executes the complete set of observer callbacks. -func (a *AsyncInstrumentState) Run(collector AsyncCollector) { +func (a *AsyncInstrumentState) Run(ctx context.Context, collector AsyncCollector) { a.lock.Lock() runners := a.runners a.lock.Unlock() @@ -144,12 +145,12 @@ func (a *AsyncInstrumentState) Run(collector AsyncCollector) { // interface has un-exported methods. if singleRunner, ok := rp.runner.(metric.AsyncSingleRunner); ok { - singleRunner.Run(rp.inst, collector.CollectAsync) + singleRunner.Run(ctx, rp.inst, collector.CollectAsync) continue } if multiRunner, ok := rp.runner.(metric.AsyncBatchRunner); ok { - multiRunner.Run(collector.CollectAsync) + multiRunner.Run(ctx, collector.CollectAsync) continue } diff --git a/internal/metric/mock.go b/internal/metric/mock.go index 985ea7fc0..320d83053 100644 --- a/internal/metric/mock.go +++ b/internal/metric/mock.go @@ -187,5 +187,5 @@ func (m *MeterImpl) collect(ctx context.Context, labels []kv.KeyValue, measureme } func (m *MeterImpl) RunAsyncInstruments() { - m.asyncInstruments.Run(m) + m.asyncInstruments.Run(context.Background(), m) } diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index 3a6b9888d..f4a6b315f 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -425,7 +425,7 @@ func BenchmarkObserverRegistration(b *testing.B) { for i := 0; i < b.N; i++ { names = append(names, fmt.Sprintf("test.valueobserver.%d", i)) } - cb := func(result metric.Int64ObserverResult) {} + cb := func(_ context.Context, result metric.Int64ObserverResult) {} b.ResetTimer() @@ -438,7 +438,7 @@ func BenchmarkValueObserverObservationInt64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - _ = fix.meter.RegisterInt64ValueObserver("test.valueobserver", func(result metric.Int64ObserverResult) { + _ = fix.meter.RegisterInt64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((int64)(i), labs...) } @@ -453,7 +453,7 @@ func BenchmarkValueObserverObservationFloat64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - _ = fix.meter.RegisterFloat64ValueObserver("test.valueobserver", func(result metric.Float64ObserverResult) { + _ = fix.meter.RegisterFloat64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((float64)(i), labs...) } diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index 8eccc0fa0..d2c8a173d 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -320,13 +320,13 @@ func TestObserverCollection(t *testing.T) { ctx := context.Background() meter, sdk, integrator := newSDK(t) - _ = Must(meter).RegisterFloat64ValueObserver("float.valueobserver", func(result metric.Float64ObserverResult) { + _ = Must(meter).RegisterFloat64ValueObserver("float.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1, kv.String("A", "B")) // last value wins result.Observe(-1, kv.String("A", "B")) result.Observe(-1, kv.String("C", "D")) }) - _ = Must(meter).RegisterInt64ValueObserver("int.valueobserver", func(result metric.Int64ObserverResult) { + _ = Must(meter).RegisterInt64ValueObserver("int.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-1, kv.String("A", "B")) result.Observe(1) // last value wins @@ -334,12 +334,12 @@ func TestObserverCollection(t *testing.T) { result.Observe(1) }) - _ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(result metric.Float64ObserverResult) { + _ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1, kv.String("A", "B")) result.Observe(2, kv.String("A", "B")) result.Observe(1, kv.String("C", "D")) }) - _ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(result metric.Int64ObserverResult) { + _ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(2, kv.String("A", "B")) result.Observe(1) // last value wins @@ -347,7 +347,7 @@ func TestObserverCollection(t *testing.T) { result.Observe(1) }) - _ = Must(meter).RegisterInt64ValueObserver("empty.valueobserver", func(result metric.Int64ObserverResult) { + _ = Must(meter).RegisterInt64ValueObserver("empty.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { }) collected := sdk.Collect(ctx) @@ -375,13 +375,13 @@ func TestSumObserverInputRange(t *testing.T) { ctx := context.Background() meter, sdk, integrator := newSDK(t) - _ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(result metric.Float64ObserverResult) { + _ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(-2, kv.String("A", "B")) require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) result.Observe(-1, kv.String("C", "D")) require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) }) - _ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(result metric.Int64ObserverResult) { + _ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-1, kv.String("A", "B")) require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) result.Observe(-1) @@ -407,7 +407,7 @@ func TestObserverBatch(t *testing.T) { var intSumObs metric.Int64SumObserver var batch = Must(meter).NewBatchObserver( - func(result metric.BatchObserverResult) { + func(_ context.Context, result metric.BatchObserverResult) { result.Observe( []kv.KeyValue{ kv.String("A", "B"), @@ -514,3 +514,27 @@ func TestRecordPersistence(t *testing.T) { require.Equal(t, int64(2), integrator.newAggCount) } + +func TestSyncInAsync(t *testing.T) { + ctx := context.Background() + meter, sdk, integrator := newSDK(t) + + counter := Must(meter).NewFloat64Counter("counter") + _ = Must(meter).RegisterInt64ValueObserver("observer", + func(ctx context.Context, result metric.Int64ObserverResult) { + result.Observe(10) + counter.Add(ctx, 100) + }, + ) + + sdk.Collect(ctx) + + out := batchTest.NewOutput(label.DefaultEncoder()) + for _, rec := range integrator.records { + _ = out.AddTo(rec) + } + require.EqualValues(t, map[string]float64{ + "counter//R=V": 100, + "observer//R=V": 10, + }, out.Map) +} diff --git a/sdk/metric/sdk.go b/sdk/metric/sdk.go index f3939a41d..ff0f3853c 100644 --- a/sdk/metric/sdk.go +++ b/sdk/metric/sdk.go @@ -367,8 +367,8 @@ func (m *Accumulator) Collect(ctx context.Context) int { m.collectLock.Lock() defer m.collectLock.Unlock() - checkpointed := m.collectSyncInstruments(ctx) - checkpointed += m.observeAsyncInstruments(ctx) + checkpointed := m.observeAsyncInstruments(ctx) + checkpointed += m.collectSyncInstruments(ctx) m.currentEpoch++ return checkpointed @@ -434,7 +434,7 @@ func (m *Accumulator) observeAsyncInstruments(ctx context.Context) int { asyncCollected := 0 m.asyncContext = ctx - m.asyncInstruments.Run(m) + m.asyncInstruments.Run(context.Background(), m) m.asyncContext = nil for _, inst := range m.asyncInstruments.Instruments() { From e1b144e77505a962fc6b54aae4c9e04df2c633e7 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Wed, 20 May 2020 10:38:34 +0200 Subject: [PATCH 070/108] Wait for batcher to push changes --- example/otel-collector/main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index 7c4855895..ddbdb124d 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -66,11 +66,14 @@ func main() { // Then use the OpenTelemetry tracing library, like we normally would. ctx, span := tracer.Start(context.Background(), "CollectorExporter-Example") - defer span.End() for i := 0; i < 10; i++ { _, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i)) <-time.After(time.Second) iSpan.End() } + + span.End() + // Wait 1 second before ending + <-time.After(time.Second) } From 244ed23e44dcad96b7372bc3a5f0ec9076e3131b Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Wed, 20 May 2020 20:12:57 +0300 Subject: [PATCH 071/108] Rename ScheduleDelayMillis to BatchTimeout (#752) Co-authored-by: Joshua MacDonald --- example/zipkin/main.go | 2 +- exporters/otlp/example_test.go | 4 ++-- exporters/otlp/otlp_integration_test.go | 2 +- sdk/trace/batch_span_processor.go | 26 ++++++++++++------------- sdk/trace/batch_span_processor_test.go | 18 ++++++++--------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/example/zipkin/main.go b/example/zipkin/main.go index badfae971..94e0e6e29 100644 --- a/example/zipkin/main.go +++ b/example/zipkin/main.go @@ -49,7 +49,7 @@ func initTracer(url string) { tp, err := sdktrace.NewProvider( sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), sdktrace.WithBatcher(exporter, - sdktrace.WithScheduleDelayMillis(5), + sdktrace.WithBatchTimeout(5), sdktrace.WithMaxExportBatchSize(10), ), ) diff --git a/exporters/otlp/example_test.go b/exporters/otlp/example_test.go index 8c0a8b858..5cf14f92b 100644 --- a/exporters/otlp/example_test.go +++ b/exporters/otlp/example_test.go @@ -39,7 +39,7 @@ func Example_insecure() { tp, _ := sdktrace.NewProvider( sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), sdktrace.WithBatcher(exp, // add following two options to ensure flush - sdktrace.WithScheduleDelayMillis(5), + sdktrace.WithBatchTimeout(5), sdktrace.WithMaxExportBatchSize(10), )) if err != nil { @@ -80,7 +80,7 @@ func Example_withTLS() { tp, err := sdktrace.NewProvider( sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), sdktrace.WithBatcher(exp, // add following two options to ensure flush - sdktrace.WithScheduleDelayMillis(5), + sdktrace.WithBatchTimeout(5), sdktrace.WithMaxExportBatchSize(10), )) if err != nil { diff --git a/exporters/otlp/otlp_integration_test.go b/exporters/otlp/otlp_integration_test.go index 04d17dbd8..081437c71 100644 --- a/exporters/otlp/otlp_integration_test.go +++ b/exporters/otlp/otlp_integration_test.go @@ -81,7 +81,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) pOpts := []sdktrace.ProviderOption{ sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), sdktrace.WithBatcher(exp, // add following two options to ensure flush - sdktrace.WithScheduleDelayMillis(15), + sdktrace.WithBatchTimeout(15), sdktrace.WithMaxExportBatchSize(10), ), } diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index 31aa6da3a..4030e0a34 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -27,7 +27,7 @@ import ( const ( DefaultMaxQueueSize = 2048 - DefaultScheduledDelay = 5000 * time.Millisecond + DefaultBatchTimeout = 5000 * time.Millisecond DefaultMaxExportBatchSize = 512 ) @@ -43,10 +43,10 @@ type BatchSpanProcessorOptions struct { // The default value of MaxQueueSize is 2048. MaxQueueSize int - // ScheduledDelayMillis is the delay interval in milliseconds between two consecutive - // processing of batches. - // The default value of ScheduledDelayMillis is 5000 msec. - ScheduledDelayMillis time.Duration + // BatchTimeout is the maximum duration for constructing a batch. Processor + // forcefully sends available spans when timeout is reached. + // The default value of BatchTimeout is 5000 msec. + BatchTimeout time.Duration // MaxExportBatchSize is the maximum number of spans to process in a single batch. // If there are more than one batch worth of spans then it processes multiple batches @@ -90,9 +90,9 @@ func NewBatchSpanProcessor(e export.SpanBatcher, opts ...BatchSpanProcessorOptio } o := BatchSpanProcessorOptions{ - ScheduledDelayMillis: DefaultScheduledDelay, - MaxQueueSize: DefaultMaxQueueSize, - MaxExportBatchSize: DefaultMaxExportBatchSize, + BatchTimeout: DefaultBatchTimeout, + MaxQueueSize: DefaultMaxQueueSize, + MaxExportBatchSize: DefaultMaxExportBatchSize, } for _, opt := range opts { opt(&o) @@ -101,7 +101,7 @@ func NewBatchSpanProcessor(e export.SpanBatcher, opts ...BatchSpanProcessorOptio e: e, o: o, batch: make([]*export.SpanData, 0, o.MaxExportBatchSize), - timer: time.NewTimer(o.ScheduledDelayMillis), + timer: time.NewTimer(o.BatchTimeout), queue: make(chan *export.SpanData, o.MaxQueueSize), stopCh: make(chan struct{}), } @@ -147,9 +147,9 @@ func WithMaxExportBatchSize(size int) BatchSpanProcessorOption { } } -func WithScheduleDelayMillis(delay time.Duration) BatchSpanProcessorOption { +func WithBatchTimeout(delay time.Duration) BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { - o.ScheduledDelayMillis = delay + o.BatchTimeout = delay } } @@ -161,7 +161,7 @@ func WithBlocking() BatchSpanProcessorOption { // exportSpans is a subroutine of processing and draining the queue. func (bsp *BatchSpanProcessor) exportSpans() { - bsp.timer.Reset(bsp.o.ScheduledDelayMillis) + bsp.timer.Reset(bsp.o.BatchTimeout) if len(bsp.batch) > 0 { bsp.e.ExportSpans(context.Background(), bsp.batch) @@ -171,7 +171,7 @@ func (bsp *BatchSpanProcessor) exportSpans() { // processQueue removes spans from the `queue` channel until processor // is shut down. It calls the exporter in batches of up to MaxExportBatchSize -// waiting up to ScheduledDelayMillis to form a batch. +// waiting up to BatchTimeout to form a batch. func (bsp *BatchSpanProcessor) processQueue() { defer bsp.stopWait.Done() defer bsp.timer.Stop() diff --git a/sdk/trace/batch_span_processor_test.go b/sdk/trace/batch_span_processor_test.go index abc91f4cd..3e80a20d3 100644 --- a/sdk/trace/batch_span_processor_test.go +++ b/sdk/trace/batch_span_processor_test.go @@ -82,18 +82,18 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { genNumSpans: 2053, }, { - name: "non-default ScheduledDelayMillis", + name: "non-default BatchTimeout", o: []sdktrace.BatchSpanProcessorOption{ - sdktrace.WithScheduleDelayMillis(schDelay), + sdktrace.WithBatchTimeout(schDelay), }, wantNumSpans: 2053, wantBatchCount: 4, genNumSpans: 2053, }, { - name: "non-default MaxQueueSize and ScheduledDelayMillis", + name: "non-default MaxQueueSize and BatchTimeout", o: []sdktrace.BatchSpanProcessorOption{ - sdktrace.WithScheduleDelayMillis(schDelay), + sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxQueueSize(200), }, wantNumSpans: 205, @@ -101,9 +101,9 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { genNumSpans: 205, }, { - name: "non-default MaxQueueSize, ScheduledDelayMillis and MaxExportBatchSize", + name: "non-default MaxQueueSize, BatchTimeout and MaxExportBatchSize", o: []sdktrace.BatchSpanProcessorOption{ - sdktrace.WithScheduleDelayMillis(schDelay), + sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxQueueSize(205), sdktrace.WithMaxExportBatchSize(20), }, @@ -114,7 +114,7 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { { name: "blocking option", o: []sdktrace.BatchSpanProcessorOption{ - sdktrace.WithScheduleDelayMillis(schDelay), + sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxQueueSize(200), sdktrace.WithMaxExportBatchSize(20), sdktrace.WithBlocking(), @@ -126,7 +126,7 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { { name: "parallel span generation", o: []sdktrace.BatchSpanProcessorOption{ - sdktrace.WithScheduleDelayMillis(schDelay), + sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxQueueSize(200), }, wantNumSpans: 205, @@ -137,7 +137,7 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) { { name: "parallel span blocking", o: []sdktrace.BatchSpanProcessorOption{ - sdktrace.WithScheduleDelayMillis(schDelay), + sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxExportBatchSize(200), sdktrace.WithBlocking(), }, From 15e8edd498c29b1e1398fd50adc1db2a82b4bb4a Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Wed, 20 May 2020 10:19:51 -0700 Subject: [PATCH 072/108] Add the UpDownSumObserver instrument (#750) * Add the UpDownSumObserver instrument * Precommit * Downcase error message --- api/metric/api_test.go | 22 +++++++++ api/metric/async.go | 12 +++++ api/metric/kind.go | 2 + api/metric/kind_string.go | 5 ++- api/metric/meter.go | 51 +++++++++++++++++++++ api/metric/must.go | 40 +++++++++++++++++ api/metric/observer.go | 34 ++++++++++++++ sdk/metric/correct_test.go | 91 +++++++++++++++++++++++++++++++++----- sdk/metric/sdk.go | 42 +++++++++++++++--- 9 files changed, 279 insertions(+), 20 deletions(-) diff --git a/api/metric/api_test.go b/api/metric/api_test.go index 369baa661..e5c194977 100644 --- a/api/metric/api_test.go +++ b/api/metric/api_test.go @@ -224,6 +224,28 @@ func TestObserverInstruments(t *testing.T) { -142, ) }) + t.Run("float updownsumobserver", func(t *testing.T) { + labels := []kv.KeyValue{kv.String("O", "P")} + mockSDK, meter := mockTest.NewMeter() + o := Must(meter).RegisterFloat64UpDownSumObserver("test.updownsumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { + result.Observe(42.1, labels...) + }) + mockSDK.RunAsyncInstruments() + checkObserverBatch(t, labels, mockSDK, metric.Float64NumberKind, metric.UpDownSumObserverKind, o.AsyncImpl(), + 42.1, + ) + }) + t.Run("int updownsumobserver", func(t *testing.T) { + labels := []kv.KeyValue{} + mockSDK, meter := mockTest.NewMeter() + o := Must(meter).RegisterInt64UpDownSumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) { + result.Observe(-142, labels...) + }) + mockSDK.RunAsyncInstruments() + checkObserverBatch(t, labels, mockSDK, metric.Int64NumberKind, metric.UpDownSumObserverKind, o.AsyncImpl(), + -142, + ) + }) } func checkSyncBatches(t *testing.T, ctx context.Context, labels []kv.KeyValue, mock *mockTest.MeterImpl, nkind metric.NumberKind, mkind metric.Kind, instrument metric.InstrumentImpl, expected ...float64) { diff --git a/api/metric/async.go b/api/metric/async.go index c82fdc409..d721dffef 100644 --- a/api/metric/async.go +++ b/api/metric/async.go @@ -203,3 +203,15 @@ func wrapFloat64SumObserverInstrument(asyncInst AsyncImpl, err error) (Float64Su common, err := checkNewAsync(asyncInst, err) return Float64SumObserver{asyncInstrument: common}, err } + +// wrapInt64UpDownSumObserverInstrument converts an AsyncImpl into Int64UpDownSumObserver. +func wrapInt64UpDownSumObserverInstrument(asyncInst AsyncImpl, err error) (Int64UpDownSumObserver, error) { + common, err := checkNewAsync(asyncInst, err) + return Int64UpDownSumObserver{asyncInstrument: common}, err +} + +// wrapFloat64UpDownSumObserverInstrument converts an AsyncImpl into Float64UpDownSumObserver. +func wrapFloat64UpDownSumObserverInstrument(asyncInst AsyncImpl, err error) (Float64UpDownSumObserver, error) { + common, err := checkNewAsync(asyncInst, err) + return Float64UpDownSumObserver{asyncInstrument: common}, err +} diff --git a/api/metric/kind.go b/api/metric/kind.go index fca4fa6fb..5020384c5 100644 --- a/api/metric/kind.go +++ b/api/metric/kind.go @@ -32,4 +32,6 @@ const ( // SumObserverKind indicates a SumObserver instrument. SumObserverKind + // UpDownSumObserverKind indicates a UpDownSumObserver instrument. + UpDownSumObserverKind ) diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index 33118e2a0..eb1a0d503 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -13,11 +13,12 @@ func _() { _ = x[CounterKind-2] _ = x[UpDownCounterKind-3] _ = x[SumObserverKind-4] + _ = x[UpDownSumObserverKind-5] } -const _Kind_name = "ValueRecorderKindValueObserverKindCounterKindUpDownCounterKindSumObserverKind" +const _Kind_name = "ValueRecorderKindValueObserverKindCounterKindUpDownCounterKindSumObserverKindUpDownSumObserverKind" -var _Kind_index = [...]uint8{0, 17, 34, 45, 62, 77} +var _Kind_index = [...]uint8{0, 17, 34, 45, 62, 77, 98} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { diff --git a/api/metric/meter.go b/api/metric/meter.go index e1b546bf9..4caa7a7a6 100644 --- a/api/metric/meter.go +++ b/api/metric/meter.go @@ -170,6 +170,32 @@ func (m Meter) RegisterFloat64SumObserver(name string, callback Float64ObserverC newFloat64AsyncRunner(callback))) } +// RegisterInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument +// with the given name, running a given callback, and customized with +// options. May return an error if the name is invalid (e.g., empty) +// or improperly registered (e.g., duplicate registration). +func (m Meter) RegisterInt64UpDownSumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64UpDownSumObserver, error) { + if callback == nil { + return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil) + } + return wrapInt64UpDownSumObserverInstrument( + m.newAsync(name, UpDownSumObserverKind, Int64NumberKind, opts, + newInt64AsyncRunner(callback))) +} + +// RegisterFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with +// the given name, running a given callback, and customized with +// options. May return an error if the name is invalid (e.g., empty) +// or improperly registered (e.g., duplicate registration). +func (m Meter) RegisterFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64UpDownSumObserver, error) { + if callback == nil { + return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil) + } + return wrapFloat64UpDownSumObserverInstrument( + m.newAsync(name, UpDownSumObserverKind, Float64NumberKind, opts, + newFloat64AsyncRunner(callback))) +} + // RegisterInt64ValueObserver creates a new integer ValueObserver instrument // with the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) @@ -220,6 +246,31 @@ func (b BatchObserver) RegisterFloat64SumObserver(name string, opts ...Option) ( b.runner)) } +// RegisterInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument +// with the given name, running in a batch callback, and customized with +// options. May return an error if the name is invalid (e.g., empty) +// or improperly registered (e.g., duplicate registration). +func (b BatchObserver) RegisterInt64UpDownSumObserver(name string, opts ...Option) (Int64UpDownSumObserver, error) { + if b.runner == nil { + return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil) + } + return wrapInt64UpDownSumObserverInstrument( + b.meter.newAsync(name, UpDownSumObserverKind, Int64NumberKind, opts, b.runner)) +} + +// RegisterFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with +// the given name, running in a batch callback, and customized with +// options. May return an error if the name is invalid (e.g., empty) +// or improperly registered (e.g., duplicate registration). +func (b BatchObserver) RegisterFloat64UpDownSumObserver(name string, opts ...Option) (Float64UpDownSumObserver, error) { + if b.runner == nil { + return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil) + } + return wrapFloat64UpDownSumObserverInstrument( + b.meter.newAsync(name, UpDownSumObserverKind, Float64NumberKind, opts, + b.runner)) +} + // MeterImpl returns the underlying MeterImpl of this Meter. func (m Meter) MeterImpl() MeterImpl { return m.impl diff --git a/api/metric/must.go b/api/metric/must.go index bf4b60284..9c5115812 100644 --- a/api/metric/must.go +++ b/api/metric/must.go @@ -133,6 +133,26 @@ func (mm MeterMust) RegisterFloat64SumObserver(name string, callback Float64Obse } } +// RegisterInt64UpDownSumObserver calls `Meter.RegisterInt64UpDownSumObserver` and +// returns the instrument, panicking if it encounters an error. +func (mm MeterMust) RegisterInt64UpDownSumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64UpDownSumObserver { + if inst, err := mm.meter.RegisterInt64UpDownSumObserver(name, callback, oos...); err != nil { + panic(err) + } else { + return inst + } +} + +// RegisterFloat64UpDownSumObserver calls `Meter.RegisterFloat64UpDownSumObserver` and +// returns the instrument, panicking if it encounters an error. +func (mm MeterMust) RegisterFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64UpDownSumObserver { + if inst, err := mm.meter.RegisterFloat64UpDownSumObserver(name, callback, oos...); err != nil { + panic(err) + } else { + return inst + } +} + // NewBatchObserver returns a wrapper around BatchObserver that panics // when any instrument constructor returns an error. func (mm MeterMust) NewBatchObserver(callback BatchObserverCallback) BatchObserverMust { @@ -180,3 +200,23 @@ func (bm BatchObserverMust) RegisterFloat64SumObserver(name string, oos ...Optio return inst } } + +// RegisterInt64UpDownSumObserver calls `BatchObserver.RegisterInt64UpDownSumObserver` and +// returns the instrument, panicking if it encounters an error. +func (bm BatchObserverMust) RegisterInt64UpDownSumObserver(name string, oos ...Option) Int64UpDownSumObserver { + if inst, err := bm.batch.RegisterInt64UpDownSumObserver(name, oos...); err != nil { + panic(err) + } else { + return inst + } +} + +// RegisterFloat64UpDownSumObserver calls `BatchObserver.RegisterFloat64UpDownSumObserver` and +// returns the instrument, panicking if it encounters an error. +func (bm BatchObserverMust) RegisterFloat64UpDownSumObserver(name string, oos ...Option) Float64UpDownSumObserver { + if inst, err := bm.batch.RegisterFloat64UpDownSumObserver(name, oos...); err != nil { + panic(err) + } else { + return inst + } +} diff --git a/api/metric/observer.go b/api/metric/observer.go index f9100f310..1616908cc 100644 --- a/api/metric/observer.go +++ b/api/metric/observer.go @@ -45,6 +45,18 @@ type Float64SumObserver struct { asyncInstrument } +// Int64UpDownSumObserver is a metric that captures a precomputed sum of +// int64 values at a point in time. +type Int64UpDownSumObserver struct { + asyncInstrument +} + +// Float64UpDownSumObserver is a metric that captures a precomputed sum of +// float64 values at a point in time. +type Float64UpDownSumObserver struct { + asyncInstrument +} + // Observation returns an Observation, a BatchObserverCallback // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, @@ -88,3 +100,25 @@ func (f Float64SumObserver) Observation(v float64) Observation { instrument: f.instrument, } } + +// Observation returns an Observation, a BatchObserverCallback +// argument, for an asynchronous integer instrument. +// This returns an implementation-level object for use by the SDK, +// users should not refer to this. +func (i Int64UpDownSumObserver) Observation(v int64) Observation { + return Observation{ + number: NewInt64Number(v), + instrument: i.instrument, + } +} + +// Observation returns an Observation, a BatchObserverCallback +// argument, for an asynchronous integer instrument. +// This returns an implementation-level object for use by the SDK, +// users should not refer to this. +func (f Float64UpDownSumObserver) Observation(v float64) Observation { + return Observation{ + number: NewFloat64Number(v), + instrument: f.instrument, + } +} diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index d2c8a173d..35a64c16b 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -347,27 +347,45 @@ func TestObserverCollection(t *testing.T) { result.Observe(1) }) + _ = Must(meter).RegisterFloat64UpDownSumObserver("float.updownsumobserver", func(_ context.Context, result metric.Float64ObserverResult) { + result.Observe(1, kv.String("A", "B")) + result.Observe(-2, kv.String("A", "B")) + result.Observe(1, kv.String("C", "D")) + }) + _ = Must(meter).RegisterInt64UpDownSumObserver("int.updownsumobserver", func(_ context.Context, result metric.Int64ObserverResult) { + result.Observe(2, kv.String("A", "B")) + result.Observe(1) + // last value wins + result.Observe(1, kv.String("A", "B")) + result.Observe(-1) + }) + _ = Must(meter).RegisterInt64ValueObserver("empty.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { }) collected := sdk.Collect(ctx) - require.Equal(t, 8, collected) - require.Equal(t, 8, len(integrator.records)) + require.Equal(t, collected, len(integrator.records)) out := batchTest.NewOutput(label.DefaultEncoder()) for _, rec := range integrator.records { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ - "float.sumobserver/A=B/R=V": 2, - "float.sumobserver/C=D/R=V": 1, - "int.sumobserver//R=V": 1, - "int.sumobserver/A=B/R=V": 1, "float.valueobserver/A=B/R=V": -1, "float.valueobserver/C=D/R=V": -1, "int.valueobserver//R=V": 1, "int.valueobserver/A=B/R=V": 1, + + "float.sumobserver/A=B/R=V": 2, + "float.sumobserver/C=D/R=V": 1, + "int.sumobserver//R=V": 1, + "int.sumobserver/A=B/R=V": 1, + + "float.updownsumobserver/A=B/R=V": -2, + "float.updownsumobserver/C=D/R=V": 1, + "int.updownsumobserver//R=V": -1, + "int.updownsumobserver/A=B/R=V": 1, }, out.Map) } @@ -405,6 +423,8 @@ func TestObserverBatch(t *testing.T) { var intValueObs metric.Int64ValueObserver var floatSumObs metric.Float64SumObserver var intSumObs metric.Int64SumObserver + var floatUpDownSumObs metric.Float64UpDownSumObserver + var intUpDownSumObs metric.Int64UpDownSumObserver var batch = Must(meter).NewBatchObserver( func(_ context.Context, result metric.BatchObserverResult) { @@ -418,6 +438,8 @@ func TestObserverBatch(t *testing.T) { intValueObs.Observation(1), floatSumObs.Observation(1000), intSumObs.Observation(100), + floatUpDownSumObs.Observation(-1000), + intUpDownSumObs.Observation(-100), ) result.Observe( []kv.KeyValue{ @@ -425,6 +447,7 @@ func TestObserverBatch(t *testing.T) { }, floatValueObs.Observation(-1), floatSumObs.Observation(-1), + floatUpDownSumObs.Observation(-1), ) result.Observe( nil, @@ -432,27 +455,35 @@ func TestObserverBatch(t *testing.T) { intValueObs.Observation(1), intSumObs.Observation(10), floatSumObs.Observation(1.1), + intUpDownSumObs.Observation(10), ) }) floatValueObs = batch.RegisterFloat64ValueObserver("float.valueobserver") intValueObs = batch.RegisterInt64ValueObserver("int.valueobserver") floatSumObs = batch.RegisterFloat64SumObserver("float.sumobserver") intSumObs = batch.RegisterInt64SumObserver("int.sumobserver") + floatUpDownSumObs = batch.RegisterFloat64UpDownSumObserver("float.updownsumobserver") + intUpDownSumObs = batch.RegisterInt64UpDownSumObserver("int.updownsumobserver") collected := sdk.Collect(ctx) - require.Equal(t, 8, collected) - require.Equal(t, 8, len(integrator.records)) + require.Equal(t, collected, len(integrator.records)) out := batchTest.NewOutput(label.DefaultEncoder()) for _, rec := range integrator.records { _ = out.AddTo(rec) } require.EqualValues(t, map[string]float64{ - "float.sumobserver//R=V": 1.1, - "float.sumobserver/A=B/R=V": 1000, - "int.sumobserver//R=V": 10, - "int.sumobserver/A=B/R=V": 100, + "float.sumobserver//R=V": 1.1, + "float.sumobserver/A=B/R=V": 1000, + "int.sumobserver//R=V": 10, + "int.sumobserver/A=B/R=V": 100, + + "int.updownsumobserver/A=B/R=V": -100, + "float.updownsumobserver/A=B/R=V": -1000, + "int.updownsumobserver//R=V": 10, + "float.updownsumobserver/C=D/R=V": -1, + "float.valueobserver/A=B/R=V": -1, "float.valueobserver/C=D/R=V": -1, "int.valueobserver//R=V": 1, @@ -515,6 +546,42 @@ func TestRecordPersistence(t *testing.T) { require.Equal(t, int64(2), integrator.newAggCount) } +func TestIncorrectInstruments(t *testing.T) { + // The Batch observe/record APIs are susceptible to + // uninitialized instruments. + var counter metric.Int64Counter + var observer metric.Int64ValueObserver + + ctx := context.Background() + meter, sdk, integrator := newSDK(t) + + // Now try with uninitialized instruments. + meter.RecordBatch(ctx, nil, counter.Measurement(1)) + meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) { + result.Observe(nil, observer.Observation(1)) + }) + + collected := sdk.Collect(ctx) + require.Equal(t, metricsdk.ErrUninitializedInstrument, integrator.sdkErr()) + require.Equal(t, 0, collected) + + // Now try with instruments from another SDK. + var noopMeter metric.Meter + counter = metric.Must(noopMeter).NewInt64Counter("counter") + observer = metric.Must(noopMeter).NewBatchObserver( + func(context.Context, metric.BatchObserverResult) {}, + ).RegisterInt64ValueObserver("observer") + + meter.RecordBatch(ctx, nil, counter.Measurement(1)) + meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) { + result.Observe(nil, observer.Observation(1)) + }) + + collected = sdk.Collect(ctx) + require.Equal(t, 0, collected) + require.Equal(t, metricsdk.ErrUninitializedInstrument, integrator.sdkErr()) +} + func TestSyncInAsync(t *testing.T) { ctx := context.Background() meter, sdk, integrator := newSDK(t) diff --git a/sdk/metric/sdk.go b/sdk/metric/sdk.go index ff0f3853c..10883ac06 100644 --- a/sdk/metric/sdk.go +++ b/sdk/metric/sdk.go @@ -152,6 +152,8 @@ var ( _ api.AsyncImpl = &asyncInstrument{} _ api.SyncImpl = &syncInstrument{} _ api.BoundSyncImpl = &record{} + + ErrUninitializedInstrument = fmt.Errorf("use of an uninitialized instrument") ) func (inst *instrument) Descriptor() api.Descriptor { @@ -422,8 +424,9 @@ func (m *Accumulator) CollectAsync(kv []kv.KeyValue, obs ...metric.Observation) labels := label.NewSetWithSortable(kv, &m.asyncSortSlice) for _, ob := range obs { - a := ob.AsyncImpl().Implementation().(*asyncInstrument) - a.observe(ob.Number(), &labels) + if a := m.fromAsync(ob.AsyncImpl()); a != nil { + a.observe(ob.Number(), &labels) + } } } @@ -438,8 +441,9 @@ func (m *Accumulator) observeAsyncInstruments(ctx context.Context) int { m.asyncContext = nil for _, inst := range m.asyncInstruments.Instruments() { - a := inst.Implementation().(*asyncInstrument) - asyncCollected += m.checkpointAsync(a) + if a := m.fromAsync(inst); a != nil { + asyncCollected += m.checkpointAsync(a) + } } return asyncCollected @@ -494,8 +498,10 @@ func (m *Accumulator) RecordBatch(ctx context.Context, kvs []kv.KeyValue, measur // ordered labels. var labelsPtr *label.Set for i, meas := range measurements { - s := meas.SyncImpl().Implementation().(*syncInstrument) - + s := m.fromSync(meas.SyncImpl()) + if s == nil { + continue + } h := s.acquireHandle(kvs, labelsPtr) // Re-use labels for the next measurement. @@ -538,3 +544,27 @@ func (r *record) mapkey() mapkey { ordered: r.labels.Equivalent(), } } + +// fromSync gets a sync implementation object, checking for +// uninitialized instruments and instruments created by another SDK. +func (m *Accumulator) fromSync(sync metric.SyncImpl) *syncInstrument { + if sync != nil { + if inst, ok := sync.Implementation().(*syncInstrument); ok { + return inst + } + } + m.errorHandler(ErrUninitializedInstrument) + return nil +} + +// fromSync gets an async implementation object, checking for +// uninitialized instruments and instruments created by another SDK. +func (m *Accumulator) fromAsync(async metric.AsyncImpl) *asyncInstrument { + if async != nil { + if inst, ok := async.Implementation().(*asyncInstrument); ok { + return inst + } + } + m.errorHandler(ErrUninitializedInstrument) + return nil +} From 5461669733c8ddb5b4f90a462211af77fda99474 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Wed, 20 May 2020 10:27:26 -0700 Subject: [PATCH 073/108] Add a pull controller, use it for Prometheus (#751) * Checkpoint new pull controller * Tests pass * Fix example * Example fix * Add a test * Comment * address MrAlias's feedback --- example/prometheus/main.go | 9 +- exporters/metric/prometheus/example_test.go | 21 +-- exporters/metric/prometheus/prometheus.go | 121 ++++++++---------- .../metric/prometheus/prometheus_test.go | 118 +++++------------ sdk/metric/controller/pull/config.go | 97 ++++++++++++++ sdk/metric/controller/pull/pull.go | 113 ++++++++++++++++ sdk/metric/controller/pull/pull_test.go | 112 ++++++++++++++++ sdk/metric/selector/simple/simple.go | 16 +-- 8 files changed, 416 insertions(+), 191 deletions(-) create mode 100644 sdk/metric/controller/pull/config.go create mode 100644 sdk/metric/controller/pull/pull.go create mode 100644 sdk/metric/controller/pull/pull_test.go diff --git a/example/prometheus/main.go b/example/prometheus/main.go index 117a2b13f..8000dec0e 100644 --- a/example/prometheus/main.go +++ b/example/prometheus/main.go @@ -25,15 +25,14 @@ import ( "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/exporters/metric/prometheus" - "go.opentelemetry.io/otel/sdk/metric/controller/push" ) var ( lemonsKey = kv.Key("ex.com/lemons") ) -func initMeter() *push.Controller { - pusher, exporter, err := prometheus.InstallNewPipeline(prometheus.Config{}) +func initMeter() { + exporter, err := prometheus.InstallNewPipeline(prometheus.Config{}) if err != nil { log.Panicf("failed to initialize prometheus exporter %v", err) } @@ -41,12 +40,10 @@ func initMeter() *push.Controller { go func() { _ = http.ListenAndServe(":2222", nil) }() - - return pusher } func main() { - defer initMeter().Stop() + initMeter() meter := global.Meter("ex.com/basic") observerLock := new(sync.RWMutex) diff --git a/exporters/metric/prometheus/example_test.go b/exporters/metric/prometheus/example_test.go index 1a15e38de..9c7092866 100644 --- a/exporters/metric/prometheus/example_test.go +++ b/exporters/metric/prometheus/example_test.go @@ -25,30 +25,22 @@ import ( "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/exporters/metric/prometheus" - sdk "go.opentelemetry.io/otel/sdk/metric" - integrator "go.opentelemetry.io/otel/sdk/metric/integrator/simple" - "go.opentelemetry.io/otel/sdk/metric/selector/simple" ) // This test demonstrates that it is relatively difficult to setup a // Prometheus export pipeline: // // 1. The default boundaries are difficult to pass, should be []float instead of []metric.Number -// 2. The push controller doesn't make sense b/c Prometheus is pull-bsaed // -// TODO: Address these issues; add Resources to the test. +// TODO: Address this issue; add Resources to the test. func ExampleNewExportPipeline() { // Create a meter - selector := simple.NewWithHistogramDistribution(nil) - exporter, err := prometheus.NewRawExporter(prometheus.Config{}) + exporter, err := prometheus.NewExportPipeline(prometheus.Config{}) if err != nil { panic(err) } - integrator := integrator.New(selector, true) - meterImpl := sdk.NewAccumulator(integrator) - meter := metric.WrapMeterImpl(meterImpl, "example") - + meter := exporter.Provider().Meter("example") ctx := context.Background() // Use two instruments @@ -64,13 +56,6 @@ func ExampleNewExportPipeline() { counter.Add(ctx, 100, kv.String("key", "value")) recorder.Record(ctx, 100, kv.String("key", "value")) - // Simulate a push - meterImpl.Collect(ctx) - err = exporter.Export(ctx, integrator.CheckpointSet()) - if err != nil { - panic(err) - } - // GET the HTTP endpoint var input bytes.Buffer resp := httptest.NewRecorder() diff --git a/exporters/metric/prometheus/prometheus.go b/exporters/metric/prometheus/prometheus.go index 44f7fb016..d76a1faf3 100644 --- a/exporters/metric/prometheus/prometheus.go +++ b/exporters/metric/prometheus/prometheus.go @@ -29,27 +29,34 @@ import ( "go.opentelemetry.io/otel/api/label" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregator" - "go.opentelemetry.io/otel/sdk/metric/controller/push" + "go.opentelemetry.io/otel/sdk/metric/controller/pull" "go.opentelemetry.io/otel/sdk/metric/selector/simple" ) // Exporter is an implementation of metric.Exporter that sends metrics to // Prometheus. +// +// This exporter supports Prometheus pulls, as such it does not +// implement the export.Exporter interface. type Exporter struct { handler http.Handler registerer prometheus.Registerer gatherer prometheus.Gatherer - lock sync.RWMutex - snapshot export.CheckpointSet - onError func(error) + // lock protects access to the controller. The controller + // exposes its own lock, but using a dedicated lock in this + // struct allows the exporter to potentially support multiple + // controllers (e.g., with different resources). + lock sync.RWMutex + controller *pull.Controller + + onError func(error) defaultSummaryQuantiles []float64 defaultHistogramBoundaries []metric.Number } -var _ export.Exporter = &Exporter{} var _ http.Handler = &Exporter{} // Config is a set of configs for the tally reporter. @@ -85,9 +92,9 @@ type Config struct { OnError func(error) } -// NewRawExporter returns a new prometheus exporter for prometheus metrics -// for use in a pipeline. -func NewRawExporter(config Config) (*Exporter, error) { +// NewExportPipeline sets up a complete export pipeline with the recommended setup, +// using the recommended selector and standard integrator. See the pull.Options. +func NewExportPipeline(config Config, options ...pull.Option) (*Exporter, error) { if config.Registry == nil { config.Registry = prometheus.NewRegistry() } @@ -115,9 +122,12 @@ func NewRawExporter(config Config) (*Exporter, error) { onError: config.OnError, } - c := newCollector(e) + c := &collector{ + exp: e, + } + e.SetController(config, options...) if err := config.Registerer.Register(c); err != nil { - config.OnError(fmt.Errorf("cannot register the collector: %w", err)) + return nil, fmt.Errorf("cannot register the collector: %w", err) } return e, nil @@ -126,7 +136,7 @@ func NewRawExporter(config Config) (*Exporter, error) { // InstallNewPipeline instantiates a NewExportPipeline and registers it globally. // Typically called as: // -// pipeline, hf, err := prometheus.InstallNewPipeline(prometheus.Config{...}) +// hf, err := prometheus.InstallNewPipeline(prometheus.Config{...}) // // if err != nil { // ... @@ -134,28 +144,21 @@ func NewRawExporter(config Config) (*Exporter, error) { // http.HandleFunc("/metrics", hf) // defer pipeline.Stop() // ... Done -func InstallNewPipeline(config Config, options ...push.Option) (*push.Controller, *Exporter, error) { - controller, exp, err := NewExportPipeline(config, options...) +func InstallNewPipeline(config Config, options ...pull.Option) (*Exporter, error) { + exp, err := NewExportPipeline(config, options...) if err != nil { - return controller, exp, err + return nil, err } - global.SetMeterProvider(controller.Provider()) - return controller, exp, err + global.SetMeterProvider(exp.Provider()) + return exp, nil } -// NewExportPipeline sets up a complete export pipeline with the recommended setup, -// chaining a NewRawExporter into the recommended selectors and integrators. -// -// The returned Controller contains an implementation of -// `metric.Provider`. The controller is returned unstarted and should -// be started by the caller to begin collection. -func NewExportPipeline(config Config, options ...push.Option) (*push.Controller, *Exporter, error) { - exporter, err := NewRawExporter(config) - if err != nil { - return nil, nil, err - } - - // Prometheus uses a stateful push controller since instruments are +// SetController sets up a standard *pull.Controller as the metric provider +// for this exporter. +func (e *Exporter) SetController(config Config, options ...pull.Option) { + e.lock.Lock() + defer e.lock.Unlock() + // Prometheus uses a stateful pull controller since instruments are // cumulative and should not be reset after each collection interval. // // Prometheus uses this approach to be resilient to scrape failures. @@ -163,22 +166,29 @@ func NewExportPipeline(config Config, options ...push.Option) (*push.Controller, // it could try again on the next scrape and no data would be lost, only resolution. // // Gauges (or LastValues) and Summaries are an exception to this and have different behaviors. - pusher := push.New( + // + // TODO: Prometheus supports "Gauge Histogram" which are + // expressed as delta histograms. + e.controller = pull.New( simple.NewWithHistogramDistribution(config.DefaultHistogramBoundaries), - exporter, - append(options, push.WithStateful(true))..., + append(options, pull.WithStateful(true))..., ) - - return pusher, exporter, nil } -// Export exports the provide metric record to prometheus. -func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error { - // TODO: Use the resource value in this exporter. - e.lock.Lock() - defer e.lock.Unlock() - e.snapshot = checkpointSet - return nil +// Provider returns the metric.Provider of this exporter. +func (e *Exporter) Provider() metric.Provider { + return e.controller.Provider() +} + +// Controller returns the controller object that coordinates collection for the SDK. +func (e *Exporter) Controller() *pull.Controller { + e.lock.RLock() + defer e.lock.RUnlock() + return e.controller +} + +func (e *Exporter) ServeHTTP(w http.ResponseWriter, r *http.Request) { + e.handler.ServeHTTP(w, r) } // collector implements prometheus.Collector interface. @@ -188,24 +198,11 @@ type collector struct { var _ prometheus.Collector = (*collector)(nil) -func newCollector(exporter *Exporter) *collector { - return &collector{ - exp: exporter, - } -} - func (c *collector) Describe(ch chan<- *prometheus.Desc) { c.exp.lock.RLock() defer c.exp.lock.RUnlock() - if c.exp.snapshot == nil { - return - } - - c.exp.snapshot.RLock() - defer c.exp.snapshot.RUnlock() - - _ = c.exp.snapshot.ForEach(func(record export.Record) error { + _ = c.exp.Controller().ForEach(func(record export.Record) error { ch <- c.toDesc(&record) return nil }) @@ -219,14 +216,10 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) { c.exp.lock.RLock() defer c.exp.lock.RUnlock() - if c.exp.snapshot == nil { - return - } + ctrl := c.exp.Controller() + ctrl.Collect(context.Background()) - c.exp.snapshot.RLock() - defer c.exp.snapshot.RUnlock() - - err := c.exp.snapshot.ForEach(func(record export.Record) error { + err := ctrl.ForEach(func(record export.Record) error { agg := record.Aggregator() numberKind := record.Descriptor().NumberKind() // TODO: Use the resource value in this record. @@ -359,10 +352,6 @@ func (c *collector) toDesc(record *export.Record) *prometheus.Desc { return prometheus.NewDesc(sanitize(desc.Name()), desc.Description(), labels, nil) } -func (e *Exporter) ServeHTTP(w http.ResponseWriter, r *http.Request) { - e.handler.ServeHTTP(w, r) -} - func labelsKeys(labels *label.Set) []string { iter := labels.Iter() keys := make([]string, 0, iter.Len()) diff --git a/exporters/metric/prometheus/prometheus_test.go b/exporters/metric/prometheus/prometheus_test.go index 254cd1e55..db87e0313 100644 --- a/exporters/metric/prometheus/prometheus_test.go +++ b/exporters/metric/prometheus/prometheus_test.go @@ -20,116 +20,64 @@ import ( "io/ioutil" "net/http" "net/http/httptest" - "runtime" "sort" "strings" "testing" - "time" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/exporters/metric/prometheus" - exportTest "go.opentelemetry.io/otel/exporters/metric/test" - "go.opentelemetry.io/otel/sdk/metric/controller/push" - controllerTest "go.opentelemetry.io/otel/sdk/metric/controller/test" + "go.opentelemetry.io/otel/sdk/metric/controller/pull" ) func TestPrometheusExporter(t *testing.T) { - exporter, err := prometheus.NewRawExporter(prometheus.Config{ - DefaultSummaryQuantiles: []float64{0.5, 0.9, 0.99}, + exporter, err := prometheus.NewExportPipeline(prometheus.Config{ + DefaultHistogramBoundaries: []metric.Number{metric.NewFloat64Number(-0.5), metric.NewFloat64Number(1)}, }) require.NoError(t, err) - var expected []string - checkpointSet := exportTest.NewCheckpointSet(nil) + meter := exporter.Provider().Meter("test") - counter := metric.NewDescriptor( - "counter", metric.CounterKind, metric.Float64NumberKind) - lastValue := metric.NewDescriptor( - "lastvalue", metric.ValueObserverKind, metric.Float64NumberKind) - valuerecorder := metric.NewDescriptor( - "valuerecorder", metric.ValueRecorderKind, metric.Float64NumberKind) - histogramValueRecorder := metric.NewDescriptor( - "histogram_valuerecorder", metric.ValueRecorderKind, metric.Float64NumberKind) + counter := metric.Must(meter).NewFloat64Counter("counter") + valuerecorder := metric.Must(meter).NewFloat64ValueRecorder("valuerecorder") labels := []kv.KeyValue{ kv.Key("A").String("B"), kv.Key("C").String("D"), } + ctx := context.Background() + + var expected []string + + counter.Add(ctx, 10, labels...) + counter.Add(ctx, 5.3, labels...) - checkpointSet.AddCounter(&counter, 15.3, labels...) expected = append(expected, `counter{A="B",C="D"} 15.3`) - checkpointSet.AddLastValue(&lastValue, 13.2, labels...) - expected = append(expected, `lastvalue{A="B",C="D"} 13.2`) + valuerecorder.Record(ctx, -0.6, labels...) + valuerecorder.Record(ctx, -0.4, labels...) + valuerecorder.Record(ctx, 0.6, labels...) + valuerecorder.Record(ctx, 20, labels...) - checkpointSet.AddValueRecorder(&valuerecorder, 13, labels...) - checkpointSet.AddValueRecorder(&valuerecorder, 15, labels...) - checkpointSet.AddValueRecorder(&valuerecorder, 17, labels...) - expected = append(expected, `valuerecorder{A="B",C="D",quantile="0.5"} 15`) - expected = append(expected, `valuerecorder{A="B",C="D",quantile="0.9"} 17`) - expected = append(expected, `valuerecorder{A="B",C="D",quantile="0.99"} 17`) - expected = append(expected, `valuerecorder_sum{A="B",C="D"} 45`) - expected = append(expected, `valuerecorder_count{A="B",C="D"} 3`) + expected = append(expected, `valuerecorder_bucket{A="B",C="D",le="+Inf"} 4`) + expected = append(expected, `valuerecorder_bucket{A="B",C="D",le="-0.5"} 1`) + expected = append(expected, `valuerecorder_bucket{A="B",C="D",le="1"} 3`) + expected = append(expected, `valuerecorder_count{A="B",C="D"} 4`) + expected = append(expected, `valuerecorder_sum{A="B",C="D"} 19.6`) - boundaries := []metric.Number{metric.NewFloat64Number(-0.5), metric.NewFloat64Number(1)} - checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.6, labels...) - checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.4, labels...) - checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, 0.6, labels...) - checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, 20, labels...) - - expected = append(expected, `histogram_valuerecorder_bucket{A="B",C="D",le="+Inf"} 4`) - expected = append(expected, `histogram_valuerecorder_bucket{A="B",C="D",le="-0.5"} 1`) - expected = append(expected, `histogram_valuerecorder_bucket{A="B",C="D",le="1"} 3`) - expected = append(expected, `histogram_valuerecorder_count{A="B",C="D"} 4`) - expected = append(expected, `histogram_valuerecorder_sum{A="B",C="D"} 19.6`) - - missingLabels := []kv.KeyValue{ - kv.Key("A").String("E"), - kv.Key("C").String(""), - } - - checkpointSet.AddCounter(&counter, 12, missingLabels...) - expected = append(expected, `counter{A="E",C=""} 12`) - - checkpointSet.AddLastValue(&lastValue, 32, missingLabels...) - expected = append(expected, `lastvalue{A="E",C=""} 32`) - - checkpointSet.AddValueRecorder(&valuerecorder, 19, missingLabels...) - expected = append(expected, `valuerecorder{A="E",C="",quantile="0.5"} 19`) - expected = append(expected, `valuerecorder{A="E",C="",quantile="0.9"} 19`) - expected = append(expected, `valuerecorder{A="E",C="",quantile="0.99"} 19`) - expected = append(expected, `valuerecorder_count{A="E",C=""} 1`) - expected = append(expected, `valuerecorder_sum{A="E",C=""} 19`) - - boundaries = []metric.Number{metric.NewFloat64Number(0), metric.NewFloat64Number(1)} - checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.6, missingLabels...) - checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.4, missingLabels...) - checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, -0.1, missingLabels...) - checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, 15, missingLabels...) - checkpointSet.AddHistogramValueRecorder(&histogramValueRecorder, boundaries, 15, missingLabels...) - - expected = append(expected, `histogram_valuerecorder_bucket{A="E",C="",le="+Inf"} 5`) - expected = append(expected, `histogram_valuerecorder_bucket{A="E",C="",le="0"} 3`) - expected = append(expected, `histogram_valuerecorder_bucket{A="E",C="",le="1"} 3`) - expected = append(expected, `histogram_valuerecorder_count{A="E",C=""} 5`) - expected = append(expected, `histogram_valuerecorder_sum{A="E",C=""} 28.9`) - - compareExport(t, exporter, checkpointSet, expected) + compareExport(t, exporter, expected) } -func compareExport(t *testing.T, exporter *prometheus.Exporter, checkpointSet *exportTest.CheckpointSet, expected []string) { - err := exporter.Export(context.Background(), checkpointSet) - require.Nil(t, err) - +func compareExport(t *testing.T, exporter *prometheus.Exporter, expected []string) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/metrics", nil) exporter.ServeHTTP(rec, req) output := rec.Body.String() lines := strings.Split(output, "\n") + var metricsOnly []string for _, line := range lines { if !strings.HasPrefix(line, "#") && line != "" { @@ -145,13 +93,13 @@ func compareExport(t *testing.T, exporter *prometheus.Exporter, checkpointSet *e func TestPrometheusStatefulness(t *testing.T) { // Create a meter - controller, exporter, err := prometheus.NewExportPipeline(prometheus.Config{}, push.WithPeriod(time.Minute)) + exporter, err := prometheus.NewExportPipeline( + prometheus.Config{}, + pull.WithCachePeriod(0), + ) require.NoError(t, err) - meter := controller.Provider().Meter("test") - mock := controllerTest.NewMockClock() - controller.SetClock(mock) - controller.Start() + meter := exporter.Provider().Meter("test") // GET the HTTP endpoint scrape := func() string { @@ -176,10 +124,6 @@ func TestPrometheusStatefulness(t *testing.T) { counter.Add(ctx, 100, kv.String("key", "value")) - // Trigger a push - mock.Add(time.Minute) - runtime.Gosched() - require.Equal(t, `# HELP a_counter Counts things # TYPE a_counter counter a_counter{key="value"} 100 @@ -187,10 +131,6 @@ a_counter{key="value"} 100 counter.Add(ctx, 100, kv.String("key", "value")) - // Again, now expect cumulative count - mock.Add(time.Minute) - runtime.Gosched() - require.Equal(t, `# HELP a_counter Counts things # TYPE a_counter counter a_counter{key="value"} 200 diff --git a/sdk/metric/controller/pull/config.go b/sdk/metric/controller/pull/config.go new file mode 100644 index 000000000..56509fbcc --- /dev/null +++ b/sdk/metric/controller/pull/config.go @@ -0,0 +1,97 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pull + +import ( + "time" + + sdk "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/resource" +) + +// Config contains configuration for a push Controller. +type Config struct { + // ErrorHandler is the function called when the Controller encounters an error. + // + // This option can be overridden after instantiation of the Controller + // with the `SetErrorHandler` method. + ErrorHandler sdk.ErrorHandler + + // Resource is the OpenTelemetry resource associated with all Meters + // created by the Controller. + Resource *resource.Resource + + // Stateful causes the controller to maintain state across + // collection events, so that records in the exported + // checkpoint set are cumulative. + Stateful bool + + // CachePeriod is the period which a recently-computed result + // will be returned without gathering metric data again. + // + // If the period is zero, caching of the result is disabled. + // The default value is 10 seconds. + CachePeriod time.Duration +} + +// Option is the interface that applies the value to a configuration option. +type Option interface { + // Apply sets the Option value of a Config. + Apply(*Config) +} + +// WithErrorHandler sets the ErrorHandler configuration option of a Config. +func WithErrorHandler(fn sdk.ErrorHandler) Option { + return errorHandlerOption(fn) +} + +type errorHandlerOption sdk.ErrorHandler + +func (o errorHandlerOption) Apply(config *Config) { + config.ErrorHandler = sdk.ErrorHandler(o) +} + +// WithResource sets the Resource configuration option of a Config. +func WithResource(r *resource.Resource) Option { + return resourceOption{r} +} + +type resourceOption struct{ *resource.Resource } + +func (o resourceOption) Apply(config *Config) { + config.Resource = o.Resource +} + +// WithStateful sets the Stateful configuration option of a Config. +func WithStateful(stateful bool) Option { + return statefulOption(stateful) +} + +type statefulOption bool + +func (o statefulOption) Apply(config *Config) { + config.Stateful = bool(o) +} + +// WithCachePeriod sets the CachePeriod configuration option of a Config. +func WithCachePeriod(cachePeriod time.Duration) Option { + return cachePeriodOption(cachePeriod) +} + +type cachePeriodOption time.Duration + +func (o cachePeriodOption) Apply(config *Config) { + config.CachePeriod = time.Duration(o) +} diff --git a/sdk/metric/controller/pull/pull.go b/sdk/metric/controller/pull/pull.go new file mode 100644 index 000000000..eabd7732a --- /dev/null +++ b/sdk/metric/controller/pull/pull.go @@ -0,0 +1,113 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pull // import "go.opentelemetry.io/otel/sdk/metric/controller/pull" + +import ( + "context" + "time" + + "go.opentelemetry.io/otel/api/metric" + "go.opentelemetry.io/otel/api/metric/registry" + export "go.opentelemetry.io/otel/sdk/export/metric" + sdk "go.opentelemetry.io/otel/sdk/metric" + controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time" + integrator "go.opentelemetry.io/otel/sdk/metric/integrator/simple" + "go.opentelemetry.io/otel/sdk/resource" +) + +// DefaultCachePeriod determines how long a recently-computed result +// will be returned without gathering metric data again. +const DefaultCachePeriod time.Duration = 10 * time.Second + +// Controller manages access to a *sdk.Accumulator and +// *simple.Integrator. Use Provider() for obtaining Meters. Use +// Foreach() for accessing current records. +type Controller struct { + accumulator *sdk.Accumulator + integrator *integrator.Integrator + provider *registry.Provider + period time.Duration + lastCollect time.Time + clock controllerTime.Clock + checkpoint export.CheckpointSet +} + +// New returns a *Controller configured with an aggregation selector and options. +func New(selector export.AggregationSelector, options ...Option) *Controller { + config := &Config{ + Resource: resource.Empty(), + ErrorHandler: sdk.DefaultErrorHandler, + CachePeriod: DefaultCachePeriod, + } + for _, opt := range options { + opt.Apply(config) + } + integrator := integrator.New(selector, config.Stateful) + accum := sdk.NewAccumulator( + integrator, + sdk.WithResource(config.Resource), + sdk.WithErrorHandler(config.ErrorHandler), + ) + return &Controller{ + accumulator: accum, + integrator: integrator, + provider: registry.NewProvider(accum), + period: config.CachePeriod, + checkpoint: integrator.CheckpointSet(), + clock: controllerTime.RealClock{}, + } +} + +// SetClock sets the clock used for caching. For testing purposes. +func (c *Controller) SetClock(clock controllerTime.Clock) { + c.integrator.Lock() + defer c.integrator.Unlock() + c.clock = clock +} + +// Provider returns a metric.Provider for the implementation managed +// by this controller. +func (c *Controller) Provider() metric.Provider { + return c.provider +} + +// Foreach gives the caller read-locked access to the current +// export.CheckpointSet. +func (c *Controller) ForEach(f func(export.Record) error) error { + c.integrator.RLock() + defer c.integrator.RUnlock() + + return c.checkpoint.ForEach(f) +} + +// Collect requests a collection. The collection will be skipped if +// the last collection is aged less than the CachePeriod. +func (c *Controller) Collect(ctx context.Context) { + c.integrator.Lock() + defer c.integrator.Unlock() + + if c.period > 0 { + now := c.clock.Now() + elapsed := now.Sub(c.lastCollect) + + if elapsed < c.period { + return + } + c.lastCollect = now + } + + c.accumulator.Collect(ctx) + c.checkpoint = c.integrator.CheckpointSet() +} diff --git a/sdk/metric/controller/pull/pull_test.go b/sdk/metric/controller/pull/pull_test.go new file mode 100644 index 000000000..b8d4cb583 --- /dev/null +++ b/sdk/metric/controller/pull/pull_test.go @@ -0,0 +1,112 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pull_test + +import ( + "context" + "runtime" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/api/kv" + "go.opentelemetry.io/otel/api/label" + "go.opentelemetry.io/otel/api/metric" + "go.opentelemetry.io/otel/sdk/metric/controller/pull" + controllerTest "go.opentelemetry.io/otel/sdk/metric/controller/test" + "go.opentelemetry.io/otel/sdk/metric/integrator/test" + selector "go.opentelemetry.io/otel/sdk/metric/selector/simple" +) + +func TestPullNoCache(t *testing.T) { + puller := pull.New( + selector.NewWithExactDistribution(), + pull.WithCachePeriod(0), + pull.WithStateful(true), + ) + + ctx := context.Background() + meter := puller.Provider().Meter("nocache") + counter := metric.Must(meter).NewInt64Counter("counter") + + counter.Add(ctx, 10, kv.String("A", "B")) + + puller.Collect(ctx) + records := test.NewOutput(label.DefaultEncoder()) + _ = puller.ForEach(records.AddTo) + + require.EqualValues(t, map[string]float64{ + "counter/A=B/": 10, + }, records.Map) + + counter.Add(ctx, 10, kv.String("A", "B")) + + puller.Collect(ctx) + records = test.NewOutput(label.DefaultEncoder()) + _ = puller.ForEach(records.AddTo) + + require.EqualValues(t, map[string]float64{ + "counter/A=B/": 20, + }, records.Map) +} + +func TestPullWithCache(t *testing.T) { + puller := pull.New( + selector.NewWithExactDistribution(), + pull.WithCachePeriod(time.Second), + pull.WithStateful(true), + ) + mock := controllerTest.NewMockClock() + puller.SetClock(mock) + + ctx := context.Background() + meter := puller.Provider().Meter("nocache") + counter := metric.Must(meter).NewInt64Counter("counter") + + counter.Add(ctx, 10, kv.String("A", "B")) + + puller.Collect(ctx) + records := test.NewOutput(label.DefaultEncoder()) + _ = puller.ForEach(records.AddTo) + + require.EqualValues(t, map[string]float64{ + "counter/A=B/": 10, + }, records.Map) + + counter.Add(ctx, 10, kv.String("A", "B")) + + // Cached value! + puller.Collect(ctx) + records = test.NewOutput(label.DefaultEncoder()) + _ = puller.ForEach(records.AddTo) + + require.EqualValues(t, map[string]float64{ + "counter/A=B/": 10, + }, records.Map) + + mock.Add(time.Second) + runtime.Gosched() + + // Re-computed value! + puller.Collect(ctx) + records = test.NewOutput(label.DefaultEncoder()) + _ = puller.ForEach(records.AddTo) + + require.EqualValues(t, map[string]float64{ + "counter/A=B/": 20, + }, records.Map) + +} diff --git a/sdk/metric/selector/simple/simple.go b/sdk/metric/selector/simple/simple.go index b27d0af47..b64a5e971 100644 --- a/sdk/metric/selector/simple/simple.go +++ b/sdk/metric/selector/simple/simple.go @@ -81,9 +81,7 @@ func NewWithHistogramDistribution(boundaries []metric.Number) export.Aggregation func (selectorInexpensive) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { switch descriptor.MetricKind() { - case metric.ValueObserverKind: - fallthrough - case metric.ValueRecorderKind: + case metric.ValueObserverKind, metric.ValueRecorderKind: return minmaxsumcount.New(descriptor) default: return sum.New() @@ -92,9 +90,7 @@ func (selectorInexpensive) AggregatorFor(descriptor *metric.Descriptor) export.A func (s selectorSketch) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { switch descriptor.MetricKind() { - case metric.ValueObserverKind: - fallthrough - case metric.ValueRecorderKind: + case metric.ValueObserverKind, metric.ValueRecorderKind: return ddsketch.New(s.config, descriptor) default: return sum.New() @@ -103,9 +99,7 @@ func (s selectorSketch) AggregatorFor(descriptor *metric.Descriptor) export.Aggr func (selectorExact) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { switch descriptor.MetricKind() { - case metric.ValueObserverKind: - fallthrough - case metric.ValueRecorderKind: + case metric.ValueObserverKind, metric.ValueRecorderKind: return array.New() default: return sum.New() @@ -114,9 +108,7 @@ func (selectorExact) AggregatorFor(descriptor *metric.Descriptor) export.Aggrega func (s selectorHistogram) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { switch descriptor.MetricKind() { - case metric.ValueObserverKind: - fallthrough - case metric.ValueRecorderKind: + case metric.ValueObserverKind, metric.ValueRecorderKind: return histogram.New(descriptor, s.boundaries) default: return sum.New() From 923a6c8afccbcaf20c8654388da484e68535d8a8 Mon Sep 17 00:00:00 2001 From: Anthony J Mirabella Date: Wed, 20 May 2020 16:08:43 -0400 Subject: [PATCH 074/108] Replace sdktrace.WithResourceAttributes() with WithResource() --- example/basic/main.go | 3 ++- example/otel-collector/go.mod | 5 +++++ example/otel-collector/main.go | 7 ++++--- exporters/otlp/otlp_integration_test.go | 13 +++++++++---- sdk/trace/provider.go | 9 ++++----- sdk/trace/trace_test.go | 2 +- 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/example/basic/main.go b/example/basic/main.go index fee62818a..6d42fd4d1 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -26,6 +26,7 @@ import ( metricstdout "go.opentelemetry.io/otel/exporters/metric/stdout" tracestdout "go.opentelemetry.io/otel/exporters/trace/stdout" "go.opentelemetry.io/otel/sdk/metric/controller/push" + "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) @@ -46,7 +47,7 @@ func initTracer() { } tp, err := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), - sdktrace.WithResourceAttributes(kv.String("rk1", "rv11"), kv.Int64("rk2", 5))) + sdktrace.WithResource(resource.New(kv.String("rk1", "rv11"), kv.Int64("rk2", 5)))) if err != nil { log.Panicf("failed to initialize trace provider %v", err) } diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod index 0ceb645ec..c81c00eb0 100644 --- a/example/otel-collector/go.mod +++ b/example/otel-collector/go.mod @@ -8,3 +8,8 @@ require ( go.opentelemetry.io/otel/exporters/otlp v0.5.0 google.golang.org/grpc v1.29.1 ) + +replace ( + go.opentelemetry.io/otel => ../.. + go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp +) diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index ddbdb124d..c16002f7e 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -25,6 +25,7 @@ import ( "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/exporters/otlp" + "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" "github.com/open-telemetry/opentelemetry-collector/translator/conventions" @@ -50,12 +51,12 @@ func main() { tp, err := sdktrace.NewProvider( sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), - sdktrace.WithResourceAttributes( + sdktrace.WithResource(resource.New( // the service name used to display traces in Jaeger kv.Key(conventions.AttributeServiceName).String("test-service"), - ), + )), sdktrace.WithBatcher(exp, // add following two options to ensure flush - sdktrace.WithScheduleDelayMillis(5), + sdktrace.WithBatchTimeout(5), sdktrace.WithMaxExportBatchSize(2), )) if err != nil { diff --git a/exporters/otlp/otlp_integration_test.go b/exporters/otlp/otlp_integration_test.go index 081437c71..41f5bb195 100644 --- a/exporters/otlp/otlp_integration_test.go +++ b/exporters/otlp/otlp_integration_test.go @@ -34,6 +34,7 @@ import ( "go.opentelemetry.io/otel/sdk/metric/controller/push" integrator "go.opentelemetry.io/otel/sdk/metric/integrator/simple" "go.opentelemetry.io/otel/sdk/metric/selector/simple" + "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) @@ -86,13 +87,17 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) ), } tp1, err := sdktrace.NewProvider(append(pOpts, - sdktrace.WithResourceAttributes(kv.String("rk1", "rv11)"), - kv.Int64("rk2", 5)))...) + sdktrace.WithResource(resource.New( + kv.String("rk1", "rv11)"), + kv.Int64("rk2", 5), + )))...) assert.NoError(t, err) tp2, err := sdktrace.NewProvider(append(pOpts, - sdktrace.WithResourceAttributes(kv.String("rk1", "rv12)"), - kv.Float32("rk3", 6.5)))...) + sdktrace.WithResource(resource.New( + kv.String("rk1", "rv12)"), + kv.Float32("rk3", 6.5), + )))...) assert.NoError(t, err) tr1 := tp1.Tracer("test-tracer1") diff --git a/sdk/trace/provider.go b/sdk/trace/provider.go index 6b596e4d1..b493c7d53 100644 --- a/sdk/trace/provider.go +++ b/sdk/trace/provider.go @@ -21,7 +21,6 @@ import ( export "go.opentelemetry.io/otel/sdk/export/trace" "go.opentelemetry.io/otel/sdk/resource" - "go.opentelemetry.io/otel/api/kv" apitrace "go.opentelemetry.io/otel/api/trace" ) @@ -195,10 +194,10 @@ func WithConfig(config Config) ProviderOption { } } -// WithResourceAttributes option sets the resource attributes to the provider. -// Resource is added to the span when it is started. -func WithResourceAttributes(attrs ...kv.KeyValue) ProviderOption { +// WithResource option attaches a resource to the provider. +// The resource is added to the span when it is started. +func WithResource(r *resource.Resource) ProviderOption { return func(opts *ProviderOptions) { - opts.config.Resource = resource.New(attrs...) + opts.config.Resource = r } } diff --git a/sdk/trace/trace_test.go b/sdk/trace/trace_test.go index a2dc2af54..786f504a0 100644 --- a/sdk/trace/trace_test.go +++ b/sdk/trace/trace_test.go @@ -1067,7 +1067,7 @@ func TestWithResource(t *testing.T) { var te testExporter tp, _ := NewProvider(WithSyncer(&te), WithConfig(Config{DefaultSampler: AlwaysSample()}), - WithResourceAttributes(kv.String("rk1", "rv1"), kv.Int64("rk2", 5))) + WithResource(resource.New(kv.String("rk1", "rv1"), kv.Int64("rk2", 5)))) span := startSpan(tp, "WithResource") span.SetAttributes(kv.String("key1", "value1")) got, err := endSpan(&te, span) From 068ad5b3a5c5a64b268d1d97136b67ce9c83d29a Mon Sep 17 00:00:00 2001 From: Anthony J Mirabella Date: Wed, 20 May 2020 16:42:34 -0400 Subject: [PATCH 075/108] make precommit to cleanup otel-collector/go.sum --- example/otel-collector/go.sum | 4 ---- 1 file changed, 4 deletions(-) diff --git a/example/otel-collector/go.sum b/example/otel-collector/go.sum index d28718445..683a554df 100644 --- a/example/otel-collector/go.sum +++ b/example/otel-collector/go.sum @@ -929,10 +929,6 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v0.5.0 h1:tdIR1veg/z+VRJaw/6SIxz+QX3l+m+BDleYLTs+GC1g= -go.opentelemetry.io/otel v0.5.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek= -go.opentelemetry.io/otel/exporters/otlp v0.5.0 h1:dfS89YmU0e6HmmULuJQ9s3xnfz2uu1LHz29wseFt0Jc= -go.opentelemetry.io/otel/exporters/otlp v0.5.0/go.mod h1:uQseOXa3qUrjJRaRl8At4ISGr55GgKPkILaovWY5EI4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= From f9bf364f23da5cfca858c3dc48ec752c0a065ac5 Mon Sep 17 00:00:00 2001 From: Dave McGregor Date: Wed, 20 May 2020 18:07:53 -0400 Subject: [PATCH 076/108] Ensure gRPC ClientStream override methods do not panic. Previously, the channel used to aggregate the finished state of the stream could be closed while still open to receiving stream state events. This removes the closing of the channel, and instead adds a "done" channel that is used to skip sending to the channel after the receiver is done. --- plugin/grpctrace/interceptor.go | 38 ++++++++++++++++++---------- plugin/grpctrace/interceptor_test.go | 3 +++ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/plugin/grpctrace/interceptor.go b/plugin/grpctrace/interceptor.go index 0981954c4..6c8b6315e 100644 --- a/plugin/grpctrace/interceptor.go +++ b/plugin/grpctrace/interceptor.go @@ -133,9 +133,10 @@ const ( type clientStream struct { grpc.ClientStream - desc *grpc.StreamDesc - events chan streamEvent - finished chan error + desc *grpc.StreamDesc + events chan streamEvent + eventsDone chan struct{} + finished chan error receivedMessageID int sentMessageID int @@ -147,11 +148,11 @@ func (w *clientStream) RecvMsg(m interface{}) error { err := w.ClientStream.RecvMsg(m) if err == nil && !w.desc.ServerStreams { - w.events <- streamEvent{receiveEndEvent, nil} + w.sendStreamEvent(receiveEndEvent, nil) } else if err == io.EOF { - w.events <- streamEvent{receiveEndEvent, nil} + w.sendStreamEvent(receiveEndEvent, nil) } else if err != nil { - w.events <- streamEvent{errorEvent, err} + w.sendStreamEvent(errorEvent, err) } else { w.receivedMessageID++ messageReceived.Event(w.Context(), w.receivedMessageID, m) @@ -167,7 +168,7 @@ func (w *clientStream) SendMsg(m interface{}) error { messageSent.Event(w.Context(), w.sentMessageID, m) if err != nil { - w.events <- streamEvent{errorEvent, err} + w.sendStreamEvent(errorEvent, err) } return err @@ -177,7 +178,7 @@ func (w *clientStream) Header() (metadata.MD, error) { md, err := w.ClientStream.Header() if err != nil { - w.events <- streamEvent{errorEvent, err} + w.sendStreamEvent(errorEvent, err) } return md, err @@ -187,9 +188,9 @@ func (w *clientStream) CloseSend() error { err := w.ClientStream.CloseSend() if err != nil { - w.events <- streamEvent{errorEvent, err} + w.sendStreamEvent(errorEvent, err) } else { - w.events <- streamEvent{closeEvent, nil} + w.sendStreamEvent(closeEvent, nil) } return err @@ -201,10 +202,13 @@ const ( ) func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream { - events := make(chan streamEvent, 1) + events := make(chan streamEvent) + eventsDone := make(chan struct{}) finished := make(chan error) go func() { + defer close(eventsDone) + // Both streams have to be closed state := byte(0) @@ -216,12 +220,12 @@ func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream state |= receiveEndedState case errorEvent: finished <- event.Err - close(events) + return } if state == clientClosedState|receiveEndedState { finished <- nil - close(events) + return } } }() @@ -230,10 +234,18 @@ func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream ClientStream: s, desc: desc, events: events, + eventsDone: eventsDone, finished: finished, } } +func (w *clientStream) sendStreamEvent(eventType streamEventType, err error) { + select { + case <-w.eventsDone: + case w.events <- streamEvent{Type: eventType, Err: err}: + } +} + // StreamClientInterceptor returns a grpc.StreamClientInterceptor suitable // for use in a grpc.Dial call. // diff --git a/plugin/grpctrace/interceptor_test.go b/plugin/grpctrace/interceptor_test.go index 211a9c36e..db12a3a30 100644 --- a/plugin/grpctrace/interceptor_test.go +++ b/plugin/grpctrace/interceptor_test.go @@ -376,6 +376,9 @@ func TestStreamClientInterceptor(t *testing.T) { validate("SENT", events[i].Attributes) validate("RECEIVED", events[i+1].Attributes) } + + // ensure CloseSend can be subsequently called + _ = streamClient.CloseSend() } func TestServerInterceptorError(t *testing.T) { From 63df1b5e224a3ffed2ee1da31fe174642e62d67f Mon Sep 17 00:00:00 2001 From: jmacd Date: Wed, 20 May 2020 23:20:29 -0700 Subject: [PATCH 077/108] Add merged iterator --- api/label/iterator.go | 76 ++++++++++++++++++++++++++++++ api/label/iterator_test.go | 94 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/api/label/iterator.go b/api/label/iterator.go index 6b345d092..ca852d500 100644 --- a/api/label/iterator.go +++ b/api/label/iterator.go @@ -25,6 +25,21 @@ type Iterator struct { idx int } +// MergeIterator supports iterating over two sets of labels while +// eliminating duplicate values from the combined set. The first +// iterator value takes precedence. +type MergeItererator struct { + one oneIterator + two oneIterator + current kv.KeyValue +} + +type oneIterator struct { + iter Iterator + done bool + label kv.KeyValue +} + // Next moves the iterator to the next position. Returns false if there // are no more labels. func (i *Iterator) Next() bool { @@ -75,3 +90,64 @@ func (i *Iterator) ToSlice() []kv.KeyValue { } return slice } + +// NewMergeIterator returns a MergeIterator for merging two label set +// iterators. Duplicates are resolved by taking the value from the +// first iterator. +func NewMergeIterator(iter1, iter2 Iterator) MergeItererator { + mi := MergeItererator{ + one: makeOne(iter1), + two: makeOne(iter2), + } + return mi +} + +func makeOne(iter Iterator) oneIterator { + oi := oneIterator{ + iter: iter, + } + oi.advance() + return oi +} + +func (oi *oneIterator) advance() { + if oi.done = !oi.iter.Next(); !oi.done { + oi.label = oi.iter.Label() + } +} + +// Next returns true if there is another label available. +func (m *MergeItererator) Next() bool { + if m.one.done && m.two.done { + return false + } + if m.one.done { + m.current = m.two.label + m.two.advance() + return true + } + if m.two.done { + m.current = m.one.label + m.one.advance() + return true + } + if m.one.label.Key == m.two.label.Key { + m.current = m.one.label // first iterator label value wins + m.one.advance() + m.two.advance() + return true + } + if m.one.label.Key < m.two.label.Key { + m.current = m.one.label + m.one.advance() + return true + } + m.current = m.two.label + m.two.advance() + return true +} + +// Label returns the current value after Next() returns true. +func (m *MergeItererator) Label() kv.KeyValue { + return m.current +} diff --git a/api/label/iterator_test.go b/api/label/iterator_test.go index 0548240f1..d0f7feece 100644 --- a/api/label/iterator_test.go +++ b/api/label/iterator_test.go @@ -15,6 +15,7 @@ package label_test import ( + "fmt" "testing" "go.opentelemetry.io/otel/api/kv" @@ -55,3 +56,96 @@ func TestEmptyIterator(t *testing.T) { require.Equal(t, 0, iter.Len()) require.False(t, iter.Next()) } + +func TestMergedIterator(t *testing.T) { + + type inputs struct { + name string + keys1 []string + keys2 []string + expect []string + } + + makeLabels := func(keys []string, num int) (result []kv.KeyValue) { + for _, k := range keys { + result = append(result, kv.Int(k, num)) + } + return + } + + for _, input := range []inputs{ + { + name: "one overlap", + keys1: []string{"A", "B"}, + keys2: []string{"B", "C"}, + expect: []string{"A/1", "B/1", "C/2"}, + }, + { + name: "reversed one overlap", + keys1: []string{"B", "A"}, + keys2: []string{"C", "B"}, + expect: []string{"A/1", "B/1", "C/2"}, + }, + { + name: "one empty", + keys1: nil, + keys2: []string{"C", "B"}, + expect: []string{"B/2", "C/2"}, + }, + { + name: "two empty", + keys1: []string{"C", "B"}, + keys2: nil, + expect: []string{"B/1", "C/1"}, + }, + { + name: "no overlap both", + keys1: []string{"C"}, + keys2: []string{"B"}, + expect: []string{"B/2", "C/1"}, + }, + { + name: "one empty single two", + keys1: nil, + keys2: []string{"B"}, + expect: []string{"B/2"}, + }, + { + name: "two empty single one", + keys1: []string{"A"}, + keys2: nil, + expect: []string{"A/1"}, + }, + { + name: "all empty", + keys1: nil, + keys2: nil, + expect: nil, + }, + { + name: "full overlap", + keys1: []string{"A", "B", "C", "D"}, + keys2: []string{"A", "B", "C", "D"}, + expect: []string{"A/1", "B/1", "C/1", "D/1"}, + }, + } { + t.Run(input.name, func(t *testing.T) { + labels1 := makeLabels(input.keys1, 1) + labels2 := makeLabels(input.keys2, 2) + + set1 := label.NewSet(labels1...) + set2 := label.NewSet(labels2...) + + merge := label.NewMergeIterator(set1.Iter(), set2.Iter()) + + var result []string + + for merge.Next() { + label := merge.Label() + result = append(result, fmt.Sprint(label.Key, "/", label.Value.Emit())) + } + + require.Equal(t, input.expect, result) + }) + } +} From 0b5080372a0bd2c088748fe32857017dec472382 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 00:19:08 -0700 Subject: [PATCH 078/108] Add benchmark --- exporters/metric/prometheus/go.sum | 1 + exporters/metric/prometheus/prometheus.go | 48 ++++++------- sdk/resource/benchmark_test.go | 84 +++++++++++++++++++++++ 3 files changed, 110 insertions(+), 23 deletions(-) create mode 100644 sdk/resource/benchmark_test.go diff --git a/exporters/metric/prometheus/go.sum b/exporters/metric/prometheus/go.sum index c4b834b80..2b8aff382 100644 --- a/exporters/metric/prometheus/go.sum +++ b/exporters/metric/prometheus/go.sum @@ -92,6 +92,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +go.opentelemetry.io v0.1.0 h1:EANZoRCOP+A3faIlw/iN6YEWoYb1vleZRKm1EvH8T48= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/exporters/metric/prometheus/prometheus.go b/exporters/metric/prometheus/prometheus.go index d76a1faf3..d8983b0a5 100644 --- a/exporters/metric/prometheus/prometheus.go +++ b/exporters/metric/prometheus/prometheus.go @@ -20,13 +20,11 @@ import ( "net/http" "sync" - "go.opentelemetry.io/otel/api/metric" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/otel/api/global" - "go.opentelemetry.io/otel/api/label" + "go.opentelemetry.io/otel/api/metric" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/controller/pull" @@ -203,7 +201,7 @@ func (c *collector) Describe(ch chan<- *prometheus.Desc) { defer c.exp.lock.RUnlock() _ = c.exp.Controller().ForEach(func(record export.Record) error { - ch <- c.toDesc(&record) + ch <- c.toDesc(record) return nil }) } @@ -222,9 +220,8 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) { err := ctrl.ForEach(func(record export.Record) error { agg := record.Aggregator() numberKind := record.Descriptor().NumberKind() - // TODO: Use the resource value in this record. - labels := labelValues(record.Labels()) - desc := c.toDesc(&record) + labels := labelValues(record) + desc := c.toDesc(record) if hist, ok := agg.(aggregator.Histogram); ok { if err := c.exportHistogram(ch, hist, numberKind, desc, labels); err != nil { @@ -346,30 +343,35 @@ func (c *collector) exportHistogram(ch chan<- prometheus.Metric, hist aggregator return nil } -func (c *collector) toDesc(record *export.Record) *prometheus.Desc { +func (c *collector) toDesc(record export.Record) *prometheus.Desc { desc := record.Descriptor() - labels := labelsKeys(record.Labels()) + labels := labelsKeys(record) return prometheus.NewDesc(sanitize(desc.Name()), desc.Description(), labels, nil) } -func labelsKeys(labels *label.Set) []string { - iter := labels.Iter() - keys := make([]string, 0, iter.Len()) - for iter.Next() { - kv := iter.Label() - keys = append(keys, sanitize(string(kv.Key))) +func labelsKeys(record export.Record) []string { + iter1 := record.Resource().Iter() + iter2 := record.Labels().Iter() + keys := make([]string, 0, iter1.Len()+iter2.Len()) + for iter1.Next() { + keys = append(keys, sanitize(string(iter1.Label().Key))) + } + for iter2.Next() { + keys = append(keys, sanitize(string(iter2.Label().Key))) } return keys } -func labelValues(labels *label.Set) []string { - // TODO(paivagustavo): parse the labels.Encoded() instead of calling `Emit()` directly - // this would avoid unnecessary allocations. - iter := labels.Iter() - values := make([]string, 0, iter.Len()) - for iter.Next() { - label := iter.Label() - values = append(values, label.Value.Emit()) +func labelValues(record export.Record) []string { + iter1 := record.Resource().Iter() + iter2 := record.Labels().Iter() + values := make([]string, 0, iter1.Len()+iter2.Len()) + for iter1.Next() { + values = append(values, iter1.Label().Value.Emit()) } + for iter2.Next() { + values = append(values, iter2.Label().Value.Emit()) + } + return values } diff --git a/sdk/resource/benchmark_test.go b/sdk/resource/benchmark_test.go new file mode 100644 index 000000000..730eb1dc1 --- /dev/null +++ b/sdk/resource/benchmark_test.go @@ -0,0 +1,84 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package resource_test + +import ( + "fmt" + "math/rand" + "testing" + + "go.opentelemetry.io/otel/api/kv" + "go.opentelemetry.io/otel/sdk/resource" +) + +const conflict = 0.5 + +func makeLabels(n int) (_, _ *resource.Resource) { + used := map[string]bool{} + l1 := make([]kv.KeyValue, n) + l2 := make([]kv.KeyValue, n) + for i := 0; i < n; i++ { + var k string + for { + k = fmt.Sprint("k", rand.Intn(1000000000)) + if !used[k] { + used[k] = true + break + } + } + l1[i] = kv.String(k, fmt.Sprint("v", rand.Intn(1000000000))) + + if rand.Float64() < conflict { + l2[i] = l1[i] + } else { + l2[i] = kv.String(k, fmt.Sprint("v", rand.Intn(1000000000))) + } + + } + return resource.New(l1...), resource.New(l2...) +} + +func benchmarkMergeResource(b *testing.B, size int) { + r1, r2 := makeLabels(size) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _ = resource.Merge(r1, r2) + } +} + +func BenchmarkMergeResource_1(b *testing.B) { + benchmarkMergeResource(b, 1) +} +func BenchmarkMergeResource_2(b *testing.B) { + benchmarkMergeResource(b, 2) +} +func BenchmarkMergeResource_3(b *testing.B) { + benchmarkMergeResource(b, 3) +} +func BenchmarkMergeResource_4(b *testing.B) { + benchmarkMergeResource(b, 4) +} +func BenchmarkMergeResource_6(b *testing.B) { + benchmarkMergeResource(b, 6) +} +func BenchmarkMergeResource_8(b *testing.B) { + benchmarkMergeResource(b, 8) +} +func BenchmarkMergeResource_16(b *testing.B) { + benchmarkMergeResource(b, 16) +} From 1c8c5df4db631d9a1895dd5d5c0c70ed62d6d120 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 00:33:39 -0700 Subject: [PATCH 079/108] resource.Merge uses label.MergeIterator --- api/label/iterator.go | 11 +++++------ sdk/resource/resource.go | 22 ++++++++++++++++++---- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/api/label/iterator.go b/api/label/iterator.go index ca852d500..f4870f5dd 100644 --- a/api/label/iterator.go +++ b/api/label/iterator.go @@ -91,13 +91,12 @@ func (i *Iterator) ToSlice() []kv.KeyValue { return slice } -// NewMergeIterator returns a MergeIterator for merging two label set -// iterators. Duplicates are resolved by taking the value from the -// first iterator. -func NewMergeIterator(iter1, iter2 Iterator) MergeItererator { +// NewMergeIterator returns a MergeIterator for merging two label sets +// Duplicates are resolved by taking the value from the first set. +func NewMergeIterator(s1, s2 *Set) MergeItererator { mi := MergeItererator{ - one: makeOne(iter1), - two: makeOne(iter2), + one: makeOne(s1.Iter()), + two: makeOne(s2.Iter()), } return mi } diff --git a/sdk/resource/resource.go b/sdk/resource/resource.go index 8599f04bd..d45f30d15 100644 --- a/sdk/resource/resource.go +++ b/sdk/resource/resource.go @@ -89,15 +89,24 @@ func (r *Resource) Equal(eq *Resource) bool { // If there are common keys between resource a and b, then the value // from resource a is preserved. func Merge(a, b *Resource) *Resource { + if a == nil && b == nil { + return Empty() + } if a == nil { - a = Empty() + return b } if b == nil { - b = Empty() + return a } + // Note: 'b' is listed first so that 'a' will overwrite with // last-value-wins in label.Key() - combine := append(b.Attributes(), a.Attributes()...) + // combine := append(b.Attributes(), a.Attributes()...) + mi := label.NewMergeIterator(a.LabelSet(), b.LabelSet()) + combine := make([]kv.KeyValue, 0, a.Len()+b.Len()) + for mi.Next() { + combine = append(combine, mi.Label()) + } return New(combine...) } @@ -111,10 +120,15 @@ func Empty() *Resource { // between two resources. This value is suitable for use as a key in // a map. func (r *Resource) Equivalent() label.Distinct { + return r.LabelSet().Equivalent() +} + +// LabelSet returns the equivalent *label.Set. +func (r *Resource) LabelSet() *label.Set { if r == nil { r = Empty() } - return r.labels.Equivalent() + return &r.labels } // MarshalJSON encodes labels as a JSON list of { "Key": "...", "Value": ... } From c829d3ea6793f702abbe84dbbd3163570132a988 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 00:36:17 -0700 Subject: [PATCH 080/108] Tests pass --- api/label/iterator_test.go | 2 +- exporters/metric/prometheus/go.sum | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/api/label/iterator_test.go b/api/label/iterator_test.go index d0f7feece..f210b3cca 100644 --- a/api/label/iterator_test.go +++ b/api/label/iterator_test.go @@ -136,7 +136,7 @@ func TestMergedIterator(t *testing.T) { set1 := label.NewSet(labels1...) set2 := label.NewSet(labels2...) - merge := label.NewMergeIterator(set1.Iter(), set2.Iter()) + merge := label.NewMergeIterator(&set1, &set2) var result []string diff --git a/exporters/metric/prometheus/go.sum b/exporters/metric/prometheus/go.sum index 2b8aff382..c4b834b80 100644 --- a/exporters/metric/prometheus/go.sum +++ b/exporters/metric/prometheus/go.sum @@ -92,7 +92,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -go.opentelemetry.io v0.1.0 h1:EANZoRCOP+A3faIlw/iN6YEWoYb1vleZRKm1EvH8T48= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= From 196d7740bd0b86e9e02f0f5c0df9d779f7611405 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 01:29:09 -0700 Subject: [PATCH 081/108] Add resource support --- exporters/metric/prometheus/prometheus.go | 56 +++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/exporters/metric/prometheus/prometheus.go b/exporters/metric/prometheus/prometheus.go index d8983b0a5..07f54f699 100644 --- a/exporters/metric/prometheus/prometheus.go +++ b/exporters/metric/prometheus/prometheus.go @@ -24,6 +24,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/otel/api/global" + "go.opentelemetry.io/otel/api/label" "go.opentelemetry.io/otel/api/metric" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregator" @@ -201,7 +202,9 @@ func (c *collector) Describe(ch chan<- *prometheus.Desc) { defer c.exp.lock.RUnlock() _ = c.exp.Controller().ForEach(func(record export.Record) error { - ch <- c.toDesc(record) + var labelKeys []string + mergeLabels(record, &labelKeys, nil) + ch <- c.toDesc(record, labelKeys) return nil }) } @@ -220,8 +223,11 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) { err := ctrl.ForEach(func(record export.Record) error { agg := record.Aggregator() numberKind := record.Descriptor().NumberKind() - labels := labelValues(record) - desc := c.toDesc(record) + + var labelKeys, labels []string + mergeLabels(record, &labelKeys, &labels) + + desc := c.toDesc(record, labelKeys) if hist, ok := agg.(aggregator.Histogram); ok { if err := c.exportHistogram(ch, hist, numberKind, desc, labels); err != nil { @@ -343,35 +349,29 @@ func (c *collector) exportHistogram(ch chan<- prometheus.Metric, hist aggregator return nil } -func (c *collector) toDesc(record export.Record) *prometheus.Desc { +func (c *collector) toDesc(record export.Record, labelKeys []string) *prometheus.Desc { desc := record.Descriptor() - labels := labelsKeys(record) - return prometheus.NewDesc(sanitize(desc.Name()), desc.Description(), labels, nil) + return prometheus.NewDesc(sanitize(desc.Name()), desc.Description(), labelKeys, nil) } -func labelsKeys(record export.Record) []string { - iter1 := record.Resource().Iter() - iter2 := record.Labels().Iter() - keys := make([]string, 0, iter1.Len()+iter2.Len()) - for iter1.Next() { - keys = append(keys, sanitize(string(iter1.Label().Key))) +func mergeLabels(record export.Record, keys, values *[]string) { + if keys != nil { + *keys = make([]string, 0, record.Labels().Len()+record.Resource().Len()) } - for iter2.Next() { - keys = append(keys, sanitize(string(iter2.Label().Key))) - } - return keys -} - -func labelValues(record export.Record) []string { - iter1 := record.Resource().Iter() - iter2 := record.Labels().Iter() - values := make([]string, 0, iter1.Len()+iter2.Len()) - for iter1.Next() { - values = append(values, iter1.Label().Value.Emit()) - } - for iter2.Next() { - values = append(values, iter2.Label().Value.Emit()) + if values != nil { + *values = make([]string, 0, record.Labels().Len()+record.Resource().Len()) } - return values + // Duplicate keys are resolved by taking the record label value over + // the resource value. + mi := label.NewMergeIterator(record.Labels(), record.Resource().LabelSet()) + for mi.Next() { + label := mi.Label() + if keys != nil { + *keys = append(*keys, sanitize(string(label.Key))) + } + if values != nil { + *values = append(*values, label.Value.Emit()) + } + } } From 7172b30492fa6cbf3ac9cfbc9f62a41d18836f89 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 01:38:43 -0700 Subject: [PATCH 082/108] Add resources in the Prom tests --- exporters/metric/prometheus/example_test.go | 17 +++++++++++------ exporters/metric/prometheus/prometheus_test.go | 15 ++++++++------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/exporters/metric/prometheus/example_test.go b/exporters/metric/prometheus/example_test.go index 9c7092866..14714bf10 100644 --- a/exporters/metric/prometheus/example_test.go +++ b/exporters/metric/prometheus/example_test.go @@ -25,6 +25,8 @@ import ( "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/exporters/metric/prometheus" + "go.opentelemetry.io/otel/sdk/metric/controller/pull" + "go.opentelemetry.io/otel/sdk/resource" ) // This test demonstrates that it is relatively difficult to setup a @@ -32,11 +34,14 @@ import ( // // 1. The default boundaries are difficult to pass, should be []float instead of []metric.Number // -// TODO: Address this issue; add Resources to the test. +// TODO: Address this issue. func ExampleNewExportPipeline() { // Create a meter - exporter, err := prometheus.NewExportPipeline(prometheus.Config{}) + exporter, err := prometheus.NewExportPipeline( + prometheus.Config{}, + pull.WithResource(resource.New(kv.String("R", "V"))), + ) if err != nil { panic(err) } @@ -73,10 +78,10 @@ func ExampleNewExportPipeline() { // Output: // # HELP a_counter Counts things // # TYPE a_counter counter - // a_counter{key="value"} 100 + // a_counter{R="V",key="value"} 100 // # HELP a_valuerecorder Records values // # TYPE a_valuerecorder histogram - // a_valuerecorder_bucket{key="value",le="+Inf"} 1 - // a_valuerecorder_sum{key="value"} 100 - // a_valuerecorder_count{key="value"} 1 + // a_valuerecorder_bucket{R="V",key="value",le="+Inf"} 1 + // a_valuerecorder_sum{R="V",key="value"} 100 + // a_valuerecorder_count{R="V",key="value"} 1 } diff --git a/exporters/metric/prometheus/prometheus_test.go b/exporters/metric/prometheus/prometheus_test.go index db87e0313..90cd99fe3 100644 --- a/exporters/metric/prometheus/prometheus_test.go +++ b/exporters/metric/prometheus/prometheus_test.go @@ -30,12 +30,13 @@ import ( "go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/exporters/metric/prometheus" "go.opentelemetry.io/otel/sdk/metric/controller/pull" + "go.opentelemetry.io/otel/sdk/resource" ) func TestPrometheusExporter(t *testing.T) { exporter, err := prometheus.NewExportPipeline(prometheus.Config{ DefaultHistogramBoundaries: []metric.Number{metric.NewFloat64Number(-0.5), metric.NewFloat64Number(1)}, - }) + }, pull.WithResource(resource.New(kv.String("R", "V")))) require.NoError(t, err) meter := exporter.Provider().Meter("test") @@ -54,18 +55,18 @@ func TestPrometheusExporter(t *testing.T) { counter.Add(ctx, 10, labels...) counter.Add(ctx, 5.3, labels...) - expected = append(expected, `counter{A="B",C="D"} 15.3`) + expected = append(expected, `counter{A="B",C="D",R="V"} 15.3`) valuerecorder.Record(ctx, -0.6, labels...) valuerecorder.Record(ctx, -0.4, labels...) valuerecorder.Record(ctx, 0.6, labels...) valuerecorder.Record(ctx, 20, labels...) - expected = append(expected, `valuerecorder_bucket{A="B",C="D",le="+Inf"} 4`) - expected = append(expected, `valuerecorder_bucket{A="B",C="D",le="-0.5"} 1`) - expected = append(expected, `valuerecorder_bucket{A="B",C="D",le="1"} 3`) - expected = append(expected, `valuerecorder_count{A="B",C="D"} 4`) - expected = append(expected, `valuerecorder_sum{A="B",C="D"} 19.6`) + expected = append(expected, `valuerecorder_bucket{A="B",C="D",R="V",le="+Inf"} 4`) + expected = append(expected, `valuerecorder_bucket{A="B",C="D",R="V",le="-0.5"} 1`) + expected = append(expected, `valuerecorder_bucket{A="B",C="D",R="V",le="1"} 3`) + expected = append(expected, `valuerecorder_count{A="B",C="D",R="V"} 4`) + expected = append(expected, `valuerecorder_sum{A="B",C="D",R="V"} 19.6`) compareExport(t, exporter, expected) } From b63cf49bbaf02312504f6ec5cc8b68ed3cd5ec5e Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 02:08:08 -0700 Subject: [PATCH 083/108] Use sort.Search to locate histogram bucket --- sdk/metric/aggregator/histogram/histogram.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sdk/metric/aggregator/histogram/histogram.go b/sdk/metric/aggregator/histogram/histogram.go index ccb0c2d3c..66bd8000b 100644 --- a/sdk/metric/aggregator/histogram/histogram.go +++ b/sdk/metric/aggregator/histogram/histogram.go @@ -127,13 +127,9 @@ func emptyState(boundaries []metric.Number) state { func (c *Aggregator) Update(_ context.Context, number metric.Number, desc *metric.Descriptor) error { kind := desc.NumberKind() - bucketID := len(c.boundaries) - for i, boundary := range c.boundaries { - if number.CompareNumber(kind, boundary) < 0 { - bucketID = i - break - } - } + bucketID := sort.Search(len(c.boundaries), func(i int) bool { + return number.CompareNumber(kind, c.boundaries[i]) < 0 + }) c.lock.Lock() defer c.lock.Unlock() From 3d2493463c8514ac25001aa916597cd68a828e64 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 09:53:34 -0700 Subject: [PATCH 084/108] Comments --- exporters/metric/prometheus/prometheus.go | 5 +++++ sdk/resource/resource.go | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/exporters/metric/prometheus/prometheus.go b/exporters/metric/prometheus/prometheus.go index 07f54f699..5380defd3 100644 --- a/exporters/metric/prometheus/prometheus.go +++ b/exporters/metric/prometheus/prometheus.go @@ -354,6 +354,11 @@ func (c *collector) toDesc(record export.Record, labelKeys []string) *prometheus return prometheus.NewDesc(sanitize(desc.Name()), desc.Description(), labelKeys, nil) } +// mergeLabels merges the export.Record's labels and resources into a +// single set, giving precedence to the record's labels in case of +// duplicate keys. This outputs one or both of the keys and the +// values as a slice, and either argument may be nil to avoid +// allocating an unnecessary slice. func mergeLabels(record export.Record, keys, values *[]string) { if keys != nil { *keys = make([]string, 0, record.Labels().Len()+record.Resource().Len()) diff --git a/sdk/resource/resource.go b/sdk/resource/resource.go index d45f30d15..608fd4d0e 100644 --- a/sdk/resource/resource.go +++ b/sdk/resource/resource.go @@ -99,9 +99,8 @@ func Merge(a, b *Resource) *Resource { return a } - // Note: 'b' is listed first so that 'a' will overwrite with - // last-value-wins in label.Key() - // combine := append(b.Attributes(), a.Attributes()...) + // Note: 'a' labels will overwrite 'b' with last-value-wins in label.Key() + // Meaning this is equivalent to: append(b.Attributes(), a.Attributes()...) mi := label.NewMergeIterator(a.LabelSet(), b.LabelSet()) combine := make([]kv.KeyValue, 0, a.Len()+b.Len()) for mi.Next() { From dacebd643053f3c62482f8a4d91adffb63bdfd61 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 10:29:03 -0700 Subject: [PATCH 085/108] Use []float64 for boundaries --- exporters/metric/prometheus/prometheus.go | 10 +-- .../metric/prometheus/prometheus_test.go | 2 +- exporters/metric/test/test.go | 2 +- sdk/export/metric/aggregator/aggregator.go | 4 +- sdk/metric/aggregator/histogram/histogram.go | 52 ++++-------- .../aggregator/histogram/histogram_test.go | 83 ++++++++++--------- sdk/metric/histogram_stress_test.go | 4 +- sdk/metric/selector/simple/simple.go | 4 +- sdk/metric/selector/simple/simple_test.go | 2 +- 9 files changed, 74 insertions(+), 89 deletions(-) diff --git a/exporters/metric/prometheus/prometheus.go b/exporters/metric/prometheus/prometheus.go index d76a1faf3..6dd1b0271 100644 --- a/exporters/metric/prometheus/prometheus.go +++ b/exporters/metric/prometheus/prometheus.go @@ -54,7 +54,7 @@ type Exporter struct { onError func(error) defaultSummaryQuantiles []float64 - defaultHistogramBoundaries []metric.Number + defaultHistogramBoundaries []float64 } var _ http.Handler = &Exporter{} @@ -85,7 +85,7 @@ type Config struct { // DefaultHistogramBoundaries defines the default histogram bucket // boundaries. - DefaultHistogramBoundaries []metric.Number + DefaultHistogramBoundaries []float64 // OnError is a function that handle errors that may occur while exporting metrics. // TODO: This should be refactored or even removed once we have a better error handling mechanism. @@ -330,12 +330,12 @@ func (c *collector) exportHistogram(ch chan<- prometheus.Metric, hist aggregator // The bucket with upper-bound +inf is not included. counts := make(map[float64]uint64, len(buckets.Boundaries)) for i := range buckets.Boundaries { - boundary := buckets.Boundaries[i].CoerceToFloat64(kind) - totalCount += buckets.Counts[i].AsUint64() + boundary := buckets.Boundaries[i] + totalCount += uint64(buckets.Counts[i]) counts[boundary] = totalCount } // Include the +inf bucket in the total count. - totalCount += buckets.Counts[len(buckets.Counts)-1].AsUint64() + totalCount += uint64(buckets.Counts[len(buckets.Counts)-1]) m, err := prometheus.NewConstHistogram(desc, totalCount, sum.CoerceToFloat64(kind), counts, labels...) if err != nil { diff --git a/exporters/metric/prometheus/prometheus_test.go b/exporters/metric/prometheus/prometheus_test.go index db87e0313..dbd08aba9 100644 --- a/exporters/metric/prometheus/prometheus_test.go +++ b/exporters/metric/prometheus/prometheus_test.go @@ -34,7 +34,7 @@ import ( func TestPrometheusExporter(t *testing.T) { exporter, err := prometheus.NewExportPipeline(prometheus.Config{ - DefaultHistogramBoundaries: []metric.Number{metric.NewFloat64Number(-0.5), metric.NewFloat64Number(1)}, + DefaultHistogramBoundaries: []float64{-0.5, 1}, }) require.NoError(t, err) diff --git a/exporters/metric/test/test.go b/exporters/metric/test/test.go index 9fd639493..7a788efd5 100644 --- a/exporters/metric/test/test.go +++ b/exporters/metric/test/test.go @@ -97,7 +97,7 @@ func (p *CheckpointSet) AddValueRecorder(desc *metric.Descriptor, v float64, lab p.updateAggregator(desc, array.New(), v, labels...) } -func (p *CheckpointSet) AddHistogramValueRecorder(desc *metric.Descriptor, boundaries []metric.Number, v float64, labels ...kv.KeyValue) { +func (p *CheckpointSet) AddHistogramValueRecorder(desc *metric.Descriptor, boundaries []float64, v float64, labels ...kv.KeyValue) { p.updateAggregator(desc, histogram.New(desc, boundaries), v, labels...) } diff --git a/sdk/export/metric/aggregator/aggregator.go b/sdk/export/metric/aggregator/aggregator.go index f0b6409e6..52fe13435 100644 --- a/sdk/export/metric/aggregator/aggregator.go +++ b/sdk/export/metric/aggregator/aggregator.go @@ -68,8 +68,8 @@ type ( // For a Histogram with N defined boundaries, e.g, [x, y, z]. // There are N+1 counts: [-inf, x), [x, y), [y, z), [z, +inf] Buckets struct { - Boundaries []metric.Number - Counts []metric.Number + Boundaries []float64 + Counts []float64 } // Histogram returns the count of events in pre-determined buckets. diff --git a/sdk/metric/aggregator/histogram/histogram.go b/sdk/metric/aggregator/histogram/histogram.go index 66bd8000b..bc52f9872 100644 --- a/sdk/metric/aggregator/histogram/histogram.go +++ b/sdk/metric/aggregator/histogram/histogram.go @@ -36,7 +36,7 @@ type ( lock sync.Mutex current state checkpoint state - boundaries []metric.Number + boundaries []float64 kind metric.NumberKind } @@ -44,7 +44,7 @@ type ( // the sum and counts for all observed values and // the less than equal bucket count for the pre-determined boundaries. state struct { - bucketCounts []metric.Number + bucketCounts []float64 count metric.Number sum metric.Number } @@ -63,17 +63,19 @@ var _ aggregator.Histogram = &Aggregator{} // Note that this aggregator maintains each value using independent // atomic operations, which introduces the possibility that // checkpoints are inconsistent. -func New(desc *metric.Descriptor, boundaries []metric.Number) *Aggregator { +func New(desc *metric.Descriptor, boundaries []float64) *Aggregator { // Boundaries MUST be ordered otherwise the histogram could not // be properly computed. - sortedBoundaries := numbers{ - numbers: make([]metric.Number, len(boundaries)), - kind: desc.NumberKind(), - } + // metric.SortNumbers(desc.NumberKind(), boundaries) + // sortedBoundaries := numbers{ + // numbers: make([]metric.Number, len(boundaries)), + // kind: desc.NumberKind(), + // } + sort.Float64s(boundaries) - copy(sortedBoundaries.numbers, boundaries) - sort.Sort(&sortedBoundaries) - boundaries = sortedBoundaries.numbers + // copy(sortedBoundaries.numbers, boundaries) + // sort.Sort(&sortedBoundaries) + // boundaries = sortedBoundaries.numbers return &Aggregator{ kind: desc.NumberKind(), @@ -117,9 +119,9 @@ func (c *Aggregator) Checkpoint(ctx context.Context, desc *metric.Descriptor) { c.lock.Unlock() } -func emptyState(boundaries []metric.Number) state { +func emptyState(boundaries []float64) state { return state{ - bucketCounts: make([]metric.Number, len(boundaries)+1), + bucketCounts: make([]float64, len(boundaries)+1), } } @@ -128,7 +130,7 @@ func (c *Aggregator) Update(_ context.Context, number metric.Number, desc *metri kind := desc.NumberKind() bucketID := sort.Search(len(c.boundaries), func(i int) bool { - return number.CompareNumber(kind, c.boundaries[i]) < 0 + return number.CoerceToFloat64(kind) < c.boundaries[i] }) c.lock.Lock() @@ -136,7 +138,7 @@ func (c *Aggregator) Update(_ context.Context, number metric.Number, desc *metri c.current.count.AddInt64(1) c.current.sum.AddNumber(kind, number) - c.current.bucketCounts[bucketID].AddUint64(1) + c.current.bucketCounts[bucketID]++ return nil } @@ -152,27 +154,7 @@ func (c *Aggregator) Merge(oa export.Aggregator, desc *metric.Descriptor) error c.checkpoint.count.AddNumber(metric.Uint64NumberKind, o.checkpoint.count) for i := 0; i < len(c.checkpoint.bucketCounts); i++ { - c.checkpoint.bucketCounts[i].AddNumber(metric.Uint64NumberKind, o.checkpoint.bucketCounts[i]) + c.checkpoint.bucketCounts[i] += o.checkpoint.bucketCounts[i] } return nil } - -// numbers is an auxiliary struct to order histogram bucket boundaries (slice of kv.Number) -type numbers struct { - numbers []metric.Number - kind metric.NumberKind -} - -var _ sort.Interface = (*numbers)(nil) - -func (n *numbers) Len() int { - return len(n.numbers) -} - -func (n *numbers) Less(i, j int) bool { - return -1 == n.numbers[i].CompareNumber(n.kind, n.numbers[j]) -} - -func (n *numbers) Swap(i, j int) { - n.numbers[i], n.numbers[j] = n.numbers[j], n.numbers[i] -} diff --git a/sdk/metric/aggregator/histogram/histogram_test.go b/sdk/metric/aggregator/histogram/histogram_test.go index c1541ee2b..a34b72486 100644 --- a/sdk/metric/aggregator/histogram/histogram_test.go +++ b/sdk/metric/aggregator/histogram/histogram_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package histogram +package histogram_test import ( "context" @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/api/metric" + "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/test" ) @@ -57,36 +58,33 @@ var ( }, } - boundaries = map[metric.NumberKind][]metric.Number{ - metric.Float64NumberKind: {metric.NewFloat64Number(500), metric.NewFloat64Number(250), metric.NewFloat64Number(750)}, - metric.Int64NumberKind: {metric.NewInt64Number(500), metric.NewInt64Number(250), metric.NewInt64Number(750)}, - } + boundaries = []float64{500, 250, 750} ) func TestHistogramAbsolute(t *testing.T) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) { - histogram(t, profile, positiveOnly) + testHistogram(t, profile, positiveOnly) }) } func TestHistogramNegativeOnly(t *testing.T) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) { - histogram(t, profile, negativeOnly) + testHistogram(t, profile, negativeOnly) }) } func TestHistogramPositiveAndNegative(t *testing.T) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) { - histogram(t, profile, positiveAndNegative) + testHistogram(t, profile, positiveAndNegative) }) } // Validates count, sum and buckets for a given profile and policy -func histogram(t *testing.T, profile test.Profile, policy policy) { +func testHistogram(t *testing.T, profile test.Profile, policy policy) { ctx := context.Background() descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) - agg := New(descriptor, boundaries[profile.NumberKind]) + agg := histogram.New(descriptor, boundaries) all := test.NewNumbers(profile.NumberKind) @@ -107,18 +105,21 @@ func histogram(t *testing.T, profile test.Profile, policy policy) { asum.CoerceToFloat64(profile.NumberKind), 0.000000001, "Same sum - "+policy.name) - require.Nil(t, err) + require.NoError(t, err) count, err := agg.Count() require.Equal(t, all.Count(), count, "Same count -"+policy.name) - require.Nil(t, err) + require.NoError(t, err) - require.Equal(t, len(agg.checkpoint.bucketCounts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries") + buckets, err := agg.Histogram() + require.NoError(t, err) + + require.Equal(t, len(buckets.Counts), len(boundaries)+1, "There should be b + 1 counts, where b is the number of boundaries") counts := calcBuckets(all.Points(), profile) for i, v := range counts { - bCount := agg.checkpoint.bucketCounts[i].AsUint64() - require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, agg.checkpoint.bucketCounts) + bCount := uint64(buckets.Counts[i]) + require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, buckets.Counts) } } @@ -126,12 +127,12 @@ func TestHistogramInitial(t *testing.T) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) { descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) - agg := New(descriptor, boundaries[profile.NumberKind]) + agg := histogram.New(descriptor, boundaries) buckets, err := agg.Histogram() require.NoError(t, err) - require.Equal(t, len(buckets.Counts), len(boundaries[profile.NumberKind])+1) - require.Equal(t, len(buckets.Boundaries), len(boundaries[profile.NumberKind])) + require.Equal(t, len(buckets.Counts), len(boundaries)+1) + require.Equal(t, len(buckets.Boundaries), len(boundaries)) }) } @@ -141,8 +142,8 @@ func TestHistogramMerge(t *testing.T) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) { descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) - agg1 := New(descriptor, boundaries[profile.NumberKind]) - agg2 := New(descriptor, boundaries[profile.NumberKind]) + agg1 := histogram.New(descriptor, boundaries) + agg2 := histogram.New(descriptor, boundaries) all := test.NewNumbers(profile.NumberKind) @@ -171,18 +172,21 @@ func TestHistogramMerge(t *testing.T) { asum.CoerceToFloat64(profile.NumberKind), 0.000000001, "Same sum - absolute") - require.Nil(t, err) + require.NoError(t, err) count, err := agg1.Count() require.Equal(t, all.Count(), count, "Same count - absolute") - require.Nil(t, err) + require.NoError(t, err) - require.Equal(t, len(agg1.checkpoint.bucketCounts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries") + buckets, err := agg1.Histogram() + require.NoError(t, err) + + require.Equal(t, len(buckets.Counts), len(boundaries)+1, "There should be b + 1 counts, where b is the number of boundaries") counts := calcBuckets(all.Points(), profile) for i, v := range counts { - bCount := agg1.checkpoint.bucketCounts[i].AsUint64() - require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, agg1.checkpoint.bucketCounts) + bCount := uint64(buckets.Counts[i]) + require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, buckets.Counts) } }) } @@ -193,38 +197,37 @@ func TestHistogramNotSet(t *testing.T) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) { descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) - agg := New(descriptor, boundaries[profile.NumberKind]) + agg := histogram.New(descriptor, boundaries) agg.Checkpoint(ctx, descriptor) asum, err := agg.Sum() require.Equal(t, metric.Number(0), asum, "Empty checkpoint sum = 0") - require.Nil(t, err) + require.NoError(t, err) count, err := agg.Count() require.Equal(t, int64(0), count, "Empty checkpoint count = 0") - require.Nil(t, err) + require.NoError(t, err) - require.Equal(t, len(agg.checkpoint.bucketCounts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries") - for i, bCount := range agg.checkpoint.bucketCounts { - require.Equal(t, uint64(0), bCount.AsUint64(), "Bucket #%d must have 0 observed values", i) + buckets, err := agg.Histogram() + require.NoError(t, err) + + require.Equal(t, len(buckets.Counts), len(boundaries)+1, "There should be b + 1 counts, where b is the number of boundaries") + for i, bCount := range buckets.Counts { + require.Equal(t, uint64(0), uint64(bCount), "Bucket #%d must have 0 observed values", i) } }) } func calcBuckets(points []metric.Number, profile test.Profile) []uint64 { - sortedBoundaries := numbers{ - numbers: make([]metric.Number, len(boundaries[profile.NumberKind])), - kind: profile.NumberKind, - } + sortedBoundaries := make([]float64, len(boundaries)) - copy(sortedBoundaries.numbers, boundaries[profile.NumberKind]) - sort.Sort(&sortedBoundaries) - boundaries := sortedBoundaries.numbers + copy(sortedBoundaries, boundaries) + sort.Float64s(sortedBoundaries) - counts := make([]uint64, len(boundaries)+1) + counts := make([]uint64, len(sortedBoundaries)+1) idx := 0 for _, p := range points { - for idx < len(boundaries) && p.CompareNumber(profile.NumberKind, boundaries[idx]) != -1 { + for idx < len(sortedBoundaries) && p.CoerceToFloat64(profile.NumberKind) >= boundaries[idx] { idx++ } counts[idx]++ diff --git a/sdk/metric/histogram_stress_test.go b/sdk/metric/histogram_stress_test.go index d05536622..f4a1ca687 100644 --- a/sdk/metric/histogram_stress_test.go +++ b/sdk/metric/histogram_stress_test.go @@ -26,7 +26,7 @@ import ( func TestStressInt64Histogram(t *testing.T) { desc := metric.NewDescriptor("some_metric", metric.ValueRecorderKind, metric.Int64NumberKind) - h := histogram.New(&desc, []metric.Number{metric.NewInt64Number(25), metric.NewInt64Number(50), metric.NewInt64Number(75)}) + h := histogram.New(&desc, []float64{25, 50, 75}) ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() @@ -51,7 +51,7 @@ func TestStressInt64Histogram(t *testing.T) { var realCount int64 for _, c := range b.Counts { - v := c.AsInt64() + v := int64(c) realCount += v } diff --git a/sdk/metric/selector/simple/simple.go b/sdk/metric/selector/simple/simple.go index b64a5e971..a5956e4f3 100644 --- a/sdk/metric/selector/simple/simple.go +++ b/sdk/metric/selector/simple/simple.go @@ -31,7 +31,7 @@ type ( config *ddsketch.Config } selectorHistogram struct { - boundaries []metric.Number + boundaries []float64 } ) @@ -75,7 +75,7 @@ func NewWithExactDistribution() export.AggregationSelector { // histogram, and histogram aggregators for the three kinds of metric. This // selector uses more memory than the NewWithInexpensiveDistribution because it // uses a counter per bucket. -func NewWithHistogramDistribution(boundaries []metric.Number) export.AggregationSelector { +func NewWithHistogramDistribution(boundaries []float64) export.AggregationSelector { return selectorHistogram{boundaries: boundaries} } diff --git a/sdk/metric/selector/simple/simple_test.go b/sdk/metric/selector/simple/simple_test.go index 018d49efa..35b2526b0 100644 --- a/sdk/metric/selector/simple/simple_test.go +++ b/sdk/metric/selector/simple/simple_test.go @@ -56,7 +56,7 @@ func TestExactDistribution(t *testing.T) { } func TestHistogramDistribution(t *testing.T) { - ex := simple.NewWithHistogramDistribution([]metric.Number{}) + ex := simple.NewWithHistogramDistribution(nil) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) }) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueRecorderDesc).(*histogram.Aggregator) }) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueObserverDesc).(*histogram.Aggregator) }) From 9548817e7eeb5cb3674864b08e82d208f613febc Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 10:45:26 -0700 Subject: [PATCH 086/108] Add a benchmark --- .../aggregator/histogram/benchmark_test.go | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 sdk/metric/aggregator/histogram/benchmark_test.go diff --git a/sdk/metric/aggregator/histogram/benchmark_test.go b/sdk/metric/aggregator/histogram/benchmark_test.go new file mode 100644 index 000000000..7eff730f0 --- /dev/null +++ b/sdk/metric/aggregator/histogram/benchmark_test.go @@ -0,0 +1,129 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package histogram_test + +import ( + "context" + "math/rand" + "testing" + + "go.opentelemetry.io/otel/api/metric" + "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" + "go.opentelemetry.io/otel/sdk/metric/aggregator/test" +) + +const inputRange = 1e6 + +func benchmarkHistogramSearchFloat64(b *testing.B, size int) { + boundaries := make([]float64, size) + + for i := range boundaries { + boundaries[i] = rand.Float64() * inputRange + } + + values := make([]float64, b.N) + for i := range values { + values[i] = rand.Float64() * inputRange + } + desc := test.NewAggregatorTest(metric.ValueRecorderKind, metric.Float64NumberKind) + agg := histogram.New(desc, boundaries) + ctx := context.Background() + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + agg.Update(ctx, metric.NewFloat64Number(rand.Float64()*inputRange), desc) + } +} + +func BenchmarkHistogramSearchFloat64_1(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 1) +} +func BenchmarkHistogramSearchFloat64_2(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 2) +} +func BenchmarkHistogramSearchFloat64_3(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 3) +} +func BenchmarkHistogramSearchFloat64_4(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 4) +} +func BenchmarkHistogramSearchFloat64_8(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 8) +} +func BenchmarkHistogramSearchFloat64_12(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 12) +} +func BenchmarkHistogramSearchFloat64_16(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 16) +} +func BenchmarkHistogramSearchFloat64_32(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 32) +} +func BenchmarkHistogramSearchFloat64_64(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 64) +} + +func benchmarkHistogramSearchInt64(b *testing.B, size int) { + boundaries := make([]float64, size) + + for i := range boundaries { + boundaries[i] = rand.Float64() * inputRange + } + + values := make([]int64, b.N) + for i := range values { + values[i] = int64(rand.Float64() * inputRange) + } + desc := test.NewAggregatorTest(metric.ValueRecorderKind, metric.Int64NumberKind) + agg := histogram.New(desc, boundaries) + ctx := context.Background() + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + agg.Update(ctx, metric.NewInt64Number(int64(rand.Float64()*inputRange)), desc) + } +} + +func BenchmarkHistogramSearchInt64_1(b *testing.B) { + benchmarkHistogramSearchInt64(b, 1) +} +func BenchmarkHistogramSearchInt64_2(b *testing.B) { + benchmarkHistogramSearchInt64(b, 2) +} +func BenchmarkHistogramSearchInt64_3(b *testing.B) { + benchmarkHistogramSearchInt64(b, 3) +} +func BenchmarkHistogramSearchInt64_4(b *testing.B) { + benchmarkHistogramSearchInt64(b, 4) +} +func BenchmarkHistogramSearchInt64_8(b *testing.B) { + benchmarkHistogramSearchInt64(b, 8) +} +func BenchmarkHistogramSearchInt64_12(b *testing.B) { + benchmarkHistogramSearchInt64(b, 12) +} +func BenchmarkHistogramSearchInt64_16(b *testing.B) { + benchmarkHistogramSearchInt64(b, 16) +} +func BenchmarkHistogramSearchInt64_32(b *testing.B) { + benchmarkHistogramSearchInt64(b, 32) +} +func BenchmarkHistogramSearchInt64_64(b *testing.B) { + benchmarkHistogramSearchInt64(b, 64) +} From db993ec5cc63331b3d2e4bb35b942c3c07047b42 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 10:46:22 -0700 Subject: [PATCH 087/108] Cleanup sort --- sdk/metric/aggregator/histogram/histogram.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/sdk/metric/aggregator/histogram/histogram.go b/sdk/metric/aggregator/histogram/histogram.go index bc52f9872..cf964adb6 100644 --- a/sdk/metric/aggregator/histogram/histogram.go +++ b/sdk/metric/aggregator/histogram/histogram.go @@ -66,22 +66,16 @@ var _ aggregator.Histogram = &Aggregator{} func New(desc *metric.Descriptor, boundaries []float64) *Aggregator { // Boundaries MUST be ordered otherwise the histogram could not // be properly computed. - // metric.SortNumbers(desc.NumberKind(), boundaries) - // sortedBoundaries := numbers{ - // numbers: make([]metric.Number, len(boundaries)), - // kind: desc.NumberKind(), - // } - sort.Float64s(boundaries) + sortedBoundaries := make([]float64, len(boundaries)) - // copy(sortedBoundaries.numbers, boundaries) - // sort.Sort(&sortedBoundaries) - // boundaries = sortedBoundaries.numbers + copy(sortedBoundaries, boundaries) + sort.Float64s(sortedBoundaries) return &Aggregator{ kind: desc.NumberKind(), - boundaries: boundaries, - current: emptyState(boundaries), - checkpoint: emptyState(boundaries), + boundaries: sortedBoundaries, + current: emptyState(sortedBoundaries), + checkpoint: emptyState(sortedBoundaries), } } From 2aa0f1496e5c51bbde33010da2227dae4a346678 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 11:09:10 -0700 Subject: [PATCH 088/108] Comment on linear vs binary search --- .../aggregator/histogram/benchmark_test.go | 48 +++++++++---------- sdk/metric/aggregator/histogram/histogram.go | 22 +++++++-- .../aggregator/histogram/histogram_test.go | 2 +- sdk/metric/aggregator/test/test.go | 2 + 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/sdk/metric/aggregator/histogram/benchmark_test.go b/sdk/metric/aggregator/histogram/benchmark_test.go index 7eff730f0..628d5ffb0 100644 --- a/sdk/metric/aggregator/histogram/benchmark_test.go +++ b/sdk/metric/aggregator/histogram/benchmark_test.go @@ -52,21 +52,9 @@ func benchmarkHistogramSearchFloat64(b *testing.B, size int) { func BenchmarkHistogramSearchFloat64_1(b *testing.B) { benchmarkHistogramSearchFloat64(b, 1) } -func BenchmarkHistogramSearchFloat64_2(b *testing.B) { - benchmarkHistogramSearchFloat64(b, 2) -} -func BenchmarkHistogramSearchFloat64_3(b *testing.B) { - benchmarkHistogramSearchFloat64(b, 3) -} -func BenchmarkHistogramSearchFloat64_4(b *testing.B) { - benchmarkHistogramSearchFloat64(b, 4) -} func BenchmarkHistogramSearchFloat64_8(b *testing.B) { benchmarkHistogramSearchFloat64(b, 8) } -func BenchmarkHistogramSearchFloat64_12(b *testing.B) { - benchmarkHistogramSearchFloat64(b, 12) -} func BenchmarkHistogramSearchFloat64_16(b *testing.B) { benchmarkHistogramSearchFloat64(b, 16) } @@ -76,6 +64,18 @@ func BenchmarkHistogramSearchFloat64_32(b *testing.B) { func BenchmarkHistogramSearchFloat64_64(b *testing.B) { benchmarkHistogramSearchFloat64(b, 64) } +func BenchmarkHistogramSearchFloat64_128(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 128) +} +func BenchmarkHistogramSearchFloat64_256(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 256) +} +func BenchmarkHistogramSearchFloat64_512(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 512) +} +func BenchmarkHistogramSearchFloat64_1024(b *testing.B) { + benchmarkHistogramSearchFloat64(b, 1024) +} func benchmarkHistogramSearchInt64(b *testing.B, size int) { boundaries := make([]float64, size) @@ -103,21 +103,9 @@ func benchmarkHistogramSearchInt64(b *testing.B, size int) { func BenchmarkHistogramSearchInt64_1(b *testing.B) { benchmarkHistogramSearchInt64(b, 1) } -func BenchmarkHistogramSearchInt64_2(b *testing.B) { - benchmarkHistogramSearchInt64(b, 2) -} -func BenchmarkHistogramSearchInt64_3(b *testing.B) { - benchmarkHistogramSearchInt64(b, 3) -} -func BenchmarkHistogramSearchInt64_4(b *testing.B) { - benchmarkHistogramSearchInt64(b, 4) -} func BenchmarkHistogramSearchInt64_8(b *testing.B) { benchmarkHistogramSearchInt64(b, 8) } -func BenchmarkHistogramSearchInt64_12(b *testing.B) { - benchmarkHistogramSearchInt64(b, 12) -} func BenchmarkHistogramSearchInt64_16(b *testing.B) { benchmarkHistogramSearchInt64(b, 16) } @@ -127,3 +115,15 @@ func BenchmarkHistogramSearchInt64_32(b *testing.B) { func BenchmarkHistogramSearchInt64_64(b *testing.B) { benchmarkHistogramSearchInt64(b, 64) } +func BenchmarkHistogramSearchInt64_128(b *testing.B) { + benchmarkHistogramSearchInt64(b, 128) +} +func BenchmarkHistogramSearchInt64_256(b *testing.B) { + benchmarkHistogramSearchInt64(b, 256) +} +func BenchmarkHistogramSearchInt64_512(b *testing.B) { + benchmarkHistogramSearchInt64(b, 512) +} +func BenchmarkHistogramSearchInt64_1024(b *testing.B) { + benchmarkHistogramSearchInt64(b, 1024) +} diff --git a/sdk/metric/aggregator/histogram/histogram.go b/sdk/metric/aggregator/histogram/histogram.go index cf964adb6..a8e3a76de 100644 --- a/sdk/metric/aggregator/histogram/histogram.go +++ b/sdk/metric/aggregator/histogram/histogram.go @@ -122,10 +122,26 @@ func emptyState(boundaries []float64) state { // Update adds the recorded measurement to the current data set. func (c *Aggregator) Update(_ context.Context, number metric.Number, desc *metric.Descriptor) error { kind := desc.NumberKind() + asFloat := number.CoerceToFloat64(kind) - bucketID := sort.Search(len(c.boundaries), func(i int) bool { - return number.CoerceToFloat64(kind) < c.boundaries[i] - }) + bucketID := len(c.boundaries) + for i, boundary := range c.boundaries { + if asFloat < boundary { + bucketID = i + break + } + } + // Note: Binary-search was compared using the benchmarks. The following + // code is equivalent to the linear search above: + // + // bucketID := sort.Search(len(c.boundaries), func(i int) bool { + // return asFloat < c.boundaries[i] + // }) + // + // The binary search wins for very large boundary sets, but + // the linear search performs better up through arrays between + // 256 and 512 elements, which is a relatively large histogram, so we + // continue to prefer linear search. c.lock.Lock() defer c.lock.Unlock() diff --git a/sdk/metric/aggregator/histogram/histogram_test.go b/sdk/metric/aggregator/histogram/histogram_test.go index a34b72486..13a1fcb52 100644 --- a/sdk/metric/aggregator/histogram/histogram_test.go +++ b/sdk/metric/aggregator/histogram/histogram_test.go @@ -227,7 +227,7 @@ func calcBuckets(points []metric.Number, profile test.Profile) []uint64 { counts := make([]uint64, len(sortedBoundaries)+1) idx := 0 for _, p := range points { - for idx < len(sortedBoundaries) && p.CoerceToFloat64(profile.NumberKind) >= boundaries[idx] { + for idx < len(sortedBoundaries) && p.CoerceToFloat64(profile.NumberKind) >= sortedBoundaries[idx] { idx++ } counts[idx]++ diff --git a/sdk/metric/aggregator/test/test.go b/sdk/metric/aggregator/test/test.go index edb4a1d73..53cf31959 100644 --- a/sdk/metric/aggregator/test/test.go +++ b/sdk/metric/aggregator/test/test.go @@ -81,6 +81,8 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } +// TODO: Expose Numbers in api/metric for sorting support + type Numbers struct { // numbers has to be aligned for 64-bit atomic operations. numbers []metric.Number From 17b8543050192989a17685b3a2fdf77736bed411 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 11:18:47 -0700 Subject: [PATCH 089/108] Lint --- sdk/metric/aggregator/histogram/benchmark_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/metric/aggregator/histogram/benchmark_test.go b/sdk/metric/aggregator/histogram/benchmark_test.go index 628d5ffb0..7b99b725d 100644 --- a/sdk/metric/aggregator/histogram/benchmark_test.go +++ b/sdk/metric/aggregator/histogram/benchmark_test.go @@ -45,7 +45,7 @@ func benchmarkHistogramSearchFloat64(b *testing.B, size int) { b.ResetTimer() for i := 0; i < b.N; i++ { - agg.Update(ctx, metric.NewFloat64Number(rand.Float64()*inputRange), desc) + _ = agg.Update(ctx, metric.NewFloat64Number(rand.Float64()*inputRange), desc) } } @@ -96,7 +96,7 @@ func benchmarkHistogramSearchInt64(b *testing.B, size int) { b.ResetTimer() for i := 0; i < b.N; i++ { - agg.Update(ctx, metric.NewInt64Number(int64(rand.Float64()*inputRange)), desc) + _ = agg.Update(ctx, metric.NewInt64Number(int64(rand.Float64()*inputRange)), desc) } } From 9d2e78ae2b2402202078808174858d7804b2969b Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 11:21:13 -0700 Subject: [PATCH 090/108] Comment --- sdk/export/metric/aggregator/aggregator.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sdk/export/metric/aggregator/aggregator.go b/sdk/export/metric/aggregator/aggregator.go index 52fe13435..581760412 100644 --- a/sdk/export/metric/aggregator/aggregator.go +++ b/sdk/export/metric/aggregator/aggregator.go @@ -68,8 +68,14 @@ type ( // For a Histogram with N defined boundaries, e.g, [x, y, z]. // There are N+1 counts: [-inf, x), [x, y), [y, z), [z, +inf] Buckets struct { + // Boundaries are floating point numbers, even when + // aggregating integers. Boundaries []float64 - Counts []float64 + + // Counts are floating point numbers to account for + // the possibility of sampling which allows for + // non-integer count values. + Counts []float64 } // Histogram returns the count of events in pre-determined buckets. From 4f3188ab955ea1021d2664d4f7be2674dbc526df Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 21 May 2020 11:36:52 -0700 Subject: [PATCH 091/108] Fix use of values in benchmark --- sdk/metric/aggregator/histogram/benchmark_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/metric/aggregator/histogram/benchmark_test.go b/sdk/metric/aggregator/histogram/benchmark_test.go index 7b99b725d..ff99cbdf6 100644 --- a/sdk/metric/aggregator/histogram/benchmark_test.go +++ b/sdk/metric/aggregator/histogram/benchmark_test.go @@ -45,7 +45,7 @@ func benchmarkHistogramSearchFloat64(b *testing.B, size int) { b.ResetTimer() for i := 0; i < b.N; i++ { - _ = agg.Update(ctx, metric.NewFloat64Number(rand.Float64()*inputRange), desc) + _ = agg.Update(ctx, metric.NewFloat64Number(values[i]), desc) } } @@ -96,7 +96,7 @@ func benchmarkHistogramSearchInt64(b *testing.B, size int) { b.ResetTimer() for i := 0; i < b.N; i++ { - _ = agg.Update(ctx, metric.NewInt64Number(int64(rand.Float64()*inputRange)), desc) + _ = agg.Update(ctx, metric.NewInt64Number(values[i]), desc) } } From 7329ccc8231bb8412dfe0508c0918e1a17c61ec0 Mon Sep 17 00:00:00 2001 From: Stefan Prisca Date: Thu, 21 May 2020 20:40:11 +0200 Subject: [PATCH 092/108] Change OTLP example to use syncer (#756) * change otlp-example to use syncer * precommit Co-authored-by: Joshua MacDonald --- example/otel-collector/README.md | 13 +++++-------- example/otel-collector/go.sum | 1 - example/otel-collector/main.go | 7 +------ 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 67c4857ce..382d41ae7 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -156,14 +156,11 @@ The next step is to create the TraceProvider: ```go tp, err := sdktrace.NewProvider( sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), - sdktrace.WithResourceAttributes( + sdktrace.WithResource(resource.New( // the service name used to display traces in Jaeger - core.Key(conventions.AttributeServiceName).String("test-service"), - ), - sdktrace.WithBatcher(exp, // add following two options to ensure flush - sdktrace.WithScheduleDelayMillis(5), - sdktrace.WithMaxExportBatchSize(2), - )) + kv.Key(conventions.AttributeServiceName).String("test-service"), + )), + sdktrace.WithSyncer(exp)) if err != nil { log.Fatalf("error creating trace provider: %v\n", err) } @@ -175,7 +172,7 @@ After this, you can simply start sending traces: ```go tracer := tp.Tracer("test-tracer") ctx, span := tracer.Start(context.Background(), "CollectorExporter-Example") - defer span.End() +defer span.End() ``` The traces should now be visible from the Jaeger UI (if you have it installed), or thorough the jaeger-query service, under the name `test-service`. diff --git a/example/otel-collector/go.sum b/example/otel-collector/go.sum index 683a554df..7f08f906d 100644 --- a/example/otel-collector/go.sum +++ b/example/otel-collector/go.sum @@ -414,7 +414,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index c16002f7e..446258162 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -55,10 +55,7 @@ func main() { // the service name used to display traces in Jaeger kv.Key(conventions.AttributeServiceName).String("test-service"), )), - sdktrace.WithBatcher(exp, // add following two options to ensure flush - sdktrace.WithBatchTimeout(5), - sdktrace.WithMaxExportBatchSize(2), - )) + sdktrace.WithSyncer(exp)) if err != nil { log.Fatalf("error creating trace provider: %v\n", err) } @@ -75,6 +72,4 @@ func main() { } span.End() - // Wait 1 second before ending - <-time.After(time.Second) } From ad37aae65cbc15f0519100b43719555784a064e1 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Thu, 21 May 2020 12:10:12 -0700 Subject: [PATCH 093/108] Prepare for releasing v0.6.0 --- example/basic/go.mod | 2 +- example/grpc/go.mod | 2 +- example/http/go.mod | 2 +- example/jaeger/go.mod | 4 ++-- example/namedtracer/go.mod | 2 +- example/otel-collector/go.mod | 4 ++-- example/prometheus/go.mod | 4 ++-- example/zipkin/go.mod | 4 ++-- exporters/metric/prometheus/go.mod | 2 +- exporters/otlp/go.mod | 2 +- exporters/trace/jaeger/go.mod | 2 +- exporters/trace/zipkin/go.mod | 2 +- sdk/opentelemetry.go | 2 +- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/example/basic/go.mod b/example/basic/go.mod index 6e4fde14e..aca3a251c 100644 --- a/example/basic/go.mod +++ b/example/basic/go.mod @@ -4,4 +4,4 @@ go 1.13 replace go.opentelemetry.io/otel => ../.. -require go.opentelemetry.io/otel v0.5.0 +require go.opentelemetry.io/otel v0.6.0 diff --git a/example/grpc/go.mod b/example/grpc/go.mod index 7381880ea..6f1a5ab19 100644 --- a/example/grpc/go.mod +++ b/example/grpc/go.mod @@ -6,7 +6,7 @@ replace go.opentelemetry.io/otel => ../.. require ( github.com/golang/protobuf v1.3.2 - go.opentelemetry.io/otel v0.5.0 + go.opentelemetry.io/otel v0.6.0 golang.org/x/net v0.0.0-20190311183353-d8887717615a google.golang.org/grpc v1.27.1 ) diff --git a/example/http/go.mod b/example/http/go.mod index fc1b92d5c..626f720df 100644 --- a/example/http/go.mod +++ b/example/http/go.mod @@ -4,4 +4,4 @@ go 1.13 replace go.opentelemetry.io/otel => ../.. -require go.opentelemetry.io/otel v0.5.0 +require go.opentelemetry.io/otel v0.6.0 diff --git a/example/jaeger/go.mod b/example/jaeger/go.mod index ed023b8ae..d1018e9c6 100644 --- a/example/jaeger/go.mod +++ b/example/jaeger/go.mod @@ -8,6 +8,6 @@ replace ( ) require ( - go.opentelemetry.io/otel v0.5.0 - go.opentelemetry.io/otel/exporters/trace/jaeger v0.5.0 + go.opentelemetry.io/otel v0.6.0 + go.opentelemetry.io/otel/exporters/trace/jaeger v0.6.0 ) diff --git a/example/namedtracer/go.mod b/example/namedtracer/go.mod index 6003fcfa8..298ee4b9e 100644 --- a/example/namedtracer/go.mod +++ b/example/namedtracer/go.mod @@ -4,4 +4,4 @@ go 1.13 replace go.opentelemetry.io/otel => ../.. -require go.opentelemetry.io/otel v0.5.0 +require go.opentelemetry.io/otel v0.6.0 diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod index c81c00eb0..7005f5f12 100644 --- a/example/otel-collector/go.mod +++ b/example/otel-collector/go.mod @@ -4,8 +4,8 @@ go 1.14 require ( github.com/open-telemetry/opentelemetry-collector v0.3.0 - go.opentelemetry.io/otel v0.5.0 - go.opentelemetry.io/otel/exporters/otlp v0.5.0 + go.opentelemetry.io/otel v0.6.0 + go.opentelemetry.io/otel/exporters/otlp v0.6.0 google.golang.org/grpc v1.29.1 ) diff --git a/example/prometheus/go.mod b/example/prometheus/go.mod index 1f540ce87..3a7c6d189 100644 --- a/example/prometheus/go.mod +++ b/example/prometheus/go.mod @@ -8,6 +8,6 @@ replace ( ) require ( - go.opentelemetry.io/otel v0.5.0 - go.opentelemetry.io/otel/exporters/metric/prometheus v0.5.0 + go.opentelemetry.io/otel v0.6.0 + go.opentelemetry.io/otel/exporters/metric/prometheus v0.6.0 ) diff --git a/example/zipkin/go.mod b/example/zipkin/go.mod index 9999bca0f..bd2d9b671 100644 --- a/example/zipkin/go.mod +++ b/example/zipkin/go.mod @@ -8,6 +8,6 @@ replace ( ) require ( - go.opentelemetry.io/otel v0.5.0 - go.opentelemetry.io/otel/exporters/trace/zipkin v0.5.0 + go.opentelemetry.io/otel v0.6.0 + go.opentelemetry.io/otel/exporters/trace/zipkin v0.6.0 ) diff --git a/exporters/metric/prometheus/go.mod b/exporters/metric/prometheus/go.mod index c9c42f002..12ba8b634 100644 --- a/exporters/metric/prometheus/go.mod +++ b/exporters/metric/prometheus/go.mod @@ -9,6 +9,6 @@ require ( github.com/prometheus/client_golang v1.5.0 github.com/prometheus/procfs v0.0.10 // indirect github.com/stretchr/testify v1.4.0 - go.opentelemetry.io/otel v0.5.0 + go.opentelemetry.io/otel v0.6.0 golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect ) diff --git a/exporters/otlp/go.mod b/exporters/otlp/go.mod index f16ea562d..a15cd849d 100644 --- a/exporters/otlp/go.mod +++ b/exporters/otlp/go.mod @@ -9,7 +9,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway v1.14.3 // indirect github.com/open-telemetry/opentelemetry-proto v0.3.0 github.com/stretchr/testify v1.4.0 - go.opentelemetry.io/otel v0.5.0 + go.opentelemetry.io/otel v0.6.0 golang.org/x/text v0.3.2 // indirect google.golang.org/grpc v1.27.1 ) diff --git a/exporters/trace/jaeger/go.mod b/exporters/trace/jaeger/go.mod index dcbf19246..7d3eca0fe 100644 --- a/exporters/trace/jaeger/go.mod +++ b/exporters/trace/jaeger/go.mod @@ -8,7 +8,7 @@ require ( github.com/apache/thrift v0.13.0 github.com/google/go-cmp v0.4.0 github.com/stretchr/testify v1.4.0 - go.opentelemetry.io/otel v0.5.0 + go.opentelemetry.io/otel v0.6.0 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect google.golang.org/api v0.20.0 google.golang.org/grpc v1.27.1 diff --git a/exporters/trace/zipkin/go.mod b/exporters/trace/zipkin/go.mod index ba86783b3..ae4c91632 100644 --- a/exporters/trace/zipkin/go.mod +++ b/exporters/trace/zipkin/go.mod @@ -7,6 +7,6 @@ replace go.opentelemetry.io/otel => ../../.. require ( github.com/openzipkin/zipkin-go v0.2.2 github.com/stretchr/testify v1.4.0 - go.opentelemetry.io/otel v0.5.0 + go.opentelemetry.io/otel v0.6.0 google.golang.org/grpc v1.27.1 ) diff --git a/sdk/opentelemetry.go b/sdk/opentelemetry.go index 33a9a9456..eaafcf3a1 100644 --- a/sdk/opentelemetry.go +++ b/sdk/opentelemetry.go @@ -17,5 +17,5 @@ package opentelemetry // import "go.opentelemetry.io/otel/sdk" // Version is the current release version of OpenTelemetry in use. func Version() string { - return "0.5.0" + return "0.6.0" } From b17a7845df336dddca3ef79c9bb082ed074492e5 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Thu, 21 May 2020 15:42:14 -0700 Subject: [PATCH 094/108] Rename Register* to New* (#761) --- api/global/internal/meter_test.go | 6 +-- api/global/internal/registry_test.go | 4 +- api/metric/api_test.go | 20 +++---- api/metric/meter.go | 48 ++++++++--------- api/metric/must.go | 72 ++++++++++++------------- api/metric/registry/registry_test.go | 4 +- example/basic/main.go | 2 +- example/prometheus/main.go | 2 +- exporters/otlp/otlp_integration_test.go | 4 +- sdk/metric/benchmark_test.go | 6 +-- sdk/metric/correct_test.go | 34 ++++++------ 11 files changed, 101 insertions(+), 101 deletions(-) diff --git a/api/global/internal/meter_test.go b/api/global/internal/meter_test.go index 95438259c..013a3d5a2 100644 --- a/api/global/internal/meter_test.go +++ b/api/global/internal/meter_test.go @@ -86,12 +86,12 @@ func TestDirect(t *testing.T) { valuerecorder.Record(ctx, 1, labels1...) valuerecorder.Record(ctx, 2, labels1...) - _ = Must(meter1).RegisterFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { + _ = Must(meter1).NewFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1., labels1...) result.Observe(2., labels2...) }) - _ = Must(meter1).RegisterInt64ValueObserver("test.valueobserver.int", func(_ context.Context, result metric.Int64ObserverResult) { + _ = Must(meter1).NewInt64ValueObserver("test.valueobserver.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(1, labels1...) result.Observe(2, labels2...) }) @@ -331,7 +331,7 @@ func TestImplementationIndirection(t *testing.T) { require.False(t, ok) // Async: no SDK yet - valueobserver := Must(meter1).RegisterFloat64ValueObserver( + valueobserver := Must(meter1).NewFloat64ValueObserver( "interface.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) {}, ) diff --git a/api/global/internal/registry_test.go b/api/global/internal/registry_test.go index a37ec22fd..0227bfb94 100644 --- a/api/global/internal/registry_test.go +++ b/api/global/internal/registry_test.go @@ -44,10 +44,10 @@ var ( return unwrap(MeterProvider().Meter(libraryName).NewFloat64ValueRecorder(name)) }, "valueobserver.int64": func(name, libraryName string) (metric.InstrumentImpl, error) { - return unwrap(MeterProvider().Meter(libraryName).RegisterInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {})) + return unwrap(MeterProvider().Meter(libraryName).NewInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {})) }, "valueobserver.float64": func(name, libraryName string) (metric.InstrumentImpl, error) { - return unwrap(MeterProvider().Meter(libraryName).RegisterFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {})) + return unwrap(MeterProvider().Meter(libraryName).NewFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {})) }, } ) diff --git a/api/metric/api_test.go b/api/metric/api_test.go index e5c194977..ebe21b4b9 100644 --- a/api/metric/api_test.go +++ b/api/metric/api_test.go @@ -183,7 +183,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("float valueobserver", func(t *testing.T) { labels := []kv.KeyValue{kv.String("O", "P")} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { + o := Must(meter).NewFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(42.1, labels...) }) mockSDK.RunAsyncInstruments() @@ -194,7 +194,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("int valueobserver", func(t *testing.T) { labels := []kv.KeyValue{} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterInt64ValueObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) { + o := Must(meter).NewInt64ValueObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-142, labels...) }) mockSDK.RunAsyncInstruments() @@ -205,7 +205,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("float sumobserver", func(t *testing.T) { labels := []kv.KeyValue{kv.String("O", "P")} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterFloat64SumObserver("test.sumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { + o := Must(meter).NewFloat64SumObserver("test.sumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(42.1, labels...) }) mockSDK.RunAsyncInstruments() @@ -216,7 +216,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("int sumobserver", func(t *testing.T) { labels := []kv.KeyValue{} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterInt64SumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) { + o := Must(meter).NewInt64SumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-142, labels...) }) mockSDK.RunAsyncInstruments() @@ -227,7 +227,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("float updownsumobserver", func(t *testing.T) { labels := []kv.KeyValue{kv.String("O", "P")} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterFloat64UpDownSumObserver("test.updownsumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { + o := Must(meter).NewFloat64UpDownSumObserver("test.updownsumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(42.1, labels...) }) mockSDK.RunAsyncInstruments() @@ -238,7 +238,7 @@ func TestObserverInstruments(t *testing.T) { t.Run("int updownsumobserver", func(t *testing.T) { labels := []kv.KeyValue{} mockSDK, meter := mockTest.NewMeter() - o := Must(meter).RegisterInt64UpDownSumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) { + o := Must(meter).NewInt64UpDownSumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-142, labels...) }) mockSDK.RunAsyncInstruments() @@ -309,8 +309,8 @@ func TestBatchObserverInstruments(t *testing.T) { ) }, ) - obs1 = cb.RegisterInt64ValueObserver("test.observer.int") - obs2 = cb.RegisterFloat64ValueObserver("test.observer.float") + obs1 = cb.NewInt64ValueObserver("test.observer.int") + obs2 = cb.NewFloat64ValueObserver("test.observer.float") mockSDK.RunAsyncInstruments() @@ -394,7 +394,7 @@ func TestWrappedInstrumentError(t *testing.T) { require.Equal(t, err, metric.ErrSDKReturnedNilImpl) require.NotNil(t, valuerecorder.SyncImpl()) - observer, err := meter.RegisterInt64ValueObserver("test.observer", func(_ context.Context, result metric.Int64ObserverResult) {}) + observer, err := meter.NewInt64ValueObserver("test.observer", func(_ context.Context, result metric.Int64ObserverResult) {}) require.NotNil(t, err) require.NotNil(t, observer.AsyncImpl()) @@ -404,7 +404,7 @@ func TestNilCallbackObserverNoop(t *testing.T) { // Tests that a nil callback yields a no-op observer without error. _, meter := mockTest.NewMeter() - observer := Must(meter).RegisterInt64ValueObserver("test.observer", nil) + observer := Must(meter).NewInt64ValueObserver("test.observer", nil) _, ok := observer.AsyncImpl().(metric.NoopAsync) require.True(t, ok) diff --git a/api/metric/meter.go b/api/metric/meter.go index 4caa7a7a6..200431f24 100644 --- a/api/metric/meter.go +++ b/api/metric/meter.go @@ -118,11 +118,11 @@ func (m Meter) NewFloat64ValueRecorder(name string, opts ...Option) (Float64Valu m.newSync(name, ValueRecorderKind, Float64NumberKind, opts)) } -// RegisterInt64ValueObserver creates a new integer ValueObserver instrument +// NewInt64ValueObserver creates a new integer ValueObserver instrument // with the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (m Meter) RegisterInt64ValueObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64ValueObserver, error) { +func (m Meter) NewInt64ValueObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64ValueObserver, error) { if callback == nil { return wrapInt64ValueObserverInstrument(NoopAsync{}, nil) } @@ -131,11 +131,11 @@ func (m Meter) RegisterInt64ValueObserver(name string, callback Int64ObserverCal newInt64AsyncRunner(callback))) } -// RegisterFloat64ValueObserver creates a new floating point ValueObserver with +// NewFloat64ValueObserver creates a new floating point ValueObserver with // the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (m Meter) RegisterFloat64ValueObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64ValueObserver, error) { +func (m Meter) NewFloat64ValueObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64ValueObserver, error) { if callback == nil { return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil) } @@ -144,11 +144,11 @@ func (m Meter) RegisterFloat64ValueObserver(name string, callback Float64Observe newFloat64AsyncRunner(callback))) } -// RegisterInt64SumObserver creates a new integer SumObserver instrument +// NewInt64SumObserver creates a new integer SumObserver instrument // with the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (m Meter) RegisterInt64SumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64SumObserver, error) { +func (m Meter) NewInt64SumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64SumObserver, error) { if callback == nil { return wrapInt64SumObserverInstrument(NoopAsync{}, nil) } @@ -157,11 +157,11 @@ func (m Meter) RegisterInt64SumObserver(name string, callback Int64ObserverCallb newInt64AsyncRunner(callback))) } -// RegisterFloat64SumObserver creates a new floating point SumObserver with +// NewFloat64SumObserver creates a new floating point SumObserver with // the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (m Meter) RegisterFloat64SumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64SumObserver, error) { +func (m Meter) NewFloat64SumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64SumObserver, error) { if callback == nil { return wrapFloat64SumObserverInstrument(NoopAsync{}, nil) } @@ -170,11 +170,11 @@ func (m Meter) RegisterFloat64SumObserver(name string, callback Float64ObserverC newFloat64AsyncRunner(callback))) } -// RegisterInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument +// NewInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument // with the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (m Meter) RegisterInt64UpDownSumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64UpDownSumObserver, error) { +func (m Meter) NewInt64UpDownSumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64UpDownSumObserver, error) { if callback == nil { return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil) } @@ -183,11 +183,11 @@ func (m Meter) RegisterInt64UpDownSumObserver(name string, callback Int64Observe newInt64AsyncRunner(callback))) } -// RegisterFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with +// NewFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with // the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (m Meter) RegisterFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64UpDownSumObserver, error) { +func (m Meter) NewFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64UpDownSumObserver, error) { if callback == nil { return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil) } @@ -196,11 +196,11 @@ func (m Meter) RegisterFloat64UpDownSumObserver(name string, callback Float64Obs newFloat64AsyncRunner(callback))) } -// RegisterInt64ValueObserver creates a new integer ValueObserver instrument +// NewInt64ValueObserver creates a new integer ValueObserver instrument // with the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (b BatchObserver) RegisterInt64ValueObserver(name string, opts ...Option) (Int64ValueObserver, error) { +func (b BatchObserver) NewInt64ValueObserver(name string, opts ...Option) (Int64ValueObserver, error) { if b.runner == nil { return wrapInt64ValueObserverInstrument(NoopAsync{}, nil) } @@ -208,11 +208,11 @@ func (b BatchObserver) RegisterInt64ValueObserver(name string, opts ...Option) ( b.meter.newAsync(name, ValueObserverKind, Int64NumberKind, opts, b.runner)) } -// RegisterFloat64ValueObserver creates a new floating point ValueObserver with +// NewFloat64ValueObserver creates a new floating point ValueObserver with // the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (b BatchObserver) RegisterFloat64ValueObserver(name string, opts ...Option) (Float64ValueObserver, error) { +func (b BatchObserver) NewFloat64ValueObserver(name string, opts ...Option) (Float64ValueObserver, error) { if b.runner == nil { return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil) } @@ -221,11 +221,11 @@ func (b BatchObserver) RegisterFloat64ValueObserver(name string, opts ...Option) b.runner)) } -// RegisterInt64SumObserver creates a new integer SumObserver instrument +// NewInt64SumObserver creates a new integer SumObserver instrument // with the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (b BatchObserver) RegisterInt64SumObserver(name string, opts ...Option) (Int64SumObserver, error) { +func (b BatchObserver) NewInt64SumObserver(name string, opts ...Option) (Int64SumObserver, error) { if b.runner == nil { return wrapInt64SumObserverInstrument(NoopAsync{}, nil) } @@ -233,11 +233,11 @@ func (b BatchObserver) RegisterInt64SumObserver(name string, opts ...Option) (In b.meter.newAsync(name, SumObserverKind, Int64NumberKind, opts, b.runner)) } -// RegisterFloat64SumObserver creates a new floating point SumObserver with +// NewFloat64SumObserver creates a new floating point SumObserver with // the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (b BatchObserver) RegisterFloat64SumObserver(name string, opts ...Option) (Float64SumObserver, error) { +func (b BatchObserver) NewFloat64SumObserver(name string, opts ...Option) (Float64SumObserver, error) { if b.runner == nil { return wrapFloat64SumObserverInstrument(NoopAsync{}, nil) } @@ -246,11 +246,11 @@ func (b BatchObserver) RegisterFloat64SumObserver(name string, opts ...Option) ( b.runner)) } -// RegisterInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument +// NewInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument // with the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (b BatchObserver) RegisterInt64UpDownSumObserver(name string, opts ...Option) (Int64UpDownSumObserver, error) { +func (b BatchObserver) NewInt64UpDownSumObserver(name string, opts ...Option) (Int64UpDownSumObserver, error) { if b.runner == nil { return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil) } @@ -258,11 +258,11 @@ func (b BatchObserver) RegisterInt64UpDownSumObserver(name string, opts ...Optio b.meter.newAsync(name, UpDownSumObserverKind, Int64NumberKind, opts, b.runner)) } -// RegisterFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with +// NewFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with // the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). -func (b BatchObserver) RegisterFloat64UpDownSumObserver(name string, opts ...Option) (Float64UpDownSumObserver, error) { +func (b BatchObserver) NewFloat64UpDownSumObserver(name string, opts ...Option) (Float64UpDownSumObserver, error) { if b.runner == nil { return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil) } diff --git a/api/metric/must.go b/api/metric/must.go index 9c5115812..c409c6b55 100644 --- a/api/metric/must.go +++ b/api/metric/must.go @@ -93,60 +93,60 @@ func (mm MeterMust) NewFloat64ValueRecorder(name string, mos ...Option) Float64V } } -// RegisterInt64ValueObserver calls `Meter.RegisterInt64ValueObserver` and +// NewInt64ValueObserver calls `Meter.NewInt64ValueObserver` and // returns the instrument, panicking if it encounters an error. -func (mm MeterMust) RegisterInt64ValueObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64ValueObserver { - if inst, err := mm.meter.RegisterInt64ValueObserver(name, callback, oos...); err != nil { +func (mm MeterMust) NewInt64ValueObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64ValueObserver { + if inst, err := mm.meter.NewInt64ValueObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } -// RegisterFloat64ValueObserver calls `Meter.RegisterFloat64ValueObserver` and +// NewFloat64ValueObserver calls `Meter.NewFloat64ValueObserver` and // returns the instrument, panicking if it encounters an error. -func (mm MeterMust) RegisterFloat64ValueObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64ValueObserver { - if inst, err := mm.meter.RegisterFloat64ValueObserver(name, callback, oos...); err != nil { +func (mm MeterMust) NewFloat64ValueObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64ValueObserver { + if inst, err := mm.meter.NewFloat64ValueObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } -// RegisterInt64SumObserver calls `Meter.RegisterInt64SumObserver` and +// NewInt64SumObserver calls `Meter.NewInt64SumObserver` and // returns the instrument, panicking if it encounters an error. -func (mm MeterMust) RegisterInt64SumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64SumObserver { - if inst, err := mm.meter.RegisterInt64SumObserver(name, callback, oos...); err != nil { +func (mm MeterMust) NewInt64SumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64SumObserver { + if inst, err := mm.meter.NewInt64SumObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } -// RegisterFloat64SumObserver calls `Meter.RegisterFloat64SumObserver` and +// NewFloat64SumObserver calls `Meter.NewFloat64SumObserver` and // returns the instrument, panicking if it encounters an error. -func (mm MeterMust) RegisterFloat64SumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64SumObserver { - if inst, err := mm.meter.RegisterFloat64SumObserver(name, callback, oos...); err != nil { +func (mm MeterMust) NewFloat64SumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64SumObserver { + if inst, err := mm.meter.NewFloat64SumObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } -// RegisterInt64UpDownSumObserver calls `Meter.RegisterInt64UpDownSumObserver` and +// NewInt64UpDownSumObserver calls `Meter.NewInt64UpDownSumObserver` and // returns the instrument, panicking if it encounters an error. -func (mm MeterMust) RegisterInt64UpDownSumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64UpDownSumObserver { - if inst, err := mm.meter.RegisterInt64UpDownSumObserver(name, callback, oos...); err != nil { +func (mm MeterMust) NewInt64UpDownSumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64UpDownSumObserver { + if inst, err := mm.meter.NewInt64UpDownSumObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } -// RegisterFloat64UpDownSumObserver calls `Meter.RegisterFloat64UpDownSumObserver` and +// NewFloat64UpDownSumObserver calls `Meter.NewFloat64UpDownSumObserver` and // returns the instrument, panicking if it encounters an error. -func (mm MeterMust) RegisterFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64UpDownSumObserver { - if inst, err := mm.meter.RegisterFloat64UpDownSumObserver(name, callback, oos...); err != nil { +func (mm MeterMust) NewFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64UpDownSumObserver { + if inst, err := mm.meter.NewFloat64UpDownSumObserver(name, callback, oos...); err != nil { panic(err) } else { return inst @@ -161,60 +161,60 @@ func (mm MeterMust) NewBatchObserver(callback BatchObserverCallback) BatchObserv } } -// RegisterInt64ValueObserver calls `BatchObserver.RegisterInt64ValueObserver` and +// NewInt64ValueObserver calls `BatchObserver.NewInt64ValueObserver` and // returns the instrument, panicking if it encounters an error. -func (bm BatchObserverMust) RegisterInt64ValueObserver(name string, oos ...Option) Int64ValueObserver { - if inst, err := bm.batch.RegisterInt64ValueObserver(name, oos...); err != nil { +func (bm BatchObserverMust) NewInt64ValueObserver(name string, oos ...Option) Int64ValueObserver { + if inst, err := bm.batch.NewInt64ValueObserver(name, oos...); err != nil { panic(err) } else { return inst } } -// RegisterFloat64ValueObserver calls `BatchObserver.RegisterFloat64ValueObserver` and +// NewFloat64ValueObserver calls `BatchObserver.NewFloat64ValueObserver` and // returns the instrument, panicking if it encounters an error. -func (bm BatchObserverMust) RegisterFloat64ValueObserver(name string, oos ...Option) Float64ValueObserver { - if inst, err := bm.batch.RegisterFloat64ValueObserver(name, oos...); err != nil { +func (bm BatchObserverMust) NewFloat64ValueObserver(name string, oos ...Option) Float64ValueObserver { + if inst, err := bm.batch.NewFloat64ValueObserver(name, oos...); err != nil { panic(err) } else { return inst } } -// RegisterInt64SumObserver calls `BatchObserver.RegisterInt64SumObserver` and +// NewInt64SumObserver calls `BatchObserver.NewInt64SumObserver` and // returns the instrument, panicking if it encounters an error. -func (bm BatchObserverMust) RegisterInt64SumObserver(name string, oos ...Option) Int64SumObserver { - if inst, err := bm.batch.RegisterInt64SumObserver(name, oos...); err != nil { +func (bm BatchObserverMust) NewInt64SumObserver(name string, oos ...Option) Int64SumObserver { + if inst, err := bm.batch.NewInt64SumObserver(name, oos...); err != nil { panic(err) } else { return inst } } -// RegisterFloat64SumObserver calls `BatchObserver.RegisterFloat64SumObserver` and +// NewFloat64SumObserver calls `BatchObserver.NewFloat64SumObserver` and // returns the instrument, panicking if it encounters an error. -func (bm BatchObserverMust) RegisterFloat64SumObserver(name string, oos ...Option) Float64SumObserver { - if inst, err := bm.batch.RegisterFloat64SumObserver(name, oos...); err != nil { +func (bm BatchObserverMust) NewFloat64SumObserver(name string, oos ...Option) Float64SumObserver { + if inst, err := bm.batch.NewFloat64SumObserver(name, oos...); err != nil { panic(err) } else { return inst } } -// RegisterInt64UpDownSumObserver calls `BatchObserver.RegisterInt64UpDownSumObserver` and +// NewInt64UpDownSumObserver calls `BatchObserver.NewInt64UpDownSumObserver` and // returns the instrument, panicking if it encounters an error. -func (bm BatchObserverMust) RegisterInt64UpDownSumObserver(name string, oos ...Option) Int64UpDownSumObserver { - if inst, err := bm.batch.RegisterInt64UpDownSumObserver(name, oos...); err != nil { +func (bm BatchObserverMust) NewInt64UpDownSumObserver(name string, oos ...Option) Int64UpDownSumObserver { + if inst, err := bm.batch.NewInt64UpDownSumObserver(name, oos...); err != nil { panic(err) } else { return inst } } -// RegisterFloat64UpDownSumObserver calls `BatchObserver.RegisterFloat64UpDownSumObserver` and +// NewFloat64UpDownSumObserver calls `BatchObserver.NewFloat64UpDownSumObserver` and // returns the instrument, panicking if it encounters an error. -func (bm BatchObserverMust) RegisterFloat64UpDownSumObserver(name string, oos ...Option) Float64UpDownSumObserver { - if inst, err := bm.batch.RegisterFloat64UpDownSumObserver(name, oos...); err != nil { +func (bm BatchObserverMust) NewFloat64UpDownSumObserver(name string, oos ...Option) Float64UpDownSumObserver { + if inst, err := bm.batch.NewFloat64UpDownSumObserver(name, oos...); err != nil { panic(err) } else { return inst diff --git a/api/metric/registry/registry_test.go b/api/metric/registry/registry_test.go index e80e23f39..dc06213c2 100644 --- a/api/metric/registry/registry_test.go +++ b/api/metric/registry/registry_test.go @@ -45,10 +45,10 @@ var ( return unwrap(m.NewFloat64ValueRecorder(name)) }, "valueobserver.int64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { - return unwrap(m.RegisterInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {})) + return unwrap(m.NewInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {})) }, "valueobserver.float64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) { - return unwrap(m.RegisterFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {})) + return unwrap(m.NewFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {})) }, } ) diff --git a/example/basic/main.go b/example/basic/main.go index 6d42fd4d1..911350049 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -77,7 +77,7 @@ func main() { oneMetricCB := func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1, commonLabels...) } - _ = metric.Must(meter).RegisterFloat64ValueObserver("ex.com.one", oneMetricCB, + _ = metric.Must(meter).NewFloat64ValueObserver("ex.com.one", oneMetricCB, metric.WithDescription("A ValueObserver set to 1.0"), ) diff --git a/example/prometheus/main.go b/example/prometheus/main.go index 8000dec0e..ef2bd4730 100644 --- a/example/prometheus/main.go +++ b/example/prometheus/main.go @@ -56,7 +56,7 @@ func main() { (*observerLock).RUnlock() result.Observe(value, labels...) } - _ = metric.Must(meter).RegisterFloat64ValueObserver("ex.com.one", cb, + _ = metric.Must(meter).NewFloat64ValueObserver("ex.com.one", cb, metric.WithDescription("A ValueObserver set to 1.0"), ) diff --git a/exporters/otlp/otlp_integration_test.go b/exporters/otlp/otlp_integration_test.go index 41f5bb195..0f01eeace 100644 --- a/exporters/otlp/otlp_integration_test.go +++ b/exporters/otlp/otlp_integration_test.go @@ -162,12 +162,12 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) callback := func(v int64) metricapi.Int64ObserverCallback { return metricapi.Int64ObserverCallback(func(_ context.Context, result metricapi.Int64ObserverResult) { result.Observe(v, labels...) }) }(data.val) - metricapi.Must(meter).RegisterInt64ValueObserver(name, callback) + metricapi.Must(meter).NewInt64ValueObserver(name, callback) case metricapi.Float64NumberKind: callback := func(v float64) metricapi.Float64ObserverCallback { return metricapi.Float64ObserverCallback(func(_ context.Context, result metricapi.Float64ObserverResult) { result.Observe(v, labels...) }) }(float64(data.val)) - metricapi.Must(meter).RegisterFloat64ValueObserver(name, callback) + metricapi.Must(meter).NewFloat64ValueObserver(name, callback) default: assert.Failf(t, "unsupported number testing kind", data.nKind.String()) } diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index f4a6b315f..d28649319 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -430,7 +430,7 @@ func BenchmarkObserverRegistration(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - fix.meter.RegisterInt64ValueObserver(names[i], cb) + fix.meter.NewInt64ValueObserver(names[i], cb) } } @@ -438,7 +438,7 @@ func BenchmarkValueObserverObservationInt64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - _ = fix.meter.RegisterInt64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { + _ = fix.meter.NewInt64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((int64)(i), labs...) } @@ -453,7 +453,7 @@ func BenchmarkValueObserverObservationFloat64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - _ = fix.meter.RegisterFloat64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) { + _ = fix.meter.NewFloat64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((float64)(i), labs...) } diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index 35a64c16b..c0c03139a 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -320,13 +320,13 @@ func TestObserverCollection(t *testing.T) { ctx := context.Background() meter, sdk, integrator := newSDK(t) - _ = Must(meter).RegisterFloat64ValueObserver("float.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) { + _ = Must(meter).NewFloat64ValueObserver("float.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1, kv.String("A", "B")) // last value wins result.Observe(-1, kv.String("A", "B")) result.Observe(-1, kv.String("C", "D")) }) - _ = Must(meter).RegisterInt64ValueObserver("int.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { + _ = Must(meter).NewInt64ValueObserver("int.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-1, kv.String("A", "B")) result.Observe(1) // last value wins @@ -334,12 +334,12 @@ func TestObserverCollection(t *testing.T) { result.Observe(1) }) - _ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) { + _ = Must(meter).NewFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1, kv.String("A", "B")) result.Observe(2, kv.String("A", "B")) result.Observe(1, kv.String("C", "D")) }) - _ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) { + _ = Must(meter).NewInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(2, kv.String("A", "B")) result.Observe(1) // last value wins @@ -347,12 +347,12 @@ func TestObserverCollection(t *testing.T) { result.Observe(1) }) - _ = Must(meter).RegisterFloat64UpDownSumObserver("float.updownsumobserver", func(_ context.Context, result metric.Float64ObserverResult) { + _ = Must(meter).NewFloat64UpDownSumObserver("float.updownsumobserver", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1, kv.String("A", "B")) result.Observe(-2, kv.String("A", "B")) result.Observe(1, kv.String("C", "D")) }) - _ = Must(meter).RegisterInt64UpDownSumObserver("int.updownsumobserver", func(_ context.Context, result metric.Int64ObserverResult) { + _ = Must(meter).NewInt64UpDownSumObserver("int.updownsumobserver", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(2, kv.String("A", "B")) result.Observe(1) // last value wins @@ -360,7 +360,7 @@ func TestObserverCollection(t *testing.T) { result.Observe(-1) }) - _ = Must(meter).RegisterInt64ValueObserver("empty.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { + _ = Must(meter).NewInt64ValueObserver("empty.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { }) collected := sdk.Collect(ctx) @@ -393,13 +393,13 @@ func TestSumObserverInputRange(t *testing.T) { ctx := context.Background() meter, sdk, integrator := newSDK(t) - _ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) { + _ = Must(meter).NewFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(-2, kv.String("A", "B")) require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) result.Observe(-1, kv.String("C", "D")) require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) }) - _ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) { + _ = Must(meter).NewInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-1, kv.String("A", "B")) require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr()) result.Observe(-1) @@ -458,12 +458,12 @@ func TestObserverBatch(t *testing.T) { intUpDownSumObs.Observation(10), ) }) - floatValueObs = batch.RegisterFloat64ValueObserver("float.valueobserver") - intValueObs = batch.RegisterInt64ValueObserver("int.valueobserver") - floatSumObs = batch.RegisterFloat64SumObserver("float.sumobserver") - intSumObs = batch.RegisterInt64SumObserver("int.sumobserver") - floatUpDownSumObs = batch.RegisterFloat64UpDownSumObserver("float.updownsumobserver") - intUpDownSumObs = batch.RegisterInt64UpDownSumObserver("int.updownsumobserver") + floatValueObs = batch.NewFloat64ValueObserver("float.valueobserver") + intValueObs = batch.NewInt64ValueObserver("int.valueobserver") + floatSumObs = batch.NewFloat64SumObserver("float.sumobserver") + intSumObs = batch.NewInt64SumObserver("int.sumobserver") + floatUpDownSumObs = batch.NewFloat64UpDownSumObserver("float.updownsumobserver") + intUpDownSumObs = batch.NewInt64UpDownSumObserver("int.updownsumobserver") collected := sdk.Collect(ctx) @@ -570,7 +570,7 @@ func TestIncorrectInstruments(t *testing.T) { counter = metric.Must(noopMeter).NewInt64Counter("counter") observer = metric.Must(noopMeter).NewBatchObserver( func(context.Context, metric.BatchObserverResult) {}, - ).RegisterInt64ValueObserver("observer") + ).NewInt64ValueObserver("observer") meter.RecordBatch(ctx, nil, counter.Measurement(1)) meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) { @@ -587,7 +587,7 @@ func TestSyncInAsync(t *testing.T) { meter, sdk, integrator := newSDK(t) counter := Must(meter).NewFloat64Counter("counter") - _ = Must(meter).RegisterInt64ValueObserver("observer", + _ = Must(meter).NewInt64ValueObserver("observer", func(ctx context.Context, result metric.Int64ObserverResult) { result.Observe(10) counter.Add(ctx, 100) From f8d53694be72463544ca47a44e163a43bb75534d Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Tue, 26 May 2020 11:35:34 -0700 Subject: [PATCH 095/108] Clean up tools (#762) * Update tools go deps gojq is used by verify_examples.sh * Fix script name in RELEASING.md * Remove trailing whitespace in verify_examples.sh * Actually fix gojq in go.mod --- RELEASING.md | 2 +- tools/go.mod | 4 +--- tools/go.sum | 36 +++++++++++++++++++++++++++++++++++- tools/tools.go | 1 + verify_examples.sh | 5 ++--- 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 98449fe47..a20da22e1 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -12,7 +12,7 @@ depending on the master. So it is not a concern. 4. Create a PR on github and merge the PR once approved. ``` - ./pre-release.sh -t + ./pre_release.sh -t git diff master git push ``` diff --git a/tools/go.mod b/tools/go.mod index 75faf198f..bf34227f0 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -4,9 +4,7 @@ go 1.13 require ( github.com/client9/misspell v0.3.4 - github.com/fatih/color v1.9.0 // indirect github.com/golangci/golangci-lint v1.25.1 - github.com/mattn/go-isatty v0.0.12 // indirect - golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect + github.com/itchyny/gojq v0.10.1 golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e ) diff --git a/tools/go.sum b/tools/go.sum index 080d702d7..3dfad125c 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -5,8 +5,15 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/alecthomas/go-thrift v0.0.0-20170109061633-7914173639b2/go.mod h1:CxCgO+NdpMdi9SsTlGbc0W+/UNxO3I0AabOEJZ3w61w= +github.com/alecthomas/kong v0.2.1/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI= +github.com/alecthomas/participle v0.4.2-0.20191220090139-9fbceec1d131 h1:iPgE4wTIM/fgSreWdpxnKXxaGOgGwfPqc2aVPq2BFSU= +github.com/alecthomas/participle v0.4.2-0.20191220090139-9fbceec1d131/go.mod h1:T8u4bQOSMwrkTWOSyt8/jSFPEnRtd0FKFMjVfYBlqPs= +github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -29,6 +36,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU= +github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= @@ -127,10 +136,18 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/itchyny/astgen-go v0.0.0-20200116103543-aaa595cf980e h1:PupVBrJNomt2fTXto19vW8Jh1ftn1oKxgtjSzSuLBZI= +github.com/itchyny/astgen-go v0.0.0-20200116103543-aaa595cf980e/go.mod h1:9Gyr9nZoENI+woes+xm+BFhmvYmAp6bPtXD866pQH9g= +github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA= +github.com/itchyny/gojq v0.10.1 h1:52TnrHnzmenfqUtJ52OfjG16uDoFSu1xYmfVQ5kRMuQ= +github.com/itchyny/gojq v0.10.1/go.mod h1:dJzXXNL1A+1rjDF8tDTzW5vOe4i9iIkKSH21HxV76Sw= +github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= +github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk= github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s= github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 h1:jNYPNLe3d8smommaoQlK7LOA5ESyUJJ+Wf79ZtA7Vp4= @@ -158,6 +175,10 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lestrrat-go/strftime v1.0.1 h1:o7qz5pmLzPDLyGW4lG6JvTKPUfTFXwe+vOamIYWtnVU= +github.com/lestrrat-go/strftime v1.0.1/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= @@ -171,11 +192,14 @@ github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUC github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -199,6 +223,8 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/pbnjay/strptime v0.0.0-20140226051138-5c05b0d668c9 h1:4lfz0keanz7/gAlvJ7lAe9zmE08HXxifBZJC0AdeGKo= +github.com/pbnjay/strptime v0.0.0-20140226051138-5c05b0d668c9/go.mod h1:6Hr+C/olSdkdL3z68MlyXWzwhvwmwN7KuUFXGb3PoOk= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -206,6 +232,8 @@ github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -264,12 +292,15 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2 h1:Xr9gkxfOP0KQWXKNqmwe8vEeSUiUj4Rlee9CMVX2ZUQ= github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= +github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto= +github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= github.com/tetafro/godot v0.2.5 h1:7+EYJM/Z4gYZhBFdRrVm6JTj5ZLw/QI1j4RfEOXJviE= github.com/tetafro/godot v0.2.5/go.mod h1:pT6/T8+h6//L/LwQcFc4C0xpfy1euZwzS1sHdrFCms0= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q= @@ -343,8 +374,10 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -403,6 +436,7 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/tools/tools.go b/tools/tools.go index f449e2868..f0418c71d 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -19,5 +19,6 @@ package tools import ( _ "github.com/client9/misspell/cmd/misspell" _ "github.com/golangci/golangci-lint/cmd/golangci-lint" + _ "github.com/itchyny/gojq" _ "golang.org/x/tools/cmd/stringer" ) diff --git a/verify_examples.sh b/verify_examples.sh index 8de8ba89d..dbb61a422 100755 --- a/verify_examples.sh +++ b/verify_examples.sh @@ -63,16 +63,15 @@ for dir in $PACKAGE_DIRS; do # strip double quotes replaces=("${replaces[@]%\"}") && \ replaces=("${replaces[@]#\"}") && \ - # make an array (-dropreplace=mod1 -dropreplace=mod2 …) + # make an array (-dropreplace=mod1 -dropreplace=mod2 …) dropreplaces=("${replaces[@]/#/-dropreplace=}") && \ go mod edit -module "oteltmp/${dir}" "${dropreplaces[@]}" && \ go mod tidy) done printf "Update done:\n\n" -# Build directories that contain main package. These directories are different than +# Build directories that contain main package. These directories are different than # directories that contain go.mod files. -# printf "Build examples:\n" EXAMPLES=$(./get_main_pkgs.sh ./example) for ex in $EXAMPLES; do From 90703756d49acbd82b0e4cca0eec13bcc68e2d2c Mon Sep 17 00:00:00 2001 From: Anthony J Mirabella Date: Tue, 26 May 2020 21:06:13 -0400 Subject: [PATCH 096/108] Move HTTP request semantic convention extractors from contrib --- api/standard/http.go | 277 ++++++++++++++ api/standard/http_test.go | 777 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 1054 insertions(+) create mode 100644 api/standard/http.go create mode 100644 api/standard/http_test.go diff --git a/api/standard/http.go b/api/standard/http.go new file mode 100644 index 000000000..3adbfb87d --- /dev/null +++ b/api/standard/http.go @@ -0,0 +1,277 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package standard + +import ( + "fmt" + "net" + "net/http" + "strconv" + "strings" + + "google.golang.org/grpc/codes" + + "go.opentelemetry.io/otel/api/kv" +) + +// NetAttributesFromHTTPRequest generates attributes of the net +// namespace as specified by the OpenTelemetry specification for a +// span. The network parameter is a string that net.Dial function +// from standard library can understand. +func NetAttributesFromHTTPRequest(network string, request *http.Request) []kv.KeyValue { + attrs := []kv.KeyValue{} + + switch network { + case "tcp", "tcp4", "tcp6": + attrs = append(attrs, NetTransportTCP) + case "udp", "udp4", "udp6": + attrs = append(attrs, NetTransportUDP) + case "ip", "ip4", "ip6": + attrs = append(attrs, NetTransportIP) + case "unix", "unixgram", "unixpacket": + attrs = append(attrs, NetTransportUnix) + default: + attrs = append(attrs, NetTransportOther) + } + + peerName, peerIP, peerPort := "", "", 0 + { + hostPart := request.RemoteAddr + portPart := "" + if idx := strings.LastIndex(hostPart, ":"); idx >= 0 { + hostPart = request.RemoteAddr[:idx] + portPart = request.RemoteAddr[idx+1:] + } + if hostPart != "" { + if ip := net.ParseIP(hostPart); ip != nil { + peerIP = ip.String() + } else { + peerName = hostPart + } + + if portPart != "" { + numPort, err := strconv.ParseUint(portPart, 10, 16) + if err == nil { + peerPort = (int)(numPort) + } else { + peerName, peerIP = "", "" + } + } + } + } + if peerName != "" { + attrs = append(attrs, NetPeerNameKey.String(peerName)) + } + if peerIP != "" { + attrs = append(attrs, NetPeerIPKey.String(peerIP)) + } + if peerPort != 0 { + attrs = append(attrs, NetPeerPortKey.Int(peerPort)) + } + + hostIP, hostName, hostPort := "", "", 0 + for _, someHost := range []string{request.Host, request.Header.Get("Host"), request.URL.Host} { + hostPart := "" + if idx := strings.LastIndex(someHost, ":"); idx >= 0 { + strPort := someHost[idx+1:] + numPort, err := strconv.ParseUint(strPort, 10, 16) + if err == nil { + hostPort = (int)(numPort) + } + hostPart = someHost[:idx] + } else { + hostPart = someHost + } + if hostPart != "" { + ip := net.ParseIP(hostPart) + if ip != nil { + hostIP = ip.String() + } else { + hostName = hostPart + } + break + } else { + hostPort = 0 + } + } + if hostIP != "" { + attrs = append(attrs, NetHostIPKey.String(hostIP)) + } + if hostName != "" { + attrs = append(attrs, NetHostNameKey.String(hostName)) + } + if hostPort != 0 { + attrs = append(attrs, NetHostPortKey.Int(hostPort)) + } + + return attrs +} + +// EndUserAttributesFromHTTPRequest generates attributes of the +// enduser namespace as specified by the OpenTelemetry specification +// for a span. +func EndUserAttributesFromHTTPRequest(request *http.Request) []kv.KeyValue { + if username, _, ok := request.BasicAuth(); ok { + return []kv.KeyValue{EnduserIDKey.String(username)} + } + return nil +} + +// HTTPServerAttributesFromHTTPRequest generates attributes of the +// http namespace as specified by the OpenTelemetry specification for +// a span on the server side. Currently, only basic authentication is +// supported. +func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []kv.KeyValue { + attrs := []kv.KeyValue{ + HTTPMethodKey.String(request.Method), + HTTPTargetKey.String(request.RequestURI), + } + + if serverName != "" { + attrs = append(attrs, HTTPServerNameKey.String(serverName)) + } + if request.TLS != nil { + attrs = append(attrs, HTTPSchemeHTTPS) + } else { + attrs = append(attrs, HTTPSchemeHTTP) + } + if route != "" { + attrs = append(attrs, HTTPRouteKey.String(route)) + } + if request.Host != "" { + attrs = append(attrs, HTTPHostKey.String(request.Host)) + } + if ua := request.UserAgent(); ua != "" { + attrs = append(attrs, HTTPUserAgentKey.String(ua)) + } + if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { + attrs = append(attrs, HTTPClientIPKey.String(values[0])) + } + + flavor := "" + if request.ProtoMajor == 1 { + flavor = fmt.Sprintf("1.%d", request.ProtoMinor) + } else if request.ProtoMajor == 2 { + flavor = "2" + } + if flavor != "" { + attrs = append(attrs, HTTPFlavorKey.String(flavor)) + } + + return attrs +} + +// HTTPAttributesFromHTTPStatusCode generates attributes of the http +// namespace as specified by the OpenTelemetry specification for a +// span. +func HTTPAttributesFromHTTPStatusCode(code int) []kv.KeyValue { + attrs := []kv.KeyValue{ + HTTPStatusCodeKey.Int(code), + } + text := http.StatusText(code) + if text != "" { + attrs = append(attrs, HTTPStatusTextKey.String(text)) + } + return attrs +} + +type codeRange struct { + fromInclusive int + toInclusive int +} + +func (r codeRange) contains(code int) bool { + return r.fromInclusive <= code && code <= r.toInclusive +} + +var validRangesPerCategory = map[int][]codeRange{ + 1: { + {http.StatusContinue, http.StatusEarlyHints}, + }, + 2: { + {http.StatusOK, http.StatusAlreadyReported}, + {http.StatusIMUsed, http.StatusIMUsed}, + }, + 3: { + {http.StatusMultipleChoices, http.StatusUseProxy}, + {http.StatusTemporaryRedirect, http.StatusPermanentRedirect}, + }, + 4: { + {http.StatusBadRequest, http.StatusTeapot}, // yes, teapot is so useful… + {http.StatusMisdirectedRequest, http.StatusUpgradeRequired}, + {http.StatusPreconditionRequired, http.StatusTooManyRequests}, + {http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge}, + {http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons}, + }, + 5: { + {http.StatusInternalServerError, http.StatusLoopDetected}, + {http.StatusNotExtended, http.StatusNetworkAuthenticationRequired}, + }, +} + +// SpanStatusFromHTTPStatusCode generates a status code and a message +// as specified by the OpenTelemetry specification for a span. +func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) { + spanCode := func() codes.Code { + category := code / 100 + ranges, ok := validRangesPerCategory[category] + if !ok { + return codes.Unknown + } + ok = false + for _, crange := range ranges { + ok = crange.contains(code) + if ok { + break + } + } + if !ok { + return codes.Unknown + } + switch code { + case http.StatusUnauthorized: + return codes.Unauthenticated + case http.StatusForbidden: + return codes.PermissionDenied + case http.StatusNotFound: + return codes.NotFound + case http.StatusTooManyRequests: + return codes.ResourceExhausted + case http.StatusNotImplemented: + return codes.Unimplemented + case http.StatusServiceUnavailable: + return codes.Unavailable + case http.StatusGatewayTimeout: + return codes.DeadlineExceeded + } + if category > 0 && category < 4 { + return codes.OK + } + if category == 4 { + return codes.InvalidArgument + } + if category == 5 { + return codes.Internal + } + // this really should not happen, if we get there then + // it means that the code got out of sync with + // validRangesPerCategory map + return codes.Unknown + }() + if spanCode == codes.Unknown { + return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code) + } + return spanCode, fmt.Sprintf("HTTP status code: %d", code) +} diff --git a/api/standard/http_test.go b/api/standard/http_test.go new file mode 100644 index 000000000..21fcd9efc --- /dev/null +++ b/api/standard/http_test.go @@ -0,0 +1,777 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package standard + +import ( + "crypto/tls" + "net/http" + "net/url" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "google.golang.org/grpc/codes" + + otelkv "go.opentelemetry.io/otel/api/kv" +) + +type tlsOption int + +const ( + noTLS tlsOption = iota + withTLS +) + +func TestNetAttributesFromHTTPRequest(t *testing.T) { + type testcase struct { + name string + + network string + + method string + requestURI string + proto string + remoteAddr string + host string + url *url.URL + header http.Header + + expected []otelkv.KeyValue + } + testcases := []testcase{ + { + name: "stripped, tcp", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + }, + }, + { + name: "stripped, udp", + network: "udp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.UDP"), + }, + }, + { + name: "stripped, ip", + network: "ip", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP"), + }, + }, + { + name: "stripped, unix", + network: "unix", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "Unix"), + }, + }, + { + name: "stripped, other", + network: "nih", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "other"), + }, + }, + { + name: "with remote ip and port", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + }, + }, + { + name: "with remote name and port", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "example.com:56", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.name", "example.com"), + otelkv.Int("net.peer.port", 56), + }, + }, + { + name: "with remote ip only", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + }, + }, + { + name: "with remote name only", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "example.com", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.name", "example.com"), + }, + }, + { + name: "with remote port only", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: ":56", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + }, + }, + { + name: "with host name only", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: "example.com", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + otelkv.String("net.host.name", "example.com"), + }, + }, + { + name: "with host ip only", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: "4.3.2.1", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + otelkv.String("net.host.ip", "4.3.2.1"), + }, + }, + { + name: "with host name and port", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: "example.com:78", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + otelkv.String("net.host.name", "example.com"), + otelkv.Int("net.host.port", 78), + }, + }, + { + name: "with host ip and port", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: "4.3.2.1:78", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + otelkv.String("net.host.ip", "4.3.2.1"), + otelkv.Int("net.host.port", 78), + }, + }, + { + name: "with host name and bogus port", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: "example.com:qwerty", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + otelkv.String("net.host.name", "example.com"), + }, + }, + { + name: "with host ip and bogus port", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: "4.3.2.1:qwerty", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + otelkv.String("net.host.ip", "4.3.2.1"), + }, + }, + { + name: "with empty host and port", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: ":80", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + }, + }, + { + name: "with host ip and port in headers", + network: "tcp", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: http.Header{ + "Host": []string{"4.3.2.1:78"}, + }, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + otelkv.String("net.host.ip", "4.3.2.1"), + otelkv.Int("net.host.port", 78), + }, + }, + { + name: "with host ip and port in url", + network: "tcp", + method: "GET", + requestURI: "http://4.3.2.1:78/user/123", + proto: "HTTP/1.0", + remoteAddr: "1.2.3.4:56", + host: "", + url: &url.URL{ + Host: "4.3.2.1:78", + Path: "/user/123", + }, + header: nil, + expected: []otelkv.KeyValue{ + otelkv.String("net.transport", "IP.TCP"), + otelkv.String("net.peer.ip", "1.2.3.4"), + otelkv.Int("net.peer.port", 56), + otelkv.String("net.host.ip", "4.3.2.1"), + otelkv.Int("net.host.port", 78), + }, + }, + } + for idx, tc := range testcases { + r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, noTLS) + got := NetAttributesFromHTTPRequest(tc.network, r) + assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name) + } +} + +func TestEndUserAttributesFromHTTPRequest(t *testing.T) { + r := testRequest("GET", "/user/123", "HTTP/1.1", "", "", nil, http.Header{}, withTLS) + var expected []otelkv.KeyValue + got := EndUserAttributesFromHTTPRequest(r) + assert.ElementsMatch(t, expected, got) + r.SetBasicAuth("admin", "password") + expected = []otelkv.KeyValue{otelkv.String("enduser.id", "admin")} + got = EndUserAttributesFromHTTPRequest(r) + assert.ElementsMatch(t, expected, got) +} + +func TestHTTPServerAttributesFromHTTPRequest(t *testing.T) { + type testcase struct { + name string + + serverName string + route string + + method string + requestURI string + proto string + remoteAddr string + host string + url *url.URL + header http.Header + tls tlsOption + + expected []otelkv.KeyValue + } + testcases := []testcase{ + { + name: "stripped", + serverName: "", + route: "", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + tls: noTLS, + expected: []otelkv.KeyValue{ + otelkv.String("http.method", "GET"), + otelkv.String("http.target", "/user/123"), + otelkv.String("http.scheme", "http"), + otelkv.String("http.flavor", "1.0"), + }, + }, + { + name: "with server name", + serverName: "my-server-name", + route: "", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + tls: noTLS, + expected: []otelkv.KeyValue{ + otelkv.String("http.method", "GET"), + otelkv.String("http.target", "/user/123"), + otelkv.String("http.scheme", "http"), + otelkv.String("http.flavor", "1.0"), + otelkv.String("http.server_name", "my-server-name"), + }, + }, + { + name: "with tls", + serverName: "my-server-name", + route: "", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + tls: withTLS, + expected: []otelkv.KeyValue{ + otelkv.String("http.method", "GET"), + otelkv.String("http.target", "/user/123"), + otelkv.String("http.scheme", "https"), + otelkv.String("http.flavor", "1.0"), + otelkv.String("http.server_name", "my-server-name"), + }, + }, + { + name: "with route", + serverName: "my-server-name", + route: "/user/:id", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + tls: withTLS, + expected: []otelkv.KeyValue{ + otelkv.String("http.method", "GET"), + otelkv.String("http.target", "/user/123"), + otelkv.String("http.scheme", "https"), + otelkv.String("http.flavor", "1.0"), + otelkv.String("http.server_name", "my-server-name"), + otelkv.String("http.route", "/user/:id"), + }, + }, + { + name: "with host", + serverName: "my-server-name", + route: "/user/:id", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "example.com", + url: &url.URL{ + Path: "/user/123", + }, + header: nil, + tls: withTLS, + expected: []otelkv.KeyValue{ + otelkv.String("http.method", "GET"), + otelkv.String("http.target", "/user/123"), + otelkv.String("http.scheme", "https"), + otelkv.String("http.flavor", "1.0"), + otelkv.String("http.server_name", "my-server-name"), + otelkv.String("http.route", "/user/:id"), + otelkv.String("http.host", "example.com"), + }, + }, + { + name: "with user agent", + serverName: "my-server-name", + route: "/user/:id", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "example.com", + url: &url.URL{ + Path: "/user/123", + }, + header: http.Header{ + "User-Agent": []string{"foodownloader"}, + }, + tls: withTLS, + expected: []otelkv.KeyValue{ + otelkv.String("http.method", "GET"), + otelkv.String("http.target", "/user/123"), + otelkv.String("http.scheme", "https"), + otelkv.String("http.flavor", "1.0"), + otelkv.String("http.server_name", "my-server-name"), + otelkv.String("http.route", "/user/:id"), + otelkv.String("http.host", "example.com"), + otelkv.String("http.user_agent", "foodownloader"), + }, + }, + { + name: "with proxy info", + serverName: "my-server-name", + route: "/user/:id", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.0", + remoteAddr: "", + host: "example.com", + url: &url.URL{ + Path: "/user/123", + }, + header: http.Header{ + "User-Agent": []string{"foodownloader"}, + "X-Forwarded-For": []string{"1.2.3.4"}, + }, + tls: withTLS, + expected: []otelkv.KeyValue{ + otelkv.String("http.method", "GET"), + otelkv.String("http.target", "/user/123"), + otelkv.String("http.scheme", "https"), + otelkv.String("http.flavor", "1.0"), + otelkv.String("http.server_name", "my-server-name"), + otelkv.String("http.route", "/user/:id"), + otelkv.String("http.host", "example.com"), + otelkv.String("http.user_agent", "foodownloader"), + otelkv.String("http.client_ip", "1.2.3.4"), + }, + }, + { + name: "with http 1.1", + serverName: "my-server-name", + route: "/user/:id", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/1.1", + remoteAddr: "", + host: "example.com", + url: &url.URL{ + Path: "/user/123", + }, + header: http.Header{ + "User-Agent": []string{"foodownloader"}, + "X-Forwarded-For": []string{"1.2.3.4"}, + }, + tls: withTLS, + expected: []otelkv.KeyValue{ + otelkv.String("http.method", "GET"), + otelkv.String("http.target", "/user/123"), + otelkv.String("http.scheme", "https"), + otelkv.String("http.flavor", "1.1"), + otelkv.String("http.server_name", "my-server-name"), + otelkv.String("http.route", "/user/:id"), + otelkv.String("http.host", "example.com"), + otelkv.String("http.user_agent", "foodownloader"), + otelkv.String("http.client_ip", "1.2.3.4"), + }, + }, + { + name: "with http 2", + serverName: "my-server-name", + route: "/user/:id", + method: "GET", + requestURI: "/user/123", + proto: "HTTP/2.0", + remoteAddr: "", + host: "example.com", + url: &url.URL{ + Path: "/user/123", + }, + header: http.Header{ + "User-Agent": []string{"foodownloader"}, + "X-Forwarded-For": []string{"1.2.3.4"}, + }, + tls: withTLS, + expected: []otelkv.KeyValue{ + otelkv.String("http.method", "GET"), + otelkv.String("http.target", "/user/123"), + otelkv.String("http.scheme", "https"), + otelkv.String("http.flavor", "2"), + otelkv.String("http.server_name", "my-server-name"), + otelkv.String("http.route", "/user/:id"), + otelkv.String("http.host", "example.com"), + otelkv.String("http.user_agent", "foodownloader"), + otelkv.String("http.client_ip", "1.2.3.4"), + }, + }, + } + for idx, tc := range testcases { + r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls) + got := HTTPServerAttributesFromHTTPRequest(tc.serverName, tc.route, r) + assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name) + } +} + +func TestHTTPAttributesFromHTTPStatusCode(t *testing.T) { + expected := []otelkv.KeyValue{ + otelkv.Int("http.status_code", 404), + otelkv.String("http.status_text", "Not Found"), + } + got := HTTPAttributesFromHTTPStatusCode(http.StatusNotFound) + assertElementsMatch(t, expected, got, "with valid HTTP status code") + assert.ElementsMatch(t, expected, got) + expected = []otelkv.KeyValue{ + otelkv.Int("http.status_code", 499), + } + got = HTTPAttributesFromHTTPStatusCode(499) + assertElementsMatch(t, expected, got, "with invalid HTTP status code") +} + +func TestSpanStatusFromHTTPStatusCode(t *testing.T) { + for code := 0; code < 1000; code++ { + expected := getExpectedGRPCCodeForHTTPCode(code) + got, _ := SpanStatusFromHTTPStatusCode(code) + assert.Equalf(t, expected, got, "%s vs %s", expected, got) + } +} + +func getExpectedGRPCCodeForHTTPCode(code int) codes.Code { + if http.StatusText(code) == "" { + return codes.Unknown + } + switch code { + case http.StatusUnauthorized: + return codes.Unauthenticated + case http.StatusForbidden: + return codes.PermissionDenied + case http.StatusNotFound: + return codes.NotFound + case http.StatusTooManyRequests: + return codes.ResourceExhausted + case http.StatusNotImplemented: + return codes.Unimplemented + case http.StatusServiceUnavailable: + return codes.Unavailable + case http.StatusGatewayTimeout: + return codes.DeadlineExceeded + } + category := code / 100 + if category < 4 { + return codes.OK + } + if category < 5 { + return codes.InvalidArgument + } + return codes.Internal +} + +func assertElementsMatch(t *testing.T, expected, got []otelkv.KeyValue, format string, args ...interface{}) { + if !assert.ElementsMatchf(t, expected, got, format, args...) { + t.Log("expected:", kvStr(expected)) + t.Log("got:", kvStr(got)) + } +} + +func testRequest(method, requestURI, proto, remoteAddr, host string, u *url.URL, header http.Header, tlsopt tlsOption) *http.Request { + major, minor := protoToInts(proto) + var tlsConn *tls.ConnectionState + switch tlsopt { + case noTLS: + case withTLS: + tlsConn = &tls.ConnectionState{} + } + return &http.Request{ + Method: method, + URL: u, + Proto: proto, + ProtoMajor: major, + ProtoMinor: minor, + Header: header, + Host: host, + RemoteAddr: remoteAddr, + RequestURI: requestURI, + TLS: tlsConn, + } +} + +func protoToInts(proto string) (int, int) { + switch proto { + case "HTTP/1.0": + return 1, 0 + case "HTTP/1.1": + return 1, 1 + case "HTTP/2.0": + return 2, 0 + } + // invalid proto + return 13, 42 +} + +func kvStr(kvs []otelkv.KeyValue) string { + sb := strings.Builder{} + sb.WriteRune('[') + for idx, kv := range kvs { + if idx > 0 { + sb.WriteString(", ") + } + sb.WriteString((string)(kv.Key)) + sb.WriteString(": ") + sb.WriteString(kv.Value.Emit()) + } + sb.WriteRune(']') + return sb.String() +} From 24741dd9f0dfc61cfc6c6506d20ef67125e3688e Mon Sep 17 00:00:00 2001 From: Anthony J Mirabella Date: Tue, 26 May 2020 21:18:38 -0400 Subject: [PATCH 097/108] Use HTTP attribute functions from standard package --- plugin/othttp/handler.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/plugin/othttp/handler.go b/plugin/othttp/handler.go index c75c8d1fb..3e63b3933 100644 --- a/plugin/othttp/handler.go +++ b/plugin/othttp/handler.go @@ -21,6 +21,7 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/propagation" + "go.opentelemetry.io/otel/api/standard" "go.opentelemetry.io/otel/api/trace" ) @@ -88,7 +89,11 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } - opts := append([]trace.StartOption{}, h.spanStartOptions...) // start with the configured options + opts := append([]trace.StartOption{ + trace.WithAttributes(standard.NetAttributesFromHTTPRequest("tcp", r)...), + trace.WithAttributes(standard.EndUserAttributesFromHTTPRequest(r)...), + trace.WithAttributes(standard.HTTPServerAttributesFromHTTPRequest(h.operation, "", r)...), + }, h.spanStartOptions...) // start with the configured options ctx := propagation.ExtractHTTP(r.Context(), h.propagators, r.Header) ctx, span := h.tracer.Start(ctx, h.spanNameFormatter(h.operation, r), opts...) @@ -112,16 +117,14 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { rww := &respWriterWrapper{ResponseWriter: w, record: writeRecordFunc, ctx: ctx, props: h.propagators} - setBasicAttributes(span, r) - span.SetAttributes(RemoteAddrKey.String(r.RemoteAddr)) - h.handler.ServeHTTP(rww, r.WithContext(ctx)) - setAfterServeAttributes(span, bw.read, rww.written, int64(rww.statusCode), bw.err, rww.err) + setAfterServeAttributes(span, bw.read, rww.written, rww.statusCode, bw.err, rww.err) } -func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rerr, werr error) { - kv := make([]kv.KeyValue, 0, 5) +func setAfterServeAttributes(span trace.Span, read, wrote int64, statusCode int, rerr, werr error) { + kv := []kv.KeyValue{} + // TODO: Consider adding an event after each read and write, possibly as an // option (defaulting to off), so as to not create needlessly verbose spans. if read > 0 { @@ -134,7 +137,7 @@ func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rer kv = append(kv, WroteBytesKey.Int64(wrote)) } if statusCode > 0 { - kv = append(kv, StatusCodeKey.Int64(statusCode)) + kv = append(kv, standard.HTTPAttributesFromHTTPStatusCode(statusCode)...) } if werr != nil && werr != io.EOF { kv = append(kv, WriteErrorKey.String(werr.Error())) @@ -147,7 +150,7 @@ func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rer func WithRouteTag(route string, h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { span := trace.SpanFromContext(r.Context()) - span.SetAttributes(RouteKey.String(route)) + span.SetAttributes(standard.HTTPRouteKey.String(route)) h.ServeHTTP(w, r) }) } From a6f6d402a29b14eb3f0b664ed961277ae98edeba Mon Sep 17 00:00:00 2001 From: Anthony J Mirabella Date: Tue, 26 May 2020 21:53:16 -0400 Subject: [PATCH 098/108] Use HTTP attribute functions from standard package in othttp.Transport --- api/standard/http.go | 58 +++++++++++++++++++++++++++----------- plugin/othttp/common.go | 22 --------------- plugin/othttp/transport.go | 6 ++-- 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/api/standard/http.go b/api/standard/http.go index 3adbfb87d..11ee0f3d3 100644 --- a/api/standard/http.go +++ b/api/standard/http.go @@ -129,36 +129,39 @@ func EndUserAttributesFromHTTPRequest(request *http.Request) []kv.KeyValue { return nil } -// HTTPServerAttributesFromHTTPRequest generates attributes of the +// HTTPClientAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for -// a span on the server side. Currently, only basic authentication is -// supported. -func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []kv.KeyValue { - attrs := []kv.KeyValue{ - HTTPMethodKey.String(request.Method), - HTTPTargetKey.String(request.RequestURI), +// a span on the client side. +func HTTPClientAttributesFromHTTPRequest(request *http.Request) []kv.KeyValue { + attrs := []kv.KeyValue{} + + if request.Method != "" { + attrs = append(attrs, HTTPMethodKey.String(request.Method)) + } else { + attrs = append(attrs, HTTPMethodKey.String(http.MethodGet)) } - if serverName != "" { - attrs = append(attrs, HTTPServerNameKey.String(serverName)) - } + attrs = append(attrs, HTTPUrlKey.String(request.URL.String())) + + return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) +} + +func httpCommonAttributesFromHTTPRequest(request *http.Request) []kv.KeyValue { + attrs := []kv.KeyValue{} + if request.TLS != nil { attrs = append(attrs, HTTPSchemeHTTPS) } else { attrs = append(attrs, HTTPSchemeHTTP) } - if route != "" { - attrs = append(attrs, HTTPRouteKey.String(route)) - } + if request.Host != "" { attrs = append(attrs, HTTPHostKey.String(request.Host)) } + if ua := request.UserAgent(); ua != "" { attrs = append(attrs, HTTPUserAgentKey.String(ua)) } - if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { - attrs = append(attrs, HTTPClientIPKey.String(values[0])) - } flavor := "" if request.ProtoMajor == 1 { @@ -173,6 +176,29 @@ func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http return attrs } +// HTTPServerAttributesFromHTTPRequest generates attributes of the +// http namespace as specified by the OpenTelemetry specification for +// a span on the server side. Currently, only basic authentication is +// supported. +func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []kv.KeyValue { + attrs := []kv.KeyValue{ + HTTPMethodKey.String(request.Method), + HTTPTargetKey.String(request.RequestURI), + } + + if serverName != "" { + attrs = append(attrs, HTTPServerNameKey.String(serverName)) + } + if route != "" { + attrs = append(attrs, HTTPRouteKey.String(route)) + } + if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { + attrs = append(attrs, HTTPClientIPKey.String(values[0])) + } + + return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) +} + // HTTPAttributesFromHTTPStatusCode generates attributes of the http // namespace as specified by the OpenTelemetry specification for a // span. diff --git a/plugin/othttp/common.go b/plugin/othttp/common.go index ff48ac854..365ccadf3 100644 --- a/plugin/othttp/common.go +++ b/plugin/othttp/common.go @@ -18,20 +18,10 @@ import ( "net/http" "go.opentelemetry.io/otel/api/kv" - - "go.opentelemetry.io/otel/api/trace" ) // Attribute keys that can be added to a span. const ( - HostKey = kv.Key("http.host") // the HTTP host (http.Request.Host) - MethodKey = kv.Key("http.method") // the HTTP method (http.Request.Method) - PathKey = kv.Key("http.path") // the HTTP path (http.Request.URL.Path) - URLKey = kv.Key("http.url") // the HTTP URL (http.Request.URL.String()) - UserAgentKey = kv.Key("http.user_agent") // the HTTP user agent (http.Request.UserAgent()) - RouteKey = kv.Key("http.route") // the HTTP route (ex: /users/:id) - RemoteAddrKey = kv.Key("http.remote_addr") // the network address of the client that sent the HTTP request (http.Request.RemoteAddr) - StatusCodeKey = kv.Key("http.status_code") // if set, the HTTP status ReadBytesKey = kv.Key("http.read_bytes") // if anything was read from the request body, the total number of bytes read ReadErrorKey = kv.Key("http.read_error") // If an error occurred while reading a request, the string of the error (io.EOF is not recorded) WroteBytesKey = kv.Key("http.wrote_bytes") // if anything was written to the response writer, the total number of bytes written @@ -41,15 +31,3 @@ const ( // Filter is a predicate used to determine whether a given http.request should // be traced. A Filter must return true if the request should be traced. type Filter func(*http.Request) bool - -// Setup basic span attributes before so that they -// are available to be mutated if needed. -func setBasicAttributes(span trace.Span, r *http.Request) { - span.SetAttributes( - HostKey.String(r.Host), - MethodKey.String(r.Method), - PathKey.String(r.URL.Path), - URLKey.String(r.URL.String()), - UserAgentKey.String(r.UserAgent()), - ) -} diff --git a/plugin/othttp/transport.go b/plugin/othttp/transport.go index 52fc2d5a7..19fb93ddc 100644 --- a/plugin/othttp/transport.go +++ b/plugin/othttp/transport.go @@ -21,6 +21,7 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/propagation" + "go.opentelemetry.io/otel/api/standard" "go.opentelemetry.io/otel/api/trace" "google.golang.org/grpc/codes" @@ -88,7 +89,7 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { ctx, span := t.tracer.Start(r.Context(), t.spanNameFormatter("", r), opts...) r = r.WithContext(ctx) - setBasicAttributes(span, r) + span.SetAttributes(standard.HTTPClientAttributesFromHTTPRequest(r)...) propagation.InjectHTTP(ctx, t.propagators, r.Header) res, err := t.rt.RoundTrip(r) @@ -98,7 +99,8 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { return res, err } - span.SetAttributes(StatusCodeKey.Int(res.StatusCode)) + span.SetAttributes(standard.HTTPAttributesFromHTTPStatusCode(res.StatusCode)...) + span.SetStatus(standard.SpanStatusFromHTTPStatusCode(res.StatusCode)) res.Body = &wrappedBody{ctx: ctx, span: span, body: res.Body} return res, err From 31096d4ebc42f58b857b1c7c6000947f446810a7 Mon Sep 17 00:00:00 2001 From: Anthony J Mirabella Date: Tue, 26 May 2020 21:59:30 -0400 Subject: [PATCH 099/108] Use standard HTTP attributes in httptrace.Extract() --- plugin/httptrace/httptrace.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugin/httptrace/httptrace.go b/plugin/httptrace/httptrace.go index 87746574d..af7da000a 100644 --- a/plugin/httptrace/httptrace.go +++ b/plugin/httptrace/httptrace.go @@ -22,6 +22,7 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/propagation" + "go.opentelemetry.io/otel/api/standard" "go.opentelemetry.io/otel/api/trace" ) @@ -34,10 +35,10 @@ var ( func Extract(ctx context.Context, req *http.Request) ([]kv.KeyValue, []kv.KeyValue, trace.SpanContext) { ctx = propagation.ExtractHTTP(ctx, global.Propagators(), req.Header) - attrs := []kv.KeyValue{ - URLKey.String(req.URL.String()), - // Etc. - } + attrs := append( + standard.HTTPServerAttributesFromHTTPRequest("", "", req), + standard.NetAttributesFromHTTPRequest("tcp", req)..., + ) var correlationCtxKVs []kv.KeyValue correlation.MapFromContext(ctx).Foreach(func(kv kv.KeyValue) bool { From f5364569c2124090b08d426e18d4593278ccd015 Mon Sep 17 00:00:00 2001 From: Sean Slattery Date: Tue, 26 May 2020 23:06:16 -0700 Subject: [PATCH 100/108] minor typo fix --- api/metric/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/metric/doc.go b/api/metric/doc.go index 8249b6f6d..cda2159d0 100644 --- a/api/metric/doc.go +++ b/api/metric/doc.go @@ -33,7 +33,7 @@ // and the asynchronous instruments are: // // SumObserver: additive, monotonic -// UpDownSumOnserver: additive +// UpDownSumObserver: additive // ValueObserver: non-additive // // All instruments are provided with support for either float64 or From 6929c15957b4ba005e54d8c1c47d83dcf29bdc2a Mon Sep 17 00:00:00 2001 From: Paul Thomson Date: Fri, 29 May 2020 06:24:22 +1000 Subject: [PATCH 101/108] Add application/json header --- exporters/trace/zipkin/zipkin.go | 1 + 1 file changed, 1 insertion(+) diff --git a/exporters/trace/zipkin/zipkin.go b/exporters/trace/zipkin/zipkin.go index 525a33bdf..576f3357d 100644 --- a/exporters/trace/zipkin/zipkin.go +++ b/exporters/trace/zipkin/zipkin.go @@ -105,6 +105,7 @@ func (e *Exporter) ExportSpans(ctx context.Context, batch []*export.SpanData) { e.logf("failed to create request to %s: %v", e.url, err) return } + req.Header.Set("Content-Type", "application/json") resp, err := e.client.Do(req) if err != nil { e.logf("request to %s failed: %v", e.url, err) From b3f17329c83d2677aa4dd913e909641692dec564 Mon Sep 17 00:00:00 2001 From: Paulo Janotti Date: Thu, 28 May 2020 16:08:04 -0700 Subject: [PATCH 102/108] Remove SpanID from sampling parameters Update code per latest spec, see: https://github.com/open-telemetry/opentelemetry-specification/pull/621 --- api/trace/always_off_sampler.go | 1 - api/trace/always_off_sampler_test.go | 2 +- api/trace/always_on_sampler.go | 1 - api/trace/always_on_sampler_test.go | 2 +- api/trace/sampler.go | 1 - sdk/trace/sampling.go | 1 - sdk/trace/span.go | 1 - 7 files changed, 2 insertions(+), 7 deletions(-) diff --git a/api/trace/always_off_sampler.go b/api/trace/always_off_sampler.go index f0679eb62..b81648398 100644 --- a/api/trace/always_off_sampler.go +++ b/api/trace/always_off_sampler.go @@ -33,7 +33,6 @@ func (ns alwaysOffSampler) ShouldSample( _ SpanContext, _ bool, _ ID, - _ SpanID, _ string, _ SpanKind, _ []kv.KeyValue, diff --git a/api/trace/always_off_sampler_test.go b/api/trace/always_off_sampler_test.go index 3ead33435..3ae70f341 100644 --- a/api/trace/always_off_sampler_test.go +++ b/api/trace/always_off_sampler_test.go @@ -24,7 +24,7 @@ import ( func TestNeverSamperShouldSample(t *testing.T) { gotD := AlwaysOffSampler().ShouldSample( - SpanContext{}, false, ID{}, SpanID{}, "span", SpanKindClient, []kv.KeyValue{}, []Link{}) + SpanContext{}, false, ID{}, "span", SpanKindClient, []kv.KeyValue{}, []Link{}) wantD := Decision{Sampled: false} if diff := cmp.Diff(wantD, gotD); diff != "" { t.Errorf("Decision: +got, -want%v", diff) diff --git a/api/trace/always_on_sampler.go b/api/trace/always_on_sampler.go index da8d5c913..99048bacb 100644 --- a/api/trace/always_on_sampler.go +++ b/api/trace/always_on_sampler.go @@ -33,7 +33,6 @@ func (as alwaysOnSampler) ShouldSample( _ SpanContext, _ bool, _ ID, - _ SpanID, _ string, _ SpanKind, _ []kv.KeyValue, diff --git a/api/trace/always_on_sampler_test.go b/api/trace/always_on_sampler_test.go index 033433194..ac0f85a2a 100644 --- a/api/trace/always_on_sampler_test.go +++ b/api/trace/always_on_sampler_test.go @@ -24,7 +24,7 @@ import ( func TestAlwaysOnSamplerShouldSample(t *testing.T) { gotD := AlwaysOnSampler().ShouldSample( - SpanContext{}, false, ID{}, SpanID{}, "span", SpanKindClient, []kv.KeyValue{}, []Link{}) + SpanContext{}, false, ID{}, "span", SpanKindClient, []kv.KeyValue{}, []Link{}) wantD := Decision{Sampled: true} if diff := cmp.Diff(wantD, gotD); diff != "" { t.Errorf("Decision: +got, -want%v", diff) diff --git a/api/trace/sampler.go b/api/trace/sampler.go index b90613c7a..3777b32fa 100644 --- a/api/trace/sampler.go +++ b/api/trace/sampler.go @@ -24,7 +24,6 @@ type Sampler interface { sc SpanContext, remote bool, traceID ID, - spanID SpanID, spanName string, spanKind SpanKind, attributes []kv.KeyValue, diff --git a/sdk/trace/sampling.go b/sdk/trace/sampling.go index 8cf8248df..25f0377c9 100644 --- a/sdk/trace/sampling.go +++ b/sdk/trace/sampling.go @@ -32,7 +32,6 @@ type Sampler interface { type SamplingParameters struct { ParentContext api.SpanContext TraceID api.ID - SpanID api.SpanID Name string HasRemoteParent bool Kind api.SpanKind diff --git a/sdk/trace/span.go b/sdk/trace/span.go index 50772115b..9d9ae3c81 100644 --- a/sdk/trace/span.go +++ b/sdk/trace/span.go @@ -394,7 +394,6 @@ func makeSamplingDecision(data samplingData) SamplingResult { sampled := sampler.ShouldSample(SamplingParameters{ ParentContext: data.parent, TraceID: spanContext.TraceID, - SpanID: spanContext.SpanID, Name: data.name, HasRemoteParent: data.remoteParent, Kind: data.kind, From 07d5f777490d0e1131f284b971ea914166e1becb Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Sat, 30 May 2020 14:53:32 -0700 Subject: [PATCH 103/108] Rename plugin directory to instrumentation (#779) * Rename plugin directory to instrumentation As per https://github.com/open-telemetry/opentelemetry-specification/pull/539 * Update package paths * Fix tracer names in othttp instrumentation --- example/grpc/client/main.go | 2 +- example/grpc/server/main.go | 2 +- example/http/client/client.go | 2 +- example/http/server/server.go | 2 +- {plugin => instrumentation}/grpctrace/grpctrace.go | 0 {plugin => instrumentation}/grpctrace/interceptor.go | 0 {plugin => instrumentation}/grpctrace/interceptor_test.go | 0 {plugin => instrumentation}/httptrace/api.go | 0 {plugin => instrumentation}/httptrace/clienttrace.go | 2 +- {plugin => instrumentation}/httptrace/clienttrace_test.go | 2 +- {plugin => instrumentation}/httptrace/httptrace.go | 0 {plugin => instrumentation}/othttp/common.go | 0 {plugin => instrumentation}/othttp/config.go | 0 {plugin => instrumentation}/othttp/config_test.go | 0 {plugin => instrumentation}/othttp/doc.go | 0 {plugin => instrumentation}/othttp/filters/filters.go | 2 +- {plugin => instrumentation}/othttp/filters/filters_test.go | 2 +- {plugin => instrumentation}/othttp/filters/header_go14.go | 2 +- {plugin => instrumentation}/othttp/filters/header_nongo14.go | 2 +- {plugin => instrumentation}/othttp/handler.go | 2 +- {plugin => instrumentation}/othttp/handler_example_test.go | 2 +- {plugin => instrumentation}/othttp/handler_test.go | 0 {plugin => instrumentation}/othttp/transport.go | 2 +- {plugin => instrumentation}/othttp/transport_example_test.go | 0 {plugin => instrumentation}/othttp/transport_test.go | 0 {plugin => instrumentation}/othttp/wrap.go | 0 26 files changed, 13 insertions(+), 13 deletions(-) rename {plugin => instrumentation}/grpctrace/grpctrace.go (100%) rename {plugin => instrumentation}/grpctrace/interceptor.go (100%) rename {plugin => instrumentation}/grpctrace/interceptor_test.go (100%) rename {plugin => instrumentation}/httptrace/api.go (100%) rename {plugin => instrumentation}/httptrace/clienttrace.go (98%) rename {plugin => instrumentation}/httptrace/clienttrace_test.go (99%) rename {plugin => instrumentation}/httptrace/httptrace.go (100%) rename {plugin => instrumentation}/othttp/common.go (100%) rename {plugin => instrumentation}/othttp/config.go (100%) rename {plugin => instrumentation}/othttp/config_test.go (100%) rename {plugin => instrumentation}/othttp/doc.go (100%) rename {plugin => instrumentation}/othttp/filters/filters.go (98%) rename {plugin => instrumentation}/othttp/filters/filters_test.go (99%) rename {plugin => instrumentation}/othttp/filters/header_go14.go (96%) rename {plugin => instrumentation}/othttp/filters/header_nongo14.go (96%) rename {plugin => instrumentation}/othttp/handler.go (98%) rename {plugin => instrumentation}/othttp/handler_example_test.go (98%) rename {plugin => instrumentation}/othttp/handler_test.go (100%) rename {plugin => instrumentation}/othttp/transport.go (97%) rename {plugin => instrumentation}/othttp/transport_example_test.go (100%) rename {plugin => instrumentation}/othttp/transport_test.go (100%) rename {plugin => instrumentation}/othttp/wrap.go (100%) diff --git a/example/grpc/client/main.go b/example/grpc/client/main.go index 6131c0bf9..12d07e60e 100644 --- a/example/grpc/client/main.go +++ b/example/grpc/client/main.go @@ -23,7 +23,7 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/example/grpc/api" "go.opentelemetry.io/otel/example/grpc/config" - "go.opentelemetry.io/otel/plugin/grpctrace" + "go.opentelemetry.io/otel/instrumentation/grpctrace" "google.golang.org/grpc" "google.golang.org/grpc/metadata" diff --git a/example/grpc/server/main.go b/example/grpc/server/main.go index d23da5937..04e8a00ef 100644 --- a/example/grpc/server/main.go +++ b/example/grpc/server/main.go @@ -25,7 +25,7 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/example/grpc/api" "go.opentelemetry.io/otel/example/grpc/config" - "go.opentelemetry.io/otel/plugin/grpctrace" + "go.opentelemetry.io/otel/instrumentation/grpctrace" "google.golang.org/grpc" ) diff --git a/example/http/client/client.go b/example/http/client/client.go index 9ba7b31be..d96b96f2d 100644 --- a/example/http/client/client.go +++ b/example/http/client/client.go @@ -29,7 +29,7 @@ import ( "go.opentelemetry.io/otel/api/correlation" "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/exporters/trace/stdout" - "go.opentelemetry.io/otel/plugin/httptrace" + "go.opentelemetry.io/otel/instrumentation/httptrace" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) diff --git a/example/http/server/server.go b/example/http/server/server.go index 612b006d2..58737996e 100644 --- a/example/http/server/server.go +++ b/example/http/server/server.go @@ -23,7 +23,7 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/trace" "go.opentelemetry.io/otel/exporters/trace/stdout" - "go.opentelemetry.io/otel/plugin/httptrace" + "go.opentelemetry.io/otel/instrumentation/httptrace" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) diff --git a/plugin/grpctrace/grpctrace.go b/instrumentation/grpctrace/grpctrace.go similarity index 100% rename from plugin/grpctrace/grpctrace.go rename to instrumentation/grpctrace/grpctrace.go diff --git a/plugin/grpctrace/interceptor.go b/instrumentation/grpctrace/interceptor.go similarity index 100% rename from plugin/grpctrace/interceptor.go rename to instrumentation/grpctrace/interceptor.go diff --git a/plugin/grpctrace/interceptor_test.go b/instrumentation/grpctrace/interceptor_test.go similarity index 100% rename from plugin/grpctrace/interceptor_test.go rename to instrumentation/grpctrace/interceptor_test.go diff --git a/plugin/httptrace/api.go b/instrumentation/httptrace/api.go similarity index 100% rename from plugin/httptrace/api.go rename to instrumentation/httptrace/api.go diff --git a/plugin/httptrace/clienttrace.go b/instrumentation/httptrace/clienttrace.go similarity index 98% rename from plugin/httptrace/clienttrace.go rename to instrumentation/httptrace/clienttrace.go index 66df26260..a91a94f16 100644 --- a/plugin/httptrace/clienttrace.go +++ b/instrumentation/httptrace/clienttrace.go @@ -67,7 +67,7 @@ func NewClientTrace(ctx context.Context) *httptrace.ClientTrace { activeHooks: make(map[string]context.Context), } - ct.tr = global.Tracer("go.opentelemetry.io/otel/plugin/httptrace") + ct.tr = global.Tracer("go.opentelemetry.io/otel/instrumentation/httptrace") return &httptrace.ClientTrace{ GetConn: ct.getConn, diff --git a/plugin/httptrace/clienttrace_test.go b/instrumentation/httptrace/clienttrace_test.go similarity index 99% rename from plugin/httptrace/clienttrace_test.go rename to instrumentation/httptrace/clienttrace_test.go index 072313be5..92e108399 100644 --- a/plugin/httptrace/clienttrace_test.go +++ b/instrumentation/httptrace/clienttrace_test.go @@ -25,7 +25,7 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/kv" - "go.opentelemetry.io/otel/plugin/httptrace" + "go.opentelemetry.io/otel/instrumentation/httptrace" export "go.opentelemetry.io/otel/sdk/export/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) diff --git a/plugin/httptrace/httptrace.go b/instrumentation/httptrace/httptrace.go similarity index 100% rename from plugin/httptrace/httptrace.go rename to instrumentation/httptrace/httptrace.go diff --git a/plugin/othttp/common.go b/instrumentation/othttp/common.go similarity index 100% rename from plugin/othttp/common.go rename to instrumentation/othttp/common.go diff --git a/plugin/othttp/config.go b/instrumentation/othttp/config.go similarity index 100% rename from plugin/othttp/config.go rename to instrumentation/othttp/config.go diff --git a/plugin/othttp/config_test.go b/instrumentation/othttp/config_test.go similarity index 100% rename from plugin/othttp/config_test.go rename to instrumentation/othttp/config_test.go diff --git a/plugin/othttp/doc.go b/instrumentation/othttp/doc.go similarity index 100% rename from plugin/othttp/doc.go rename to instrumentation/othttp/doc.go diff --git a/plugin/othttp/filters/filters.go b/instrumentation/othttp/filters/filters.go similarity index 98% rename from plugin/othttp/filters/filters.go rename to instrumentation/othttp/filters/filters.go index de8b888de..e5192ef7c 100644 --- a/plugin/othttp/filters/filters.go +++ b/instrumentation/othttp/filters/filters.go @@ -20,7 +20,7 @@ import ( "net/http" "strings" - "go.opentelemetry.io/otel/plugin/othttp" + "go.opentelemetry.io/otel/instrumentation/othttp" ) // Any takes a list of Filters and returns a Filter that diff --git a/plugin/othttp/filters/filters_test.go b/instrumentation/othttp/filters/filters_test.go similarity index 99% rename from plugin/othttp/filters/filters_test.go rename to instrumentation/othttp/filters/filters_test.go index b880df024..7e184363d 100644 --- a/plugin/othttp/filters/filters_test.go +++ b/instrumentation/othttp/filters/filters_test.go @@ -19,7 +19,7 @@ import ( "net/url" "testing" - "go.opentelemetry.io/otel/plugin/othttp" + "go.opentelemetry.io/otel/instrumentation/othttp" ) type scenario struct { diff --git a/plugin/othttp/filters/header_go14.go b/instrumentation/othttp/filters/header_go14.go similarity index 96% rename from plugin/othttp/filters/header_go14.go rename to instrumentation/othttp/filters/header_go14.go index 9d0a61cb2..e4091f165 100644 --- a/plugin/othttp/filters/header_go14.go +++ b/instrumentation/othttp/filters/header_go14.go @@ -20,7 +20,7 @@ import ( "net/http" "strings" - "go.opentelemetry.io/otel/plugin/othttp" + "go.opentelemetry.io/otel/instrumentation/othttp" ) // Header returns a Filter that returns true if the request diff --git a/plugin/othttp/filters/header_nongo14.go b/instrumentation/othttp/filters/header_nongo14.go similarity index 96% rename from plugin/othttp/filters/header_nongo14.go rename to instrumentation/othttp/filters/header_nongo14.go index a36d75909..9e5124439 100644 --- a/plugin/othttp/filters/header_nongo14.go +++ b/instrumentation/othttp/filters/header_nongo14.go @@ -21,7 +21,7 @@ import ( "net/textproto" "strings" - "go.opentelemetry.io/otel/plugin/othttp" + "go.opentelemetry.io/otel/instrumentation/othttp" ) // Header returns a Filter that returns true if the request diff --git a/plugin/othttp/handler.go b/instrumentation/othttp/handler.go similarity index 98% rename from plugin/othttp/handler.go rename to instrumentation/othttp/handler.go index 3e63b3933..ea4ca36b5 100644 --- a/plugin/othttp/handler.go +++ b/instrumentation/othttp/handler.go @@ -57,7 +57,7 @@ func NewHandler(handler http.Handler, operation string, opts ...Option) http.Han } defaultOpts := []Option{ - WithTracer(global.Tracer("go.opentelemetry.io/plugin/othttp")), + WithTracer(global.Tracer("go.opentelemetry.io/otel/instrumentation/othttp")), WithPropagators(global.Propagators()), WithSpanOptions(trace.WithSpanKind(trace.SpanKindServer)), WithSpanNameFormatter(defaultHandlerFormatter), diff --git a/plugin/othttp/handler_example_test.go b/instrumentation/othttp/handler_example_test.go similarity index 98% rename from plugin/othttp/handler_example_test.go rename to instrumentation/othttp/handler_example_test.go index 96f374dbb..ff2533d0f 100644 --- a/plugin/othttp/handler_example_test.go +++ b/instrumentation/othttp/handler_example_test.go @@ -27,7 +27,7 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/trace" "go.opentelemetry.io/otel/exporters/trace/stdout" - "go.opentelemetry.io/otel/plugin/othttp" + "go.opentelemetry.io/otel/instrumentation/othttp" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) diff --git a/plugin/othttp/handler_test.go b/instrumentation/othttp/handler_test.go similarity index 100% rename from plugin/othttp/handler_test.go rename to instrumentation/othttp/handler_test.go diff --git a/plugin/othttp/transport.go b/instrumentation/othttp/transport.go similarity index 97% rename from plugin/othttp/transport.go rename to instrumentation/othttp/transport.go index 19fb93ddc..e61e3a42c 100644 --- a/plugin/othttp/transport.go +++ b/instrumentation/othttp/transport.go @@ -49,7 +49,7 @@ func NewTransport(base http.RoundTripper, opts ...Option) *Transport { } defaultOpts := []Option{ - WithTracer(global.Tracer("go.opentelemetry.io/plugin/othttp")), + WithTracer(global.Tracer("go.opentelemetry.io/otel/instrumentation/othttp")), WithPropagators(global.Propagators()), WithSpanOptions(trace.WithSpanKind(trace.SpanKindClient)), WithSpanNameFormatter(defaultTransportFormatter), diff --git a/plugin/othttp/transport_example_test.go b/instrumentation/othttp/transport_example_test.go similarity index 100% rename from plugin/othttp/transport_example_test.go rename to instrumentation/othttp/transport_example_test.go diff --git a/plugin/othttp/transport_test.go b/instrumentation/othttp/transport_test.go similarity index 100% rename from plugin/othttp/transport_test.go rename to instrumentation/othttp/transport_test.go diff --git a/plugin/othttp/wrap.go b/instrumentation/othttp/wrap.go similarity index 100% rename from plugin/othttp/wrap.go rename to instrumentation/othttp/wrap.go From 86a10268b8499b4d826f36c5790bd291d4f74073 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Sat, 30 May 2020 20:16:40 -0700 Subject: [PATCH 104/108] Minor cleanups in sdk/metric/aggregator (#781) * Typo * Swap order of ddsketch.New for consistency w/ histogram.New --- api/global/internal/benchmark_test.go | 4 ++-- exporters/metric/stdout/stdout_test.go | 2 +- sdk/metric/aggregator/ddsketch/ddsketch.go | 2 +- sdk/metric/aggregator/ddsketch/ddsketch_test.go | 6 +++--- sdk/metric/benchmark_test.go | 4 ++-- sdk/metric/integrator/simple/simple.go | 2 +- sdk/metric/selector/simple/simple.go | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/api/global/internal/benchmark_test.go b/api/global/internal/benchmark_test.go index 017afbd95..51d5ceae1 100644 --- a/api/global/internal/benchmark_test.go +++ b/api/global/internal/benchmark_test.go @@ -63,9 +63,9 @@ func (*benchFixture) AggregatorFor(descriptor *metric.Descriptor) export.Aggrega if strings.HasSuffix(descriptor.Name(), "minmaxsumcount") { return minmaxsumcount.New(descriptor) } else if strings.HasSuffix(descriptor.Name(), "ddsketch") { - return ddsketch.New(ddsketch.NewDefaultConfig(), descriptor) + return ddsketch.New(descriptor, ddsketch.NewDefaultConfig()) } else if strings.HasSuffix(descriptor.Name(), "array") { - return ddsketch.New(ddsketch.NewDefaultConfig(), descriptor) + return ddsketch.New(descriptor, ddsketch.NewDefaultConfig()) } } return nil diff --git a/exporters/metric/stdout/stdout_test.go b/exporters/metric/stdout/stdout_test.go index 1d5805ba2..b401ac881 100644 --- a/exporters/metric/stdout/stdout_test.go +++ b/exporters/metric/stdout/stdout_test.go @@ -240,7 +240,7 @@ func TestStdoutValueRecorderFormat(t *testing.T) { func TestStdoutNoData(t *testing.T) { desc := metric.NewDescriptor("test.name", metric.ValueRecorderKind, metric.Float64NumberKind) for name, tc := range map[string]export.Aggregator{ - "ddsketch": ddsketch.New(ddsketch.NewDefaultConfig(), &desc), + "ddsketch": ddsketch.New(&desc, ddsketch.NewDefaultConfig()), "minmaxsumcount": minmaxsumcount.New(&desc), } { tc := tc diff --git a/sdk/metric/aggregator/ddsketch/ddsketch.go b/sdk/metric/aggregator/ddsketch/ddsketch.go index 197d95e4d..7a2e23524 100644 --- a/sdk/metric/aggregator/ddsketch/ddsketch.go +++ b/sdk/metric/aggregator/ddsketch/ddsketch.go @@ -43,7 +43,7 @@ var _ aggregator.MinMaxSumCount = &Aggregator{} var _ aggregator.Distribution = &Aggregator{} // New returns a new DDSketch aggregator. -func New(cfg *Config, desc *metric.Descriptor) *Aggregator { +func New(desc *metric.Descriptor, cfg *Config) *Aggregator { return &Aggregator{ cfg: cfg, kind: desc.NumberKind(), diff --git a/sdk/metric/aggregator/ddsketch/ddsketch_test.go b/sdk/metric/aggregator/ddsketch/ddsketch_test.go index cc68359de..2b4a3b0c0 100644 --- a/sdk/metric/aggregator/ddsketch/ddsketch_test.go +++ b/sdk/metric/aggregator/ddsketch/ddsketch_test.go @@ -34,7 +34,7 @@ func (ut *updateTest) run(t *testing.T, profile test.Profile) { ctx := context.Background() descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) - agg := New(NewDefaultConfig(), descriptor) + agg := New(descriptor, NewDefaultConfig()) all := test.NewNumbers(profile.NumberKind) for i := 0; i < count; i++ { @@ -94,8 +94,8 @@ func (mt *mergeTest) run(t *testing.T, profile test.Profile) { ctx := context.Background() descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind) - agg1 := New(NewDefaultConfig(), descriptor) - agg2 := New(NewDefaultConfig(), descriptor) + agg1 := New(descriptor, NewDefaultConfig()) + agg2 := New(descriptor, NewDefaultConfig()) all := test.NewNumbers(profile.NumberKind) for i := 0; i < count; i++ { diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index d28649319..f7a00fd54 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -67,9 +67,9 @@ func (*benchFixture) AggregatorFor(descriptor *metric.Descriptor) export.Aggrega if strings.HasSuffix(descriptor.Name(), "minmaxsumcount") { return minmaxsumcount.New(descriptor) } else if strings.HasSuffix(descriptor.Name(), "ddsketch") { - return ddsketch.New(ddsketch.NewDefaultConfig(), descriptor) + return ddsketch.New(descriptor, ddsketch.NewDefaultConfig()) } else if strings.HasSuffix(descriptor.Name(), "array") { - return ddsketch.New(ddsketch.NewDefaultConfig(), descriptor) + return ddsketch.New(descriptor, ddsketch.NewDefaultConfig()) } } return nil diff --git a/sdk/metric/integrator/simple/simple.go b/sdk/metric/integrator/simple/simple.go index 28c35ac50..2165f40ef 100644 --- a/sdk/metric/integrator/simple/simple.go +++ b/sdk/metric/integrator/simple/simple.go @@ -46,7 +46,7 @@ type ( } batch struct { - // RWMutex implements locking for the `CheckpoingSet` interface. + // RWMutex implements locking for the `CheckpointSet` interface. sync.RWMutex values map[batchKey]batchValue } diff --git a/sdk/metric/selector/simple/simple.go b/sdk/metric/selector/simple/simple.go index a5956e4f3..1cd783e0e 100644 --- a/sdk/metric/selector/simple/simple.go +++ b/sdk/metric/selector/simple/simple.go @@ -91,7 +91,7 @@ func (selectorInexpensive) AggregatorFor(descriptor *metric.Descriptor) export.A func (s selectorSketch) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator { switch descriptor.MetricKind() { case metric.ValueObserverKind, metric.ValueRecorderKind: - return ddsketch.New(s.config, descriptor) + return ddsketch.New(descriptor, s.config) default: return sum.New() } From 1f9a66f1f952f33da491a2a2756518c3e7fd946c Mon Sep 17 00:00:00 2001 From: tensorchen Date: Mon, 1 Jun 2020 00:31:30 +0800 Subject: [PATCH 105/108] using api/standard keys instead of custom keys remove httptrace dup keys format codestyle --- instrumentation/grpctrace/interceptor.go | 42 +++----- instrumentation/grpctrace/interceptor_test.go | 102 +++++++++--------- instrumentation/httptrace/clienttrace.go | 6 +- instrumentation/httptrace/httptrace.go | 5 - 4 files changed, 73 insertions(+), 82 deletions(-) diff --git a/instrumentation/grpctrace/interceptor.go b/instrumentation/grpctrace/interceptor.go index 6c8b6315e..5bc420318 100644 --- a/instrumentation/grpctrace/interceptor.go +++ b/instrumentation/grpctrace/interceptor.go @@ -22,6 +22,8 @@ import ( "net" "regexp" + "go.opentelemetry.io/otel/api/standard" + "github.com/golang/protobuf/proto" "google.golang.org/grpc" "google.golang.org/grpc/metadata" @@ -33,17 +35,7 @@ import ( "go.opentelemetry.io/otel/api/trace" ) -var ( - rpcServiceKey = kv.Key("rpc.service") - netPeerIPKey = kv.Key("net.peer.ip") - netPeerPortKey = kv.Key("net.peer.port") - - messageTypeKey = kv.Key("message.type") - messageIDKey = kv.Key("message.id") - messageUncompressedSizeKey = kv.Key("message.uncompressed_size") -) - -type messageType string +type messageType kv.KeyValue // Event adds an event of the messageType to the span associated with the // passed context with id and size (if message is a proto message). @@ -51,21 +43,21 @@ func (m messageType) Event(ctx context.Context, id int, message interface{}) { span := trace.SpanFromContext(ctx) if p, ok := message.(proto.Message); ok { span.AddEvent(ctx, "message", - messageTypeKey.String(string(m)), - messageIDKey.Int(id), - messageUncompressedSizeKey.Int(proto.Size(p)), + kv.KeyValue(m), + standard.RPCMessageIDKey.Int(id), + standard.RPCMessageUncompressedSizeKey.Int(proto.Size(p)), ) } else { span.AddEvent(ctx, "message", - messageTypeKey.String(string(m)), - messageIDKey.Int(id), + kv.KeyValue(m), + standard.RPCMessageIDKey.Int(id), ) } } -const ( - messageSent messageType = "SENT" - messageReceived messageType = "RECEIVED" +var ( + messageSent = messageType(standard.RPCMessageTypeSent) + messageReceived = messageType(standard.RPCMessageTypeReceived) ) // UnaryClientInterceptor returns a grpc.UnaryClientInterceptor suitable @@ -93,7 +85,7 @@ func UnaryClientInterceptor(tracer trace.Tracer) grpc.UnaryClientInterceptor { ctx, method, trace.WithSpanKind(trace.SpanKindClient), trace.WithAttributes(peerInfoFromTarget(cc.Target())...), - trace.WithAttributes(rpcServiceKey.String(serviceFromFullMethod(method))), + trace.WithAttributes(standard.RPCServiceKey.String(serviceFromFullMethod(method))), ) defer span.End() @@ -271,7 +263,7 @@ func StreamClientInterceptor(tracer trace.Tracer) grpc.StreamClientInterceptor { ctx, method, trace.WithSpanKind(trace.SpanKindClient), trace.WithAttributes(peerInfoFromTarget(cc.Target())...), - trace.WithAttributes(rpcServiceKey.String(serviceFromFullMethod(method))), + trace.WithAttributes(standard.RPCServiceKey.String(serviceFromFullMethod(method))), ) Inject(ctx, &metadataCopy) @@ -325,7 +317,7 @@ func UnaryServerInterceptor(tracer trace.Tracer) grpc.UnaryServerInterceptor { info.FullMethod, trace.WithSpanKind(trace.SpanKindServer), trace.WithAttributes(peerInfoFromContext(ctx)...), - trace.WithAttributes(rpcServiceKey.String(serviceFromFullMethod(info.FullMethod))), + trace.WithAttributes(standard.RPCServiceKey.String(serviceFromFullMethod(info.FullMethod))), ) defer span.End() @@ -415,7 +407,7 @@ func StreamServerInterceptor(tracer trace.Tracer) grpc.StreamServerInterceptor { info.FullMethod, trace.WithSpanKind(trace.SpanKindServer), trace.WithAttributes(peerInfoFromContext(ctx)...), - trace.WithAttributes(rpcServiceKey.String(serviceFromFullMethod(info.FullMethod))), + trace.WithAttributes(standard.RPCServiceKey.String(serviceFromFullMethod(info.FullMethod))), ) defer span.End() @@ -442,8 +434,8 @@ func peerInfoFromTarget(target string) []kv.KeyValue { } return []kv.KeyValue{ - netPeerIPKey.String(host), - netPeerPortKey.String(port), + standard.NetPeerIPKey.String(host), + standard.NetPeerPortKey.String(port), } } diff --git a/instrumentation/grpctrace/interceptor_test.go b/instrumentation/grpctrace/interceptor_test.go index db12a3a30..0b57b053c 100644 --- a/instrumentation/grpctrace/interceptor_test.go +++ b/instrumentation/grpctrace/interceptor_test.go @@ -19,6 +19,8 @@ import ( "testing" "time" + "go.opentelemetry.io/otel/api/standard" + "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -93,100 +95,100 @@ func TestUnaryClientInterceptor(t *testing.T) { { name: "/github.com.serviceName/bar", expectedAttr: map[kv.Key]value.Value{ - rpcServiceKey: value.String("serviceName"), - netPeerIPKey: value.String("fake"), - netPeerPortKey: value.String("connection"), + standard.RPCServiceKey: value.String("serviceName"), + standard.NetPeerIPKey: value.String("fake"), + standard.NetPeerPortKey: value.String("connection"), }, eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: value.String("SENT"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), + standard.RPCMessageTypeKey: value.String("SENT"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: value.String("RECEIVED"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), + standard.RPCMessageTypeKey: value.String("RECEIVED"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, { name: "/serviceName/bar", expectedAttr: map[kv.Key]value.Value{ - rpcServiceKey: value.String("serviceName"), - netPeerIPKey: value.String("fake"), - netPeerPortKey: value.String("connection"), + standard.RPCServiceKey: value.String("serviceName"), + standard.NetPeerIPKey: value.String("fake"), + standard.NetPeerPortKey: value.String("connection"), }, eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: value.String("SENT"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), + standard.RPCMessageTypeKey: value.String("SENT"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: value.String("RECEIVED"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), + standard.RPCMessageTypeKey: value.String("RECEIVED"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, { name: "serviceName/bar", expectedAttr: map[kv.Key]value.Value{ - rpcServiceKey: value.String("serviceName"), - netPeerIPKey: value.String("fake"), - netPeerPortKey: value.String("connection"), + standard.RPCServiceKey: value.String("serviceName"), + standard.NetPeerIPKey: value.String("fake"), + standard.NetPeerPortKey: value.String("connection"), }, eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: value.String("SENT"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), + standard.RPCMessageTypeKey: value.String("SENT"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: value.String("RECEIVED"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), + standard.RPCMessageTypeKey: value.String("RECEIVED"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, { name: "invalidName", expectedAttr: map[kv.Key]value.Value{ - rpcServiceKey: value.String(""), - netPeerIPKey: value.String("fake"), - netPeerPortKey: value.String("connection"), + standard.RPCServiceKey: value.String(""), + standard.NetPeerIPKey: value.String("fake"), + standard.NetPeerPortKey: value.String("connection"), }, eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: value.String("SENT"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), + standard.RPCMessageTypeKey: value.String("SENT"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: value.String("RECEIVED"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), + standard.RPCMessageTypeKey: value.String("RECEIVED"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, { name: "/github.com.foo.serviceName_123/method", expectedAttr: map[kv.Key]value.Value{ - rpcServiceKey: value.String("serviceName_123"), - netPeerIPKey: value.String("fake"), - netPeerPortKey: value.String("connection"), + standard.RPCServiceKey: value.String("serviceName_123"), + standard.NetPeerIPKey: value.String("fake"), + standard.NetPeerPortKey: value.String("connection"), }, eventsAttr: []map[kv.Key]value.Value{ { - messageTypeKey: value.String("SENT"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), + standard.RPCMessageTypeKey: value.String("SENT"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(req))), }, { - messageTypeKey: value.String("RECEIVED"), - messageIDKey: value.Int(1), - messageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), + standard.RPCMessageTypeKey: value.String("RECEIVED"), + standard.RPCMessageIDKey: value.Int(1), + standard.RPCMessageUncompressedSizeKey: value.Int(proto.Size(proto.Message(reply))), }, }, }, @@ -342,9 +344,9 @@ func TestStreamClientInterceptor(t *testing.T) { attrs := spanData.Attributes expectedAttr := map[kv.Key]string{ - rpcServiceKey: "serviceName", - netPeerIPKey: "fake", - netPeerPortKey: "connection", + standard.RPCServiceKey: "serviceName", + standard.NetPeerIPKey: "fake", + standard.NetPeerPortKey: "connection", } for _, attr := range attrs { @@ -365,10 +367,10 @@ func TestStreamClientInterceptor(t *testing.T) { msgID := i/2 + 1 validate := func(eventName string, attrs []kv.KeyValue) { for _, attr := range attrs { - if attr.Key == messageTypeKey && attr.Value.AsString() != eventName { + if attr.Key == standard.RPCMessageTypeKey && attr.Value.AsString() != eventName { t.Errorf("invalid event on index: %d expecting %s event, receive %s event", i, eventName, attr.Value.AsString()) } - if attr.Key == messageIDKey && attr.Value != value.Int(msgID) { + if attr.Key == standard.RPCMessageIDKey && attr.Value != value.Int(msgID) { t.Errorf("invalid id for message event expected %d received %d", msgID, attr.Value.AsInt32()) } } diff --git a/instrumentation/httptrace/clienttrace.go b/instrumentation/httptrace/clienttrace.go index a91a94f16..78c54e4a7 100644 --- a/instrumentation/httptrace/clienttrace.go +++ b/instrumentation/httptrace/clienttrace.go @@ -22,6 +22,8 @@ import ( "strings" "sync" + "go.opentelemetry.io/otel/api/standard" + "google.golang.org/grpc/codes" "go.opentelemetry.io/otel/api/global" @@ -150,7 +152,7 @@ func (ct *clientTracer) span(hook string) trace.Span { } func (ct *clientTracer) getConn(host string) { - ct.start("http.getconn", "http.getconn", HostKey.String(host)) + ct.start("http.getconn", "http.getconn", standard.HTTPHostKey.String(host)) } func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) { @@ -170,7 +172,7 @@ func (ct *clientTracer) gotFirstResponseByte() { } func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) { - ct.start("http.dns", "http.dns", HostKey.String(info.Host)) + ct.start("http.dns", "http.dns", standard.HTTPHostKey.String(info.Host)) } func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) { diff --git a/instrumentation/httptrace/httptrace.go b/instrumentation/httptrace/httptrace.go index af7da000a..69a566b03 100644 --- a/instrumentation/httptrace/httptrace.go +++ b/instrumentation/httptrace/httptrace.go @@ -26,11 +26,6 @@ import ( "go.opentelemetry.io/otel/api/trace" ) -var ( - HostKey = kv.Key("http.host") - URLKey = kv.Key("http.url") -) - // Returns the Attributes, Context Entries, and SpanContext that were encoded by Inject. func Extract(ctx context.Context, req *http.Request) ([]kv.KeyValue, []kv.KeyValue, trace.SpanContext) { ctx = propagation.ExtractHTTP(ctx, global.Propagators(), req.Header) From aea2257ee4d9a04cbb6630c8e795a38083156338 Mon Sep 17 00:00:00 2001 From: Anthony J Mirabella Date: Mon, 1 Jun 2020 13:47:43 -0400 Subject: [PATCH 106/108] Add option to specify propagators to use with httptrace Extract and Inject --- instrumentation/httptrace/httptrace.go | 33 +++- instrumentation/httptrace/httptrace_test.go | 163 ++++++++++++++++++++ 2 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 instrumentation/httptrace/httptrace_test.go diff --git a/instrumentation/httptrace/httptrace.go b/instrumentation/httptrace/httptrace.go index 69a566b03..4f382217d 100644 --- a/instrumentation/httptrace/httptrace.go +++ b/instrumentation/httptrace/httptrace.go @@ -26,9 +26,33 @@ import ( "go.opentelemetry.io/otel/api/trace" ) +// Option is a function that allows configuration of the httptrace Extract() +// and Inject() functions +type Option func(*config) + +type config struct { + propagators propagation.Propagators +} + +func newConfig(opts []Option) *config { + c := &config{propagators: global.Propagators()} + for _, o := range opts { + o(c) + } + return c +} + +// WithPropagators sets the propagators to use for Extraction and Injection +func WithPropagators(props propagation.Propagators) Option { + return func(c *config) { + c.propagators = props + } +} + // Returns the Attributes, Context Entries, and SpanContext that were encoded by Inject. -func Extract(ctx context.Context, req *http.Request) ([]kv.KeyValue, []kv.KeyValue, trace.SpanContext) { - ctx = propagation.ExtractHTTP(ctx, global.Propagators(), req.Header) +func Extract(ctx context.Context, req *http.Request, opts ...Option) ([]kv.KeyValue, []kv.KeyValue, trace.SpanContext) { + c := newConfig(opts) + ctx = propagation.ExtractHTTP(ctx, c.propagators, req.Header) attrs := append( standard.HTTPServerAttributesFromHTTPRequest("", "", req), @@ -44,6 +68,7 @@ func Extract(ctx context.Context, req *http.Request) ([]kv.KeyValue, []kv.KeyVal return attrs, correlationCtxKVs, trace.RemoteSpanContextFromContext(ctx) } -func Inject(ctx context.Context, req *http.Request) { - propagation.InjectHTTP(ctx, global.Propagators(), req.Header) +func Inject(ctx context.Context, req *http.Request, opts ...Option) { + c := newConfig(opts) + propagation.InjectHTTP(ctx, c.propagators, req.Header) } diff --git a/instrumentation/httptrace/httptrace_test.go b/instrumentation/httptrace/httptrace_test.go new file mode 100644 index 000000000..cc05ba6a8 --- /dev/null +++ b/instrumentation/httptrace/httptrace_test.go @@ -0,0 +1,163 @@ +package httptrace_test + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + + "go.opentelemetry.io/otel/api/correlation" + "go.opentelemetry.io/otel/api/global" + "go.opentelemetry.io/otel/api/kv" + "go.opentelemetry.io/otel/api/propagation" + "go.opentelemetry.io/otel/api/standard" + "go.opentelemetry.io/otel/instrumentation/httptrace" + export "go.opentelemetry.io/otel/sdk/export/trace" + sdktrace "go.opentelemetry.io/otel/sdk/trace" +) + +func TestRoundtrip(t *testing.T) { + exp := &testExporter{ + spanMap: make(map[string][]*export.SpanData), + } + tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()})) + global.SetTraceProvider(tp) + + tr := tp.Tracer("httptrace/client") + + var expectedAttrs map[kv.Key]string + expectedCorrs := map[kv.Key]string{kv.Key("foo"): "bar"} + + // Mock http server + ts := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + attrs, corrs, span := httptrace.Extract(r.Context(), r) + + actualAttrs := make(map[kv.Key]string) + for _, attr := range attrs { + if attr.Key == standard.NetPeerPortKey { + // Peer port will be non-deterministic + continue + } + actualAttrs[attr.Key] = attr.Value.Emit() + } + + if diff := cmp.Diff(actualAttrs, expectedAttrs); diff != "" { + t.Fatalf("[TestRoundtrip] Attributes are different: %v", diff) + } + + actualCorrs := make(map[kv.Key]string) + for _, corr := range corrs { + actualCorrs[corr.Key] = corr.Value.Emit() + } + + if diff := cmp.Diff(actualCorrs, expectedCorrs); diff != "" { + t.Fatalf("[TestRoundtrip] Correlations are different: %v", diff) + } + + if !span.IsValid() { + t.Fatalf("[TestRoundtrip] Invalid span extracted: %v", span) + } + + _, err := w.Write([]byte("OK")) + if err != nil { + t.Fatal(err) + } + }), + ) + defer ts.Close() + + address := ts.Listener.Addr() + hp := strings.Split(address.String(), ":") + expectedAttrs = map[kv.Key]string{ + standard.HTTPFlavorKey: "1.1", + standard.HTTPHostKey: address.String(), + standard.HTTPMethodKey: "GET", + standard.HTTPSchemeKey: "http", + standard.HTTPTargetKey: "/", + standard.HTTPUserAgentKey: "Go-http-client/1.1", + standard.NetHostIPKey: hp[0], + standard.NetHostPortKey: hp[1], + standard.NetPeerIPKey: "127.0.0.1", + standard.NetTransportKey: "IP.TCP", + } + + client := ts.Client() + err := tr.WithSpan(context.Background(), "test", + func(ctx context.Context) error { + ctx = correlation.ContextWithMap(ctx, correlation.NewMap(correlation.MapUpdate{SingleKV: kv.Key("foo").String("bar")})) + req, _ := http.NewRequest("GET", ts.URL, nil) + httptrace.Inject(ctx, req) + + res, err := client.Do(req) + if err != nil { + t.Fatalf("Request failed: %s", err.Error()) + } + _ = res.Body.Close() + + return nil + }) + if err != nil { + panic("unexpected error in http request: " + err.Error()) + } +} + +func TestSpecifyPropagators(t *testing.T) { + exp := &testExporter{ + spanMap: make(map[string][]*export.SpanData), + } + tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()})) + global.SetTraceProvider(tp) + + tr := tp.Tracer("httptrace/client") + + expectedCorrs := map[kv.Key]string{kv.Key("foo"): "bar"} + + // Mock http server + ts := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, corrs, span := httptrace.Extract(r.Context(), r, httptrace.WithPropagators(propagation.New(propagation.WithExtractors(correlation.DefaultHTTPPropagator())))) + + actualCorrs := make(map[kv.Key]string) + for _, corr := range corrs { + actualCorrs[corr.Key] = corr.Value.Emit() + } + + if diff := cmp.Diff(actualCorrs, expectedCorrs); diff != "" { + t.Fatalf("[TestRoundtrip] Correlations are different: %v", diff) + } + + if span.IsValid() { + t.Fatalf("[TestRoundtrip] valid span extracted, expected none: %v", span) + } + + _, err := w.Write([]byte("OK")) + if err != nil { + t.Fatal(err) + } + }), + ) + defer ts.Close() + + client := ts.Client() + err := tr.WithSpan(context.Background(), "test", + func(ctx context.Context) error { + ctx = correlation.ContextWithMap(ctx, correlation.NewMap(correlation.MapUpdate{SingleKV: kv.Key("foo").String("bar")})) + req, _ := http.NewRequest("GET", ts.URL, nil) + httptrace.Inject(ctx, req, httptrace.WithPropagators(propagation.New(propagation.WithInjectors(correlation.DefaultHTTPPropagator())))) + + res, err := client.Do(req) + if err != nil { + t.Fatalf("Request failed: %s", err.Error()) + } + _ = res.Body.Close() + + return nil + }) + if err != nil { + panic("unexpected error in http request: " + err.Error()) + } +} From a5c21051c8eb2e12e833b2cd1d04490f1bfc83f2 Mon Sep 17 00:00:00 2001 From: Anthony J Mirabella Date: Mon, 1 Jun 2020 13:53:56 -0400 Subject: [PATCH 107/108] Add option to specify propagators to use with grpctrace Extract and Inject --- instrumentation/grpctrace/grpctrace.go | 33 ++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/instrumentation/grpctrace/grpctrace.go b/instrumentation/grpctrace/grpctrace.go index 294cc0db6..3b536fad9 100644 --- a/instrumentation/grpctrace/grpctrace.go +++ b/instrumentation/grpctrace/grpctrace.go @@ -26,6 +26,29 @@ import ( "go.opentelemetry.io/otel/api/trace" ) +// Option is a function that allows configuration of the grpctrace Extract() +// and Inject() functions +type Option func(*config) + +type config struct { + propagators propagation.Propagators +} + +func newConfig(opts []Option) *config { + c := &config{propagators: global.Propagators()} + for _, o := range opts { + o(c) + } + return c +} + +// WithPropagators sets the propagators to use for Extraction and Injection +func WithPropagators(props propagation.Propagators) Option { + return func(c *config) { + c.propagators = props + } +} + type metadataSupplier struct { metadata *metadata.MD } @@ -45,8 +68,9 @@ func (s *metadataSupplier) Set(key string, value string) { // Inject injects correlation context and span context into the gRPC // metadata object. This function is meant to be used on outgoing // requests. -func Inject(ctx context.Context, metadata *metadata.MD) { - propagation.InjectHTTP(ctx, global.Propagators(), &metadataSupplier{ +func Inject(ctx context.Context, metadata *metadata.MD, opts ...Option) { + c := newConfig(opts) + propagation.InjectHTTP(ctx, c.propagators, &metadataSupplier{ metadata: metadata, }) } @@ -54,8 +78,9 @@ func Inject(ctx context.Context, metadata *metadata.MD) { // Extract returns the correlation context and span context that // another service encoded in the gRPC metadata object with Inject. // This function is meant to be used on incoming requests. -func Extract(ctx context.Context, metadata *metadata.MD) ([]kv.KeyValue, trace.SpanContext) { - ctx = propagation.ExtractHTTP(ctx, global.Propagators(), &metadataSupplier{ +func Extract(ctx context.Context, metadata *metadata.MD, opts ...Option) ([]kv.KeyValue, trace.SpanContext) { + c := newConfig(opts) + ctx = propagation.ExtractHTTP(ctx, c.propagators, &metadataSupplier{ metadata: metadata, }) From be093d9d4b217afe9860c88b09ccd784a40cd12b Mon Sep 17 00:00:00 2001 From: Anthony J Mirabella Date: Mon, 1 Jun 2020 14:08:32 -0400 Subject: [PATCH 108/108] Add license header to httptrace_test.go --- instrumentation/httptrace/httptrace_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/instrumentation/httptrace/httptrace_test.go b/instrumentation/httptrace/httptrace_test.go index cc05ba6a8..33df19efa 100644 --- a/instrumentation/httptrace/httptrace_test.go +++ b/instrumentation/httptrace/httptrace_test.go @@ -1,3 +1,17 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package httptrace_test import (