├── .envrc ├── plugin.cfg.yaml ├── .travis.yml ├── main.go ├── Makefile ├── .gitignore ├── go.mod ├── flake.lock ├── flake.nix ├── plugins └── rrl │ ├── metrics.go │ ├── kubernetes.go │ ├── cache │ ├── cache_test.go │ ├── shard_test.go │ └── cache.go │ ├── rrl_bench_test.go │ ├── handler.go │ ├── handler_test.go │ ├── setup.go │ ├── rrl_test.go │ ├── rrl.go │ └── setup_test.go ├── README.md ├── README-DEV.md ├── LICENSE └── go.sum /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /plugin.cfg.yaml: -------------------------------------------------------------------------------- 1 | name: "rrl" 2 | after: false 3 | landmark: "acl" 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.16.x 5 | 6 | script: 7 | - make test 8 | 9 | after_success: 10 | - bash <(curl -s https://codecov.io/bash) 11 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/coredns/coredns/core/dnsserver" 5 | _ "github.com/coredns/coredns/core/plugin" 6 | "github.com/coredns/coredns/coremain" 7 | 8 | _ "github.com/replit/rrl/plugins/rrl" 9 | ) 10 | 11 | func init() { 12 | dnsserver.Directives = append([]string{"rrl"}, dnsserver.Directives...) 13 | } 14 | 15 | func main() { 16 | coremain.Run() 17 | } 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GITCOMMIT:=$(shell git describe --dirty --always) 2 | BINARY:=coredns 3 | SYSTEM:= 4 | VERBOSE:=-v 5 | 6 | all: coredns 7 | 8 | .PHONY: coredns 9 | coredns: 10 | GO111MODULE=on CGO_ENABLED=0 $(SYSTEM) go build $(VERBOSE) -ldflags="-s -w -X github.com/coredns/coredns/coremain.GitCommit=$(GITCOMMIT)" -o $(BINARY) 11 | 12 | .PHONY: test 13 | test: 14 | GO111MODULE=on go test -v -race ./... 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ./rrl 2 | coredns 3 | Corefile 4 | .idea 5 | 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.dll 9 | *.so 10 | *.dylib 11 | 12 | # Test binary, build with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 19 | .glide/ 20 | 21 | .direnv 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/replit/rrl 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect 7 | github.com/coredns/caddy v1.1.1 8 | github.com/coredns/coredns v1.8.6 9 | github.com/miekg/dns v1.1.43 10 | github.com/prometheus/client_golang v1.11.0 11 | k8s.io/api v0.22.2 12 | k8s.io/apimachinery v0.22.2 13 | k8s.io/client-go v0.22.2 14 | ) 15 | 16 | replace github.com/DataDog/dd-trace-go v0.6.1 => github.com/datadog/dd-trace-go v0.6.1 17 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1675698036, 6 | "narHash": "sha256-BgsQkQewdlQi8gapJN4phpxkI/FCE/2sORBaFcYbp/A=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "1046c7b92e908a1202c0f1ba3fc21d19e1cf1b62", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "id": "nixpkgs", 14 | "type": "indirect" 15 | } 16 | }, 17 | "root": { 18 | "inputs": { 19 | "nixpkgs": "nixpkgs" 20 | } 21 | } 22 | }, 23 | "root": "root", 24 | "version": 7 25 | } 26 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A DNS response ratelimiter."; 3 | 4 | outputs = { self, nixpkgs }: 5 | let 6 | mkDevShell = system: 7 | let 8 | pkgs = nixpkgs.legacyPackages.${system}; 9 | in 10 | pkgs.mkShell { 11 | nativeBuildInputs = with pkgs; [ 12 | go gopls 13 | ]; 14 | }; 15 | in 16 | { 17 | devShells.aarch64-linux.default = mkDevShell "aarch64-linux"; 18 | devShells.aarch64-darwin.default = mkDevShell "aarch64-darwin"; 19 | devShells.x86_64-linux.default = mkDevShell "x86_64-linux"; 20 | devShells.x86_64-darwin.default = mkDevShell "x86_64-darwin"; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /plugins/rrl/metrics.go: -------------------------------------------------------------------------------- 1 | package rrl 2 | 3 | import ( 4 | "github.com/coredns/coredns/plugin" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promauto" 8 | ) 9 | 10 | // Variables declared for monitoring. 11 | var ( 12 | RequestsExceeded = promauto.NewCounterVec(prometheus.CounterOpts{ 13 | Namespace: plugin.Namespace, 14 | Subsystem: "rrl", 15 | Name: "requests_exceeded_total", 16 | Help: "Counter of requests exceeding QPS limit.", 17 | }, []string{"client_ip"}) 18 | 19 | ResponsesExceeded = promauto.NewCounterVec(prometheus.CounterOpts{ 20 | Namespace: plugin.Namespace, 21 | Subsystem: "rrl", 22 | Name: "responses_exceeded_total", 23 | Help: "Counter of responses exceeding QPS limit.", 24 | }, []string{"client_ip"}) 25 | ) 26 | -------------------------------------------------------------------------------- /plugins/rrl/kubernetes.go: -------------------------------------------------------------------------------- 1 | package rrl 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | corev1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/client-go/kubernetes" 10 | "k8s.io/client-go/rest" 11 | ) 12 | 13 | // PodCounter counts the number of healthy pods running. 14 | type PodCounter interface { 15 | PodCount(ctx context.Context) (int64, error) 16 | } 17 | 18 | // KubernetesClient is a wrapper over the kubernetes client used to 19 | // communicate with the kubernetes API. 20 | type KubernetesClient struct { 21 | clientset *kubernetes.Clientset 22 | namespace string 23 | app string 24 | } 25 | 26 | // NewKubernetesClient creates a new kubernetes client using in-cluster authn/authz. 27 | func NewKubernetesClient(namespace, app string) (*KubernetesClient, error) { 28 | config, err := rest.InClusterConfig() 29 | if err != nil { 30 | return nil, fmt.Errorf("could not get kubernetes client config: %w", err) 31 | } 32 | 33 | clientset, err := kubernetes.NewForConfig(config) 34 | if err != nil { 35 | return nil, fmt.Errorf("could not create kubernetes client: %w", err) 36 | } 37 | 38 | return &KubernetesClient{ 39 | clientset: clientset, 40 | namespace: namespace, 41 | app: app, 42 | }, nil 43 | } 44 | 45 | // PodCount returns the number of healthy pods. 46 | func (c *KubernetesClient) PodCount(ctx context.Context) (int64, error) { 47 | pods, err := c.clientset.CoreV1().Pods(c.namespace).List(ctx, metav1.ListOptions{ 48 | FieldSelector: "status.phase=Running", 49 | LabelSelector: fmt.Sprintf("app=%s", c.app), 50 | }) 51 | if err != nil { 52 | return 0, err 53 | } 54 | 55 | ready := int64(0) 56 | for _, pod := range pods.Items { 57 | for _, cond := range pod.Status.Conditions { 58 | if cond.Type == corev1.PodReady && cond.Status == corev1.ConditionTrue { 59 | ready++ 60 | break 61 | } 62 | } 63 | } 64 | 65 | return ready, nil 66 | } 67 | -------------------------------------------------------------------------------- /plugins/rrl/cache/cache_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "testing" 4 | 5 | func TestCacheAddGetRemove(t *testing.T) { 6 | c := New(4) 7 | c.Add("1", 1) 8 | 9 | if _, found := c.Get("1"); !found { 10 | t.Fatal("failed to find inserted record") 11 | } 12 | 13 | c.Remove("1") 14 | 15 | if _, found := c.Get("1"); found { 16 | t.Fatal("failed to remove inserted record") 17 | } 18 | } 19 | 20 | func TestCacheUpdateAdd(t *testing.T) { 21 | c := New(4) 22 | 23 | updateFunc := func(el interface{}) interface{} { 24 | iptr := el.(*int) 25 | *iptr += 1 26 | el = iptr 27 | return iptr 28 | } 29 | 30 | addFunc := func() interface{} { 31 | i := 1 32 | return &i 33 | } 34 | 35 | // first call should insert value returned by add 36 | el := c.UpdateAdd("a", updateFunc, addFunc) 37 | 38 | el, found := c.Get("a") 39 | if !found { 40 | t.Fatal("failed to find inserted record") 41 | } 42 | i := *(el.(*int)) 43 | if i != 1 { 44 | t.Fatalf("expected to see inital value of 1, got %v", i) 45 | } 46 | 47 | // second call should increment the value, and return it 48 | el = c.UpdateAdd("a", updateFunc, addFunc) 49 | 50 | i = *(el.(*int)) 51 | 52 | if i != 2 { 53 | t.Fatalf("expected to see return value of 2, got %v", i) 54 | } 55 | el, found = c.Get("a") 56 | if !found { 57 | t.Fatal("failed to find inserted record") 58 | } 59 | i = *(el.(*int)) 60 | if i != 2 { 61 | t.Fatalf("expected to see value incremented to 2, got %v", i) 62 | } 63 | } 64 | 65 | func TestCacheLen(t *testing.T) { 66 | c := New(4) 67 | 68 | c.Add("1", 1) 69 | if l := c.Len(); l != 1 { 70 | t.Fatalf("cache size should %d, got %d", 1, l) 71 | } 72 | 73 | c.Add("1", 1) 74 | if l := c.Len(); l != 1 { 75 | t.Fatalf("cache size should %d, got %d", 1, l) 76 | } 77 | 78 | c.Add("2", 2) 79 | if l := c.Len(); l != 2 { 80 | t.Fatalf("cache size should %d, got %d", 2, l) 81 | } 82 | } 83 | 84 | func BenchmarkCache(b *testing.B) { 85 | b.ReportAllocs() 86 | 87 | c := New(4) 88 | for n := 0; n < b.N; n++ { 89 | c.Add("1", 1) 90 | c.Get("1") 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /plugins/rrl/cache/shard_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "testing" 4 | 5 | func TestShardAddAndGet(t *testing.T) { 6 | s := newShard(4) 7 | s.Add("1", 1) 8 | 9 | if _, found := s.Get("1"); !found { 10 | t.Fatal("Failed to find inserted record") 11 | } 12 | } 13 | 14 | func TestShardLen(t *testing.T) { 15 | s := newShard(4) 16 | 17 | s.Add("1", 1) 18 | if l := s.Len(); l != 1 { 19 | t.Fatalf("Shard size should %d, got %d", 1, l) 20 | } 21 | 22 | s.Add("1", 1) 23 | if l := s.Len(); l != 1 { 24 | t.Fatalf("Shard size should %d, got %d", 1, l) 25 | } 26 | 27 | s.Add("2", 2) 28 | if l := s.Len(); l != 2 { 29 | t.Fatalf("Shard size should %d, got %d", 2, l) 30 | } 31 | } 32 | 33 | func TestShardEvict(t *testing.T) { 34 | s := newShard(1) 35 | s.Add("1", 1) 36 | s.Add("2", 2) 37 | // 1 should be gone 38 | 39 | if _, found := s.Get("1"); found { 40 | t.Fatal("Found item that should have been evicted") 41 | } 42 | } 43 | 44 | func TestShardLenEvict(t *testing.T) { 45 | s := newShard(4) 46 | s.Add("1", 1) 47 | s.Add("2", 1) 48 | s.Add("3", 1) 49 | s.Add("4", 1) 50 | 51 | if l := s.Len(); l != 4 { 52 | t.Fatalf("Shard size should %d, got %d", 4, l) 53 | } 54 | 55 | // This should evict one element 56 | s.Add("5", 1) 57 | if l := s.Len(); l != 4 { 58 | t.Fatalf("Shard size should %d, got %d", 4, l) 59 | } 60 | } 61 | 62 | func TestShardUpdateAdd(t *testing.T) { 63 | s := newShard(1) 64 | 65 | updateFunc := func(el interface{}) interface{} { 66 | iptr := el.(*int) 67 | *iptr += 1 68 | el = iptr 69 | return iptr 70 | } 71 | 72 | addFunc := func() interface{} { 73 | i := 1 74 | return &i 75 | } 76 | 77 | // first call should insert value returned by add 78 | el := s.UpdateAdd("a", updateFunc, addFunc) 79 | 80 | el, found := s.Get("a") 81 | if !found { 82 | t.Fatal("failed to find inserted record") 83 | } 84 | i := el.(*int) 85 | if *i != 1 { 86 | t.Fatalf("expected to see inital value of 1, got %v", *i) 87 | } 88 | 89 | // second call should increment the value, and return it 90 | el = s.UpdateAdd("a", updateFunc, addFunc) 91 | 92 | i = el.(*int) 93 | 94 | if *i != 2 { 95 | t.Fatalf("expected to see return value of 2, got %v", i) 96 | } 97 | el, found = s.Get("a") 98 | if !found { 99 | t.Fatal("failed to find inserted record") 100 | } 101 | i = el.(*int) 102 | if *i != 2 { 103 | t.Fatalf("expected to see value incremented to 2, got %v", i) 104 | } 105 | 106 | // Adding another key should evict the prior key 107 | el = s.UpdateAdd("b", updateFunc, addFunc) 108 | el, found = s.Get("a") 109 | if found { 110 | t.Fatal("expected 'a' to be evicted, but found it") 111 | } 112 | el, found = s.Get("b") 113 | if !found { 114 | t.Fatal("failed to find inserted record") 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /plugins/rrl/rrl_bench_test.go: -------------------------------------------------------------------------------- 1 | package rrl 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/coredns/coredns/plugin" 9 | "github.com/coredns/coredns/plugin/test" 10 | "github.com/miekg/dns" 11 | ) 12 | 13 | func BenchmarkBuildToken(b *testing.B) { 14 | rrl := RRL{ 15 | ipv4PrefixLength: 24, 16 | ipv6PrefixLength: 56, 17 | } 18 | b.ReportAllocs() 19 | b.StartTimer() 20 | for i := 0; i < b.N; i++ { 21 | rrl.buildToken(rTypeResponse, dns.TypeA, "example.org.", "101.102.103.104:4321") 22 | } 23 | } 24 | 25 | func BenchmarkDebit(b *testing.B) { 26 | rrl := RRL{ 27 | window: 15 * second, 28 | responsesInterval: second / 10, 29 | maxTableSize: 10000, 30 | } 31 | rrl.initTable() 32 | b.ReportAllocs() 33 | b.StartTimer() 34 | for i := 0; i < b.N; i++ { 35 | rrl.debit(10, "0/0/101.102.103.0/"+strconv.Itoa(i%10000)+".example.org") 36 | } 37 | } 38 | 39 | func BenchmarkServeDNS(b *testing.B) { 40 | rrl := RRL{ 41 | Zones: []string{"example.org."}, 42 | Next: backendHandler(), 43 | window: 15 * second, 44 | ipv4PrefixLength: 24, 45 | ipv6PrefixLength: 56, 46 | responsesInterval: second / 10, 47 | nxdomainsInterval: second / 10, 48 | errorsInterval: second / 10, 49 | maxTableSize: 1000, 50 | } 51 | rrl.initTable() 52 | 53 | ctx := context.TODO() 54 | 55 | names := []string{"a", "b", "c", "d", "e", "f", "A", "B", "C", "D", "E", "F", "0", "1", "2", "3", "4", "5"} 56 | 57 | var reqs []*dns.Msg 58 | for _, q := range names { 59 | m := new(dns.Msg) 60 | m.SetQuestion(q+".example.org.", dns.TypeA) 61 | reqs = append(reqs, m) 62 | m2 := new(dns.Msg) 63 | m2.SetQuestion(q+".example.org.", dns.TypeAAAA) 64 | reqs = append(reqs, m2) 65 | } 66 | 67 | j := 0 68 | l := len(reqs) 69 | b.ReportAllocs() 70 | b.StartTimer() 71 | 72 | b.Run("with-rrl", func(b *testing.B) { 73 | for i := 0; i < b.N; i++ { 74 | rrl.ServeDNS(ctx, &test.ResponseWriter{}, reqs[j]) 75 | j = (j + 1) % l 76 | } 77 | }) 78 | 79 | b.Run("without-rrl", func(b *testing.B) { 80 | for i := 0; i < b.N; i++ { 81 | rrl.Next.ServeDNS(ctx, &test.ResponseWriter{}, reqs[j]) 82 | j = (j + 1) % l 83 | } 84 | }) 85 | } 86 | 87 | // backendHandler returns a response based on the first character of the qname. 88 | // a-z: NOERROR (NODATA) 89 | // A-Z: NXDOMAIN 90 | // 0-9: SERVFAIL 91 | // Only rcode matters in these benchmarks, the records in the response are 92 | // not important so we don't need to return any records. 93 | func backendHandler() plugin.Handler { 94 | return plugin.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 95 | m := new(dns.Msg) 96 | m.SetReply(r) 97 | m.Response = true 98 | m.RecursionAvailable = true 99 | 100 | qname := m.Question[0].Name 101 | if byte(qname[0]) >= 65 && byte(qname[0]) < 90 { 102 | m.Rcode = dns.RcodeNameError 103 | w.WriteMsg(m) 104 | return dns.RcodeSuccess, nil 105 | } else if byte(qname[0]) >= 48 && byte(qname[0]) < 58 { 106 | m.Rcode = dns.RcodeServerFailure 107 | w.WriteMsg(m) 108 | return dns.RcodeSuccess, nil 109 | } 110 | m.Rcode = dns.RcodeSuccess 111 | w.WriteMsg(m) 112 | return dns.RcodeSuccess, nil 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /plugins/rrl/handler.go: -------------------------------------------------------------------------------- 1 | package rrl 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "sync/atomic" 7 | 8 | "github.com/miekg/dns" 9 | 10 | "github.com/coredns/coredns/plugin" 11 | "github.com/coredns/coredns/plugin/pkg/nonwriter" 12 | "github.com/coredns/coredns/request" 13 | ) 14 | 15 | // Name implements the Handler interface. 16 | func (rrl *RRL) Name() string { return "rrl" } 17 | 18 | // ServeDNS implements the Handler interface. 19 | func (rrl *RRL) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 20 | state := request.Request{W: w, Req: r} 21 | 22 | // only limit rates for applied zones 23 | zone := plugin.Zones(rrl.Zones).Matches(state.Name()) 24 | if zone == "" { 25 | return plugin.NextOrFailure(rrl.Name(), rrl.Next, ctx, w, r) 26 | } 27 | 28 | // Limit request rate 29 | if rrl.requestsInterval != 0 { 30 | // We multiply because allowance is (1s / RPS) 31 | requestsInterval := rrl.requestsInterval * atomic.LoadInt64(&rrl.podCount) 32 | 33 | t := rrl.addrPrefix(state.RemoteAddr()) 34 | b, _, err := rrl.debit(requestsInterval, t) // ignore slip when request limit is exceeded (there is no response to slip) 35 | // if the balance is negative, drop the request (don't write response to client) 36 | if b < 0 && err == nil { 37 | log.Debugf("request rate exceeded from %v (token='%v', balance=%.1f)", state.IP(), t, float64(b)/float64(requestsInterval)) 38 | RequestsExceeded.WithLabelValues(state.IP()).Add(1) 39 | // always return success, to prevent writing of error statuses to client 40 | if !rrl.reportOnly { 41 | return dns.RcodeSuccess, errReqRateLimit 42 | } 43 | } 44 | } 45 | 46 | // Limit response rate 47 | // dont limit responses rates for tcp requests 48 | if state.Proto() == "tcp" { 49 | return plugin.NextOrFailure(rrl.Name(), rrl.Next, ctx, w, r) 50 | } 51 | 52 | // create a non-writer, because we need to look at the response before writing to the client 53 | nw := nonwriter.New(w) 54 | rcode, err := plugin.NextOrFailure(rrl.Name(), rrl.Next, ctx, nw, r) 55 | if err != nil || nw.Msg == nil { 56 | return rcode, err 57 | } 58 | 59 | // get token for response and debit the balance 60 | rtype := responseType(nw.Msg) 61 | t := rrl.responseToToken(ctx, nw, rtype) 62 | allowance := rrl.allowanceForRtype(rtype) 63 | // a zero allowance indicates that no RRL should be performed for the response type, so write the response to client 64 | if allowance == 0 { 65 | err = w.WriteMsg(nw.Msg) 66 | return rcode, err 67 | } 68 | b, slip, err := rrl.debit(allowance, t) 69 | 70 | // if the balance is negative, drop the response (don't write response to client) 71 | if b < 0 && err == nil { 72 | log.Debugf("response rate exceeded to %v for \"%v\" %v (token='%v', balance=%.1f)", nw.RemoteAddr().String(), nw.Msg.Question[0].String(), dns.RcodeToString[nw.Msg.Rcode], t, float64(b)/float64(allowance)) 73 | // always return success, to prevent writing of error statuses to client 74 | ResponsesExceeded.WithLabelValues(state.IP()).Add(1) 75 | if !rrl.reportOnly { 76 | if !slip { 77 | // drop the response. Return success, otherwise server will return an error response to client. 78 | return dns.RcodeSuccess, errRespRateLimit 79 | } 80 | // truncate the response to just the header and let it slip through 81 | nw.Msg.Ns = []dns.RR{} 82 | nw.Msg.Answer = []dns.RR{} 83 | nw.Msg.Extra = []dns.RR{} 84 | nw.Msg.Truncated = true 85 | } 86 | } 87 | 88 | if err != nil { 89 | log.Warningf("%v", err) 90 | } 91 | 92 | // write response to client 93 | err = w.WriteMsg(nw.Msg) 94 | return rcode, err 95 | } 96 | 97 | var ( 98 | errReqRateLimit = errors.New("query rate exceeded the limit") 99 | errRespRateLimit = errors.New("response rate exceeded the limit") 100 | ) 101 | -------------------------------------------------------------------------------- /plugins/rrl/cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "errors" 5 | "hash/fnv" 6 | "sync" 7 | ) 8 | 9 | // Hash returns the FNV hash of what. 10 | func Hash(what []byte) uint64 { 11 | h := fnv.New64() 12 | h.Write(what) 13 | return h.Sum64() 14 | } 15 | 16 | // Cache is cache with a customizable eviction policy. 17 | type Cache struct { 18 | shards [numShards]*shard 19 | } 20 | 21 | type EvictFn func(interface{}) bool 22 | 23 | // evictAll will evict the first item in the shard - effectively a random eviction 24 | // this is the default mode of eviction if not set with SetEvict() 25 | var evictAll = func(interface{}) bool { return true } 26 | 27 | // shard is a cache with customizable eviction policy. 28 | type shard struct { 29 | items map[string]interface{} 30 | size int 31 | evictable EvictFn 32 | 33 | sync.RWMutex 34 | } 35 | 36 | // New returns a new cache. 37 | func New(size int) *Cache { 38 | ssize := size / numShards 39 | if ssize < 4 { 40 | ssize = 4 41 | } 42 | 43 | c := &Cache{} 44 | 45 | // Initialize all the shards 46 | for i := 0; i < numShards; i++ { 47 | c.shards[i] = newShard(ssize) 48 | } 49 | return c 50 | } 51 | 52 | func (c *Cache) SetEvict(e EvictFn) { 53 | for _, s := range c.shards { 54 | s.evictable = e 55 | } 56 | } 57 | 58 | func keyShard(key string) uint64 { 59 | return Hash([]byte(key)) & (numShards - 1) 60 | } 61 | 62 | // Add adds a new element to the cache. If the element already exists it is overwritten. 63 | func (c *Cache) Add(key string, el interface{}) error { 64 | return c.shards[keyShard(key)].Add(key, el) 65 | } 66 | 67 | func (c *Cache) UpdateAdd(key string, update func(interface{}) interface{}, add func() interface{}) interface{} { 68 | return c.shards[keyShard(key)].UpdateAdd(key, update, add) 69 | } 70 | 71 | // Get looks up element index under key. 72 | func (c *Cache) Get(key string) (interface{}, bool) { 73 | return c.shards[keyShard(key)].Get(key) 74 | } 75 | 76 | // Remove removes the element indexed with key. 77 | func (c *Cache) Remove(key string) { 78 | c.shards[keyShard(key)].Remove(key) 79 | } 80 | 81 | // Len returns an estimate number of elements in the cache. 82 | // This is an estimate, because each shard is locked one at a time, and 83 | // items can be added/removed from other shards as each shard is counted. 84 | func (c *Cache) Len() int { 85 | l := 0 86 | for _, s := range c.shards { 87 | l += s.Len() 88 | } 89 | return l 90 | } 91 | 92 | // newShard returns a new shard with size. 93 | func newShard(size int) *shard { 94 | return &shard{ 95 | items: make(map[string]interface{}), 96 | size: size, 97 | evictable: evictAll, 98 | } 99 | } 100 | 101 | // Add adds element indexed by key into the cache. Any existing element is overwritten 102 | func (s *shard) Add(key string, el interface{}) error { 103 | s.Lock() 104 | if s.len() >= s.size && !s.evict() { 105 | s.Unlock() 106 | return errors.New("failed to add item, shard full") 107 | } 108 | 109 | s.items[key] = &el 110 | s.Unlock() 111 | return nil 112 | } 113 | 114 | // Remove locks the shard and removes the element indexed by key from the cache. 115 | func (s *shard) Remove(key string) { 116 | s.Lock() 117 | s.remove(key) 118 | s.Unlock() 119 | } 120 | 121 | // remove removes the element indexed by key from the cache. 122 | func (s *shard) remove(key string) { 123 | delete(s.items, key) 124 | } 125 | 126 | // evict removes the first evictable item from the shard. If no items are evictable, return false. 127 | func (s *shard) evict() bool { 128 | for key, item := range s.items { 129 | if !s.evictable(item) { 130 | continue 131 | } 132 | s.remove(key) 133 | return true 134 | } 135 | return false 136 | } 137 | 138 | // Get looks up the element indexed under key. 139 | func (s *shard) Get(key string) (interface{}, bool) { 140 | s.RLock() 141 | el, found := s.items[key] 142 | s.RUnlock() 143 | if found { 144 | return el, true 145 | } 146 | return nil, false 147 | } 148 | 149 | // UpdateAdd executes the function `update` on the element indexed under key. 150 | // If key does not exist, then it is added, with a value equal to the result of function `add`. 151 | func (s *shard) UpdateAdd(key string, update func(interface{}) interface{}, add func() interface{}) interface{} { 152 | s.Lock() 153 | defer s.Unlock() 154 | el, found := s.items[key] 155 | if found { 156 | resp := update(el) 157 | return resp 158 | } 159 | l := len(s.items) 160 | if l >= s.size { 161 | if !s.evict() { 162 | return errors.New("failed to add item, shard full") 163 | } 164 | } 165 | newItem := add() 166 | s.items[key] = newItem 167 | return nil 168 | } 169 | 170 | // Len returns the current length of the cache. 171 | func (s *shard) Len() int { 172 | s.RLock() 173 | l := s.len() 174 | s.RUnlock() 175 | return l 176 | } 177 | 178 | // len returns the current length of the cache. 179 | func (s *shard) len() int { 180 | l := len(s.items) 181 | return l 182 | } 183 | 184 | const numShards = 256 185 | -------------------------------------------------------------------------------- /plugins/rrl/handler_test.go: -------------------------------------------------------------------------------- 1 | package rrl 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/coredns/coredns/plugin/pkg/dnstest" 8 | 9 | "github.com/miekg/dns" 10 | 11 | "github.com/coredns/coredns/plugin/test" 12 | ) 13 | 14 | func TestServeDNSRateLimit(t *testing.T) { 15 | tc := test.Case{Qname: "example.com", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess} 16 | 17 | rrl := defaultRRL() 18 | rrl.Next = test.HandlerFunc(fixedAnswer) 19 | rrl.Zones = []string{"example.com."} 20 | rrl.window = 2 * second 21 | rrl.responsesInterval = second 22 | rrl.initTable() 23 | 24 | ctx := context.TODO() 25 | 26 | w := dnstest.NewRecorder(&test.ResponseWriter{}) 27 | _, err := rrl.ServeDNS(ctx, w, tc.Msg()) 28 | if err != nil { 29 | t.Errorf("expected no error, got: %v", err) 30 | } 31 | // ensure that the message was written to the client 32 | if w.Len == 0 { 33 | t.Errorf("expected message to be written to client") 34 | } 35 | 36 | // redo the query a couple of times to deplete balance to negative 37 | for i := 0; i < 2; i++ { 38 | w = dnstest.NewRecorder(&test.ResponseWriter{}) 39 | _, err := rrl.ServeDNS(ctx, w, tc.Msg()) 40 | if err == nil { 41 | t.Error("expected rate limit error, got no error") 42 | } 43 | } 44 | 45 | // ensure that the last message was not written to the client 46 | if w.Len != 0 { 47 | t.Errorf("expected message to be dropped") 48 | } 49 | } 50 | 51 | func TestServeDNStcp(t *testing.T) { 52 | tc := test.Case{Qname: "example.com", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess} 53 | 54 | rrl := defaultRRL() 55 | rrl.Next = test.HandlerFunc(fixedAnswer) 56 | rrl.Zones = []string{"example.com."} 57 | rrl.window = 2 * second 58 | rrl.responsesInterval = second 59 | rrl.initTable() 60 | 61 | ctx := context.TODO() 62 | 63 | var w *dnstest.Recorder 64 | 65 | // deplete balance to what would be negative if we were rate limiting 66 | for i := 0; i < 3; i++ { 67 | w = dnstest.NewRecorder(&test.ResponseWriter{TCP: true}) 68 | _, err := rrl.ServeDNS(ctx, w, tc.Msg()) 69 | if err != nil { 70 | t.Errorf("expected no error, got: %v", err) 71 | } 72 | } 73 | 74 | // ensure that the last message was written to the client 75 | if w.Len == 0 { 76 | t.Errorf("expected message to be written to client") 77 | } 78 | 79 | } 80 | 81 | func TestServeDNSForeignZone(t *testing.T) { 82 | tc := test.Case{Qname: "example.com", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess} 83 | 84 | rrl := defaultRRL() 85 | rrl.Next = test.HandlerFunc(fixedAnswer) 86 | rrl.Zones = []string{"not.example.com."} 87 | rrl.window = 2 * second 88 | rrl.responsesInterval = second 89 | rrl.initTable() 90 | 91 | ctx := context.TODO() 92 | 93 | var w *dnstest.Recorder 94 | 95 | // deplete balance to what would be negative if we were rate limiting 96 | for i := 0; i < 3; i++ { 97 | w = dnstest.NewRecorder(&test.ResponseWriter{}) 98 | _, err := rrl.ServeDNS(ctx, w, tc.Msg()) 99 | if err != nil { 100 | t.Errorf("expected no error, got: %v", err) 101 | } 102 | } 103 | 104 | // ensure that the last message was written to the client 105 | if w.Len == 0 { 106 | t.Errorf("expected message to be written to client") 107 | } 108 | 109 | } 110 | func TestServeDNSZeroAllowance(t *testing.T) { 111 | tc := test.Case{Qname: "example.com", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess} 112 | 113 | rrl := defaultRRL() 114 | rrl.Next = test.HandlerFunc(fixedAnswer) 115 | rrl.Zones = []string{"example.com."} 116 | rrl.window = 2 * second 117 | rrl.responsesInterval = 0 // zero allowance should disable rate limiting 118 | rrl.initTable() 119 | 120 | ctx := context.TODO() 121 | 122 | var w *dnstest.Recorder 123 | 124 | // deplete balance to what would be negative if we were rate limiting 125 | for i := 0; i < 3; i++ { 126 | w = dnstest.NewRecorder(&test.ResponseWriter{}) 127 | _, err := rrl.ServeDNS(ctx, w, tc.Msg()) 128 | if err != nil { 129 | t.Errorf("expected no error, got: %v", err) 130 | } 131 | } 132 | 133 | // ensure that the last message was written to the client 134 | if w.Len == 0 { 135 | t.Errorf("expected message to be written to client") 136 | } 137 | 138 | } 139 | 140 | func TestServeDNSRateLimitSlips(t *testing.T) { 141 | t.Run("0", func(t *testing.T) { 142 | testServeDNSRateLimitSlip(t, 0, 0) 143 | }) 144 | t.Run("1", func(t *testing.T) { 145 | testServeDNSRateLimitSlip(t, 1, 10) 146 | }) 147 | t.Run("2", func(t *testing.T) { 148 | testServeDNSRateLimitSlip(t, 2, 5) 149 | }) 150 | t.Run("5", func(t *testing.T) { 151 | testServeDNSRateLimitSlip(t, 5, 2) 152 | }) 153 | t.Run("10", func(t *testing.T) { 154 | testServeDNSRateLimitSlip(t, 10, 1) 155 | }) 156 | } 157 | 158 | func testServeDNSRateLimitSlip(t *testing.T, slipRatio uint, expectedSlips int) { 159 | tc := test.Case{Qname: "example.com", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess} 160 | 161 | rrl := defaultRRL() 162 | rrl.Next = test.HandlerFunc(fixedAnswer) 163 | rrl.Zones = []string{"example.com."} 164 | rrl.window = 2 * second 165 | rrl.responsesInterval = second 166 | rrl.slipRatio = slipRatio 167 | rrl.initTable() 168 | 169 | ctx := context.TODO() 170 | 171 | w := dnstest.NewRecorder(&test.ResponseWriter{}) 172 | _, err := rrl.ServeDNS(ctx, w, tc.Msg()) 173 | if err != nil { 174 | t.Errorf("expected no error, got: %v", err) 175 | } 176 | // ensure that the message was written to the client 177 | if w.Len == 0 { 178 | t.Errorf("expected message to be written to client") 179 | } 180 | 181 | var responses []*dns.Msg 182 | for i := 0; i < 10; i++ { 183 | w = dnstest.NewRecorder(&test.ResponseWriter{}) 184 | rrl.ServeDNS(ctx, w, tc.Msg()) 185 | if w.Msg == nil { 186 | continue 187 | } 188 | responses = append(responses, w.Msg) 189 | } 190 | 191 | for _, r := range responses { 192 | if !r.Truncated { 193 | t.Errorf("Slipped message not marked truncated") 194 | } 195 | if len(r.Answer) != 0 { 196 | t.Errorf("Slipped message Answer section is not empty") 197 | } 198 | } 199 | 200 | if len(responses) != expectedSlips { 201 | t.Errorf("expected %v messages to slip, got %v", expectedSlips, len(responses)) 202 | } 203 | } 204 | 205 | func fixedAnswer(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 206 | r.Answer = []dns.RR{test.A("example.com. 5 IN A 1.2.3.4")} 207 | w.WriteMsg(r) 208 | return dns.RcodeSuccess, nil 209 | } 210 | -------------------------------------------------------------------------------- /plugins/rrl/setup.go: -------------------------------------------------------------------------------- 1 | package rrl 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/coredns/caddy" 8 | "github.com/coredns/coredns/core/dnsserver" 9 | "github.com/coredns/coredns/plugin" 10 | clog "github.com/coredns/coredns/plugin/pkg/log" 11 | ) 12 | 13 | var log = clog.NewWithPlugin("rrl") 14 | 15 | func init() { 16 | caddy.RegisterPlugin("rrl", caddy.Plugin{ 17 | ServerType: "dns", 18 | Action: setup, 19 | }) 20 | } 21 | 22 | func setup(c *caddy.Controller) error { 23 | e, err := rrlParse(c) 24 | if err != nil { 25 | return plugin.Error("rrl", err) 26 | } 27 | 28 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 29 | e.Next = next 30 | return e 31 | }) 32 | 33 | return nil 34 | } 35 | 36 | func defaultRRL() RRL { 37 | return RRL{ 38 | window: 15 * second, 39 | ipv4PrefixLength: 24, 40 | ipv6PrefixLength: 56, 41 | maxTableSize: 100000, 42 | podCount: 1, 43 | } 44 | } 45 | 46 | func rrlParse(c *caddy.Controller) (*RRL, error) { 47 | rrl := defaultRRL() 48 | 49 | var ( 50 | nodataIntervalSet bool 51 | nxdomainsIntervalSet bool 52 | referralsIntervalSet bool 53 | errorsIntervalSet bool 54 | scanKubernetesApp string 55 | scanKubernetesNamespace string 56 | ) 57 | 58 | for c.Next() { 59 | rrl.Zones = plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys) 60 | 61 | if c.NextBlock() { 62 | for { 63 | switch c.Val() { 64 | case "window": 65 | args := c.RemainingArgs() 66 | if len(args) != 1 { 67 | return nil, c.ArgErr() 68 | } 69 | w, err := strconv.ParseFloat(args[0], 64) 70 | if err != nil { 71 | return nil, c.Errf("%v invalid value. %v", c.Val(), err) 72 | } 73 | if w <= 0 { 74 | return nil, c.Err("window must be greater than zero") 75 | } 76 | rrl.window = int64(w * second) 77 | case "ipv4-prefix-length": 78 | args := c.RemainingArgs() 79 | if len(args) != 1 { 80 | return nil, c.ArgErr() 81 | } 82 | i, err := strconv.Atoi(c.Val()) 83 | if err != nil { 84 | return nil, c.Errf("%v invalid value. %v", c.Val(), err) 85 | } 86 | if i <= 0 || i > 32 { 87 | return nil, c.Errf("%v must be between 1 and 32", c.Val()) 88 | } 89 | rrl.ipv4PrefixLength = i 90 | case "ipv6-prefix-length": 91 | args := c.RemainingArgs() 92 | if len(args) != 1 { 93 | return nil, c.ArgErr() 94 | } 95 | i, err := strconv.Atoi(c.Val()) 96 | if err != nil { 97 | return nil, c.Errf("%v invalid value. %v", c.Val(), err) 98 | } 99 | if i <= 0 || i > 128 { 100 | return nil, c.Errf("%v must be between 1 and 128", c.Val()) 101 | } 102 | rrl.ipv6PrefixLength = i 103 | case "responses-per-second": 104 | i, err := getIntervalArg(c) 105 | if err != nil { 106 | return nil, err 107 | } 108 | rrl.responsesInterval = i 109 | case "nodata-per-second": 110 | i, err := getIntervalArg(c) 111 | if err != nil { 112 | return nil, err 113 | } 114 | rrl.nodataInterval = i 115 | nodataIntervalSet = true 116 | case "nxdomains-per-second": 117 | i, err := getIntervalArg(c) 118 | if err != nil { 119 | return nil, err 120 | } 121 | rrl.nxdomainsInterval = i 122 | nxdomainsIntervalSet = true 123 | case "referrals-per-second": 124 | i, err := getIntervalArg(c) 125 | if err != nil { 126 | return nil, err 127 | } 128 | rrl.referralsInterval = i 129 | referralsIntervalSet = true 130 | case "errors-per-second": 131 | i, err := getIntervalArg(c) 132 | if err != nil { 133 | return nil, err 134 | } 135 | rrl.errorsInterval = i 136 | errorsIntervalSet = true 137 | case "slip-ratio": 138 | args := c.RemainingArgs() 139 | if len(args) != 1 { 140 | return nil, c.ArgErr() 141 | } 142 | i, err := strconv.Atoi(c.Val()) 143 | if err != nil { 144 | return nil, c.Errf("slip-ratio '%v' invalid value. %v", c.Val(), err) 145 | } 146 | if i < 0 || i > 10 { 147 | return nil, c.Errf("slip-ratio '%v' must be between 0 and 10", c.Val()) 148 | } 149 | rrl.slipRatio = uint(i) 150 | case "requests-per-second": 151 | i, err := getIntervalArg(c) 152 | if err != nil { 153 | return nil, err 154 | } 155 | rrl.requestsInterval = i 156 | case "max-table-size": 157 | args := c.RemainingArgs() 158 | if len(args) != 1 { 159 | return nil, c.ArgErr() 160 | } 161 | i, err := strconv.Atoi(args[0]) 162 | if err != nil { 163 | return nil, c.Errf("%v invalid value. %v", c.Val(), err) 164 | } 165 | if i < 0 { 166 | return nil, c.Errf("%v cannot be negative", c.Val()) 167 | } 168 | rrl.maxTableSize = i 169 | case "report-only": 170 | args := c.RemainingArgs() 171 | if len(args) > 0 { 172 | return nil, c.ArgErr() 173 | } 174 | rrl.reportOnly = true 175 | case "node-count-from-kubernetes-app": 176 | args := c.RemainingArgs() 177 | if len(args) != 2 { 178 | return nil, c.ArgErr() 179 | } 180 | scanKubernetesNamespace = args[0] 181 | scanKubernetesApp = args[1] 182 | default: 183 | if c.Val() != "}" { 184 | return nil, c.Errf("unknown property '%s'", c.Val()) 185 | } 186 | } 187 | 188 | if !c.Next() { 189 | break 190 | } 191 | } 192 | } 193 | 194 | // If any allowance intervals were not set, default them to responsesInterval 195 | if !nodataIntervalSet { 196 | rrl.nodataInterval = rrl.responsesInterval 197 | } 198 | if !nxdomainsIntervalSet { 199 | rrl.nxdomainsInterval = rrl.responsesInterval 200 | } 201 | if !referralsIntervalSet { 202 | rrl.referralsInterval = rrl.responsesInterval 203 | } 204 | if !errorsIntervalSet { 205 | rrl.errorsInterval = rrl.responsesInterval 206 | } 207 | 208 | // initialize table 209 | rrl.initTable() 210 | 211 | if scanKubernetesApp != "" && scanKubernetesNamespace != "" { 212 | client, err := NewKubernetesClient(scanKubernetesNamespace, scanKubernetesApp) 213 | if err != nil { 214 | return nil, fmt.Errorf("failed to create kubernetes client: %w", err) 215 | } 216 | 217 | go rrl.scanKubernetesPods(client) 218 | } 219 | 220 | return &rrl, nil 221 | } 222 | return nil, nil 223 | } 224 | 225 | func getIntervalArg(c *caddy.Controller) (int64, error) { 226 | args := c.RemainingArgs() 227 | if len(args) != 1 { 228 | return 0, c.ArgErr() 229 | } 230 | rps, err := strconv.ParseFloat(args[0], 64) 231 | if err != nil { 232 | return 0, c.Errf("%v invalid value. %v", c.Val(), err) 233 | } 234 | if rps < 0 { 235 | return 0, c.Errf("%v cannot be negative", c.Val()) 236 | } 237 | if rps == 0.0 { 238 | return 0, nil 239 | } else { 240 | return int64(second / rps), nil 241 | } 242 | } 243 | 244 | const second = 1000000000 245 | -------------------------------------------------------------------------------- /plugins/rrl/rrl_test.go: -------------------------------------------------------------------------------- 1 | package rrl 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/replit/rrl/plugins/rrl/cache" 8 | 9 | "github.com/miekg/dns" 10 | 11 | "github.com/coredns/coredns/plugin/test" 12 | ) 13 | 14 | func TestDebit(t *testing.T) { 15 | 16 | rrl := defaultRRL() 17 | rrl.window = 5 * second 18 | rrl.responsesInterval = second / 10 19 | rrl.nxdomainsInterval = second / 100 20 | rrl.table = cache.New(rrl.maxTableSize) 21 | 22 | _, _, err := rrl.debit(rrl.allowanceForRtype(rTypeResponse), "token1") 23 | if err != nil { 24 | t.Errorf("got error: %v", err) 25 | } 26 | ra, _ := rrl.table.Get("token1") 27 | bal := time.Now().UnixNano() - ra.(*ResponseAccount).allowTime 28 | if bal < second-rrl.responsesInterval { 29 | t.Errorf("expected balance not less than %v, got %v", second-rrl.responsesInterval, bal) 30 | } 31 | 32 | bal, _, err = rrl.debit(rrl.allowanceForRtype(rTypeResponse), "token1") 33 | if bal > second-rrl.responsesInterval { 34 | t.Errorf("expected balance of < %v, got %v", second-rrl.responsesInterval, bal) 35 | } 36 | 37 | _, _, err = rrl.debit(rrl.allowanceForRtype(rTypeNxdomain), "token2") 38 | if err != nil { 39 | t.Errorf("got error: %v", err) 40 | } 41 | time.Sleep(time.Second) // sleep 1 second, balance should max out 42 | bal, _, err = rrl.debit(rrl.allowanceForRtype(rTypeNxdomain), "token2") 43 | if bal != second-rrl.nxdomainsInterval { 44 | t.Errorf("expected balance of %v, got %v", rrl.window-rrl.nxdomainsInterval, bal) 45 | } 46 | 47 | } 48 | 49 | func TestResponseType(t *testing.T) { 50 | tests := []struct { 51 | msg dns.Msg 52 | expected byte 53 | }{ 54 | { 55 | msg: dns.Msg{ 56 | MsgHdr: dns.MsgHdr{Rcode: dns.RcodeSuccess}, 57 | Answer: []dns.RR{ 58 | test.A("example.com. 5 IN A 1.2.3.4"), 59 | }}, 60 | expected: rTypeResponse, 61 | }, 62 | { 63 | msg: dns.Msg{ 64 | MsgHdr: dns.MsgHdr{Rcode: dns.RcodeSuccess}, 65 | Answer: []dns.RR{}, 66 | }, 67 | expected: rTypeNodata, 68 | }, 69 | { 70 | msg: dns.Msg{ 71 | MsgHdr: dns.MsgHdr{Rcode: dns.RcodeSuccess}, 72 | Ns: []dns.RR{ 73 | test.NS("example.com. 12345 IN NS ns1.example.com."), 74 | }, 75 | }, 76 | expected: rTypeReferral, 77 | }, 78 | { 79 | msg: dns.Msg{ 80 | MsgHdr: dns.MsgHdr{Rcode: dns.RcodeNameError}, 81 | Answer: []dns.RR{}, 82 | }, 83 | expected: rTypeNxdomain, 84 | }, 85 | { 86 | msg: dns.Msg{ 87 | MsgHdr: dns.MsgHdr{Rcode: dns.RcodeFormatError}, 88 | Answer: []dns.RR{}, 89 | }, 90 | expected: rTypeError, 91 | }, 92 | } 93 | for _, c := range tests { 94 | got := responseType(&c.msg) 95 | if got != c.expected { 96 | t.Errorf("expected '%v', got '%v'", c.expected, got) 97 | } 98 | } 99 | } 100 | 101 | func TestAllowanceForRtype(t *testing.T) { 102 | rtypes := []uint8{rTypeResponse, rTypeNodata, rTypeError, rTypeNxdomain, rTypeReferral} 103 | 104 | rrl := defaultRRL() 105 | rrl.responsesInterval = 100 106 | rrl.nodataInterval = 100 107 | rrl.nxdomainsInterval = 100 108 | rrl.referralsInterval = 100 109 | rrl.errorsInterval = 100 110 | 111 | for _, rtype := range rtypes { 112 | got := rrl.allowanceForRtype(rtype) 113 | if got != 100 { 114 | t.Errorf("expected '%v', got '%v'", 100, got) 115 | } 116 | } 117 | } 118 | 119 | func TestBuildToken(t *testing.T) { 120 | tests := []struct { 121 | rtype uint8 122 | qtype uint16 123 | name string 124 | remoteAddr string 125 | expected string 126 | }{ 127 | { 128 | rtype: rTypeResponse, 129 | qtype: dns.TypeA, 130 | name: "example.com", 131 | remoteAddr: "1.2.3.4:1234", 132 | expected: "1.2.3.0/0/1/example.com", 133 | }, 134 | { 135 | rtype: rTypeNodata, 136 | qtype: dns.TypeA, 137 | name: "example.com", 138 | remoteAddr: "1.2.3.4:1234", 139 | expected: "1.2.3.0/1//example.com", 140 | }, 141 | { 142 | rtype: rTypeError, 143 | qtype: dns.TypeA, 144 | name: "example.com", 145 | remoteAddr: "1.2.3.4:1234", 146 | expected: "1.2.3.0/4//", 147 | }, 148 | { 149 | rtype: rTypeNxdomain, 150 | qtype: dns.TypeA, 151 | name: "example.com", 152 | remoteAddr: "1.2.3.4:1234", 153 | expected: "1.2.3.0/2//example.com", 154 | }, 155 | { 156 | rtype: rTypeReferral, 157 | qtype: dns.TypeA, 158 | name: "example.com", 159 | remoteAddr: "1.2.3.4:1234", 160 | expected: "1.2.3.0/3/1/example.com", 161 | }, 162 | } 163 | rrl := defaultRRL() 164 | for _, c := range tests { 165 | got := rrl.buildToken(c.rtype, c.qtype, c.name, c.remoteAddr) 166 | if got != c.expected { 167 | t.Errorf("expected '%v', got '%v'", c.expected, got) 168 | } 169 | } 170 | } 171 | 172 | func TestAddrPrefix(t *testing.T) { 173 | tests := []struct { 174 | ipv4Prefix, ipv6Prefix int 175 | remoteAddr, expected string 176 | }{ 177 | { 178 | ipv4Prefix: 24, 179 | remoteAddr: "1.2.3.4:1234", 180 | expected: "1.2.3.0", 181 | }, 182 | { 183 | ipv6Prefix: 56, 184 | remoteAddr: "[1234:5678::1]:80", 185 | expected: "1234:5678::", 186 | }, 187 | { 188 | ipv4Prefix: 8, 189 | remoteAddr: "1.2.3.4:1234", 190 | expected: "1.0.0.0", 191 | }, 192 | { 193 | ipv6Prefix: 16, 194 | remoteAddr: "[1234:5678::1]:80", 195 | expected: "1234::", 196 | }, 197 | { 198 | ipv4Prefix: 32, 199 | remoteAddr: "1.2.3.4:1234", 200 | expected: "1.2.3.4", 201 | }, 202 | { 203 | ipv6Prefix: 128, 204 | remoteAddr: "[1234:5678::1]:80", 205 | expected: "1234:5678::1", 206 | }, 207 | } 208 | 209 | rrl := defaultRRL() 210 | for _, c := range tests { 211 | rrl.ipv4PrefixLength = c.ipv4Prefix 212 | rrl.ipv6PrefixLength = c.ipv6Prefix 213 | got := rrl.addrPrefix(c.remoteAddr) 214 | if got != c.expected { 215 | t.Errorf("expected '%v', got '%v'", c.expected, got) 216 | } 217 | } 218 | } 219 | 220 | func TestPodCount(t *testing.T) { 221 | responseRPS := int64(10) 222 | nxdomainsRPS := int64(100) 223 | 224 | rrl := defaultRRL() 225 | rrl.window = 5 * second 226 | rrl.responsesInterval = second / (responseRPS * 2) 227 | rrl.nxdomainsInterval = second / (nxdomainsRPS * 2) 228 | rrl.podCount = 2 229 | rrl.table = cache.New(rrl.maxTableSize) 230 | 231 | _, _, err := rrl.debit(rrl.allowanceForRtype(rTypeResponse), "token1") 232 | if err != nil { 233 | t.Errorf("got error: %v", err) 234 | } 235 | ra, _ := rrl.table.Get("token1") 236 | bal := time.Now().UnixNano() - ra.(*ResponseAccount).allowTime 237 | if bal < second-(second/responseRPS) { 238 | t.Errorf("expected balance not less than %v, got %v", second-rrl.responsesInterval, bal) 239 | } 240 | 241 | bal, _, err = rrl.debit(rrl.allowanceForRtype(rTypeResponse), "token1") 242 | if bal > second-(second/responseRPS) { 243 | t.Errorf("expected balance of < %v, got %v", second-rrl.responsesInterval, bal) 244 | } 245 | 246 | _, _, err = rrl.debit(rrl.allowanceForRtype(rTypeNxdomain), "token2") 247 | if err != nil { 248 | t.Errorf("got error: %v", err) 249 | } 250 | time.Sleep(time.Second) // sleep 1 second, balance should max out 251 | bal, _, err = rrl.debit(rrl.allowanceForRtype(rTypeNxdomain), "token2") 252 | if bal != second-(second/nxdomainsRPS) { 253 | t.Errorf("expected balance of %v, got %v", rrl.window-rrl.nxdomainsInterval, bal) 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rrl 2 | 3 | ## Name 4 | 5 | *rrl* - provides BIND-like Response Rate Limiting to help mitigate DNS 6 | amplification attacks. *rrl* also allows request rate limiting. 7 | 8 | ## Description 9 | 10 | The *rrl* plugin tracks response rates per category of response. 11 | The category of a given response consists of the following: 12 | 13 | * Prefix of the client IP (per the ipv4/6-prefix-length) 14 | * Requested name (qname) excluding response type of error (see response type below) 15 | * Requested type (qtype) excluding response type of error (see response type below) 16 | * Response type (each corresponding to the configurable per-second allowances) 17 | * response - for positive responses that contain answers 18 | * nodata - for NODATA responses 19 | * nxdomain - for NXDOMAIN responses 20 | * referrals - for referrals or delegations 21 | * error - for all DNS errors (except NXDOMAIN) 22 | 23 | 24 | To better protect against attacks using invalid requests, requested name 25 | and type are not categorized separately for error type requests. In other 26 | words, all error responses are limited collectively per client, regardless 27 | of qname or qtype. 28 | 29 | Each category has an account balance which is credited at a rate of the 30 | configured *per-second* allowance for that response type, and debited each 31 | time a response in that category would be sent to a client. When an account 32 | balance is negative, responses in the category are dropped until the balance 33 | goes non-negative. Account balances cannot be more positive than *per-second* 34 | allowance, and cannot be more negative than *window* * *per-second* allowance. 35 | 36 | The response rate limiting implementation intends to replicate the behavior 37 | of BIND 9's response rate limiting feature. 38 | 39 | When limiting requests, the category of each request is determined by the 40 | prefix of the client IP (per the ipv4/6-prefix-length). 41 | 42 | 43 | ## Syntax 44 | 45 | ``` 46 | rrl [ZONES...] { 47 | window SECONDS 48 | ipv4-prefix-length LENGTH 49 | ipv6-prefix-length LENGTH 50 | responses-per-second ALLOWANCE 51 | nodata-per-second ALLOWANCE 52 | nxdomains-per-second ALLOWANCE 53 | referrals-per-second ALLOWANCE 54 | errors-per-second ALLOWANCE 55 | slip-ratio N 56 | requests-per-second ALLOWANCE 57 | max-table-size SIZE 58 | report-only 59 | } 60 | ``` 61 | 62 | * `window SECONDS` - the rolling window in **SECONDS** during which response rates are tracked. Default 15. 63 | 64 | * `ipv4-prefix-length LENGTH` - the prefix **LENGTH** in bits to use for identifying a ipv4 client. Default 24. 65 | 66 | * `ipv6-prefix-length LENGTH` - the prefix **LENGTH** in bits to use for identifying a ipv6 client. Default 56. 67 | 68 | * `responses-per-second ALLOWANCE` - the number of positive responses allowed per second. An **ALLOWANCE** of 0 disables rate limiting of positive responses. Default 0. 69 | 70 | * `nodata-per-second ALLOWANCE` - the number of `NODATA` responses allowed per second. An **ALLOWANCE** of 0 disables rate limiting of NODATA responses. Defaults to responses-per-second. 71 | 72 | * `nxdomains-per-second ALLOWANCE` - the number of `NXDOMAIN` responses allowed per second. An **ALLOWANCE** of 0 disables rate limiting of NXDOMAIN responses. Defaults to responses-per-second. 73 | 74 | * `referrals-per-second ALLOWANCE` - the number of referral responses allowed per second. An **ALLOWANCE** of 0 disables rate limiting of referral responses. Defaults to responses-per-second. 75 | 76 | * `errors-per-second ALLOWANCE` - the number of error responses allowed per second (excluding NXDOMAIN). An **ALLOWANCE** of 0 disables rate limiting of error responses. Defaults to responses-per-second. 77 | 78 | * `slip-ratio N` - Let every **N**th dropped response slip through truncated. Responses that slip through are marked 79 | truncated and have all sections emptied before being relayed. A client receiving a truncated response will retry using TCP, 80 | which is not subject to response rate limiting. This provides a way for clients making legitimate requests to get an 81 | answer while their IP prefix is being blocked by response rate limiting. For **N** = 1 slip every dropped response through; 82 | **N** = 4 slip every 4th dropped response through; etc. The default is **N** = 0, don't slip any responses through. 83 | 84 | * `requests-per-second ALLOWANCE` - the number of requests allowed per second. An **ALLOWANCE** of 0 disables rate limiting of requests. Default 0. 85 | 86 | * `max-table-size SIZE` - the maximum number of responses to be tracked at one time. When exceeded, rrl stops rate limiting new responses. Defaults to 100000. 87 | 88 | * `report-only` - Do not drop requests/responses when rates are exceeded, only log metrics. Defaults to false. 89 | 90 | ## Mitigate Wildcard Flooding with the metadata Plugin 91 | 92 | An attacker can evade _rrl_ rate limits when launching a reflection attack if they know of the existence of a wildcard record. 93 | In a nutshell, the attacker can spread the reflection attack across an unlimited number of unique query names synthesized by 94 | a wildcard keeping the rate of responses for each individual name under limits. 95 | To protect against this, enable the _metadata_ plugin. When the _metadata_ plugin is enabled, _rrl_ will account for all 96 | responses synthesized by known wildcards under the parent domain of the wildcard. e.g. Both `a.example.org.` and 97 | `a.example.org.` would be accounted for as `example.org.`, if they are synthesized from the wildcard record `*.example.org.` 98 | This approach follows BIND9's solution to the same problem. 99 | 100 | *Important:* 101 | * The _metadata_ plugin MUST be enabled for this to work. 102 | * CoreDNS MUST be >= *TBD*. Plugins in CoreDNS do not produce the required metadata until this version. 103 | * This cannot protect against attacks leveraging wildcard records hosted by upstream nameservers. 104 | * External plugins that can synthesize wildcard responses must be updated produce the metadata `zone/wildcard` in order 105 | to protect against flooding with wildcards it serves. 106 | * Some plugins such as `rewrite` and `template` can emulate wildcard-like behavior in such a way that they can be leveraged 107 | in the same way by an attacker to launch an undetected reflection attack. This is possible if the plugin produces a 108 | positive answer for an unbounded set of questions. `rewrite` and `template` do not produce the metadata required to 109 | mitigate wildcard flooding. 110 | 111 | ## Metrics 112 | 113 | If monitoring is enabled (via the *prometheus* plugin) then the following metrics are exported: 114 | 115 | * `coredns_rrl_responses_exceeded_total{client_ip}` - Counter of responses exceeding QPS limit. 116 | * `coredns_rrl_requests_exceeded_total{client_ip}` - Counter of requests exceeding QPS limit. 117 | 118 | ## External Plugin 119 | 120 | *RRL* is an *external* plugin, which means it is not included in CoreDNS releases. To use *rrl*, you'll need to build a CoreDNS image with *rrl* included (near the top of the plugin list). In a nutshell you'll need to: 121 | * Clone https://github.com/coredns/coredns 122 | * Add this plugin to [plugin.cfg](https://github.com/coredns/coredns/blob/master/plugin.cfg) per instructions therein. 123 | * `make -f Makefile.release DOCKER=your-docker-repo release` 124 | * `make -f Makefile.release DOCKER=your-docker-repo docker` 125 | * `make -f Makefile.release DOCKER=your-docker-repo docker-push` 126 | 127 | ## Examples 128 | 129 | Example 1 130 | 131 | ~~~ corefile 132 | 133 | . { 134 | rrl . { 135 | responses-per-second 10 136 | } 137 | } 138 | 139 | ~~~ 140 | 141 | ## Known Issues 142 | 143 | *rrl* is vulnerable to wildcard flooding. See the section above for mitigating this vulnerability: **Mitigate Wildcard Flooding with the metadata Plugin** 144 | 145 | ## Additional References 146 | 147 | [A Quick Introduction to Response Rate Limiting](https://kb.isc.org/docs/aa-01000) 148 | 149 | [This Plugin's Design Spec](./README-DEV.md) 150 | -------------------------------------------------------------------------------- /README-DEV.md: -------------------------------------------------------------------------------- 1 | # CoreDNS RRL Plugin Design Spec 2 | 3 | This spec defines a CoreDNS plugin intended to replicate the behavior of 4 | the rate-limit feature in BIND. 5 | 6 | In the interest of keeping PRs as small as possible, RRL will first 7 | implement the following minimal set of sub-functions (aka minimal viable product). 8 | 9 | * Parsing of Corefile 10 | * Categorization of responses, and accounts debits/credit 11 | * Always block response when account is negative (no slip, i.e. slip = 0) 12 | 13 | The following functions, if added, would bring RRL into feature parity 14 | with BIND’s implementation of rate-limit. These can be added in 15 | separate PRs, to keep PRs small and easily digestible. 16 | 17 | * configurable slip ratio (slipping = send truncated response instead of dropping) 18 | * all-per-second (upper limit RRL at which we stop slipping) 19 | * expose metrics 20 | * exempt-clients (client list / cidrs to exempt from RRL) 21 | * qps-scale (scale down allowances proportionally to current qps load) 22 | 23 | 24 | ## Initial Features Spec 25 | 26 | RRL will be delivered as a plugin that accepts a list of zones for which 27 | it will track and enforce rate limiting for UDP responses. e.g. 28 | 29 | ``` 30 | rrl example.org { 31 | responses-per-second 10 32 | } 33 | ``` 34 | 35 | As a plugin, RRL should take an incoming response, pass it through the 36 | remaining plugins in the plugin chain, and then track/process the result 37 | before responding to the client. For this reason, it needs to be near the 38 | top of the plugin list. 39 | 40 | ### Plugin Directives 41 | 42 | Available configuration options (following naming convention of BIND)… 43 | 44 | ``` 45 | rrl [ZONES...] { 46 | window SECONDS 47 | ipv4-prefix-length LENGTH 48 | ipv6-prefix-length LENGTH 49 | responses-per-second ALLOWANCE 50 | nodata-per-second ALLOWANCE 51 | nxdomains-per-second ALLOWANCE 52 | referrals-per-second ALLOWANCE 53 | errors-per-second ALLOWANCE 54 | max-table-size SIZE 55 | } 56 | ``` 57 | 58 | * `window SECONDS` - defines a rolling window in SECONDS during which response rates are tracked. Default 15 59 | 60 | * `ipv4-prefix-length LENGTH` - the prefix LENGTH in bits to use for identifying a ipv4 client. Default 24 61 | 62 | * `ipv6-prefix-length LENGTH` - the prefix LENGTH in bits to use for identifying a ipv6 client. Default 56 63 | 64 | * `responses-per-second ALLOWANCE` - the number of positive responses allowed per second. Default 0 65 | 66 | * `nodata-per-second ALLOWANCE` - the number of empty (NODATA) responses allowed per second. Defaults to responses-per-second. 67 | 68 | * `nxdomains-per-second ALLOWANCE` - the number of negative (NXDOMAIN) responses allowed per second. Defaults to responses-per-second. 69 | 70 | * `referrals-per-second ALLOWANCE` - the number of negative (NXDOMAIN) responses allowed per second. Defaults to responses-per-second. 71 | 72 | * `errors-per-second ALLOWANCE` - the number of error responses allowed per second (excluding NXDOMAIN). Defaults to responses-per-second. 73 | 74 | * `max-table-size SIZE` - the maximum number of responses to be tracked at one time. When exceeded, rrl stops rate limiting new responses. 75 | 76 | 77 | ### Record Keeping: ResponseAccounts 78 | 79 | RRL tracks responses rates using a table of *ResponseAccounts*. A 80 | *ResponseAccount* consists of a *token*, and a *balance*. 81 | 82 | The *ResponseAccount* *token* uniquely identifies a category of responses and is 83 | comprised the following data extracted from a response: 84 | 85 | * Prefix of the client IP (per the ipv4/6-prefix-length) 86 | * Requested name (qname) see exceptions below 87 | * Requested type (qtype) excluding response type of error (see response type below) 88 | * Response type (each corresponding to the configurable per-second allowances) 89 | * response - for positive responses that contain answers 90 | * nodata - for NODATA responses 91 | * nxdomain - for NXDOMAIN responses 92 | * referrals - for referrals or delegations 93 | * error - for all DNS errors (except NXDOMAIN) 94 | 95 | To better protect against attacks using invalid requests, requested name and type are not used in the *token* for error type requests. In other words, all error responses are limited collectively per client, regardless of qname or qtype. 96 | 97 | For nxdomain and referrals, the authoritative domain is used instead of the full qname. 98 | 99 | The *ResponseAccount balance* is an integer. When the *balance* becomes negative 100 | for a *ResponseAccount*, any responses that match its *token* are dropped until 101 | the *balance* becomes positive again. 102 | The *ResponseAccount balance* cannot become more positive than the `per-second` allowance and 103 | cannot become more negative than `window` * the `per-second` allowance of the 104 | response type. 105 | 106 | *ResponseAccount* balances are credited and debited as outlined below. 107 | 108 | 109 | ### ResponseAccount Credits 110 | 111 | _Conceptually_, RRL will credit once per second each existing *ResponseAccount balance* by an amount equal to `per-second` allowance of the the corresponding response type. If a *ResponseAccount balance* exceeds window, then the *ResponseAccount* can be evicted to keep the *ResponseAccount* table from running out of space. 112 | 113 | _As implemented_, it's probably more performant to calculate credits on demand (at debit time) instead of in a separate asynchronous thread. In the same vein, it's probably more performant to defer evictions until space is needed (at insert time, when space runs out). 114 | 115 | ### ResponseAccount Debits 116 | 117 | *ResponseAccount balances* are debited at the time of sending a UDP response to a client, using the following logic ... 118 | 119 | 1. Calculate the *ResponseAccount token* for the response 120 | 1. If the *token* doesn’t exist in the to the *ResponseAccount* table, add the token as follows… 121 | 1. If the `max-table-size` is reached/exceeded, log an error/warning, and send response to client (done) 122 | 1. Add the *token* to the *ResponseAccount* table 123 | 1. Credit the *balance* to maximum - 1. I.e. `window` - 1 124 | 1. If the *token* does exist, debit the balance by 1 125 | 1. If *balance* is >= 0, send response to client. (done) 126 | 1. If *balance* is < 0, then drop the response, sending nothing to the client (done) 127 | 128 | ### ResponseAccount Concurrency 129 | 130 | Since the *ResponseAccount* table will be read and written to from parallel threads, locking should be used to ensure data integrity is maintained for all reads/writes. 131 | 132 | ## Follow up features 133 | 134 | ### Wildcard Flooding Mitigation 135 | 136 | Implement better protect against wildcard flooding. For domains CoreDNS is authoritative for, we can introduce an interface `Authoritative` to be implemented by any plugins that are authoritative. The interface would include functions listing the zones the pliugin is authortiative for, and all wildcard base domains produced by the plugin. RRL could check if plugins implememt `Authoritative`, and compare responses to the lists of authoritative zones and wildcard base domains to account for responses in the same way BIND does. We would also use this to more correctly check for authoritative domains for negative responses and referrals, for which we currently rely on neg cache SOAs. 137 | 138 | ### Slip ratio 139 | 140 | With a slip ratio defined, responses will not always be dropped when a balance goes negative - we let some slip through. 141 | However, instead of sending the whole response to the client, we send a small truncated response `TC=1`. This truncated response is intended to prompt the client to retry on TCP. 142 | Slipping in this way lets legitimate clients know to use TCP instead without flooding. 143 | 144 | Implementing slip ratios will introduce a new plugin directive: slip 145 | 146 | ``` 147 | rrl [ZONES...] { 148 | slip RATE 149 | } 150 | ``` 151 | 152 | Slip `RATE` is an integer between 0 and 10. 0 means never slip. 1 means always slip. Other numbers mean slip every nth time. 153 | Furthermore, a new property should be added to the *ResponseAccount*, *slipCount*, which defaults to `slip`. This value should be decremented every time we block a response matching the ResponseAccount token. 154 | When the value hits zero, we should slip a truncated response to the client, and reset the slipCount to slip. 155 | Response types that cannot be truncated such as `REFUSED` and `SERVFAIL` should be leaked in their entirety at the slip rate. 156 | 157 | ### All-per-second 158 | 159 | TBD (upper limit rates for all response types) 160 | 161 | ### Exposing metrics 162 | 163 | TBD 164 | 165 | ### Exempt-clients 166 | 167 | TBD (client list / cidrs to exempt from RRL) 168 | 169 | ### QPS-scale 170 | 171 | TBD (scale down allowances proportionally to current qps load) 172 | -------------------------------------------------------------------------------- /plugins/rrl/rrl.go: -------------------------------------------------------------------------------- 1 | package rrl 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "net" 7 | "strconv" 8 | "strings" 9 | "sync/atomic" 10 | "time" 11 | 12 | "github.com/coredns/coredns/plugin/metadata" 13 | "github.com/replit/rrl/plugins/rrl/cache" 14 | 15 | "github.com/miekg/dns" 16 | 17 | "github.com/coredns/coredns/plugin" 18 | "github.com/coredns/coredns/plugin/pkg/nonwriter" 19 | ) 20 | 21 | // RRL performs response rate limiting 22 | type RRL struct { 23 | Next plugin.Handler 24 | Zones []string 25 | 26 | window int64 27 | 28 | ipv4PrefixLength int 29 | ipv6PrefixLength int 30 | 31 | responsesInterval int64 32 | nodataInterval int64 33 | nxdomainsInterval int64 34 | referralsInterval int64 35 | errorsInterval int64 36 | 37 | requestsInterval int64 38 | 39 | podCount int64 40 | 41 | slipRatio uint 42 | 43 | reportOnly bool 44 | 45 | maxTableSize int 46 | 47 | table *cache.Cache 48 | } 49 | 50 | // ResponseAccount holds accounting for a category of response 51 | type ResponseAccount struct { 52 | allowTime int64 // Next response is allowed if current time >= allowTime 53 | slipCountdown uint // When at 1, a dropped response slips through instead of being dropped 54 | } 55 | 56 | // Theses constants are categories of response types 57 | const ( 58 | rTypeResponse = 0 59 | rTypeNodata = 1 60 | rTypeNxdomain = 2 61 | rTypeReferral = 3 62 | rTypeError = 4 63 | ) 64 | 65 | // responseType returns the RRL response type for a response 66 | func responseType(m *dns.Msg) byte { 67 | if len(m.Answer) > 0 { 68 | return rTypeResponse 69 | } else if m.Rcode == dns.RcodeNameError { 70 | return rTypeNxdomain 71 | } else if m.Rcode == dns.RcodeSuccess { 72 | if len(m.Ns) > 0 && m.Ns[0].Header().Rrtype == dns.TypeNS { 73 | return rTypeReferral 74 | } 75 | return rTypeNodata 76 | } else { 77 | return rTypeError 78 | } 79 | } 80 | 81 | // allowanceForRtype returns allowed response interval for the given rtype 82 | func (rrl *RRL) allowanceForRtype(rtype uint8) int64 { 83 | var allowance int64 84 | switch rtype { 85 | case rTypeResponse: 86 | allowance = rrl.responsesInterval 87 | case rTypeNodata: 88 | allowance = rrl.nodataInterval 89 | case rTypeNxdomain: 90 | allowance = rrl.nxdomainsInterval 91 | case rTypeReferral: 92 | allowance = rrl.referralsInterval 93 | case rTypeError: 94 | allowance = rrl.errorsInterval 95 | default: 96 | return -1 97 | } 98 | 99 | // We multiply because allowance is (1s / RPS) 100 | return allowance * atomic.LoadInt64(&rrl.podCount) 101 | } 102 | 103 | // scanKubernetesPods periodically determines the number of pods running in kubernetes 104 | // which will then be used to adjust the ratelimits so the overall ratelimit is divided 105 | // by the total number of pods. 106 | func (rrl *RRL) scanKubernetesPods(client PodCounter) { 107 | ctx := context.Background() 108 | for range time.Tick(10 * time.Second) { 109 | count, err := client.PodCount(ctx) 110 | if err != nil { 111 | log.Errorf("failed to scan kubernetes pods: %v", err) 112 | continue 113 | } 114 | // Ensure we don't ever fall below 0 healthy pods and disable limiting. 115 | if count < 1 { 116 | count = 1 117 | } 118 | atomic.StoreInt64(&rrl.podCount, count) 119 | } 120 | } 121 | 122 | // initTable creates a new cache table and sets the cache eviction function 123 | func (rrl *RRL) initTable() { 124 | rrl.table = cache.New(rrl.maxTableSize) 125 | // This eviction function returns true if the allowance is >= max value (window) 126 | rrl.table.SetEvict(func(el interface{}) bool { 127 | ra, ok := el.(*ResponseAccount) 128 | if !ok { 129 | return true 130 | } 131 | return time.Now().UnixNano()-ra.allowTime >= rrl.window 132 | }) 133 | } 134 | 135 | // responseToToken returns a token string for the response in writer 136 | func (rrl *RRL) responseToToken(ctx context.Context, nw *nonwriter.Writer, rtype byte) string { 137 | var name string 138 | if rtype == rTypeNxdomain || rtype == rTypeReferral { 139 | // for these types we index on the authoritative domain, not the full qname 140 | // if there is no auth section, dont index on name at all (treat all identical) 141 | if len(nw.Msg.Ns) > 0 { 142 | name = nw.Msg.Ns[0].Header().Name 143 | } 144 | } else { 145 | // if the record was synthesized from a wildcard record, set name to the wildcard record parent 146 | if f := metadata.ValueFunc(ctx, "zone/wildcard"); f != nil { 147 | name = f()[2:] // strip "*." to get the parent of the wildcard record 148 | } else { 149 | name = nw.Msg.Question[0].Name 150 | } 151 | } 152 | return rrl.buildToken(rtype, nw.Msg.Question[0].Qtype, name, nw.RemoteAddr().String()) 153 | } 154 | 155 | // buildToken returns a token string for the given inputs 156 | func (rrl *RRL) buildToken(rtype uint8, qtype uint16, name, remoteAddr string) string { 157 | // "Per BIND" references below are copied from the BIND 9.11 Manual 158 | // https://ftp.isc.org/isc/bind9/cur/9.11/doc/arm/Bv9ARM.pdf 159 | prefix := rrl.addrPrefix(remoteAddr) 160 | rtypestr := strconv.FormatUint(uint64(rtype), 10) 161 | switch rtype { 162 | case rTypeResponse: 163 | // Per BIND: All non-empty responses for a valid domain name (qname) and record type (qtype) are identical 164 | qtypeStr := strconv.FormatUint(uint64(qtype), 10) 165 | return strings.Join([]string{prefix, rtypestr, qtypeStr, name}, "/") 166 | case rTypeNodata: 167 | // Per BIND: All empty (NODATA) responses for a valid domain, regardless of query type, are identical. 168 | return strings.Join([]string{prefix, rtypestr, "", name}, "/") 169 | case rTypeNxdomain: 170 | // Per BIND: Requests for any and all undefined subdomains of a given valid domain result in NXDOMAIN errors 171 | // and are identical regardless of query type. 172 | return strings.Join([]string{prefix, rtypestr, "", name}, "/") 173 | case rTypeReferral: 174 | // Per BIND: Referrals or delegations to the server of a given domain are identical. 175 | qtypeStr := strconv.FormatUint(uint64(qtype), 10) 176 | return strings.Join([]string{prefix, rtypestr, qtypeStr, name}, "/") 177 | case rTypeError: 178 | // Per BIND: All requests that result in DNS errors other than NXDOMAIN, such as SERVFAIL and FORMERR, are 179 | // identical regardless of requested name (qname) or record type (qtype). 180 | return strings.Join([]string{prefix, rtypestr, "", ""}, "/") 181 | } 182 | return "" 183 | } 184 | 185 | // debit will update an existing response account in the rrl table and recalculate the current balance, 186 | // or if the response account does not exist, it will add it. 187 | func (rrl *RRL) debit(allowance int64, t string) (int64, bool, error) { 188 | 189 | type balances struct { 190 | balance int64 191 | slip bool 192 | } 193 | result := rrl.table.UpdateAdd(t, 194 | // the 'update' function updates the account and returns the new balance 195 | func(el interface{}) interface{} { 196 | if el == nil { 197 | return nil 198 | } 199 | ra := el.(*ResponseAccount) 200 | now := time.Now().UnixNano() 201 | balance := now - ra.allowTime - allowance 202 | if balance >= second { 203 | // positive balance can't exceed 1 second 204 | balance = second - allowance 205 | } else if balance < -rrl.window { 206 | // balance can't be more negative than window 207 | balance = -rrl.window 208 | } 209 | ra.allowTime = now - balance 210 | if balance > 0 || ra.slipCountdown == 0 { 211 | return balances{balance, false} 212 | } 213 | if ra.slipCountdown == 1 { 214 | ra.slipCountdown = rrl.slipRatio 215 | return balances{balance, true} 216 | } 217 | ra.slipCountdown -= 1 218 | return balances{balance, false} 219 | 220 | }, 221 | // the 'add' function returns a new ResponseAccount for the response type 222 | func() interface{} { 223 | ra := &ResponseAccount{ 224 | allowTime: time.Now().UnixNano() - second + allowance, 225 | slipCountdown: rrl.slipRatio, 226 | } 227 | return ra 228 | }) 229 | 230 | if result == nil { 231 | return 0, false, nil 232 | } 233 | if err, ok := result.(error); ok { 234 | return 0, false, err 235 | } 236 | if b, ok := result.(balances); ok { 237 | return b.balance, b.slip, nil 238 | } 239 | return 0, false, errors.New("unexpected result type") 240 | } 241 | 242 | // addrPrefix returns the address prefix of the net.Addr style address string (e.g. 1.2.3.4:1234 or [1:2::3:4]:1234) 243 | func (rrl *RRL) addrPrefix(addr string) string { 244 | i := strings.LastIndex(addr, ":") 245 | ip := net.ParseIP(addr[:i]) 246 | if ip.To4() != nil { 247 | ip = ip.Mask(net.CIDRMask(rrl.ipv4PrefixLength, 32)) 248 | return ip.String() 249 | } 250 | ip = net.ParseIP(addr[1 : i-1]) // strip brackets from ipv6 e.g. [2001:db8::1] 251 | ip = ip.Mask(net.CIDRMask(rrl.ipv6PrefixLength, 128)) 252 | 253 | return ip.String() 254 | } 255 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugins/rrl/setup_test.go: -------------------------------------------------------------------------------- 1 | package rrl 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/coredns/caddy" 8 | ) 9 | 10 | func TestSetupZones(t *testing.T) { 11 | tests := []struct { 12 | input string 13 | shouldErr bool 14 | expected RRL 15 | }{ 16 | {input: `rrl`, 17 | shouldErr: false, 18 | expected: RRL{ 19 | Zones: []string{}, 20 | }, 21 | }, 22 | {input: `rrl { 23 | }`, 24 | shouldErr: false, 25 | expected: RRL{ 26 | Zones: []string{}, 27 | }, 28 | }, 29 | {input: `rrl example.com { 30 | }`, 31 | shouldErr: false, 32 | expected: RRL{ 33 | Zones: []string{"example.com."}, 34 | }, 35 | }, 36 | } 37 | 38 | for i, test := range tests { 39 | c := caddy.NewTestController("dns", test.input) 40 | rrl, err := rrlParse(c) 41 | 42 | if test.shouldErr && err == nil { 43 | t.Errorf("Test %v: Expected error but found nil", i) 44 | continue 45 | } else if !test.shouldErr && err != nil { 46 | t.Errorf("Test %v: Expected no error but found error: %v", i, err) 47 | continue 48 | } 49 | if test.shouldErr && err != nil { 50 | continue 51 | } 52 | 53 | if fmt.Sprintf("%v", rrl.Zones) != fmt.Sprintf("%v", test.expected.Zones) { 54 | t.Errorf("Test %v: Expected Zones '%v' but found: '%v'", i, test.expected.Zones, rrl.Zones) 55 | } 56 | } 57 | } 58 | 59 | func TestSetupAllowances(t *testing.T) { 60 | tests := []struct { 61 | input string 62 | shouldErr bool 63 | expected RRL 64 | }{ 65 | {input: `rrl`, 66 | shouldErr: false, 67 | expected: defaultRRL(), 68 | }, 69 | {input: `rrl { 70 | responses-per-second 10 71 | }`, 72 | shouldErr: false, 73 | expected: RRL{ 74 | responsesInterval: second / 10, 75 | nodataInterval: second / 10, 76 | nxdomainsInterval: second / 10, 77 | referralsInterval: second / 10, 78 | errorsInterval: second / 10, 79 | }, 80 | }, 81 | {input: `rrl { 82 | responses-per-second 10 83 | nodata-per-second 5 84 | nxdomains-per-second 6 85 | referrals-per-second 7 86 | errors-per-second 8 87 | }`, 88 | shouldErr: false, 89 | expected: RRL{ 90 | responsesInterval: second / 10, 91 | nodataInterval: second / 5, 92 | nxdomainsInterval: second / 6, 93 | referralsInterval: second / 7, 94 | errorsInterval: second / 8, 95 | }, 96 | }, 97 | {input: `rrl { 98 | responses-per-second 10 11 99 | }`, 100 | shouldErr: true, 101 | expected: RRL{}, 102 | }, 103 | {input: `rrl { 104 | nodata-per-second 10 11 105 | }`, 106 | shouldErr: true, 107 | expected: RRL{}, 108 | }, 109 | {input: `rrl { 110 | nxdomains-per-second 10 11 111 | }`, 112 | shouldErr: true, 113 | expected: RRL{}, 114 | }, 115 | {input: `rrl { 116 | referrals-per-second 10 11 117 | }`, 118 | shouldErr: true, 119 | expected: RRL{}, 120 | }, 121 | {input: `rrl { 122 | errors-per-second 10 11 123 | }`, 124 | shouldErr: true, 125 | expected: RRL{}, 126 | }, 127 | {input: `rrl { 128 | responses-per-second -1 129 | }`, 130 | shouldErr: true, 131 | expected: RRL{}, 132 | }, 133 | {input: `rrl { 134 | nodata-per-second -1 135 | }`, 136 | shouldErr: true, 137 | expected: RRL{}, 138 | }, 139 | {input: `rrl { 140 | nxdomains-per-second -1 141 | }`, 142 | shouldErr: true, 143 | expected: RRL{}, 144 | }, 145 | {input: `rrl { 146 | referrals-per-second -1 147 | }`, 148 | shouldErr: true, 149 | expected: RRL{}, 150 | }, 151 | {input: `rrl { 152 | errors-per-second -1 153 | }`, 154 | shouldErr: true, 155 | expected: RRL{}, 156 | }, 157 | {input: `rrl { 158 | responses-per-second abc 159 | }`, 160 | shouldErr: true, 161 | expected: RRL{}, 162 | }, 163 | {input: `rrl { 164 | nodata-per-second abc 165 | }`, 166 | shouldErr: true, 167 | expected: RRL{}, 168 | }, 169 | {input: `rrl { 170 | nxdomains-per-second abc 171 | }`, 172 | shouldErr: true, 173 | expected: RRL{}, 174 | }, 175 | {input: `rrl { 176 | referrals-per-second abc 177 | }`, 178 | shouldErr: true, 179 | expected: RRL{}, 180 | }, 181 | {input: `rrl { 182 | errors-per-second abc 183 | }`, 184 | shouldErr: true, 185 | expected: RRL{}, 186 | }, 187 | } 188 | 189 | for i, test := range tests { 190 | c := caddy.NewTestController("dns", test.input) 191 | rrl, err := rrlParse(c) 192 | 193 | if test.shouldErr && err == nil { 194 | t.Errorf("Test %v: Expected error but found nil", i) 195 | continue 196 | } else if !test.shouldErr && err != nil { 197 | t.Errorf("Test %v: Expected no error but found error: %v", i, err) 198 | continue 199 | } 200 | if test.shouldErr && err != nil { 201 | continue 202 | } 203 | 204 | if rrl.responsesInterval != test.expected.responsesInterval { 205 | t.Errorf("Test %v: Expected responsesInterval %v but found: %v", i, test.expected.responsesInterval, rrl.responsesInterval) 206 | } 207 | if rrl.nodataInterval != test.expected.nodataInterval { 208 | t.Errorf("Test %v: Expected nodataInterval %v but found: %v", i, test.expected.nodataInterval, rrl.nodataInterval) 209 | } 210 | if rrl.nxdomainsInterval != test.expected.nxdomainsInterval { 211 | t.Errorf("Test %v: Expected nxdomainsInterval %v but found: %v", i, test.expected.nxdomainsInterval, rrl.nxdomainsInterval) 212 | } 213 | if rrl.referralsInterval != test.expected.referralsInterval { 214 | t.Errorf("Test %v: Expected referralsInterval %v but found: %v", i, test.expected.referralsInterval, rrl.referralsInterval) 215 | } 216 | if rrl.errorsInterval != test.expected.errorsInterval { 217 | t.Errorf("Test %v: Expected errorsInterval %v but found: %v", i, test.expected.errorsInterval, rrl.errorsInterval) 218 | } 219 | } 220 | } 221 | 222 | func TestSetupWindow(t *testing.T) { 223 | tests := []struct { 224 | input string 225 | shouldErr bool 226 | expected RRL 227 | }{ 228 | {input: `rrl`, 229 | shouldErr: false, 230 | expected: defaultRRL(), 231 | }, 232 | {input: `rrl { 233 | window 10 234 | }`, 235 | shouldErr: false, 236 | expected: RRL{ 237 | window: 10 * second, 238 | }, 239 | }, 240 | {input: `rrl { 241 | window 0 242 | }`, 243 | shouldErr: true, 244 | expected: RRL{}, 245 | }, 246 | {input: `rrl { 247 | window five 248 | }`, 249 | shouldErr: true, 250 | expected: RRL{}, 251 | }, 252 | {input: `rrl { 253 | window 1 2 254 | }`, 255 | shouldErr: true, 256 | expected: RRL{}, 257 | }, 258 | } 259 | 260 | for i, test := range tests { 261 | c := caddy.NewTestController("dns", test.input) 262 | rrl, err := rrlParse(c) 263 | 264 | if test.shouldErr && err == nil { 265 | t.Errorf("Test %v: Expected error but found nil", i) 266 | continue 267 | } else if !test.shouldErr && err != nil { 268 | t.Errorf("Test %v: Expected no error but found error: %v", i, err) 269 | continue 270 | } 271 | if test.shouldErr && err != nil { 272 | continue 273 | } 274 | 275 | if rrl.window != test.expected.window { 276 | t.Errorf("Test %v: Expected window %v but found: %v", i, test.expected.window, rrl.window) 277 | } 278 | } 279 | } 280 | 281 | func TestSetupPrefixes(t *testing.T) { 282 | tests := []struct { 283 | input string 284 | shouldErr bool 285 | expected RRL 286 | }{ 287 | {input: `rrl`, 288 | shouldErr: false, 289 | expected: defaultRRL(), 290 | }, 291 | {input: `rrl { 292 | ipv4-prefix-length 25 293 | ipv6-prefix-length 57 294 | }`, 295 | shouldErr: false, 296 | expected: RRL{ 297 | ipv4PrefixLength: 25, 298 | ipv6PrefixLength: 57, 299 | }, 300 | }, 301 | {input: `rrl { 302 | ipv4-prefix-length 33 303 | }`, 304 | shouldErr: true, 305 | expected: RRL{}, 306 | }, 307 | {input: `rrl { 308 | ipv6-prefix-length 129 309 | }`, 310 | shouldErr: true, 311 | expected: RRL{}, 312 | }, 313 | {input: `rrl { 314 | ipv4-prefix-length -1 315 | }`, 316 | shouldErr: true, 317 | expected: RRL{}, 318 | }, 319 | {input: `rrl { 320 | ipv6-prefix-length -1 321 | }`, 322 | shouldErr: true, 323 | expected: RRL{}, 324 | }, 325 | {input: `rrl { 326 | ipv4-prefix-length 1 2 327 | }`, 328 | shouldErr: true, 329 | expected: RRL{}, 330 | }, 331 | {input: `rrl { 332 | ipv6-prefix-length 3 4 333 | }`, 334 | shouldErr: true, 335 | expected: RRL{}, 336 | }, 337 | {input: `rrl { 338 | ipv4-prefix-length orange 339 | }`, 340 | shouldErr: true, 341 | expected: RRL{}, 342 | }, 343 | {input: `rrl { 344 | ipv6-prefix-length banana 345 | }`, 346 | shouldErr: true, 347 | expected: RRL{}, 348 | }, 349 | } 350 | 351 | for i, test := range tests { 352 | c := caddy.NewTestController("dns", test.input) 353 | rrl, err := rrlParse(c) 354 | 355 | if test.shouldErr && err == nil { 356 | t.Errorf("Test %v: Expected error but found nil", i) 357 | continue 358 | } else if !test.shouldErr && err != nil { 359 | t.Errorf("Test %v: Expected no error but found error: %v", i, err) 360 | continue 361 | } 362 | if test.shouldErr && err != nil { 363 | continue 364 | } 365 | 366 | if fmt.Sprint(rrl.ipv4PrefixLength) != fmt.Sprint(test.expected.ipv4PrefixLength) { 367 | t.Errorf("Test %v: Expected ipv4PrefixLength %v but found: %v", i, fmt.Sprint(test.expected.ipv4PrefixLength), fmt.Sprint(rrl.ipv4PrefixLength)) 368 | } 369 | if fmt.Sprint(rrl.ipv6PrefixLength) != fmt.Sprint(test.expected.ipv6PrefixLength) { 370 | t.Errorf("Test %v: Expected ipv6PrefixLength %v but found: %v", i, fmt.Sprint(test.expected.ipv6PrefixLength), fmt.Sprint(rrl.ipv6PrefixLength)) 371 | } 372 | } 373 | } 374 | 375 | func TestSetupTableSize(t *testing.T) { 376 | tests := []struct { 377 | input string 378 | shouldErr bool 379 | expected RRL 380 | }{ 381 | {input: `rrl`, 382 | shouldErr: false, 383 | expected: defaultRRL(), 384 | }, 385 | {input: `rrl { 386 | max-table-size 500000 387 | }`, 388 | shouldErr: false, 389 | expected: RRL{maxTableSize: 500000}, 390 | }, 391 | {input: `rrl { 392 | max-table-size -1 393 | }`, 394 | shouldErr: true, 395 | expected: RRL{}, 396 | }, 397 | {input: `rrl { 398 | max-table-size 1 3 399 | }`, 400 | shouldErr: true, 401 | expected: RRL{}, 402 | }, 403 | {input: `rrl { 404 | max-table-size ginormous 405 | }`, 406 | shouldErr: true, 407 | expected: RRL{}, 408 | }, 409 | } 410 | 411 | for i, test := range tests { 412 | c := caddy.NewTestController("dns", test.input) 413 | rrl, err := rrlParse(c) 414 | 415 | if test.shouldErr && err == nil { 416 | t.Errorf("Test %v: Expected error but found nil", i) 417 | continue 418 | } else if !test.shouldErr && err != nil { 419 | t.Errorf("Test %v: Expected no error but found error: %v", i, err) 420 | continue 421 | } 422 | if test.shouldErr && err != nil { 423 | continue 424 | } 425 | 426 | if rrl.maxTableSize != test.expected.maxTableSize { 427 | t.Errorf("Test %v: Expected maxTableSize %v but found: %v", i, test.expected.maxTableSize, rrl.maxTableSize) 428 | } 429 | } 430 | } 431 | 432 | func TestSetupSlipRatio(t *testing.T) { 433 | tests := []struct { 434 | input string 435 | shouldErr bool 436 | expected RRL 437 | }{ 438 | {input: `rrl`, 439 | shouldErr: false, 440 | expected: defaultRRL(), 441 | }, 442 | {input: `rrl { 443 | slip-ratio 5 444 | }`, 445 | shouldErr: false, 446 | expected: RRL{slipRatio: 5}, 447 | }, 448 | {input: `rrl { 449 | slip-ratio -1 450 | }`, 451 | shouldErr: true, 452 | expected: RRL{}, 453 | }, 454 | {input: `rrl { 455 | slip-ratio 2 3 456 | }`, 457 | shouldErr: true, 458 | expected: RRL{}, 459 | }, 460 | {input: `rrl { 461 | slip-ratio nine 462 | }`, 463 | shouldErr: true, 464 | expected: RRL{}, 465 | }, 466 | {input: `rrl { 467 | slip-ratio 468 | }`, 469 | shouldErr: true, 470 | expected: RRL{}, 471 | }, 472 | } 473 | 474 | for i, test := range tests { 475 | c := caddy.NewTestController("dns", test.input) 476 | rrl, err := rrlParse(c) 477 | 478 | if test.shouldErr && err == nil { 479 | t.Errorf("Test %v: Expected error but found nil", i) 480 | continue 481 | } else if !test.shouldErr && err != nil { 482 | t.Errorf("Test %v: Expected no error but found error: %v", i, err) 483 | continue 484 | } 485 | if test.shouldErr && err != nil { 486 | continue 487 | } 488 | 489 | if rrl.slipRatio != test.expected.slipRatio { 490 | t.Errorf("Test %v: Expected slipRatio %v but found: %v", i, test.expected.slipRatio, rrl.slipRatio) 491 | } 492 | } 493 | } 494 | 495 | func TestSetupInvalidOption(t *testing.T) { 496 | tests := []struct { 497 | input string 498 | shouldErr bool 499 | expected RRL 500 | }{ 501 | {input: `rrl { 502 | blah 503 | }`, 504 | shouldErr: true, 505 | expected: RRL{}, 506 | }, 507 | } 508 | 509 | for i, test := range tests { 510 | c := caddy.NewTestController("dns", test.input) 511 | _, err := rrlParse(c) 512 | 513 | if test.shouldErr && err == nil { 514 | t.Errorf("Test %v: Expected error but found nil", i) 515 | continue 516 | } else if !test.shouldErr && err != nil { 517 | t.Errorf("Test %v: Expected no error but found error: %v", i, err) 518 | continue 519 | } 520 | if test.shouldErr && err != nil { 521 | continue 522 | } 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /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 v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 17 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 18 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 19 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 20 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 21 | cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= 22 | cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= 23 | cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= 24 | cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= 25 | cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= 26 | cloud.google.com/go v0.94.1 h1:DwuSvDZ1pTYGbXo8yOJevCTr3BoBlE+OVkHAKiYQUXc= 27 | cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= 28 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 29 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 30 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 31 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 32 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 33 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 34 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 35 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 36 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 37 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 38 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 39 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 40 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 41 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 42 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 43 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 44 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 45 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 46 | github.com/Azure/azure-sdk-for-go v53.3.0+incompatible h1:DFyCwv0VetPlvKYckSGJRYWUSc+NKRDSryVWVvvVkFw= 47 | github.com/Azure/azure-sdk-for-go v53.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= 48 | github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= 49 | github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 50 | github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= 51 | github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= 52 | github.com/Azure/go-autorest/autorest v0.11.21 h1:w77zY/9RnUAWcIQyDC0Fc89mCvwftR8F+zsR/OH6enk= 53 | github.com/Azure/go-autorest/autorest v0.11.21/go.mod h1:Do/yuMSW/13ayUkcVREpsMHGG+MvV81uzSCFgYPj4tM= 54 | github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= 55 | github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= 56 | github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= 57 | github.com/Azure/go-autorest/autorest/adal v0.9.14 h1:G8hexQdV5D4khOXrWG2YuLCFKhWYmWD8bHYaXN5ophk= 58 | github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= 59 | github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc= 60 | github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= 61 | github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= 62 | github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= 63 | github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= 64 | github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= 65 | github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= 66 | github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= 67 | github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= 68 | github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= 69 | github.com/Azure/go-autorest/autorest/to v0.2.0 h1:nQOZzFCudTh+TvquAtCRjM01VEYx85e9qbwt5ncW4L8= 70 | github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= 71 | github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= 72 | github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= 73 | github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= 74 | github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= 75 | github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= 76 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 77 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 78 | github.com/DataDog/datadog-go v4.4.0+incompatible h1:R7WqXWP4fIOAqWJtUKmSfuc7eDsBT58k9AY5WSHVosk= 79 | github.com/DataDog/datadog-go v4.4.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= 80 | github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= 81 | github.com/DataDog/sketches-go v1.0.0 h1:chm5KSXO7kO+ywGWJ0Zs6tdmWU8PBXSbywFVciL6BG4= 82 | github.com/DataDog/sketches-go v1.0.0/go.mod h1:O+XkJHWk9w4hDwY2ZUDU31ZC9sNYlYo8DiFsxjYeo1k= 83 | github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= 84 | github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= 85 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 86 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 87 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 88 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 89 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 90 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 91 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 92 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 93 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 94 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 95 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 96 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 97 | github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= 98 | github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= 99 | github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= 100 | github.com/aws/aws-sdk-go v1.40.54 h1:8zYzK8wI06G2+Bg2hwTUwzIYCCo6/Wd7lfS0G+GwqXU= 101 | github.com/aws/aws-sdk-go v1.40.54/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= 102 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 103 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 104 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 105 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 106 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 107 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 108 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 109 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 110 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 111 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 112 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 113 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 114 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 115 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 116 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 117 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 118 | github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 119 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 120 | github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= 121 | github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= 122 | github.com/coredns/coredns v1.8.6 h1:7EatrqyeKTtjN1hrRIbQD2eomThlonkMw9xJkiXPQyc= 123 | github.com/coredns/coredns v1.8.6/go.mod h1:LDz00qDV/oBJ28pFP+D9riDO0kN6zVEZln4LNgT8Ih4= 124 | github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= 125 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 126 | github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= 127 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 128 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 129 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 130 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 131 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 132 | github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= 133 | github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= 134 | github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= 135 | github.com/dnstap/golang-dnstap v0.4.0 h1:KRHBoURygdGtBjDI2w4HifJfMAhhOqDuktAokaSa234= 136 | github.com/dnstap/golang-dnstap v0.4.0/go.mod h1:FqsSdH58NAmkAvKcpyxht7i4FoBjKu8E4JUPt8ipSUs= 137 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 138 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 139 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 140 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 141 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 142 | github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 143 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 144 | github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 145 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 146 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 147 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 148 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 149 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 150 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 151 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= 152 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 153 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 154 | github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= 155 | github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 156 | github.com/farsightsec/golang-framestream v0.3.0 h1:/spFQHucTle/ZIPkYqrfshQqPe2VQEzesH243TjIwqA= 157 | github.com/farsightsec/golang-framestream v0.3.0/go.mod h1:eNde4IQyEiA5br02AouhEHCu3p3UzrCdFR4LuQHklMI= 158 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= 159 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 160 | github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= 161 | github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= 162 | github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= 163 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 164 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 165 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 166 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 167 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 168 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 169 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 170 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 171 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 172 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 173 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 174 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 175 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 176 | github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= 177 | github.com/go-logr/logr v1.0.0 h1:kH951GinvFVaQgy/ki/B3YYmQtRpExGigSJg6O8z5jo= 178 | github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 179 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 180 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= 181 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 182 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 183 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 184 | github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 185 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 186 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 187 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 188 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 189 | github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o= 190 | github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= 191 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 192 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 193 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 194 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 195 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 196 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 197 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 198 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 199 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 200 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 201 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 202 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 203 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 204 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 205 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 206 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 207 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 208 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 209 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 210 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 211 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 212 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 213 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 214 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 215 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 216 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 217 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 218 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 219 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 220 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 221 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 222 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 223 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 224 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 225 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 226 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 227 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 228 | github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 229 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 230 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 231 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 232 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 233 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 234 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 235 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 236 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 237 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 238 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 239 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 240 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= 241 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 242 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 243 | github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 244 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 245 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 246 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 247 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 248 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 249 | github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 250 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 251 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 252 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 253 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 254 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 255 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 256 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 257 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 258 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 259 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 260 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 261 | github.com/google/pprof v0.0.0-20210423192551-a2663126120b/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 262 | github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 263 | github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 264 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 265 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 266 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 267 | github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= 268 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 269 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 270 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 271 | github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= 272 | github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU= 273 | github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= 274 | github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= 275 | github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= 276 | github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= 277 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 278 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 279 | github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= 280 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 281 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 282 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 283 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 284 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 285 | github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= 286 | github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= 287 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 288 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 289 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 290 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 291 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 292 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 293 | github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= 294 | github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 295 | github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 h1:w66aaP3c6SIQ0pi3QH1Tb4AMO3aWoEPxd1CNvLphbkA= 296 | github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9/go.mod h1:BaIJzjD2ZnHmx2acPF6XfGLPzNCMiBbMRqJr+8/8uRI= 297 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 298 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 299 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 300 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 301 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 302 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 303 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 304 | github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= 305 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 306 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 307 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 308 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 309 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 310 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 311 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 312 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 313 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 314 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 315 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 316 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 317 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 318 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 319 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 320 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 321 | github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 322 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 323 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 324 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 325 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 326 | github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= 327 | github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= 328 | github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= 329 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 330 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 331 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 332 | github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= 333 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 334 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 335 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 336 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 337 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 338 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 339 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 340 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 341 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 342 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 343 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 344 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 345 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 346 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 347 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 348 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 349 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 350 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 351 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 352 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 353 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 354 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 355 | github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= 356 | github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= 357 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 358 | github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= 359 | github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 360 | github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= 361 | github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= 362 | github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 363 | github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI= 364 | github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 365 | github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw= 366 | github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s= 367 | github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= 368 | github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= 369 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 370 | github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= 371 | github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= 372 | github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= 373 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 374 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 375 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 376 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 377 | github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 378 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 379 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 380 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 381 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 382 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 383 | github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= 384 | github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 385 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 386 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 387 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 388 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 389 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 390 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 391 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 392 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 393 | github.com/prometheus/common v0.31.1 h1:d18hG4PkHnNAKNMOmFuXFaiY8Us0nird/2m60uS1AMs= 394 | github.com/prometheus/common v0.31.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= 395 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 396 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 397 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 398 | github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= 399 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 400 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 401 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 402 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 403 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 404 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 405 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 406 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 407 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 408 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 409 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 410 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 411 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 412 | github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= 413 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 414 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 415 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 416 | github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= 417 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 418 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 419 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 420 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 421 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 422 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 423 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 424 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 425 | github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= 426 | github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= 427 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 428 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 429 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 430 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 431 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 432 | go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw= 433 | go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 434 | go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU= 435 | go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 436 | go.etcd.io/etcd/client/v3 v3.5.0 h1:62Eh0XOro+rDwkrypAGDfgmNh5Joq+z+W9HZdlXMzek= 437 | go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= 438 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 439 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 440 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 441 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 442 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 443 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 444 | go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= 445 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 446 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 447 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 448 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 449 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 450 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 451 | go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= 452 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 453 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 454 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 455 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 456 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 457 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 458 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 459 | golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 460 | golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 461 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= 462 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 463 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= 464 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 465 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 466 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 467 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 468 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 469 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 470 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 471 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 472 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 473 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 474 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 475 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 476 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 477 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 478 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 479 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 480 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 481 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 482 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 483 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 484 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 485 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 486 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 487 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 488 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 489 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 490 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 491 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 492 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 493 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 494 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 495 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 496 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 497 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 498 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 499 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 500 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 501 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 502 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 503 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 504 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 505 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 506 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 507 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 508 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 509 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 510 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 511 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 512 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 513 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 514 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 515 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 516 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 517 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 518 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 519 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 520 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 521 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 522 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 523 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 524 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 525 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 526 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 527 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 528 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 529 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 530 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 531 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 532 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 533 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 534 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 535 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 536 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 537 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 538 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 539 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 540 | golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 541 | golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 542 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 543 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= 544 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 545 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= 546 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 547 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 548 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 549 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 550 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 551 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 552 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 553 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 554 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 555 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 556 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 557 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 558 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 559 | golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 560 | golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 561 | golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= 562 | golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 563 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 564 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 565 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 566 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 567 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 568 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 569 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 570 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 571 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 572 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 573 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 574 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 575 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 576 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 577 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 578 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 579 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 580 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 581 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 582 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 583 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 584 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 585 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 586 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 587 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 588 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 589 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 590 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 591 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 592 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 593 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 594 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 595 | golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 596 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 597 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 598 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 599 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 600 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 601 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 602 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 603 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 604 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 605 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 606 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 607 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 608 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 609 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 610 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 611 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 612 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 613 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 614 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 615 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 616 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 617 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 618 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 619 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 620 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 621 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 622 | golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 623 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 624 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 625 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 626 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 627 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 628 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 629 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 630 | golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 631 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 632 | golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 633 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 634 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 635 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 636 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 637 | golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 638 | golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= 639 | golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 640 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 641 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 642 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= 643 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 644 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 645 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 646 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 647 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 648 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 649 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 650 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 651 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 652 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 653 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 654 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 655 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 656 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= 657 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 658 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 659 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 660 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 661 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 662 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 663 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 664 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 665 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 666 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 667 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 668 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 669 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 670 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 671 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 672 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 673 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 674 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 675 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 676 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 677 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 678 | golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 679 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 680 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 681 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 682 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 683 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 684 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 685 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 686 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 687 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 688 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 689 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 690 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 691 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 692 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 693 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 694 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 695 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 696 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 697 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 698 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 699 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 700 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 701 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 702 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 703 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 704 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 705 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 706 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 707 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 708 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 709 | golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 710 | golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 711 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 712 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 713 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 714 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 715 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 716 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 717 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 718 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 719 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 720 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 721 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 722 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 723 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 724 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 725 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 726 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 727 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 728 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 729 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 730 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 731 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 732 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 733 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 734 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 735 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 736 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 737 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 738 | google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= 739 | google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= 740 | google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= 741 | google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= 742 | google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= 743 | google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 744 | google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 745 | google.golang.org/api v0.58.0 h1:MDkAbYIB1JpSgCTOCYYoIec/coMlKK4oVbpnBLLcyT0= 746 | google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E= 747 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 748 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 749 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 750 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 751 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 752 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 753 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 754 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 755 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 756 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 757 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 758 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 759 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 760 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 761 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 762 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 763 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 764 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 765 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 766 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 767 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 768 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 769 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 770 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 771 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 772 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 773 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 774 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 775 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 776 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 777 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 778 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 779 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 780 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 781 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 782 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 783 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 784 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 785 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 786 | google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 787 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 788 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 789 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 790 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 791 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 792 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 793 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 794 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 795 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 796 | google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= 797 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 798 | google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 799 | google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 800 | google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= 801 | google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= 802 | google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= 803 | google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= 804 | google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= 805 | google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= 806 | google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 807 | google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 808 | google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 809 | google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 810 | google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 h1:ysnBoUyeL/H6RCvNRhWHjKoDEmguI+mPU+qHgK8qv/w= 811 | google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 812 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 813 | google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= 814 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 815 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 816 | google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 817 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 818 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 819 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 820 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 821 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 822 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 823 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 824 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 825 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 826 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 827 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 828 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 829 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 830 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 831 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 832 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 833 | google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 834 | google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 835 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 836 | google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= 837 | google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= 838 | google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 839 | google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= 840 | google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= 841 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 842 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 843 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 844 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 845 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 846 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 847 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 848 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 849 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 850 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 851 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 852 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 853 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 854 | google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= 855 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 856 | gopkg.in/DataDog/dd-trace-go.v1 v1.33.0 h1:goLas2M46NJ1NH6c5sPUI/KrYAaaiBZkctJMj2dgJ/w= 857 | gopkg.in/DataDog/dd-trace-go.v1 v1.33.0/go.mod h1:MFdmxQL1OfAGjPrYPU02P82Z5lJ/19f4JVAvXwK1brY= 858 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 859 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 860 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 861 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 862 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 863 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 864 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 865 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 866 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 867 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 868 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 869 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 870 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 871 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 872 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 873 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 874 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 875 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 876 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 877 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 878 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 879 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 880 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 881 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 882 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 883 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 884 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 885 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 886 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 887 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 888 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 889 | k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= 890 | k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= 891 | k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= 892 | k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= 893 | k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= 894 | k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= 895 | k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 896 | k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= 897 | k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= 898 | k8s.io/klog/v2 v2.20.0 h1:tlyxlSvd63k7axjhuchckaRJm+a92z5GSOrTOQY5sHw= 899 | k8s.io/klog/v2 v2.20.0/go.mod h1:Gm8eSIfQN6457haJuPaMxZw4wyP5k+ykPFlrhQDvhvw= 900 | k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= 901 | k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= 902 | k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= 903 | k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= 904 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 905 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 906 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 907 | sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 908 | sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= 909 | sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= 910 | sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= 911 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 912 | --------------------------------------------------------------------------------