├── pkg ├── prober │ ├── label.go │ ├── time.go │ ├── probe.go │ ├── timeout_checker.go │ ├── transit_probes.go │ ├── collector_test.go │ ├── receiver.go │ ├── packet.go │ ├── sender.go │ ├── prober.go │ ├── socket.go │ └── collector.go ├── frontend │ └── frontend.go ├── measurement │ └── measurement.go └── config │ ├── config_test.go │ └── config.go ├── matroschka.yml ├── CONTRIBUTORS ├── .gitignore ├── AUTHORS ├── CONTRIBUTING.md ├── README.md ├── .travis.yml ├── go.mod ├── main.go ├── LICENSE └── go.sum /pkg/prober/label.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | type Label struct { 4 | Key string 5 | Value string 6 | } 7 | -------------------------------------------------------------------------------- /pkg/prober/time.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import "time" 4 | 5 | type clock interface { 6 | Now() time.Time 7 | } 8 | 9 | type realClock struct{} 10 | 11 | func (realClock) Now() time.Time { 12 | return time.Now() 13 | } 14 | -------------------------------------------------------------------------------- /matroschka.yml: -------------------------------------------------------------------------------- 1 | defaults: 2 | src_range: 169.254.0.0/30 3 | src_interface: dummy1 4 | 5 | routers: 6 | - name: core01.fra01 7 | dst_range: 10.3.0.255/32 8 | 9 | paths: 10 | - name: core01.fra01 11 | hops: 12 | - core01.fra01 -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute 2 | # (and typically have contributed) code to the 3 | # matroschka-prober repository. 4 | # The AUTHORS file lists the copyright holders; this file 5 | # lists people. For example, Exaring employees are listed here 6 | # but not in AUTHORS, because Exaring holds the copyright. 7 | 8 | Oliver Herms -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | # IDE specific files 17 | .idea/ 18 | *.iml 19 | .vscode 20 | 21 | # 'go build' binary 22 | matroschka-prober -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of matroschka-prober‚ authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as one of 6 | # Organization's name 7 | # Individual's name 8 | # Individual's name 9 | # See CONTRIBUTORS for the meaning of multiple email addresses. 10 | 11 | # Please keep the list sorted. 12 | 13 | EXARING AG 14 | Oliver Herms -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | Want to contribute? Great! First, read this page. 5 | 6 | ## Code guidelines 7 | 8 | We expect all submissions to be properly formatted using gofmt. 9 | Golint and govet shall not complain about your code. Gocyclo shall not report 10 | complexity above 15. And your code should not lower the testcoverage of our 11 | codebase. 12 | 13 | ## Code reviews 14 | 15 | All submissions, including submissions by project members, require review. We 16 | use Github pull requests for this purpose. 17 | 18 | ## License 19 | 20 | By sending us your code you agree to release your contribution under the projects 21 | chosen license: Apache License 2.0 (see LICENSE file). -------------------------------------------------------------------------------- /pkg/prober/probe.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | ) 8 | 9 | type probe struct { 10 | Seq uint64 11 | Ts int64 12 | } 13 | 14 | func unmarshal(data []byte) (*probe, error) { 15 | p := &probe{} 16 | err := binary.Read(bytes.NewReader(data), binary.BigEndian, p) 17 | if err != nil { 18 | return nil, fmt.Errorf("Unable to unmarshal read packet: %v", err) 19 | } 20 | 21 | return p, nil 22 | } 23 | 24 | func (p *probe) marshal() ([]byte, error) { 25 | var b bytes.Buffer 26 | err := binary.Write(&b, binary.BigEndian, p) 27 | if err != nil { 28 | return nil, fmt.Errorf("Unable to marshal: %v", err) 29 | } 30 | 31 | return b.Bytes(), nil 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # matroschka-prober 2 | 3 | Matroschka-Prober is a GRE based blackbox network monitoring tool 4 | 5 | [![Build Status](https://travis-ci.org/exaring/matroschka-prober.svg?branch=master)](https://travis-ci.org/exaring/matroschka-prober) 6 | [![Go ReportCard](http://goreportcard.com/badge/exaring/matroschka-prober)](http://goreportcard.com/report/exaring/matroschka-prober) 7 | [![Go Doc](https://godoc.org/github.com/exaring/matroschka-prober?status.svg)](https://godoc.org/github.com/exaring/matroschka-prober) 8 | 9 | ## Install 10 | 11 | ```go get github.com/exaring/matroschka-prober``` 12 | 13 | ## Documentation 14 | [Matroschka introduction by Takt at iNOG::12](https://www.youtube.com/watch?v=Jh00l7QtOgE) 15 | -------------------------------------------------------------------------------- /pkg/prober/timeout_checker.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "time" 5 | 6 | log "github.com/sirupsen/logrus" 7 | ) 8 | 9 | func (p *Prober) rttTimeoutChecker() { 10 | t := time.NewTicker(time.Duration(p.cfg.MeasurementLengthMS) * time.Millisecond) 11 | 12 | for { 13 | select { 14 | case <-p.stop: 15 | return 16 | case <-t.C: 17 | timeout := p.cfg.MeasurementLengthMS * uint64(time.Millisecond) 18 | maxTS := (uint64(time.Now().UnixNano()) - 3*timeout) 19 | for s := range p.transitProbes.getLt(int64(maxTS)) { 20 | err := p.transitProbes.remove(s) 21 | if err != nil { 22 | log.Infof("Probe %d timeouted: Unable to remove: %v", s, err) 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | 3 | addons: 4 | apt: 5 | sources: 6 | - ubuntu-toolchain-r-test 7 | packages: 8 | - wget 9 | - pkg-config 10 | - zip 11 | - g++ 12 | - zlib1g-dev 13 | - unzip 14 | - python 15 | 16 | language: go 17 | 18 | go: 19 | - 1.14 20 | 21 | script: 22 | - mkdir -p $HOME/gopath/src/github.com/exaring/ 23 | - ln -s $TRAVIS_BUILD_DIR $HOME/gopath/src/github.com/exaring/matroschka-prober || true 24 | - cd $HOME/gopath/src/github.com/exaring/matroschka-prober 25 | - go test -v -cover -coverprofile=coverage.out ./... 26 | - go get github.com/q3k/goveralls 27 | - go install github.com/q3k/goveralls/ 28 | - $HOME/gopath/bin/goveralls -coverprofile=coverage.out -merge=false -------------------------------------------------------------------------------- /pkg/prober/transit_probes.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type transitProbes struct { 9 | m map[uint64]int64 // index is the unix time in seconds, value is the sequence number 10 | l sync.RWMutex 11 | } 12 | 13 | func (t *transitProbes) add(p *probe) { 14 | t.l.Lock() 15 | defer t.l.Unlock() 16 | t.m[p.Seq] = p.Ts 17 | } 18 | 19 | func (t *transitProbes) remove(s uint64) error { 20 | t.l.Lock() 21 | 22 | if _, ok := t.m[s]; !ok { 23 | t.l.Unlock() 24 | return fmt.Errorf("Sequence number %d not found", s) 25 | } 26 | 27 | delete(t.m, s) 28 | t.l.Unlock() 29 | return nil 30 | } 31 | 32 | func (t *transitProbes) getLt(lt int64) map[uint64]int64 { 33 | ret := make(map[uint64]int64) 34 | t.l.RLock() 35 | defer t.l.RUnlock() 36 | 37 | for s, ts := range t.m { 38 | if ts < lt { 39 | ret[s] = ts 40 | } 41 | } 42 | 43 | return ret 44 | } 45 | 46 | func newTransitProbes() *transitProbes { 47 | return &transitProbes{ 48 | m: make(map[uint64]int64), 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/prober/collector_test.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func uint64ptr(v uint64) *uint64 { 11 | return &v 12 | } 13 | 14 | type mockClock struct { 15 | t time.Time 16 | } 17 | 18 | func (m mockClock) Now() time.Time { 19 | return m.t 20 | } 21 | 22 | func TestLastFinishedMeasurement(t *testing.T) { 23 | tests := []struct { 24 | name string 25 | p *Prober 26 | expected int64 27 | }{ 28 | { 29 | name: "Test #1", 30 | p: &Prober{ 31 | clock: mockClock{ 32 | t: time.Unix(1542556558, 0), 33 | }, 34 | cfg: Config{ 35 | MeasurementLengthMS: 1000, 36 | TimeoutMS: 200, 37 | }, 38 | }, 39 | expected: 1542556556000000000, 40 | }, 41 | { 42 | name: "Test #2", 43 | p: &Prober{ 44 | clock: mockClock{ 45 | t: time.Unix(1542556558, 250000000), 46 | }, 47 | cfg: Config{ 48 | MeasurementLengthMS: 1000, 49 | TimeoutMS: 200, 50 | }, 51 | }, 52 | expected: 1542556557000000000, 53 | }, 54 | } 55 | 56 | for _, test := range tests { 57 | ts := test.p.lastFinishedMeasurement() 58 | assert.Equalf(t, test.expected, ts, test.name) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/exaring/matroschka-prober 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/google/gopacket v1.1.19 7 | github.com/pkg/errors v0.9.1 8 | github.com/prometheus/client_golang v1.13.0 9 | github.com/q3k/statusz v0.0.0-20180806125932-924f04ea7114 10 | github.com/sirupsen/logrus v1.9.0 11 | github.com/stretchr/testify v1.8.0 12 | golang.org/x/net v0.7.0 13 | gopkg.in/yaml.v2 v2.4.0 14 | ) 15 | 16 | require ( 17 | github.com/beorn7/perks v1.0.1 // indirect 18 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/go-ole/go-ole v1.2.6 // indirect 21 | github.com/golang/glog v1.0.0 // indirect 22 | github.com/golang/protobuf v1.5.2 // indirect 23 | github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect 24 | github.com/pmezard/go-difflib v1.0.0 // indirect 25 | github.com/prometheus/client_model v0.2.0 // indirect 26 | github.com/prometheus/common v0.37.0 // indirect 27 | github.com/prometheus/procfs v0.8.0 // indirect 28 | github.com/shirou/gopsutil v2.21.11+incompatible // indirect 29 | github.com/yusufpapurcu/wmi v1.2.2 // indirect 30 | golang.org/x/sys v0.5.0 // indirect 31 | google.golang.org/protobuf v1.28.1 // indirect 32 | gopkg.in/yaml.v3 v3.0.1 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /pkg/prober/receiver.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "sync/atomic" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | ) 9 | 10 | func (p *Prober) receiver() { 11 | defer p.udpConn.Close() 12 | 13 | recvBuffer := make([]byte, p.mtu) 14 | for { 15 | select { 16 | case <-p.stop: 17 | return 18 | default: 19 | } 20 | 21 | _, err := p.udpConn.Read(recvBuffer) 22 | now := time.Now().UnixNano() 23 | if err != nil { 24 | log.Errorf("Unable to read from UDP socket: %v", err) 25 | return 26 | } 27 | 28 | atomic.AddUint64(&p.probesReceived, 1) 29 | 30 | pkt, err := unmarshal(recvBuffer) 31 | if err != nil { 32 | log.Errorf("Unable to unmarshal message: %v", err) 33 | return 34 | } 35 | 36 | err = p.transitProbes.remove(pkt.Seq) 37 | if err != nil { 38 | // Probe was count as lost, so we ignore it from here on 39 | continue 40 | } 41 | 42 | rtt := now - pkt.Ts 43 | if p.timedOut(rtt) { 44 | // Probe arrived late. rttTimoutChecker() will clean up after it. So we ignore it from here on 45 | atomic.AddUint64(&p.latePackets, 1) 46 | continue 47 | } 48 | 49 | p.measurements.AddRecv(pkt.Ts, uint64(rtt), p.cfg.MeasurementLengthMS) 50 | } 51 | } 52 | 53 | func (p *Prober) timedOut(s int64) bool { 54 | return s > int64(msToNS(p.cfg.TimeoutMS)) 55 | } 56 | 57 | func msToNS(s uint64) uint64 { 58 | return s * 1000000 59 | } 60 | -------------------------------------------------------------------------------- /pkg/prober/packet.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/google/gopacket" 8 | "github.com/google/gopacket/layers" 9 | ) 10 | 11 | const ( 12 | ttl = 64 13 | ) 14 | 15 | func (p *Prober) getSrcAddrHop(hop int, seq uint64) net.IP { 16 | return p.cfg.Hops[hop-1].SrcRange[seq%uint64(len(p.cfg.Hops[hop-1].SrcRange))] 17 | } 18 | 19 | func (p *Prober) getDstAddr(hop int, seq uint64) net.IP { 20 | return p.cfg.Hops[hop].DstRange[seq%uint64(len(p.cfg.Hops[hop].DstRange))] 21 | } 22 | 23 | func (p *Prober) craftPacket(pr *probe) ([]byte, error) { 24 | probeSer, err := pr.marshal() 25 | if err != nil { 26 | return nil, fmt.Errorf("Unable to marshal probe: %v", err) 27 | } 28 | 29 | buf := gopacket.NewSerializeBuffer() 30 | opts := gopacket.SerializeOptions{ 31 | FixLengths: true, 32 | ComputeChecksums: true, 33 | } 34 | 35 | l := make([]gopacket.SerializableLayer, 0, (len(p.cfg.Hops)-1)*2+5) 36 | l = append(l, &layers.GRE{ 37 | Protocol: layers.EthernetTypeIPv4, 38 | }) 39 | 40 | for i := range p.cfg.Hops { 41 | if i == 0 { 42 | continue 43 | } 44 | 45 | l = append(l, &layers.IPv4{ 46 | SrcIP: p.getSrcAddrHop(i, pr.Seq), 47 | DstIP: p.getDstAddr(i, pr.Seq), 48 | Version: 4, 49 | Protocol: layers.IPProtocolGRE, 50 | TOS: p.cfg.TOS.Value, 51 | TTL: ttl, 52 | }) 53 | 54 | l = append(l, &layers.GRE{ 55 | Protocol: layers.EthernetTypeIPv4, 56 | }) 57 | } 58 | 59 | // Create final UDP packet that will return 60 | ip := &layers.IPv4{ 61 | SrcIP: p.getSrcAddrHop(len(p.cfg.Hops), pr.Seq), 62 | DstIP: p.localAddr, 63 | Version: 4, 64 | Protocol: layers.IPProtocolUDP, 65 | TOS: p.cfg.TOS.Value, 66 | TTL: ttl, 67 | } 68 | l = append(l, ip) 69 | 70 | udp := &layers.UDP{ 71 | SrcPort: layers.UDPPort(p.dstUDPPort), 72 | DstPort: layers.UDPPort(p.dstUDPPort), 73 | } 74 | 75 | udp.SetNetworkLayerForChecksum(ip) 76 | l = append(l, udp) 77 | l = append(l, gopacket.Payload(probeSer)) 78 | l = append(l, p.payload) 79 | 80 | err = gopacket.SerializeLayers(buf, opts, l...) 81 | if err != nil { 82 | return nil, fmt.Errorf("Unable to serialize layers: %v", err) 83 | } 84 | 85 | return buf.Bytes(), nil 86 | 87 | } 88 | -------------------------------------------------------------------------------- /pkg/prober/sender.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "net" 7 | "sync/atomic" 8 | "time" 9 | 10 | log "github.com/sirupsen/logrus" 11 | "golang.org/x/net/ipv4" 12 | ) 13 | 14 | func (p *Prober) sender() { 15 | defer p.rawConn.Close() 16 | 17 | p.desynchronizeStartTime() 18 | p.setLocalAddr() 19 | seq := uint64(0) 20 | pr := probe{} 21 | t := time.NewTicker(time.Second / time.Duration(p.cfg.PPS)) 22 | 23 | for { 24 | select { 25 | case <-p.stop: 26 | return 27 | case <-t.C: 28 | } 29 | 30 | pr.Seq = seq 31 | pr.Ts = time.Now().UnixNano() 32 | pkt, err := p.craftPacket(&pr) 33 | if err != nil { 34 | log.Errorf("Unable to craft packet: %v", err) 35 | continue 36 | } 37 | 38 | p.transitProbes.add(&pr) 39 | 40 | tsAligned := pr.Ts - (pr.Ts % (int64(p.cfg.MeasurementLengthMS) * int64(time.Millisecond))) 41 | p.measurements.AddSent(tsAligned) 42 | 43 | srcAddr := p.getSrcAddr(seq) 44 | dstAddr := p.cfg.Hops[0].getAddr(seq) 45 | err = p.sendPacket(pkt, srcAddr, dstAddr) 46 | if err != nil { 47 | log.Errorf("Unable to send packet: %v", err) 48 | p.transitProbes.remove(pr.Seq) 49 | continue 50 | } 51 | 52 | atomic.AddUint64(&p.probesSent, 1) 53 | seq++ 54 | } 55 | } 56 | 57 | func (p *Prober) sendPacket(payload []byte, src net.IP, dst net.IP) error { 58 | iph := &ipv4.Header{ 59 | Src: src, 60 | Dst: dst, 61 | Version: ipv4.Version, 62 | Len: ipv4.HeaderLen, 63 | TOS: int(p.cfg.TOS.Value), 64 | TotalLen: ipv4.HeaderLen + len(payload), 65 | TTL: ttl, 66 | Protocol: 47, //GRE 67 | } 68 | 69 | // Set source IP on socket in order to enforce "ip rule..." rules (possible Linux bug) 70 | cm := ipv4.ControlMessage{} 71 | if p.cfg.ConfiguredSrcAddr != nil { 72 | cm.Src = p.cfg.ConfiguredSrcAddr 73 | } 74 | 75 | if err := p.rawConn.WriteTo(iph, payload, &cm); err != nil { 76 | return fmt.Errorf("Unable to send packet: %v", err) 77 | } 78 | 79 | return nil 80 | } 81 | 82 | func (p *Prober) desynchronizeStartTime() { 83 | time.Sleep(time.Duration(random(int64(p.cfg.TimeoutMS))) * time.Microsecond) 84 | } 85 | 86 | func random(max int64) int { 87 | rand.Seed(time.Now().Unix()) 88 | return rand.Intn(int(max)) 89 | } 90 | -------------------------------------------------------------------------------- /pkg/frontend/frontend.go: -------------------------------------------------------------------------------- 1 | package frontend 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promhttp" 8 | log "github.com/sirupsen/logrus" 9 | 10 | _ "github.com/q3k/statusz" 11 | ) 12 | 13 | // ProberRegistry is the interface to a prober registry 14 | type ProberRegistry interface { 15 | GetCollectors() []prometheus.Collector 16 | } 17 | 18 | // Config is a fronend config 19 | type Config struct { 20 | Version string 21 | MetricsPath string 22 | ListenAddress string 23 | } 24 | 25 | // Frontend represents an HTTP prometheus interface 26 | type Frontend struct { 27 | cfg *Config 28 | proberReg ProberRegistry 29 | } 30 | 31 | // New creates a new HTTP frontend 32 | func New(cfg *Config, proberReg ProberRegistry) *Frontend { 33 | return &Frontend{ 34 | cfg: cfg, 35 | proberReg: proberReg, 36 | } 37 | } 38 | 39 | // Start starts the frontend 40 | func (fe *Frontend) Start() { 41 | log.Infof("Starting Matroschka Prober (Version: %s)\n", fe.cfg.Version) 42 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 43 | w.Write([]byte(` 44 | Matroschka Prober (Version ` + fe.cfg.Version + `) 45 | 46 |

