diff --git a/cmd/stunstamp/api.go b/cmd/stunstamp/api.go deleted file mode 100644 index 8effda6e4..000000000 --- a/cmd/stunstamp/api.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -package main - -import ( - "compress/gzip" - "encoding/json" - "errors" - "net/http" - "net/url" - "strconv" - "strings" - "time" - - sq "github.com/Masterminds/squirrel" -) - -type api struct { - db *db - mux *http.ServeMux -} - -func newAPI(db *db) *api { - a := &api{ - db: db, - } - mux := http.NewServeMux() - mux.HandleFunc("/query", a.query) - a.mux = mux - return a -} - -type apiResult struct { - At int `json:"at"` // time.Time.Unix() - RegionID int `json:"regionID"` - Hostname string `json:"hostname"` - Af int `json:"af"` // 4 or 6 - Addr string `json:"addr"` - Source int `json:"source"` // timestampSourceUserspace (0) or timestampSourceKernel (1) - StableConn bool `json:"stableConn"` - DstPort int `json:"dstPort"` - RttNS *int `json:"rttNS"` -} - -func getTimeBounds(vals url.Values) (from time.Time, to time.Time, err error) { - lastForm, ok := vals["last"] - if ok && len(lastForm) > 0 { - dur, err := time.ParseDuration(lastForm[0]) - if err != nil { - return time.Time{}, time.Time{}, err - } - now := time.Now() - return now.Add(-dur), now, nil - } - - fromForm, ok := vals["from"] - if ok && len(fromForm) > 0 { - fromUnixSec, err := strconv.Atoi(fromForm[0]) - if err != nil { - return time.Time{}, time.Time{}, err - } - from = time.Unix(int64(fromUnixSec), 0) - toForm, ok := vals["to"] - if ok && len(toForm) > 0 { - toUnixSec, err := strconv.Atoi(toForm[0]) - if err != nil { - return time.Time{}, time.Time{}, err - } - to = time.Unix(int64(toUnixSec), 0) - } else { - return time.Time{}, time.Time{}, errors.New("from specified without to") - } - return from, to, nil - } - - // no time bounds specified, default to last 1h - now := time.Now() - return now.Add(-time.Hour), now, nil -} - -func (a *api) ServeHTTP(w http.ResponseWriter, r *http.Request) { - a.mux.ServeHTTP(w, r) -} - -func (a *api) query(w http.ResponseWriter, r *http.Request) { - err := r.ParseForm() - if err != nil { - http.Error(w, err.Error(), 500) - return - } - from, to, err := getTimeBounds(r.Form) - if err != nil { - http.Error(w, err.Error(), 500) - return - } - - sb := sq.Select("at_unix", "region_id", "hostname", "af", "address", "timestamp_source", "stable_conn", "dst_port", "rtt_ns").From("rtt") - sb = sb.Where(sq.And{ - sq.GtOrEq{"at_unix": from.Unix()}, - sq.LtOrEq{"at_unix": to.Unix()}, - }) - query, args, err := sb.ToSql() - if err != nil { - return - } - - rows, err := a.db.Query(query, args...) - if err != nil { - http.Error(w, err.Error(), 500) - return - } - results := make([]apiResult, 0) - for rows.Next() { - rtt := 0 - result := apiResult{ - RttNS: &rtt, - } - err = rows.Scan(&result.At, &result.RegionID, &result.Hostname, &result.Af, &result.Addr, &result.Source, &result.StableConn, &result.DstPort, &result.RttNS) - if err != nil { - http.Error(w, err.Error(), 500) - return - } - results = append(results, result) - } - if rows.Err() != nil { - http.Error(w, rows.Err().Error(), 500) - return - } - if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { - gz := gzip.NewWriter(w) - defer gz.Close() - w.Header().Set("Content-Encoding", "gzip") - err = json.NewEncoder(gz).Encode(&results) - } else { - err = json.NewEncoder(w).Encode(&results) - } - if err != nil { - http.Error(w, err.Error(), 500) - return - } -} diff --git a/cmd/stunstamp/stunstamp.go b/cmd/stunstamp/stunstamp.go index 0ec0efeb2..6c009de46 100644 --- a/cmd/stunstamp/stunstamp.go +++ b/cmd/stunstamp/stunstamp.go @@ -38,11 +38,8 @@ var ( flagDERPMap = flag.String("derp-map", "https://login.tailscale.com/derpmap/default", "URL to DERP map") - flagOut = flag.String("out", "", "output sqlite filename") flagInterval = flag.Duration("interval", time.Minute, "interval to probe at in time.ParseDuration() format") - flagAPI = flag.String("api", "", "listen addr for HTTP API") flagIPv6 = flag.Bool("ipv6", false, "probe IPv6 addresses") - flagRetention = flag.Duration("retention", time.Hour*24*7, "sqlite retention period in time.ParseDuration() format") flagRemoteWriteURL = flag.String("rw-url", "", "prometheus remote write URL") flagInstance = flag.String("instance", "", "instance label value; defaults to hostname if unspecified") flagDstPorts = flag.String("dst-ports", "", "comma-separated list of destination ports to monitor") @@ -639,15 +636,9 @@ func main() { if len(*flagDERPMap) < 1 { log.Fatal("derp-map flag is unset") } - if len(*flagOut) < 1 { - log.Fatal("out flag is unset") - } if *flagInterval < minInterval || *flagInterval > maxBufferDuration { log.Fatalf("interval must be >= %s and <= %s", minInterval, maxBufferDuration) } - if *flagRetention < *flagInterval { - log.Fatal("retention must be >= interval") - } if len(*flagRemoteWriteURL) < 1 { log.Fatal("rw-url flag is unset") } @@ -693,49 +684,6 @@ func main() { } } - db, err := newDB(*flagOut) - if err != nil { - log.Fatalf("error opening output file for writing: %v", err) - } - defer db.Close() - - _, err = db.Exec("PRAGMA journal_mode=WAL") - if err != nil { - log.Fatalf("error enabling WAL mode: %v", err) - } - - // No indices or primary key. Keep it simple for now. Reads will be full - // scans. We can AUTOINCREMENT rowid in the future and hold an in-memory - // index to at_unix if needed as reads are almost always going to be - // time-bound (e.g. WHERE at_unix >= ?). At the time of authorship we have - // ~300 data points per-interval w/o ipv6 w/kernel timestamping resulting - // in ~2.6m rows in 24h w/a 10s probe interval. - _, err = db.Exec(` -CREATE TABLE IF NOT EXISTS rtt(at_unix INT, region_id INT, hostname TEXT, af INT, address TEXT, timestamp_source INT, stable_conn INT, dst_port INT, rtt_ns INT) -`) - if err != nil { - log.Fatalf("error initializing db: %v", err) - } - - wg := sync.WaitGroup{} - httpErrCh := make(chan error, 1) - var httpServer *http.Server - if len(*flagAPI) > 0 { - api := newAPI(db) - httpServer = &http.Server{ - Addr: *flagAPI, - Handler: api, - ReadTimeout: time.Second * 60, - WriteTimeout: time.Second * 60, - } - wg.Add(1) - go func() { - err := httpServer.ListenAndServe() - httpErrCh <- err - wg.Done() - }() - } - tsCh := make(chan []prompb.TimeSeries, maxBufferDuration / *flagInterval) remoteWriteDoneCh := make(chan struct{}) rwc := newRemoteWriteClient(*flagRemoteWriteURL) @@ -745,9 +693,6 @@ func main() { }() shutdown := func() { - if httpServer != nil { - httpServer.Close() - } close(tsCh) select { case <-time.After(time.Second * 10): // give goroutine some time to flush @@ -766,7 +711,6 @@ func main() { cancel() } - wg.Wait() return } @@ -787,20 +731,9 @@ func main() { defer derpMapTicker.Stop() probeTicker := time.NewTicker(*flagInterval) defer probeTicker.Stop() - cleanupTicker := time.NewTicker(time.Hour) - defer cleanupTicker.Stop() for { select { - case <-cleanupTicker.C: - older := time.Now().Add(-*flagRetention) - log.Printf("cleaning up measurements older than %v", older) - _, err := db.Exec("DELETE FROM rtt WHERE at_unix < ?", older.Unix()) - if err != nil { - log.Printf("error cleaning up old data: %v", err) - shutdown() - return - } case <-probeTicker.C: results, err := probeNodes(nodeMetaByAddr, stableConns, dstPorts) if err != nil { @@ -819,32 +752,6 @@ func main() { tsCh <- ts } } - tx, err := db.Begin() - if err != nil { - log.Printf("error beginning sqlite tx: %v", err) - shutdown() - return - } - for _, result := range results { - af := 4 - if result.key.meta.addr.Is6() { - af = 6 - } - _, err = tx.Exec("INSERT INTO rtt(at_unix, region_id, hostname, af, address, timestamp_source, stable_conn, dst_port, rtt_ns) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)", - result.at.Unix(), result.key.meta.regionID, result.key.meta.hostname, af, result.key.meta.addr.String(), result.key.timestampSource, result.key.connStability, result.key.dstPort, result.rtt) - if err != nil { - tx.Rollback() - log.Printf("error adding result to tx: %v", err) - shutdown() - return - } - } - err = tx.Commit() - if err != nil { - log.Printf("error committing tx: %v", err) - shutdown() - return - } case dm := <-dmCh: staleMeta, err := nodeMetaFromDERPMap(dm, nodeMetaByAddr, *flagIPv6) if err != nil { @@ -874,10 +781,6 @@ func main() { dmCh <- updatedDM } }() - case err := <-httpErrCh: - log.Printf("http server error: %v", err) - shutdown() - return case <-sigCh: shutdown() return diff --git a/cmd/stunstamp/stunstamp_db_default.go b/cmd/stunstamp/stunstamp_db_default.go deleted file mode 100644 index 3de3a0197..000000000 --- a/cmd/stunstamp/stunstamp_db_default.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -//go:build !(windows && 386) - -package main - -import ( - "database/sql" - - _ "modernc.org/sqlite" -) - -type db struct { - *sql.DB -} - -func newDB(path string) (*db, error) { - d, err := sql.Open("sqlite", *flagOut) - if err != nil { - return nil, err - } - return &db{ - DB: d, - }, nil -} diff --git a/cmd/stunstamp/stunstamp_db_windows_386.go b/cmd/stunstamp/stunstamp_db_windows_386.go deleted file mode 100644 index 67dba08ca..000000000 --- a/cmd/stunstamp/stunstamp_db_windows_386.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -package main - -import ( - "database/sql" - "errors" -) - -type db struct { - *sql.DB -} - -func newDB(path string) (*db, error) { - return nil, errors.New("unsupported platform") -} diff --git a/go.mod b/go.mod index 1746c0d69..cb23701b3 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.22.0 require ( filippo.io/mkcert v1.4.4 fybrik.io/crdoc v0.6.3 - github.com/Masterminds/squirrel v1.5.4 github.com/akutz/memconn v0.1.0 github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa github.com/andybalholm/brotli v1.1.0 @@ -113,7 +112,6 @@ require ( k8s.io/apimachinery v0.30.1 k8s.io/apiserver v0.30.1 k8s.io/client-go v0.30.1 - modernc.org/sqlite v1.29.10 nhooyr.io/websocket v1.8.10 sigs.k8s.io/controller-runtime v0.18.4 sigs.k8s.io/controller-tools v0.15.1-0.20240618033008-7824932b0cab @@ -127,21 +125,10 @@ require ( github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14 // indirect github.com/dave/brenda v1.1.0 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect github.com/gobuffalo/flect v1.0.2 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect + github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect github.com/gorilla/securecookie v1.1.2 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect - github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/ncruces/go-strftime v0.1.9 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect - modernc.org/libc v1.49.3 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.8.0 // indirect - modernc.org/strutil v1.2.0 // indirect - modernc.org/token v1.1.0 // indirect ) require ( diff --git a/go.sum b/go.sum index 07acdc7a6..0f416a49a 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,6 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= -github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= -github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= @@ -265,8 +263,6 @@ github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqY github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/dsnet/try v0.0.3 h1:ptR59SsrcFUYbT/FhAbKTV6iLkeD6O18qfIWRml2fqI= github.com/dsnet/try v0.0.3/go.mod h1:WBM8tRpUmnXXhY1U6/S8dt6UWdHTQ7y8A5YSkRCkq40= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= @@ -522,8 +518,6 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 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/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= @@ -623,10 +617,6 @@ github.com/kunwardeep/paralleltest v1.0.6 h1:FCKYMF1OF2+RveWlABsdnmsvJrei5aoyZoa github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes= github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ= github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA= github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= @@ -699,8 +689,6 @@ github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81 github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nishanths/exhaustive v0.10.0 h1:BMznKAcVa9WOoLq/kTGp4NJOJSMwEpcpjFNAVRfPlSo= @@ -791,8 +779,6 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -1475,32 +1461,6 @@ k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7F k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk= -modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= -modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA= -modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI= -modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= -modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= -modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= -modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg= -modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= -modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= -modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= -modernc.org/sqlite v1.29.10 h1:3u93dz83myFnMilBGCOLbr+HjklS6+5rJLx4q86RDAg= -modernc.org/sqlite v1.29.10/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA= -modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= -modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= -modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= -modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= mvdan.cc/gofumpt v0.5.0 h1:0EQ+Z56k8tXjj/6TQD25BFNKQXpCvT0rnansIc7Ug5E= mvdan.cc/gofumpt v0.5.0/go.mod h1:HBeVDtMKRZpXyxFciAirzdKklDlGu8aAy1wEbH5Y9js= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=