diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..90e81eb --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,26 @@ +permissions: + contents: read +name: golangci-lint + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v5 + with: + go-version: '1.25' + cache: false + - name: Checkout code + uses: actions/checkout@v4 + - name: golangci-lint + run: | + go tool golangci-lint run --timeout 5m diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..fa23c88 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,99 @@ +run: + timeout: 5m + +issues: + exclude-dirs: + - testvectors + - scripts + exclude-rules: + - path: _test\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + - path: _test\.go + text: "unused-parameter" + linters: + - revive + - text: 'shadow: declaration of "(err|ctx)" shadows declaration at' + linters: [ govet ] + +linters-settings: + govet: + enable-all: true + revive: + rules: + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: error-return + - name: error-strings + - name: error-naming + - name: exported + - name: if-return + - name: increment-decrement + - name: var-naming + - name: var-declaration + - name: package-comments + - name: range + - name: receiver-naming + - name: time-naming + - name: unexported-return + - name: indent-error-flow + - name: errorf + staticcheck: + checks: ["all"] + stylecheck: + checks: ["all"] + misspell: + locale: US + gocyclo: + min-complexity: 15 + dupl: + threshold: 100 + goconst: + min-len: 3 + min-occurrences: 3 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - hugeParam + - rangeValCopy + +linters: + disable-all: true + enable: + - bodyclose + # - depguard + - dogsled + - dupl + - errcheck + - goconst + - gocritic + - gocyclo + - gofmt + - goimports + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - lll + - misspell + - nakedret + - prealloc + - revive + - staticcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - whitespace diff --git a/cmd/public_node/main.go b/cmd/public_node/main.go index 90b8ec0..40f1483 100644 --- a/cmd/public_node/main.go +++ b/cmd/public_node/main.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,11 +20,11 @@ import ( "context" "log" - "github.com/cinode/go/pkg/cmd/public_node" + "github.com/cinode/go/pkg/cmd/publicnode" ) func main() { - if err := public_node.Execute(context.Background()); err != nil { + if err := publicnode.Execute(context.Background()); err != nil { log.Fatal(err) } } diff --git a/cmd/static_datastore_builder/main.go b/cmd/static_datastore_builder/main.go index 06f2b6b..b639b64 100644 --- a/cmd/static_datastore_builder/main.go +++ b/cmd/static_datastore_builder/main.go @@ -19,11 +19,11 @@ package main import ( "log" - "github.com/cinode/go/pkg/cmd/static_datastore" + "github.com/cinode/go/pkg/cmd/staticdatastore" ) func main() { - if err := static_datastore.RootCmd().Execute(); err != nil { + if err := staticdatastore.RootCmd().Execute(); err != nil { log.Fatal(err.Error()) } } diff --git a/cmd/web_proxy/main.go b/cmd/web_proxy/main.go index 8fb30f0..bec39ad 100644 --- a/cmd/web_proxy/main.go +++ b/cmd/web_proxy/main.go @@ -1,5 +1,5 @@ /* -Copyright © 2022 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,11 +20,11 @@ import ( "context" "log" - "github.com/cinode/go/pkg/cmd/cinode_web_proxy" + "github.com/cinode/go/pkg/cmd/cinodewebproxy" ) func main() { - if err := cinode_web_proxy.Execute(context.Background()); err != nil { + if err := cinodewebproxy.Execute(context.Background()); err != nil { log.Fatal(err) } } diff --git a/go.mod b/go.mod index 1f5395a..89ee3a9 100644 --- a/go.mod +++ b/go.mod @@ -11,11 +11,190 @@ require ( ) require ( + 4d63.com/gocheckcompilerdirectives v1.3.0 // indirect + 4d63.com/gochecknoglobals v0.2.2 // indirect + github.com/4meepo/tagalign v1.4.2 // indirect + github.com/Abirdcfly/dupword v0.1.3 // indirect + github.com/Antonboom/errname v1.0.0 // indirect + github.com/Antonboom/nilnil v1.0.1 // indirect + github.com/Antonboom/testifylint v1.5.2 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect + github.com/Crocmagnon/fatcontext v0.7.1 // indirect + github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect + github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect + github.com/alecthomas/go-check-sumtype v0.3.1 // indirect + github.com/alexkohler/nakedret/v2 v2.0.5 // indirect + github.com/alexkohler/prealloc v1.0.0 // indirect + github.com/alingse/asasalint v0.0.11 // indirect + github.com/alingse/nilnesserr v0.1.2 // indirect + github.com/ashanbrown/forbidigo v1.6.0 // indirect + github.com/ashanbrown/makezero v1.2.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bkielbasa/cyclop v1.2.3 // indirect + github.com/blizzy78/varnamelen v0.8.0 // indirect + github.com/bombsimon/wsl/v4 v4.5.0 // indirect + github.com/breml/bidichk v0.3.2 // indirect + github.com/breml/errchkjson v0.4.0 // indirect + github.com/butuzov/ireturn v0.3.1 // indirect + github.com/butuzov/mirror v1.3.0 // indirect + github.com/catenacyber/perfsprint v0.8.2 // indirect + github.com/ccojocar/zxcvbn-go v1.0.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charithe/durationcheck v0.0.10 // indirect + github.com/chavacava/garif v0.1.0 // indirect + github.com/ckaznocha/intrange v0.3.0 // indirect + github.com/curioswitch/go-reassign v0.3.0 // indirect + github.com/daixiang0/gci v0.13.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/denis-tingaikin/go-header v0.5.0 // indirect + github.com/ettle/strcase v0.2.0 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/fatih/structtag v1.2.0 // indirect + github.com/firefart/nonamedreturns v1.0.5 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fzipp/gocyclo v0.6.0 // indirect + github.com/ghostiam/protogetter v0.3.9 // indirect + github.com/go-critic/go-critic v0.12.0 // indirect + github.com/go-toolsmith/astcast v1.1.0 // indirect + github.com/go-toolsmith/astcopy v1.1.0 // indirect + github.com/go-toolsmith/astequal v1.2.0 // indirect + github.com/go-toolsmith/astfmt v1.1.0 // indirect + github.com/go-toolsmith/astp v1.1.0 // indirect + github.com/go-toolsmith/strparse v1.1.0 // indirect + github.com/go-toolsmith/typep v1.1.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect + github.com/golangci/go-printf-func-name v0.1.0 // indirect + github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect + github.com/golangci/golangci-lint v1.64.8 // indirect + github.com/golangci/misspell v0.6.0 // indirect + github.com/golangci/plugin-module-register v0.1.1 // indirect + github.com/golangci/revgrep v0.8.0 // indirect + github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/gordonklaus/ineffassign v0.1.0 // indirect + github.com/gostaticanalysis/analysisutil v0.7.1 // indirect + github.com/gostaticanalysis/comment v1.5.0 // indirect + github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect + github.com/gostaticanalysis/nilerr v0.1.1 // indirect + github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jgautheron/goconst v1.7.1 // indirect + github.com/jingyugao/rowserrcheck v1.1.1 // indirect + github.com/jjti/go-spancheck v0.6.4 // indirect + github.com/julz/importas v0.2.0 // indirect + github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect + github.com/kisielk/errcheck v1.9.0 // indirect + github.com/kkHAIKE/contextcheck v1.1.6 // indirect + github.com/kulti/thelper v0.6.3 // indirect + github.com/kunwardeep/paralleltest v1.0.10 // indirect + github.com/lasiar/canonicalheader v1.1.2 // indirect + github.com/ldez/exptostd v0.4.2 // indirect + github.com/ldez/gomoddirectives v0.6.1 // indirect + github.com/ldez/grignotin v0.9.0 // indirect + github.com/ldez/tagliatelle v0.7.1 // indirect + github.com/ldez/usetesting v0.4.2 // indirect + github.com/leonklingele/grouper v1.1.2 // indirect + github.com/macabu/inamedparam v0.1.3 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/maratori/testableexamples v1.0.0 // indirect + github.com/maratori/testpackage v1.1.1 // indirect + github.com/matoous/godox v1.1.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mgechev/revive v1.7.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/moricho/tparallel v0.3.2 // indirect + github.com/nakabonne/nestif v0.3.1 // indirect + github.com/nishanths/exhaustive v0.12.0 // indirect + github.com/nishanths/predeclared v0.2.2 // indirect + github.com/nunnatsa/ginkgolinter v0.19.1 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/polyfloyd/go-errorlint v1.7.1 // indirect + github.com/prometheus/client_golang v1.12.1 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect + github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect + github.com/quasilyte/gogrep v0.5.0 // indirect + github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect + github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect + github.com/raeperd/recvcheck v0.2.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/ryancurrah/gomodguard v1.3.5 // indirect + github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect + github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect + github.com/sashamelentyev/interfacebloat v1.1.0 // indirect + github.com/sashamelentyev/usestdlibvars v1.28.0 // indirect + github.com/securego/gosec/v2 v2.22.2 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sivchari/containedctx v1.0.3 // indirect + github.com/sivchari/tenv v1.12.1 // indirect + github.com/sonatard/noctx v0.1.0 // indirect + github.com/sourcegraph/go-diff v0.7.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/viper v1.12.0 // indirect + github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect + github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.4.1 // indirect + github.com/tdakkota/asciicheck v0.4.1 // indirect + github.com/tetafro/godot v1.5.0 // indirect + github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 // indirect + github.com/timonwong/loggercheck v0.10.1 // indirect + github.com/tomarrell/wrapcheck/v2 v2.10.0 // indirect + github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect + github.com/ultraware/funlen v0.2.0 // indirect + github.com/ultraware/whitespace v0.2.0 // indirect + github.com/uudashr/gocognit v1.2.0 // indirect + github.com/uudashr/iface v1.3.1 // indirect + github.com/xen0n/gosmopolitan v1.2.2 // indirect + github.com/yagipy/maintidx v1.0.0 // indirect + github.com/yeya24/promlinter v0.3.0 // indirect + github.com/ykadowak/zerologlint v0.1.5 // indirect + gitlab.com/bosi/decorder v0.4.2 // indirect + go-simpler.org/musttag v0.13.0 // indirect + go-simpler.org/sloglint v0.9.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.24.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/sync v0.16.0 // indirect golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.35.0 // indirect + golang.org/x/tools/go/expect v0.1.1-deprecated // indirect + golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + honnef.co/go/tools v0.6.1 // indirect + mvdan.cc/gofumpt v0.7.0 // indirect + mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect ) + +tool github.com/golangci/golangci-lint/cmd/golangci-lint diff --git a/go.sum b/go.sum index 52a3a4d..064dbcc 100644 --- a/go.sum +++ b/go.sum @@ -1,29 +1,984 @@ +4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A= +4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= +4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= +4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= +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/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E= +github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI= +github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE= +github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw= +github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA= +github.com/Antonboom/errname v1.0.0/go.mod h1:gMOBFzK/vrTiXN9Oh+HFs+e6Ndl0eTFbtsRTSRdXyGI= +github.com/Antonboom/nilnil v1.0.1 h1:C3Tkm0KUxgfO4Duk3PM+ztPncTFlOf0b2qadmS0s4xs= +github.com/Antonboom/nilnil v1.0.1/go.mod h1:CH7pW2JsRNFgEh8B2UaPZTEPhCMuFowP/e8Udp9Nnb0= +github.com/Antonboom/testifylint v1.5.2 h1:4s3Xhuv5AvdIgbd8wOOEeo0uZG7PbDKQyKY5lGoQazk= +github.com/Antonboom/testifylint v1.5.2/go.mod h1:vxy8VJ0bc6NavlYqjZfmp6EfqXMtBgQ4+mhCojwC1P8= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Crocmagnon/fatcontext v0.7.1 h1:SC/VIbRRZQeQWj/TcQBS6JmrXcfA+BU4OGSVUt54PjM= +github.com/Crocmagnon/fatcontext v0.7.1/go.mod h1:1wMvv3NXEBJucFGfwOJBxSVWcoIO6emV215SMkW9MFU= +github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= +github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnhpIE0thVASYjvosApmHuD2k= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= +github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU= +github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +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/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU= +github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= +github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= +github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= +github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= +github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= +github.com/alingse/nilnesserr v0.1.2 h1:Yf8Iwm3z2hUUrP4muWfW83DF4nE3r1xZ26fGWUKCZlo= +github.com/alingse/nilnesserr v0.1.2/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= +github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= +github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= +github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU= +github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= +github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= +github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= +github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= +github.com/bombsimon/wsl/v4 v4.5.0 h1:iZRsEvDdyhd2La0FVi5k6tYehpOR/R7qIUjmKk7N74A= +github.com/bombsimon/wsl/v4 v4.5.0/go.mod h1:NOQ3aLF4nD7N5YPXMruR6ZXDOAqLoM0GEpLwTdvmOSc= +github.com/breml/bidichk v0.3.2 h1:xV4flJ9V5xWTqxL+/PMFF6dtJPvZLPsyixAoPe8BGJs= +github.com/breml/bidichk v0.3.2/go.mod h1:VzFLBxuYtT23z5+iVkamXO386OB+/sVwZOpIj6zXGos= +github.com/breml/errchkjson v0.4.0 h1:gftf6uWZMtIa/Is3XJgibewBm2ksAQSY/kABDNFTAdk= +github.com/breml/errchkjson v0.4.0/go.mod h1:AuBOSTHyLSaaAFlWsRSuRBIroCh3eh7ZHh5YeelDIk8= +github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY= +github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M= +github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= +github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI= +github.com/catenacyber/perfsprint v0.8.2 h1:+o9zVmCSVa7M4MvabsWvESEhpsMkhfE7k0sHNGL95yw= +github.com/catenacyber/perfsprint v0.8.2/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM= +github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= +github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= +github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= +github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= +github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= +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/ckaznocha/intrange v0.3.0 h1:VqnxtK32pxgkhJgYQEeOArVidIPg+ahLP7WBOXZd5ZY= +github.com/ckaznocha/intrange v0.3.0/go.mod h1:+I/o2d2A1FBHgGELbGxzIcyd3/9l9DuwjM8FsbSS3Lo= +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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= +github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= +github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= +github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= +github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +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/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= +github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= +github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= +github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= +github.com/ghostiam/protogetter v0.3.9 h1:j+zlLLWzqLay22Cz/aYwTHKQ88GE2DQ6GkWSYFOI4lQ= +github.com/ghostiam/protogetter v0.3.9/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= +github.com/go-critic/go-critic v0.12.0 h1:iLosHZuye812wnkEz1Xu3aBwn5ocCPfc9yqmFG9pa6w= +github.com/go-critic/go-critic v0.12.0/go.mod h1:DpE0P6OVc6JzVYzmM5gq5jMU31zLr4am5mB/VfFK64w= +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-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= +github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= +github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= +github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw= +github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4= +github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ= +github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw= +github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY= +github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco= +github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4= +github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA= +github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA= +github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk= +github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus= +github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= +github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw= +github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= +github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= +github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY= +github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +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/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/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw= +github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E= +github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU= +github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s= +github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE= +github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY= +github.com/golangci/golangci-lint v1.64.8 h1:y5TdeVidMtBGG32zgSC7ZXTFNHrsJkDnpO4ItB3Am+I= +github.com/golangci/golangci-lint v1.64.8/go.mod h1:5cEsUQBSr6zi8XI8OjmcY2Xmliqc4iYL7YoPrL+zLJ4= +github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= +github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= +github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c= +github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= +github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s= +github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= +github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= +github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= +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.2/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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +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/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= +github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= +github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= +github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= +github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= +github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8= +github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc= +github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk= +github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY= +github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= +github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= +github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= +github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= +github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= +github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo= +github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.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/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 h1:4zOlv2my+vf98jT1nQt4bT/yKWUImevYPJ2H344CloE= github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6/go.mod h1:r/8JmuR0qjuCiEhAolkfvdZgmPiHTnJaG0UXCSeR1Zo= +github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= +github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= +github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= +github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc= +github.com/jjti/go-spancheck v0.6.4/go.mod h1:yAEYdKJ2lRkDA8g7X+oKUHXOWVAXSBJRv04OhF+QUjk= +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/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= +github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY= +github.com/karamaru-alpha/copyloopvar v1.2.1 h1:wmZaZYIjnJ0b5UoKDjUHrikcV0zuPyyxI4SVplLd2CI= +github.com/karamaru-alpha/copyloopvar v1.2.1/go.mod h1:nFmMlFNlClC2BPvNaHMdkirmTJxVCY0lhxBtlfOypMM= +github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M= +github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE= +github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg= +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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= +github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= +github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs= +github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= +github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= +github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= +github.com/ldez/exptostd v0.4.2 h1:l5pOzHBz8mFOlbcifTxzfyYbgEmoUqjxLFHZkjlbHXs= +github.com/ldez/exptostd v0.4.2/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ= +github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc= +github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs= +github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow= +github.com/ldez/grignotin v0.9.0/go.mod h1:uaVTr0SoZ1KBii33c47O1M8Jp3OP3YDwhZCmzT9GHEk= +github.com/ldez/tagliatelle v0.7.1 h1:bTgKjjc2sQcsgPiT902+aadvMjCeMHrY7ly2XKFORIk= +github.com/ldez/tagliatelle v0.7.1/go.mod h1:3zjxUpsNB2aEZScWiZTHrAXOl1x25t3cRmzfK1mlo2I= +github.com/ldez/usetesting v0.4.2 h1:J2WwbrFGk3wx4cZwSMiCQQ00kjGR0+tuuyW0Lqm4lwA= +github.com/ldez/usetesting v0.4.2/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ= +github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= +github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= +github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= +github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= +github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= +github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= +github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc= +github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4= +github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +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/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY= +github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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/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/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= +github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= +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/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= +github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= +github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= +github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= +github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= +github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= +github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4= +github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= +github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +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/polyfloyd/go-errorlint v1.7.1 h1:RyLVXIbosq1gBdk/pChWA8zWYLsq9UEw7a1L5TVMCnA= +github.com/polyfloyd/go-errorlint v1.7.1/go.mod h1:aXjNb1x2TNhoLsk26iv1yl7a+zTnXPhwEMtEXukiLR8= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +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/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +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.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/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.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo= +github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= +github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= +github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= +github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= +github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= +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/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI= +github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU= +github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= +github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= +github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= +github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= +github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= +github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= +github.com/sashamelentyev/usestdlibvars v1.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxXUzwsMDBkR21cyQ= +github.com/sashamelentyev/usestdlibvars v1.28.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= +github.com/securego/gosec/v2 v2.22.2 h1:IXbuI7cJninj0nRpZSLCUlotsj8jGusohfONMrHoF6g= +github.com/securego/gosec/v2 v2.22.2/go.mod h1:UEBGA+dSKb+VqM6TdehR7lnQtIIMorYJ4/9CW1KVQBE= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= +github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= +github.com/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY= +github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw= +github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= +github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= +github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= +github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= +github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= +github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= +github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= +github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8Bg8etvARQ1rpyl4= +github.com/stbenjam/no-sprintf-host-port v0.2.0/go.mod h1:eL0bQ9PasS0hsyTyfTjjG+E80QIyPnBVQbYZyv20Jfk= +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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= +github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8= +github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8= +github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= +github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= +github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= +github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= +github.com/tetafro/godot v1.5.0 h1:aNwfVI4I3+gdxjMgYPus9eHmoBeJIbnajOyqZYStzuw= +github.com/tetafro/godot v1.5.0/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 h1:y4mJRFlM6fUyPhoXuFg/Yu02fg/nIPFMOY8tOqppoFg= +github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460= +github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg= +github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= +github.com/tomarrell/wrapcheck/v2 v2.10.0 h1:SzRCryzy4IrAH7bVGG4cK40tNUhmVmMDuJujy4XwYDg= +github.com/tomarrell/wrapcheck/v2 v2.10.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= +github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= +github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= +github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI= +github.com/ultraware/funlen v0.2.0/go.mod h1:ZE0q4TsJ8T1SQcjmkhN/w+MceuatI6pBFSxxyteHIJA= +github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSWoFa+g= +github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= +github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA= +github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU= +github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U= +github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= +github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= +github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= +github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= +github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= +github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs= +github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+YcPQN+mW4= +github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= +github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= +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= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= +gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= +go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= +go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= +go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= +go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= +go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE= +go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww= +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= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +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/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +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/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4= +golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +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/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +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= +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-20201020160332-67f06af15bc9/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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +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-20191204072324-ce4227a45e2e/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-20200930185726-fdedc70b468f/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-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +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/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +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-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +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-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +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-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= +golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/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= +honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= +honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= +mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= +mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= +mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U= +mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= +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= diff --git a/pkg/blenc/datastore.go b/pkg/blenc/datastore.go index 07fe94b..421f6cf 100644 --- a/pkg/blenc/datastore.go +++ b/pkg/blenc/datastore.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import ( "context" "crypto/rand" "io" + "math" "time" "github.com/cinode/go/pkg/blobtypes" @@ -32,10 +33,13 @@ import ( // the storage layer func FromDatastore(ds datastore.DS) BE { return &beDatastore{ - ds: ds, - rand: rand.Reader, - generateVersion: func() uint64 { return uint64(time.Now().UnixMicro()) }, - newSecureFifo: securefifo.New, + ds: ds, + rand: rand.Reader, + generateVersion: func() uint64 { + // nolint:gosec // UnixMicro should never return negative value + return uint64(time.Now().UnixMicro() & math.MaxInt64) + }, + newSecureFifo: securefifo.New, } } @@ -79,7 +83,13 @@ func (be *beDatastore) Create( return nil, nil, nil, blobtypes.ErrUnknownBlobType } -func (be *beDatastore) Update(ctx context.Context, name *common.BlobName, authInfo *common.AuthInfo, key *common.BlobKey, r io.Reader) error { +func (be *beDatastore) Update( + ctx context.Context, + name *common.BlobName, + authInfo *common.AuthInfo, + key *common.BlobKey, + r io.Reader, +) error { switch name.Type() { case blobtypes.Static: return be.updateStatic(ctx, name, authInfo, key, r) diff --git a/pkg/blenc/datastore_dynamic_link.go b/pkg/blenc/datastore_dynamic_link.go index fdb670a..7973071 100644 --- a/pkg/blenc/datastore_dynamic_link.go +++ b/pkg/blenc/datastore_dynamic_link.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -41,7 +41,6 @@ func (be *beDatastore) openDynamicLink( io.ReadCloser, error, ) { - // TODO: Protect against long links - there should be max size limit and maybe some streaming involved? rc, err := be.ds.Open(ctx, name) diff --git a/pkg/blenc/datastore_dynamic_link_test.go b/pkg/blenc/datastore_dynamic_link_test.go index 29ca3c4..f88dae6 100644 --- a/pkg/blenc/datastore_dynamic_link_test.go +++ b/pkg/blenc/datastore_dynamic_link_test.go @@ -88,7 +88,6 @@ func TestDynamicLinkErrors(t *testing.T) { dataLen := 0 t.Run(fmt.Sprintf("error at byte %d", i), func(t *testing.T) { - dsw.openFn = func(ctx context.Context, name *common.BlobName) (io.ReadCloser, error) { origRC, err := dsw.DS.Open(ctx, name) require.NoError(t, err) @@ -122,7 +121,6 @@ func TestDynamicLinkErrors(t *testing.T) { }) t.Run("fail to create dynamic link key pair", func(t *testing.T) { - injectedErr := errors.New("test") be.(*beDatastore).rand = iotest.ErrReader(injectedErr) @@ -138,7 +136,6 @@ func TestDynamicLinkErrors(t *testing.T) { require.Empty(t, ai) be.(*beDatastore).rand = rand.Reader - }) t.Run("fail to store new dynamic link blob", func(t *testing.T) { diff --git a/pkg/blenc/datastore_static.go b/pkg/blenc/datastore_static.go index 1dc28c4..dc22afe 100644 --- a/pkg/blenc/datastore_static.go +++ b/pkg/blenc/datastore_static.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -32,8 +32,11 @@ var ( ErrCanNotUpdateStaticBlob = errors.New("blob update is not supported for static blobs") ) -func (be *beDatastore) openStatic(ctx context.Context, name *common.BlobName, key *common.BlobKey) (io.ReadCloser, error) { - +func (be *beDatastore) openStatic( + ctx context.Context, + name *common.BlobName, + key *common.BlobKey, +) (io.ReadCloser, error) { rc, err := be.ds.Open(ctx, name) if err != nil { return nil, err diff --git a/pkg/blenc/interface.go b/pkg/blenc/interface.go index b793ee2..5128f35 100644 --- a/pkg/blenc/interface.go +++ b/pkg/blenc/interface.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -39,23 +39,51 @@ type BE interface { // // If returned error is not nil, the reader must be nil. Otherwise it is required to // close the reader once done working with it. - Open(ctx context.Context, name *common.BlobName, key *common.BlobKey) (io.ReadCloser, error) + Open( + ctx context.Context, + name *common.BlobName, + key *common.BlobKey, + ) ( + io.ReadCloser, + error, + ) // Create completely new blob with given dataset, as a result, the blob name and optional // AuthInfo that allows blob's update is returned - Create(ctx context.Context, blobType common.BlobType, r io.Reader) (*common.BlobName, *common.BlobKey, *common.AuthInfo, error) + Create( + ctx context.Context, + blobType common.BlobType, + r io.Reader, + ) ( + *common.BlobName, + *common.BlobKey, + *common.AuthInfo, + error, + ) // Update updates given blob type with new data, // The update must happen within a single blob name (i.e. it can not end up with blob with different name) // and may not be available for certain blob types such as static blobs. // A valid auth info is necessary to ensure a correct new content can be created - Update(ctx context.Context, name *common.BlobName, ai *common.AuthInfo, key *common.BlobKey, r io.Reader) error + Update( + ctx context.Context, + name *common.BlobName, + ai *common.AuthInfo, + key *common.BlobKey, + r io.Reader, + ) error // Exists does check whether blob of given name exists. It forwards the call // to underlying datastore. - Exists(ctx context.Context, name *common.BlobName) (bool, error) + Exists( + ctx context.Context, + name *common.BlobName, + ) (bool, error) // Delete tries to remove blob with given name. It forwards the call to // underlying datastore. - Delete(ctx context.Context, name *common.BlobName) error + Delete( + ctx context.Context, + name *common.BlobName, + ) error } diff --git a/pkg/blenc/interface_test.go b/pkg/blenc/interface_test.go index 51e1cc5..eafaa94 100644 --- a/pkg/blenc/interface_test.go +++ b/pkg/blenc/interface_test.go @@ -142,7 +142,6 @@ func (s *BlencTestSuite) TestStaticBlobs() { require.Nil(t, rc) }) }) - } func (s *BlencTestSuite) TestDynamicLinkSuccessPath() { @@ -190,7 +189,6 @@ func (s *BlencTestSuite) TestDynamicLinkSuccessPath() { }) t.Run("work with second dynamic link", func(t *testing.T) { - data2 := []byte("Hello Cinode!") bn2, key2, ai2, err := s.be.Create( @@ -285,7 +283,6 @@ func (s *BlencTestSuite) TestDynamicLinkSuccessPath() { ) require.ErrorIs(t, err, injectedErr) }) - }) t.Run("must fail to create link on read errors", func(t *testing.T) { diff --git a/pkg/cinodefs/cinodefs_interface.go b/pkg/cinodefs/cinodefs_interface.go index 9d03ed8..eee0fd2 100644 --- a/pkg/cinodefs/cinodefs_interface.go +++ b/pkg/cinodefs/cinodefs_interface.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -130,11 +130,10 @@ type FS interface { type cinodeFS struct { c graphContext - maxLinkRedirects int - timeFunc func() time.Time randSource io.Reader - - rootEP node + rootEP node + timeFunc func() time.Time + maxLinkRedirects int } func New( diff --git a/pkg/cinodefs/cinodefs_interface_bb_test.go b/pkg/cinodefs/cinodefs_interface_bb_test.go index e2ac04c..15c2c7e 100644 --- a/pkg/cinodefs/cinodefs_interface_bb_test.go +++ b/pkg/cinodefs/cinodefs_interface_bb_test.go @@ -113,21 +113,20 @@ func (w *testBEWrapper) Update( } type testFileEntry struct { - path []string content string mimeType string + path []string } type CinodeFSMultiFileTestSuite struct { suite.Suite - - ds datastore.DS be testBEWrapper + ds datastore.DS fs cinodefs.FS - contentMap []testFileEntry - maxLinkRedirects int randSource io.Reader timeFunc func() time.Time + contentMap []testFileEntry + maxLinkRedirects int } type randReaderForCinodeFSMultiFileTestSuite CinodeFSMultiFileTestSuite @@ -189,22 +188,22 @@ func (c *CinodeFSMultiFileTestSuite) SetupTest() { require.NoError(t, err) } -func (c *CinodeFSMultiFileTestSuite) checkContentMap(fs cinodefs.FS) { - t := c.T() - +func (c *CinodeFSMultiFileTestSuite) checkContentMap(t *testing.T, fs cinodefs.FS) { for _, file := range c.contentMap { - ep, err := fs.FindEntry(t.Context(), file.path) - require.NoError(t, err) - require.Contains(t, ep.MimeType(), file.mimeType) + t.Run(strings.Join(file.path, "/"), func(t *testing.T) { + ep, err := fs.FindEntry(t.Context(), file.path) + require.NoError(t, err) + require.Contains(t, ep.MimeType(), file.mimeType) - rc, err := fs.OpenEntrypointData(t.Context(), ep) - require.NoError(t, err) - defer rc.Close() + rc, err := fs.OpenEntrypointData(t.Context(), ep) + require.NoError(t, err) + defer rc.Close() - data, err := io.ReadAll(rc) - require.NoError(t, err) + data, err := io.ReadAll(rc) + require.NoError(t, err) - require.Equal(t, file.content, string(data)) + require.Equal(t, file.content, string(data)) + }) } } @@ -222,7 +221,7 @@ func (c *CinodeFSMultiFileTestSuite) TestReopeningInReadOnlyMode() { require.NoError(t, err) require.NotNil(t, fs2) - c.checkContentMap(fs2) + c.checkContentMap(t, fs2) _, err = c.fs.SetEntryFile(t.Context(), c.contentMap[0].path, @@ -231,7 +230,7 @@ func (c *CinodeFSMultiFileTestSuite) TestReopeningInReadOnlyMode() { require.NoError(t, err) // Data in fs was not yet flushed to the datastore, fs2 should still refer to the old content - c.checkContentMap(fs2) + c.checkContentMap(t, fs2) err = c.fs.Flush(t.Context()) require.NoError(t, err) @@ -246,7 +245,7 @@ func (c *CinodeFSMultiFileTestSuite) TestReopeningInReadOnlyMode() { // Check with modified content map c.contentMap[0].content = "modified content" - c.checkContentMap(fs2) + c.checkContentMap(t, fs2) // We should not be allowed to modify fs2 without writer info ep, err := fs2.SetEntryFile( @@ -256,8 +255,8 @@ func (c *CinodeFSMultiFileTestSuite) TestReopeningInReadOnlyMode() { ) require.ErrorIs(t, err, cinodefs.ErrMissingWriterInfo) require.Nil(t, ep) - c.checkContentMap(c.fs) - c.checkContentMap(fs2) + c.checkContentMap(t, c.fs) + c.checkContentMap(t, fs2) } func (c *CinodeFSMultiFileTestSuite) TestReopeningInReadWriteMode() { @@ -275,7 +274,7 @@ func (c *CinodeFSMultiFileTestSuite) TestReopeningInReadWriteMode() { require.NoError(t, err) require.NotNil(t, fs3) - c.checkContentMap(fs3) + c.checkContentMap(t, fs3) // With a proper auth info we can modify files in the root path ep, err := fs3.SetEntryFile( @@ -287,7 +286,7 @@ func (c *CinodeFSMultiFileTestSuite) TestReopeningInReadWriteMode() { require.NotNil(t, ep) c.contentMap[0].content = "modified through fs3" - c.checkContentMap(fs3) + c.checkContentMap(t, fs3) } func (c *CinodeFSMultiFileTestSuite) TestRemovalOfAFile() { @@ -297,7 +296,7 @@ func (c *CinodeFSMultiFileTestSuite) TestRemovalOfAFile() { require.NoError(t, err) c.contentMap = c.contentMap[1:] - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) } func (c *CinodeFSMultiFileTestSuite) TestRemovalOfADirectory() { @@ -321,12 +320,12 @@ func (c *CinodeFSMultiFileTestSuite) TestRemovalOfADirectory() { c.contentMap = filteredEntries require.NotZero(t, removed) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) err = c.fs.DeleteEntry(t.Context(), removedPath) require.ErrorIs(t, err, cinodefs.ErrEntryNotFound) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) ep, err := c.fs.FindEntry(t.Context(), removedPath) require.ErrorIs(t, err, cinodefs.ErrEntryNotFound) @@ -339,7 +338,7 @@ func (c *CinodeFSMultiFileTestSuite) TestRemovalOfADirectory() { func (c *CinodeFSMultiFileTestSuite) TestDeleteTreatFileAsDirectory() { t := c.T() - path := append(c.contentMap[0].path, "sub-file") + path := slices.Concat(c.contentMap[0].path, []string{"sub-file"}) err := c.fs.DeleteEntry(t.Context(), path) require.ErrorIs(t, err, cinodefs.ErrNotADirectory) } @@ -365,12 +364,12 @@ func (c *CinodeFSMultiFileTestSuite) TestResetDir() { c.contentMap = filteredEntries require.NotZero(t, removed) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) err = c.fs.ResetDir(t.Context(), removedPath) require.NoError(t, err) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) ep, err := c.fs.FindEntry(t.Context(), removedPath) require.ErrorIs(t, err, cinodefs.ErrModifiedDirectory) @@ -381,7 +380,7 @@ func (c *CinodeFSMultiFileTestSuite) TestSettingEntry() { t := c.T() t.Run("prevent treating file as directory", func(t *testing.T) { - path := append(c.contentMap[0].path, "sub-file") + path := slices.Concat(c.contentMap[0].path, []string{"sub-file"}) _, err := c.fs.SetEntryFile(t.Context(), path, strings.NewReader("should not happen")) require.ErrorIs(t, err, cinodefs.ErrNotADirectory) }) @@ -395,7 +394,6 @@ func (c *CinodeFSMultiFileTestSuite) TestSettingEntry() { t.Run(strings.Join(path, "::"), func(t *testing.T) { _, err := c.fs.SetEntryFile(t.Context(), path, strings.NewReader("should not succeed")) require.ErrorIs(t, err, cinodefs.ErrEmptyName) - }) } }) @@ -437,7 +435,7 @@ func (c *CinodeFSMultiFileTestSuite) TestSettingEntry() { mimeType: ep.MimeType(), }) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) }) } @@ -508,30 +506,30 @@ func (c *CinodeFSMultiFileTestSuite) TestSubLinksAndWriteOnlyPath() { content: "linked-file", mimeType: ep.MimeType(), }) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) // Convert path to the file to a dynamic link wi, err := c.fs.InjectDynamicLink(t.Context(), linkPath) require.NoError(t, err) require.NotNil(t, wi) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) // Ensure flushing through the dynamic link works err = c.fs.Flush(t.Context()) require.NoError(t, err) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) // Ensure the content can still be changed - corresponding auth info // is still kept in the concept _, err = c.fs.SetEntryFile(t.Context(), path, strings.NewReader("updated-linked-file")) require.NoError(t, err) c.contentMap[len(c.contentMap)-1].content = "updated-linked-file" - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) // Ensure flushing works after the change behind the link err = c.fs.Flush(t.Context()) require.NoError(t, err) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) rootWriterInfo, err := c.fs.RootWriterInfo(t.Context()) require.NoError(t, err) @@ -543,7 +541,7 @@ func (c *CinodeFSMultiFileTestSuite) TestSubLinksAndWriteOnlyPath() { cinodefs.RootWriterInfoString(rootWriterInfo.String()), ) require.NoError(t, err) - c.checkContentMap(fs2) + c.checkContentMap(t, fs2) // Can not do any operation below the split point ep, err = fs2.SetEntryFile( @@ -632,51 +630,51 @@ func (c *CinodeFSMultiFileTestSuite) TestMalformedDirectory() { brokenEP.BlobName = []byte{} for _, d := range []struct { + err error n string d []byte - err error }{ { - "malformed data", - []byte{23, 45, 67, 89, 12, 34, 56, 78, 90}, // Some malformed message - cinodefs.ErrCantOpenDir, + n: "malformed data", + d: []byte{23, 45, 67, 89, 12, 34, 56, 78, 90}, // Some malformed message + err: cinodefs.ErrCantOpenDir, }, { - "entry with empty name", - golang.Must(proto.Marshal(&protobuf.Directory{ + n: "entry with empty name", + d: golang.Must(proto.Marshal(&protobuf.Directory{ Entries: []*protobuf.Directory_Entry{{ Name: "", }}, })), - cinodefs.ErrEmptyName, + err: cinodefs.ErrEmptyName, }, { - "two entries with the same name", - golang.Must(proto.Marshal(&protobuf.Directory{ + n: "two entries with the same name", + d: golang.Must(proto.Marshal(&protobuf.Directory{ Entries: []*protobuf.Directory_Entry{ {Name: "entry", Ep: &ep}, {Name: "entry", Ep: &ep}, }, })), - cinodefs.ErrCantOpenDirDuplicateEntry, + err: cinodefs.ErrCantOpenDirDuplicateEntry, }, { - "missing entrypoint", - golang.Must(proto.Marshal(&protobuf.Directory{ + n: "missing entrypoint", + d: golang.Must(proto.Marshal(&protobuf.Directory{ Entries: []*protobuf.Directory_Entry{ {Name: "entry"}, }, })), - cinodefs.ErrInvalidEntrypointDataNil, + err: cinodefs.ErrInvalidEntrypointDataNil, }, { - "missing blob name", - golang.Must(proto.Marshal(&protobuf.Directory{ + n: "missing blob name", + d: golang.Must(proto.Marshal(&protobuf.Directory{ Entries: []*protobuf.Directory_Entry{ {Name: "entry", Ep: &brokenEP}, }, })), - common.ErrInvalidBlobName, + err: common.ErrInvalidBlobName, }, } { t.Run(d.n, func(t *testing.T) { @@ -716,7 +714,7 @@ func (c *CinodeFSMultiFileTestSuite) TestMalformedLink() { _, err = c.fs.SetEntryFile(t.Context(), []string{"link", "file"}, strings.NewReader("test")) require.NoError(t, err) - linkWI_, err := c.fs.InjectDynamicLink(t.Context(), []string{"link"}) + linkWI, err := c.fs.InjectDynamicLink(t.Context(), []string{"link"}) require.NoError(t, err) // Flush is needed so that we can update entrypoint data and the fs cache won't get into our way @@ -724,28 +722,28 @@ func (c *CinodeFSMultiFileTestSuite) TestMalformedLink() { require.NoError(t, err) for _, d := range []struct { + err error n string d []byte - err error }{ { - "malformed data", - []byte{23, 45, 67, 89, 12, 34, 56, 78, 90}, // Some malformed message - cinodefs.ErrCantOpenLink, + n: "malformed data", + d: []byte{23, 45, 67, 89, 12, 34, 56, 78, 90}, // Some malformed message + err: cinodefs.ErrCantOpenLink, }, { - "missing target blob name", - golang.Must(proto.Marshal(&brokenEP)), - common.ErrInvalidBlobName, + n: "missing target blob name", + d: golang.Must(proto.Marshal(&brokenEP)), + err: common.ErrInvalidBlobName, }, } { t.Run(d.n, func(t *testing.T) { - var linkWI protobuf.WriterInfo - err = proto.Unmarshal(linkWI_.Bytes(), &linkWI) + var linkWIParsed protobuf.WriterInfo + err = proto.Unmarshal(linkWI.Bytes(), &linkWIParsed) require.NoError(t, err) - linkBlobName := golang.Must(common.BlobNameFromBytes(linkWI.BlobName)) - linkAuthInfo := common.AuthInfoFromBytes(linkWI.AuthInfo) - linkKey := common.BlobKeyFromBytes(linkWI.Key) + linkBlobName := golang.Must(common.BlobNameFromBytes(linkWIParsed.BlobName)) + linkAuthInfo := common.AuthInfoFromBytes(linkWIParsed.AuthInfo) + linkKey := common.BlobKeyFromBytes(linkWIParsed.Key) err = c.be.Update(t.Context(), linkBlobName, linkAuthInfo, linkKey, bytes.NewReader(d.d), @@ -802,7 +800,7 @@ func (c *CinodeFSMultiFileTestSuite) TestPathWithMultipleLinks() { content: initialContent, mimeType: ep.MimeType(), }) - c.checkContentMap(c.fs) + c.checkContentMap(t, c.fs) // Modify the content of the file in the original filesystem, not yet flushed const modifiedContent1 = "modified content 1" @@ -810,15 +808,15 @@ func (c *CinodeFSMultiFileTestSuite) TestPathWithMultipleLinks() { require.NoError(t, err) // Change not yet observed through the second filesystem due to no flush - c.checkContentMap(fs2) + c.checkContentMap(t, fs2) err = c.fs.Flush(t.Context()) require.NoError(t, err) // Change must now be observed through the second filesystem c.contentMap[len(c.contentMap)-1].content = modifiedContent1 - c.checkContentMap(c.fs) - c.checkContentMap(fs2) + c.checkContentMap(t, c.fs) + c.checkContentMap(t, fs2) } func (c *CinodeFSMultiFileTestSuite) TestBlobWriteErrorWhenCreatingFile() { @@ -868,7 +866,13 @@ func (c *CinodeFSMultiFileTestSuite) TestBlobWriteWhenCreatingLink() { t := c.T() injectedErr := errors.New("link creation error") - c.be.updateFunc = func(ctx context.Context, name *common.BlobName, ai *common.AuthInfo, key *common.BlobKey, r io.Reader) error { + c.be.updateFunc = func( + ctx context.Context, + name *common.BlobName, + ai *common.AuthInfo, + key *common.BlobKey, + r io.Reader, + ) error { return injectedErr } diff --git a/pkg/cinodefs/context.go b/pkg/cinodefs/context.go index 01cfdb8..bf445fc 100644 --- a/pkg/cinodefs/context.go +++ b/pkg/cinodefs/context.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ type graphContext struct { // but more advanced methods of obtaining the key may be added // through this function in the future. func (c *graphContext) keyFromEntrypoint( - ctx context.Context, + _ context.Context, ep *Entrypoint, ) (*common.BlobKey, error) { if ep.ep.KeyInfo == nil || diff --git a/pkg/cinodefs/entrypoint.go b/pkg/cinodefs/entrypoint.go index e0625b4..299a351 100644 --- a/pkg/cinodefs/entrypoint.go +++ b/pkg/cinodefs/entrypoint.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -39,12 +39,12 @@ var ( ) type Entrypoint struct { - ep protobuf.Entrypoint bn *common.BlobName + ep protobuf.Entrypoint } func EntrypointFromString(s string) (*Entrypoint, error) { - if len(s) == 0 { + if s == "" { return nil, fmt.Errorf("%w: empty string", ErrInvalidEntrypointData) } diff --git a/pkg/cinodefs/httphandler/http.go b/pkg/cinodefs/httphandler/http.go index a6f9630..cacf32e 100644 --- a/pkg/cinodefs/httphandler/http.go +++ b/pkg/cinodefs/httphandler/http.go @@ -29,9 +29,9 @@ import ( ) type Handler struct { + Log *slog.Logger FS cinodefs.FS IndexFile string - Log *slog.Logger } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -72,7 +72,7 @@ func (h *Handler) serveGet(w http.ResponseWriter, r *http.Request, log *slog.Log // that will in the end load the index file if present. http.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect) return - case h.handleHttpError(err, w, log, "Error finding entrypoint"): + case h.handleHTTPError(err, w, log, "Error finding entrypoint"): return } @@ -87,17 +87,17 @@ func (h *Handler) serveGet(w http.ResponseWriter, r *http.Request, log *slog.Log } rc, err := h.FS.OpenEntrypointData(r.Context(), fileEP) - if h.handleHttpError(err, w, log, "Error opening file") { + if h.handleHTTPError(err, w, log, "Error opening file") { return } defer rc.Close() w.Header().Set("Content-Type", fileEP.MimeType()) _, err = io.Copy(w, rc) - h.handleHttpError(err, w, log, "Error sending file") + h.handleHTTPError(err, w, log, "Error sending file") } -func (h *Handler) handleHttpError(err error, w http.ResponseWriter, log *slog.Logger, logMsg string) bool { +func (h *Handler) handleHTTPError(err error, w http.ResponseWriter, log *slog.Logger, logMsg string) bool { if err != nil { log.Error(logMsg, "err", err) http.Error(w, diff --git a/pkg/cinodefs/httphandler/http_test.go b/pkg/cinodefs/httphandler/http_test.go index 7fc1466..65274d2 100644 --- a/pkg/cinodefs/httphandler/http_test.go +++ b/pkg/cinodefs/httphandler/http_test.go @@ -98,8 +98,17 @@ func (s *HandlerTestSuite) setEntry(t *testing.T, data string, path ...string) { require.NoError(t, err) } -func (s *HandlerTestSuite) getEntryETag(t *testing.T, path, etag string) (string, string, string, int) { - req, err := http.NewRequest(http.MethodGet, s.server.URL+path, nil) +func (s *HandlerTestSuite) getEntryETag( + t *testing.T, + path string, + etag string, +) ( + data string, + contentType string, + respEtag string, + code int, +) { + req, err := http.NewRequest(http.MethodGet, s.server.URL+path, http.NoBody) require.NoError(t, err) if etag != "" { @@ -110,14 +119,18 @@ func (s *HandlerTestSuite) getEntryETag(t *testing.T, path, etag string) (string require.NoError(t, err) defer resp.Body.Close() - data, err := io.ReadAll(resp.Body) + bodyData, err := io.ReadAll(resp.Body) require.NoError(t, err) - return string(data), resp.Header.Get("content-type"), resp.Header.Get("ETag"), resp.StatusCode + return string(bodyData), resp.Header.Get("content-type"), resp.Header.Get("ETag"), resp.StatusCode } -func (s *HandlerTestSuite) getEntry(path string) (string, string, int) { - data, contentType, _, code := s.getEntryETag(s.T(), path, "") +func (s *HandlerTestSuite) getEntry(path string) ( + data string, + contentType string, + code int, +) { + data, contentType, _, code = s.getEntryETag(s.T(), path, "") return data, contentType, code } diff --git a/pkg/cinodefs/node_directory.go b/pkg/cinodefs/node_directory.go index 6a1cf3e..bb8c7d9 100644 --- a/pkg/cinodefs/node_directory.go +++ b/pkg/cinodefs/node_directory.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -102,7 +102,7 @@ func (d *nodeDirectory) flush(ctx context.Context, gc *graphContext) (node, *Ent }, ep, nil } -func (c *nodeDirectory) traverse( +func (d *nodeDirectory) traverse( ctx context.Context, gc *graphContext, path []string, @@ -117,10 +117,10 @@ func (c *nodeDirectory) traverse( error, ) { if pathPosition == len(path) { - return whenReached(ctx, c, isWritable) + return whenReached(ctx, d, isWritable) } - subNode, found := c.entries[path[pathPosition]] + subNode, found := d.entries[path[pathPosition]] if !found { if !opts.createNodes { return nil, 0, ErrEntryNotFound @@ -129,19 +129,18 @@ func (c *nodeDirectory) traverse( return nil, 0, ErrMissingWriterInfo } // create new sub-path - newNode, err := c.traverseRecursiveNewPath( + newNode, err := d.traverseRecursiveNewPath( ctx, path, pathPosition+1, - opts, whenReached, ) if err != nil { return nil, 0, err } - c.entries[path[pathPosition]] = newNode - c.dState = dsDirty - return c, dsDirty, nil + d.entries[path[pathPosition]] = newNode + d.dState = dsDirty + return d, dsDirty, nil } // found path entry, descend to sub-node @@ -159,37 +158,35 @@ func (c *nodeDirectory) traverse( return nil, 0, err } if opts.doNotCache { - return c, dsClean, nil + return d, dsClean, nil } - c.entries[path[pathPosition]] = replacement + d.entries[path[pathPosition]] = replacement if replacementState == dsDirty { // child is dirty, this propagates down to the current node - c.dState = dsDirty - return c, dsDirty, nil + d.dState = dsDirty + return d, dsDirty, nil } if replacementState == dsSubDirty { // child itself is not dirty, but some sub-node is, sub-dirtiness // propagates to the current node, but if the directory is // already directly dirty (stronger dirtiness), keep it as it is - if c.dState != dsDirty { - c.dState = dsSubDirty + if d.dState != dsDirty { + d.dState = dsSubDirty } - return c, dsSubDirty, nil + return d, dsSubDirty, nil } golang.Assert(replacementState == dsClean, "ensure correct dirtiness state") // leave current state as it is - return c, dsClean, nil - + return d, dsClean, nil } -func (c *nodeDirectory) traverseRecursiveNewPath( +func (d *nodeDirectory) traverseRecursiveNewPath( ctx context.Context, path []string, pathPosition int, - opts traverseOptions, whenReached traverseGoalFunc, ) ( node, @@ -200,11 +197,10 @@ func (c *nodeDirectory) traverseRecursiveNewPath( return replacement, err } - sub, err := c.traverseRecursiveNewPath( + sub, err := d.traverseRecursiveNewPath( ctx, path, pathPosition+1, - opts, whenReached, ) if err != nil { @@ -219,24 +215,24 @@ func (c *nodeDirectory) traverseRecursiveNewPath( }, nil } -func (c *nodeDirectory) entrypoint() (*Entrypoint, error) { - if c.dState == dsDirty { +func (d *nodeDirectory) entrypoint() (*Entrypoint, error) { + if d.dState == dsDirty { return nil, ErrModifiedDirectory } golang.Assert( - c.dState == dsClean || c.dState == dsSubDirty, + d.dState == dsClean || d.dState == dsSubDirty, "ensure dirtiness state is valid", ) - return c.stored, nil + return d.stored, nil } -func (c *nodeDirectory) deleteEntry(name string) bool { - if _, hasEntry := c.entries[name]; !hasEntry { +func (d *nodeDirectory) deleteEntry(name string) bool { + if _, hasEntry := d.entries[name]; !hasEntry { return false } - delete(c.entries, name) - c.dState = dsDirty + delete(d.entries, name) + d.dState = dsDirty return true } diff --git a/pkg/cinodefs/uploader/directory.go b/pkg/cinodefs/uploader/directory.go index 9a69a40..c314df1 100644 --- a/pkg/cinodefs/uploader/directory.go +++ b/pkg/cinodefs/uploader/directory.go @@ -88,9 +88,9 @@ type dirCompiler struct { fsys fs.FS cfs cinodefs.FS log *slog.Logger + indexFileName string basePath []string createIndexFile bool - indexFileName string } type dirEntry struct { diff --git a/pkg/cinodefs/uploader/directory_test.go b/pkg/cinodefs/uploader/directory_test.go index c5e4359..98a6bf6 100644 --- a/pkg/cinodefs/uploader/directory_test.go +++ b/pkg/cinodefs/uploader/directory_test.go @@ -265,7 +265,12 @@ func (s *DirectoryTestSuite) TestFailUploadReadDir() { type wrappedCinodeFS struct { cinodefs.FS - setEntryFileFunc func(ctx context.Context, path []string, data io.Reader, opts ...cinodefs.EntrypointOption) (*cinodefs.Entrypoint, error) + setEntryFileFunc func( + ctx context.Context, + path []string, + data io.Reader, + opts ...cinodefs.EntrypointOption, + ) (*cinodefs.Entrypoint, error) } func (w *wrappedCinodeFS) SetEntryFile( @@ -289,7 +294,12 @@ func (s *DirectoryTestSuite) TestFailStoreFile() { for _, fName := range []string{"file.txt", "index.html"} { s.cfs = &wrappedCinodeFS{ FS: origFs, - setEntryFileFunc: func(ctx context.Context, path []string, data io.Reader, opts ...cinodefs.EntrypointOption) (*cinodefs.Entrypoint, error) { + setEntryFileFunc: func( + ctx context.Context, + path []string, + data io.Reader, + opts ...cinodefs.EntrypointOption, + ) (*cinodefs.Entrypoint, error) { if path[0] == fName { return nil, injectErr } diff --git a/pkg/cinodefs/writerinfo.go b/pkg/cinodefs/writerinfo.go index 03a65ff..4aeb90b 100644 --- a/pkg/cinodefs/writerinfo.go +++ b/pkg/cinodefs/writerinfo.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ func (wi *WriterInfo) String() string { } func WriterInfoFromString(s string) (*WriterInfo, error) { - if len(s) == 0 { + if s == "" { return nil, fmt.Errorf("%w: empty string", ErrInvalidWriterInfoData) } @@ -68,7 +68,11 @@ func WriterInfoFromBytes(b []byte) (*WriterInfo, error) { return &wi, nil } -func writerInfoFromBlobNameKeyAndAuthInfo(bn *common.BlobName, key *common.BlobKey, authInfo *common.AuthInfo) *WriterInfo { +func writerInfoFromBlobNameKeyAndAuthInfo( + bn *common.BlobName, + key *common.BlobKey, + authInfo *common.AuthInfo, +) *WriterInfo { return &WriterInfo{ wi: protobuf.WriterInfo{ BlobName: bn.Bytes(), diff --git a/pkg/cmd/cinode_web_proxy/integration_test.go b/pkg/cmd/cinodewebproxy/integration_test.go similarity index 96% rename from pkg/cmd/cinode_web_proxy/integration_test.go rename to pkg/cmd/cinodewebproxy/integration_test.go index d08187f..7b7535e 100644 --- a/pkg/cmd/cinode_web_proxy/integration_test.go +++ b/pkg/cmd/cinodewebproxy/integration_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cinode_web_proxy_test +package cinodewebproxy_test import ( "context" @@ -31,7 +31,7 @@ import ( "github.com/cinode/go/pkg/blenc" "github.com/cinode/go/pkg/cinodefs" "github.com/cinode/go/pkg/cinodefs/uploader" - "github.com/cinode/go/pkg/cmd/cinode_web_proxy" + "github.com/cinode/go/pkg/cmd/cinodewebproxy" "github.com/cinode/go/pkg/datastore" "github.com/stretchr/testify/require" ) @@ -94,7 +94,7 @@ func TestIntegration(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - cinode_web_proxy.Execute(ctx) + cinodewebproxy.Execute(ctx) }() time.Sleep(time.Millisecond) // Wait for the server, TODO: This is ugly way to do this @@ -148,7 +148,7 @@ func TestIntegration(t *testing.T) { data, err := os.ReadFile(filepath.Join(dir, fl.Name())) require.NoError(t, err) - err = os.WriteFile(filepath.Join(partialDir, fl.Name()), data, 0666) + err = os.WriteFile(filepath.Join(partialDir, fl.Name()), data, 0o666) require.NoError(t, err) } diff --git a/pkg/cmd/cinode_web_proxy/root.go b/pkg/cmd/cinodewebproxy/root.go similarity index 99% rename from pkg/cmd/cinode_web_proxy/root.go rename to pkg/cmd/cinodewebproxy/root.go index 29c4a57..25a186a 100644 --- a/pkg/cmd/cinode_web_proxy/root.go +++ b/pkg/cmd/cinodewebproxy/root.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cinode_web_proxy +package cinodewebproxy import ( "bytes" diff --git a/pkg/cmd/cinode_web_proxy/root_test.go b/pkg/cmd/cinodewebproxy/root_test.go similarity index 98% rename from pkg/cmd/cinode_web_proxy/root_test.go rename to pkg/cmd/cinodewebproxy/root_test.go index 631aee3..cc59596 100644 --- a/pkg/cmd/cinode_web_proxy/root_test.go +++ b/pkg/cmd/cinodewebproxy/root_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cinode_web_proxy +package cinodewebproxy import ( "bytes" @@ -62,7 +62,7 @@ func TestGetConfig(t *testing.T) { t.Run("entrypoint file", func(t *testing.T) { t.Run("valid", func(t *testing.T) { entrypointFile := filepath.Join(t.TempDir(), "ep.txt") - err := os.WriteFile(entrypointFile, []byte("54321"), 0666) + err := os.WriteFile(entrypointFile, []byte("54321"), 0o666) require.NoError(t, err) t.Setenv("CINODE_ENTRYPOINT_FILE", entrypointFile) @@ -161,7 +161,6 @@ func TestWebProxyHandlerInvalidEntrypoint(t *testing.T) { } func TestWebProxyHandlerSimplePage(t *testing.T) { - ds := datastore.InMemory() be := blenc.FromDatastore(ds) @@ -172,10 +171,10 @@ func TestWebProxyHandlerSimplePage(t *testing.T) { "index.html": "index", "sub/index.html": "sub-index", } { - err := os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0755) + err := os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0o755) require.NoError(t, err) - err = os.WriteFile(filepath.Join(dir, name), []byte(content), 0644) + err = os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644) require.NoError(t, err) } diff --git a/pkg/cmd/public_node/root.go b/pkg/cmd/publicnode/root.go similarity index 95% rename from pkg/cmd/public_node/root.go rename to pkg/cmd/publicnode/root.go index 4411cad..aed6382 100644 --- a/pkg/cmd/public_node/root.go +++ b/pkg/cmd/publicnode/root.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package public_node +package publicnode import ( "context" @@ -43,7 +43,7 @@ func Execute(ctx context.Context) error { } func executeWithConfig(ctx context.Context, cfg *config) error { - handler, err := buildHttpHandler(cfg) + handler, err := buildHTTPHandler(cfg) if err != nil { return err } @@ -65,7 +65,7 @@ func executeWithConfig(ctx context.Context, cfg *config) error { ) } -func buildHttpHandler(cfg *config) (http.Handler, error) { +func buildHTTPHandler(cfg *config) (http.Handler, error) { mainDS, err := datastore.FromLocation(cfg.mainDSLocation) if err != nil { return nil, fmt.Errorf("could not create main datastore: %w", err) @@ -109,7 +109,7 @@ func buildHttpHandler(cfg *config) (http.Handler, error) { // but it does not do any harm. username, password, ok := r.BasicAuth() - var validAuth int = 0 + var validAuth = 0 if ok { validAuth = 1 } @@ -139,13 +139,12 @@ func buildHttpHandler(cfg *config) (http.Handler, error) { } type config struct { + log *slog.Logger mainDSLocation string + uploadUsername string + uploadPassword string additionalDSLocations []string port int - log *slog.Logger - - uploadUsername string - uploadPassword string } func getConfig() (*config, error) { diff --git a/pkg/cmd/public_node/root_test.go b/pkg/cmd/publicnode/root_test.go similarity index 87% rename from pkg/cmd/public_node/root_test.go rename to pkg/cmd/publicnode/root_test.go index 05ab21e..02cbdb8 100644 --- a/pkg/cmd/public_node/root_test.go +++ b/pkg/cmd/publicnode/root_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package public_node +package publicnode import ( "context" @@ -84,7 +84,7 @@ func TestGetConfig(t *testing.T) { func TestBuildHttpHandler(t *testing.T) { t.Run("Successfully created handler", func(t *testing.T) { - h, err := buildHttpHandler(&config{ + h, err := buildHTTPHandler(&config{ mainDSLocation: t.TempDir(), additionalDSLocations: []string{ t.TempDir(), @@ -108,21 +108,20 @@ func TestBuildHttpHandler(t *testing.T) { }) t.Run("Upload auth", func(t *testing.T) { + const ValidUserName = "Alice" + const InvalidUserName = "Bob" + const ValidPassword = "secret" + const InvalidPassword = "plaintext" - const VALID_USERNAME = "Alice" - const INVALID_USERNAME = "Bob" - const VALID_PASSWORD = "secret" - const INVALID_PASSWORD = "plaintext" - - h, err := buildHttpHandler(&config{ + h, err := buildHTTPHandler(&config{ mainDSLocation: t.TempDir(), additionalDSLocations: []string{ t.TempDir(), t.TempDir(), t.TempDir(), }, - uploadUsername: VALID_USERNAME, - uploadPassword: VALID_PASSWORD, + uploadUsername: ValidUserName, + uploadPassword: ValidPassword, }) require.NoError(t, err) require.NotNil(t, h) @@ -133,16 +132,16 @@ func TestBuildHttpHandler(t *testing.T) { err = testblobs.DynamicLink.Put(server.URL) require.ErrorContains(t, err, "403") - err = testblobs.DynamicLink.PutWithAuth(server.URL, VALID_USERNAME, VALID_PASSWORD) + err = testblobs.DynamicLink.PutWithAuth(server.URL, ValidUserName, ValidPassword) require.NoError(t, err) - err = testblobs.DynamicLink.PutWithAuth(server.URL, VALID_USERNAME, INVALID_PASSWORD) + err = testblobs.DynamicLink.PutWithAuth(server.URL, ValidUserName, InvalidPassword) require.ErrorContains(t, err, "403") - err = testblobs.DynamicLink.PutWithAuth(server.URL, INVALID_USERNAME, VALID_PASSWORD) + err = testblobs.DynamicLink.PutWithAuth(server.URL, InvalidUserName, ValidPassword) require.ErrorContains(t, err, "403") - err = testblobs.DynamicLink.PutWithAuth(server.URL, INVALID_USERNAME, INVALID_PASSWORD) + err = testblobs.DynamicLink.PutWithAuth(server.URL, InvalidUserName, InvalidPassword) require.ErrorContains(t, err, "403") _, err = testblobs.DynamicLink.Get(server.URL) @@ -150,7 +149,7 @@ func TestBuildHttpHandler(t *testing.T) { }) t.Run("invalid main datastore", func(t *testing.T) { - h, err := buildHttpHandler(&config{ + h, err := buildHTTPHandler(&config{ mainDSLocation: "", }) require.ErrorContains(t, err, "could not create main datastore") @@ -158,7 +157,7 @@ func TestBuildHttpHandler(t *testing.T) { }) t.Run("invalid additional datastore", func(t *testing.T) { - h, err := buildHttpHandler(&config{ + h, err := buildHTTPHandler(&config{ mainDSLocation: "memory://", additionalDSLocations: []string{""}, }) diff --git a/pkg/cmd/static_datastore/compile.go b/pkg/cmd/staticdatastore/compile.go similarity index 96% rename from pkg/cmd/static_datastore/compile.go rename to pkg/cmd/staticdatastore/compile.go index ffd6128..4df2460 100644 --- a/pkg/cmd/static_datastore/compile.go +++ b/pkg/cmd/staticdatastore/compile.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package static_datastore +package staticdatastore import ( "context" @@ -57,7 +57,7 @@ func compileCmd() *cobra.Command { fatalResult := func(format string, args ...interface{}) error { msg := fmt.Sprintf(format, args...) - enc.Encode(map[string]string{ + _ = enc.Encode(map[string]string{ "result": "ERROR", "msg": msg, }) @@ -67,7 +67,7 @@ func compileCmd() *cobra.Command { return errors.New(msg) } - if len(rootWriterInfoFile) > 0 { + if rootWriterInfoFile != "" { data, err := os.ReadFile(rootWriterInfoFile) if err != nil { return fatalResult("Couldn't read data from the writer info file at '%s': %v", rootWriterInfoFile, err) @@ -77,7 +77,7 @@ func compileCmd() *cobra.Command { } rootWriterInfoStr = string(data) } - if len(rootWriterInfoStr) > 0 { + if rootWriterInfoStr != "" { wi, err := cinodefs.WriterInfoFromString(rootWriterInfoStr) if err != nil { return fatalResult("Couldn't parse writer info: %v", err) @@ -107,7 +107,8 @@ func compileCmd() *cobra.Command { if wi != nil { result["writer-info"] = wi.String() } - enc.Encode(result) + + _ = enc.Encode(result) log.Println("DONE") return nil @@ -132,7 +133,7 @@ func compileCmd() *cobra.Command { "if set to true, use raw filesystem instead of the optimized one, "+ "can be used to create dataset for a standard http server", ) - cmd.Flags().MarkHidden("raw-filesystem") + _ = cmd.Flags().MarkHidden("raw-filesystem") cmd.Flags().StringVarP( &rootWriterInfoStr, "writer-info", "w", "", "writer info for the root dynamic link, if neither writer info nor writer info file is specified, "+ @@ -161,12 +162,12 @@ func compileCmd() *cobra.Command { } type compileFSOptions struct { + writerInfo *cinodefs.WriterInfo srcDir string dstLocation string + indexFile string static bool - writerInfo *cinodefs.WriterInfo generateIndexFiles bool - indexFile string append bool } @@ -184,11 +185,13 @@ func compileFS( } opts := []cinodefs.Option{} - if o.static { + + switch { + case o.static: opts = append(opts, cinodefs.NewRootStaticDirectory()) - } else if o.writerInfo == nil { + case o.writerInfo == nil: opts = append(opts, cinodefs.NewRootDynamicLink()) - } else { + default: opts = append(opts, cinodefs.RootWriterInfo(o.writerInfo)) } diff --git a/pkg/cmd/static_datastore/root.go b/pkg/cmd/staticdatastore/root.go similarity index 93% rename from pkg/cmd/static_datastore/root.go rename to pkg/cmd/staticdatastore/root.go index 44bd710..e2eb4ac 100644 --- a/pkg/cmd/static_datastore/root.go +++ b/pkg/cmd/staticdatastore/root.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package static_datastore +package staticdatastore import ( "github.com/spf13/cobra" @@ -35,8 +35,8 @@ Note that this tool is supposed to be used for testing purposes only. It does not guarantee secrecy since the encryption key for the root node is stored in a plaintext in a file called 'entrypoint.txt'. `, - Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() }, } diff --git a/pkg/cmd/static_datastore/static_datastore_test.go b/pkg/cmd/staticdatastore/static_datastore_test.go similarity index 98% rename from pkg/cmd/static_datastore/static_datastore_test.go rename to pkg/cmd/staticdatastore/static_datastore_test.go index 79679c2..bd2fec7 100644 --- a/pkg/cmd/static_datastore/static_datastore_test.go +++ b/pkg/cmd/staticdatastore/static_datastore_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package static_datastore +package staticdatastore import ( "bytes" @@ -116,10 +116,10 @@ func (s *CompileAndReadTestSuite) uploadDatasetToDatastore( dir := t.TempDir() for _, td := range dataset { - err := os.MkdirAll(filepath.Join(dir, filepath.Dir(td.fName)), 0777) + err := os.MkdirAll(filepath.Join(dir, filepath.Dir(td.fName)), 0o777) require.NoError(t, err) - err = os.WriteFile(filepath.Join(dir, td.fName), []byte(td.contents), 0600) + err = os.WriteFile(filepath.Join(dir, td.fName), []byte(td.contents), 0o600) require.NoError(t, err) } @@ -267,7 +267,7 @@ func (s *CompileAndReadTestSuite) TestCompileAndRead() { t.Run("Read writer info from file", func(t *testing.T) { wiFile := filepath.Join(t.TempDir(), "epfile") - require.NoError(t, os.WriteFile(wiFile, []byte(wi.String()), 0777)) + require.NoError(t, os.WriteFile(wiFile, []byte(wi.String()), 0o777)) _, ep := s.uploadDatasetToDatastore(t, s.initialTestDataset, datastoreAddress, "--writer-info-file", wiFile, @@ -356,13 +356,13 @@ func TestInvalidOptions(t *testing.T) { tempDir := t.TempDir() emptyFile := filepath.Join(tempDir, "empty") - err := os.WriteFile(emptyFile, []byte{}, 0777) + err := os.WriteFile(emptyFile, []byte{}, 0o777) require.NoError(t, err) for _, d := range []struct { name string - args []string errorContains string + args []string }{ { name: "invalid root writer info", diff --git a/pkg/common/blob_name.go b/pkg/common/blob_name.go index d664c69..5aff9f5 100644 --- a/pkg/common/blob_name.go +++ b/pkg/common/blob_name.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ func BlobNameFromHashAndType(hash []byte, t BlobType) (*BlobName, error) { copy(bn[1:], hash) - scrambledTypeByte := byte(t.t) + scrambledTypeByte := t.t for _, b := range hash { scrambledTypeByte ^= b } diff --git a/pkg/datastore/factory.go b/pkg/datastore/factory.go index e72b9c6..c920033 100644 --- a/pkg/datastore/factory.go +++ b/pkg/datastore/factory.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,8 +24,8 @@ import ( const ( filePrefix = "file://" rawFilePrefix = "file-raw://" - webPrefixHttp = "http://" - webPrefixHttps = "https://" + webPrefixHTTP = "http://" + webPrefixHTTPS = "https://" memoryPrefix = "memory://" ) @@ -36,9 +36,12 @@ var ( // FromLocation creates new instance of the datastore from location string. // // The string may be of the following form: -// - file:// - create datastore using local filesystem's path (optimized) as the storage, see InFileSystem for more details -// - file-raw:// - create datastore using local filesystem's path (simplified) as the storage, see InRawFileSystem for more details -// - http://
or https://
- connects to datastore exposed through a http protocol, see FromWeb for more details +// - file:// - create datastore using local filesystem's path (optimized) as the storage, +// see InFileSystem for more details +// - file-raw:// - create datastore using local filesystem's path (simplified) as the storage, +// see InRawFileSystem for more details +// - http://
or https://
- connects to datastore exposed through a http protocol, +// see FromWeb for more details // - memory:// - creates a local in-process datastore without persistent storage // - - equivalent to file:// func FromLocation(location string) (DS, error) { @@ -49,8 +52,8 @@ func FromLocation(location string) (DS, error) { case strings.HasPrefix(location, rawFilePrefix): return InRawFileSystem(location[len(rawFilePrefix):]) - case strings.HasPrefix(location, webPrefixHttp), - strings.HasPrefix(location, webPrefixHttps): + case strings.HasPrefix(location, webPrefixHTTP), + strings.HasPrefix(location, webPrefixHTTPS): return FromWeb(location) case strings.HasPrefix(location, memoryPrefix): diff --git a/pkg/datastore/factory_test.go b/pkg/datastore/factory_test.go index 77f9000..954719f 100644 --- a/pkg/datastore/factory_test.go +++ b/pkg/datastore/factory_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,16 +24,40 @@ import ( func TestNewFromLocation(t *testing.T) { for _, d := range []struct { + expectedMainType any + expectedStorageType any location string - expectedMainType interface{} - expectedStorageType interface{} }{ - {"file://" + t.TempDir(), &datastore{}, &fileSystem{}}, - {"file-raw://" + t.TempDir(), &datastore{}, &rawFileSystem{}}, - {"memory://", &datastore{}, &memory{}}, - {"http://some.domain.com", &webConnector{}, nil}, - {"https://some.domain.com", &webConnector{}, nil}, - {t.TempDir(), &datastore{}, &fileSystem{}}, + { + location: "file://" + t.TempDir(), + expectedMainType: &datastore{}, + expectedStorageType: &fileSystem{}, + }, + { + location: "file-raw://" + t.TempDir(), + expectedMainType: &datastore{}, + expectedStorageType: &rawFileSystem{}, + }, + { + location: "memory://", + expectedMainType: &datastore{}, + expectedStorageType: &memory{}, + }, + { + location: "http://some.domain.com", + expectedMainType: &webConnector{}, + expectedStorageType: nil, + }, + { + location: "https://some.domain.com", + expectedMainType: &webConnector{}, + expectedStorageType: nil, + }, + { + location: t.TempDir(), + expectedMainType: &datastore{}, + expectedStorageType: &fileSystem{}, + }, } { t.Run(d.location, func(t *testing.T) { ds, err := FromLocation(d.location) diff --git a/pkg/datastore/interface_test.go b/pkg/datastore/interface_test.go index 7188814..da4d539 100644 --- a/pkg/datastore/interface_test.go +++ b/pkg/datastore/interface_test.go @@ -25,25 +25,25 @@ import ( func TestDatastoreTestSuite(t *testing.T) { t.Run("InMemory", func(t *testing.T) { - suite.Run(t, &DatastoreTestSuite{ + suite.Run(t, &TestSuite{ CreateDS: func() (DS, error) { return InMemory(), nil }, }) }) t.Run("InFileSystem", func(t *testing.T) { - suite.Run(t, &DatastoreTestSuite{ + suite.Run(t, &TestSuite{ CreateDS: func() (DS, error) { return InFileSystem(t.TempDir()) }, }) }) t.Run("InRawFileSystem", func(t *testing.T) { - suite.Run(t, &DatastoreTestSuite{ + suite.Run(t, &TestSuite{ CreateDS: func() (DS, error) { return InRawFileSystem(t.TempDir()) }, }) }) t.Run("FromWeb", func(t *testing.T) { - suite.Run(t, &DatastoreTestSuite{ + suite.Run(t, &TestSuite{ CreateDS: func() (DS, error) { server := httptest.NewServer(WebInterface(InMemory())) t.Cleanup(func() { server.Close() }) diff --git a/pkg/datastore/interface_testsuite.go b/pkg/datastore/interface_testsuite.go index 4a38454..0b70e62 100644 --- a/pkg/datastore/interface_testsuite.go +++ b/pkg/datastore/interface_testsuite.go @@ -32,7 +32,7 @@ import ( "github.com/stretchr/testify/suite" ) -type DatastoreTestSuite struct { +type TestSuite struct { suite.Suite // CreateDS is a function that creates a new datastore for each test. @@ -42,13 +42,13 @@ type DatastoreTestSuite struct { DS DS } -func (s *DatastoreTestSuite) SetupTest() { +func (s *TestSuite) SetupTest() { ds, err := s.CreateDS() s.Require().NoError(err) s.DS = ds } -func (s *DatastoreTestSuite) TestOpenNonExisting() { +func (s *TestSuite) TestOpenNonExisting() { for _, name := range testutils.EmptyBlobNamesOfAllTypes { s.Run(fmt.Sprint(name.Type()), func() { r, err := s.DS.Open(context.Background(), name) @@ -58,7 +58,7 @@ func (s *DatastoreTestSuite) TestOpenNonExisting() { } } -func (s *DatastoreTestSuite) TestOpenInvalidBlobType() { +func (s *TestSuite) TestOpenInvalidBlobType() { bn, err := common.BlobNameFromHashAndType(sha256.New().Sum(nil), common.NewBlobType(0xFF)) s.Require().NoError(err) @@ -70,7 +70,7 @@ func (s *DatastoreTestSuite) TestOpenInvalidBlobType() { s.Require().ErrorIs(err, blobtypes.ErrUnknownBlobType) } -func (s *DatastoreTestSuite) TestBlobValidationFailed() { +func (s *TestSuite) TestBlobValidationFailed() { for _, name := range testutils.EmptyBlobNamesOfAllTypes { s.Run(fmt.Sprint(name.Type()), func() { err := s.DS.Update(context.Background(), name, bytes.NewReader([]byte("test"))) @@ -79,9 +79,8 @@ func (s *DatastoreTestSuite) TestBlobValidationFailed() { } } -func (s *DatastoreTestSuite) TestSaveSuccessfulStatic() { +func (s *TestSuite) TestSaveSuccessfulStatic() { for _, b := range testutils.TestBlobs { - exists, err := s.DS.Exists(context.Background(), b.Name) s.Require().NoError(err) s.Require().False(exists) @@ -121,10 +120,10 @@ func (s *DatastoreTestSuite) TestSaveSuccessfulStatic() { } } -func (s *DatastoreTestSuite) TestErrorWhileUpdating() { +func (s *TestSuite) TestErrorWhileUpdating() { for i, b := range testutils.TestBlobs { s.Run(fmt.Sprint(i), func() { - errRet := errors.New("Test error") + errRet := errors.New("test error") err := s.DS.Update(context.Background(), b.Name, testutils.BReader(b.Data, func() error { return errRet }, nil)) @@ -137,10 +136,9 @@ func (s *DatastoreTestSuite) TestErrorWhileUpdating() { } } -func (s *DatastoreTestSuite) TestErrorWhileOverwriting() { +func (s *TestSuite) TestErrorWhileOverwriting() { for i, b := range testutils.TestBlobs { s.Run(fmt.Sprint(i), func() { - err := s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data)) s.Require().NoError(err) @@ -173,7 +171,7 @@ func (s *DatastoreTestSuite) TestErrorWhileOverwriting() { } } -func (s *DatastoreTestSuite) TestDeleteNonExisting() { +func (s *TestSuite) TestDeleteNonExisting() { b := testutils.TestBlobs[0] err := s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data)) @@ -187,8 +185,7 @@ func (s *DatastoreTestSuite) TestDeleteNonExisting() { s.Require().True(exists) } -func (s *DatastoreTestSuite) TestDeleteExisting() { - +func (s *TestSuite) TestDeleteExisting() { b := testutils.TestBlobs[0] err := s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data)) s.Require().NoError(err) @@ -209,17 +206,17 @@ func (s *DatastoreTestSuite) TestDeleteExisting() { s.Require().Nil(r) } -func (s *DatastoreTestSuite) TestGetKind() { +func (s *TestSuite) TestGetKind() { k := s.DS.Kind() s.Require().NotEmpty(k) } -func (s *DatastoreTestSuite) TestAddress() { +func (s *TestSuite) TestAddress() { address := s.DS.Address() s.Require().Regexp(`^[a-zA-Z0-9_-]+://`, address) } -func (s *DatastoreTestSuite) TestSimultaneousReads() { +func (s *TestSuite) TestSimultaneousReads() { const threadCnt = 10 const readCnt = 200 @@ -254,18 +251,14 @@ func (s *DatastoreTestSuite) TestSimultaneousReads() { wg.Wait() } -func (s *DatastoreTestSuite) TestSimultaneousUpdates() { +func (s *TestSuite) TestSimultaneousUpdates() { const threadCnt = 3 b := testutils.TestBlobs[0] - wg := sync.WaitGroup{} - wg.Add(threadCnt) - - for i := 0; i < threadCnt; i++ { - go func(i int) { - defer wg.Done() + for range threadCnt { + wg.Go(func() { err := s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data)) if errors.Is(err, ErrUploadInProgress) { // TODO: We should be able to handle this case @@ -277,7 +270,7 @@ func (s *DatastoreTestSuite) TestSimultaneousUpdates() { exists, err := s.DS.Exists(context.Background(), b.Name) s.Require().NoError(err) s.Require().True(exists) - }(i) + }) } wg.Wait() @@ -297,7 +290,7 @@ func (s *DatastoreTestSuite) TestSimultaneousUpdates() { s.Require().NoError(err) } -func (s *DatastoreTestSuite) updateDynamicLink(num int) { +func (s *TestSuite) updateDynamicLink(num int) { err := s.DS.Update( context.Background(), testutils.DynamicLinkPropagationData[num].Name, @@ -306,7 +299,7 @@ func (s *DatastoreTestSuite) updateDynamicLink(num int) { s.Require().NoError(err) } -func (s *DatastoreTestSuite) readDynamicLinkData() []byte { +func (s *TestSuite) readDynamicLinkData() []byte { r, err := s.DS.Open(context.Background(), testutils.DynamicLinkPropagationData[0].Name) s.Require().NoError(err) @@ -322,14 +315,14 @@ func (s *DatastoreTestSuite) readDynamicLinkData() []byte { return elink } -func (s *DatastoreTestSuite) expectDynamicLinkData(num int) { +func (s *TestSuite) expectDynamicLinkData(num int) { s.Require().Equal( testutils.DynamicLinkPropagationData[num].Expected, s.readDynamicLinkData(), ) } -func (s *DatastoreTestSuite) TestDynamicLinkPropagation() { +func (s *TestSuite) TestDynamicLinkPropagation() { s.updateDynamicLink(0) s.expectDynamicLinkData(0) diff --git a/pkg/datastore/multisource/multi_source.go b/pkg/datastore/multisource/multi_source.go index 55e1000..33c7af5 100644 --- a/pkg/datastore/multisource/multi_source.go +++ b/pkg/datastore/multisource/multi_source.go @@ -34,16 +34,23 @@ const ( ) type multiSourceDatastoreBlobState struct { + downloadingFinishedCChan chan struct{} lastUpdateTime time.Time downloading bool notFound bool - downloadingFinishedCChan chan struct{} } type multiSourceDatastore struct { + // Logger output + log *slog.Logger + // Main datastore main datastore.DS + // Last update time for blobs, either for dynamic content or last result of not found for + // static ones + blobStates map[string]multiSourceDatastoreBlobState + // Additional sources that will be queried whenever the main source // does not contain the data or contains outdated content additional []datastore.DS @@ -54,24 +61,17 @@ type multiSourceDatastore struct { // Time between re-checking blob existence in additional datastores notFoundRecheckTime time.Duration - // Last update time for blobs, either for dynamic content or last result of not found for - // static ones - blobStates map[string]multiSourceDatastoreBlobState - // Guard additional sources and update time map m sync.Mutex - - // Logger output - log *slog.Logger } func New(main datastore.DS, options ...Option) datastore.DS { ds := &multiSourceDatastore{ main: main, + blobStates: map[string]multiSourceDatastoreBlobState{}, additional: nil, dynamicDataRefreshTime: defaultDynamicDataRefreshTime, notFoundRecheckTime: defaultNotFoundRecheckTime, - blobStates: map[string]multiSourceDatastoreBlobState{}, log: slog.Default(), } @@ -119,94 +119,59 @@ func (m *multiSourceDatastore) fetch(ctx context.Context, name *common.BlobName) // do an update process for { - waitChan, startDownload := func() (chan struct{}, bool) { - m.m.Lock() - defer m.m.Unlock() - - needsDownload := false - - state, found := m.blobStates[name.String()] - - switch { - case !found: - // Seen for the first time - needsDownload = true - - case state.downloading: - // Blob currently being downloaded - return state.downloadingFinishedCChan, false - - case m.needsDownload(state, name, time.Now()): - // We should update the blob - needsDownload = true - } - - if !needsDownload { - return nil, false - } - - // State not found, request new download - ch := make(chan struct{}) - m.blobStates[name.String()] = multiSourceDatastoreBlobState{ - downloading: true, - downloadingFinishedCChan: ch, - } - return ch, true - }() - - if startDownload { - m.log.Info("Starting download", - "blob", name.String(), - ) - wasFound := false - for i, ds := range m.additional { - r, err := ds.Open(ctx, name) - if err != nil { - m.log.Debug("Failed to fetch blob from additional datastore", - "blob", name.String(), - "datastore", ds.Address(), - "err", err, - ) - continue - } - - m.log.Info("Blob found in additional datastore", - "blob", name.String(), - "datastore-num", i+1, - ) - err = m.main.Update(ctx, name, r) - r.Close() - if err != nil { - m.log.Error("Failed to store blob in local datastore", - slog.Any("err", err), - slog.String("blob", name.String()), - ) - } - wasFound = true - } - if !wasFound { - m.log.Warn("Did not find blob in any datastore", - "blob", name.String(), - ) - } - defer close(waitChan) - - m.m.Lock() - defer m.m.Unlock() - - m.blobStates[name.String()] = multiSourceDatastoreBlobState{ - lastUpdateTime: time.Now(), - notFound: !wasFound, - } - return - } + waitChan, startDownload := m.determineBlobState(name) - if waitChan == nil { + switch { + case startDownload: + // Start the download routine + m.startBlobDownload(ctx, name, waitChan) + + case waitChan == nil: + // No ned to do anything, blob ready return + + default: + // Wait for the current in-progress download + <-waitChan } + } +} + +func (m *multiSourceDatastore) determineBlobState(name *common.BlobName) ( + finishedDownloadingChannel chan struct{}, + needsDownload bool, +) { + m.m.Lock() + defer m.m.Unlock() - <-waitChan + state, found := m.blobStates[name.String()] + + switch { + case !found: + // Seen for the first time + needsDownload = true + + case state.downloading: + // Blob currently being downloaded + return state.downloadingFinishedCChan, false + + case m.needsDownload(state, name, time.Now()): + // We should update the blob + needsDownload = true } + + if !needsDownload { + return nil, false + } + + // State not found, request new download + ch := make(chan struct{}) + m.blobStates[name.String()] = multiSourceDatastoreBlobState{ + downloading: true, + downloadingFinishedCChan: ch, + } + + return ch, true } // needsDownload checks if the blob needs to be downloaded based on the state and the current time. @@ -226,3 +191,55 @@ func (m *multiSourceDatastore) needsDownload( return now.After(state.lastUpdateTime.Add(m.dynamicDataRefreshTime)) } } + +func (m *multiSourceDatastore) startBlobDownload( + ctx context.Context, + name *common.BlobName, + waitChan chan struct{}, +) { + m.log.Info("Starting download", + "blob", name.String(), + ) + + wasFound := false + + for i, ds := range m.additional { + r, err := ds.Open(ctx, name) + if err != nil { + m.log.Debug("Failed to fetch blob from additional datastore", + "blob", name.String(), + "datastore", ds.Address(), + "err", err, + ) + continue + } + + m.log.Info("Blob found in additional datastore", + "blob", name.String(), + "datastore-num", i+1, + ) + err = m.main.Update(ctx, name, r) + r.Close() + if err != nil { + m.log.Error("Failed to store blob in local datastore", + slog.Any("err", err), + slog.String("blob", name.String()), + ) + } + wasFound = true + } + if !wasFound { + m.log.Warn("Did not find blob in any datastore", + "blob", name.String(), + ) + } + defer close(waitChan) + + m.m.Lock() + defer m.m.Unlock() + + m.blobStates[name.String()] = multiSourceDatastoreBlobState{ + lastUpdateTime: time.Now(), + notFound: !wasFound, + } +} diff --git a/pkg/datastore/multisource/multi_source_test.go b/pkg/datastore/multisource/multi_source_test.go index af99f37..8c90af2 100644 --- a/pkg/datastore/multisource/multi_source_test.go +++ b/pkg/datastore/multisource/multi_source_test.go @@ -36,7 +36,7 @@ import ( ) func TestInterface(t *testing.T) { - suite.Run(t, &datastore.DatastoreTestSuite{ + suite.Run(t, &datastore.TestSuite{ CreateDS: func() (datastore.DS, error) { return New(datastore.InMemory()), nil }, @@ -180,7 +180,6 @@ func (s *MultiSourceDatastoreTestSuite) TestDynamicLinkRefresh() { // The updated data should be available in the main datastore by now require.EqualValues(t, "Hello world 2", s.fetchLink(ds, bn, key)) - } func (s *MultiSourceDatastoreTestSuite) updateLink( diff --git a/pkg/datastore/storage_filesystem.go b/pkg/datastore/storage_filesystem.go index 7472584..accf61f 100644 --- a/pkg/datastore/storage_filesystem.go +++ b/pkg/datastore/storage_filesystem.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ type fileSystem struct { var _ storage = (*fileSystem)(nil) func newStorageFilesystem(path string) (*fileSystem, error) { - err := os.MkdirAll(path, 0755) + err := os.MkdirAll(path, 0o755) if err != nil { return nil, err } @@ -64,7 +64,7 @@ func (fs *fileSystem) createTemporaryWriteStream(name *common.BlobName) (*os.Fil tempName := fs.getFileName(name, fsSuffixUpload) // Ensure dir exists - err := os.MkdirAll(filepath.Dir(tempName), 0755) + err := os.MkdirAll(filepath.Dir(tempName), 0o755) if err != nil { return nil, err } @@ -73,7 +73,7 @@ func (fs *fileSystem) createTemporaryWriteStream(name *common.BlobName) (*os.Fil fh, err := os.OpenFile( tempName, os.O_CREATE|os.O_EXCL|os.O_APPEND|os.O_WRONLY, - 0644, + 0o644, ) if os.IsExist(err) { return nil, ErrUploadInProgress @@ -122,7 +122,6 @@ func (w *fileSystemWriteCloser) Close() error { } func (fs *fileSystem) openWriteStream(ctx context.Context, name *common.BlobName) (WriteCloseCanceller, error) { - fl, err := fs.createTemporaryWriteStream(name) if err != nil { return nil, err diff --git a/pkg/datastore/storage_filesystem_test.go b/pkg/datastore/storage_filesystem_test.go index aeb15e5..888392d 100644 --- a/pkg/datastore/storage_filesystem_test.go +++ b/pkg/datastore/storage_filesystem_test.go @@ -31,13 +31,12 @@ func temporaryFS(t *testing.T) *fileSystem { return ds } -func touchFile(t *testing.T, fName string) string { - err := os.MkdirAll(filepath.Dir(fName), 0777) +func touchFile(t *testing.T, fName string) { + err := os.MkdirAll(filepath.Dir(fName), 0o777) require.NoError(t, err) fl, err := os.Create(fName) require.NoError(t, err) fl.Close() - return fName } func protect(t *testing.T, fName string) func() { @@ -68,13 +67,12 @@ func TestFilesystemSaveFailureDir(t *testing.T) { } func TestFilesystemSaveFailureTempFile(t *testing.T) { - fs := temporaryFS(t) // Create blob's directory as unmodifiable fName := fs.getFileName(testutils.EmptyBlobNameStatic, fsSuffixCurrent) dirPath := filepath.Dir(fName) - err := os.MkdirAll(dirPath, 0777) + err := os.MkdirAll(dirPath, 0o777) require.NoError(t, err) defer protect(t, dirPath)() @@ -84,12 +82,11 @@ func TestFilesystemSaveFailureTempFile(t *testing.T) { } func TestFilesystemRenameFailure(t *testing.T) { - fs := temporaryFS(t) // Create directory where blob should be fName := fs.getFileName(testutils.EmptyBlobNameStatic, fsSuffixCurrent) - os.MkdirAll(fName, 0777) + os.MkdirAll(fName, 0o777) w, err := fs.openWriteStream(t.Context(), testutils.EmptyBlobNameStatic) require.NoError(t, err) @@ -103,7 +100,7 @@ func TestFilesystemDeleteFailure(t *testing.T) { // Create directory where blob should be with some file inside fName := fs.getFileName(testutils.EmptyBlobNameStatic, fsSuffixCurrent) - os.MkdirAll(fName, 0777) + os.MkdirAll(fName, 0o777) touchFile(t, fName+"/keep.me") err := fs.delete(t.Context(), testutils.EmptyBlobNameStatic) @@ -123,7 +120,7 @@ func TestFilesystemExistsFailure(t *testing.T) { // Create blob's directory as unmodifiable fName := fs.getFileName(testutils.EmptyBlobNameStatic, fsSuffixCurrent) dirPath := filepath.Dir(fName) - err := os.MkdirAll(dirPath, 0777) + err := os.MkdirAll(dirPath, 0o777) require.NoError(t, err) defer protect(t, dirPath)() diff --git a/pkg/datastore/storage_memory.go b/pkg/datastore/storage_memory.go index 3abf51e..8d0e8a1 100644 --- a/pkg/datastore/storage_memory.go +++ b/pkg/datastore/storage_memory.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -67,9 +67,9 @@ func (m *memory) openReadStream(ctx context.Context, name *common.BlobName) (io. } type memoryWriteCloser struct { + m *memory b *bytes.Buffer n string - m *memory } func (w *memoryWriteCloser) Write(b []byte) (int, error) { diff --git a/pkg/datastore/storage_memory_test.go b/pkg/datastore/storage_memory_test.go index d48e552..7830a1d 100644 --- a/pkg/datastore/storage_memory_test.go +++ b/pkg/datastore/storage_memory_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2022 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import ( "github.com/stretchr/testify/require" ) -func temporaryMemory(t *testing.T) *memory { +func temporaryMemory(_ *testing.T) *memory { return newStorageMemory() } diff --git a/pkg/datastore/storage_raw_filesystem.go b/pkg/datastore/storage_raw_filesystem.go index a7f8253..ab2755c 100644 --- a/pkg/datastore/storage_raw_filesystem.go +++ b/pkg/datastore/storage_raw_filesystem.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ type rawFileSystem struct { var _ storage = (*rawFileSystem)(nil) func newStorageRawFilesystem(path string) (*rawFileSystem, error) { - err := os.MkdirAll(path, 0755) + err := os.MkdirAll(path, 0o755) if err != nil { return nil, err } diff --git a/pkg/datastore/storage_test.go b/pkg/datastore/storage_test.go index a1e6861..3ed399e 100644 --- a/pkg/datastore/storage_test.go +++ b/pkg/datastore/storage_test.go @@ -176,7 +176,6 @@ func TestStorageDelete(t *testing.T) { }) } }) - }) } } @@ -184,7 +183,6 @@ func TestStorageDelete(t *testing.T) { func TestStorageTooManySimultaneousSaves(t *testing.T) { for _, st := range allTestStorages(t) { t.Run(st.kind(), func(t *testing.T) { - // Start the first writer w1, err := st.openWriteStream(t.Context(), testutils.EmptyBlobNameStatic) require.NoError(t, err) @@ -215,7 +213,6 @@ func TestStorageTooManySimultaneousSaves(t *testing.T) { func TestStorageSaveWhileDeleting(t *testing.T) { for _, st := range allTestStorages(t) { t.Run(st.kind(), func(t *testing.T) { - w, err := st.openWriteStream(t.Context(), testutils.EmptyBlobNameStatic) require.NoError(t, err) diff --git a/pkg/datastore/testutils/generate/tesblobs.go.tpl b/pkg/datastore/testutils/generate/tesblobs.go.tpl index 535a6d5..3beb888 100644 --- a/pkg/datastore/testutils/generate/tesblobs.go.tpl +++ b/pkg/datastore/testutils/generate/tesblobs.go.tpl @@ -22,6 +22,7 @@ import ( "github.com/jbenet/go-base58" ) +// nolint:lll // test data vectors var TestBlobs = []struct { Name *common.BlobName Data []byte @@ -36,6 +37,7 @@ var TestBlobs = []struct { {{- end }} } +// nolint:lll // test data vectors var DynamicLinkPropagationData = []struct { Name *common.BlobName Data []byte diff --git a/pkg/datastore/testutils/testblobs.go b/pkg/datastore/testutils/testblobs.go index 51ae9f9..230e8f4 100644 --- a/pkg/datastore/testutils/testblobs.go +++ b/pkg/datastore/testutils/testblobs.go @@ -22,6 +22,7 @@ import ( "github.com/jbenet/go-base58" ) +// nolint:lll // test data vectors var TestBlobs = []struct { Name *common.BlobName Data []byte @@ -59,6 +60,7 @@ var TestBlobs = []struct { }, } +// nolint:lll // test data vectors var DynamicLinkPropagationData = []struct { Name *common.BlobName Data []byte diff --git a/pkg/datastore/testutils/testutils.go b/pkg/datastore/testutils/testutils.go index e78a7c4..2145a3d 100644 --- a/pkg/datastore/testutils/testutils.go +++ b/pkg/datastore/testutils/testutils.go @@ -42,8 +42,11 @@ type helperReader struct { onEOF func() error } -func BReader(b []byte, onRead func() error, onEOF func() error) *helperReader { - +func BReader( + b []byte, + onRead func() error, + onEOF func() error, +) io.Reader { nop := func() error { return nil } diff --git a/pkg/datastore/webconnector.go b/pkg/datastore/webconnector.go index 3017e54..b2b069e 100644 --- a/pkg/datastore/webconnector.go +++ b/pkg/datastore/webconnector.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -38,25 +38,25 @@ var ( ) type webConnector struct { - baseURL string client *http.Client customizeRequest func(*http.Request) error + baseURL string } var _ DS = (*webConnector)(nil) -type webConnectorOption func(*webConnector) +type WebConnectorOption func(*webConnector) -func WebOptionHttpClient(client *http.Client) webConnectorOption { +func WebOptionHTTPClient(client *http.Client) WebConnectorOption { return func(wc *webConnector) { wc.client = client } } -func WebOptionCustomizeRequest(f func(*http.Request) error) webConnectorOption { +func WebOptionCustomizeRequest(f func(*http.Request) error) WebConnectorOption { return func(wc *webConnector) { wc.customizeRequest = f } } // FromWeb returns Datastore implementation that connects to external url -func FromWeb(baseURL string, options ...webConnectorOption) (DS, error) { +func FromWeb(baseURL string, options ...WebConnectorOption) (DS, error) { _, err := url.Parse(baseURL) if err != nil { return nil, err @@ -95,12 +95,7 @@ func (w *webConnector) Open(ctx context.Context, name *common.BlobName) (io.Read } func (w *webConnector) openStatic(ctx context.Context, name *common.BlobName) (io.ReadCloser, error) { - req, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - w.baseURL+name.String(), - nil, - ) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, w.baseURL+name.String(), http.NoBody) if err != nil { return nil, err } @@ -126,12 +121,7 @@ func (w *webConnector) openStatic(ctx context.Context, name *common.BlobName) (i } func (w *webConnector) openDynamicLink(ctx context.Context, name *common.BlobName) (io.ReadCloser, error) { - req, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - w.baseURL+name.String(), - nil, - ) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, w.baseURL+name.String(), http.NoBody) if err != nil { return nil, err } @@ -179,12 +169,7 @@ func (w *webConnector) Update(ctx context.Context, name *common.BlobName, r io.R } func (w *webConnector) Exists(ctx context.Context, name *common.BlobName) (bool, error) { - req, err := http.NewRequestWithContext( - ctx, - http.MethodHead, - w.baseURL+name.String(), - nil, - ) + req, err := http.NewRequestWithContext(ctx, http.MethodHead, w.baseURL+name.String(), http.NoBody) if err != nil { return false, err } @@ -206,12 +191,7 @@ func (w *webConnector) Exists(ctx context.Context, name *common.BlobName) (bool, } func (w *webConnector) Delete(ctx context.Context, name *common.BlobName) error { - req, err := http.NewRequestWithContext( - ctx, - http.MethodDelete, - w.baseURL+name.String(), - nil, - ) + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, w.baseURL+name.String(), http.NoBody) if err != nil { return err } diff --git a/pkg/datastore/webconnector_test.go b/pkg/datastore/webconnector_test.go index 95efb3a..22b744a 100644 --- a/pkg/datastore/webconnector_test.go +++ b/pkg/datastore/webconnector_test.go @@ -54,7 +54,6 @@ func TestWebConnectorInvalidUrl(t *testing.T) { } func TestWebConnectorInvalidContext(t *testing.T) { - var nilCtx context.Context c, err := FromWeb("http://datastore.local") @@ -104,7 +103,6 @@ func TestWebConnectorServerSideError(t *testing.T) { } func TestWebConnectorDetectInvalidBlobRead(t *testing.T) { - // Test web interface and web connector server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, I should not be here!")) @@ -157,7 +155,7 @@ func TestWebConnectorInvalidErrorCode(t *testing.T) { func TestWebConnectorOptions(t *testing.T) { t.Run("http client", func(t *testing.T) { cl := &http.Client{} - ds, err := FromWeb("http://test.local/", WebOptionHttpClient(cl)) + ds, err := FromWeb("http://test.local/", WebOptionHTTPClient(cl)) require.NoError(t, err) require.Equal(t, cl, ds.(*webConnector).client) }) @@ -169,5 +167,4 @@ func TestWebConnectorOptions(t *testing.T) { require.NoError(t, err) require.Equal(t, testErr, ds.(*webConnector).customizeRequest(nil)) }) - } diff --git a/pkg/datastore/webinterface.go b/pkg/datastore/webinterface.go index d9d5e49..124a8cd 100644 --- a/pkg/datastore/webinterface.go +++ b/pkg/datastore/webinterface.go @@ -37,15 +37,15 @@ type webInterface struct { log *slog.Logger } -type webInterfaceOption func(i *webInterface) +type WebInterfaceOption func(i *webInterface) -func WebInterfaceOptionLogger(log *slog.Logger) webInterfaceOption { +func WebInterfaceOptionLogger(log *slog.Logger) WebInterfaceOption { return func(i *webInterface) { i.log = log } } // WebInterface returns http handler representing web interface to given // Datastore instance -func WebInterface(ds DS, opts ...webInterfaceOption) http.Handler { +func WebInterface(ds DS, opts ...WebInterfaceOption) http.Handler { ret := &webInterface{ ds: ds, } @@ -76,7 +76,7 @@ func (i *webInterface) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } -func (i *webInterface) getName(w http.ResponseWriter, r *http.Request) (*common.BlobName, error) { +func (i *webInterface) getName(_ http.ResponseWriter, r *http.Request) (*common.BlobName, error) { // Don't allow url queries and require path to start with '/' if r.URL.Path[0] != '/' || r.URL.RawQuery != "" { return nil, common.ErrInvalidBlobName @@ -90,17 +90,22 @@ func (i *webInterface) getName(w http.ResponseWriter, r *http.Request) (*common. return bn, nil } -func (i *webInterface) sendName(name *common.BlobName, w http.ResponseWriter, r *http.Request) { +func (i *webInterface) sendName(name *common.BlobName, w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-type", "application/json") - json.NewEncoder(w).Encode(&webNameResponse{ + _ = json.NewEncoder(w).Encode(&webNameResponse{ Name: name.String(), }) } -func (i *webInterface) sendError(w http.ResponseWriter, httpCode int, code string, message string) { +func (i *webInterface) sendError( + w http.ResponseWriter, + httpCode int, + code string, + message string, +) { w.Header().Set("Content-type", "application/json") w.WriteHeader(httpCode) - json.NewEncoder(w).Encode(&webErrResponse{ + _ = json.NewEncoder(w).Encode(&webErrResponse{ Code: code, Message: message, }) @@ -146,9 +151,14 @@ func (i *webInterface) serveGet(w http.ResponseWriter, r *http.Request) { } defer rc.Close() - io.Copy(w, rc) - // TODO: Log error / drop the connection ? It may be too late to send the error to the user - // thus we have to assume that the blob will be validated on the other side + + _, err = io.Copy(w, rc) + if err != nil { + i.log.Error("Error while copying blob data to response", "err", err) + // TODO: Drop the connection or other error passing mechanism? + // It may be too late to send the error to the user thus we have to assume + // that the blob will be validated on the other side + } } type partReader struct { @@ -170,7 +180,6 @@ func (r *partReader) Close() error { } func (i *webInterface) getUploadReader(r *http.Request) (io.ReadCloser, error) { - mpr, err := r.MultipartReader() if err == http.ErrNotMultipart { // Not multipart, read raw body data @@ -222,7 +231,6 @@ func (i *webInterface) servePut(w http.ResponseWriter, r *http.Request) { } func (i *webInterface) serveDelete(w http.ResponseWriter, r *http.Request) { - name, err := i.getName(w, r) if !i.checkErr(err, w, r) { return diff --git a/pkg/datastore/webinterface_test.go b/pkg/datastore/webinterface_test.go index f6b12d3..f1105d0 100644 --- a/pkg/datastore/webinterface_test.go +++ b/pkg/datastore/webinterface_test.go @@ -44,12 +44,25 @@ func testServer(t *testing.T) string { return server.URL + "/" } -func testHTTPResponse(t *testing.T, method string, path string, data io.Reader, code int) { +func testHTTPResponse( + t *testing.T, + method string, + path string, + data io.Reader, + code int, +) { url := testServer(t) testHTTPResponseOwnServer(t, method, url+path, data, code) } -func testHTTPResponseOwnServerContentType(t *testing.T, method string, url string, data io.Reader, contentType string, code int) { +func testHTTPResponseOwnServerContentType( + t *testing.T, + method string, + url string, + data io.Reader, + contentType string, + code int, +) { req, err := http.NewRequest(method, url, data) require.NoError(t, err) @@ -62,37 +75,116 @@ func testHTTPResponseOwnServerContentType(t *testing.T, method string, url strin require.Equal(t, code, resp.StatusCode) } -func testHTTPResponseOwnServer(t *testing.T, method string, url string, data io.Reader, code int) { - testHTTPResponseOwnServerContentType(t, method, url, data, "application/octet-stream", code) +func testHTTPResponseOwnServer( + t *testing.T, + method string, + url string, + data io.Reader, + code int, +) { + testHTTPResponseOwnServerContentType( + t, + method, + url, + data, + "application/octet-stream", + code, + ) } func TestWebInterfaceInvalidMethod(t *testing.T) { - testHTTPResponse(t, http.MethodOptions, "", nil, http.StatusMethodNotAllowed) + testHTTPResponse( + t, + http.MethodOptions, + "", + nil, + http.StatusMethodNotAllowed, + ) } func TestWebInterfaceGetQueryString(t *testing.T) { url := testServer(t) - testHTTPResponseOwnServer(t, http.MethodPut, url+testutils.EmptyBlobNameStatic.String(), bytes.NewBuffer(nil), http.StatusOK) - testHTTPResponseOwnServer(t, http.MethodGet, url+testutils.EmptyBlobNameStatic.String()+"?param=value", nil, http.StatusBadRequest) + testHTTPResponseOwnServer( + t, + http.MethodPut, + url+testutils.EmptyBlobNameStatic.String(), + bytes.NewBuffer(nil), + http.StatusOK, + ) + testHTTPResponseOwnServer( + t, + http.MethodGet, + url+testutils.EmptyBlobNameStatic.String()+"?param=value", + nil, + http.StatusBadRequest, + ) } func TestWebInterfacePutQueryString(t *testing.T) { - testHTTPResponse(t, http.MethodPut, testutils.EmptyBlobNameStatic.String()+"?param=value", bytes.NewBuffer(nil), http.StatusBadRequest) - testHTTPResponse(t, http.MethodPut, testutils.EmptyBlobNameStatic.String(), bytes.NewBuffer(nil), http.StatusOK) + testHTTPResponse( + t, + http.MethodPut, + testutils.EmptyBlobNameStatic.String()+"?param=value", + bytes.NewBuffer(nil), + http.StatusBadRequest, + ) + testHTTPResponse( + t, + http.MethodPut, + testutils.EmptyBlobNameStatic.String(), + bytes.NewBuffer(nil), + http.StatusOK, + ) } func TestWebInterfaceHeadQueryString(t *testing.T) { url := testServer(t) - testHTTPResponseOwnServer(t, http.MethodPut, url+testutils.EmptyBlobNameStatic.String(), bytes.NewBuffer(nil), http.StatusOK) - testHTTPResponseOwnServer(t, http.MethodHead, url+testutils.EmptyBlobNameStatic.String()+"?param=value", nil, http.StatusBadRequest) - testHTTPResponseOwnServer(t, http.MethodHead, url+testutils.EmptyBlobNameStatic.String(), nil, http.StatusOK) + testHTTPResponseOwnServer( + t, + http.MethodPut, + url+testutils.EmptyBlobNameStatic.String(), + bytes.NewBuffer(nil), + http.StatusOK, + ) + testHTTPResponseOwnServer( + t, + http.MethodHead, + url+testutils.EmptyBlobNameStatic.String()+"?param=value", + nil, + http.StatusBadRequest, + ) + testHTTPResponseOwnServer( + t, + http.MethodHead, + url+testutils.EmptyBlobNameStatic.String(), + nil, + http.StatusOK, + ) } func TestWebInterfaceDeleteQueryString(t *testing.T) { url := testServer(t) - testHTTPResponseOwnServer(t, http.MethodPut, url+testutils.EmptyBlobNameStatic.String(), bytes.NewBuffer(nil), http.StatusOK) - testHTTPResponseOwnServer(t, http.MethodDelete, url+testutils.EmptyBlobNameStatic.String()+"?param=value", nil, http.StatusBadRequest) - testHTTPResponseOwnServer(t, http.MethodDelete, url+testutils.EmptyBlobNameStatic.String(), nil, http.StatusOK) + testHTTPResponseOwnServer( + t, + http.MethodPut, + url+testutils.EmptyBlobNameStatic.String(), + bytes.NewBuffer(nil), + http.StatusOK, + ) + testHTTPResponseOwnServer( + t, + http.MethodDelete, + url+testutils.EmptyBlobNameStatic.String()+"?param=value", + nil, + http.StatusBadRequest, + ) + testHTTPResponseOwnServer( + t, + http.MethodDelete, + url+testutils.EmptyBlobNameStatic.String(), + nil, + http.StatusOK, + ) } func TestWebIntefaceExistsFailure(t *testing.T) { @@ -103,7 +195,13 @@ func TestWebIntefaceExistsFailure(t *testing.T) { })) defer server.Close() - testHTTPResponseOwnServer(t, http.MethodHead, server.URL+"/"+testutils.EmptyBlobNameStatic.String(), nil, http.StatusInternalServerError) + testHTTPResponseOwnServer( + t, + http.MethodHead, + server.URL+"/"+testutils.EmptyBlobNameStatic.String(), + nil, + http.StatusInternalServerError, + ) } func TestWebInterfaceMultipartSave(t *testing.T) { @@ -116,7 +214,14 @@ func TestWebInterfaceMultipartSave(t *testing.T) { err = writer.Close() require.NoError(t, err) - testHTTPResponseOwnServerContentType(t, http.MethodPut, url+testutils.EmptyBlobNameStatic.String(), body, writer.FormDataContentType(), http.StatusOK) + testHTTPResponseOwnServerContentType( + t, + http.MethodPut, + url+testutils.EmptyBlobNameStatic.String(), + body, + writer.FormDataContentType(), + http.StatusOK, + ) } func TestWebInterfaceMultipartNoDataSave(t *testing.T) { @@ -131,5 +236,12 @@ func TestWebInterfaceMultipartNoDataSave(t *testing.T) { err = writer.Close() require.NoError(t, err) - testHTTPResponseOwnServerContentType(t, http.MethodPut, url+testutils.EmptyBlobNameStatic.String(), body, writer.FormDataContentType(), http.StatusBadRequest) + testHTTPResponseOwnServerContentType( + t, + http.MethodPut, + url+testutils.EmptyBlobNameStatic.String(), + body, + writer.FormDataContentType(), + http.StatusBadRequest, + ) } diff --git a/pkg/internal/blobtypes/dynamiclink/public.go b/pkg/internal/blobtypes/dynamiclink/public.go index ded9c80..69f6108 100644 --- a/pkg/internal/blobtypes/dynamiclink/public.go +++ b/pkg/internal/blobtypes/dynamiclink/public.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -32,18 +32,51 @@ import ( ) var ( - ErrInvalidDynamicLinkData = fmt.Errorf("%w for dynamic link", blobtypes.ErrValidationFailed) - ErrInvalidDynamicLinkDataReservedByte = fmt.Errorf("%w: invalid value of the reserved byte", ErrInvalidDynamicLinkData) - ErrInvalidDynamicLinkDataBlobName = fmt.Errorf("%w: blob name mismatch", ErrInvalidDynamicLinkData) - ErrInvalidDynamicLinkDataSignature = fmt.Errorf("%w: signature mismatch", ErrInvalidDynamicLinkData) - ErrInvalidDynamicLinkDataTruncated = fmt.Errorf("%w: data truncated", ErrInvalidDynamicLinkData) - ErrInvalidDynamicLinkDataBlockSize = fmt.Errorf("%w: block size too large", ErrInvalidDynamicLinkData) - ErrInvalidDynamicLinkInternalReservedByte = fmt.Errorf("%w: invalid value of the internal reserved byte", ErrInvalidDynamicLinkData) - - ErrInvalidDynamicLinkIVMismatch = fmt.Errorf("%w: iv mismatch", ErrInvalidDynamicLinkData) - ErrInvalidDynamicLinkKeyMismatch = fmt.Errorf("%w: key mismatch", ErrInvalidDynamicLinkData) - ErrInvalidDynamicLinkKeyValidationBlock = fmt.Errorf("%w: invalid key validation block", ErrInvalidDynamicLinkData) - ErrInvalidDynamicLinkKeyValidationBlockSignature = fmt.Errorf("%w signature", ErrInvalidDynamicLinkKeyValidationBlock) + ErrInvalidDynamicLinkData = fmt.Errorf( + "%w for dynamic link", + blobtypes.ErrValidationFailed, + ) + ErrInvalidDynamicLinkDataReservedByte = fmt.Errorf( + "%w: invalid value of the reserved byte", + ErrInvalidDynamicLinkData, + ) + ErrInvalidDynamicLinkDataBlobName = fmt.Errorf( + "%w: blob name mismatch", + ErrInvalidDynamicLinkData, + ) + ErrInvalidDynamicLinkDataSignature = fmt.Errorf( + "%w: signature mismatch", + ErrInvalidDynamicLinkData, + ) + ErrInvalidDynamicLinkDataTruncated = fmt.Errorf( + "%w: data truncated", + ErrInvalidDynamicLinkData, + ) + ErrInvalidDynamicLinkDataBlockSize = fmt.Errorf( + "%w: block size too large", + ErrInvalidDynamicLinkData, + ) + ErrInvalidDynamicLinkInternalReservedByte = fmt.Errorf( + "%w: invalid value of the internal reserved byte", + ErrInvalidDynamicLinkData, + ) + + ErrInvalidDynamicLinkIVMismatch = fmt.Errorf( + "%w: iv mismatch", + ErrInvalidDynamicLinkData, + ) + ErrInvalidDynamicLinkKeyMismatch = fmt.Errorf( + "%w: key mismatch", + ErrInvalidDynamicLinkData, + ) + ErrInvalidDynamicLinkKeyValidationBlock = fmt.Errorf( + "%w: invalid key validation block", + ErrInvalidDynamicLinkData, + ) + ErrInvalidDynamicLinkKeyValidationBlockSignature = fmt.Errorf( + "%w signature", + ErrInvalidDynamicLinkKeyValidationBlock, + ) ) const ( @@ -77,11 +110,11 @@ func (d *Public) BlobName() *common.BlobName { // The data can only be read once due to a streaming nature (it read // the data on-the-fly from another reader). type PublicReader struct { + r io.Reader + iv *common.BlobIV + signature []byte Public contentVersion uint64 - signature []byte - iv *common.BlobIV - r io.Reader } // FromPublicData creates an encrypted dynamic link data (public part) from given io.Reader @@ -152,7 +185,6 @@ func FromPublicData(name *common.BlobName, r io.Reader) (*PublicReader, error) { // after the whole data is read elink, err := func() ([]byte, error) { - elink, err := io.ReadAll(r) if err != nil { return nil, err @@ -169,7 +201,6 @@ func FromPublicData(name *common.BlobName, r io.Reader) (*PublicReader, error) { } return elink, nil - }() if err != nil { @@ -191,7 +222,6 @@ func (d *PublicReader) GetEncryptedLinkReader() io.Reader { } func (d *PublicReader) GetPublicDataReader() io.Reader { - // Preamble - static link data w := bytes.NewBuffer(nil) @@ -267,7 +297,7 @@ func (d *PublicReader) validateKeyInLinkData(key *common.BlobKey, r io.Reader) e // That signature is fed into the key generator and builds the key keyGenerator := cipherfactory.NewKeyGenerator(blobtypes.DynamicLink) - keyGenerator.Write(signature) + _, _ = keyGenerator.Write(signature) generatedKey := keyGenerator.Generate() if !generatedKey.Equal(key) { @@ -278,7 +308,6 @@ func (d *PublicReader) validateKeyInLinkData(key *common.BlobKey, r io.Reader) e } func (d *PublicReader) GetLinkDataReader(key *common.BlobKey) (io.Reader, error) { - r, err := cipherfactory.StreamCipherReader(key, d.iv, d.GetEncryptedLinkReader()) if err != nil { return nil, err diff --git a/pkg/internal/blobtypes/dynamiclink/public_test.go b/pkg/internal/blobtypes/dynamiclink/public_test.go index 5c030eb..79d568a 100644 --- a/pkg/internal/blobtypes/dynamiclink/public_test.go +++ b/pkg/internal/blobtypes/dynamiclink/public_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -52,7 +52,6 @@ func TestFromPublicData(t *testing.T) { }) t.Run("fail when reading link data", func(t *testing.T) { - dl, err := Create(rand.Reader) require.NoError(t, err) @@ -148,9 +147,9 @@ func TestFromPublicData(t *testing.T) { require.NoError(t, err) for _, d := range []struct { - name string dl1 *Publisher dl2 *Publisher + name string }{ { // changing key pair must generate different blog name. @@ -170,7 +169,6 @@ func TestFromPublicData(t *testing.T) { }, } { t.Run(d.name, func(t *testing.T) { - pr1, key, err := d.dl1.UpdateLinkData(bytes.NewReader(nil), 0) require.NoError(t, err) require.NotEmpty(t, key) @@ -197,13 +195,11 @@ func TestFromPublicData(t *testing.T) { require.NotNil(t, dl4) }) } - }) }) } func TestGreaterThan(t *testing.T) { - const testSize = 100 const versions = 10 const reSortingCount = 10 diff --git a/pkg/internal/blobtypes/dynamiclink/publisher.go b/pkg/internal/blobtypes/dynamiclink/publisher.go index b296d7a..ee0e44c 100644 --- a/pkg/internal/blobtypes/dynamiclink/publisher.go +++ b/pkg/internal/blobtypes/dynamiclink/publisher.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -33,8 +33,8 @@ var ( ) type Publisher struct { - Public privKey ed25519.PrivateKey + Public } func nonceFromRand(randSource io.Reader) (uint64, error) { @@ -108,17 +108,17 @@ func (dl *Publisher) AuthInfo() *common.AuthInfo { return common.AuthInfoFromBytes(ret[:]) } -func (dl *Publisher) calculateEncryptionKey() (*common.BlobKey, []byte) { +func (dl *Publisher) calculateEncryptionKey() (key *common.BlobKey, signature []byte) { dataSeed := append( []byte{signatureForEncryptionKeyGeneration}, dl.BlobName().Bytes()..., ) - signature := ed25519.Sign(dl.privKey, dataSeed) + signature = ed25519.Sign(dl.privKey, dataSeed) keyGenerator := cipherfactory.NewKeyGenerator(blobtypes.DynamicLink) - keyGenerator.Write(signature) - key := keyGenerator.Generate() + _, _ = keyGenerator.Write(signature) + key = keyGenerator.Generate() // Key validation block - it is used to ensure the key was generated in a correct way return key, signature @@ -151,7 +151,7 @@ func (dl *Publisher) UpdateLinkData(r io.Reader, version uint64) (*PublicReader, pr.contentVersion = version ivGenerator := pr.ivGeneratorPrefilled() - ivGenerator.Write(unencryptedLink) + _, _ = ivGenerator.Write(unencryptedLink) pr.iv = ivGenerator.Generate() encryptedLinkBuff := bytes.NewBuffer(nil) diff --git a/pkg/internal/blobtypes/dynamiclink/publisher_test.go b/pkg/internal/blobtypes/dynamiclink/publisher_test.go index 2e6b8f6..4d8842e 100644 --- a/pkg/internal/blobtypes/dynamiclink/publisher_test.go +++ b/pkg/internal/blobtypes/dynamiclink/publisher_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -29,7 +29,6 @@ import ( ) func TestCreate(t *testing.T) { - t.Run("successful creation", func(t *testing.T) { dl, err := Create(rand.Reader) require.NoError(t, err) @@ -41,7 +40,6 @@ func TestCreate(t *testing.T) { }) t.Run("error from rand source", func(t *testing.T) { - for goodBytes := 0; goodBytes < ed25519.SeedSize+8; goodBytes++ { injectedErr := errors.New("test") r := io.MultiReader( diff --git a/pkg/internal/blobtypes/dynamiclink/vectors_test.go b/pkg/internal/blobtypes/dynamiclink/vectors_test.go index 8a41646..e63e987 100644 --- a/pkg/internal/blobtypes/dynamiclink/vectors_test.go +++ b/pkg/internal/blobtypes/dynamiclink/vectors_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -42,6 +42,7 @@ func TestVectors(t *testing.T) { testCase := struct { Name string `json:"name"` Description string `json:"description"` + GoErrorContains string `json:"go_error_contains"` Details []string `json:"details"` BlobName []byte `json:"blob_name"` EncryptionKey []byte `json:"encryption_key"` @@ -49,7 +50,6 @@ func TestVectors(t *testing.T) { DecryptedDataset []byte `json:"decrypted_dataset"` ValidPublicly bool `json:"valid_publicly"` ValidPrivately bool `json:"valid_privately"` - GoErrorContains string `json:"go_error_contains"` }{} data, err := os.ReadFile(path) diff --git a/pkg/internal/utilities/cipherfactory/cipher_factory.go b/pkg/internal/utilities/cipherfactory/cipher_factory.go index 6d8f3c0..189d83c 100644 --- a/pkg/internal/utilities/cipherfactory/cipher_factory.go +++ b/pkg/internal/utilities/cipherfactory/cipher_factory.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -31,9 +31,20 @@ var ( // key can not be interpreted ErrInvalidEncryptionConfig = errors.New("invalid encryption config") - ErrInvalidEncryptionConfigKeyType = fmt.Errorf("%w: wrong key type", ErrInvalidEncryptionConfig) - ErrInvalidEncryptionConfigKeySize = fmt.Errorf("%w: wrong XChaCha20 key size, expected %d bytes", ErrInvalidEncryptionConfig, chacha20.KeySize+1) - ErrInvalidEncryptionConfigIVSize = fmt.Errorf("%w: wrong XChaCha20 iv size, expected %d bytes", ErrInvalidEncryptionConfig, chacha20.NonceSizeX) + ErrInvalidEncryptionConfigKeyType = fmt.Errorf( + "%w: wrong key type", + ErrInvalidEncryptionConfig, + ) + ErrInvalidEncryptionConfigKeySize = fmt.Errorf( + "%w: wrong XChaCha20 key size, expected %d bytes", + ErrInvalidEncryptionConfig, + chacha20.KeySize+1, + ) + ErrInvalidEncryptionConfigIVSize = fmt.Errorf( + "%w: wrong XChaCha20 iv size, expected %d bytes", + ErrInvalidEncryptionConfig, + chacha20.NonceSizeX, + ) ) const ( diff --git a/pkg/internal/utilities/cipherfactory/cipher_factory_test.go b/pkg/internal/utilities/cipherfactory/cipher_factory_test.go index 2a19954..f3d2ce0 100644 --- a/pkg/internal/utilities/cipherfactory/cipher_factory_test.go +++ b/pkg/internal/utilities/cipherfactory/cipher_factory_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,36 +27,35 @@ import ( ) func TestCipherForKeyIV(t *testing.T) { - for _, d := range []struct { + err error desc string key []byte iv []byte - err error }{ { - "Empty key", - nil, - nil, - ErrInvalidEncryptionConfigKeyType, + desc: "Empty key", + key: nil, + iv: nil, + err: ErrInvalidEncryptionConfigKeyType, }, { - "Invalid Chacha20 key size", - make([]byte, chacha20.KeySize), - make([]byte, chacha20.NonceSizeX), - ErrInvalidEncryptionConfigKeySize, + desc: "Invalid Chacha20 key size", + key: make([]byte, chacha20.KeySize), + iv: make([]byte, chacha20.NonceSizeX), + err: ErrInvalidEncryptionConfigKeySize, }, { - "Invalid Chacha20 nonce size", - make([]byte, chacha20.KeySize+1), - make([]byte, chacha20.NonceSizeX-1), - ErrInvalidEncryptionConfigIVSize, + desc: "Invalid Chacha20 nonce size", + key: make([]byte, chacha20.KeySize+1), + iv: make([]byte, chacha20.NonceSizeX-1), + err: ErrInvalidEncryptionConfigIVSize, }, { - "Valid chacha20 key", - make([]byte, chacha20.KeySize+1), - make([]byte, chacha20.NonceSizeX), - nil, + desc: "Valid chacha20 key", + key: make([]byte, chacha20.KeySize+1), + iv: make([]byte, chacha20.NonceSizeX), + err: nil, }, } { t.Run(d.desc, func(t *testing.T) { diff --git a/pkg/internal/utilities/headwriter/headwriter.go b/pkg/internal/utilities/headwriter/headwriter.go index 4dfa6be..e0ff0c1 100644 --- a/pkg/internal/utilities/headwriter/headwriter.go +++ b/pkg/internal/utilities/headwriter/headwriter.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,14 +17,14 @@ limitations under the License. package headwriter type Writer struct { - limit int data []byte + limit int } func New(limit int) Writer { return Writer{ - limit: limit, data: make([]byte, 0, limit), + limit: limit, } } diff --git a/pkg/internal/utilities/securefifo/secure_fifo.go b/pkg/internal/utilities/securefifo/secure_fifo.go index 6aab732..bcca5e8 100644 --- a/pkg/internal/utilities/securefifo/secure_fifo.go +++ b/pkg/internal/utilities/securefifo/secure_fifo.go @@ -1,5 +1,5 @@ /* -Copyright © 2022 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -128,7 +128,6 @@ func (r *reader) Reset() (Reader, error) { // New creates new secure fifo pipe. That pipe may handle large amounts of data by using a temporary storage // but ensures that even if the data can be accessed from disk, it can not be decrypted. func New() (wr Writer, err error) { - var randData [chacha20.KeySize + chacha20.NonceSize]byte _, err = rand.Read(randData[:]) if err != nil { diff --git a/pkg/internal/utilities/securefifo/secure_fifo_test.go b/pkg/internal/utilities/securefifo/secure_fifo_test.go index 7885703..4e4ae50 100644 --- a/pkg/internal/utilities/securefifo/secure_fifo_test.go +++ b/pkg/internal/utilities/securefifo/secure_fifo_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2022 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ limitations under the License. package securefifo import ( + "fmt" "io" "os" "strings" @@ -26,7 +27,7 @@ import ( ) func TestSecureFifoReadBack(t *testing.T) { - for _, d := range []struct { + for i, d := range []struct { data []byte }{ {data: []byte{}}, @@ -36,45 +37,47 @@ func TestSecureFifoReadBack(t *testing.T) { {data: []byte(strings.Repeat("a", 17))}, {data: []byte(strings.Repeat("a", 16*1024))}, } { - w, err := New() - require.NoError(t, err) - defer w.Close() + t.Run(fmt.Sprint(i), func(t *testing.T) { + w, err := New() + require.NoError(t, err) + defer w.Close() - n, err := w.Write(d.data) - require.NoError(t, err) - require.EqualValues(t, len(d.data), n) + n, err := w.Write(d.data) + require.NoError(t, err) + require.EqualValues(t, len(d.data), n) - r, err := w.Done() - require.NoError(t, err) - defer r.Close() + r, err := w.Done() + require.NoError(t, err) + defer r.Close() - err = w.Close() - require.NoError(t, err) + err = w.Close() + require.NoError(t, err) - // Close must be idempotent - err = w.Close() - require.NoError(t, err) + // Close must be idempotent + err = w.Close() + require.NoError(t, err) - readBack, err := io.ReadAll(r) - require.NoError(t, err) - require.Equal(t, readBack, d.data) + readBack, err := io.ReadAll(r) + require.NoError(t, err) + require.Equal(t, readBack, d.data) - r2, err := r.Reset() - require.NoError(t, err) + r2, err := r.Reset() + require.NoError(t, err) - err = r.Close() - require.NoError(t, err) + err = r.Close() + require.NoError(t, err) - // Close must be idempotent - err = r.Close() - require.NoError(t, err) + // Close must be idempotent + err = r.Close() + require.NoError(t, err) - readBack, err = io.ReadAll(r2) - require.NoError(t, err) - require.Equal(t, readBack, d.data) + readBack, err = io.ReadAll(r2) + require.NoError(t, err) + require.Equal(t, readBack, d.data) - err = r2.Close() - require.NoError(t, err) + err = r2.Close() + require.NoError(t, err) + }) } } diff --git a/pkg/internal/utilities/validatingreader/hashvalidatingreader.go b/pkg/internal/utilities/validatingreader/hashvalidatingreader.go index 8f557c4..5597669 100644 --- a/pkg/internal/utilities/validatingreader/hashvalidatingreader.go +++ b/pkg/internal/utilities/validatingreader/hashvalidatingreader.go @@ -1,5 +1,5 @@ /* -Copyright © 2022 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,8 +25,8 @@ import ( type hashValidatingReader struct { r io.Reader hasher hash.Hash - expectedHash []byte err error + expectedHash []byte } func (h hashValidatingReader) Read(b []byte) (int, error) { diff --git a/pkg/internal/utilities/validatingreader/hashvalidatingreader_test.go b/pkg/internal/utilities/validatingreader/hashvalidatingreader_test.go index e289b3a..4f65f5d 100644 --- a/pkg/internal/utilities/validatingreader/hashvalidatingreader_test.go +++ b/pkg/internal/utilities/validatingreader/hashvalidatingreader_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2022 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import ( ) func TestHashValidatingReader(t *testing.T) { - for _, data := range [][]byte{ {}, {0x00}, diff --git a/pkg/internal/utilities/validatingreader/oneofcheckreader.go b/pkg/internal/utilities/validatingreader/oneofcheckreader.go index 7709339..4fc9542 100644 --- a/pkg/internal/utilities/validatingreader/oneofcheckreader.go +++ b/pkg/internal/utilities/validatingreader/oneofcheckreader.go @@ -1,5 +1,5 @@ /* -Copyright © 2022 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,12 +18,12 @@ package validatingreader import "io" -type onEofCheckReader struct { +type onEOFCheckReader struct { r io.Reader onCloseCheck func() error } -func (h onEofCheckReader) Read(b []byte) (int, error) { +func (h onEOFCheckReader) Read(b []byte) (int, error) { n, err := h.r.Read(b) if err == io.EOF { @@ -37,7 +37,7 @@ func (h onEofCheckReader) Read(b []byte) (int, error) { } func CheckOnEOF(r io.Reader, check func() error) io.Reader { - return onEofCheckReader{ + return onEOFCheckReader{ r: r, onCloseCheck: check, } diff --git a/pkg/internal/utilities/validatingreader/oneofcheckreader_test.go b/pkg/internal/utilities/validatingreader/oneofcheckreader_test.go index 82468b7..917aeef 100644 --- a/pkg/internal/utilities/validatingreader/oneofcheckreader_test.go +++ b/pkg/internal/utilities/validatingreader/oneofcheckreader_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2022 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import ( func TestOnCloseCheckReader(t *testing.T) { t.Run("properly read if read function does not raise an error", func(t *testing.T) { - r := validatingreader.CheckOnEOF( bytes.NewBufferString("Hello world"), func() error { return nil }, @@ -50,5 +49,4 @@ func TestOnCloseCheckReader(t *testing.T) { _, err := io.ReadAll(r) require.Equal(t, err, injectedError) }) - } diff --git a/pkg/utilities/httpserver/httpserver.go b/pkg/utilities/httpserver/httpserver.go index f218815..b9081a7 100644 --- a/pkg/utilities/httpserver/httpserver.go +++ b/pkg/utilities/httpserver/httpserver.go @@ -18,19 +18,23 @@ package httpserver import ( "context" + "errors" "log/slog" "net" "net/http" "os" "os/signal" "strconv" + "sync" "syscall" + "time" ) type cfg struct { - handler http.Handler - listenAddr string - log *slog.Logger + log *slog.Logger + handler http.Handler + listenAddr string + gracefulShutdownTimeout time.Duration } type Option func(c *cfg) @@ -40,33 +44,32 @@ func ListenAddr(listenAddr string) Option { return func(c *cfg) { c.listenAddr = func Logger(log *slog.Logger) Option { return func(c *cfg) { c.log = log } } func RunGracefully(ctx context.Context, handler http.Handler, opt ...Option) error { - cfg := cfg{ - handler: handler, - listenAddr: ":http", - log: slog.Default(), + c := cfg{ + handler: handler, + listenAddr: ":http", + log: slog.Default(), + gracefulShutdownTimeout: 5 * time.Second, } for _, o := range opt { - o(&cfg) + o(&c) } - server, _, err := startGracefully(ctx, cfg) + c.log.Info("Starting http server", "listenAddr", c.listenAddr) + + listener, err := net.Listen("tcp", c.listenAddr) if err != nil { return err } - return endGracefully(ctx, server, cfg) -} + ctx, signalCtxCancel := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) + defer signalCtxCancel() -func startGracefully(ctx context.Context, cfg cfg) (*http.Server, net.Listener, error) { - cfg.log.Info("Starting http server", "listenAddr", cfg.listenAddr) - listener, err := net.Listen("tcp", cfg.listenAddr) - if err != nil { - return nil, nil, err - } + return runUntilContextNotDone(ctx, c, listener) +} +func runUntilContextNotDone(ctx context.Context, cfg cfg, listener net.Listener) error { server := &http.Server{ - Addr: cfg.listenAddr, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cfg.log.Info( "http request", @@ -78,25 +81,39 @@ func startGracefully(ctx context.Context, cfg cfg) (*http.Server, net.Listener, ) cfg.handler.ServeHTTP(w, r) }), + ReadHeaderTimeout: 5 * time.Second, // Prevent Slowloris attacks } - go server.Serve(listener) + wg := sync.WaitGroup{} + wg.Go(func() { + <-ctx.Done() - return server, listener, nil -} + cfg.log.Info("Shutting down") -func endGracefully(ctx context.Context, server *http.Server, cfg cfg) error { - ctx, _ = signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) + shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.gracefulShutdownTimeout) + defer cancel() - <-ctx.Done() - cfg.log.Info("Shutting down") + // TODO: More graceful way? + if err := server.Shutdown(shutdownCtx); err != nil { + cfg.log.Error("Failed to shutdown gracefully") + server.Close() + } else { + cfg.log.Info("Shutdown complete") + } + }) + defer wg.Wait() + + err := server.Serve(listener) + if errors.Is(err, http.ErrServerClosed) { + err = nil + } - // TODO: More graceful way? - return server.Close() + return err } func FailResponseOnError(w http.ResponseWriter, err error) { if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) + return } } diff --git a/pkg/utilities/httpserver/httpserver_test.go b/pkg/utilities/httpserver/httpserver_test.go index c48af43..39a4489 100644 --- a/pkg/utilities/httpserver/httpserver_test.go +++ b/pkg/utilities/httpserver/httpserver_test.go @@ -26,6 +26,7 @@ import ( "net" "net/http" "net/http/httptest" + "sync" "testing" "time" @@ -67,18 +68,29 @@ func TestCancelWithSignal(t *testing.T) { func TestEnsureHandlerIsCalled(t *testing.T) { handlerCalled := false - server, listener, err := startGracefully( - t.Context(), - cfg{ - handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - handlerCalled = true - }), - listenAddr: ":0", - log: slog.Default(), - }, - ) + ctx, shutdown := context.WithCancel(t.Context()) + defer shutdown() + + listener, err := net.Listen("tcp", ":0") require.NoError(t, err) + wg := sync.WaitGroup{} + defer wg.Wait() + + wg.Go(func() { + err := runUntilContextNotDone(ctx, + cfg{ + log: slog.Default(), + handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handlerCalled = true + }), + gracefulShutdownTimeout: 5 * time.Second, + }, + listener, + ) + require.NoError(t, err) + }) + resp, err := http.Get( fmt.Sprintf("http://localhost:%d/", listener.Addr().(*net.TCPAddr).Port), ) @@ -88,10 +100,65 @@ func TestEnsureHandlerIsCalled(t *testing.T) { _, err = io.ReadAll(resp.Body) require.NoError(t, err) - err = server.Close() + require.True(t, handlerCalled) + + shutdown() +} + +func TestCloseOnShutdownTimeout(t *testing.T) { + ctx, shutdown := context.WithCancel(t.Context()) + defer shutdown() + + listener, err := net.Listen("tcp", ":0") require.NoError(t, err) - require.True(t, handlerCalled) + wg := sync.WaitGroup{} + defer wg.Wait() + + handlerHang := make(chan struct{}) + requestStarted := make(chan struct{}) + serverClosed := make(chan struct{}) + + wg.Go(func() { + defer close(serverClosed) + err := runUntilContextNotDone(ctx, + cfg{ + handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Send some larger buffer to initiate connection, send all headers etc. + w.Write(bytes.Repeat([]byte("A"), 1024*1024)) + <-handlerHang + }), + log: slog.Default(), + // Very short graceful timeout to quickly switch to forcible shutdown + gracefulShutdownTimeout: time.Millisecond, + }, + listener, + ) + require.NoError(t, err) + }) + + wg.Go(func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/", listener.Addr().(*net.TCPAddr).Port), + ) + require.NoError(t, err) + defer resp.Body.Close() + + // Synchronization point to ensure the request is in progress + close(requestStarted) + + io.ReadAll(resp.Body) + }) + + // Wait for the request to be in progress + <-requestStarted + + // Initiate shutdown + shutdown() + <-serverClosed + + // Let the handler finish + close(handlerHang) } func TestFailOnInvalidListenAddr(t *testing.T) { @@ -105,20 +172,20 @@ func TestFailOnInvalidListenAddr(t *testing.T) { func TestOptions(t *testing.T) { t.Run("ListenPort", func(t *testing.T) { - cfg := cfg{} - ListenPort(54321)(&cfg) - require.Equal(t, ":54321", cfg.listenAddr) + config := cfg{} + ListenPort(54321)(&config) + require.Equal(t, ":54321", config.listenAddr) }) t.Run("ListenAddr", func(t *testing.T) { - cfg := cfg{} - ListenAddr(":12345")(&cfg) - require.Equal(t, ":12345", cfg.listenAddr) + config := cfg{} + ListenAddr(":12345")(&config) + require.Equal(t, ":12345", config.listenAddr) }) t.Run("Logger", func(t *testing.T) { log := slog.New(slog.NewJSONHandler(bytes.NewBuffer(nil), nil)) - cfg := cfg{} - Logger(log)(&cfg) - require.Equal(t, log, cfg.log) + config := cfg{} + Logger(log)(&config) + require.Equal(t, log, config.log) }) } diff --git a/testvectors/generate.go b/testvectors/generate.go index d572b02..88bed68 100644 --- a/testvectors/generate.go +++ b/testvectors/generate.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Bartłomiej Święcki (byo) +Copyright © 2025 Bartłomiej Święcki (byo) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -313,7 +313,7 @@ var ( func writeLinkData(tc TestCase) error { fName := tc.Name + ".json" - err := os.MkdirAll(filepath.Dir(fName), 0777) + err := os.MkdirAll(filepath.Dir(fName), 0o777) if err != nil { return err } @@ -335,7 +335,7 @@ func writeLinkData(tc TestCase) error { return err } - err = os.WriteFile(fName, jsonData, 0666) + err = os.WriteFile(fName, jsonData, 0o666) if err != nil { return err } @@ -380,7 +380,7 @@ func writeTestBlobCode( ) fileName := filepath.Join("testblobs", strings.ToLower(name)+".go") - err := os.WriteFile(fileName, []byte(code), 0666) + err := os.WriteFile(fileName, []byte(code), 0o666) if err != nil { return err }