Skip to content

Commit 6fd38b9

Browse files
author
Eric Lee
authored
Cortex Exporter Send Pipeline (#210)
* Add addHeaders for adding headers to a HTTP request and TestAddHeaders * Add buildMessage for creating a compressed protobuf message and tests * Add buildRequest for creating the HTTP request and TestBuildRequest * Add sendRequest, TestSendRequest, and test helper function * Run make precommit * Change test helper function name, adjust comments, run make precommit
1 parent 020b7b5 commit 6fd38b9

File tree

5 files changed

+309
-1
lines changed

5 files changed

+309
-1
lines changed

exporters/metric/cortex/cortex.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@
1515
package cortex
1616

1717
import (
18+
"bytes"
1819
"context"
20+
"fmt"
21+
"net/http"
22+
23+
"github.com/gogo/protobuf/proto"
24+
"github.com/golang/snappy"
25+
"github.com/prometheus/prometheus/prompb"
1926

2027
"go.opentelemetry.io/otel/api/global"
2128
apimetric "go.opentelemetry.io/otel/api/metric"
@@ -78,3 +85,75 @@ func InstallNewPipeline(config Config, options ...push.Option) (*push.Controller
7885
global.SetMeterProvider(pusher.Provider())
7986
return pusher, nil
8087
}
88+
89+
// addHeaders adds required headers as well as all headers in Header map to a http
90+
// request.
91+
func (e *Exporter) addHeaders(req *http.Request) {
92+
// Cortex expects Snappy-compressed protobuf messages. These three headers are
93+
// hard-coded as they should be on every request.
94+
req.Header.Add("X-Prometheus-Remote-Write-Version", "0.1.0")
95+
req.Header.Add("Content-Encoding", "snappy")
96+
req.Header.Set("Content-Type", "application/x-protobuf")
97+
98+
// Add all user-supplied headers to the request.
99+
for name, field := range e.config.Headers {
100+
req.Header.Add(name, field)
101+
}
102+
}
103+
104+
// buildMessage creates a Snappy-compressed protobuf message from a slice of TimeSeries.
105+
func (e *Exporter) buildMessage(timeseries []*prompb.TimeSeries) ([]byte, error) {
106+
// Wrap the TimeSeries as a WriteRequest since Cortex requires it.
107+
writeRequest := &prompb.WriteRequest{
108+
Timeseries: timeseries,
109+
}
110+
111+
// Convert the struct to a slice of bytes and then compress it.
112+
message, err := proto.Marshal(writeRequest)
113+
if err != nil {
114+
return nil, err
115+
}
116+
compressed := snappy.Encode(nil, message)
117+
118+
return compressed, nil
119+
}
120+
121+
// buildRequest creates an http POST request with a Snappy-compressed protocol buffer
122+
// message as the body and with all the headers attached.
123+
func (e *Exporter) buildRequest(message []byte) (*http.Request, error) {
124+
req, err := http.NewRequest(
125+
http.MethodPost,
126+
e.config.Endpoint,
127+
bytes.NewBuffer(message),
128+
)
129+
if err != nil {
130+
return nil, err
131+
}
132+
133+
// Add the required headers and the headers from Config.Headers.
134+
e.addHeaders(req)
135+
136+
return req, nil
137+
}
138+
139+
// sendRequest sends an http request using the Exporter's http Client.
140+
func (e *Exporter) sendRequest(req *http.Request) error {
141+
// Set a client if the user didn't provide one.
142+
if e.config.Client == nil {
143+
e.config.Client = http.DefaultClient
144+
e.config.Client.Timeout = e.config.RemoteTimeout
145+
}
146+
147+
// Attempt to send request.
148+
res, err := e.config.Client.Do(req)
149+
if err != nil {
150+
return err
151+
}
152+
defer res.Body.Close()
153+
154+
// The response should have a status code of 200.
155+
if res.StatusCode != http.StatusOK {
156+
return fmt.Errorf("%v", res.Status)
157+
}
158+
return nil
159+
}

exporters/metric/cortex/cortex_test.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,19 @@
1515
package cortex
1616

1717
import (
18+
"fmt"
19+
"io/ioutil"
1820
"net/http"
21+
"net/http/httptest"
22+
"strconv"
1923
"testing"
2024
"time"
2125

26+
"github.com/gogo/protobuf/proto"
27+
"github.com/golang/snappy"
2228
"github.com/google/go-cmp/cmp"
29+
"github.com/prometheus/prometheus/prompb"
30+
"github.com/stretchr/testify/require"
2331

2432
"go.opentelemetry.io/otel/api/global"
2533
"go.opentelemetry.io/otel/sdk/export/metric"
@@ -97,3 +105,171 @@ func TestInstallNewPipeline(t *testing.T) {
97105
t.Fatalf("Failed to register push Controller provider globally")
98106
}
99107
}
108+
109+
// TestAddHeaders tests whether the correct headers are correctly added to a http request.
110+
func TestAddHeaders(t *testing.T) {
111+
testConfig := Config{
112+
Headers: map[string]string{
113+
"TestHeaderOne": "TestFieldTwo",
114+
"TestHeaderTwo": "TestFieldTwo",
115+
},
116+
}
117+
exporter := Exporter{testConfig}
118+
119+
// Create http request to add headers to.
120+
req, err := http.NewRequest("POST", "test.com", nil)
121+
require.Nil(t, err)
122+
exporter.addHeaders(req)
123+
124+
// Check that all the headers are there.
125+
for name, field := range testConfig.Headers {
126+
require.Equal(t, req.Header.Get(name), field)
127+
}
128+
require.Equal(t, req.Header.Get("Content-Encoding"), "snappy")
129+
require.Equal(t, req.Header.Get("Content-Type"), "application/x-protobuf")
130+
require.Equal(t, req.Header.Get("X-Prometheus-Remote-Write-Version"), "0.1.0")
131+
}
132+
133+
// TestBuildMessage tests whether BuildMessage successfully returns a Snappy-compressed
134+
// protobuf message.
135+
func TestBuildMessage(t *testing.T) {
136+
exporter := Exporter{validConfig}
137+
timeseries := []*prompb.TimeSeries{}
138+
139+
// buildMessage returns the error that proto.Marshal() returns. Since the proto
140+
// package has its own tests, buildMessage should work as expected as long as there
141+
// are no errors.
142+
_, err := exporter.buildMessage(timeseries)
143+
require.Nil(t, err)
144+
}
145+
146+
// TestBuildRequest tests whether a http request is a POST request, has the correct body,
147+
// and has the correct headers.
148+
func TestBuildRequest(t *testing.T) {
149+
// Make fake exporter and message for testing.
150+
var testMessage = []byte(`Test Message`)
151+
exporter := Exporter{validConfig}
152+
153+
// Create the http request.
154+
req, err := exporter.buildRequest(testMessage)
155+
require.Nil(t, err)
156+
157+
// Verify the http method, url, and body.
158+
require.Equal(t, req.Method, http.MethodPost)
159+
require.Equal(t, req.URL.String(), validConfig.Endpoint)
160+
161+
reqMessage, err := ioutil.ReadAll(req.Body)
162+
require.Nil(t, err)
163+
require.Equal(t, reqMessage, testMessage)
164+
165+
// Verify headers.
166+
for name, field := range exporter.config.Headers {
167+
require.Equal(t, req.Header.Get(name), field)
168+
}
169+
require.Equal(t, req.Header.Get("Content-Encoding"), "snappy")
170+
require.Equal(t, req.Header.Get("Content-Type"), "application/x-protobuf")
171+
require.Equal(t, req.Header.Get("X-Prometheus-Remote-Write-Version"), "0.1.0")
172+
}
173+
174+
// verifyExporterRequest checks a HTTP request from the export pipeline. It checks whether
175+
// the request contains a correctly formatted remote_write body and the required headers.
176+
func verifyExporterRequest(req *http.Request) error {
177+
// Check for required headers.
178+
if req.Header.Get("X-Prometheus-Remote-Write-Version") != "0.1.0" ||
179+
req.Header.Get("Content-Encoding") != "snappy" ||
180+
req.Header.Get("Content-Type") != "application/x-protobuf" {
181+
return fmt.Errorf("Request does not contain the three required headers")
182+
}
183+
184+
// Check body format and headers.
185+
compressed, err := ioutil.ReadAll(req.Body)
186+
if err != nil {
187+
return fmt.Errorf("Failed to read request body")
188+
}
189+
uncompressed, err := snappy.Decode(nil, compressed)
190+
if err != nil {
191+
return fmt.Errorf("Failed to uncompress request body")
192+
}
193+
wr := &prompb.WriteRequest{}
194+
err = proto.Unmarshal(uncompressed, wr)
195+
if err != nil {
196+
return fmt.Errorf("Failed to unmarshal message into WriteRequest struct")
197+
}
198+
199+
return nil
200+
}
201+
202+
// TestSendRequest checks if the Exporter can successfully send a http request with a
203+
// correctly formatted body and the correct headers. A test server returns different
204+
// status codes to test if the Exporter responds to a send failure correctly.
205+
func TestSendRequest(t *testing.T) {
206+
tests := []struct {
207+
testName string
208+
config *Config
209+
expectedError error
210+
isStatusNotFound bool
211+
}{
212+
{
213+
testName: "Successful Export",
214+
config: &validConfig,
215+
expectedError: nil,
216+
isStatusNotFound: false,
217+
},
218+
{
219+
testName: "Export Failure",
220+
config: &Config{},
221+
expectedError: fmt.Errorf("%v", "404 Not Found"),
222+
isStatusNotFound: true,
223+
},
224+
}
225+
226+
// Set up a test server to receive the request. The server responds with a 400 Bad
227+
// Request status code if any headers are missing or if the body is not of the correct
228+
// format. Additionally, the server can respond with status code 404 Not Found to
229+
// simulate send failures.
230+
handler := func(rw http.ResponseWriter, req *http.Request) {
231+
err := verifyExporterRequest(req)
232+
if err != nil {
233+
rw.WriteHeader(http.StatusBadRequest)
234+
return
235+
}
236+
237+
// Return a status code 400 if header isStatusNotFound is "true", 200 otherwise.
238+
if req.Header.Get("isStatusNotFound") == "true" {
239+
rw.WriteHeader(http.StatusNotFound)
240+
} else {
241+
rw.WriteHeader(http.StatusOK)
242+
}
243+
}
244+
server := httptest.NewServer(http.HandlerFunc(handler))
245+
defer server.Close()
246+
247+
for _, test := range tests {
248+
t.Run(test.testName, func(t *testing.T) {
249+
// Set up an Exporter that uses the test server's endpoint and attaches the
250+
// test's isStatusNotFound header.
251+
test.config.Endpoint = server.URL
252+
test.config.Headers = map[string]string{
253+
"isStatusNotFound": strconv.FormatBool(test.isStatusNotFound),
254+
}
255+
exporter := Exporter{*test.config}
256+
257+
// Create an empty Snappy-compressed message.
258+
msg, err := exporter.buildMessage([]*prompb.TimeSeries{})
259+
require.Nil(t, err)
260+
261+
// Create a http POST request with the compressed message.
262+
req, err := exporter.buildRequest(msg)
263+
require.Nil(t, err)
264+
265+
// Send the request to the test server and verify the error.
266+
err = exporter.sendRequest(req)
267+
if err != nil {
268+
errorString := err.Error()
269+
require.Equal(t, errorString, test.expectedError.Error())
270+
} else {
271+
require.Nil(t, test.expectedError)
272+
}
273+
})
274+
}
275+
}

exporters/metric/cortex/go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ module go.opentelemetry.io/contrib/exporters/metric/cortex
33
go 1.14
44

55
require (
6+
github.com/gogo/protobuf v1.3.1
7+
github.com/golang/snappy v0.0.1
68
github.com/google/go-cmp v0.5.1
9+
github.com/grpc-ecosystem/grpc-gateway v1.14.6 // indirect
10+
github.com/prometheus/prometheus v2.5.0+incompatible
711
github.com/stretchr/testify v1.6.1
812
go.opentelemetry.io/otel v0.10.0
913
go.opentelemetry.io/otel/sdk v0.10.0

exporters/metric/cortex/go.sum

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2+
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
23
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
34
github.com/DataDog/sketches-go v0.0.1 h1:RtG+76WKgZuz6FIaGsjoPePmadDBkuD/KC6+ZWu78b8=
45
github.com/DataDog/sketches-go v0.0.1/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
6+
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
57
github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg=
68
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
79
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -11,8 +13,12 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
1113
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1214
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1315
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
16+
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
1417
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
1518
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
19+
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
20+
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
21+
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
1622
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
1723
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
1824
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -25,6 +31,8 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
2531
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
2632
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
2733
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
34+
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
35+
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
2836
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
2937
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
3038
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -33,6 +41,10 @@ github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
3341
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
3442
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
3543
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
44+
github.com/grpc-ecosystem/grpc-gateway v1.14.6 h1:8ERzHx8aj1Sc47mu9n/AksaKCSWrMchFtkdrS4BIj5o=
45+
github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
46+
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
47+
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
3648
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
3749
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
3850
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -42,6 +54,10 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
4254
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4355
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4456
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
57+
github.com/prometheus/prometheus v2.5.0+incompatible h1:7QPitgO2kOFG8ecuRn9O/4L9+10He72rVRJvMXrE9Hg=
58+
github.com/prometheus/prometheus v2.5.0+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
59+
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
60+
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
4561
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
4662
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
4763
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
@@ -57,17 +73,25 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
5773
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
5874
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
5975
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
76+
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
6077
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
6178
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
6279
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
80+
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0 h1:2mqDk8w/o6UmeUCu5Qiq2y7iMf6anbx+YA8d1JFoFrs=
81+
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
6382
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
83+
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
6484
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
6585
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
86+
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
6687
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
6788
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
6889
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
90+
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
6991
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
92+
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
7093
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
94+
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
7195
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
7296
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
7397
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -78,11 +102,14 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
78102
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
79103
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
80104
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
81-
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8=
82105
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
106+
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 h1:fiNLklpBwWK1mth30Hlwk+fcdBmIALlgF5iy77O37Ig=
107+
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
83108
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
84109
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
85110
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
111+
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
112+
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
86113
google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
87114
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
88115
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -95,6 +122,7 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
95122
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
96123
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
97124
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
125+
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
98126
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
99127
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
100128
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

0 commit comments

Comments
 (0)