Matroschka Prober

47 |

Metrics

48 |

More information:

49 |

github.com/exaring/matroschka-prober

50 | 51 | `)) 52 | }) 53 | http.HandleFunc(fe.cfg.MetricsPath, fe.handleMetricsRequest) 54 | 55 | log.Infof("Listening for %s on %s\n", fe.cfg.MetricsPath, fe.cfg.ListenAddress) 56 | log.Fatal(http.ListenAndServe(fe.cfg.ListenAddress, nil)) 57 | } 58 | 59 | func (fe *Frontend) handleMetricsRequest(w http.ResponseWriter, r *http.Request) { 60 | reg := prometheus.NewRegistry() 61 | for _, p := range fe.proberReg.GetCollectors() { 62 | reg.MustRegister(p) 63 | } 64 | 65 | promhttp.HandlerFor(reg, promhttp.HandlerOpts{ 66 | ErrorLog: errLogger{log.New()}, 67 | ErrorHandling: promhttp.ContinueOnError}).ServeHTTP(w, r) 68 | } 69 | 70 | type errLogger struct { 71 | *log.Logger 72 | } 73 | 74 | func (l errLogger) Println(args ...interface{}) { 75 | l.Errorln(args...) 76 | } 77 | -------------------------------------------------------------------------------- /pkg/measurement/measurement.go: -------------------------------------------------------------------------------- 1 | package measurement 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | ) 9 | 10 | // Measurement represents a measurement 11 | type Measurement struct { 12 | Sent uint64 13 | Received uint64 14 | RTTSum uint64 15 | RTTMin uint64 16 | RTTMax uint64 17 | RTTs []uint64 18 | } 19 | 20 | // MeasurementsDB manages measurements 21 | type MeasurementsDB struct { 22 | m map[int64]*Measurement 23 | l sync.RWMutex 24 | } 25 | 26 | // NewDB creates a new measurements database 27 | func NewDB() *MeasurementsDB { 28 | return &MeasurementsDB{ 29 | m: make(map[int64]*Measurement), 30 | } 31 | } 32 | 33 | // AddSent adds a sent probe to the db 34 | func (m *MeasurementsDB) AddSent(ts int64) { 35 | m.l.Lock() 36 | 37 | if m.m[ts] == nil { 38 | m.m[ts] = &Measurement{ 39 | RTTs: make([]uint64, 0), 40 | } 41 | } 42 | m.m[ts].Sent++ 43 | 44 | m.l.Unlock() // This is not defered for performance reason 45 | } 46 | 47 | // AddRecv adds a received probe to the db 48 | func (m *MeasurementsDB) AddRecv(sentTsNS int64, rtt uint64, measurementDurationMS uint64) { 49 | m.l.RLock() 50 | 51 | allignedTs := sentTsNS - sentTsNS%int64(measurementDurationMS*uint64(time.Millisecond)) 52 | if _, ok := m.m[allignedTs]; !ok { 53 | log.Debugf("Received probe at %d sent at %d with rtt %d after bucket %d was removed. Now=%d", sentTsNS+int64(rtt), sentTsNS, allignedTs, rtt, time.Now().UnixNano()) 54 | m.l.RUnlock() // This is not defered for performance reason 55 | return 56 | } 57 | 58 | m.m[allignedTs].Received++ 59 | m.m[allignedTs].RTTs = append(m.m[allignedTs].RTTs, rtt) 60 | m.m[allignedTs].RTTSum += rtt 61 | 62 | if rtt < m.m[allignedTs].RTTMin || m.m[allignedTs].RTTMin == 0 { 63 | m.m[allignedTs].RTTMin = rtt 64 | } 65 | 66 | if rtt > m.m[allignedTs].RTTMax { 67 | m.m[allignedTs].RTTMax = rtt 68 | } 69 | 70 | m.l.RUnlock() // This is not defered for performance reason 71 | } 72 | 73 | // RemoveOlder removes all probes from the db that are older than ts 74 | func (m *MeasurementsDB) RemoveOlder(ts int64) { 75 | m.l.Lock() 76 | defer m.l.Unlock() 77 | 78 | for t := range m.m { 79 | if t < ts { 80 | delete(m.m, t) 81 | } 82 | } 83 | } 84 | 85 | // Get get's the measurement at ts 86 | func (m *MeasurementsDB) Get(ts int64) *Measurement { 87 | m.l.RLock() 88 | defer m.l.RUnlock() 89 | 90 | if _, ok := m.m[ts]; !ok { 91 | return nil 92 | } 93 | 94 | ret := *m.m[ts] 95 | return &ret 96 | } 97 | -------------------------------------------------------------------------------- /pkg/config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestConfigApplyDefaults(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | cfg *Config 13 | expected *Config 14 | }{ 15 | { 16 | name: "Test #1: loading of default settings", 17 | cfg: &Config{}, 18 | expected: &Config{ 19 | MetricsPath: &dfltMetricsPath, 20 | ListenAddress: &dfltListenAddress, 21 | BasePort: &dfltBasePort, 22 | SrcRange: &dfltSrcRange, 23 | Defaults: &Defaults{ 24 | MeasurementLengthMS: &dfltMeasurementLengthMS, 25 | PayloadSizeBytes: &dfltPayloadSizeBytes, 26 | PPS: &dfltPPS, 27 | SrcRange: &dfltSrcRange, 28 | TimeoutMS: &dfltTimeoutMS, 29 | }, 30 | Classes: []Class{ 31 | { 32 | Name: "BE", 33 | TOS: 0x00, 34 | }, 35 | }, 36 | }, 37 | }, 38 | { 39 | name: "Test #2: loading default settings for paths", 40 | cfg: &Config{ 41 | Paths: []Path{ 42 | { 43 | Name: "Some path test", 44 | Hops: []string{ 45 | "SomeRouter02.SomeMetro01", 46 | }, 47 | }, 48 | }, 49 | Routers: []Router{ 50 | { 51 | Name: "SomeRouter02.SomeMetro01", 52 | DstRange: "192.168.0.0/24", 53 | SrcRange: "192.168.100.0/24", 54 | }, 55 | }, 56 | }, 57 | expected: &Config{ 58 | MetricsPath: &dfltMetricsPath, 59 | ListenAddress: &dfltListenAddress, 60 | BasePort: &dfltBasePort, 61 | SrcRange: &dfltSrcRange, 62 | Defaults: &Defaults{ 63 | MeasurementLengthMS: &dfltMeasurementLengthMS, 64 | PayloadSizeBytes: &dfltPayloadSizeBytes, 65 | PPS: &dfltPPS, 66 | SrcRange: &dfltSrcRange, 67 | TimeoutMS: &dfltTimeoutMS, 68 | }, 69 | Paths: []Path{ 70 | { 71 | Name: "Some path test", 72 | Hops: []string{ 73 | "SomeRouter02.SomeMetro01", 74 | }, 75 | MeasurementLengthMS: &dfltMeasurementLengthMS, 76 | PayloadSizeBytes: &dfltPayloadSizeBytes, 77 | PPS: &dfltPPS, 78 | TimeoutMS: &dfltTimeoutMS, 79 | }, 80 | }, 81 | Routers: []Router{ 82 | { 83 | Name: "SomeRouter02.SomeMetro01", 84 | DstRange: "192.168.0.0/24", 85 | SrcRange: "192.168.100.0/24", 86 | }, 87 | }, 88 | Classes: []Class{ 89 | { 90 | Name: "BE", 91 | TOS: 0x00, 92 | }, 93 | }, 94 | }, 95 | }, 96 | } 97 | 98 | for _, test := range tests { 99 | test.cfg.ApplyDefaults() 100 | assert.Equal(t, test.expected, test.cfg, test.name) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /pkg/prober/prober.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | 8 | "github.com/exaring/matroschka-prober/pkg/measurement" 9 | "github.com/google/gopacket" 10 | ) 11 | 12 | const ( 13 | mtuMax = uint16(9216) 14 | ) 15 | 16 | // Prober keeps the state of a prober instance. There is one instance per probed path. 17 | type Prober struct { 18 | cfg Config 19 | dstUDPPort uint16 20 | localAddr net.IP 21 | clock clock 22 | mtu uint16 23 | payload gopacket.Payload 24 | probesReceived uint64 25 | probesSent uint64 26 | rawConn rawSocket // Used to send GRE packets 27 | stop chan struct{} 28 | transitProbes *transitProbes // Keeps track of in-flight packets 29 | udpConn udpSocket // Used to receive returning packets 30 | measurements *measurement.MeasurementsDB 31 | latePackets uint64 32 | } 33 | 34 | // Config is the configuration of a prober 35 | type Config struct { 36 | BasePort uint16 37 | ConfiguredSrcAddr net.IP 38 | SrcAddrs []net.IP 39 | Hops []Hop 40 | StaticLabels []Label 41 | TOS TOS 42 | PPS uint64 43 | PayloadSizeBytes uint64 44 | MeasurementLengthMS uint64 45 | TimeoutMS uint64 46 | } 47 | 48 | // TOS represents a type of service mapping 49 | type TOS struct { 50 | Name string 51 | Value uint8 52 | } 53 | 54 | // Hop represents a hop on a path to be probed 55 | type Hop struct { 56 | Name string 57 | DstRange []net.IP 58 | SrcRange []net.IP 59 | } 60 | 61 | func (h *Hop) getAddr(s uint64) net.IP { 62 | return h.DstRange[s%uint64(len(h.DstRange))] 63 | } 64 | 65 | // New creates a new prober 66 | func New(c Config) (*Prober, error) { 67 | pr := &Prober{ 68 | cfg: c, 69 | clock: realClock{}, 70 | mtu: mtuMax, 71 | transitProbes: newTransitProbes(), 72 | measurements: measurement.NewDB(), 73 | stop: make(chan struct{}), 74 | payload: make(gopacket.Payload, c.PayloadSizeBytes), 75 | } 76 | 77 | return pr, nil 78 | } 79 | 80 | // Start starts the prober 81 | func (p *Prober) Start() error { 82 | err := p.init() 83 | if err != nil { 84 | return fmt.Errorf("Failed to init: %v", err) 85 | } 86 | 87 | go p.rttTimeoutChecker() 88 | go p.sender() 89 | go p.receiver() 90 | go p.cleaner() 91 | return nil 92 | } 93 | 94 | // Stop stops the prober 95 | func (p *Prober) Stop() { 96 | close(p.stop) 97 | } 98 | 99 | func (p *Prober) cleaner() { 100 | for { 101 | select { 102 | case <-p.stop: 103 | return 104 | default: 105 | time.Sleep(time.Second) 106 | p.measurements.RemoveOlder(p.lastFinishedMeasurement()) 107 | } 108 | } 109 | } 110 | 111 | func (p *Prober) getSrcAddr(s uint64) net.IP { 112 | return p.cfg.SrcAddrs[s%uint64(len(p.cfg.SrcAddrs))] 113 | } 114 | 115 | func (p *Prober) init() error { 116 | err := p.initRawSocket() 117 | if err != nil { 118 | return fmt.Errorf("Unable to initialize RAW socket: %v", err) 119 | } 120 | 121 | err = p.initUDPSocket() 122 | if err != nil { 123 | return fmt.Errorf("Unable to initialize UDP socket: %v", err) 124 | } 125 | 126 | return nil 127 | } 128 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | 9 | "gopkg.in/yaml.v2" 10 | 11 | "github.com/exaring/matroschka-prober/pkg/config" 12 | "github.com/exaring/matroschka-prober/pkg/frontend" 13 | "github.com/exaring/matroschka-prober/pkg/prober" 14 | "github.com/prometheus/client_golang/prometheus" 15 | log "github.com/sirupsen/logrus" 16 | 17 | _ "net/http/pprof" 18 | ) 19 | 20 | var ( 21 | cfgFilepath = flag.String("config.file", "matroschka.yml", "Config file") 22 | logLevel = flag.String("log.level", "debug", "Log Level") 23 | ) 24 | 25 | func main() { 26 | flag.Parse() 27 | 28 | level, err := log.ParseLevel(*logLevel) 29 | if err != nil { 30 | log.Errorf("Unable to parse log.level: %v", err) 31 | os.Exit(1) 32 | } 33 | log.SetLevel(level) 34 | 35 | cfg, err := loadConfig(*cfgFilepath) 36 | if err != nil { 37 | log.Errorf("Unable to load config: %v", err) 38 | os.Exit(1) 39 | } 40 | 41 | confSrc, err := cfg.GetConfiguredSrcAddr() 42 | if err != nil { 43 | log.Errorf("Unable to get configured src addr: %v", err) 44 | os.Exit(1) 45 | } 46 | 47 | probers := make([]*prober.Prober, 0) 48 | for i := range cfg.Paths { 49 | for j := range cfg.Classes { 50 | log.Infof("Starting prober for path %q class %q", cfg.Paths[i].Name, cfg.Classes[j].Name) 51 | p, err := prober.New(prober.Config{ 52 | BasePort: *cfg.BasePort, 53 | ConfiguredSrcAddr: confSrc, 54 | SrcAddrs: config.GenerateAddrs(*cfg.SrcRange), 55 | Hops: cfg.PathToProberHops(cfg.Paths[i]), 56 | StaticLabels: []prober.Label{}, 57 | TOS: prober.TOS{ 58 | Name: cfg.Classes[j].Name, 59 | Value: cfg.Classes[j].TOS, 60 | }, 61 | PPS: *cfg.Paths[i].PPS, 62 | PayloadSizeBytes: *cfg.Paths[i].PayloadSizeBytes, 63 | MeasurementLengthMS: *cfg.Paths[i].MeasurementLengthMS, 64 | TimeoutMS: *cfg.Paths[i].TimeoutMS, 65 | }) 66 | 67 | if err != nil { 68 | log.Errorf("Unable to get new prober: %v", err) 69 | os.Exit(1) 70 | } 71 | 72 | err = p.Start() 73 | if err != nil { 74 | log.Errorf("Unable to start prober: %v", err) 75 | os.Exit(1) 76 | } 77 | probers = append(probers, p) 78 | } 79 | } 80 | 81 | fe := frontend.New(&frontend.Config{ 82 | Version: cfg.Version, 83 | MetricsPath: *cfg.MetricsPath, 84 | ListenAddress: *cfg.ListenAddress, 85 | }, newRegistry(probers)) 86 | go fe.Start() 87 | select {} 88 | } 89 | 90 | type registry struct { 91 | probers []*prober.Prober 92 | } 93 | 94 | func newRegistry(probers []*prober.Prober) *registry { 95 | return ®istry{ 96 | probers: probers, 97 | } 98 | } 99 | 100 | func (r *registry) GetCollectors() []prometheus.Collector { 101 | ret := make([]prometheus.Collector, len(r.probers)) 102 | for i := range r.probers { 103 | ret[i] = r.probers[i] 104 | } 105 | 106 | return ret 107 | } 108 | 109 | func loadConfig(path string) (*config.Config, error) { 110 | cfgFile, err := ioutil.ReadFile(path) 111 | if err != nil { 112 | return nil, fmt.Errorf("Unable to read file %q: %v", path, err) 113 | } 114 | 115 | cfg := &config.Config{} 116 | err = yaml.Unmarshal(cfgFile, cfg) 117 | if err != nil { 118 | return nil, fmt.Errorf("Unable to unmarshal: %v", err) 119 | } 120 | 121 | cfg.ApplyDefaults() 122 | return cfg, nil 123 | } 124 | -------------------------------------------------------------------------------- /pkg/prober/socket.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "golang.org/x/net/ipv4" 9 | ) 10 | 11 | const ( 12 | maxPort = uint16(65535) 13 | ) 14 | 15 | type rawSocket interface { 16 | WriteTo(*ipv4.Header, []byte, *ipv4.ControlMessage) error 17 | Close() error 18 | } 19 | 20 | type udpSocket interface { 21 | Read([]byte) (int, error) 22 | Close() error 23 | } 24 | 25 | type rawSockWrapper struct { 26 | rawConn *ipv4.RawConn 27 | } 28 | 29 | func newRawSockWrapper() (*rawSockWrapper, error) { 30 | c, err := net.ListenPacket("ip4:47", "0.0.0.0") // GRE for IPv4 31 | if err != nil { 32 | return nil, fmt.Errorf("Unable to listen for GRE packets: %v", err) 33 | } 34 | 35 | rc, err := ipv4.NewRawConn(c) 36 | if err != nil { 37 | return nil, fmt.Errorf("Unable to create raw connection: %v", err) 38 | } 39 | 40 | return &rawSockWrapper{ 41 | rawConn: rc, 42 | }, nil 43 | } 44 | 45 | func (s *rawSockWrapper) WriteTo(h *ipv4.Header, p []byte, cm *ipv4.ControlMessage) error { 46 | return s.rawConn.WriteTo(h, p, cm) 47 | } 48 | 49 | func (s *rawSockWrapper) Close() error { 50 | return s.rawConn.Close() 51 | } 52 | 53 | type udpSockWrapper struct { 54 | udpConn *net.UDPConn 55 | port uint16 56 | } 57 | 58 | func newUDPSockWrapper(basePort uint16) (*udpSockWrapper, error) { 59 | var udpConn *net.UDPConn 60 | 61 | port := basePort 62 | // Try to find a free UDP port 63 | for { 64 | udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port)) 65 | if err != nil { 66 | return nil, fmt.Errorf("Unable to resolve address: %v", err) 67 | } 68 | 69 | udpConn, err = net.ListenUDP("udp", udpAddr) 70 | if err != nil { 71 | log.Debugf("UDP port %d is busy. Trying next one.", port) 72 | port++ 73 | if port > maxPort { 74 | return nil, fmt.Errorf("Unable to listen for UDP packets: %v", err) 75 | } 76 | continue 77 | } 78 | break 79 | } 80 | 81 | return &udpSockWrapper{ 82 | udpConn: udpConn, 83 | port: port, 84 | }, nil 85 | } 86 | 87 | func (u *udpSockWrapper) getPort() uint16 { 88 | return u.port 89 | } 90 | 91 | func (u *udpSockWrapper) Read(b []byte) (int, error) { 92 | return u.udpConn.Read(b) 93 | } 94 | 95 | func (u *udpSockWrapper) Close() error { 96 | return u.udpConn.Close() 97 | } 98 | 99 | func (p *Prober) initRawSocket() error { 100 | rc, err := newRawSockWrapper() 101 | if err != nil { 102 | return fmt.Errorf("Unable to create rack socket wrapper: %v", err) 103 | } 104 | 105 | p.rawConn = rc 106 | return nil 107 | } 108 | 109 | func (p *Prober) initUDPSocket() error { 110 | s, err := newUDPSockWrapper(p.cfg.BasePort) 111 | if err != nil { 112 | return fmt.Errorf("Unable to get UDP socket wrapper: %v", err) 113 | } 114 | 115 | p.udpConn = s 116 | p.dstUDPPort = s.getPort() 117 | return nil 118 | } 119 | 120 | func (p *Prober) setLocalAddr() error { 121 | if p.cfg.ConfiguredSrcAddr != nil { 122 | p.localAddr = p.cfg.ConfiguredSrcAddr 123 | return nil 124 | } 125 | 126 | addr, err := getLocalAddr(p.cfg.Hops[0].DstRange[0]) 127 | if err != nil { 128 | return fmt.Errorf("Unable to get local address: %v", err) 129 | } 130 | 131 | p.localAddr = addr 132 | return nil 133 | } 134 | 135 | func getLocalAddr(dest net.IP) (net.IP, error) { 136 | conn, err := net.Dial("udp", fmt.Sprintf("%s:123", dest.String())) 137 | if err != nil { 138 | return nil, fmt.Errorf("Dial failed: %v", err) 139 | } 140 | 141 | conn.Close() 142 | 143 | host, _, err := net.SplitHostPort(conn.LocalAddr().String()) 144 | if err != nil { 145 | return nil, fmt.Errorf("Unable to split host and port: %v", err) 146 | } 147 | 148 | return net.ParseIP(host), nil 149 | } 150 | -------------------------------------------------------------------------------- /pkg/prober/collector.go: -------------------------------------------------------------------------------- 1 | package prober 2 | 3 | import ( 4 | "strings" 5 | "sync/atomic" 6 | "time" 7 | 8 | "github.com/exaring/matroschka-prober/pkg/measurement" 9 | "github.com/prometheus/client_golang/prometheus" 10 | log "github.com/sirupsen/logrus" 11 | ) 12 | 13 | const ( 14 | metricPrefix = "matroschka_" 15 | ) 16 | 17 | // Describe is required by prometheus interface 18 | func (p *Prober) Describe(ch chan<- *prometheus.Desc) { 19 | } 20 | 21 | // Collect collects data from the collector and send it to prometheus 22 | func (p *Prober) Collect(ch chan<- prometheus.Metric) { 23 | ts := p.lastFinishedMeasurement() 24 | m := p.measurements.Get(ts) 25 | if m == nil { 26 | log.Infof("Requested timestamp %d not found", ts) 27 | return 28 | } 29 | 30 | p.collectSent(ch, m) 31 | p.collectReceived(ch, m) 32 | p.collectRTTMin(ch, m) 33 | p.collectRTTMax(ch, m) 34 | p.collectRTTAvg(ch, m) 35 | p.collectLatePackets(ch, m) 36 | } 37 | 38 | func (p *Prober) labels() []string { 39 | keys := make([]string, len(p.cfg.StaticLabels)+2) 40 | for i, l := range p.cfg.StaticLabels { 41 | keys[i] = l.Key 42 | } 43 | 44 | keys[len(keys)-2] = "tos" 45 | keys[len(keys)-1] = "path" 46 | return keys 47 | } 48 | 49 | func (p *Prober) labelValues() []string { 50 | values := make([]string, len(p.cfg.StaticLabels)+2) 51 | for i, l := range p.cfg.StaticLabels { 52 | values[i] = l.Value 53 | } 54 | 55 | values[len(values)-2] = p.cfg.TOS.Name 56 | values[len(values)-1] = strings.Join(p.getHopNames(), "-") 57 | return values 58 | } 59 | 60 | func (p *Prober) getHopNames() []string { 61 | ret := make([]string, len(p.cfg.Hops)) 62 | 63 | for i, x := range p.cfg.Hops { 64 | ret[i] = x.Name 65 | } 66 | 67 | return ret 68 | } 69 | 70 | func (p *Prober) collectSent(ch chan<- prometheus.Metric, m *measurement.Measurement) { 71 | desc := prometheus.NewDesc(metricPrefix+"packets_sent", "Sent packets", p.labels(), nil) 72 | ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, float64(m.Sent), p.labelValues()...) 73 | } 74 | 75 | func (p *Prober) collectReceived(ch chan<- prometheus.Metric, m *measurement.Measurement) { 76 | desc := prometheus.NewDesc(metricPrefix+"packets_received", "Received packets", p.labels(), nil) 77 | ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, float64(m.Received), p.labelValues()...) 78 | } 79 | 80 | func (p *Prober) collectRTTMin(ch chan<- prometheus.Metric, m *measurement.Measurement) { 81 | desc := prometheus.NewDesc(metricPrefix+"rtt_min", "RTT Min", p.labels(), nil) 82 | ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(m.RTTMin), p.labelValues()...) 83 | } 84 | 85 | func (p *Prober) collectRTTMax(ch chan<- prometheus.Metric, m *measurement.Measurement) { 86 | desc := prometheus.NewDesc(metricPrefix+"rtt_max", "RTT Max", p.labels(), nil) 87 | ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(m.RTTMax), p.labelValues()...) 88 | } 89 | 90 | func (p *Prober) collectRTTAvg(ch chan<- prometheus.Metric, m *measurement.Measurement) { 91 | desc := prometheus.NewDesc(metricPrefix+"rtt_avg", "RTT Average", p.labels(), nil) 92 | v := float64(0) 93 | if m.Received != 0 { 94 | v = float64(m.RTTSum / m.Received) 95 | } 96 | ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, v, p.labelValues()...) 97 | } 98 | 99 | func (p *Prober) collectLatePackets(ch chan<- prometheus.Metric, m *measurement.Measurement) { 100 | desc := prometheus.NewDesc(metricPrefix+"late_packets_total", "Timedout but received packets", p.labels(), nil) 101 | n := atomic.LoadUint64(&p.latePackets) 102 | ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, float64(n), p.labelValues()...) 103 | } 104 | 105 | func (p *Prober) lastFinishedMeasurement() int64 { 106 | measurementLengthNS := int64(p.cfg.MeasurementLengthMS) * int64(time.Millisecond) 107 | timeoutNS := int64(p.cfg.TimeoutMS) * int64(time.Millisecond) 108 | nowNS := p.clock.Now().UnixNano() 109 | ts := nowNS - timeoutNS - measurementLengthNS 110 | return ts - ts%measurementLengthNS 111 | } 112 | -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "net" 8 | 9 | "github.com/exaring/matroschka-prober/pkg/prober" 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | var ( 14 | dfltBasePort = uint16(32768) 15 | dfltClass = Class{ 16 | Name: "BE", 17 | TOS: 0x00, 18 | } 19 | dfltTimeoutMS = uint64(500) 20 | dfltListenAddress = ":9517" 21 | dfltMeasurementLengthMS = uint64(1000) 22 | dfltPayloadSizeBytes = uint64(0) 23 | dfltPPS = uint64(25) 24 | dfltSrcRange = "169.254.0.0/16" 25 | dfltMetricsPath = "/metrics" 26 | ) 27 | 28 | // Config represents the configuration of matroschka-prober 29 | type Config struct { 30 | Version string 31 | MetricsPath *string `yaml:"metrcis_path"` 32 | ListenAddress *string `yaml:"listen_address"` 33 | BasePort *uint16 `yaml:"base_port"` 34 | Defaults *Defaults `yaml:"defaults"` 35 | SrcRange *string `yaml:"src_range"` 36 | Classes []Class `yaml:"classes"` 37 | Paths []Path `yaml:"paths"` 38 | Routers []Router `yaml:"routers"` 39 | } 40 | 41 | // Defaults represents the default section of the config 42 | type Defaults struct { 43 | MeasurementLengthMS *uint64 `yaml:"measurement_length_ms"` 44 | PayloadSizeBytes *uint64 `yaml:"payload_size_bytes"` 45 | PPS *uint64 `yaml:"pps"` 46 | SrcRange *string `yaml:"src_range"` 47 | TimeoutMS *uint64 `yaml:"timeout"` 48 | SrcInterface *string `yaml:"src_interface"` 49 | } 50 | 51 | // Class reperesnets a traffic class in the config file 52 | type Class struct { 53 | Name string `yaml:"name"` 54 | TOS uint8 `yaml:"tos"` 55 | } 56 | 57 | // Path represents a path to be probed 58 | type Path struct { 59 | Name string `yaml:"name"` 60 | Hops []string `yaml:"hops"` 61 | MeasurementLengthMS *uint64 `yaml:"measurement_length_ms"` 62 | PayloadSizeBytes *uint64 `yaml:"payload_size_bytes"` 63 | PPS *uint64 `yaml:"pps"` 64 | TimeoutMS *uint64 `yaml:"timeout"` 65 | } 66 | 67 | // Router represents a router used a an explicit hop in a path 68 | type Router struct { 69 | Name string `yaml:"name"` 70 | DstRange string `yaml:"dst_range"` 71 | SrcRange string `yaml:"src_range"` 72 | } 73 | 74 | // Validate validates a configuration 75 | func (c *Config) Validate() error { 76 | err := c.validatePaths() 77 | if err != nil { 78 | return fmt.Errorf("Path validation failed: %v", err) 79 | } 80 | 81 | err = c.validateRouters() 82 | if err != nil { 83 | return fmt.Errorf("Router validation failed: %v", err) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | func (c *Config) validatePaths() error { 90 | for i := range c.Paths { 91 | for j := range c.Paths[i].Hops { 92 | if !c.routerExists(c.Paths[i].Hops[j]) { 93 | return fmt.Errorf("Router %q of path %q does not exist", c.Paths[i].Hops[j], c.Paths[i].Name) 94 | } 95 | } 96 | } 97 | 98 | return nil 99 | } 100 | 101 | func (c *Config) routerExists(needle string) bool { 102 | for i := range c.Routers { 103 | if c.Routers[i].Name == needle { 104 | return true 105 | } 106 | } 107 | 108 | return false 109 | } 110 | 111 | func (c *Config) validateRouters() error { 112 | for i := range c.Routers { 113 | _, _, err := net.ParseCIDR(c.Routers[i].DstRange) 114 | if err != nil { 115 | return fmt.Errorf("Unable to parse dst IP range for router %q: %v", c.Routers[i].Name, err) 116 | } 117 | } 118 | 119 | return nil 120 | } 121 | 122 | // ApplyDefaults applies default settings if they are missing from loaded config. 123 | func (c *Config) ApplyDefaults() { 124 | if c.Defaults == nil { 125 | c.Defaults = &Defaults{} 126 | } 127 | c.Defaults.applyDefaults() 128 | 129 | if c.SrcRange == nil { 130 | c.SrcRange = c.Defaults.SrcRange 131 | } 132 | 133 | if c.MetricsPath == nil { 134 | c.MetricsPath = &dfltMetricsPath 135 | } 136 | 137 | if c.ListenAddress == nil { 138 | c.ListenAddress = &dfltListenAddress 139 | } 140 | 141 | if c.BasePort == nil { 142 | c.BasePort = &dfltBasePort 143 | } 144 | 145 | for i := range c.Paths { 146 | c.Paths[i].applyDefaults(c.Defaults) 147 | } 148 | 149 | for i := range c.Routers { 150 | c.Routers[i].applyDefaults(c.Defaults) 151 | } 152 | 153 | if c.Classes == nil { 154 | c.Classes = []Class{ 155 | dfltClass, 156 | } 157 | } 158 | } 159 | 160 | func (r *Router) applyDefaults(d *Defaults) { 161 | if r.SrcRange == "" { 162 | r.SrcRange = *d.SrcRange 163 | } 164 | } 165 | 166 | func (p *Path) applyDefaults(d *Defaults) { 167 | if p.MeasurementLengthMS == nil { 168 | p.MeasurementLengthMS = d.MeasurementLengthMS 169 | } 170 | 171 | if p.PayloadSizeBytes == nil { 172 | p.PayloadSizeBytes = d.PayloadSizeBytes 173 | } 174 | 175 | if p.PPS == nil { 176 | p.PPS = d.PPS 177 | } 178 | 179 | if p.TimeoutMS == nil { 180 | p.TimeoutMS = d.TimeoutMS 181 | } 182 | } 183 | 184 | func (d *Defaults) applyDefaults() { 185 | if d.MeasurementLengthMS == nil { 186 | d.MeasurementLengthMS = &dfltMeasurementLengthMS 187 | } 188 | 189 | if d.PayloadSizeBytes == nil { 190 | d.PayloadSizeBytes = &dfltPayloadSizeBytes 191 | } 192 | 193 | if d.PPS == nil { 194 | d.PPS = &dfltPPS 195 | } 196 | 197 | if d.SrcRange == nil { 198 | d.SrcRange = &dfltSrcRange 199 | } 200 | 201 | if d.TimeoutMS == nil { 202 | d.TimeoutMS = &dfltTimeoutMS 203 | } 204 | } 205 | 206 | // GetConfiguredSrcAddr gets an IPv4 address of the configured src interface 207 | func (c *Config) GetConfiguredSrcAddr() (net.IP, error) { 208 | if c.Defaults.SrcInterface == nil { 209 | return nil, nil 210 | } 211 | 212 | return GetInterfaceAddr(*c.Defaults.SrcInterface) 213 | } 214 | 215 | // GetInterfaceAddr gets an interface first IPv4 address 216 | func GetInterfaceAddr(ifName string) (net.IP, error) { 217 | ifa, err := net.InterfaceByName(ifName) 218 | if err != nil { 219 | return nil, errors.Wrap(err, "Unable to get interface") 220 | } 221 | 222 | addrs, err := ifa.Addrs() 223 | if err != nil { 224 | return nil, errors.Wrap(err, "Unable to get addresses") 225 | } 226 | 227 | for _, a := range addrs { 228 | ip, _, err := net.ParseCIDR(a.String()) 229 | if err != nil { 230 | continue 231 | } 232 | 233 | if ip.To4() == nil { 234 | continue 235 | } 236 | 237 | return ip, nil 238 | } 239 | 240 | return nil, nil 241 | } 242 | 243 | // PathToProberHops generates prober hops 244 | func (c *Config) PathToProberHops(pathCfg Path) []prober.Hop { 245 | res := make([]prober.Hop, 0) 246 | 247 | for i := range pathCfg.Hops { 248 | for j := range c.Routers { 249 | if pathCfg.Hops[i] != c.Routers[j].Name { 250 | continue 251 | } 252 | 253 | h := prober.Hop{ 254 | Name: c.Routers[j].Name, 255 | DstRange: GenerateAddrs(c.Routers[j].DstRange), 256 | SrcRange: GenerateAddrs(c.Routers[j].SrcRange), 257 | } 258 | res = append(res, h) 259 | } 260 | } 261 | 262 | return res 263 | } 264 | 265 | // GenerateAddrs returns a list of all IPs in addrRange 266 | func GenerateAddrs(addrRange string) []net.IP { 267 | _, n, err := net.ParseCIDR(addrRange) 268 | if err != nil { 269 | panic(err) 270 | } 271 | 272 | baseAddr := getCIDRBase(*n) 273 | c := maskAddrCount(*n) 274 | ret := make([]net.IP, c) 275 | 276 | for i := uint32(0); i < c; i++ { 277 | ret[i] = net.IP(uint32Byte(baseAddr + i%c)) 278 | } 279 | 280 | return ret 281 | } 282 | 283 | func getCIDRBase(n net.IPNet) uint32 { 284 | return uint32b(n.IP) 285 | } 286 | 287 | func uint32b(data []byte) (ret uint32) { 288 | buf := bytes.NewBuffer(data) 289 | binary.Read(buf, binary.BigEndian, &ret) 290 | return 291 | } 292 | 293 | func getNthAddr(n net.IPNet, i uint32) net.IP { 294 | baseAddr := getCIDRBase(n) 295 | c := maskAddrCount(n) 296 | return net.IP(uint32Byte(baseAddr + i%c)) 297 | } 298 | 299 | func maskAddrCount(n net.IPNet) uint32 { 300 | ones, bits := n.Mask.Size() 301 | if ones == bits { 302 | return 1 303 | } 304 | 305 | x := uint32(1) 306 | for i := ones; i < bits; i++ { 307 | x = x * 2 308 | } 309 | return x 310 | } 311 | 312 | func uint32Byte(data uint32) (ret []byte) { 313 | buf := new(bytes.Buffer) 314 | binary.Write(buf, binary.BigEndian, data) 315 | return buf.Bytes() 316 | } 317 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | 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= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 33 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 34 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 35 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 36 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 37 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 38 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 39 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 40 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 41 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 42 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 43 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 44 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 45 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 46 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 47 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 48 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 49 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 50 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 51 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 52 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 53 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 54 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 55 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 56 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 57 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 58 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 59 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 60 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 61 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 62 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 63 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 64 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 65 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 66 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 67 | github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 68 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 69 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 70 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 71 | github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 72 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 73 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 74 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 75 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 76 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 77 | github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= 78 | github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= 79 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 80 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 81 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 82 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 83 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 84 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 85 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 86 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 87 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 88 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 89 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 90 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 91 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 92 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 93 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 94 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 95 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 96 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 97 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 98 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 99 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 100 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 101 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 102 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 103 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 104 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 105 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 106 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 107 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 108 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 109 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 110 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 111 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 112 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 113 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 114 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 115 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 116 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 117 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 118 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 119 | github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= 120 | github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= 121 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 122 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 123 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 124 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 125 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 126 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 127 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 128 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 129 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 130 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 131 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 132 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 133 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 134 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 135 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 136 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 137 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 138 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 139 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 140 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 141 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 142 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 143 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 144 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 145 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 146 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 147 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 148 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 149 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 150 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 151 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 152 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 153 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 154 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 155 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 156 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 157 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 158 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 159 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 160 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 161 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 162 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 163 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 164 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 165 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 166 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 167 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 168 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 169 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 170 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 171 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 172 | github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 173 | github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 174 | github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= 175 | github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= 176 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 177 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 178 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 179 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 180 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 181 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 182 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 183 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 184 | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= 185 | github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= 186 | github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= 187 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 188 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 189 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 190 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 191 | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 192 | github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= 193 | github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= 194 | github.com/q3k/statusz v0.0.0-20180806125932-924f04ea7114 h1:yE+9QBoq7TtskQmSe0bszFW2HvjHEdFzZiXtBEqHz0I= 195 | github.com/q3k/statusz v0.0.0-20180806125932-924f04ea7114/go.mod h1:XPU0+S0KWRKvdApuopRhvxEetTgY+9glGSKxIwONFuQ= 196 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 197 | github.com/shirou/gopsutil v2.21.11+incompatible h1:lOGOyCG67a5dv2hq5Z1BLDUqqKp3HkbjPcz5j6XMS0U= 198 | github.com/shirou/gopsutil v2.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 199 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 200 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 201 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 202 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 203 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 204 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 205 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 206 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 207 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 208 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 209 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 210 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 211 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 212 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 213 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 214 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 215 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 216 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 217 | github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= 218 | github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 219 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 220 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 221 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 222 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 223 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 224 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 225 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 226 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 227 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 228 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 229 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 230 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 231 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 232 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 233 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 234 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 235 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 236 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 237 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 238 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 239 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 240 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 241 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 242 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 243 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 244 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 245 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 246 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 247 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 248 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 249 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 250 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 251 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 252 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 253 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 254 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 255 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 256 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 257 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 258 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 259 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 260 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 261 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 262 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 263 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 264 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 265 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 266 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 267 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 268 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 269 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 270 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 271 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 272 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 273 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 274 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 275 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 276 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 277 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 278 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 279 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 280 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 281 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 282 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 283 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 284 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 285 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 286 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 287 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 288 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 289 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 290 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 291 | golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= 292 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 293 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 294 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 295 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 296 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 297 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 298 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 299 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 300 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 301 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 302 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 303 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 304 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 305 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 306 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 307 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 308 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 309 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 310 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 311 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 312 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 313 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 314 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 315 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 316 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 317 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 318 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 319 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 320 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 321 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 322 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 323 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 324 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 325 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 326 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 327 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 328 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 329 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 330 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 331 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 332 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 333 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 334 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 335 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 336 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 337 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 338 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 339 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 340 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 341 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 342 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 343 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 344 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 345 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 346 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 347 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 348 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 349 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 350 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 351 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 352 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 353 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 354 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 355 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 356 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 357 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 358 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 359 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 360 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 361 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 362 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 363 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 364 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 365 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 366 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 367 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 368 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 369 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 370 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 371 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 372 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 373 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 374 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 375 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 376 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 377 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 378 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 379 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 380 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 381 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 382 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 383 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 384 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 385 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 386 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 387 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 388 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 389 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 390 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 391 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 392 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 393 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 394 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 395 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 396 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 397 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 398 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 399 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 400 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 401 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 402 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 403 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 404 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 405 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 406 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 407 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 408 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 409 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 410 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 411 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 412 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 413 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 414 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 415 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 416 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 417 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 418 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 419 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 420 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 421 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 422 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 423 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 424 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 425 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 426 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 427 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 428 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 429 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 430 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 431 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 432 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 433 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 434 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 435 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 436 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 437 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 438 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 439 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 440 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 441 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 442 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 443 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 444 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 445 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 446 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 447 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 448 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 449 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 450 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 451 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 452 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 453 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 454 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 455 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 456 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 457 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 458 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 459 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 460 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 461 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 462 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 463 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 464 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 465 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 466 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 467 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 468 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 469 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 470 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 471 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 472 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 473 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 474 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 475 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 476 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 477 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 478 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 479 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 480 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 481 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 482 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 483 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 484 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 485 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 486 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 487 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 488 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 489 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 490 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 491 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 492 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 493 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 494 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 495 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 496 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 497 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 498 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 499 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 500 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 501 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 502 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 503 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 504 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 505 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 506 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 507 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 508 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 509 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 510 | --------------------------------------------------------------------------------