mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
fix: Audit tests on the correct response writer type (#9445)
This commit is contained in:
parent
c2529260e7
commit
d090a17ed0
@ -27,6 +27,7 @@ import (
|
||||
"github.com/minio/minio/cmd/config/etcd/dns"
|
||||
"github.com/minio/minio/cmd/crypto"
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/http/stats"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/handlers"
|
||||
"github.com/rs/cors"
|
||||
@ -535,12 +536,21 @@ func setHTTPStatsHandler(h http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
func (h httpStatsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
isS3Request := !strings.HasPrefix(r.URL.Path, minioReservedBucketPath)
|
||||
// record s3 connection stats.
|
||||
r.Body = &recordTrafficRequest{ReadCloser: r.Body, isS3Request: isS3Request}
|
||||
recordResponse := &recordTrafficResponse{ResponseWriter: w, isS3Request: isS3Request}
|
||||
// Meters s3 connection stats.
|
||||
meteredRequest := &stats.IncomingTrafficMeter{ReadCloser: r.Body}
|
||||
meteredResponse := &stats.OutgoingTrafficMeter{ResponseWriter: w}
|
||||
|
||||
// Execute the request
|
||||
h.handler.ServeHTTP(recordResponse, r)
|
||||
r.Body = meteredRequest
|
||||
h.handler.ServeHTTP(meteredResponse, r)
|
||||
|
||||
if strings.HasPrefix(r.URL.Path, minioReservedBucketPath) {
|
||||
globalConnStats.incInputBytes(meteredRequest.BytesCount())
|
||||
globalConnStats.incOutputBytes(meteredResponse.BytesCount())
|
||||
} else {
|
||||
globalConnStats.incS3InputBytes(meteredRequest.BytesCount())
|
||||
globalConnStats.incS3OutputBytes(meteredResponse.BytesCount())
|
||||
}
|
||||
}
|
||||
|
||||
// requestValidityHandler validates all the incoming paths for
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
@ -35,6 +36,8 @@ import (
|
||||
"github.com/minio/minio/pkg/bucket/object/tagging"
|
||||
"github.com/minio/minio/pkg/handlers"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
|
||||
stats "github.com/minio/minio/cmd/http/stats"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -360,36 +363,19 @@ func httpTraceHdrs(f http.HandlerFunc) http.HandlerFunc {
|
||||
|
||||
func collectAPIStats(api string, f http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
globalHTTPStats.currentS3Requests.Inc(api)
|
||||
defer globalHTTPStats.currentS3Requests.Dec(api)
|
||||
|
||||
isS3Request := !strings.HasPrefix(r.URL.Path, minioReservedBucketPath)
|
||||
statsWriter := stats.NewRecordAPIStats(w)
|
||||
|
||||
// Time start before the call is about to start.
|
||||
tBefore := UTCNow()
|
||||
|
||||
apiStatsWriter := &recordAPIStats{ResponseWriter: w, TTFB: tBefore, isS3Request: isS3Request}
|
||||
|
||||
if isS3Request {
|
||||
globalHTTPStats.currentS3Requests.Inc(api)
|
||||
}
|
||||
|
||||
// Execute the request
|
||||
f.ServeHTTP(apiStatsWriter, r)
|
||||
|
||||
if isS3Request {
|
||||
globalHTTPStats.currentS3Requests.Dec(api)
|
||||
}
|
||||
|
||||
// Firstbyte read.
|
||||
tAfter := apiStatsWriter.TTFB
|
||||
f.ServeHTTP(statsWriter, r)
|
||||
|
||||
// Time duration in secs since the call started.
|
||||
//
|
||||
// We don't need to do nanosecond precision in this
|
||||
// simply for the fact that it is not human readable.
|
||||
durationSecs := tAfter.Sub(tBefore).Seconds()
|
||||
durationSecs := time.Since(statsWriter.StartTime).Seconds()
|
||||
|
||||
// Update http statistics
|
||||
globalHTTPStats.updateStats(api, r, apiStatsWriter, durationSecs)
|
||||
globalHTTPStats.updateStats(api, r, statsWriter, durationSecs)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
stats "github.com/minio/minio/cmd/http/stats"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
@ -166,18 +167,18 @@ func (st *HTTPStats) toServerHTTPStats() ServerHTTPStats {
|
||||
}
|
||||
|
||||
// Update statistics from http request and response data
|
||||
func (st *HTTPStats) updateStats(api string, r *http.Request, w *recordAPIStats, durationSecs float64) {
|
||||
func (st *HTTPStats) updateStats(api string, r *http.Request, w *stats.RecordAPIStats, durationSecs float64) {
|
||||
// A successful request has a 2xx response code
|
||||
successReq := (w.respStatusCode >= 200 && w.respStatusCode < 300)
|
||||
successReq := (w.RespStatusCode >= 200 && w.RespStatusCode < 300)
|
||||
|
||||
if w.isS3Request && !strings.HasSuffix(r.URL.Path, prometheusMetricsPath) {
|
||||
if !strings.HasSuffix(r.URL.Path, prometheusMetricsPath) {
|
||||
st.totalS3Requests.Inc(api)
|
||||
if !successReq && w.respStatusCode != 0 {
|
||||
if !successReq && w.RespStatusCode != 0 {
|
||||
st.totalS3Errors.Inc(api)
|
||||
}
|
||||
}
|
||||
|
||||
if w.isS3Request && r.Method == "GET" {
|
||||
if r.Method == "GET" {
|
||||
// Increment the prometheus http request response histogram with appropriate label
|
||||
httpRequestsDuration.With(prometheus.Labels{"api": api}).Observe(durationSecs)
|
||||
}
|
||||
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* MinIO Cloud Storage, (C) 2019 MinIO, Inc.
|
||||
*
|
||||
* 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 cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// records the incoming bytes from the underlying request.Body.
|
||||
type recordTrafficRequest struct {
|
||||
io.ReadCloser
|
||||
isS3Request bool
|
||||
}
|
||||
|
||||
// Records the bytes read.
|
||||
func (r *recordTrafficRequest) Read(p []byte) (n int, err error) {
|
||||
n, err = r.ReadCloser.Read(p)
|
||||
globalConnStats.incInputBytes(n)
|
||||
if r.isS3Request {
|
||||
globalConnStats.incS3InputBytes(n)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Records the outgoing bytes through the responseWriter.
|
||||
type recordTrafficResponse struct {
|
||||
// wrapper for underlying http.ResponseWriter.
|
||||
http.ResponseWriter
|
||||
isS3Request bool
|
||||
}
|
||||
|
||||
// Records the output bytes
|
||||
func (r *recordTrafficResponse) Write(p []byte) (n int, err error) {
|
||||
n, err = r.ResponseWriter.Write(p)
|
||||
globalConnStats.incOutputBytes(n)
|
||||
// Check if it is s3 request
|
||||
if r.isS3Request {
|
||||
globalConnStats.incS3OutputBytes(n)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Calls the underlying Flush.
|
||||
func (r *recordTrafficResponse) Flush() {
|
||||
r.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// Records the outgoing bytes through the responseWriter.
|
||||
type recordAPIStats struct {
|
||||
http.ResponseWriter
|
||||
TTFB time.Time // TimeToFirstByte.
|
||||
firstByteRead bool
|
||||
respStatusCode int
|
||||
isS3Request bool
|
||||
}
|
||||
|
||||
// Calls the underlying WriteHeader.
|
||||
func (r *recordAPIStats) WriteHeader(i int) {
|
||||
r.respStatusCode = i
|
||||
r.ResponseWriter.WriteHeader(i)
|
||||
}
|
||||
|
||||
// Records the TTFB on the first byte write.
|
||||
func (r *recordAPIStats) Write(p []byte) (n int, err error) {
|
||||
if !r.firstByteRead {
|
||||
r.TTFB = UTCNow()
|
||||
r.firstByteRead = true
|
||||
}
|
||||
return r.ResponseWriter.Write(p)
|
||||
}
|
||||
|
||||
// Calls the underlying Flush.
|
||||
func (r *recordAPIStats) Flush() {
|
||||
r.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
107
cmd/http/stats/http-traffic-recorder.go
Normal file
107
cmd/http/stats/http-traffic-recorder.go
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* MinIO Cloud Storage, (C) 2019-2020 MinIO, Inc.
|
||||
*
|
||||
* 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 stats
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IncomingTrafficMeter counts the incoming bytes from the underlying request.Body.
|
||||
type IncomingTrafficMeter struct {
|
||||
io.ReadCloser
|
||||
countBytes int
|
||||
}
|
||||
|
||||
// Read calls the underlying Read and counts the transferred bytes.
|
||||
func (r *IncomingTrafficMeter) Read(p []byte) (n int, err error) {
|
||||
n, err = r.ReadCloser.Read(p)
|
||||
r.countBytes += n
|
||||
return n, err
|
||||
}
|
||||
|
||||
// BytesCount returns the number of transferred bytes
|
||||
func (r IncomingTrafficMeter) BytesCount() int {
|
||||
return r.countBytes
|
||||
}
|
||||
|
||||
// OutgoingTrafficMeter counts the outgoing bytes through the responseWriter.
|
||||
type OutgoingTrafficMeter struct {
|
||||
// wrapper for underlying http.ResponseWriter.
|
||||
http.ResponseWriter
|
||||
countBytes int
|
||||
}
|
||||
|
||||
// Write calls the underlying write and counts the output bytes
|
||||
func (w *OutgoingTrafficMeter) Write(p []byte) (n int, err error) {
|
||||
n, err = w.ResponseWriter.Write(p)
|
||||
w.countBytes += n
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Flush calls the underlying Flush.
|
||||
func (w *OutgoingTrafficMeter) Flush() {
|
||||
w.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// BytesCount returns the number of transferred bytes
|
||||
func (w OutgoingTrafficMeter) BytesCount() int {
|
||||
return w.countBytes
|
||||
}
|
||||
|
||||
// RecordAPIStats is a response writer which stores
|
||||
// information of the underlying http response.
|
||||
type RecordAPIStats struct {
|
||||
http.ResponseWriter
|
||||
TTFB time.Duration // TimeToFirstByte.
|
||||
StartTime time.Time
|
||||
RespStatusCode int
|
||||
|
||||
firstByteRead bool
|
||||
}
|
||||
|
||||
// NewRecordAPIStats creates a new response writer with
|
||||
// start time set to the function call time.
|
||||
func NewRecordAPIStats(w http.ResponseWriter) *RecordAPIStats {
|
||||
return &RecordAPIStats{
|
||||
ResponseWriter: w,
|
||||
StartTime: time.Now().UTC(),
|
||||
}
|
||||
}
|
||||
|
||||
// WriteHeader calls the underlying WriteHeader
|
||||
// and records the response status code.
|
||||
func (r *RecordAPIStats) WriteHeader(i int) {
|
||||
r.RespStatusCode = i
|
||||
r.ResponseWriter.WriteHeader(i)
|
||||
}
|
||||
|
||||
// Write calls the underlying Write and updates TTFB and other info
|
||||
func (r *RecordAPIStats) Write(p []byte) (n int, err error) {
|
||||
if !r.firstByteRead {
|
||||
r.TTFB = time.Now().UTC().Sub(r.StartTime)
|
||||
r.firstByteRead = true
|
||||
}
|
||||
n, err = r.ResponseWriter.Write(p)
|
||||
return
|
||||
}
|
||||
|
||||
// Flush calls the underlying Flush.
|
||||
func (r *RecordAPIStats) Flush() {
|
||||
r.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
@ -26,6 +26,8 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/cmd/logger/message/audit"
|
||||
|
||||
stats "github.com/minio/minio/cmd/http/stats"
|
||||
)
|
||||
|
||||
// ResponseWriter - is a wrapper to trap the http response status code.
|
||||
@ -132,14 +134,22 @@ func AddAuditTarget(t Target) {
|
||||
|
||||
// AuditLog - logs audit logs to all audit targets.
|
||||
func AuditLog(w http.ResponseWriter, r *http.Request, api string, reqClaims map[string]interface{}) {
|
||||
var statusCode int
|
||||
var timeToResponse time.Duration
|
||||
var timeToFirstByte time.Duration
|
||||
lrw, ok := w.(*ResponseWriter)
|
||||
// Fast exit if there is not audit target configured
|
||||
if len(AuditTargets) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
statusCode int
|
||||
timeToResponse time.Duration
|
||||
timeToFirstByte time.Duration
|
||||
)
|
||||
|
||||
st, ok := w.(*stats.RecordAPIStats)
|
||||
if ok {
|
||||
statusCode = lrw.StatusCode
|
||||
timeToResponse = time.Now().UTC().Sub(lrw.StartTime)
|
||||
timeToFirstByte = lrw.TimeToFirstByte
|
||||
statusCode = st.RespStatusCode
|
||||
timeToResponse = time.Now().UTC().Sub(st.StartTime)
|
||||
timeToFirstByte = st.TTFB
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
@ -149,16 +159,17 @@ func AuditLog(w http.ResponseWriter, r *http.Request, api string, reqClaims map[
|
||||
object = vars["object"]
|
||||
}
|
||||
|
||||
entry := audit.ToEntry(w, r, reqClaims, globalDeploymentID)
|
||||
entry.API.Name = api
|
||||
entry.API.Bucket = bucket
|
||||
entry.API.Object = object
|
||||
entry.API.Status = http.StatusText(statusCode)
|
||||
entry.API.StatusCode = statusCode
|
||||
entry.API.TimeToFirstByte = timeToFirstByte.String()
|
||||
entry.API.TimeToResponse = timeToResponse.String()
|
||||
|
||||
// Send audit logs only to http targets.
|
||||
for _, t := range AuditTargets {
|
||||
entry := audit.ToEntry(w, r, reqClaims, globalDeploymentID)
|
||||
entry.API.Name = api
|
||||
entry.API.Bucket = bucket
|
||||
entry.API.Object = object
|
||||
entry.API.Status = http.StatusText(statusCode)
|
||||
entry.API.StatusCode = statusCode
|
||||
entry.API.TimeToFirstByte = timeToFirstByte.String()
|
||||
entry.API.TimeToResponse = timeToResponse.String()
|
||||
_ = t.Send(entry, string(All))
|
||||
}
|
||||
}
|
||||
|
3
go.mod
3
go.mod
@ -114,10 +114,12 @@ require (
|
||||
go.uber.org/multierr v1.1.0 // indirect
|
||||
go.uber.org/zap v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
golang.org/x/tools v0.0.0-20200428211428-0c9eba77bc32 // indirect
|
||||
google.golang.org/api v0.5.0
|
||||
google.golang.org/appengine v1.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190513181449-d00d292a067c // indirect
|
||||
@ -126,4 +128,5 @@ require (
|
||||
gopkg.in/ldap.v3 v3.0.3
|
||||
gopkg.in/olivere/elastic.v5 v5.0.80
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 // indirect
|
||||
)
|
||||
|
25
go.sum
25
go.sum
@ -47,6 +47,7 @@ github.com/cheggaaa/pb v1.0.28 h1:kWGpdAcSp3MxMU9CCHOwz/8V0kCHN4+9yQm2MzWuI98=
|
||||
github.com/cheggaaa/pb v1.0.28/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
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 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coredns/coredns v1.4.0 h1:RubBkYmkByUqZWWkjRHvNLnUHgkRVqAWgSMmRFvpE1A=
|
||||
github.com/coredns/coredns v1.4.0/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0=
|
||||
@ -138,6 +139,7 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57 h1:eqyIo2HjKhKe/mJzTG8n4VqvLXIOEG+SLdDqX7xGtkY=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc=
|
||||
@ -378,6 +380,7 @@ github.com/rcrowley/go-metrics v0.0.0-20190704165056-9c2d0518ed81/go.mod h1:bCqn
|
||||
github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8=
|
||||
github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
|
||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
@ -436,6 +439,7 @@ github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
|
||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||
@ -452,8 +456,10 @@ golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f h1:kz4KIr+xcPUsI3VMoqWfPMvtnJ6MGfiVwsWSVzphMO4=
|
||||
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@ -462,6 +468,11 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -474,6 +485,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -487,6 +499,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
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-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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -523,7 +536,15 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190914235951-31e00f45c22e h1:nOOVVcLC+/3MeovP40q5lCiWmP1Z1DaN8yn8ngU63hw=
|
||||
golang.org/x/tools v0.0.0-20190914235951-31e00f45c22e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200428211428-0c9eba77bc32 h1:Xvf3ZQTm5bjXPxhI7g+dwqsCqadK1rcNtwtszuatetk=
|
||||
golang.org/x/tools v0.0.0-20200428211428-0c9eba77bc32/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
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=
|
||||
google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.5.0 h1:lj9SyhMzyoa38fgFF0oO2T6pjs5IzkLPKfVtxpyCRMM=
|
||||
@ -554,6 +575,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.48.0 h1:URjZc+8ugRY5mL5uUeQH/a63JcHwdX9xZaWvmNWD7z8=
|
||||
@ -584,5 +606,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
|
||||
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 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
Loading…
Reference in New Issue
Block a user