diff --git a/.drone.yml b/.drone.yml index be1720f8..990b1913 100644 --- a/.drone.yml +++ b/.drone.yml @@ -3,6 +3,10 @@ kind: pipeline type: kubernetes name: default +environment: + GOCACHE: /cache/pkg/cache + GOMODCACHE: /cache/pkg/mod + steps: - name: fetch-tags image: alpine/git @@ -17,13 +21,14 @@ steps: memory: 100MiB - name: test - image: golang:1.17.2 + image: golang:1.21.3 volumes: - - name: cache + - name: go path: /go + - name: gopkg + path: /cache commands: - ./scripts/download-test-geoip - - go get - go test -v ./... - go build ./... resources: @@ -32,10 +37,10 @@ steps: memory: 128MiB limits: cpu: 2000 - memory: 512MiB + memory: 2GiB - name: goreleaser - image: golang:1.17.2 + image: golang:1.21.3 resources: requests: cpu: 4000 @@ -44,15 +49,18 @@ steps: cpu: 10000 memory: 2048MiB volumes: - - name: cache + - name: go path: /go + - name: gopkg + path: /cache commands: + - git status - ./scripts/run-goreleaser - echo Done when: ref: - refs/heads/main - - refs/heads/drone-test + - refs/heads/avro - refs/tags/** depends_on: [test] @@ -71,7 +79,7 @@ steps: secret_key: from_secret: s3_secret_key bucket: geodns - target: /builds/${DRONE_BUILD_NUMBER} + target: /geodns/builds/test/${DRONE_BUILD_NUMBER} source: dist/* strip_prefix: dist/ endpoint: https://minio-ewr1.develooper.com/ @@ -79,7 +87,7 @@ steps: depends_on: [goreleaser] - name: fury-publish - image: golang:1.17.2 + image: golang:1.21.3 resources: requests: cpu: 250 @@ -100,8 +108,11 @@ steps: depends_on: [goreleaser] volumes: - - name: cache + - name: go temp: {} + - name: gopkg + claim: + name: go-pkg trigger: event: @@ -116,9 +127,19 @@ name: publish-production steps: - name: download - image: golang:1.17.2 + image: golang:1.21.3 commands: - - ./scripts/download-release ${DRONE_BUILD_PARENT} dist/ + - ./scripts/download-release geodns test/${DRONE_BUILD_PARENT} dist/ + resources: + requests: + cpu: 250 + memory: 64MiB + limits: + cpu: 250 + memory: 256MiB + + - name: upload + image: plugins/s3 resources: requests: cpu: 250 @@ -126,9 +147,21 @@ steps: limits: cpu: 250 memory: 256MiB + settings: + access_key: + from_secret: s3_access_key + secret_key: + from_secret: s3_secret_key + bucket: geodns + target: /geodns/builds/release/${DRONE_BUILD_NUMBER} + source: dist/* + strip_prefix: dist/ + endpoint: https://minio-ewr1.develooper.com/ + path_style: true + depends_on: ["download"] - name: fury-publish - image: golang:1.17.2 + image: golang:1.21.3 resources: requests: cpu: 250 @@ -150,6 +183,6 @@ trigger: - publish --- kind: signature -hmac: d6a923125d5b4d28469fb999962a7aecfeb71a1f59680e0b92bc5e864d0027ff +hmac: e548b46090913220734b26fbf4c8ff97b8b0931f11f63d0c39f5eefe4c128c0d ... diff --git a/.gitignore b/.gitignore index 51317cd3..3772b52c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ +dist/ .DS_Store /geodns /REVISION @@ -7,3 +8,4 @@ /dns/geodns.conf geodns-*-* geodns-*-*.tar +/devel/ diff --git a/.goreleaser.yml b/.goreleaser.yml index c6a4dfff..b216acf2 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -8,25 +8,7 @@ builds: - CGO_ENABLED=0 ldflags: - -s -w - - -X main.VERSION={{.Version}} - - -X main.gitVersion={{.ShortCommit}} - - -X main.buildTime={{.Date}} - goos: - - linux - - freebsd - - darwin - ignore: - - goos: darwin - goarch: 386 - - goos: freebsd - goarch: 386 - - goos: freebsd - goarch: arm64 - - id: geodns-logs - main: ./geodns-logs/ - binary: geodns-logs - env: - - CGO_ENABLED=0 + - -X go.ntppool.org/common/version.VERSION={{.Version}} goos: - linux - freebsd @@ -38,12 +20,13 @@ builds: goarch: 386 - goos: freebsd goarch: arm64 + archives: - files: - service/** - - service-logs/** - LICENSE - README.md + checksum: name_template: "checksums.txt" snapshot: @@ -64,22 +47,16 @@ nfpms: # release: {{ if index .Env "DRONE_BUILD_NUMBER" }}{{ .Env.DRONE_BUILD_NUMBER }}{{ else }}1{{ end }} - # You can change the file name of the package. - # Default: `{{ .PackageName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}` - file_name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" - vendor: NTP Pool Project homepage: https://www.ntppool.org/ maintainer: Ask Bjørn Hansen description: GeoDNS server license: Apache 2.0 + file_name_template: "{{ .ConventionalFileName }}" formats: - deb - rpm - apk - replacements: - 386: i686 - amd64: x86_64 bindir: /usr/bin contents: - src: "scripts/geodns.service" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 31ec186c..00000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -dist: bionic -language: go -go_import_path: github.com/abh/geodns - -go: - - "1.16.9" - - "1.17.2" - - tip - -before_install: - - sudo apt-get -y update && sudo apt-get -y install curl - # we add the maxmind repository to get geoipupdate - #- sudo apt-get -y install software-properties-common - #- sudo add-apt-repository -y ppa:maxmind/ppa && sudo apt-get -y update - #- sudo apt-get -y install geoipupdate geoip-database geoip-bin curl - -install: - - sudo mkdir -p /usr/share/GeoIP/ - - sudo curl -fso /usr/share/GeoIP/GeoLite2-ASN.mmdb https://geodns.bitnames.com/geoip/GeoLite2-ASN.mmdb - - sudo curl -fso /usr/share/GeoIP/GeoLite2-City.mmdb https://geodns.bitnames.com/geoip/GeoLite2-City.mmdb - - sudo curl -fso /usr/share/GeoIP/GeoLite2-Country.mmdb https://geodns.bitnames.com/geoip/GeoLite2-Country.mmdb -# - sudo ln -s /usr/share/GeoIP $TRAVIS_BUILD_DIR/db -# - echo [geodns] >> dns/geodns.conf -# - echo Directory=/usr/share/GeoIP/ >> dns/geodns.conf - - go build -v - - go install - -script: - - cd $TRAVIS_BUILD_DIR && make test diff --git a/CHANGES.md b/CHANGES.md index 91e3bf1d..9f894833 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,44 @@ # GeoDNS Changelog +## Next +- DNS configuration options (see dns/geodns.conf.sample) for + disabling qnames being in the prometheus labels and enabling + public debug queries. + +## 3.3.3 August 2023 +- Fix how NS / SOA queries are treated for alias records pointing + to the zone apex +- Update Go to 1.21.0 and package dependencies + +## 3.3.2 August 2023 +- Update Go to 1.20.7 and package dependencies +- Minor Avro logging improvements + +## 3.3.1 July 2023 +- Use server IP if ECS provided IP is unhelpful +- Avro logging fixes +- Remove deprecated geodns-logs tool +- Lowercase 'version' in prometheus build_info + +## 3.3.0 July 2023 +- Avro logging feature +- Default to use all CPUs on the system. +- Graceful shutdown on term/quit/interrupt signals + +## 3.2.3 June 2023 +- querylog: Add software version, answer data and IsTCP fields +- Make Go module paths semantic versions +- Remove extra bogus json field from query log +- Update dependencies (and Go 1.20.5) + +## 3.2.2 May 2023 +* Go 1.20.4 +* Updated dependencies + +## 3.2.1 November 2022 +* Go 1.19.3 +* Add new country codes + ## 3.2.0 October 2021 * Reload GeoIP 2 databases when they change (Tyler Davis) diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index acfe192c..00000000 --- a/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM golang:1.17-alpine3.14 as build - -RUN apk add --no-cache git tar - -WORKDIR /go/src/github.com/abh/geodns - -ENV CGO_ENABLED=0 - -ADD vendor/ vendor/ -ADD applog/ applog/ -ADD countries/ countries/ -ADD geodns-logs/ geodns-logs/ -ADD health/ health/ -ADD monitor/ monitor/ -ADD querylog/ querylog/ -ADD server/ server/ -ADD targeting/ targeting/ -ADD typeutil/ typeutil/ -ADD zones/ zones/ -ADD service/ service/ -ADD service-logs/ service-logs/ -ADD .git/ .git/ -ADD *.go build ./ - -RUN ./build -RUN ls -l -RUN ls -l dist - -RUN ln dist/* / - -FROM scratch -COPY --from=build /geodns-linux-amd64 /geodns -COPY --from=build /geodns-logs-linux-amd64 /geodns-logs -ENTRYPOINT ["/geodns"] diff --git a/Makefile b/Makefile index 6911a190..c7626dc4 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,9 @@ docker-test: .PHONY golang:1.14-alpine3.11 -- \ go test ./... +sign: + drone sign --save ntppool/geodns + devel: go build -tags devel diff --git a/README.md b/README.md index eec7ccf6..293ceacb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ This is the DNS server powering the [NTP Pool](http://www.pool.ntp.org/) system and other similar services. -[![Build Status](https://travis-ci.org/abh/geodns.svg?branch=master)](https://travis-ci.org/abh/geodns) + +[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/7022/badge)](https://bestpractices.coreinfrastructure.org/projects/7022) ## Questions or suggestions? @@ -23,7 +24,7 @@ Release builds are available in a yum repository at If you don't have Go installed the easiest way to build geodns from source is to download and install Go from `https://golang.org/dl/`. -GeoDNS generally requires a recent version of Go (1.15+). +GeoDNS generally requires a recent version of Go (one of the last few major versions) ```sh git clone https://github.com/abh/geodns.git @@ -32,6 +33,8 @@ go build ./geodns -h ``` +You can also build with [goreleaser](https://github.com/goreleaser/goreleaser). + ## Sample configuration There's a sample configuration file in `dns/example.com.json`. This is currently @@ -65,7 +68,9 @@ or more simply put The binary can be moved to /usr/local/bin, /opt/geodns/ or wherever you find appropriate. -### Command options +### Configuration + +See the [sample configuration file](https://github.com/abh/geodns/blob/main/dns/geodns.conf.sample). Notable command line parameters (and their defaults) @@ -103,26 +108,24 @@ cluster the server is part of, etc. This is used in (future) reporting/statistic Enable to get lots of extra logging, only useful for testing and debugging. Absolutely not recommended in production unless you get very few queries (less than 1-200/second). -* -cpus=1 +* -cpus=4 -Maximum number of CPUs to use. Set to 0 to match the number of CPUs available on the system. -Only "1" (the default) has been extensively tested. +Maximum number of CPUs to use. Set to 0 to match the number of CPUs +available on the system (also the default). -## WebSocket interface +## Logging -geodns runs a WebSocket server on port 8053 that outputs various performance -metrics. The WebSocket URL is `/monitor`. There's a "companion program" that can -use this across a cluster to show aggregate statistics, email for more information. +GeoDNS supports query logging to JSON or Avro files (see the sample configuration file +for options). -## Runtime status +## Prometheus metrics -There's a page with various runtime information (queries per second, queries and -most frequently requested labels per zone, etc) at `/status`. +`/metrics` on the http port provides a number of metrics in Prometheus format. -## StatHat integration +### Runtime status page, Websocket metrics & StatHat integration -GeoDNS can post runtime data to [StatHat](http://www.stathat.com/). -([Documentation](https://github.com/abh/geodns/wiki/StatHat)) +The runtime status page, websocket feature and StatHat integration have +been replaced with Prometheus metrics. ## Country and continent lookups @@ -206,8 +209,6 @@ Set the default TTL for the zone (default 120). * max_hosts - - * contact Set the soa 'contact' field (default is "hostmaster.$domain"). @@ -295,7 +296,6 @@ Or with weights An SPF record is semantically identical to a TXT record with the exception that the label is set to 'spf'. An example of an spf record with weights: - { "spf": "v=spf1 ~all]", "weight": 1 } An spf record is typically at the root of a zone, and a label can have an array of SPF records, e.g diff --git a/config.go b/appconfig/appconfig.go similarity index 63% rename from config.go rename to appconfig/appconfig.go index d38be261..58458a48 100644 --- a/config.go +++ b/appconfig/appconfig.go @@ -1,24 +1,22 @@ -package main +package appconfig import ( - "fmt" + "context" "log" "os" "sync" "time" - "github.com/abh/geodns/targeting/geoip2" - "github.com/fsnotify/fsnotify" - gcfg "gopkg.in/gcfg.v1" + "gopkg.in/gcfg.v1" + + "github.com/abh/geodns/v3/targeting/geoip2" ) type AppConfig struct { - StatHat struct { - ApiKey string - } - Flags struct { - HasStatHat bool + DNS struct { + PublicDebugQueries bool + DetailedMetrics bool } GeoIP struct { Directory string @@ -32,6 +30,11 @@ type AppConfig struct { MaxSize int Keep int } + AvroLog struct { + Path string + MaxSize int // rotate files at this size + MaxTime string // rotate active files after this time, even if small + } Health struct { Directory string } @@ -48,20 +51,10 @@ type AppConfig struct { } } +// Singleton to keep the latest read config var Config = new(AppConfig) -var cfgMutex sync.RWMutex - -func (conf *AppConfig) HasStatHat() bool { - cfgMutex.RLock() - defer cfgMutex.RUnlock() - return conf.Flags.HasStatHat -} -func (conf *AppConfig) StatHatApiKey() string { - cfgMutex.RLock() - defer cfgMutex.RUnlock() - return conf.StatHat.ApiKey -} +var cfgMutex sync.RWMutex func (conf *AppConfig) GeoIPDirectory() string { cfgMutex.RLock() @@ -72,43 +65,47 @@ func (conf *AppConfig) GeoIPDirectory() string { return geoip2.FindDB() } -func configWatcher(fileName string) { +func ConfigWatcher(ctx context.Context, fileName string) error { watcher, err := fsnotify.NewWatcher() if err != nil { - fmt.Println(err) - return + return err } - if err := watcher.Add(*flagconfig); err != nil { - fmt.Println(err) - return + if err := watcher.Add(fileName); err != nil { + return err } for { select { + case <-ctx.Done(): + return nil case ev := <-watcher.Events: if ev.Name == fileName { // Write = when the file is updated directly // Rename = when it's updated atomicly // Chmod = for `touch` - if ev.Op&fsnotify.Write == fsnotify.Write || - ev.Op&fsnotify.Rename == fsnotify.Rename || - ev.Op&fsnotify.Chmod == fsnotify.Chmod { + if ev.Has(fsnotify.Write) || + ev.Has(fsnotify.Rename) || + ev.Has(fsnotify.Chmod) { time.Sleep(200 * time.Millisecond) - configReader(fileName) + err := ConfigReader(fileName) + if err != nil { + // don't quit because we'll just keep the old config at this + // stage and try again next it changes + log.Printf("error reading config file: %s", err) + } } } case err := <-watcher.Errors: - log.Println("fsnotify error:", err) + log.Printf("fsnotify error: %s", err) } } - } var lastReadConfig time.Time -func configReader(fileName string) error { +func ConfigReader(fileName string) error { stat, err := os.Stat(fileName) if err != nil { @@ -132,11 +129,6 @@ func configReader(fileName string) error { return err } - cfg.Flags.HasStatHat = len(cfg.StatHat.ApiKey) > 0 - - // log.Println("STATHAT APIKEY:", cfg.StatHat.ApiKey) - // log.Println("STATHAT FLAG :", cfg.Flags.HasStatHat) - cfgMutex.Lock() *Config = *cfg // shallow copy to prevent race conditions in referring to Config.foo() cfgMutex.Unlock() diff --git a/build b/build index 44be2124..ec019f68 100755 --- a/build +++ b/build @@ -14,15 +14,11 @@ ARCH=${GOARCH:-`go env GOARCH`} set -ex go build -o dist/geodns-$OS-$ARCH \ - -mod=vendor \ -trimpath \ -ldflags "-X main.gitVersion=$REVISION -X main.buildTime=$BUILDTIME" \ -v && \ - (cd geodns-logs && go build -trimpath -mod=vendor -v -o ../dist/geodns-logs-$OS-$ARCH && cd ..) && \ cd dist && \ - rm -f service service-logs && \ + rm -f service && \ ln -s ../service . && \ - ln -s ../service-logs . && \ tar -cvhf geodns-$OS-$ARCH.tar \ - --exclude \*~ geodns-$OS-$ARCH \ - geodns-logs-$OS-$ARCH service service-logs + --exclude \*~ geodns-$OS-$ARCH service diff --git a/config_test.go b/config_test.go index c28a988a..c9c40156 100644 --- a/config_test.go +++ b/config_test.go @@ -1,10 +1,14 @@ package main -import "testing" +import ( + "testing" + + "github.com/abh/geodns/v3/appconfig" +) func TestConfig(t *testing.T) { // check that the sample config parses - err := configReader("dns/geodns.conf.sample") + err := appconfig.ConfigReader("dns/geodns.conf.sample") if err != nil { t.Fatalf("Could not read config: %s", err) } diff --git a/countries/countries.go b/countries/countries.go index 72fd2d03..669e5d7e 100644 --- a/countries/countries.go +++ b/countries/countries.go @@ -32,6 +32,7 @@ var CountryContinent = map[string]string{ "bm": "north-america", "bn": "asia", "bo": "south-america", + "bq": "north-america", "br": "south-america", "bs": "north-america", "bt": "asia", @@ -54,8 +55,9 @@ var CountryContinent = map[string]string{ "cr": "north-america", "cu": "north-america", "cv": "africa", - "cx": "asia", - "cy": "asia", + "cw": "north-america", + "cx": "oceania", + "cy": "europe", "cz": "europe", "de": "europe", "dj": "africa", @@ -210,8 +212,10 @@ var CountryContinent = map[string]string{ "sn": "africa", "so": "africa", "sr": "south-america", + "ss": "africa", "st": "africa", "sv": "north-america", + "sx": "north-america", "sy": "asia", "sz": "africa", "tc": "north-america", @@ -221,7 +225,7 @@ var CountryContinent = map[string]string{ "th": "asia", "tj": "asia", "tk": "oceania", - "tl": "asia", + "tl": "oceania", "tm": "asia", "tn": "africa", "to": "oceania", @@ -245,6 +249,7 @@ var CountryContinent = map[string]string{ "vu": "oceania", "wf": "oceania", "ws": "oceania", + "xk": "europe", "ye": "asia", "yt": "africa", "za": "africa", diff --git a/dns/geodns.conf.sample b/dns/geodns.conf.sample index fe5085f4..373b6295 100644 --- a/dns/geodns.conf.sample +++ b/dns/geodns.conf.sample @@ -3,6 +3,12 @@ ; It is recommended to distribute the configuration file globally ; with your .json zone files. +[dns] +# allow _status queries from anywhere (versus only localhost) +publicdebugqueries = false +# include query label in prometheus metrics +detailedmetrics = true + [geoip] ;; Directory containing the GeoIP2 .mmdb database files; defaults ;; to looking through a list of common directories looking for one @@ -17,6 +23,17 @@ path = log/queries.log ;; keep up to this many rotated log files (default 1) ; keep = 2 + +;; avro logging will replace the json querylog if configured +; [avrolog] +;; The avro schema is specified in https://github.com/abh/geodns/blob/main/querylog/querylog.avsc +;; files being written are suffixed .tmp; closed files are suffixed .avro +; path = log/avro/ +;; rotate file after it reaches this size +; maxsize = 5000000 +;; rotate the file after this many seconds +; maxtime = 10s + [http] ; require basic HTTP authentication; not encrypted or safe over the public internet ; user = stats diff --git a/dns/test.example.com.json b/dns/test.example.com.json index 9bf4086f..0efd052f 100644 --- a/dns/test.example.com.json +++ b/dns/test.example.com.json @@ -168,6 +168,9 @@ "bar-alias": { "alias": "bar" }, + "root-alias": { + "alias": "" + }, "www-alias": { "alias": "www" }, diff --git a/geodns-logs/process-stats.go b/geodns-logs/process-stats.go deleted file mode 100644 index c90bf182..00000000 --- a/geodns-logs/process-stats.go +++ /dev/null @@ -1,216 +0,0 @@ -package main - -import ( - "bufio" - "encoding/json" - "flag" - "log" - "net/http" - "os" - "strings" - "sync" - - "github.com/miekg/dns" - "github.com/nxadm/tail" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - - "github.com/abh/geodns/countries" - "github.com/abh/geodns/querylog" -) - -// TODO: -// Add vendor yes/no -// add server region tag (identifier)? - -var version string = "2.1" - -func main() { - - log.Printf("Starting geodns-logs/%q", version) - - identifierFlag := flag.String("identifier", "", "identifier (hostname, pop name or similar)") - // verboseFlag := flag.Bool("verbose", false, "verbose output") - flag.Parse() - - var serverID string - // var serverGroups []string - - if len(*identifierFlag) > 0 { - ids := strings.Split(*identifierFlag, ",") - serverID = ids[0] - // if len(ids) > 1 { - // serverGroups = ids[1:] - // } - } - - if len(serverID) == 0 { - var err error - serverID, err = os.Hostname() - if err != nil { - log.Printf("Could not get hostname: %s", err) - os.Exit(2) - } - } - - queries = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "dns_logs_total", - Help: "Number of served queries", - }, - []string{"zone", "vendor", "usercc", "poolcc", "qtype"}, - ) - prometheus.MustRegister(queries) - - buildInfo := prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "geodns_logs_build_info", - Help: "GeoDNS logs build information (in labels)", - }, - []string{"Version"}, - ) - prometheus.MustRegister(buildInfo) - buildInfo.WithLabelValues("geodns-logs/" + version).Set(1) - - http.Handle("/metrics", promhttp.Handler()) - go func() { - err := http.ListenAndServe(":8054", nil) - if err != nil { - log.Printf("could not start http server: %s", err) - } - }() - - if len(flag.Args()) < 1 { - log.Printf("filename to process required") - os.Exit(2) - } - - filename := flag.Arg(0) - - logf, err := tail.TailFile(filename, tail.Config{ - // Location: &tail.SeekInfo{-1, 0}, - Poll: true, // inotify is flaky on EL6, so try this ... - ReOpen: true, - MustExist: false, - Follow: true, - }) - if err != nil { - log.Printf("Could not tail '%s': %s", filename, err) - } - - in := make(chan string) - go processChan(in, nil) - - for line := range logf.Lines { - if line.Err != nil { - log.Printf("Error tailing file: %s", line.Err) - } - in <- line.Text - } - -} - -var extraValidLabels = map[string]struct{}{ - "uk": struct{}{}, - "_status": struct{}{}, - "_country": struct{}{}, - "www": struct{}{}, - "nag-test": struct{}{}, -} - -func validCC(label string) bool { - if _, ok := countries.CountryContinent[label]; ok { - return true - } - if _, ok := countries.ContinentCountries[label]; ok { - return true - } - if _, ok := countries.RegionGroupRegions[label]; ok { - return true - } - if _, ok := countries.RegionGroups[label]; ok { - return true - } - if _, ok := extraValidLabels[label]; ok { - return true - } - return false -} - -func getPoolCC(label string) (string, bool) { - l := dns.SplitDomainName(label) - // log.Printf("LABEL: %+v", l) - if len(l) == 0 { - return "", true - } - - for _, cc := range l { - if validCC(cc) { - return cc, true - } - } - - if len(l[0]) == 1 && strings.ContainsAny(l[0], "01234") { - if len(l) == 1 { - return "", true - } - } - - // log.Printf("LABEL '%s' unhandled cc...", label) - return "", false -} - -func processChan(in chan string, wg *sync.WaitGroup) error { - e := querylog.Entry{} - - stats := NewStats() - - for line := range in { - err := json.Unmarshal([]byte(line), &e) - if err != nil { - log.Printf("Can't unmarshal '%s': %s", line, err) - return err - } - e.Name = strings.ToLower(e.Name) - - // fmt.Printf("%s %s\n", e.Origin, e.Name) - - err = stats.Add(&e) - if err != nil { - return err - } - } - - if wg != nil { - wg.Done() - } - return nil -} - -func processFile(file string, out chan<- *Stats) error { - fh, err := os.Open(file) - if err != nil { - return err - } - - in := make(chan string) - - wg := sync.WaitGroup{} - wg.Add(1) - go processChan(in, &wg) - - scanner := bufio.NewScanner(fh) - - for scanner.Scan() { - in <- scanner.Text() - } - if err := scanner.Err(); err != nil { - log.Println("reading standard input:", err) - } - - close(in) - - wg.Wait() - - return nil -} diff --git a/geodns-logs/process-stats_test.go b/geodns-logs/process-stats_test.go deleted file mode 100644 index cc7013f6..00000000 --- a/geodns-logs/process-stats_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import "testing" - -func TestGetPoolCC(t *testing.T) { - tests := []struct { - label string - cc string - ok bool - }{ - {"1.debian.pool.ntp.org.", "", false}, - {"2.dk.pool.ntp.org.", "dk", true}, - {"dk.pool.ntp.org.", "dk", true}, - {"0.asia.pool.ntp.org.", "asia", true}, - {"1.pool.ntp.org.", "", false}, - } - - for _, input := range tests { - cc, ok := getPoolCC(input.label) - if cc != input.cc || ok != input.ok { - t.Logf("%q got %q (%t), expected %q (%t)", input.label, cc, ok, input.cc, input.ok) - t.Fail() - } - } -} diff --git a/geodns-logs/stats.go b/geodns-logs/stats.go deleted file mode 100644 index 5470dc1e..00000000 --- a/geodns-logs/stats.go +++ /dev/null @@ -1,138 +0,0 @@ -package main - -import ( - "fmt" - "log" - "strings" - "time" - - "github.com/abh/geodns/querylog" - "github.com/miekg/dns" - "github.com/prometheus/client_golang/prometheus" -) - -type statsEntry struct { - Time int64 - Origin string - Name string - Vendor string - Label string - Qtype string - PoolCC string - Count int -} - -type Stats struct { - Count int - Map map[string]*statsEntry -} - -var queries *prometheus.CounterVec - -func NewStats() *Stats { - return &Stats{ - Map: map[string]*statsEntry{}, - } -} - -func (s *Stats) Key(e *querylog.Entry) string { - return fmt.Sprintf("%s %s %s %d", e.Origin, e.Name, e.LabelName, e.Qtype) -} - -func vendorName(n string) string { - idx := strings.Index(n, ".pool.ntp.org.") - // log.Printf("IDX for %s: %d", n, idx) - if idx <= 0 { - return "" - } - n = n[0:idx] - - l := dns.SplitDomainName(n) - - v := l[len(l)-1] - - if len(v) == 1 && strings.ContainsAny(v, "01234") { - return "" - } - - if len(v) == 2 { - // country code - return "_country" - } - - if v == "asia" || v == "north-america" || v == "europe" || v == "south-america" || v == "oceania" || v == "africa" { - return "_continent" - } - - return v -} - -func (stats *Stats) Add(e *querylog.Entry) error { - if e.Rcode > 0 { - // NXDOMAIN, count separately? - return nil - } - if e.Answers == 0 { - // No answers, count separately? - return nil - } - - var vendor, poolCC string - - if e.Origin == "pool.ntp.org" || strings.HasSuffix(e.Origin, "ntppool.org") { - vendor = vendorName(e.Name) - - if len(vendor) == 0 && e.Name != e.Origin { - var ok bool - poolCC, ok = getPoolCC(e.LabelName) - if !ok { - log.Printf("Could not get valid poolCC label for %+v", e) - } - } - } - - stats.Count++ - - qtypeString := dns.TypeToString[e.Qtype] - - userCC := "" - for _, cc := range e.Targets { - if len(cc) == 2 { - userCC = cc - break - } - } - - // []string{"zone", "vendor", "usercc", "poolcc", "qtype"}, - queries.WithLabelValues(e.Origin, vendor, userCC, poolCC, qtypeString).Inc() - - key := stats.Key(e) - - if s, ok := stats.Map[key]; ok { - s.Count++ - } else { - stats.Map[key] = &statsEntry{ - // Time: time.Unix(e.Time/int64(time.Second), 0), - Time: e.Time, - Origin: e.Origin, - Name: e.Name, - Vendor: vendor, - Label: e.LabelName, - PoolCC: poolCC, - Qtype: qtypeString, - Count: 1, - } - } - - return nil -} - -func (stats *Stats) Summarize() { - // pretty.Println(stats) - var timeStamp int64 - for k := range stats.Map { - timeStamp = stats.Map[k].Time - break - } - fmt.Printf("Stats %s count total: %d, summarized: %d\n", time.Unix(timeStamp, 0).String(), stats.Count, len(stats.Map)) -} diff --git a/geodns-logs/stats_test.go b/geodns-logs/stats_test.go deleted file mode 100644 index d8c7091b..00000000 --- a/geodns-logs/stats_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import ( - "flag" - "os" - "testing" -) - -func TestMain(m *testing.M) { - flag.Parse() - os.Exit(m.Run()) -} - -func TestPoolCC(t *testing.T) { - tests := []struct { - Input string - Expected string - Ok bool - }{ - {"pool.ntp.org", "", false}, - {"2.pool.ntp.org", "", false}, - {"us.pool.ntp.org", "us", true}, - {"0.us.pool.ntp.org", "us", true}, - {"asia.pool.ntp.org", "asia", true}, - {"3.asia.pool.ntp.org", "asia", true}, - {"3.example.pool.ntp.org", "", false}, - } - - for _, x := range tests { - got, ok := getPoolCC(x.Input) - if got != x.Expected { - t.Logf("Got '%s' but expected '%s' for '%s'", got, x.Expected, x.Input) - t.Fail() - } - if ok != x.Ok { - t.Logf("Got '%t' but expected '%t' for '%s'", ok, x.Ok, x.Input) - t.Fail() - } - } -} - -func TestVendorName(t *testing.T) { - tests := []struct { - Input string - Expected string - }{ - - {"pool.ntp.org.", ""}, - {"2.pool.ntp.org.", ""}, - {"us.pool.ntp.org.", "_country"}, - {"2.us.pool.ntp.org.", "_country"}, - {"europe.pool.ntp.org.", "_continent"}, - {"2.europe.pool.ntp.org.", "_continent"}, - {"0.example.pool.ntp.org.", "example"}, - {"3.example.pool.ntp.org.", "example"}, - } - - for _, x := range tests { - got := vendorName(x.Input) - if got != x.Expected { - t.Logf("Got '%s' but expected '%s' for '%s'", got, x.Expected, x.Input) - t.Fail() - } - } -} diff --git a/geodns.go b/geodns.go index e4571511..42843f50 100644 --- a/geodns.go +++ b/geodns.go @@ -17,6 +17,7 @@ package main */ import ( + "context" "flag" "fmt" "log" @@ -27,44 +28,40 @@ import ( "runtime" "runtime/pprof" "strings" + "syscall" "time" - "github.com/abh/geodns/applog" - "github.com/abh/geodns/health" - "github.com/abh/geodns/monitor" - "github.com/abh/geodns/querylog" - "github.com/abh/geodns/server" - "github.com/abh/geodns/targeting" - "github.com/abh/geodns/targeting/geoip2" - "github.com/abh/geodns/zones" "github.com/pborman/uuid" + "golang.org/x/sync/errgroup" + + "go.ntppool.org/common/version" + + "github.com/abh/geodns/v3/appconfig" + "github.com/abh/geodns/v3/applog" + "github.com/abh/geodns/v3/health" + "github.com/abh/geodns/v3/monitor" + "github.com/abh/geodns/v3/querylog" + "github.com/abh/geodns/v3/server" + "github.com/abh/geodns/v3/targeting" + "github.com/abh/geodns/v3/targeting/geoip2" + "github.com/abh/geodns/v3/zones" ) -// VERSION is the current version of GeoDNS -var VERSION string = "3.1.0-dev" -var buildTime string -var gitVersion string - -// Set development with the 'devel' build flag to load -// templates from disk instead of from the binary. -var development bool - var ( serverInfo *monitor.ServerInfo ) var ( - flagconfig = flag.String("config", "./dns/", "directory of zone files") - flagconfigfile = flag.String("configfile", "geodns.conf", "filename of config file (in 'config' directory)") - flagcheckconfig = flag.Bool("checkconfig", false, "check configuration and exit") - flagidentifier = flag.String("identifier", "", "identifier (hostname, pop name or similar)") - flaginter = flag.String("interface", "*", "set the listener address") - flagport = flag.String("port", "53", "default port number") - flaghttp = flag.String("http", ":8053", "http listen address (:8053)") - flaglog = flag.Bool("log", false, "be more verbose") - flagcpus = flag.Int("cpus", 1, "Set the maximum number of CPUs to use") - flagLogFile = flag.String("logfile", "", "log to file") - flagPrivateDebug = flag.Bool("privatedebug", false, "Make debugging queries accepted only on loopback") + flagconfig = flag.String("config", "./dns/", "directory of zone files") + flagconfigfile = flag.String("configfile", "geodns.conf", "filename of config file (in 'config' directory)") + flagcheckconfig = flag.Bool("checkconfig", false, "check configuration and exit") + flagidentifier = flag.String("identifier", "", "identifier (hostname, pop name or similar)") + flaginter = flag.String("interface", "*", "set the listener address") + flagport = flag.String("port", "53", "default port number") + flaghttp = flag.String("http", ":8053", "http listen address (:8053)") + flaglog = flag.Bool("log", false, "be more verbose") + flagcpus = flag.Int("cpus", 0, "Set the maximum number of CPUs to use") + flagLogFile = flag.String("logfile", "", "log to file") flagShowVersion = flag.Bool("version", false, "Show GeoDNS version") @@ -73,15 +70,12 @@ var ( ) func init() { - if len(gitVersion) > 0 { - VERSION = VERSION + "/" + gitVersion - } log.SetPrefix("geodns ") log.SetFlags(log.Lmicroseconds | log.Lshortfile) serverInfo = &monitor.ServerInfo{} - serverInfo.Version = VERSION + serverInfo.Version = version.Version() serverInfo.UUID = uuid.New() serverInfo.Started = time.Now() @@ -95,12 +89,7 @@ func main() { } if *flagShowVersion { - extra := []string{} - if len(buildTime) > 0 { - extra = append(extra, buildTime) - } - extra = append(extra, runtime.Version()) - fmt.Printf("geodns %s (%s)\n", VERSION, strings.Join(extra, ", ")) + fmt.Printf("geodns %s\n", version.Version()) os.Exit(0) } @@ -129,7 +118,7 @@ func main() { } if *flagcheckconfig { - err := configReader(configFileName) + err := appconfig.ConfigReader(configFileName) if err != nil { log.Println("Errors reading config", err) os.Exit(2) @@ -148,13 +137,25 @@ func main() { return } - if *flagcpus == 0 { - runtime.GOMAXPROCS(runtime.NumCPU()) - } else { + if *flagcpus > 0 { runtime.GOMAXPROCS(*flagcpus) } - log.Printf("Starting geodns %s (%s)\n", VERSION, runtime.Version()) + log.Printf("Starting geodns %s\n", version.Version()) + + ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill, syscall.SIGTERM) + g, ctx := errgroup.WithContext(ctx) + + g.Go(func() error { + <-ctx.Done() + log.Printf("server shutting down") + go func() { + time.Sleep(time.Second * 5) + log.Fatal("shutdown appears stalled; force exit") + os.Exit(99) + }() + return nil + }) if *cpuprofile != "" { prof, err := os.Create(*cpuprofile) @@ -174,14 +175,25 @@ func main() { } // load geodns.conf config - configReader(configFileName) + err := appconfig.ConfigReader(configFileName) + if err != nil { + log.Printf("error reading config file %s: %s", configFileName, err) + os.Exit(2) + } - if len(Config.Health.Directory) > 0 { - go health.DirectoryReader(Config.Health.Directory) + if len(appconfig.Config.Health.Directory) > 0 { + go health.DirectoryReader(appconfig.Config.Health.Directory) } // load (and re-load) zone data - go configWatcher(configFileName) + g.Go(func() error { + err := appconfig.ConfigWatcher(ctx, configFileName) + if err != nil { + log.Printf("config watcher error: %s", err) + return err + } + return nil + }) if *flaginter == "*" { addrs, _ := net.InterfaceAddrs() @@ -201,12 +213,8 @@ func main() { inter := getInterfaces() - if Config.HasStatHat() { - log.Println("StatHat integration has been removed in favor of more generic metrics") - } - - if len(Config.GeoIPDirectory()) > 0 { - geoProvider, err := geoip2.New(Config.GeoIPDirectory()) + if len(appconfig.Config.GeoIPDirectory()) > 0 { + geoProvider, err := geoip2.New(appconfig.Config.GeoIPDirectory()) if err != nil { log.Printf("Configuring geo provider: %s", err) } @@ -215,9 +223,29 @@ func main() { } } - srv := server.NewServer(serverInfo) + srv := server.NewServer(appconfig.Config, serverInfo) + + if qlc := appconfig.Config.AvroLog; len(qlc.Path) > 0 { + + maxsize := qlc.MaxSize + if maxsize < 50000 { + maxsize = 1000000 + } + maxtime, err := time.ParseDuration(qlc.MaxTime) + if err != nil { + log.Printf("could not parse avrolog maxtime setting %q: %s", qlc.MaxTime, err) + } + if maxtime < 1*time.Second { + maxtime = 1 * time.Second + } - if qlc := Config.QueryLog; len(qlc.Path) > 0 { + ql, err := querylog.NewAvroLogger(qlc.Path, maxsize, maxtime) + if err != nil { + log.Fatalf("Could not start avro query logger: %s", err) + } + srv.SetQueryLogger(ql) + + } else if qlc := appconfig.Config.QueryLog; len(qlc.Path) > 0 { ql, err := querylog.NewFileLogger(qlc.Path, qlc.MaxSize, qlc.Keep) if err != nil { log.Fatalf("Could not start file query logger: %s", err) @@ -229,24 +257,41 @@ func main() { if err != nil { log.Printf("error loading zones: %s", err) } - go muxm.Run() + + g.Go(func() error { + muxm.Run(ctx) + return nil + }) for _, host := range inter { - go srv.ListenAndServe(host) + host := host + g.Go(func() error { + return srv.ListenAndServe(ctx, host) + }) } + g.Go(func() error { + <-ctx.Done() + log.Printf("shutting down DNS servers") + err = srv.Shutdown() + if err != nil { + return err + } + return nil + }) + if len(*flaghttp) > 0 { - go func() { + g.Go(func() error { hs := NewHTTPServer(muxm, serverInfo) - hs.Run(*flaghttp) - }() + err := hs.Run(ctx, *flaghttp) + return err + }) } - terminate := make(chan os.Signal) - signal.Notify(terminate, os.Interrupt) - - <-terminate - log.Printf("geodns: signal received, stopping") + err = g.Wait() + if err != nil { + log.Printf("server error: %s", err) + } if *memprofile != "" { f, err := os.Create(*memprofile) diff --git a/go.mod b/go.mod index 4caca94f..263089c1 100644 --- a/go.mod +++ b/go.mod @@ -1,27 +1,50 @@ -module github.com/abh/geodns +module github.com/abh/geodns/v3 -go 1.16 +go 1.21 require ( - github.com/BurntSushi/toml v0.4.1 // indirect - github.com/abh/errorutil v0.0.0-20130729183701-f9bd360d00b9 - github.com/fsnotify/fsnotify v1.4.9 - github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/miekg/dns v1.1.43 - github.com/nxadm/tail v1.4.8 - github.com/oschwald/geoip2-golang v1.5.0 + github.com/abh/errorutil v1.0.0 + github.com/fsnotify/fsnotify v1.6.0 + github.com/golang/geo v0.0.0-20230421003525-6adc56603217 + github.com/hamba/avro/v2 v2.16.0 + github.com/miekg/dns v1.1.56 + github.com/oschwald/geoip2-golang v1.9.0 github.com/pborman/uuid v1.2.1 - github.com/prometheus/client_golang v1.11.0 - github.com/prometheus/common v0.30.0 // indirect - github.com/prometheus/procfs v0.7.2 // indirect - github.com/stretchr/testify v1.7.0 - golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect - golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect - google.golang.org/protobuf v1.27.1 // indirect + github.com/prometheus/client_golang v1.17.0 + github.com/stretchr/testify v1.8.4 + go.ntppool.org/common v0.2.1 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d + golang.org/x/sync v0.4.0 gopkg.in/gcfg.v1 v1.2.3 - gopkg.in/natefinch/lumberjack.v2 v2.0.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oschwald/maxminddb-golang v1.12.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4370b8e1..63eb672f 100644 --- a/go.sum +++ b/go.sum @@ -1,509 +1,144 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/abh/errorutil v0.0.0-20130729183701-f9bd360d00b9 h1:xwBQqR2Uq0q7FDzd1z9lr/sB6NrvMBvOBW5xzDn+DcM= -github.com/abh/errorutil v0.0.0-20130729183701-f9bd360d00b9/go.mod h1:L6nuAnQTJ2ZqobKWmzIcnTvpoOSWV8LulEOCk2rdhmM= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/abh/errorutil v1.0.0 h1:Ooe51ceN0MgmIWhagdOUFQSPQMvxpGoZT0HJE228Ojg= +github.com/abh/errorutil v1.0.0/go.mod h1:EY+X82MG0wLqrNvLnxHdhPPLeHOe9EL2Skq1PvgqIco= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo= -github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I= +github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hamba/avro/v2 v2.14.1 h1:mRkiRKjRTTs+yx0nVuM6z/q5zg3VBZfOe/01ngAnU6A= +github.com/hamba/avro/v2 v2.14.1/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo= +github.com/hamba/avro/v2 v2.16.0 h1:0XhyP65Hs8iMLtdSR0v7ZrwRjsbIZdvr7KzYgmx1Mbo= +github.com/hamba/avro/v2 v2.16.0/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= +github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw= -github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s= -github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= -github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc= +github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y= +github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= +github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.2 h1:zE6zJjRS9S916ptrZ326OU0++1XRwHgxkvCFflxx6Fo= -github.com/prometheus/procfs v0.7.2/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +go.ntppool.org/common v0.2.0 h1:ufVBJoflAwq1HzT1/kezUBPTP2lkYEBPRmg1wkuqDbo= +go.ntppool.org/common v0.2.0/go.mod h1:2vW9Wsc+N45GkBoo+i8gn4a2dPeGP9gLQntzw6aKH6E= +go.ntppool.org/common v0.2.1 h1:UZFFn/39Rn6esx+gzVceY4v5oznyNORJ7JugixdmKzM= +go.ntppool.org/common v0.2.1/go.mod h1:rTTb+LHJRogQ8rdmu3lZsa7zwWA9vg33fNaM6u/EKtI= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= +golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/health/health.go b/health/health.go index b16fe106..835c9929 100644 --- a/health/health.go +++ b/health/health.go @@ -3,7 +3,7 @@ package health import ( "fmt" - "github.com/abh/geodns/typeutil" + "github.com/abh/geodns/v3/typeutil" ) type HealthTester interface { diff --git a/health/healthtest/healthtest.go b/health/healthtest/healthtest.go index 61966c81..00ff441f 100644 --- a/health/healthtest/healthtest.go +++ b/health/healthtest/healthtest.go @@ -7,8 +7,8 @@ import ( "sync" "time" - "github.com/abh/geodns/applog" - "github.com/abh/geodns/typeutil" + "github.com/abh/geodns/v3/applog" + "github.com/abh/geodns/v3/typeutil" "github.com/miekg/dns" ) diff --git a/health/healthtest/healthtesters.go b/health/healthtest/healthtesters.go index b3a4749c..099238d3 100644 --- a/health/healthtest/healthtesters.go +++ b/health/healthtest/healthtesters.go @@ -16,8 +16,8 @@ import ( "strings" "time" - "github.com/abh/geodns/applog" - "github.com/abh/geodns/typeutil" + "github.com/abh/geodns/v3/applog" + "github.com/abh/geodns/v3/typeutil" ) /* diff --git a/http.go b/http.go index a2c6113f..60e03fbf 100644 --- a/http.go +++ b/http.go @@ -1,15 +1,20 @@ package main import ( + "context" + "errors" "fmt" "io" "log" "net/http" "strconv" + "time" - "github.com/abh/geodns/monitor" - "github.com/abh/geodns/zones" + "github.com/abh/geodns/v3/appconfig" + "github.com/abh/geodns/v3/monitor" + "github.com/abh/geodns/v3/zones" "github.com/prometheus/client_golang/prometheus/promhttp" + "golang.org/x/sync/errgroup" ) type httpServer struct { @@ -73,9 +78,36 @@ func (hs *httpServer) Mux() *http.ServeMux { return hs.mux } -func (hs *httpServer) Run(listen string) { +func (hs *httpServer) Run(ctx context.Context, listen string) error { log.Println("Starting HTTP interface on", listen) - log.Fatal(http.ListenAndServe(listen, &basicauth{h: hs.mux})) + + srv := http.Server{ + Addr: listen, + Handler: &basicauth{h: hs.mux}, + ReadTimeout: 5 * time.Second, + IdleTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + g, ctx := errgroup.WithContext(ctx) + + g.Go(func() error { + err := srv.ListenAndServe() + if err != nil { + if !errors.Is(err, http.ErrServerClosed) { + return err + } + } + return nil + }) + + g.Go(func() error { + <-ctx.Done() + log.Printf("shutting down http server") + return srv.Shutdown(ctx) + }) + + return g.Wait() } func (hs *httpServer) mainServer(w http.ResponseWriter, req *http.Request) { @@ -94,10 +126,10 @@ type basicauth struct { func (b *basicauth) ServeHTTP(w http.ResponseWriter, r *http.Request) { - cfgMutex.RLock() - user := Config.HTTP.User - password := Config.HTTP.Password - cfgMutex.RUnlock() + // cfgMutex.RLock() + user := appconfig.Config.HTTP.User + password := appconfig.Config.HTTP.Password + // cfgMutex.RUnlock() if len(user) == 0 { b.h.ServeHTTP(w, r) @@ -114,5 +146,4 @@ func (b *basicauth) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm=%q`, "GeoDNS Status")) http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return } diff --git a/http_test.go b/http_test.go index a67edd4f..af502840 100644 --- a/http_test.go +++ b/http_test.go @@ -2,16 +2,16 @@ package main import ( "bytes" - "io/ioutil" + "io" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/require" - "github.com/abh/geodns/targeting" - "github.com/abh/geodns/targeting/geoip2" - "github.com/abh/geodns/zones" + "github.com/abh/geodns/v3/targeting" + "github.com/abh/geodns/v3/targeting/geoip2" + "github.com/abh/geodns/v3/zones" ) func TestHTTP(t *testing.T) { @@ -36,7 +36,7 @@ func TestHTTP(t *testing.T) { res, err := http.Get(baseurl + "/version") require.Nil(t, err) - page, _ := ioutil.ReadAll(res.Body) + page, _ := io.ReadAll(res.Body) if !bytes.HasPrefix(page, []byte("GeoDNS ")) { t.Log("/version didn't start with 'GeoDNS '") diff --git a/querylog/avro.go b/querylog/avro.go new file mode 100644 index 00000000..2a3996d1 --- /dev/null +++ b/querylog/avro.go @@ -0,0 +1,266 @@ +package querylog + +import ( + "context" + _ "embed" + "fmt" + "log" + "os" + "path" + "strings" + "sync" + "sync/atomic" + "time" + + "golang.org/x/exp/slices" + + "github.com/hamba/avro/v2" + "github.com/hamba/avro/v2/ocf" +) + +//go:embed querylog.avsc +var schemaJson string + +type AvroLogger struct { + path string + maxsize int + maxtime time.Duration + + schema avro.Schema + + ctx context.Context + cancel context.CancelCauseFunc + wg sync.WaitGroup + + ch chan *Entry +} + +func NewAvroLogger(path string, maxsize int, maxtime time.Duration) (*AvroLogger, error) { + + schema, err := AvroSchema() + if err != nil { + return nil, err + } + + ctx, cancel := context.WithCancelCause(context.Background()) + l := &AvroLogger{ + ctx: ctx, + cancel: cancel, + path: path, + maxsize: maxsize, + maxtime: maxtime, + schema: schema, + ch: make(chan *Entry, 2000), + wg: sync.WaitGroup{}, + } + + go l.writer(ctx) + return l, nil +} + +func AvroSchema() (avro.Schema, error) { + schema, err := avro.Parse(schemaJson) + if err != nil { + return nil, err + } + return schema, nil +} + +func (l *AvroLogger) Write(e *Entry) error { + select { + case l.ch <- e: + return nil + default: + return fmt.Errorf("buffer full") + } +} + +// func (l *AvroFile) + +type avroFile struct { + fh *os.File + enc *ocf.Encoder + open bool + count int +} + +func (l *AvroLogger) writer(ctx context.Context) { + + mu := sync.Mutex{} + + timer := time.After(l.maxtime) + + openFiles := []*avroFile{} + + var fileCounter atomic.Int32 + + openFile := func() (*avroFile, error) { + // todo: communicate back to the main process when this goes wrong + + now := time.Now().UTC().Format("20060102-150405") + + fileCounter.Add(1) + + f, err := os.OpenFile(path.Join(l.path, fmt.Sprintf("log.%s.%d.avro.tmp", now, fileCounter.Load())), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0660) + if err != nil { + return nil, err + } + + enc, err := ocf.NewEncoder(schemaJson, f, ocf.WithCodec(ocf.Snappy)) + + if err != nil { + return nil, err + } + + l.wg.Add(1) + a := &avroFile{fh: f, enc: enc, open: true} + + // log.Printf("opened %s", a.fh.Name()) + + mu.Lock() + defer mu.Unlock() + openFiles = append([]*avroFile{a}, openFiles...) + + timer = time.After(l.maxtime) + + return a, nil + } + + currentFile, err := openFile() + if err != nil { + log.Fatalf("openfile error: %s", err) + } + + closeFile := func(af *avroFile) error { + + mu.Lock() + idx := slices.Index(openFiles, af) + if idx >= 0 { + openFiles = slices.Delete(openFiles, idx, idx+1) + } else { + log.Printf("could not find avroFile for closing in openFiles list") + } + + if !af.open { + mu.Unlock() + log.Printf("called closeFile on file already being closed %s", af.fh.Name()) + return nil + } + + af.open = false + mu.Unlock() + + defer l.wg.Done() + + // log.Printf("closing %s", af.fh.Name()) + + if err := af.enc.Flush(); err != nil { + return err + } + if err := af.fh.Sync(); err != nil { + return err + } + if err := af.fh.Close(); err != nil { + return err + } + + tmpName := af.fh.Name() + newName := strings.TrimSuffix(tmpName, ".tmp") + if tmpName == newName { + return fmt.Errorf("unexpected tmp file name %s", tmpName) + } + + // log.Printf("renaming to %s", newName) + if err := os.Rename(tmpName, newName); err != nil { + return err + } + return nil + } + + for { + select { + case e := <-l.ch: + currentFile.count++ + err := currentFile.enc.Encode(e) + if err != nil { + log.Fatal(err) + } + if currentFile.count%1000 == 0 { + size, err := currentFile.fh.Seek(0, 2) + if err != nil { + log.Printf("could not seek avro file: %s", err) + continue + } + if size > int64(l.maxsize) { + // log.Printf("rotating avro file for size") + currentFile, err = openFile() + if err != nil { + log.Printf("could not open new avro file: %s", err) + } + } + } + + case <-ctx.Done(): + log.Printf("closing avro files") + + // drain the buffer within reason + count := 0 + drain: + for { + select { + case e := <-l.ch: + count++ + err := currentFile.enc.Encode(e) + if err != nil { + log.Fatal(err) + } + if count > 40000 { + break drain + } + default: + break drain + } + } + + for i := len(openFiles) - 1; i >= 0; i-- { + err := closeFile(openFiles[i]) + if err != nil { + log.Printf("error closing file: %s", err) + } + } + return + + case <-timer: + if currentFile.count == 0 { + timer = time.After(l.maxtime) + continue + } + + // log.Printf("rotating avro file for time") + + var err error + currentFile, err = openFile() + if err != nil { + log.Printf("could not open new avrofile: %s", err) + } else { + for i, af := range openFiles { + if i == 0 || af == currentFile { + continue + } + err := closeFile(af) + if err != nil { + log.Printf("error closing old avro files: %s", err) + } + } + } + } + } + +} + +func (l *AvroLogger) Close() error { + l.cancel(fmt.Errorf("closing")) + <-l.ctx.Done() + l.wg.Wait() // wait for all files to be closed + return nil +} diff --git a/querylog/avro_test.go b/querylog/avro_test.go new file mode 100644 index 00000000..cb593dc4 --- /dev/null +++ b/querylog/avro_test.go @@ -0,0 +1,55 @@ +package querylog + +import ( + "encoding/json" + "io" + "os" + "testing" + "time" +) + +func TestAvro(t *testing.T) { + + tmppath, err := os.MkdirTemp("", "geodns.avro") + if err != nil { + t.Fatalf("could not create temp dir: %s", err) + } + + lg, err := NewAvroLogger(tmppath, 5000000, 4*time.Second) + if err != nil { + t.Log(err) + t.FailNow() + } + + dataFh, err := os.Open("testdata/queries.log") + if err != nil { + t.Log("no test data available") + t.SkipNow() + } + dec := json.NewDecoder(dataFh) + + count := 0 + for { + e := Entry{} + err := dec.Decode(&e) + if err != nil { + if err == io.EOF { + break + } + t.Logf("could not decode test data: %s", err) + continue + } + count++ + lg.Write(&e) + } + + t.Logf("Write count: %d", count) + + // time.Sleep(time.Second * 2) + + err = lg.Close() + if err != nil { + t.Log(err) + t.Fail() + } +} diff --git a/querylog/file.go b/querylog/file.go new file mode 100644 index 00000000..0ccbf6d3 --- /dev/null +++ b/querylog/file.go @@ -0,0 +1,35 @@ +package querylog + +import ( + "encoding/json" + + "gopkg.in/natefinch/lumberjack.v2" +) + +type FileLogger struct { + logger lumberjack.Logger +} + +func NewFileLogger(filename string, maxsize int, keep int) (*FileLogger, error) { + fl := &FileLogger{} + fl.logger = lumberjack.Logger{ + Filename: filename, + MaxSize: maxsize, // megabytes + MaxBackups: keep, + } + return fl, nil +} + +func (l *FileLogger) Write(e *Entry) error { + js, err := json.Marshal(e) + if err != nil { + return err + } + js = append(js, []byte("\n")...) + _, err = l.logger.Write(js) + return err +} + +func (l *FileLogger) Close() error { + return l.logger.Close() +} diff --git a/querylog/querylog.avsc b/querylog/querylog.avsc new file mode 100644 index 00000000..dc5350af --- /dev/null +++ b/querylog/querylog.avsc @@ -0,0 +1,34 @@ +{ + "type": "record", + "name": "GeodnsQuery", + "namespace": "develooper", + "fields" : [ + {"name": "Time", "type": "long", "logicalType": "timestamp-micros"}, + {"name": "Hostname", "type": "string"}, + {"name": "Origin", "type": "string"}, + {"name": "Name", "type": "string", "default": "" }, + {"name": "Qtype", "type": "int"}, + {"name": "Rcode", "type": "int"}, + {"name": "AnswerCount", "type": "int"}, + {"name": "Targets", + "type": { + "type": "array", + "items": "string", + "default": [] + } + }, + {"name": "AnswerData", + "type": { + "type": "array", + "items": "string", + "default": [] + } + }, + {"name": "LabelName", "type": "string"}, + {"name": "RemoteAddr", "type": "string"}, + {"name": "ClientAddr", "type": "string"}, + {"name": "HasECS", "type": "boolean"}, + {"name": "IsTCP", "type": "boolean"}, + {"name": "Version", "type": "string"} + ] +} \ No newline at end of file diff --git a/querylog/querylog.go b/querylog/querylog.go index c55c37ee..9b296830 100644 --- a/querylog/querylog.go +++ b/querylog/querylog.go @@ -1,50 +1,24 @@ package querylog -import ( - "encoding/json" - - "gopkg.in/natefinch/lumberjack.v2" -) - type QueryLogger interface { Write(*Entry) error + Close() error } -// easyjson:json type Entry struct { - Time int64 - Origin string - Name string - Qtype uint16 - Rcode int - Answers int - Targets []string - LabelName string - RemoteAddr string - ClientAddr string - HasECS bool -} - -type FileLogger struct { - logger lumberjack.Logger -} - -func NewFileLogger(filename string, maxsize int, keep int) (*FileLogger, error) { - fl := &FileLogger{} - fl.logger = lumberjack.Logger{ - Filename: filename, - MaxSize: maxsize, // megabytes - MaxBackups: keep, - } - return fl, nil -} - -func (l *FileLogger) Write(e *Entry) error { - js, err := json.Marshal(e) - if err != nil { - return err - } - js = append(js, []byte("\n")...) - _, err = l.logger.Write(js) - return err + Time int64 + Hostname string `json:",omitempty"` // not filled in by geodns + Origin string + Name string + Qtype uint16 + Rcode int + AnswerCount int `json:"Answers"` + Targets []string + AnswerData []string + LabelName string + RemoteAddr string + ClientAddr string + HasECS bool + IsTCP bool + Version string } diff --git a/querylog/testdata/queries.log b/querylog/testdata/queries.log new file mode 100644 index 00000000..1d72450f --- /dev/null +++ b/querylog/testdata/queries.log @@ -0,0 +1,20 @@ +{"Time":1688414139649978091,"Origin":"pool.ntp.org","Name":"0.ubuntu.pool.ntp.org.","Qtype":2,"Rcode":0,"Answers":0,"Targets":["de","europe","@"],"LabelName":"","RemoteAddr":"141.35.40.33","ClientAddr":"141.35.40.33/32","HasECS":false} +{"Time":1688414139650499477,"Origin":"pool.ntp.org","Name":"pool.ntp.org.","Qtype":28,"Rcode":0,"Answers":0,"Targets":["in","asia","@"],"LabelName":"","RemoteAddr":"2405:200:1632:1957:78::4","ClientAddr":"2405:200:1632:1957:78::4/128","HasECS":false} +{"Time":1688414139650694570,"Origin":"pool.ntp.org","Name":"2.debian.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":4,"Targets":["us","north-america","@"],"LabelName":"2.us","RemoteAddr":"192.178.36.9","ClientAddr":"70.225.160.0/24","HasECS":true} +{"Time":1688414139650814098,"Origin":"pool.ntp.org","Name":"ru.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":4,"Targets":["br","south-america","@"],"LabelName":"ru","RemoteAddr":"187.58.136.98","ClientAddr":"187.58.136.98/32","HasECS":false} +{"Time":1688414139650917846,"Origin":"pool.ntp.org","Name":"pool.ntp.org.","Qtype":28,"Rcode":0,"Answers":0,"Targets":["in","asia","@"],"LabelName":"","RemoteAddr":"2405:200:1961:3937:78::5","ClientAddr":"2405:200:1961:3937:78::5/128","HasECS":false} +{"Time":1688414139651209653,"Origin":"pool.ntp.org","Name":"0.fr.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":4,"Targets":["fr","europe","@"],"LabelName":"0.fr","RemoteAddr":"2a00:1db8:0:3::22","ClientAddr":"2a00:1db8:0:3::22/128","HasECS":false} +{"Time":1688414139651529434,"Origin":"pool.ntp.org","Name":"android.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":4,"Targets":["de","europe","@"],"LabelName":"de","RemoteAddr":"85.22.54.58","ClientAddr":"85.22.54.58/32","HasECS":false} +{"Time":1688414139651670493,"Origin":"pool.ntp.org","Name":"2.sonostime.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":4,"Targets":["cn","asia","@"],"LabelName":"2.cn","RemoteAddr":"2409:8018:2001::2","ClientAddr":"2409:8018:2001::2/128","HasECS":false} +{"Time":1688414139651980436,"Origin":"pool.ntp.org","Name":"pool.ntp.org.","Qtype":28,"Rcode":0,"Answers":0,"Targets":["in","asia","@"],"LabelName":"","RemoteAddr":"49.45.29.164","ClientAddr":"49.45.29.164/32","HasECS":false} +{"Time":1688414139652110915,"Origin":"pool.ntp.org","Name":"ubnt.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":4,"Targets":["us","north-america","@"],"LabelName":"us","RemoteAddr":"67.146.32.23","ClientAddr":"67.146.32.23/32","HasECS":false} +{"Time":1688414139652558730,"Origin":"pool.ntp.org","Name":"0.openwrt.pool.ntp.org.","Qtype":28,"Rcode":0,"Answers":0,"Targets":["cn","asia","@"],"LabelName":"","RemoteAddr":"172.253.5.2","ClientAddr":"112.226.217.0/24","HasECS":true} +{"Time":1688414139652799400,"Origin":"pool.ntp.org","Name":"0.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":4,"Targets":["us","north-america","@"],"LabelName":"0.us","RemoteAddr":"74.125.191.8","ClientAddr":"209.236.115.0/24","HasECS":true} +{"Time":1688414139653295247,"Origin":"pool.ntp.org","Name":"hk.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":4,"Targets":["cn","asia","@"],"LabelName":"hk","RemoteAddr":"2409:8020:2000:924::d","ClientAddr":"2409:8020:2000:924::d/128","HasECS":false} +{"Time":1688414139653833966,"Origin":"pool.ntp.org","Name":"pool.ntp.org.","Qtype":28,"Rcode":0,"Answers":0,"Targets":["in","asia","@"],"LabelName":"","RemoteAddr":"2405:200:160b:1957:78::5","ClientAddr":"2405:200:160b:1957:78::5/128","HasECS":false} +{"Time":1688414139653988631,"Origin":"pool.ntp.org","Name":"0.formlabs.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":2,"Targets":["kr","asia","@"],"LabelName":"0.kr","RemoteAddr":"106.241.133.17","ClientAddr":"106.241.133.17/32","HasECS":false} +{"Time":1688414139654098471,"Origin":"pool.ntp.org","Name":"ru.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":4,"Targets":["bd","asia","@"],"LabelName":"ru","RemoteAddr":"2404:6800:4000:101d::105","ClientAddr":"103.206.231.0/24","HasECS":true} +{"Time":1688414139654304414,"Origin":"pool.ntp.org","Name":"3.debian.pool.ntp.org.","Qtype":28,"Rcode":0,"Answers":0,"Targets":["tw","asia","@"],"LabelName":"","RemoteAddr":"2001:b030:2154:fffc::1","ClientAddr":"2001:b030:2154:fffc::1/128","HasECS":false} +{"Time":1688414139654809920,"Origin":"pool.ntp.org","Name":"0.pool.ntp.org.","Qtype":1,"Rcode":0,"Answers":2,"Targets":["bd","asia","@"],"LabelName":"0.bd","RemoteAddr":"2404:6800:4000:1002::102","ClientAddr":"103.106.164.0/24","HasECS":true} +{"Time":1688414139654945178,"Origin":"pool.ntp.org","Name":"europe.pool.ntp.org.","Qtype":28,"Rcode":0,"Answers":0,"Targets":["cn","asia","@"],"LabelName":"","RemoteAddr":"172.253.6.3","ClientAddr":"123.149.74.0/24","HasECS":true} +{"Time":1688414139655044528,"Origin":"pool.ntp.org","Name":"sg.pool.ntp.org.","Qtype":28,"Rcode":0,"Answers":0,"Targets":["my","asia","@"],"LabelName":"","RemoteAddr":"123.136.100.101","ClientAddr":"123.136.100.101/32","HasECS":false} diff --git a/scripts/download-release b/scripts/download-release index 57f1cdcf..130620c2 100755 --- a/scripts/download-release +++ b/scripts/download-release @@ -1,23 +1,32 @@ #!/bin/bash -BUILD=$1 -DIR=$2 +BASE=$1 +BUILD=$2 +DIR=$3 set -euo pipefail if [ -z "$DIR" ]; then - echo run with $0 BUILD_NUMBER DIR + echo run with $0 NAME BUILD_NUMBER DIR exit 2 fi mkdir -p $DIR -BASE=https://geodns.bitnames.com/builds/$BUILD +BASE=https://geodns.bitnames.com/${BASE}/builds/${BUILD} + +files=`curl -sSf ${BASE}/checksums.txt | awk '{print $2}'` +metafiles="checksums.txt metadata.json CHANGELOG.md artifacts.json" + +for f in $metafiles; do + url=$BASE/$f + echo downloading $url + curl --remove-on-error -sSfRo $DIR/$f $url || true +done -files=`curl -sf $BASE/checksums.txt | awk '{print $2}'` for f in $files; do url=$BASE/$f - echo downloading $f - curl -sfRo $DIR/$f $url + echo downloading $url + curl --remove-on-error -sSfRo $DIR/$f $url done diff --git a/scripts/run-goreleaser b/scripts/run-goreleaser index 2261b3e9..078aa5a7 100755 --- a/scripts/run-goreleaser +++ b/scripts/run-goreleaser @@ -2,6 +2,8 @@ set -euo pipefail +go install github.com/goreleaser/goreleaser@v1.20.0 + DRONE_TAG=${DRONE_TAG-""} is_snapshot="" @@ -10,4 +12,4 @@ if [ -z "$DRONE_TAG" ]; then is_snapshot="--snapshot" fi -curl -fsL https://git.io/goreleaser | bash -s -- release --snapshot -p 3 +goreleaser release $is_snapshot -p 6 --skip-publish diff --git a/server/querylog_test.go b/server/querylog_test.go new file mode 100644 index 00000000..6da406ae --- /dev/null +++ b/server/querylog_test.go @@ -0,0 +1,56 @@ +package server + +import ( + "testing" + + "github.com/abh/geodns/v3/querylog" + "github.com/miekg/dns" +) + +type testLogger struct { + lastLog querylog.Entry +} + +func (l *testLogger) Close() error { + return nil +} + +func (l *testLogger) Write(ql *querylog.Entry) error { + l.lastLog = *ql + return nil +} + +func (l *testLogger) Last() querylog.Entry { + // l.logged = false + return l.lastLog +} + +func testQueryLog(srv *Server) func(*testing.T) { + + tlog := &testLogger{} + + srv.SetQueryLogger(tlog) + + return func(t *testing.T) { + + r := exchange(t, "www-alias.example.com.", dns.TypeA) + expected := "geo.bitnames.com." + answer := r.Answer[0].(*dns.CNAME).Target + if answer != expected { + t.Logf("expected CNAME %s, got %s", expected, answer) + t.Fail() + } + + last := tlog.Last() + // t.Logf("last log: %+v", last) + + if last.Name != "www-alias.example.com." { + t.Logf("didn't get qname in Name querylog") + t.Fail() + } + if last.LabelName != "www" { + t.Logf("LabelName didn't contain resolved label") + t.Fail() + } + } +} diff --git a/server/serve.go b/server/serve.go index 67523758..f28bdda2 100644 --- a/server/serve.go +++ b/server/serve.go @@ -4,16 +4,18 @@ import ( "encoding/hex" "encoding/json" "fmt" + "log" "net" + "net/netip" "os" "strconv" "strings" "time" - "github.com/abh/geodns/applog" - "github.com/abh/geodns/edns" - "github.com/abh/geodns/querylog" - "github.com/abh/geodns/zones" + "github.com/abh/geodns/v3/applog" + "github.com/abh/geodns/v3/edns" + "github.com/abh/geodns/v3/querylog" + "github.com/abh/geodns/v3/zones" "github.com/miekg/dns" "github.com/prometheus/client_golang/prometheus" @@ -33,11 +35,19 @@ func (srv *Server) serve(w dns.ResponseWriter, req *dns.Msg, z *zones.Zone) { var qle *querylog.Entry if srv.queryLogger != nil { + + var isTcp bool + if net := w.LocalAddr().Network(); net == "tcp" { + isTcp = true + } + qle = &querylog.Entry{ - Time: time.Now().UnixNano(), - Origin: z.Origin, - Name: strings.ToLower(qnamefqdn), - Qtype: qtype, + Time: time.Now().UnixNano(), + Origin: z.Origin, + Name: strings.ToLower(qnamefqdn), + Qtype: qtype, + Version: srv.info.Version, + IsTCP: isTcp, } defer srv.queryLogger.Write(qle) } @@ -78,7 +88,15 @@ func (srv *Server) serve(w dns.ResponseWriter, req *dns.Msg, z *zones.Zone) { applog.Println("Got edns-client-subnet", e.Address, e.Family, e.SourceNetmask, e.SourceScope) if e.Address != nil { ecs = e - ip = e.Address + + if ecsip, ok := netip.AddrFromSlice(e.Address); ok { + if ecsip.IsGlobalUnicast() && + !(ecsip.IsPrivate() || + ecsip.IsLinkLocalMulticast() || + ecsip.IsInterfaceLocalMulticast()) { + ip = ecsip.AsSlice() + } + } if qle != nil { qle.HasECS = true @@ -98,6 +116,11 @@ func (srv *Server) serve(w dns.ResponseWriter, req *dns.Msg, z *zones.Zone) { targets, netmask, location := z.Options.Targeting.GetTargets(ip, z.HasClosest) + // if the ECS IP didn't get targets, try the real IP instead + if l := len(targets); (l == 0 || l == 1 && targets[0] == "@") && !ip.Equal(realIP) { + targets, netmask, location = z.Options.Targeting.GetTargets(realIP, z.HasClosest) + } + m := &dns.Msg{} // setup logging of answers and rcode @@ -105,7 +128,30 @@ func (srv *Server) serve(w dns.ResponseWriter, req *dns.Msg, z *zones.Zone) { qle.Targets = targets defer func() { qle.Rcode = m.Rcode - qle.Answers = len(m.Answer) + qle.AnswerCount = len(m.Answer) + + for _, rr := range m.Answer { + var s string + switch a := rr.(type) { + case *dns.A: + s = a.A.String() + case *dns.AAAA: + s = a.AAAA.String() + case *dns.CNAME: + s = a.Target + case *dns.MX: + s = a.Mx + case *dns.NS: + s = a.Ns + case *dns.SRV: + s = a.Target + case *dns.TXT: + s = strings.Join(a.Txt, " ") + } + if len(s) > 0 { + qle.AnswerData = append(qle.AnswerData, s) + } + } }() } @@ -253,7 +299,7 @@ func (srv *Server) serve(w dns.ResponseWriter, req *dns.Msg, z *zones.Zone) { if qle != nil { qle.LabelName = label.Label - qle.Answers = len(m.Answer) + qle.AnswerCount = len(m.Answer) } break @@ -265,11 +311,16 @@ func (srv *Server) serve(w dns.ResponseWriter, req *dns.Msg, z *zones.Zone) { m.Ns = append(m.Ns, z.SoaRR()) } + qlabelMetric := "_" + if srv.DetailedMetrics { + qlabelMetric = qlabel + } + srv.metrics.Queries.With( prometheus.Labels{ "zone": z.Origin, "qtype": dns.TypeToString[qtype], - "qname": qlabel, + "qname": qlabelMetric, "rcode": dns.RcodeToString[m.Rcode], }).Inc() @@ -285,7 +336,7 @@ func (srv *Server) serve(w dns.ResponseWriter, req *dns.Msg, z *zones.Zone) { applog.Printf("Error writing packet: %q, %s", err, m) dns.HandleFailed(w, req) } - return + } func (srv *Server) statusRR(label string) []dns.RR { @@ -302,6 +353,9 @@ func (srv *Server) statusRR(label string) []dns.RR { status["up"] = strconv.Itoa(int(time.Since(srv.info.Started).Seconds())) js, err := json.Marshal(status) + if err != nil { + log.Printf("error marshaling json status: %s", err) + } return []dns.RR{&dns.TXT{Hdr: h, Txt: []string{string(js)}}} } diff --git a/server/serve_test.go b/server/serve_test.go index 3f0d6db0..baba09f8 100644 --- a/server/serve_test.go +++ b/server/serve_test.go @@ -1,6 +1,7 @@ package server import ( + "context" "net" "reflect" "strings" @@ -10,8 +11,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/abh/geodns/monitor" - "github.com/abh/geodns/zones" + "github.com/abh/geodns/v3/appconfig" + "github.com/abh/geodns/v3/monitor" + "github.com/abh/geodns/v3/zones" "github.com/miekg/dns" ) @@ -22,23 +24,31 @@ const ( func TestServe(t *testing.T) { serverInfo := &monitor.ServerInfo{} - srv := NewServer(serverInfo) + srv := NewServer(appconfig.Config, serverInfo) + ctx, cancel := context.WithCancel(context.Background()) mm, err := zones.NewMuxManager("../dns", srv) if err != nil { t.Fatalf("Loading test zones: %s", err) } - go mm.Run() + go mm.Run(ctx) - // listenAndServe returns after listening on udp + tcp, so just - // wait for it before continuing - srv.ListenAndServe(PORT) + go func() { + srv.ListenAndServe(ctx, PORT) + }() // ensure service has properly started before we query it time.Sleep(500 * time.Millisecond) t.Run("Serving", testServing) + t.Run("QueryLog", testQueryLog(srv)) + + // todo: run test queries? + + cancel() + + srv.Shutdown() } func testServing(t *testing.T) { @@ -72,11 +82,34 @@ func testServing(t *testing.T) { // NOERROR for A request checkRcode(t, r.Rcode, dns.RcodeSuccess, "_status.pgeodns") + // bar is an alias r = exchange(t, "bar.test.example.com.", dns.TypeA) ip := r.Answer[0].(*dns.A).A + if ip.String() != "192.168.1.2" { + t.Logf("unexpected A record for bar.test.example.com: %s", ip.String()) + t.Fail() + } + + // bar is an alias to test, the SOA record should be for test + r = exchange(t, "_.root-alias.test.example.com.", dns.TypeA) + if len(r.Answer) > 0 { + t.Errorf("got answers for _.root-alias.test.example.com") + } + if len(r.Ns) == 0 { + t.Fatalf("_.root-alias.test didn't return auth section") + } + if n := r.Ns[0].(*dns.SOA).Header().Name; n != "test.example.com." { + t.Fatalf("_.root-alias.test didn't have test.example.com soa: %s", n) + } - // c.Check(ip.String(), Equals, "192.168.1.2") - // c.Check(int(r.Answer[0].Header().Ttl), Equals, 601) + // root-alias is an alias to test (apex), but the NS records shouldn't be on root-alias + r = exchange(t, "root-alias.test.example.com.", dns.TypeNS) + if len(r.Answer) > 0 { + t.Errorf("got unexpected answers for root-alias.test.example.com NS") + } + if len(r.Ns) == 0 { + t.Fatalf("root-alias.test NS didn't return auth section") + } r = exchange(t, "test.example.com.", dns.TypeSOA) soa := r.Answer[0].(*dns.SOA) @@ -84,11 +117,10 @@ func testServing(t *testing.T) { assert.Equal(t, 3, int(serial)) // no AAAA records for 'bar', so check we get a soa record back - r = exchange(t, "test.example.com.", dns.TypeAAAA) + r = exchange(t, "bar.test.example.com.", dns.TypeAAAA) soa2 := r.Ns[0].(*dns.SOA) if !reflect.DeepEqual(soa, soa2) { - t.Logf("AAAA empty NOERROR soa record different from SOA request") - t.Fail() + t.Errorf("AAAA empty NOERROR soa record different from SOA request") } // CNAMEs diff --git a/server/server.go b/server/server.go index 5be0fc92..ea91b9bf 100644 --- a/server/server.go +++ b/server/server.go @@ -1,11 +1,18 @@ package server import ( + "context" + "errors" "log" + "sync" + "time" - "github.com/abh/geodns/monitor" - "github.com/abh/geodns/querylog" - "github.com/abh/geodns/zones" + "github.com/abh/geodns/v3/appconfig" + "github.com/abh/geodns/v3/monitor" + "github.com/abh/geodns/v3/querylog" + "github.com/abh/geodns/v3/zones" + "go.ntppool.org/common/version" + "golang.org/x/sync/errgroup" "github.com/miekg/dns" "github.com/prometheus/client_golang/prometheus" @@ -17,15 +24,20 @@ type serverMetrics struct { // Server ... type Server struct { - queryLogger querylog.QueryLogger - mux *dns.ServeMux PublicDebugQueries bool - info *monitor.ServerInfo - metrics *serverMetrics + DetailedMetrics bool + + queryLogger querylog.QueryLogger + mux *dns.ServeMux + info *monitor.ServerInfo + metrics *serverMetrics + + lock sync.Mutex + dnsServers []*dns.Server } // NewServer ... -func NewServer(si *monitor.ServerInfo) *Server { +func NewServer(config *appconfig.AppConfig, si *monitor.ServerInfo) *Server { mux := dns.NewServeMux() queries := prometheus.NewCounterVec( @@ -37,20 +49,21 @@ func NewServer(si *monitor.ServerInfo) *Server { ) prometheus.MustRegister(queries) - buildInfo := prometheus.NewGaugeVec( + version.RegisterMetric("geodns", prometheus.DefaultRegisterer) + + instanceInfo := prometheus.NewGaugeVec( prometheus.GaugeOpts{ - Name: "geodns_build_info", - Help: "GeoDNS build information (in labels)", + Name: "geodns_instance_info", + Help: "GeoDNS instance information", }, - []string{"Version", "ID", "IP", "Group"}, + []string{"ID", "IP", "Group"}, ) - prometheus.MustRegister(buildInfo) - + prometheus.MustRegister(instanceInfo) group := "" if len(si.Groups) > 0 { group = si.Groups[0] } - buildInfo.WithLabelValues(si.Version, si.ID, si.IP, group).Set(1) + instanceInfo.WithLabelValues(si.ID, si.IP, group).Set(1) startTime := prometheus.NewGauge( prometheus.GaugeOpts{ @@ -67,7 +80,14 @@ func NewServer(si *monitor.ServerInfo) *Server { Queries: queries, } - return &Server{mux: mux, info: si, metrics: metrics} + return &Server{ + PublicDebugQueries: appconfig.Config.DNS.PublicDebugQueries, + DetailedMetrics: appconfig.Config.DNS.DetailedMetrics, + + mux: mux, + info: si, + metrics: metrics, + } } // SetQueryLogger configures the query logger. For now it only supports writing to @@ -97,26 +117,68 @@ func (srv *Server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { srv.mux.ServeDNS(w, r) } +func (srv *Server) addDNSServer(dnsServer *dns.Server) { + srv.lock.Lock() + defer srv.lock.Unlock() + srv.dnsServers = append(srv.dnsServers, dnsServer) +} + // ListenAndServe starts the DNS server on the specified IP -// (both tcp and udp) and returns. If something goes wrong -// it will crash the process with an error message. -func (srv *Server) ListenAndServe(ip string) { +// (both tcp and udp). It returns an error if +// something goes wrong. +func (srv *Server) ListenAndServe(ctx context.Context, ip string) error { prots := []string{"udp", "tcp"} + g, _ := errgroup.WithContext(ctx) + for _, prot := range prots { - go func(p string) { + + p := prot + + g.Go(func() error { server := &dns.Server{ Addr: ip, Net: p, Handler: srv, } + srv.addDNSServer(server) + log.Printf("Opening on %s %s", ip, p) if err := server.ListenAndServe(); err != nil { - log.Fatalf("geodns: failed to setup %s %s: %s", ip, p, err) + log.Printf("geodns: failed to setup %s %s: %s", ip, p, err) + return err } - log.Fatalf("geodns: ListenAndServe unexpectedly returned") - }(prot) + return nil + }) + } + + // the servers will be shutdown when Shutdown() is called + return g.Wait() +} + +// Shutdown gracefully shuts down the server +func (srv *Server) Shutdown() error { + var errs []error + + for _, dnsServer := range srv.dnsServers { + timeoutCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + err := dnsServer.ShutdownContext(timeoutCtx) + if err != nil { + errs = append(errs, err) + } + } + + if srv.queryLogger != nil { + err := srv.queryLogger.Close() + if err != nil { + errs = append(errs, err) + } } + + err := errors.Join(errs...) + + return err } diff --git a/service-logs/log/run b/service-logs/log/run deleted file mode 100755 index e0b30d29..00000000 --- a/service-logs/log/run +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -mkdir -p /var/log/geodns-logs -chown nobody /var/log/geodns-logs -exec setuidgid nobody multilog s10000000 n3 /var/log/geodns-logs diff --git a/service-logs/run b/service-logs/run deleted file mode 100755 index 14b71c58..00000000 --- a/service-logs/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -exec 2>&1 -sleep 1 # just in case we spin for some reason - -cd /opt/geodns - -ulimit -n 8192 - -ID="" -if [ -e env/ID ]; then - ID=`head -1 env/ID` - if [ ! -z "$ID" ]; then - ID="--identifier=$ID" - fi -fi - -exec softlimit -d200000000 envdir ./env setuidgid nobody ./geodns-logs $ID /var/log/geodns/queries.log diff --git a/targeting/geoip2/geoip2.go b/targeting/geoip2/geoip2.go index 43428181..3e96f56d 100644 --- a/targeting/geoip2/geoip2.go +++ b/targeting/geoip2/geoip2.go @@ -11,8 +11,8 @@ import ( "sync" "time" - "github.com/abh/geodns/countries" - "github.com/abh/geodns/targeting/geo" + "github.com/abh/geodns/v3/countries" + "github.com/abh/geodns/v3/targeting/geo" gdb "github.com/oschwald/geoip2-golang" ) @@ -40,6 +40,7 @@ func FindDB() string { "/usr/share/local/GeoIP/", // source install? "/usr/local/share/GeoIP/", // FreeBSD "/opt/local/share/GeoIP/", // MacPorts + "/opt/homebrew/var/GeoIP", // Homebrew } for _, dir := range dirs { if _, err := os.Stat(dir); err != nil { diff --git a/targeting/targeting.go b/targeting/targeting.go index 057bfe9f..faa800d8 100644 --- a/targeting/targeting.go +++ b/targeting/targeting.go @@ -6,7 +6,7 @@ import ( "net" "strings" - "github.com/abh/geodns/targeting/geo" + "github.com/abh/geodns/v3/targeting/geo" ) type TargetOptions int @@ -173,7 +173,7 @@ func ParseTargets(v string) (tgt TargetOptions, err error) { case "ip": x = TargetIP default: - err = fmt.Errorf("Unknown targeting option '%s'", t) + err = fmt.Errorf("unknown targeting option '%s'", t) } tgt = tgt | x } diff --git a/targeting/targeting_test.go b/targeting/targeting_test.go index c2da5aee..8a9907d9 100644 --- a/targeting/targeting_test.go +++ b/targeting/targeting_test.go @@ -5,7 +5,7 @@ import ( "reflect" "testing" - "github.com/abh/geodns/targeting/geoip2" + "github.com/abh/geodns/v3/targeting/geoip2" ) func TestTargetString(t *testing.T) { @@ -25,7 +25,7 @@ func TestTargetParse(t *testing.T) { t.Logf("Expected '@ country', got '%s'", str) t.Fail() } - if err.Error() != "Unknown targeting option 'foo'" { + if err.Error() != "unknown targeting option 'foo'" { t.Log("Failed erroring on an unknown targeting option") t.Fail() } @@ -54,7 +54,7 @@ func TestGetTargets(t *testing.T) { g, err := geoip2.New(geoip2.FindDB()) if err != nil { - t.Fatalf("opening geoip2: %s", err) + t.Skipf("opening geoip2: %s", err) } Setup(g) diff --git a/templates_devel.go b/templates_devel.go deleted file mode 100644 index 5c226cff..00000000 --- a/templates_devel.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build devel - -package main - -func init() { - // load templates from disk - development = true -} diff --git a/zones/muxmanager.go b/zones/muxmanager.go index b73a2590..1e6003ce 100644 --- a/zones/muxmanager.go +++ b/zones/muxmanager.go @@ -1,11 +1,12 @@ package zones import ( + "context" "crypto/sha256" "encoding/hex" "fmt" - "io/ioutil" "log" + "os" "path" "strings" "time" @@ -52,13 +53,17 @@ func NewMuxManager(path string, reg RegistrationAPI) (*MuxManager, error) { return mm, err } -func (mm *MuxManager) Run() { +func (mm *MuxManager) Run(ctx context.Context) { for { err := mm.reload() if err != nil { log.Printf("error reading zones: %s", err) } - time.Sleep(2 * time.Second) + select { + case <-time.After(2 * time.Second): + case <-ctx.Done(): + return + } } } @@ -68,7 +73,7 @@ func (mm *MuxManager) Zones() ZoneList { } func (mm *MuxManager) reload() error { - dir, err := ioutil.ReadDir(mm.path) + dir, err := os.ReadDir(mm.path) if err != nil { return fmt.Errorf("could not read '%s': %s", mm.path, err) } @@ -85,12 +90,17 @@ func (mm *MuxManager) reload() error { continue } + fileInfo, err := file.Info() + if err != nil { + return err + } + modTime := fileInfo.ModTime() + zoneName := fileName[0:strings.LastIndex(fileName, ".")] seenZones[zoneName] = true - if _, ok := mm.lastRead[zoneName]; !ok || file.ModTime().After(mm.lastRead[zoneName].time) { - modTime := file.ModTime() + if _, ok := mm.lastRead[zoneName]; !ok || modTime.After(mm.lastRead[zoneName].time) { if ok { log.Printf("Reloading %s\n", fileName) mm.lastRead[zoneName].time = modTime @@ -131,7 +141,7 @@ func (mm *MuxManager) reload() error { zone := NewZone(zoneName) err := zone.ReadZoneFile(filename) if zone == nil || err != nil { - parseErr = fmt.Errorf("Error reading zone '%s': %s", zoneName, err) + parseErr = fmt.Errorf("error reading zone '%s': %s", zoneName, err) log.Println(parseErr.Error()) continue } @@ -146,7 +156,7 @@ func (mm *MuxManager) reload() error { if zoneName == "pgeodns" { continue } - if ok, _ := seenZones[zoneName]; ok { + if ok := seenZones[zoneName]; ok { continue } log.Println("Removing zone", zone.Origin) @@ -191,7 +201,7 @@ func (mm *MuxManager) setupRootZone() { } func sha256File(fn string) string { - data, err := ioutil.ReadFile(fn) + data, err := os.ReadFile(fn) if err != nil { return "" } diff --git a/zones/picker.go b/zones/picker.go index 11fd7cf2..cff25e83 100644 --- a/zones/picker.go +++ b/zones/picker.go @@ -3,8 +3,8 @@ package zones import ( "math/rand" - "github.com/abh/geodns/health" - "github.com/abh/geodns/targeting/geo" + "github.com/abh/geodns/v3/health" + "github.com/abh/geodns/v3/targeting/geo" "github.com/miekg/dns" ) diff --git a/zones/reader.go b/zones/reader.go index ea2965c7..5a3f1281 100644 --- a/zones/reader.go +++ b/zones/reader.go @@ -3,6 +3,7 @@ package zones import ( "encoding/json" "fmt" + "io" "log" "net" "os" @@ -11,8 +12,8 @@ import ( "strconv" "strings" - "github.com/abh/geodns/targeting" - "github.com/abh/geodns/typeutil" + "github.com/abh/geodns/v3/targeting" + "github.com/abh/geodns/v3/typeutil" "github.com/abh/errorutil" "github.com/miekg/dns" @@ -48,7 +49,7 @@ func (zone *Zone) ReadZoneFile(fileName string) (zerr error) { if err = decoder.Decode(&objmap); err != nil { extra := "" if serr, ok := err.(*json.SyntaxError); ok { - if _, serr := fh.Seek(0, os.SEEK_SET); serr != nil { + if _, serr := fh.Seek(0, io.SeekStart); serr != nil { log.Fatalf("seek error: %v", serr) } line, col, highlight := errorutil.HighlightBytePosition(fh, serr.Offset) @@ -204,11 +205,11 @@ func setupZoneData(data map[string]interface{}, zone *Zone) { records := make(map[string][]interface{}) - switch rdata.(type) { + switch rd := rdata.(type) { case map[string]interface{}: // Handle NS map syntax, map[ns2.example.net: ns1.example.net:] tmp := make([]interface{}, 0) - for rdataK, rdataV := range rdata.(map[string]interface{}) { + for rdataK, rdataV := range rd { if rdataV == nil { rdataV = "" } @@ -218,7 +219,7 @@ func setupZoneData(data map[string]interface{}, zone *Zone) { case string: // CNAME and alias tmp := make([]interface{}, 1) - tmp[0] = rdata.(string) + tmp[0] = rd records[rType] = tmp default: records[rType] = rdata.([]interface{}) @@ -299,19 +300,18 @@ func setupZoneData(data map[string]interface{}, zone *Zone) { switch dnsType { case dns.TypePTR: record.RR = &dns.PTR{Hdr: h, Ptr: ip} - break case dns.TypeA: if x := net.ParseIP(ip); x != nil { record.RR = &dns.A{Hdr: h, A: x} - break + } else { + panic(fmt.Errorf("bad A record %q for %q", ip, dk)) } - panic(fmt.Errorf("Bad A record %q for %q", ip, dk)) case dns.TypeAAAA: if x := net.ParseIP(ip); x != nil { record.RR = &dns.AAAA{Hdr: h, AAAA: x} - break + } else { + panic(fmt.Errorf("bad AAAA record %q for %q", ip, dk)) } - panic(fmt.Errorf("Bad AAAA record %q for %q", ip, dk)) } case dns.TypeMX: diff --git a/zones/reader_test.go b/zones/reader_test.go index 839d1815..7ea7d65a 100644 --- a/zones/reader_test.go +++ b/zones/reader_test.go @@ -3,12 +3,11 @@ package zones import ( "fmt" "io" - "io/ioutil" "os" "testing" - "github.com/abh/geodns/targeting" - "github.com/abh/geodns/targeting/geoip2" + "github.com/abh/geodns/v3/targeting" + "github.com/abh/geodns/v3/targeting/geoip2" "github.com/stretchr/testify/assert" ) @@ -18,7 +17,7 @@ func loadZones(t *testing.T) *MuxManager { t.Logf("Setting up geo provider") dbDir := geoip2.FindDB() if len(dbDir) == 0 { - t.Fatalf("Could not find geoip directory") + t.Skip("Could not find geoip directory") } geoprovider, err := geoip2.New(dbDir) if err == nil { @@ -95,7 +94,7 @@ func TestReadConfigs(t *testing.T) { } func TestRemoveConfig(t *testing.T) { - dir, err := ioutil.TempDir("", "geodns-test.") + dir, err := os.MkdirTemp("", "geodns-test.") if err != nil { t.Fail() } @@ -120,7 +119,7 @@ func TestRemoveConfig(t *testing.T) { t.Fail() } - err = ioutil.WriteFile(dir+"/invalid.example.org.json", []byte("not-json"), 0644) + err = os.WriteFile(dir+"/invalid.example.org.json", []byte("not-json"), 0644) if err != nil { t.Log(err) t.Fail() diff --git a/zones/zone.go b/zones/zone.go index 8e8aa0c2..6967d6bf 100644 --- a/zones/zone.go +++ b/zones/zone.go @@ -4,14 +4,15 @@ import ( "encoding/json" "log" "net" + "slices" "strconv" "strings" "sync" - "github.com/abh/geodns/applog" - "github.com/abh/geodns/health" - "github.com/abh/geodns/targeting" - "github.com/abh/geodns/targeting/geo" + "github.com/abh/geodns/v3/applog" + "github.com/abh/geodns/v3/health" + "github.com/abh/geodns/v3/targeting" + "github.com/abh/geodns/v3/targeting/geo" "github.com/miekg/dns" ) @@ -246,9 +247,20 @@ func (z *Zone) FindLabels(s string, targets []string, qts []uint16) []LabelMatch continue case dns.TypeMF: if label.Records[dns.TypeMF] != nil { + + // don't follow NS and SOA records for aliases + aliasQts := slices.DeleteFunc(qts, func(q uint16) bool { + if slices.Contains( + []uint16{dns.TypeNS, dns.TypeSOA}, + q) { + return true + } + return false + }) + name = label.FirstRR(dns.TypeMF).(*dns.MF).Mf // TODO: need to avoid loops here somehow - aliases := z.FindLabels(name, targets, qts) + aliases := z.FindLabels(name, targets, aliasQts) matches = append(matches, aliases...) continue } diff --git a/zones/zone_health_test.go b/zones/zone_health_test.go index 9fff62db..e842631e 100644 --- a/zones/zone_health_test.go +++ b/zones/zone_health_test.go @@ -4,7 +4,7 @@ import ( "math/rand" "testing" - "github.com/abh/geodns/health" + "github.com/abh/geodns/v3/health" "github.com/miekg/dns" ) diff --git a/zones/zone_stats.go b/zones/zone_stats.go index 3b37051e..c2b14d59 100644 --- a/zones/zone_stats.go +++ b/zones/zone_stats.go @@ -86,7 +86,7 @@ func (zs *zoneLabelStats) Counts() map[string]int { counts := make(map[string]int) for i, l := range zs.log { - if zs.rotated == false && i >= zs.pos { + if !zs.rotated && i >= zs.pos { break } counts[l]++ diff --git a/zones/zone_test.go b/zones/zone_test.go index 14658d76..e54cc342 100644 --- a/zones/zone_test.go +++ b/zones/zone_test.go @@ -70,8 +70,9 @@ func TestExampleComZone(t *testing.T) { // Test that we get the expected NS records (in any order because // of the configuration format used for this zone) + re := regexp.MustCompile(`^ns[12]\.example\.net.$`) for i := 0; i < 2; i++ { - if matched, err := regexp.MatchString("^ns[12]\\.example\\.net.$", Ns[i].RR.(*dns.NS).Ns); err != nil || !matched { + if matched := re.MatchString(Ns[i].RR.(*dns.NS).Ns); !matched { if err != nil { t.Fatal(err) }