diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6cb7484..e2f2557 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,11 @@ name: Build on: + pull_request: + types: + - edited + - reopened + - opened + - synchronize push: branches: - main @@ -59,7 +65,7 @@ jobs: path: ./bin/ingest - name: Release uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + if: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' }} with: files: ./bin/ingest - name: Docker Meta @@ -94,7 +100,7 @@ jobs: id: docker_build_and_push uses: docker/build-push-action@v4 with: - push: true + push: ${{ github.event_name != 'pull_request' }} pull: true context: . file: Dockerfile diff --git a/go.mod b/go.mod index 16693ca..4c7e0bd 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/xeptore/wireuse go 1.20 require ( + github.com/fsnotify/fsnotify v1.6.0 github.com/golang/mock v1.6.0 github.com/joho/godotenv v1.5.1 github.com/rs/zerolog v1.29.0 @@ -17,6 +18,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.16.3 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.18 // indirect github.com/mdlayher/genetlink v1.3.1 // indirect @@ -25,6 +27,7 @@ require ( github.com/montanaflynn/stats v0.7.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect @@ -34,6 +37,7 @@ require ( golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/text v0.8.0 // indirect - golang.zx2c4.com/wireguard v0.0.0-20230317141804-1417a47c8fa8 // indirect + golang.zx2c4.com/wireguard v0.0.0-20230324160507-6f895be10d74 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index cbd42df..aed6e1d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,10 @@ github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= @@ -18,11 +21,14 @@ github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2C github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -40,10 +46,13 @@ github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE9 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= @@ -55,8 +64,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= @@ -106,6 +116,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -125,13 +136,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= -golang.zx2c4.com/wireguard v0.0.0-20230317141804-1417a47c8fa8 h1:gFnsQLPjrJsKpKJDlqbXWGJLyeFSdnd+8EVpEc+RawM= -golang.zx2c4.com/wireguard v0.0.0-20230317141804-1417a47c8fa8/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4= +golang.zx2c4.com/wireguard v0.0.0-20230324160507-6f895be10d74 h1:GYLUo7LqqEYBPNDtEYeQExxaoeV63QN4H0d5PySR1rk= +golang.zx2c4.com/wireguard v0.0.0-20230324160507-6f895be10d74/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde h1:ybF7AMzIUikL9x4LgwEmzhXtzRpKNqngme1VGDWz+Nk= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/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= diff --git a/ingest/Makefile b/ingest/Makefile index 395b1f0..8b59fa8 100644 --- a/ingest/Makefile +++ b/ingest/Makefile @@ -1,3 +1,3 @@ gen: - mockgen -source ingest.go -destination mocks/ingest.go -package mocks + go run github.com/golang/mock/mockgen@v1.6.0 -source ingest.go -destination mocks/ingest.go -package mocks .PHONY: gen diff --git a/ingest/cmd/main.go b/ingest/cmd/main.go index 5b3d2c3..217a2ae 100644 --- a/ingest/cmd/main.go +++ b/ingest/cmd/main.go @@ -7,10 +7,12 @@ import ( "fmt" "os" "os/signal" + "path/filepath" "strings" "syscall" "time" + "github.com/fsnotify/fsnotify" "github.com/joho/godotenv" "github.com/rs/zerolog" "go.mongodb.org/mongo-driver/bson" @@ -27,8 +29,8 @@ import ( ) var ( - restartMarkFileName string - wgDeviceName string + watchDir string + wgDeviceName string ) func main() { @@ -49,15 +51,15 @@ func main() { log.Fatal().Msg("TZ environment variable must be set to UTC") } - flag.StringVar(&restartMarkFileName, "r", "", "restart-mark file name") + flag.StringVar(&watchDir, "w", "", "directory path to watch for wireguard device up and dump files") flag.StringVar(&wgDeviceName, "i", "", "wireguard interface") flag.Parse() if nonFlagArgs := flag.Args(); len(nonFlagArgs) > 0 { log.Fatal().Msgf("expected no additional flags, got: %s", strings.Join(nonFlagArgs, ",")) } - if restartMarkFileName == "" { - log.Fatal().Msg("restart-mark file name option is required and cannot be empty") + if watchDir == "" { + log.Fatal().Msg("directory path to watch for wireguard device up and dump files option is required and cannot be empty") } if wgDeviceName == "" { log.Fatal().Msg("wireguard device name option is required and cannot be empty") @@ -76,7 +78,9 @@ func main() { log.Fatal().Err(err).Msg("failed to verify database connectivity") } defer func() { - if err := client.Disconnect(ctx); err != nil { + disconnectCtx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := client.Disconnect(disconnectCtx); err != nil { log.Err(err).Msg("failed to disconnect from database") return } @@ -107,29 +111,72 @@ func main() { signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGINT) - ctx, cancel := context.WithCancelCause(ctx) + runCtx, cancelEngineRun := context.WithCancelCause(ctx) stopSignalErr := errors.New("stop signal received") go func() { <-signals - cancel(stopSignalErr) + cancelEngineRun(stopSignalErr) }() - timeTicker := time.NewTicker(5 * time.Second) - engineTicker := make(chan struct{}) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) go func() { + log = log.With().Str("app", "watcher").Logger() + w, err := fsnotify.NewWatcher() + if nil != err { + log.Fatal().Err(err).Msg("failed to initialize") + } + if err := w.Add(watchDir); nil != err { + log.Fatal().Err(err).Msg("failed to add directory") + } + log.Debug().Str("dir", watchDir).Msg("started watching directory") + for { + select { + case <-runCtx.Done(): + log.Info().Msg("existing loop due to context done") + return + case err, open := <-w.Errors: + if !open { + log.Info().Msg("exiting loop due to closure") + return + } + if nil != err { + log.Error().Err(err).Msg("received error") + return + } + case event, open := <-w.Events: + if !open { + log.Info().Msg("exiting loop due to closure") + return + } + + if !event.Has(fsnotify.Create) && !event.Has(fsnotify.Write) { + log.Debug().Msg("ignoring event with irrelevant op") + } + + if filename := filepath.Base(event.Name); strings.HasSuffix(filename, fmt.Sprintf("%s.up", wgDeviceName)) { + wgUpEvents <- ingest.WgUpEvent{} + } else if strings.HasSuffix(filename, fmt.Sprintf("%s.down", wgDeviceName)) { + wgDownEvents <- ingest.WgDownEvent{ChangedAt: time.Now(), FileName: event.Name} + } + } + } + }() + + ticker := make(chan ingest.None) + go func() { + timeTicker := time.NewTicker(5 * time.Second) for range timeTicker.C { - engineTicker <- struct{}{} + ticker <- ingest.None{} } }() - rmf := restartMarkFileReadRemover{} wp := wgPeers{wg} store := storeMongo{collection} - engine := ingest.NewEngine(&rmf, &wp, &store, log) - if err := engine.Run(ctx, engineTicker, restartMarkFileName); nil != err { - if err := ctx.Err(); nil != err { + engine := ingest.NewEngine(&wp, &store, log.With().Str("app", "engine").Logger()) + if err := engine.Run(runCtx, ticker, wgUpEvents, wgDownEvents); nil != err { + if err := runCtx.Err(); nil != err { if errors.Is(err, context.Canceled) { - if errors.Is(context.Cause(ctx), stopSignalErr) { + if errors.Is(context.Cause(runCtx), stopSignalErr) { log.Info().Msg("root context was canceled due to receiving an interrupt signal") return } @@ -151,12 +198,12 @@ type storeMongo struct { collection *mongo.Collection } -func (m *storeMongo) LoadBeforeRestartUsage(ctx context.Context) (map[string]ingest.PeerUsage, error) { +func (m *storeMongo) LoadUsage(ctx context.Context) (map[string]ingest.PeerUsage, error) { cursor, err := m.collection.Aggregate(ctx, bson.A{ bson.M{"$project": bson.M{"lastUsage": bson.M{"$last": "$usage"}, "_id": 0, "publicKey": 1}}, }) if nil != err { - return nil, fmt.Errorf("failed to query before restart last usage data: %v", err) + return nil, fmt.Errorf("failed to query last usage data: %v", err) } var results []struct { @@ -210,38 +257,11 @@ func (wg *wgPeers) Usage(ctx context.Context) ([]ingest.PeerUsage, time.Time, er out := funcutils.Map(dev.Peers, func(p wgtypes.Peer) ingest.PeerUsage { return ingest.PeerUsage{ - Upload: uint(p.TransmitBytes), - Download: uint(p.ReceiveBytes), + Upload: uint(p.ReceiveBytes), + Download: uint(p.TransmitBytes), PublicKey: p.PublicKey.String(), } }) return out, gatheredAt, nil } - -type restartMarkFileReadRemover struct{} - -func (*restartMarkFileReadRemover) Read(filename string) ([1]byte, error) { - file, err := os.Open(restartMarkFileName) - if nil != err { - return [1]byte{0}, fmt.Errorf("failed to open restart-mark file: %w", err) - } - - buf := make([]byte, 1) - n, err := file.Read(buf) - if nil != err { - return [1]byte{0}, fmt.Errorf("failed to read first byte of restart-mark file: %w", err) - } - if n > 1 { - return [1]byte{0}, fmt.Errorf("expected to read at most 1 byte from file read: %d", n) - } - if n == 0 { - return [1]byte{0}, nil - } - - return [1]byte{buf[0]}, nil -} - -func (*restartMarkFileReadRemover) Remove(filename string) error { - return os.Remove(filename) -} diff --git a/ingest/ingest.go b/ingest/ingest.go index bfda776..b84b1f0 100644 --- a/ingest/ingest.go +++ b/ingest/ingest.go @@ -3,13 +3,23 @@ package ingest import ( "context" "errors" - "fmt" "os" "time" "github.com/rs/zerolog" + "github.com/xeptore/wireuse/pkg/dump" + "github.com/xeptore/wireuse/pkg/funcutils" ) +type None struct{} + +type WgUpEvent struct{} + +type WgDownEvent struct { + ChangedAt time.Time + FileName string +} + type PeerUsage struct { Upload uint Download uint @@ -17,7 +27,7 @@ type PeerUsage struct { } type Store interface { - LoadBeforeRestartUsage(ctx context.Context) (map[string]PeerUsage, error) + LoadUsage(ctx context.Context) (map[string]PeerUsage, error) IngestUsage(ctx context.Context, peersUsage []PeerUsage, gatheredAt time.Time) error } @@ -25,56 +35,83 @@ type WgPeers interface { Usage(ctx context.Context) (peersUsage []PeerUsage, gatheredAt time.Time, err error) } -type RestartMarkFileReadRemover interface { - Read(filename string) ([1]byte, error) - Remove(filename string) error -} - type Engine struct { - restartMarkFile RestartMarkFileReadRemover - wgPeers WgPeers - store Store - logger zerolog.Logger + wgPeers WgPeers + store Store + logger zerolog.Logger } -func NewEngine( - restartMarkFile RestartMarkFileReadRemover, - wgPeers WgPeers, - store Store, - logger zerolog.Logger, -) Engine { +func NewEngine(wgPeers WgPeers, store Store, logger zerolog.Logger) Engine { return Engine{ - restartMarkFile: restartMarkFile, - wgPeers: wgPeers, - store: store, - logger: logger, + wgPeers: wgPeers, + store: store, + logger: logger, } } -func (e *Engine) Run(ctx context.Context, tick <-chan struct{}, restartMarkFileName string) error { +func (e *Engine) Run(ctx context.Context, tick <-chan None, wgUpEvents <-chan WgUpEvent, wgDownEvents <-chan WgDownEvent) error { var previousPeersUsage map[string]PeerUsage - for range tick { + var mustLoadUsage bool +loop: + for { select { case <-ctx.Done(): return ctx.Err() - default: + case event := <-wgDownEvents: + file, err := os.Open(event.FileName) + if nil != err { + e.logger.Error().Err(err).Msg("failed to open wireguard down file for read") + continue loop + } + + peers, err := dump.Parse(file) + if nil != err { + e.logger.Error().Err(err).Msg("failed to parse wireguard down dump file content") + continue loop + } + + peersUsage := funcutils.Map(peers, func(p dump.Peer) PeerUsage { + return PeerUsage{ + Upload: uint(p.ReceiveBytes), + Download: uint(p.TransmitBytes), + PublicKey: p.PublicKey.String(), + } + }) + + if len(peers) > 0 { + if err := e.store.IngestUsage(ctx, peersUsage, event.ChangedAt); nil != err { + e.logger.Error().Err(err).Msg("failed to ingest peers usage data") + continue loop + } + } + case <-wgUpEvents: + var err error + previousPeersUsage, err = e.store.LoadUsage(ctx) + if nil != err { + e.logger.Error().Err(err).Msg("failed to load before restart peers usage data") + mustLoadUsage = true + continue loop + } + mustLoadUsage = false + case <-tick: peersUsage, gatheredAt, err := e.wgPeers.Usage(ctx) if nil != err { - e.logger.Error().Err(err).Msg("failed to get wireguard peers usage data") - continue + if errors.Is(err, os.ErrNotExist) { + e.logger.Warn().Msg("could not find specified wireguard device") + } else { + e.logger.Error().Err(err).Msg("failed to get specified wireguard usage info") + } + continue loop } - mustDeleteRestartMarkFile := false - content, err := e.restartMarkFile.Read(restartMarkFileName) - if nil != err && !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("failed to read restart-mark file: %w", err) - } else if content == [1]byte{1} { - previousPeersUsage, err = e.store.LoadBeforeRestartUsage(ctx) + if mustLoadUsage { + previousPeersUsage, err = e.store.LoadUsage(ctx) if nil != err { e.logger.Error().Err(err).Msg("failed to load before restart peers usage data") - continue + mustLoadUsage = true + continue loop } - mustDeleteRestartMarkFile = true + mustLoadUsage = false } if nil != previousPeersUsage { @@ -89,17 +126,9 @@ func (e *Engine) Run(ctx context.Context, tick <-chan struct{}, restartMarkFileN if len(peersUsage) > 0 { if err := e.store.IngestUsage(ctx, peersUsage, gatheredAt); nil != err { e.logger.Error().Err(err).Msg("failed to ingest peers usage data") - continue - } - } - - if mustDeleteRestartMarkFile { - if err := e.restartMarkFile.Remove(restartMarkFileName); nil != err && !errors.Is(err, os.ErrNotExist) { - e.logger.Error().Err(err).Msg("failed to remove restart-mark file") + continue loop } } } } - - return nil } diff --git a/ingest/ingest_test.go b/ingest/ingest_test.go index f5c4617..3adce7d 100644 --- a/ingest/ingest_test.go +++ b/ingest/ingest_test.go @@ -22,48 +22,39 @@ func TestEngineContextCancellation(t *testing.T) { ctrl, ctx := gomock.WithContext(ctx, t) runCtx, cancelRunCtx := context.WithCancelCause(ctx) - var runErr error gatherTime := time.Now() store := mocks.NewMockStore(ctrl) - store.EXPECT().LoadBeforeRestartUsage(runCtx).Times(0) + store.EXPECT().LoadUsage(runCtx).Times(0) store.EXPECT().IngestUsage(runCtx, []ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1) - readRestartMarkFile := mocks.NewMockRestartMarkFileReadRemover(ctrl) - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(1) - readRestartMarkFile.EXPECT().Remove("TODO").Return(nil).Times(0) - - readWGPeersUsage := mocks.NewMockWgPeers(ctrl) - readWGPeersUsage.EXPECT().Usage(runCtx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1) + wgPeers := mocks.NewMockWgPeers(ctrl) + wgPeers.EXPECT().Usage(runCtx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1) - e := ingest.NewEngine(readRestartMarkFile, readWGPeersUsage, store, zerolog.New(io.Discard)) + e := ingest.NewEngine(wgPeers, store, zerolog.New(io.Discard)) - ticker := make(chan struct{}) + ticker := make(chan ingest.None) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) + var runErr error wait := make(chan struct{}) go func() { defer func() { wait <- struct{}{} }() - runErr = e.Run(runCtx, ticker, "TODO") + runErr = e.Run(runCtx, ticker, wgUpEvents, wgDownEvents) }() select { - case ticker <- struct{}{}: + case ticker <- ingest.None{}: case <-wait: t.Fatal("unexpected engine run termination") } cancelCause := errors.New("some error") cancelRunCtx(cancelCause) - select { - case ticker <- struct{}{}: - case <-wait: - t.Fatal("unexpected engine run termination") - } - close(ticker) <-wait require.NotNil(t, runErr) require.ErrorIs(t, runErr, context.Canceled) @@ -72,13 +63,13 @@ func TestEngineContextCancellation(t *testing.T) { func TestEngineSingleStaticPeer(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx, done := context.WithCancel(context.Background()) ctrl, ctx := gomock.WithContext(ctx, t) gatherTime := time.Now() store := mocks.NewMockStore(ctrl) - store.EXPECT().LoadBeforeRestartUsage(ctx).Times(0) + store.EXPECT().LoadUsage(ctx).Times(0) gomock.InOrder( store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), @@ -92,27 +83,24 @@ func TestEngineSingleStaticPeer(t *testing.T) { store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), ) - readRestartMarkFile := mocks.NewMockRestartMarkFileReadRemover(ctrl) - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(10) - readRestartMarkFile.EXPECT().Remove("TODO").Return(nil).Times(0) - - readWGPeersUsage := mocks.NewMockWgPeers(ctrl) + wgPeers := mocks.NewMockWgPeers(ctrl) gomock.InOrder( - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 30, Download: 90, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 80, Download: 240, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 30, Download: 90, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 80, Download: 240, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime, nil).Times(1), ) - e := ingest.NewEngine(readRestartMarkFile, readWGPeersUsage, store, zerolog.New(io.Discard)) + e := ingest.NewEngine(wgPeers, store, zerolog.New(io.Discard)) - ticker := make(chan struct{}) + ticker := make(chan ingest.None) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) var runErr error wait := make(chan struct{}) @@ -120,63 +108,45 @@ func TestEngineSingleStaticPeer(t *testing.T) { defer func() { wait <- struct{}{} }() - runErr = e.Run(ctx, ticker, "TODO") + runErr = e.Run(ctx, ticker, wgUpEvents, wgDownEvents) }() for i := 0; i < 10; i++ { select { - case ticker <- struct{}{}: + case ticker <- ingest.None{}: case <-wait: t.Fatal("unexpected engine run termination") } } - close(ticker) + done() <-wait - require.Nil(t, runErr) + require.ErrorIs(t, runErr, context.Canceled) } -func TestEngineSingleStaticPeerWithWgReadPeersUsageFailure(t *testing.T) { +func TestEngineSingleStaticPeerUnavailableWgServer(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx, done := context.WithCancel(context.Background()) ctrl, ctx := gomock.WithContext(ctx, t) gatherTime := time.Now() store := mocks.NewMockStore(ctrl) - store.EXPECT().LoadBeforeRestartUsage(ctx).Times(0) - gomock.InOrder( - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - ) - - readRestartMarkFile := mocks.NewMockRestartMarkFileReadRemover(ctrl) - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(8) - readRestartMarkFile.EXPECT().Remove("TODO").Return(nil).Times(0) + store.EXPECT().LoadUsage(ctx).Times(0) + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1) - readWGPeersUsage := mocks.NewMockWgPeers(ctrl) + wgPeers := mocks.NewMockWgPeers(ctrl) gomock.InOrder( - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return(nil, gatherTime, errors.New("unknown error")).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return(nil, gatherTime, errors.New("network error")).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return(nil, gatherTime, os.ErrNotExist).Times(1), + wgPeers.EXPECT().Usage(ctx).Return(nil, gatherTime, os.ErrNotExist).Times(1), + wgPeers.EXPECT().Usage(ctx).Return(nil, gatherTime, os.ErrNotExist).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), ) - e := ingest.NewEngine(readRestartMarkFile, readWGPeersUsage, store, zerolog.New(io.Discard)) + e := ingest.NewEngine(wgPeers, store, zerolog.New(io.Discard)) - ticker := make(chan struct{}) + ticker := make(chan ingest.None) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) var runErr error wait := make(chan struct{}) @@ -184,65 +154,60 @@ func TestEngineSingleStaticPeerWithWgReadPeersUsageFailure(t *testing.T) { defer func() { wait <- struct{}{} }() - runErr = e.Run(ctx, ticker, "TODO") + runErr = e.Run(ctx, ticker, wgUpEvents, wgDownEvents) }() - for i := 0; i < 10; i++ { + for i := 0; i < 4; i++ { select { - case ticker <- struct{}{}: + case ticker <- ingest.None{}: case <-wait: t.Fatal("unexpected engine run termination") } } - close(ticker) + done() <-wait - require.Nil(t, runErr) + require.ErrorIs(t, runErr, context.Canceled) } -func TestEngineSingleStaticPeerWithIngestFailure(t *testing.T) { +func TestEngineSingleStaticPeerWithWgReadPeersUsageFailure(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx, done := context.WithCancel(context.Background()) ctrl, ctx := gomock.WithContext(ctx, t) gatherTime := time.Now() store := mocks.NewMockStore(ctrl) - store.EXPECT().LoadBeforeRestartUsage(ctx).Times(0) + store.EXPECT().LoadUsage(ctx).Times(0) gomock.InOrder( store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime).Return(errors.New("unknown error")).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 30, Download: 90, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime).Return(errors.New("network error")).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 80, Download: 240, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), ) - readRestartMarkFile := mocks.NewMockRestartMarkFileReadRemover(ctrl) - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(10) - readRestartMarkFile.EXPECT().Remove("TODO").Return(nil).Times(0) - - readWGPeersUsage := mocks.NewMockWgPeers(ctrl) + wgPeers := mocks.NewMockWgPeers(ctrl) gomock.InOrder( - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 30, Download: 90, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 80, Download: 240, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return(nil, gatherTime, errors.New("unknown error")).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return(nil, gatherTime, errors.New("network error")).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime, nil).Times(1), ) - e := ingest.NewEngine(readRestartMarkFile, readWGPeersUsage, store, zerolog.New(io.Discard)) + e := ingest.NewEngine(wgPeers, store, zerolog.New(io.Discard)) - ticker := make(chan struct{}) + ticker := make(chan ingest.None) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) var runErr error wait := make(chan struct{}) @@ -250,63 +215,62 @@ func TestEngineSingleStaticPeerWithIngestFailure(t *testing.T) { defer func() { wait <- struct{}{} }() - runErr = e.Run(ctx, ticker, "TODO") + runErr = e.Run(ctx, ticker, wgUpEvents, wgDownEvents) }() for i := 0; i < 10; i++ { select { - case ticker <- struct{}{}: + case ticker <- ingest.None{}: case <-wait: t.Fatal("unexpected engine run termination") } } - close(ticker) + done() <-wait - require.Nil(t, runErr) + require.ErrorIs(t, runErr, context.Canceled) } -func TestEngineSingleStaticPeerWithRestartMarkFileReadFailure(t *testing.T) { +func TestEngineSingleStaticPeerWithIngestFailure(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx, done := context.WithCancel(context.Background()) ctrl, ctx := gomock.WithContext(ctx, t) gatherTime := time.Now() store := mocks.NewMockStore(ctrl) - store.EXPECT().LoadBeforeRestartUsage(ctx).Times(0) + store.EXPECT().LoadUsage(ctx).Times(0) gomock.InOrder( store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime).Return(errors.New("unknown error")).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 30, Download: 90, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - ) - - readRestartMarkFile := mocks.NewMockRestartMarkFileReadRemover(ctrl) - gomock.InOrder( - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(3), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{2}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(2), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrPermission).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime).Return(errors.New("network error")).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 80, Download: 240, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), ) - readRestartMarkFile.EXPECT().Remove("TODO").Return(nil).Times(0) - readWGPeersUsage := mocks.NewMockWgPeers(ctrl) + wgPeers := mocks.NewMockWgPeers(ctrl) gomock.InOrder( - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 30, Download: 90, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 30, Download: 90, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 80, Download: 240, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime, nil).Times(1), ) - e := ingest.NewEngine(readRestartMarkFile, readWGPeersUsage, store, zerolog.New(io.Discard)) + e := ingest.NewEngine(wgPeers, store, zerolog.New(io.Discard)) - ticker := make(chan struct{}) + ticker := make(chan ingest.None) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) var runErr error wait := make(chan struct{}) @@ -314,75 +278,67 @@ func TestEngineSingleStaticPeerWithRestartMarkFileReadFailure(t *testing.T) { defer func() { wait <- struct{}{} }() - runErr = e.Run(ctx, ticker, "TODO") + runErr = e.Run(ctx, ticker, wgUpEvents, wgDownEvents) }() - for i := 0; i < 7; i++ { + for i := 0; i < 10; i++ { select { - case ticker <- struct{}{}: + case ticker <- ingest.None{}: case <-wait: t.Fatal("unexpected engine run termination") } } - close(ticker) + done() <-wait - require.NotNil(t, runErr) - require.ErrorIs(t, runErr, os.ErrPermission) + require.ErrorIs(t, runErr, context.Canceled) } -func TestEngineSingleStaticPeerWithRestartsAndLoadBeforeRestartUsageFailure(t *testing.T) { +func TestEngineSingleStaticPeerWithRestartsAndLoadUsageFailure(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx, done := context.WithCancel(context.Background()) ctrl, ctx := gomock.WithContext(ctx, t) gatherTime := time.Now() store := mocks.NewMockStore(ctrl) gomock.InOrder( - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 10, Download: 80, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(nil, errors.New("unknown error")).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 10 + 10, Download: 30 + 80, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(nil, errors.New("network error")).Times(2), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 50 + 10 + 10, Download: 150 + 30 + 80, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(nil, errors.New("unknown error")).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 10, Download: 80, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(nil, errors.New("unknown error")).Times(2), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 10 + 10, Download: 30 + 80, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(nil, errors.New("network error")).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 30 + 10 + 10, Download: 90 + 30 + 80, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(nil, errors.New("unknown error")).Times(3), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 120, Download: 410, PublicKey: "xyz"}}, nil).Times(1), ) gomock.InOrder( store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 10 + 10, Download: 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 30 + 10 + 10, Download: 90 + 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 40 + 10 + 10, Download: 120 + 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 50 + 10 + 10, Download: 150 + 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 80 + 50 + 10 + 10, Download: 240 + 150 + 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 90 + 50 + 10 + 10, Download: 270 + 150 + 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), - ) - - readRestartMarkFile := mocks.NewMockRestartMarkFileReadRemover(ctrl) - gomock.InOrder( - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(3), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(2), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(3), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 40 + 30 + 10 + 10, Download: 120 + 90 + 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 50 + 30 + 10 + 10, Download: 150 + 90 + 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 60 + 30 + 10 + 10, Download: 180 + 90 + 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 70 + 30 + 10 + 10, Download: 210 + 90 + 30 + 80, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), + store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 100 + 120, Download: 300 + 410, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), ) - readRestartMarkFile.EXPECT().Remove("TODO").Return(nil).Times(3) - readWGPeersUsage := mocks.NewMockWgPeers(ctrl) + wgPeers := mocks.NewMockWgPeers(ctrl) gomock.InOrder( - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 30, Download: 90, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 80, Download: 240, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 10, Download: 30, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 20, Download: 60, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 30, Download: 90, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 40, Download: 120, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 50, Download: 150, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 60, Download: 180, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 70, Download: 210, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 80, Download: 240, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 90, Download: 270, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 100, Download: 300, PublicKey: "xyz"}}, gatherTime, nil).Times(1), ) - e := ingest.NewEngine(readRestartMarkFile, readWGPeersUsage, store, zerolog.New(io.Discard)) + e := ingest.NewEngine(wgPeers, store, zerolog.New(io.Discard)) - ticker := make(chan struct{}) + ticker := make(chan ingest.None) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) var runErr error wait := make(chan struct{}) @@ -390,36 +346,99 @@ func TestEngineSingleStaticPeerWithRestartsAndLoadBeforeRestartUsageFailure(t *t defer func() { wait <- struct{}{} }() - runErr = e.Run(ctx, ticker, "TODO") + runErr = e.Run(ctx, ticker, wgUpEvents, wgDownEvents) }() - for i := 0; i < 10; i++ { - select { - case ticker <- struct{}{}: - case <-wait: - t.Fatal("unexpected engine run termination") - } + select { + case wgUpEvents <- ingest.WgUpEvent{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case wgUpEvents <- ingest.WgUpEvent{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case wgUpEvents <- ingest.WgUpEvent{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case wgUpEvents <- ingest.WgUpEvent{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") } - close(ticker) + done() <-wait - require.Nil(t, runErr) + require.ErrorIs(t, runErr, context.Canceled) } func TestEngineSingleStaticPeerWithRestart(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx, done := context.WithCancel(context.Background()) ctrl, ctx := gomock.WithContext(ctx, t) gatherTime := time.Now() store := mocks.NewMockStore(ctrl) gomock.InOrder( - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 154, Download: 215, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 5852, Download: 43146, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 6406, Download: 43888, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 8555, Download: 67015, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 154, Download: 215, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 5852, Download: 43146, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 6406, Download: 43888, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 8555, Download: 67015, PublicKey: "xyz"}}, nil).Times(1), ) gomock.InOrder( store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 120, Download: 169, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), @@ -459,62 +478,49 @@ func TestEngineSingleStaticPeerWithRestart(t *testing.T) { store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 16545, Download: 166809, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), ) - readRestartMarkFile := mocks.NewMockRestartMarkFileReadRemover(ctrl) - readRestartMarkFile.EXPECT().Remove("TODO").Return(nil).Times(4) - gomock.InOrder( - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(7), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(6), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(6), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(6), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(6), - ) - - readWGPeersUsage := mocks.NewMockWgPeers(ctrl) + wgPeers := mocks.NewMockWgPeers(ctrl) gomock.InOrder( - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 120, Download: 169, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 122, Download: 170, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 141, Download: 176, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 142, Download: 186, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 150, Download: 194, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 151, Download: 198, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 154, Download: 215, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 3976, Download: 29392, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4076, Download: 31561, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4408, Download: 32317, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4477, Download: 35529, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4735, Download: 37798, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 5505, Download: 38365, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 5698, Download: 42931, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 513, Download: 701, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 531, Download: 702, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 540, Download: 708, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 542, Download: 721, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 544, Download: 730, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 551, Download: 738, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 554, Download: 742, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1370, Download: 13296, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1490, Download: 14059, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1510, Download: 14988, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1913, Download: 16065, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1960, Download: 19509, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 2028, Download: 20622, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 2149, Download: 23127, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 6502, Download: 76574, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 6506, Download: 82500, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7082, Download: 83086, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7360, Download: 90087, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7417, Download: 91845, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7528, Download: 96378, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7990, Download: 99794, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 120, Download: 169, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 122, Download: 170, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 141, Download: 176, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 142, Download: 186, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 150, Download: 194, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 151, Download: 198, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 154, Download: 215, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 3976, Download: 29392, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4076, Download: 31561, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4408, Download: 32317, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4477, Download: 35529, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4735, Download: 37798, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 5505, Download: 38365, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 5698, Download: 42931, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 513, Download: 701, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 531, Download: 702, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 540, Download: 708, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 542, Download: 721, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 544, Download: 730, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 551, Download: 738, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 554, Download: 742, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1370, Download: 13296, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1490, Download: 14059, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1510, Download: 14988, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1913, Download: 16065, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1960, Download: 19509, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 2028, Download: 20622, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 2149, Download: 23127, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 6502, Download: 76574, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 6506, Download: 82500, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7082, Download: 83086, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7360, Download: 90087, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7417, Download: 91845, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7528, Download: 96378, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7990, Download: 99794, PublicKey: "xyz"}}, gatherTime, nil).Times(1), ) - e := ingest.NewEngine(readRestartMarkFile, readWGPeersUsage, store, zerolog.New(io.Discard)) + e := ingest.NewEngine(wgPeers, store, zerolog.New(io.Discard)) - ticker := make(chan struct{}) + ticker := make(chan ingest.None) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) var runErr error wait := make(chan struct{}) @@ -522,37 +528,53 @@ func TestEngineSingleStaticPeerWithRestart(t *testing.T) { defer func() { wait <- struct{}{} }() - runErr = e.Run(ctx, ticker, "TODO") + runErr = e.Run(ctx, ticker, wgUpEvents, wgDownEvents) }() - for i := 0; i < 35; i++ { + for i := 0; i < 4; i++ { + for i := 0; i < 7; i++ { + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + } + select { - case ticker <- struct{}{}: + case wgUpEvents <- ingest.WgUpEvent{}: case <-wait: t.Fatal("unexpected engine run termination") } } - close(ticker) + for i := 0; i < 7; i++ { + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + } + + done() <-wait - require.Nil(t, runErr) + require.ErrorIs(t, runErr, context.Canceled) } -func TestEngineSingleStaticPeerWithRestartStartingWithRestartMarkFileExistence(t *testing.T) { +func TestEngineSingleStaticPeerWithRestartsAndStartingAfterWgRestart(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx, done := context.WithCancel(context.Background()) ctrl, ctx := gomock.WithContext(ctx, t) gatherTime := time.Now() store := mocks.NewMockStore(ctrl) gomock.InOrder( - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 1, Download: 2, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 155, Download: 217, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 5853, Download: 43148, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 6407, Download: 43890, PublicKey: "xyz"}}, nil).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 8556, Download: 67017, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 1, Download: 2, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 155, Download: 217, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 5853, Download: 43148, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 6407, Download: 43890, PublicKey: "xyz"}}, nil).Times(1), + store.EXPECT().LoadUsage(ctx).Return(map[string]ingest.PeerUsage{"xyz": {Upload: 8556, Download: 67017, PublicKey: "xyz"}}, nil).Times(1), ) gomock.InOrder( store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 121, Download: 171, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), @@ -592,63 +614,49 @@ func TestEngineSingleStaticPeerWithRestartStartingWithRestartMarkFileExistence(t store.EXPECT().IngestUsage(ctx, []ingest.PeerUsage{{Upload: 16546, Download: 166811, PublicKey: "xyz"}}, gatherTime).Return(nil).Times(1), ) - readRestartMarkFile := mocks.NewMockRestartMarkFileReadRemover(ctrl) - readRestartMarkFile.EXPECT().Remove("TODO").Return(nil).Times(5) - gomock.InOrder( - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(6), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(6), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(6), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(6), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(6), - ) - - readWGPeersUsage := mocks.NewMockWgPeers(ctrl) + wgPeers := mocks.NewMockWgPeers(ctrl) gomock.InOrder( - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 120, Download: 169, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 122, Download: 170, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 141, Download: 176, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 142, Download: 186, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 150, Download: 194, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 151, Download: 198, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 154, Download: 215, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 3976, Download: 29392, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4076, Download: 31561, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4408, Download: 32317, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4477, Download: 35529, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4735, Download: 37798, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 5505, Download: 38365, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 5698, Download: 42931, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 513, Download: 701, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 531, Download: 702, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 540, Download: 708, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 542, Download: 721, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 544, Download: 730, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 551, Download: 738, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 554, Download: 742, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1370, Download: 13296, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1490, Download: 14059, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1510, Download: 14988, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1913, Download: 16065, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1960, Download: 19509, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 2028, Download: 20622, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 2149, Download: 23127, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 6502, Download: 76574, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 6506, Download: 82500, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7082, Download: 83086, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7360, Download: 90087, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7417, Download: 91845, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7528, Download: 96378, PublicKey: "xyz"}}, gatherTime, nil).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7990, Download: 99794, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 120, Download: 169, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 122, Download: 170, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 141, Download: 176, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 142, Download: 186, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 150, Download: 194, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 151, Download: 198, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 154, Download: 215, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 3976, Download: 29392, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4076, Download: 31561, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4408, Download: 32317, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4477, Download: 35529, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 4735, Download: 37798, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 5505, Download: 38365, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 5698, Download: 42931, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 513, Download: 701, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 531, Download: 702, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 540, Download: 708, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 542, Download: 721, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 544, Download: 730, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 551, Download: 738, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 554, Download: 742, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1370, Download: 13296, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1490, Download: 14059, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1510, Download: 14988, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1913, Download: 16065, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 1960, Download: 19509, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 2028, Download: 20622, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 2149, Download: 23127, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 6502, Download: 76574, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 6506, Download: 82500, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7082, Download: 83086, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7360, Download: 90087, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7417, Download: 91845, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7528, Download: 96378, PublicKey: "xyz"}}, gatherTime, nil).Times(1), + wgPeers.EXPECT().Usage(ctx).Return([]ingest.PeerUsage{{Upload: 7990, Download: 99794, PublicKey: "xyz"}}, gatherTime, nil).Times(1), ) - e := ingest.NewEngine(readRestartMarkFile, readWGPeersUsage, store, zerolog.New(io.Discard)) + e := ingest.NewEngine(wgPeers, store, zerolog.New(io.Discard)) - ticker := make(chan struct{}) + ticker := make(chan ingest.None) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) var runErr error wait := make(chan struct{}) @@ -656,32 +664,41 @@ func TestEngineSingleStaticPeerWithRestartStartingWithRestartMarkFileExistence(t defer func() { wait <- struct{}{} }() - runErr = e.Run(ctx, ticker, "TODO") + runErr = e.Run(ctx, ticker, wgUpEvents, wgDownEvents) }() - for i := 0; i < 35; i++ { + for i := 0; i < 5; i++ { select { - case ticker <- struct{}{}: + case wgUpEvents <- ingest.WgUpEvent{}: case <-wait: t.Fatal("unexpected engine run termination") } + + for i := 0; i < 7; i++ { + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + } } - close(ticker) + done() <-wait - require.Nil(t, runErr) + require.ErrorIs(t, runErr, context.Canceled) } -func TestEngineMultipleDynamicPeers(t *testing.T) { +func TestEngineMultipleDynamicPeersWithRestarts(t *testing.T) { t.Parallel() - ctx := context.Background() + + ctx, done := context.WithCancel(context.Background()) ctrl, ctx := gomock.WithContext(ctx, t) gatherTime := time.Now() store := mocks.NewMockStore(ctrl) gomock.InOrder( - store.EXPECT().LoadBeforeRestartUsage(ctx).Return( + store.EXPECT().LoadUsage(ctx).Return( map[string]ingest.PeerUsage{ "abc": {Upload: 25, Download: 65, PublicKey: "abc"}, "xyz": {Upload: 20, Download: 60, PublicKey: "xyz"}, @@ -689,32 +706,32 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { }, nil, ).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return( + store.EXPECT().LoadUsage(ctx).Return( map[string]ingest.PeerUsage{ - "xyz": {Upload: 50, Download: 150, PublicKey: "xyz"}, - "852": {Upload: 186580512, Download: 995098551, PublicKey: "852"}, - "abc": {Upload: 128001692, Download: 186202004, PublicKey: "abc"}, - "123": {Upload: 57, Download: 153, PublicKey: "123"}, + "abc": {Upload: 6531511 + 25, Download: 55474931 + 65, PublicKey: "abc"}, + "xyz": {Upload: 12 + 20, Download: 33 + 60, PublicKey: "xyz"}, + "123": {Upload: 17, Download: 53, PublicKey: "123"}, }, nil, - ).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return( + ).Times(2), + store.EXPECT().LoadUsage(ctx).Return( map[string]ingest.PeerUsage{ - "xyz": {Upload: 50, Download: 150, PublicKey: "xyz"}, + "xyz": {Upload: 69, Download: 191, PublicKey: "xyz"}, "852": {Upload: 186580512, Download: 995098551, PublicKey: "852"}, - "abc": {Upload: 128001692, Download: 186202004, PublicKey: "abc"}, - "123": {Upload: 107, Download: 263, PublicKey: "123"}, + "abc": {Upload: 6531585, Download: 55475133, PublicKey: "abc"}, + "123": {Upload: 62, Download: 173, PublicKey: "123"}, + "qwe": {Upload: 20, Download: 40, PublicKey: "qwe"}, }, nil, ).Times(1), - store.EXPECT().LoadBeforeRestartUsage(ctx).Return( + store.EXPECT().LoadUsage(ctx).Return( map[string]ingest.PeerUsage{ "852": {Upload: 186580512, Download: 995098551, PublicKey: "852"}, - "qwe": {Upload: 40, Download: 85, PublicKey: "qwe"}, - "xyz": {Upload: 37 + 50, Download: 98 + 150, PublicKey: "xyz"}, - "123": {Upload: 45 + 107, Download: 120 + 263, PublicKey: "123"}, + "qwe": {Upload: 60, Download: 125, PublicKey: "qwe"}, + "xyz": {Upload: 106, Download: 289, PublicKey: "xyz"}, + "123": {Upload: 156, Download: 312, PublicKey: "123"}, "456": {Upload: 124728866, Download: 155917550, PublicKey: "456"}, - "abc": {Upload: 49 + 128001692, Download: 137 + 186202004, PublicKey: "abc"}, + "abc": {Upload: 6531634, Download: 55475270, PublicKey: "abc"}, }, nil, ).Times(1), @@ -749,17 +766,17 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { store.EXPECT().IngestUsage( ctx, []ingest.PeerUsage{ - {Upload: 50, Download: 150, PublicKey: "xyz"}, + {Upload: 30 + 12 + 20, Download: 90 + 33 + 60, PublicKey: "xyz"}, {Upload: 186580512, Download: 995098551, PublicKey: "852"}, - {Upload: 128001692, Download: 186202004, PublicKey: "abc"}, - {Upload: 57, Download: 153, PublicKey: "123"}, + {Upload: 128001667 + 6531511 + 25, Download: 186201939 + 55474931 + 65, PublicKey: "abc"}, + {Upload: 40 + 17, Download: 100 + 53, PublicKey: "123"}, }, gatherTime, ).Return(nil).Times(1), store.EXPECT().IngestUsage( ctx, []ingest.PeerUsage{ - {Upload: 50 + 57, Download: 110 + 153, PublicKey: "123"}, + {Upload: 50 + 17, Download: 110 + 53, PublicKey: "123"}, }, gatherTime, ).Return(nil).Times(1), @@ -767,28 +784,28 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { ctx, []ingest.PeerUsage{ {Upload: 20, Download: 40, PublicKey: "qwe"}, - {Upload: 37 + 50, Download: 98 + 150, PublicKey: "xyz"}, - {Upload: 45 + 57 + 50, Download: 120 + 110 + 153, PublicKey: "123"}, - {Upload: 49 + 128001692, Download: 137 + 186202004, PublicKey: "abc"}, + {Upload: 37 + 12 + 20, Download: 98 + 33 + 60, PublicKey: "xyz"}, + {Upload: 45 + 17, Download: 120 + 53, PublicKey: "123"}, + {Upload: 49 + 6531511 + 25, Download: 137 + 55474931 + 65, PublicKey: "abc"}, }, gatherTime, ).Return(nil).Times(1), store.EXPECT().IngestUsage( ctx, []ingest.PeerUsage{ - {Upload: 30, Download: 75, PublicKey: "qwe"}, - {Upload: 53 + 107, Download: 132 + 263, PublicKey: "123"}, + {Upload: 30 + 20, Download: 75 + 40, PublicKey: "qwe"}, + {Upload: 53 + 62, Download: 132 + 173, PublicKey: "123"}, }, gatherTime, ).Return(nil).Times(1), store.EXPECT().IngestUsage( ctx, []ingest.PeerUsage{ - {Upload: 40, Download: 85, PublicKey: "qwe"}, - {Upload: 37 + 50, Download: 98 + 150, PublicKey: "xyz"}, - {Upload: 45 + 107, Download: 120 + 263, PublicKey: "123"}, + {Upload: 40 + 20, Download: 85 + 40, PublicKey: "qwe"}, + {Upload: 37 + 69, Download: 98 + 191, PublicKey: "xyz"}, + {Upload: 94 + 62, Download: 139 + 173, PublicKey: "123"}, {Upload: 124728866, Download: 155917550, PublicKey: "456"}, - {Upload: 49 + 128001692, Download: 137 + 186202004, PublicKey: "abc"}, + {Upload: 49 + 6531585, Download: 137 + 55475133, PublicKey: "abc"}, }, gatherTime, ).Return(nil).Times(1), @@ -796,27 +813,16 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { ctx, []ingest.PeerUsage{ {Upload: 203658612 + 124728866, Download: 220351578 + 155917550, PublicKey: "456"}, - {Upload: 50 + 40, Download: 95 + 85, PublicKey: "qwe"}, - {Upload: 76 + 37 + 50, Download: 168 + 98 + 150, PublicKey: "xyz"}, + {Upload: 50 + 186580512, Download: 95 + 995098551, PublicKey: "852"}, + {Upload: 76 + 37 + 69, Download: 168 + 98 + 191, PublicKey: "xyz"}, }, gatherTime, ).Return(nil).Times(1), ) - readRestartMarkFile := mocks.NewMockRestartMarkFileReadRemover(ctrl) - readRestartMarkFile.EXPECT().Remove("TODO").Return(nil).Times(4) - gomock.InOrder( - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(2), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(1), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(2), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{0}, os.ErrNotExist).Times(3), - readRestartMarkFile.EXPECT().Read("TODO").Return([1]byte{1}, nil).Times(1), - ) - - readWGPeersUsage := mocks.NewMockWgPeers(ctrl) + wgPeers := mocks.NewMockWgPeers(ctrl) gomock.InOrder( - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{ {Upload: 10, Download: 30, PublicKey: "xyz"}, {Upload: 15, Download: 35, PublicKey: "abc"}, @@ -824,7 +830,7 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { gatherTime, nil, ).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{ {Upload: 20, Download: 60, PublicKey: "xyz"}, {Upload: 17, Download: 53, PublicKey: "123"}, @@ -833,7 +839,7 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { gatherTime, nil, ).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{ {Upload: 6531511, Download: 55474931, PublicKey: "abc"}, {Upload: 12, Download: 33, PublicKey: "xyz"}, @@ -841,7 +847,7 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { gatherTime, nil, ).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{ {Upload: 30, Download: 90, PublicKey: "xyz"}, {Upload: 186580512, Download: 995098551, PublicKey: "852"}, @@ -851,19 +857,19 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { gatherTime, nil, ).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{ {Upload: 50, Download: 110, PublicKey: "123"}, }, gatherTime, nil, ).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{}, gatherTime, nil, ).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{ {Upload: 20, Download: 40, PublicKey: "qwe"}, {Upload: 37, Download: 98, PublicKey: "xyz"}, @@ -873,7 +879,7 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { gatherTime, nil, ).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{ {Upload: 30, Download: 75, PublicKey: "qwe"}, {Upload: 53, Download: 132, PublicKey: "123"}, @@ -881,21 +887,21 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { gatherTime, nil, ).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{ {Upload: 40, Download: 85, PublicKey: "qwe"}, {Upload: 37, Download: 98, PublicKey: "xyz"}, - {Upload: 45, Download: 120, PublicKey: "123"}, + {Upload: 94, Download: 139, PublicKey: "123"}, {Upload: 124728866, Download: 155917550, PublicKey: "456"}, {Upload: 49, Download: 137, PublicKey: "abc"}, }, gatherTime, nil, ).Times(1), - readWGPeersUsage.EXPECT().Usage(ctx).Return( + wgPeers.EXPECT().Usage(ctx).Return( []ingest.PeerUsage{ {Upload: 203658612, Download: 220351578, PublicKey: "456"}, - {Upload: 50, Download: 95, PublicKey: "qwe"}, + {Upload: 50, Download: 95, PublicKey: "852"}, {Upload: 76, Download: 168, PublicKey: "xyz"}, }, gatherTime, @@ -903,9 +909,10 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { ).Times(1), ) - e := ingest.NewEngine(readRestartMarkFile, readWGPeersUsage, store, zerolog.New(io.Discard)) + e := ingest.NewEngine(wgPeers, store, zerolog.New(io.Discard)) - ticker := make(chan struct{}) + ticker := make(chan ingest.None) + wgUpEvents, wgDownEvents := make(chan ingest.WgUpEvent), make(chan ingest.WgDownEvent) var runErr error wait := make(chan struct{}) @@ -913,18 +920,64 @@ func TestEngineMultipleDynamicPeers(t *testing.T) { defer func() { wait <- struct{}{} }() - runErr = e.Run(ctx, ticker, "TODO") + runErr = e.Run(ctx, ticker, wgUpEvents, wgDownEvents) }() - for i := 0; i < 10; i++ { + for i := 0; i < 2; i++ { + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + } + select { + case wgUpEvents <- ingest.WgUpEvent{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + for i := 0; i < 2; i++ { select { - case ticker <- struct{}{}: + case wgUpEvents <- ingest.WgUpEvent{}: case <-wait: t.Fatal("unexpected engine run termination") } } + for i := 0; i < 4; i++ { + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + } + select { + case wgUpEvents <- ingest.WgUpEvent{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + for i := 0; i < 2; i++ { + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + } + select { + case wgUpEvents <- ingest.WgUpEvent{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } + select { + case ticker <- ingest.None{}: + case <-wait: + t.Fatal("unexpected engine run termination") + } - close(ticker) + done() <-wait - require.Nil(t, runErr) + require.ErrorIs(t, runErr, context.Canceled) } diff --git a/ingest/mocks/ingest.go b/ingest/mocks/ingest.go index 06dd7a5..aeffe79 100644 --- a/ingest/mocks/ingest.go +++ b/ingest/mocks/ingest.go @@ -50,19 +50,19 @@ func (mr *MockStoreMockRecorder) IngestUsage(ctx, peersUsage, gatheredAt interfa return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IngestUsage", reflect.TypeOf((*MockStore)(nil).IngestUsage), ctx, peersUsage, gatheredAt) } -// LoadBeforeRestartUsage mocks base method. -func (m *MockStore) LoadBeforeRestartUsage(ctx context.Context) (map[string]ingest.PeerUsage, error) { +// LoadUsage mocks base method. +func (m *MockStore) LoadUsage(ctx context.Context) (map[string]ingest.PeerUsage, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LoadBeforeRestartUsage", ctx) + ret := m.ctrl.Call(m, "LoadUsage", ctx) ret0, _ := ret[0].(map[string]ingest.PeerUsage) ret1, _ := ret[1].(error) return ret0, ret1 } -// LoadBeforeRestartUsage indicates an expected call of LoadBeforeRestartUsage. -func (mr *MockStoreMockRecorder) LoadBeforeRestartUsage(ctx interface{}) *gomock.Call { +// LoadUsage indicates an expected call of LoadUsage. +func (mr *MockStoreMockRecorder) LoadUsage(ctx interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadBeforeRestartUsage", reflect.TypeOf((*MockStore)(nil).LoadBeforeRestartUsage), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadUsage", reflect.TypeOf((*MockStore)(nil).LoadUsage), ctx) } // MockWgPeers is a mock of WgPeers interface. @@ -103,55 +103,3 @@ func (mr *MockWgPeersMockRecorder) Usage(ctx interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Usage", reflect.TypeOf((*MockWgPeers)(nil).Usage), ctx) } - -// MockRestartMarkFileReadRemover is a mock of RestartMarkFileReadRemover interface. -type MockRestartMarkFileReadRemover struct { - ctrl *gomock.Controller - recorder *MockRestartMarkFileReadRemoverMockRecorder -} - -// MockRestartMarkFileReadRemoverMockRecorder is the mock recorder for MockRestartMarkFileReadRemover. -type MockRestartMarkFileReadRemoverMockRecorder struct { - mock *MockRestartMarkFileReadRemover -} - -// NewMockRestartMarkFileReadRemover creates a new mock instance. -func NewMockRestartMarkFileReadRemover(ctrl *gomock.Controller) *MockRestartMarkFileReadRemover { - mock := &MockRestartMarkFileReadRemover{ctrl: ctrl} - mock.recorder = &MockRestartMarkFileReadRemoverMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRestartMarkFileReadRemover) EXPECT() *MockRestartMarkFileReadRemoverMockRecorder { - return m.recorder -} - -// Read mocks base method. -func (m *MockRestartMarkFileReadRemover) Read(filename string) ([1]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Read", filename) - ret0, _ := ret[0].([1]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Read indicates an expected call of Read. -func (mr *MockRestartMarkFileReadRemoverMockRecorder) Read(filename interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockRestartMarkFileReadRemover)(nil).Read), filename) -} - -// Remove mocks base method. -func (m *MockRestartMarkFileReadRemover) Remove(filename string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Remove", filename) - ret0, _ := ret[0].(error) - return ret0 -} - -// Remove indicates an expected call of Remove. -func (mr *MockRestartMarkFileReadRemoverMockRecorder) Remove(filename interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockRestartMarkFileReadRemover)(nil).Remove), filename) -} diff --git a/pkg/dump/parse.go b/pkg/dump/parse.go new file mode 100644 index 0000000..fa4d083 --- /dev/null +++ b/pkg/dump/parse.go @@ -0,0 +1,137 @@ +package dump + +import ( + "bufio" + "errors" + "fmt" + "io" + "net" + "net/netip" + "strconv" + "strings" + "time" + + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +type Peer struct { + PublicKey wgtypes.Key + PresharedKey wgtypes.Key + Endpoint *net.UDPAddr + AllowedIPs []net.IPNet + LastHandshakeTime time.Time + ReceiveBytes uint64 + TransmitBytes uint64 + PersistentKeepaliveInterval time.Duration +} + +func Parse(r io.Reader) ([]Peer, error) { + var out []Peer + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + splits := strings.SplitN(scanner.Text(), "\t", 8) + if len(splits) != 8 { + return nil, errors.New("invalid line") + } + + raw := splits[0] + publicKey, err := wgtypes.ParseKey(raw) + if nil != err { + return nil, fmt.Errorf("invalid public key (%s): %v", raw, err) + } + + raw = splits[1] + presharedKey, err := wgtypes.ParseKey(raw) + if nil != err { + return nil, fmt.Errorf("invalid preshared key (%s): %v", raw, err) + } + + endpoint, err := func() (*net.UDPAddr, error) { + raw := splits[2] + if raw == "(none)" { + return nil, nil + } + + addrPort, err := netip.ParseAddrPort(raw) + if nil != err { + return nil, fmt.Errorf("invalid endpoint (%s): %v", raw, err) + } + endpoint := net.UDPAddrFromAddrPort(addrPort) + return endpoint, nil + }() + if nil != err { + return nil, err + } + + var allowedIPs []net.IPNet + raw = splits[3] + rawAllowedIPs := strings.Split(raw, ",") + for _, v := range rawAllowedIPs { + _, ipnet, err := net.ParseCIDR(v) + if nil != err { + return nil, fmt.Errorf("invalid allowed ip (%s): %v", v, err) + } + + allowedIPs = append(allowedIPs, *ipnet) + } + + raw = splits[4] + var latestHandshakeTime time.Time + if raw != "0" { + i, err := strconv.ParseInt(raw, 10, 64) + if nil != err { + return nil, fmt.Errorf("invalid latest handshake unix timestamp (%s): %v", raw, err) + } + if i < 0 { + return nil, fmt.Errorf("unexpected negative latest handshake unix timestamp value (%s)", raw) + } + latestHandshakeTime = time.Unix(i, 0) + } + + raw = splits[5] + receiveBytes, err := strconv.ParseInt(raw, 10, 64) + if nil != err { + return nil, fmt.Errorf("invalid number of received bytes (%s): %v", raw, err) + } + + raw = splits[6] + transmitBytes, err := strconv.ParseInt(raw, 10, 64) + if nil != err { + return nil, fmt.Errorf("invalid number of transmitted bytes (%s): %v", raw, err) + } + + persistentKeepaliveInterval, err := func() (time.Duration, error) { + raw := splits[7] + if raw == "off" { + return 0, nil + } + + i, err := strconv.ParseInt(raw, 10, 32) + if nil != err { + return 0, fmt.Errorf("invalid persistent keepalive interval (%s): %v", raw, err) + } + if i < 0 { + return 0, fmt.Errorf("unexpected negative persistent keepalive value (%s)", raw) + } + + return time.Duration(i), nil + }() + if nil != err { + return nil, err + } + + out = append(out, Peer{ + PublicKey: publicKey, + PresharedKey: presharedKey, + Endpoint: endpoint, + AllowedIPs: allowedIPs, + LastHandshakeTime: latestHandshakeTime, + ReceiveBytes: uint64(receiveBytes), + TransmitBytes: uint64(transmitBytes), + PersistentKeepaliveInterval: time.Duration(persistentKeepaliveInterval), + }) + } + + return out, nil +} diff --git a/pkg/dump/parse_test.go b/pkg/dump/parse_test.go new file mode 100644 index 0000000..2690860 --- /dev/null +++ b/pkg/dump/parse_test.go @@ -0,0 +1,769 @@ +package dump_test + +import ( + "bufio" + "bytes" + _ "embed" + "net" + "net/netip" + "testing" + "time" + + "github.com/stretchr/testify/require" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + + "github.com/xeptore/wireuse/pkg/dump" +) + +var ( + //go:embed testdata/dump.txt + rawValidDump []byte + //go:embed testdata/dump-invalid.txt + rawInvalidDump []byte +) + +func mustParseKey(t *testing.T, s string) wgtypes.Key { + key, err := wgtypes.ParseKey(s) + require.Nil(t, err) + + return key +} + +func mustParseAllowedIPs(t *testing.T, s ...string) []net.IPNet { + out := make([]net.IPNet, len(s)) + for i := 0; i < len(s); i++ { + _, ipNet, err := net.ParseCIDR(s[i]) + require.Nil(t, err) + out[i] = *ipNet + } + + return out +} + +func mustParseUDPAddr(t *testing.T, s string) *net.UDPAddr { + addrPort, err := netip.ParseAddrPort(s) + require.Nil(t, err) + + return net.UDPAddrFromAddrPort(addrPort) +} + +func mustParseUnixTime(t *testing.T, u int64) time.Time { + return time.Unix(u, 0) +} + +func TestParseValidDumpFile(t *testing.T) { + peers, err := dump.Parse(bytes.NewBuffer(rawValidDump)) + require.Nil(t, err) + require.NotNil(t, peers) + require.Equal(t, []dump.Peer{ + { + PublicKey: mustParseKey(t, "yLk+kQwd2Zm4I6f6Aeg/OGlT3GLtHsNmY+kUXK2sfD0="), + PresharedKey: mustParseKey(t, "QMXzIPq+o5zNvecAIQZn61rB9SmolhW0fgj7Zwylzvg="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.19/32", "fdd0:438e:19ba:5069::13/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "XuyQmI/KXTBvwdpfjUzULj4PfBW15mrMPXm8pS1OnhQ="), + PresharedKey: mustParseKey(t, "kT0tuNL/rhPoWjWiQhSCHFJuaSKLxlqg2aAuxwwpz4I="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.20/32", "fdd0:438e:19ba:5069::14/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "UDrZG/thxkzPbow3Lr1gGHYeKbNlQF8Znob9hujRYjo="), + PresharedKey: mustParseKey(t, "fkKqj8RmLjHbo8T63OtLn6JM8MnCIp+LZLpvxiIl2HU="), + Endpoint: mustParseUDPAddr(t, "83.123.9.201:42944"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.21/32", "fdd0:438e:19ba:5069::15/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679600629), + ReceiveBytes: 7138444, + TransmitBytes: 103247936, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "qwQBlkUITgLsddSi9kbKM14HCkvqmVYi1VT/8n9u6kY="), + PresharedKey: mustParseKey(t, "MFToe6QumdelJu3whvgSDniVNM+jmuTNMXP9FY07F8g="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.29/32", "fdd0:438e:19ba:5069::1d/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "5jNQhl3WZJBOI4+M5e4Pk7+9SCMSg6GyG+Dkc+wpCVA="), + PresharedKey: mustParseKey(t, "l7itiN0Pr0fQMD7Ae8EMpDHmQ0rL4KyTNuL/5J9+O6o="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.30/32", "fdd0:438e:19ba:5069::1e/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "OafCe4tLL8rShnOiQod35G+NdDPQhwIN4JKRoyKbNkI="), + PresharedKey: mustParseKey(t, "mJBHO390BWD3GxHMIdWFey70N7c6x5EDrVuzcROh/Ao="), + Endpoint: mustParseUDPAddr(t, "5.125.49.118:32738"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.41/32", "fdd0:438e:19ba:5069::29/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679608693), + ReceiveBytes: 69519144, + TransmitBytes: 475482968, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "R86vOf/VdN9m0Ey4AD80D4i5UdTefMsKJw/+BuOSeS4="), + PresharedKey: mustParseKey(t, "+V9iBlvEB7FCZcUMcY40c1FazbleACJY/qButRjkUUg="), + Endpoint: mustParseUDPAddr(t, "95.38.119.159:52491"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.42/32", "fdd0:438e:19ba:5069::2a/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679597842), + ReceiveBytes: 8385684, + TransmitBytes: 188015208, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "FaibxGztHHy3pm0kiiBfI6GMM6UpdiD6Kinekh0ARDk="), + PresharedKey: mustParseKey(t, "8R5QEYDbJw/VYsNw62LX9p+RGtn3NKeVyDeCol1zzT8="), + Endpoint: mustParseUDPAddr(t, "188.229.11.180:62680"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.43/32", "fdd0:438e:19ba:5069::2b/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679610586), + ReceiveBytes: 91786228, + TransmitBytes: 109298580, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "gOZgWvD4pVpGNx8ZBJOrEFbUvdOmIqyJ5xaWkb0QkwY="), + PresharedKey: mustParseKey(t, "4fpiAvyjMmUPGDsi5IV7EPWmq9ANOCsuBU81s1KKvwg="), + Endpoint: mustParseUDPAddr(t, "5.123.27.254:10852"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.44/32", "fdd0:438e:19ba:5069::2c/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679622245), + ReceiveBytes: 38969328, + TransmitBytes: 500779248, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "nRhGvfiDIdUts1yJn4Bt2QVURBK73ks++SrqdseUZjA="), + PresharedKey: mustParseKey(t, "DK+KyyjvtAHARlSkvH6ogNYeCb//GSVfp/SaYGCkTvc="), + Endpoint: mustParseUDPAddr(t, "83.122.114.201:53992"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.45/32", "fdd0:438e:19ba:5069::2d/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679614805), + ReceiveBytes: 49627932, + TransmitBytes: 1698468224, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "cM6cjxjXEfbV/MmdIaqnI3iG6NX0eTGrxi2xdr8+XlA="), + PresharedKey: mustParseKey(t, "yHHspi9idt69QY3EawOQrEVdi2kMQ4CdW4jadVHDWHo="), + Endpoint: mustParseUDPAddr(t, "5.126.154.201:32527"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.46/32", "fdd0:438e:19ba:5069::2e/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679622324), + ReceiveBytes: 41659912, + TransmitBytes: 387348224, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "gsqgOUyV3wl2QjGemvO73O1MD9PB/umq6lH18Px/1RU="), + PresharedKey: mustParseKey(t, "+NjgCZQ3224mpbDZUgIj24mzX6NcovotLuDOqjTHLw8="), + Endpoint: mustParseUDPAddr(t, "5.125.223.142:36784"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.47/32", "fdd0:438e:19ba:5069::2f/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679611750), + ReceiveBytes: 60529560, + TransmitBytes: 1421720032, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "mZNGrAccdCJ9a/7UOevRHKxjxOy7fPvgW3ev+bcROWk="), + PresharedKey: mustParseKey(t, "/Km1HGiYK+O/jJJJWdPZIEC6pNBzsghhYm9hsc61xFg="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.48/32", "fdd0:438e:19ba:5069::30/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "mYDlBMbMZKEjcX8nTjYWmOGQlguyLomQVm+q9yc9eAM="), + PresharedKey: mustParseKey(t, "MXSRqHNEMM48WGXqyUmCC2YxsueGZvyxIW8UnwFibt0="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.49/32", "fdd0:438e:19ba:5069::31/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "P7wDzaPcNHAfR2MVuQV//qD+YfXHR3Ad/7uLm61xUXA="), + PresharedKey: mustParseKey(t, "VuYrRbWESPD7kFpXdSs6BJ7fMppBM4IBxOURx/lYv9Q="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.50/32", "fdd0:438e:19ba:5069::32/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "rauZLam+acOP+/mC0C/vAjIweVoWZNQ0jK5iBmpAT0w="), + PresharedKey: mustParseKey(t, "dQQA+bYhLgtXwnPug9rQVok1R+ud9RRAqKgo04K/ljU="), + Endpoint: mustParseUDPAddr(t, "5.119.75.31:47338"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.51/32", "fdd0:438e:19ba:5069::33/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679622318), + ReceiveBytes: 49183124, + TransmitBytes: 946856032, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "xwlGtnl3nmV+uR+nN8No7mMQiv1OTTOKMdgrwaDBMFk="), + PresharedKey: mustParseKey(t, "NKeDh2sJkweEbC7ZspA5fPBL+9ahk/eqqKFcl0IemBg="), + Endpoint: mustParseUDPAddr(t, "37.129.244.184:43698"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.52/32", "fdd0:438e:19ba:5069::34/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679613152), + ReceiveBytes: 57819876, + TransmitBytes: 687229524, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "VF3fBaIiqEAlPdYWQO/XNy1cXxAzJOnHRWgJh4knglw="), + PresharedKey: mustParseKey(t, "SBX4QINGYs7wCF0GCIyqt1doc50FXgbQ5CYQz2X0YZk="), + Endpoint: mustParseUDPAddr(t, "5.115.179.111:36227"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.54/32", "fdd0:438e:19ba:5069::36/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679622359), + ReceiveBytes: 147183980, + TransmitBytes: 1465251556, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "4ZhiIszWe4sPsnCf+bHDs+O2pM+SSDI92urE9gO1AyY="), + PresharedKey: mustParseKey(t, "oxOYXuZsIZ1cvmN/r61l63XG7I4/ZeqJQk1Eizmslps="), + Endpoint: mustParseUDPAddr(t, "5.125.254.72:63259"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.55/32", "fdd0:438e:19ba:5069::37/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679604971), + ReceiveBytes: 30436016, + TransmitBytes: 800796928, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "dwwb9pB9b43e2mDF1OAF4ViKbRy8MhNog2/5zfsbHxM="), + PresharedKey: mustParseKey(t, "T+uhSXH45y+SAB/74swecdmKwbzoQkAk3QubTCQU4Xs="), + Endpoint: mustParseUDPAddr(t, "5.126.41.5:17955"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.56/32", "fdd0:438e:19ba:5069::38/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679622289), + ReceiveBytes: 193450932, + TransmitBytes: 1615951448, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "Sgz2K9TQGaDPPuMPgjdPnlrKCutJqUPkf6GrO9FdAgQ="), + PresharedKey: mustParseKey(t, "aFkkiUHklssOZ0oCGpG+Oj4Oj/P0FWf5Hei8GKRlCGc="), + Endpoint: mustParseUDPAddr(t, "5.124.107.243:18896"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.57/32", "fdd0:438e:19ba:5069::39/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679621178), + ReceiveBytes: 47751636, + TransmitBytes: 323048336, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "g0KUtRZ0tJG6YfDgVzfoY6pkmbe1fkeqsp8qu0CrjFk="), + PresharedKey: mustParseKey(t, "jg9Hor8qi3AOkBeRDV51l19Z0UQU+Ytrcap7Zb9v/9c="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.59/32", "fdd0:438e:19ba:5069::3b/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "sfIcIpMqJ1+RL34PDS56Y4m2f4P5Qj+KzhiWtJCjDVs="), + PresharedKey: mustParseKey(t, "DMcD4y+fKUyBEXW/DzZFBcr28hpcd2GSqMZQvg7Fmiw="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.60/32", "fdd0:438e:19ba:5069::3c/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "yNnsydGMEqEa3M3pi+cXtuWHlHKP7s5NMGReU+Jt81Q="), + PresharedKey: mustParseKey(t, "ZyJ35QWf+SKrnsLjHEKCUECLH6VPbwn5p+av3bFjJLE="), + Endpoint: mustParseUDPAddr(t, "5.125.201.34:11978"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.61/32", "fdd0:438e:19ba:5069::3d/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679608317), + ReceiveBytes: 9238972, + TransmitBytes: 141261976, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "bpvh8xyCKBZ5wjQRTKI7UxAWAFYXscV2ffsDhWyB+Ek="), + PresharedKey: mustParseKey(t, "1ZWVE8w0bk6q6Md4jhNmp8kDaadN+QBZZ8pZSZd/JjY="), + Endpoint: mustParseUDPAddr(t, "37.129.171.116:54772"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.62/32", "fdd0:438e:19ba:5069::3e/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679602156), + ReceiveBytes: 25262308, + TransmitBytes: 758835208, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "YESGUd6GzN4sldhXn80Nz3UeX/Tv1nANyTW98gHQTEY="), + PresharedKey: mustParseKey(t, "tXKZHknajC+JeytG8BIPjyir0D+fyctLz9CVG7FwZGU="), + Endpoint: mustParseUDPAddr(t, "5.232.199.164:18016"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.64/32", "fdd0:438e:19ba:5069::40/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679609091), + ReceiveBytes: 53371808, + TransmitBytes: 828476932, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "pw8kkrzQ762vv3fp3z0DSMpNtmLwRYj0m/iyzVMaYXM="), + PresharedKey: mustParseKey(t, "m3CxYl7p2snW00B+VIx/vLDG/le93DwfWMPnPC9EpL4="), + Endpoint: mustParseUDPAddr(t, "83.122.106.35:33994"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.65/32", "fdd0:438e:19ba:5069::41/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679620210), + ReceiveBytes: 54243332, + TransmitBytes: 1026011872, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "538nb9hYbOk+idgQ6qfY4qigMY4lKRZyDKkcaQ1E0mw="), + PresharedKey: mustParseKey(t, "StqYgoU+2ckQvK4AVVQYHQA5GJvj/KbMRbQM6P62I1A="), + Endpoint: mustParseUDPAddr(t, "5.124.60.6:59688"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.67/32", "fdd0:438e:19ba:5069::43/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679622348), + ReceiveBytes: 38488680, + TransmitBytes: 694107612, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "Zc7k5xA70Nfbo2nLJziFFBVY/2OZu5HTsGLQH+KUBRU="), + PresharedKey: mustParseKey(t, "bMO4+pL4mnQjLpPSgXNDUUMhjtXmQAyHqc1sZadb+S0="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.68/32", "fdd0:438e:19ba:5069::44/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "t4LWLF/J7BpPtS8p0WX8+oGpQoIO31eE2Uwm2uCoOBo="), + PresharedKey: mustParseKey(t, "OpTNAEpQHp+C0SM6AMSr3/keabLhdq3vI9KWZNU8Htc="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.69/32", "fdd0:438e:19ba:5069::45/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "KgIUwSPETcpI5f1tL6Zcsvvpr/sCbaCVVXeWgdlupAM="), + PresharedKey: mustParseKey(t, "RU+OfbsJnOqzLeQkwmdrMYgmkv43XKJ52bhsYK7yXdU="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.70/32", "fdd0:438e:19ba:5069::46/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "Ik8ivyv+wRAagtGRJP/kjBIfj8Gp0HKH4YW1c886THw="), + PresharedKey: mustParseKey(t, "tSWKQWD3632XTURXkqRLt18iTz2RxK2L617S04b08Mg="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.71/32", "fdd0:438e:19ba:5069::47/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "9chK4Nm6kokH7DKLBkwDtiu1ohUi2IKjHvUQ7ryYilQ="), + PresharedKey: mustParseKey(t, "8qCqx2Jc8b41bqJgsUbceIFPZK+tiLscjtDoE7wQ7WA="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.72/32", "fdd0:438e:19ba:5069::48/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "3uXe1350QPqYkCFti5P1p8QNb9JG5Q1aiMWTvCqSai0="), + PresharedKey: mustParseKey(t, "OnFaqQUryKomwWEUU4YNxMshiZTY006VzS/8HSahEaE="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.73/32", "fdd0:438e:19ba:5069::49/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "htNim8Y//7BuWZcMW9RmEDl4jrQrfx3/l4GOmp6vLk4="), + PresharedKey: mustParseKey(t, "eW+z9QOsTGzrIQG8mnqUw54j0C5mWkY7LqgvwIbvkDM="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.74/32", "fdd0:438e:19ba:5069::4a/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "4rBI2mkO/tXLLbAgerKzEllf5AnmKQO0Yaz9NZBnwE4="), + PresharedKey: mustParseKey(t, "Xw1IoyiR1q9g9jgkgRR+0AgDQbYnjCuOfxjz/b4uLdQ="), + Endpoint: mustParseUDPAddr(t, "2.181.107.248:10574"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.76/32", "fdd0:438e:19ba:5069::4c/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679620078), + ReceiveBytes: 60474420, + TransmitBytes: 1988225048, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "qvbdv+lPHuGDWUjrojSJSIHY/tjg8e3N3NTFAPp7zDY="), + PresharedKey: mustParseKey(t, "RktzPDRjLXNdytEPSXy0cVme2K2u0GPzzVU8SbzXrDU="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.78/32", "fdd0:438e:19ba:5069::4e/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "QZKGqgNRF2cS8g5qS3wcx+/CUpMQOCjAqsOKRaoiNS4="), + PresharedKey: mustParseKey(t, "kjZdEwHD4eV8je1EdypnRTs1ipZxiseYQe2cpE/SzdM="), + Endpoint: mustParseUDPAddr(t, "5.125.118.44:7585"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.79/32", "fdd0:438e:19ba:5069::4f/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679612230), + ReceiveBytes: 58485180, + TransmitBytes: 549983952, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "tcaDGgjy0/MHmnNaBpYV10vEb+HIE8A66y1q63XtKD8="), + PresharedKey: mustParseKey(t, "qxXReWqCnJOfEreDRSiggrUBAsvSL8wEgnZPiV4Lmhk="), + Endpoint: mustParseUDPAddr(t, "5.125.241.99:14167"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.80/32", "fdd0:438e:19ba:5069::50/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679621917), + ReceiveBytes: 91208368, + TransmitBytes: 1594288628, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "SxSm/S+Sr/VSptW0O2WTCRLjz6XUFvC6KvAWdHwjMB4="), + PresharedKey: mustParseKey(t, "Es9JNg13wuC5YvEchEvT6bS281ozKM8nzdnUu3XBOkU="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.81/32", "fdd0:438e:19ba:5069::51/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "XWMRIDUA0SjDF6pUFPOpa4ffgkCW2luMGGnK2nuL3X8="), + PresharedKey: mustParseKey(t, "ZLU0MTM7of2f244My92DY8LfCLyg0BMQQSa+992NlZM="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.82/32", "fdd0:438e:19ba:5069::52/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "Ol/ET8HoIOqK6DvisCbPDUS2WWXlBarKmpZ0y+dsCys="), + PresharedKey: mustParseKey(t, "fh8U59aCSPtj7+rXT87hbnQjwRiRKpip0DIfXEFFL9I="), + Endpoint: mustParseUDPAddr(t, "83.123.96.52:49644"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.83/32", "fdd0:438e:19ba:5069::53/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679602712), + ReceiveBytes: 57556000, + TransmitBytes: 864674044, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "9llEVDcvyhKrsKUoKkfXJuigLBcRl4U/tPtcUfVMdyU="), + PresharedKey: mustParseKey(t, "PfZX2T/KiVC2fRiZ+JzvXUppa50UQ/6mZiA8W27mIgg="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.84/32", "fdd0:438e:19ba:5069::54/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "L7Cmw+UIFKbT/ATkhQjPTrZzp4yphHuv+0oYW9EZdHk="), + PresharedKey: mustParseKey(t, "/eM2JPyOP5rEejZYP8iXZesrnlkD7S5Gc9MQuW9Lit8="), + Endpoint: mustParseUDPAddr(t, "5.124.231.78:17780"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.85/32", "fdd0:438e:19ba:5069::55/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679622312), + ReceiveBytes: 96118376, + TransmitBytes: 2048227908, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "HQGRHBO8qFMwRs6R4VIcM7zrNRJH/hO19vFu3gWVyk8="), + PresharedKey: mustParseKey(t, "d+O833BjLsxc2WwBmGsdcB8ogDtBYGaXn57xTrJGaAw="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.86/32", "fdd0:438e:19ba:5069::56/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "sPgKlh9EnLc1UKG93ibfpIAovwpn8YjYlOBbQT/sjSM="), + PresharedKey: mustParseKey(t, "6qsQOM1KgdWLrFOY5u6QPep8TfUHcInsc3jv6FyIuGE="), + Endpoint: mustParseUDPAddr(t, "5.126.94.232:59749"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.87/32", "fdd0:438e:19ba:5069::57/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679609810), + ReceiveBytes: 46650644, + TransmitBytes: 320625632, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "+cOve1HTMMq42V4HBb3MIIH4paGdmBT2AJ/ejV0ZcH4="), + PresharedKey: mustParseKey(t, "NWmkGO1bQJ7q894n24JG+PMv9Wq9sHJ63vocV7LAIdo="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.88/32", "fdd0:438e:19ba:5069::58/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "4ATnN6sLBFrmnFbEDDGLdaxtokPX3kSJ4dI+NWDGozA="), + PresharedKey: mustParseKey(t, "WVlJrNwx76dcr4SH94ircmCR/LfPGRdNwG2n3Mvq2CE="), + Endpoint: mustParseUDPAddr(t, "5.125.220.175:17516"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.89/32", "fdd0:438e:19ba:5069::59/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679608542), + ReceiveBytes: 95543172, + TransmitBytes: 328747492, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "k//CV10qBAipId1soU4e8+J+lsBY47+g9oeuyYQf1UQ="), + PresharedKey: mustParseKey(t, "WxEXQe2/HyV+mAXKzOzUFBBLRrbXRvCY7mNKYRvAcos="), + Endpoint: mustParseUDPAddr(t, "83.123.33.104:58065"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.90/32", "fdd0:438e:19ba:5069::5a/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679601734), + ReceiveBytes: 17100408, + TransmitBytes: 193644292, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "a5H1GKKO+9dYr1EjvI+bMKSsNfQz9ghO22oBLbCV1W8="), + PresharedKey: mustParseKey(t, "St9drSEDjTXMkBRFPVYXDOa6vmJ4f7dtM5A6zCbaF9M="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.91/32", "fdd0:438e:19ba:5069::5b/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "dZd8wgMH+AdaYiAbb/emUsieNYSu3VLh+R5I8FGmCCE="), + PresharedKey: mustParseKey(t, "Wf8tdgYDJ6R6BsyAptSwtoPDgdaFykexjPRdX/7TZJQ="), + Endpoint: mustParseUDPAddr(t, "172.80.252.9:40934"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.92/32", "fdd0:438e:19ba:5069::5c/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679607825), + ReceiveBytes: 38538968, + TransmitBytes: 479650036, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "WSxAe8GiFWTRwA+NZXYHG2fy5CVSCyt6H00iE4gb/QM="), + PresharedKey: mustParseKey(t, "R1wPt+WT/66PR3zGv59EjbCvwDMLQvTXEAuhFEh86Y4="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.93/32", "fdd0:438e:19ba:5069::5d/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "vf/YD28YgyaitJiGyOFw9P9dEj3m4XI7NBEhmsQtuS4="), + PresharedKey: mustParseKey(t, "OMKwZuXGTrTPmIIBkA9leNTXiceeTBXYtmM3E2dDHFM="), + Endpoint: mustParseUDPAddr(t, "83.122.177.202:63738"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.94/32", "fdd0:438e:19ba:5069::5e/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679622119), + ReceiveBytes: 214899792, + TransmitBytes: 403954636, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "Fgyh9YI3H0BMXpIwuzGsPNuqZBpNX6MHjL9bn2Ao2mc="), + PresharedKey: mustParseKey(t, "HPk6anM0vNx7NBle4LqoJip2yMd8RmQzJMDyG7gBOtU="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.95/32", "fdd0:438e:19ba:5069::5f/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "S/VgSky31TeNufxjySvoX71kKhJ6c20gTx3qedTa7Hc="), + PresharedKey: mustParseKey(t, "Ad/h2P5vN2A5D3irqe+a6OoZp9+feGkxdsbZv06z0J4="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.96/32", "fdd0:438e:19ba:5069::60/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "UM0khRt0dMUguG+S/mPoqY0QlGnPpFWtpPo6z7BGQWc="), + PresharedKey: mustParseKey(t, "GjqKzSvi+1M86YTuZuDwjZ/kN4UHk4m1JqNkbRUpmGk="), + Endpoint: mustParseUDPAddr(t, "95.162.238.226:15423"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.99/32", "fdd0:438e:19ba:5069::63/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679622287), + ReceiveBytes: 200415180, + TransmitBytes: 1479932240, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "oxN6Cn4EPglTKBCwgyTiHZ1bU540zD/h8li1laXCbj0="), + PresharedKey: mustParseKey(t, "eS7dvTHZF5T60vF1AQ8S/u8jauj6uVz+HMEFOfK85zs="), + Endpoint: mustParseUDPAddr(t, "5.126.57.129:51519"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.100/32", "fdd0:438e:19ba:5069::64/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679613535), + ReceiveBytes: 61072720, + TransmitBytes: 1065353052, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "4xcn/eRHubjW9Nytc0AwoN6WSRaTKtjPesz/7ImuCzA="), + PresharedKey: mustParseKey(t, "yhWvanfIx4spsiCXH5HT27rvlmL6fjjm2VgxQ4V7/m4="), + Endpoint: mustParseUDPAddr(t, "37.129.11.127:32809"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.101/32", "fdd0:438e:19ba:5069::65/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679619234), + ReceiveBytes: 27569848, + TransmitBytes: 539436024, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "nkAGr/kNARuXwSHSKCm7XdpMhrimPLzeWHcsKJcko1g="), + PresharedKey: mustParseKey(t, "NPT8zU1f+zm9OTeIODh7ba9JGOoOmpE2un6OPBYz5Xw="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.102/32", "fdd0:438e:19ba:5069::66/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "zMsFGX2HA5AVK2NzOjPXZz0tL6cb70Uwt41YxLEogTA="), + PresharedKey: mustParseKey(t, "Xx9kbd2Vtzm6INWM4irctZjdCA3vXwPJfEkj4DpCX9Y="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.103/32", "fdd0:438e:19ba:5069::67/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "WhIPaOV1QuE2Wqgf3kKIzAS/W9rX81cBwFbMFATv7Sg="), + PresharedKey: mustParseKey(t, "R01ZFMDTCvB2n+2YCySVowy8v0kXAxSScUi1/ELuVok="), + Endpoint: mustParseUDPAddr(t, "5.119.170.37:61162"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.104/32", "fdd0:438e:19ba:5069::68/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679617644), + ReceiveBytes: 21586800, + TransmitBytes: 286196660, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "tCXkXzYf1FXFXNOUP7J0gGEbLYa2+OtmN3xGMjHvn2Y="), + PresharedKey: mustParseKey(t, "02znarJ3rTH03+7ZGMORYkfWtMNpDUXv/39be7QQxBY="), + Endpoint: mustParseUDPAddr(t, "5.114.158.124:5085"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.105/32", "fdd0:438e:19ba:5069::69/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679610971), + ReceiveBytes: 92314284, + TransmitBytes: 1539250464, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "pyoJ/0UqULHxtXDVbVJTQsOxj0bkrhcxv5QbiAo/2yA="), + PresharedKey: mustParseKey(t, "oE5g05yXJhiY19WMCyorKURphqG7/0vfIYTu21bqdGU="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.106/32", "fdd0:438e:19ba:5069::6a/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "2VWoryzJr403WxW/Q0g7d+akXniBOdH7xMzSBkBz4gk="), + PresharedKey: mustParseKey(t, "iO61JNmmi4BdL2MWwpIsVBzgO/YuEOXn/5VhIOGQTfE="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.107/32", "fdd0:438e:19ba:5069::6b/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "NehSfEfBgEIMmMWKJc6dNe+Kk+eowsokeUTJlioPOWM="), + PresharedKey: mustParseKey(t, "h/76X+Cuwo123DQETPWEDqVTZwiZXHM0zCS9b000WaM="), + Endpoint: mustParseUDPAddr(t, "83.122.28.199:49330"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.108/32", "fdd0:438e:19ba:5069::6c/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679599088), + ReceiveBytes: 28062836, + TransmitBytes: 626552728, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "1wgfj6gR/fhrfW909Ejnu9xPS+gCMDhvqCQFzH8tP0Y="), + PresharedKey: mustParseKey(t, "Eqekgl1wqY4Gkf8VaApuXVw0354BWbCelcpZv9WzYHc="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.109/32", "fdd0:438e:19ba:5069::6d/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "WtNgBST4LflxP2IR/oaNkULvQ5909BhZaizrtODXFQs="), + PresharedKey: mustParseKey(t, "zoR7N5mGIR1/A9PNC08DK2eOFHCBdG+SapPNyXyNGIo="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.110/32", "fdd0:438e:19ba:5069::6e/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "kPuHjkT58MsNP55oQFJszXhpuN6ukG++WUDsR4DJQjo="), + PresharedKey: mustParseKey(t, "G4Z57Ihjk3Et+O68iFj8I3CT2SgbjMDnpa+/lFuH6gU="), + Endpoint: nil, + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.111/32", "fdd0:438e:19ba:5069::6f/128"), + LastHandshakeTime: time.Time{}, + ReceiveBytes: 0, + TransmitBytes: 0, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "4ejeXAAOX6UrBnaAivqQ11JSanLzXxBpVd+A1Aozl3A="), + PresharedKey: mustParseKey(t, "77EuIYC1+S1i4LP5j8TibwL8didMy+mI+5bOfqKC+sA="), + Endpoint: mustParseUDPAddr(t, "5.232.204.179:10610"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.112/32", "fdd0:438e:19ba:5069::70/128"), + LastHandshakeTime: mustParseUnixTime(t, 1679607401), + ReceiveBytes: 20695096, + TransmitBytes: 349602820, + PersistentKeepaliveInterval: 0, + }, + { + PublicKey: mustParseKey(t, "RFrjTKnZWr/CmI/XC+zgPeGJzbDGQhA9eOBb+ge90wA="), + PresharedKey: mustParseKey(t, "uox3nW4W81djWoJEN/lgdrvjqtLnl36lhkJZcbSkwOM="), + Endpoint: mustParseUDPAddr(t, "5.232.204.179:10611"), + AllowedIPs: mustParseAllowedIPs(t, "10.0.0.113/32", "fdd0:438e:19ba:5069::71/128"), + LastHandshakeTime: mustParseUnixTime(t, 1672307401), + ReceiveBytes: 10695096, + TransmitBytes: 349602820, + PersistentKeepaliveInterval: 10, + }, + }, peers) +} + +func TestParseInvalidDumpFile(t *testing.T) { + scanner := bufio.NewScanner(bytes.NewBuffer(rawInvalidDump)) + for scanner.Scan() { + peers, err := dump.Parse(bytes.NewBuffer(scanner.Bytes())) + require.Nil(t, peers) + require.NotNil(t, err) + } +} diff --git a/pkg/dump/testdata/dump-invalid.txt b/pkg/dump/testdata/dump-invalid.txt new file mode 100644 index 0000000..484fb76 --- /dev/null +++ b/pkg/dump/testdata/dump-invalid.txt @@ -0,0 +1,17 @@ +yLk+kQwd2Zm4I6f6Aeg/OGlT3GLtHsNmY+kUXK2sfD0= QMXzIPq+o5zNvecAIQZn61rB9SmolhW0fgj7Zwylzvg= (none) 10.0.0.19/32,fdd0:438e:19ba:5069::13/128 0 0 0 offs +XuyQmI/KXTBvwdpfjUzULj4PfBW15mrMPXm8pS1OnhQ= kT0tuNL/rhPoWjWiQhSCHFJuaSKLxlqg2aAuxwwpz4I= (none) 10.0.0.20/32,fdd0:438e:19ba:5069::14/128 -8 0 0 off +UDrZG/thxkzPbow3Lr1gGHYeKbNlQF8Znob9hujRYjo= fkKqj8RmLjHbo8T63OtLn6JM8MnCIp+LZLpvxiIl2HU= 83.123abc.9.201:42944 10.0.0.21/32,fdd0:438e:19ba:5069::15/128 1679600629 7138444 103247936 off +qwQBlkUITgLsddSi9kbKM14HCkvqmVYi1VT/8n9u6kY= MFToe6QumdelJu3whvgSDniVNM+jmuTNMXP9FY07F8g= - 10.0.0.29/32,fdd0:438e:19ba:5069::1d/128 0 0 0 off +5jNQhl3WZJBOI4+M5e4Pk7+9SCMSg6GyG+Dkc+wpCVA= l7itiN0Pr0fQMD7Ae8EMpDHmQ0rL-4KyTNuL/5J9+O6o= (none) 10.0.0.30/32,fdd0:438e:19ba:5069::1e/128 0 0 0 off +OafCe4tLL8rShnOiQod35G+NdDPQh-wIN4JKRoyKbNkI= mJBHO390BWD3GxHMIdWFey70N7c6x5EDrVuzcROh/Ao= 5.125.49.118:32738 10.0.0.41/32,fdd0:438e:19ba:5069::29/128 1679608693 69519144 475482968 off +R86vOf/VdN9m0Ey4AD80D4i5UdTefMsKJw/+BuOSeS4= +V9iBlvEB7FCZcUMcY40c1FazbleACJY/qButRjkUUg= 95.38.119.159:524919 10.0.0.42/32,fdd0:438e:19ba:5069::2a/128 1679597842 8385684 188015208 off +FaibxGztHHy3pm0kiiBfI6GMM6UpdiD6Kinekh0ARDk= 8R5QEYDbJw/VYsNw62LX9p+RGtn3NKeVyDeCol1zzT8= 188.229.11.180:62680 1679610586 91786228 109298580 off +gOZgWvD4pVpGNx8ZBJOrEFbUvdOmIqyJ5xaWkb0QkwY= 4fpiAvyjMmUPGDsi5IV7EPWmq9ANOCsuBU81s1KKvwg= 5.123.27.254:10852 10.0.0.444/32,fdd0:438e:19ba:5069::2c/128 1679622245 38969328 500779248 off +nRhGvfiDIdUts1yJn4Bt2QVURBK73ks++SrqdseUZjA= DK+KyyjvtAHARlSkvH6ogNYeCb//GSVfp/SaYGCkTvc= 83.122.114.201:53992 10.0.0.45/32,fdd0:438e:19ba:g069::2d/128 1679614805 49627932 1698468224 off +cM6cjxjXEfbV/MmdIaqnI3iG6NX0eTGrxi2xdr8+XlA= yHHspi9idt69QY3EawOQrEVdi2kMQ4CdW4jadVHDWHo= 5.126.154.201:32527 10.0.0.46/32,fdd0:438e:19ba:5069::2e/129 1679622324 41659912 387348224 off +gsqgOUyV3wl2QjGemvO73O1MD9PB/umq6lH18Px/1RU= +NjgCZQ3224mpbDZUgIj24mzX6NcovotLuDOqjTHLw8= 5.125.223.142:36784 10.0.0.47/32,fdd0:438e:19ba:5069::2f/128 1679611750q 60529560 1421720032 off +mYDlBMbMZKEjcX8nTjYWmOGQlguyLomQVm+q9yc9eAM= MXSRqHNEMM48WGXqyUmCC2YxsueGZvyxIW8UnwFibt0= (none) 10.0.0.49/32,fdd0:438e:19ba:5069::31/128 0 - 0 off +P7wDzaPcNHAfR2MVuQV//qD+YfXHR3Ad/7uLm61xUXA= VuYrRbWESPD7kFpXdSs6BJ7fMppBM4IBxOURx/lYv9Q= (none) 10.0.0.50/32,fdd0:438e:19ba:5069::32/128 0 0 x off +rauZLam+acOP+/mC0C/vAjIweVoWZNQ0jK5iBmpAT0w= dQQA+bYhLgtXwnPug9rQVok1R+ud9RRAqKgo04K/ljU= 5.119.75.31:47338 10.0.0.51/32,fdd0:438e:19ba:5069::33/128 1679622318 49183124 946856032 -9 +xwlGtnl3nmV+uR+nN8No7mMQiv1OTTOKMdgrwaDBMFk= NKeDh2sJkweEbC7ZspA5fPBL+9ahk/eqqKFcl0IemBg= 37.129.244.184:43698 10.0.0.52/32,fdd0:438e:19ba:5069::34/128 1679613152 57819876 687229524 +xwlGtnl3nmV+uR+nN8No7mMQiv1OTTOKMdgrwaDBMFk= NKeDh2sJkweEbC7ZspA5fPBL+9ahk/eqqKFcl0IemBg= 37.129.244.184:43698 10.0.0.52/32,fdd0:438e:19ba:5069::34/128 1679613152 57819876 687229524 off 123 \ No newline at end of file diff --git a/pkg/dump/testdata/dump.txt b/pkg/dump/testdata/dump.txt new file mode 100644 index 0000000..6ad7ef2 --- /dev/null +++ b/pkg/dump/testdata/dump.txt @@ -0,0 +1,70 @@ +yLk+kQwd2Zm4I6f6Aeg/OGlT3GLtHsNmY+kUXK2sfD0= QMXzIPq+o5zNvecAIQZn61rB9SmolhW0fgj7Zwylzvg= (none) 10.0.0.19/32,fdd0:438e:19ba:5069::13/128 0 0 0 off +XuyQmI/KXTBvwdpfjUzULj4PfBW15mrMPXm8pS1OnhQ= kT0tuNL/rhPoWjWiQhSCHFJuaSKLxlqg2aAuxwwpz4I= (none) 10.0.0.20/32,fdd0:438e:19ba:5069::14/128 0 0 0 off +UDrZG/thxkzPbow3Lr1gGHYeKbNlQF8Znob9hujRYjo= fkKqj8RmLjHbo8T63OtLn6JM8MnCIp+LZLpvxiIl2HU= 83.123.9.201:42944 10.0.0.21/32,fdd0:438e:19ba:5069::15/128 1679600629 7138444 103247936 off +qwQBlkUITgLsddSi9kbKM14HCkvqmVYi1VT/8n9u6kY= MFToe6QumdelJu3whvgSDniVNM+jmuTNMXP9FY07F8g= (none) 10.0.0.29/32,fdd0:438e:19ba:5069::1d/128 0 0 0 off +5jNQhl3WZJBOI4+M5e4Pk7+9SCMSg6GyG+Dkc+wpCVA= l7itiN0Pr0fQMD7Ae8EMpDHmQ0rL4KyTNuL/5J9+O6o= (none) 10.0.0.30/32,fdd0:438e:19ba:5069::1e/128 0 0 0 off +OafCe4tLL8rShnOiQod35G+NdDPQhwIN4JKRoyKbNkI= mJBHO390BWD3GxHMIdWFey70N7c6x5EDrVuzcROh/Ao= 5.125.49.118:32738 10.0.0.41/32,fdd0:438e:19ba:5069::29/128 1679608693 69519144 475482968 off +R86vOf/VdN9m0Ey4AD80D4i5UdTefMsKJw/+BuOSeS4= +V9iBlvEB7FCZcUMcY40c1FazbleACJY/qButRjkUUg= 95.38.119.159:52491 10.0.0.42/32,fdd0:438e:19ba:5069::2a/128 1679597842 8385684 188015208 off +FaibxGztHHy3pm0kiiBfI6GMM6UpdiD6Kinekh0ARDk= 8R5QEYDbJw/VYsNw62LX9p+RGtn3NKeVyDeCol1zzT8= 188.229.11.180:62680 10.0.0.43/32,fdd0:438e:19ba:5069::2b/128 1679610586 91786228 109298580 off +gOZgWvD4pVpGNx8ZBJOrEFbUvdOmIqyJ5xaWkb0QkwY= 4fpiAvyjMmUPGDsi5IV7EPWmq9ANOCsuBU81s1KKvwg= 5.123.27.254:10852 10.0.0.44/32,fdd0:438e:19ba:5069::2c/128 1679622245 38969328 500779248 off +nRhGvfiDIdUts1yJn4Bt2QVURBK73ks++SrqdseUZjA= DK+KyyjvtAHARlSkvH6ogNYeCb//GSVfp/SaYGCkTvc= 83.122.114.201:53992 10.0.0.45/32,fdd0:438e:19ba:5069::2d/128 1679614805 49627932 1698468224 off +cM6cjxjXEfbV/MmdIaqnI3iG6NX0eTGrxi2xdr8+XlA= yHHspi9idt69QY3EawOQrEVdi2kMQ4CdW4jadVHDWHo= 5.126.154.201:32527 10.0.0.46/32,fdd0:438e:19ba:5069::2e/128 1679622324 41659912 387348224 off +gsqgOUyV3wl2QjGemvO73O1MD9PB/umq6lH18Px/1RU= +NjgCZQ3224mpbDZUgIj24mzX6NcovotLuDOqjTHLw8= 5.125.223.142:36784 10.0.0.47/32,fdd0:438e:19ba:5069::2f/128 1679611750 60529560 1421720032 off +mZNGrAccdCJ9a/7UOevRHKxjxOy7fPvgW3ev+bcROWk= /Km1HGiYK+O/jJJJWdPZIEC6pNBzsghhYm9hsc61xFg= (none) 10.0.0.48/32,fdd0:438e:19ba:5069::30/128 0 0 0 off +mYDlBMbMZKEjcX8nTjYWmOGQlguyLomQVm+q9yc9eAM= MXSRqHNEMM48WGXqyUmCC2YxsueGZvyxIW8UnwFibt0= (none) 10.0.0.49/32,fdd0:438e:19ba:5069::31/128 0 0 0 off +P7wDzaPcNHAfR2MVuQV//qD+YfXHR3Ad/7uLm61xUXA= VuYrRbWESPD7kFpXdSs6BJ7fMppBM4IBxOURx/lYv9Q= (none) 10.0.0.50/32,fdd0:438e:19ba:5069::32/128 0 0 0 off +rauZLam+acOP+/mC0C/vAjIweVoWZNQ0jK5iBmpAT0w= dQQA+bYhLgtXwnPug9rQVok1R+ud9RRAqKgo04K/ljU= 5.119.75.31:47338 10.0.0.51/32,fdd0:438e:19ba:5069::33/128 1679622318 49183124 946856032 off +xwlGtnl3nmV+uR+nN8No7mMQiv1OTTOKMdgrwaDBMFk= NKeDh2sJkweEbC7ZspA5fPBL+9ahk/eqqKFcl0IemBg= 37.129.244.184:43698 10.0.0.52/32,fdd0:438e:19ba:5069::34/128 1679613152 57819876 687229524 off +VF3fBaIiqEAlPdYWQO/XNy1cXxAzJOnHRWgJh4knglw= SBX4QINGYs7wCF0GCIyqt1doc50FXgbQ5CYQz2X0YZk= 5.115.179.111:36227 10.0.0.54/32,fdd0:438e:19ba:5069::36/128 1679622359 147183980 1465251556 off +4ZhiIszWe4sPsnCf+bHDs+O2pM+SSDI92urE9gO1AyY= oxOYXuZsIZ1cvmN/r61l63XG7I4/ZeqJQk1Eizmslps= 5.125.254.72:63259 10.0.0.55/32,fdd0:438e:19ba:5069::37/128 1679604971 30436016 800796928 off +dwwb9pB9b43e2mDF1OAF4ViKbRy8MhNog2/5zfsbHxM= T+uhSXH45y+SAB/74swecdmKwbzoQkAk3QubTCQU4Xs= 5.126.41.5:17955 10.0.0.56/32,fdd0:438e:19ba:5069::38/128 1679622289 193450932 1615951448 off +Sgz2K9TQGaDPPuMPgjdPnlrKCutJqUPkf6GrO9FdAgQ= aFkkiUHklssOZ0oCGpG+Oj4Oj/P0FWf5Hei8GKRlCGc= 5.124.107.243:18896 10.0.0.57/32,fdd0:438e:19ba:5069::39/128 1679621178 47751636 323048336 off +g0KUtRZ0tJG6YfDgVzfoY6pkmbe1fkeqsp8qu0CrjFk= jg9Hor8qi3AOkBeRDV51l19Z0UQU+Ytrcap7Zb9v/9c= (none) 10.0.0.59/32,fdd0:438e:19ba:5069::3b/128 0 0 0 off +sfIcIpMqJ1+RL34PDS56Y4m2f4P5Qj+KzhiWtJCjDVs= DMcD4y+fKUyBEXW/DzZFBcr28hpcd2GSqMZQvg7Fmiw= (none) 10.0.0.60/32,fdd0:438e:19ba:5069::3c/128 0 0 0 off +yNnsydGMEqEa3M3pi+cXtuWHlHKP7s5NMGReU+Jt81Q= ZyJ35QWf+SKrnsLjHEKCUECLH6VPbwn5p+av3bFjJLE= 5.125.201.34:11978 10.0.0.61/32,fdd0:438e:19ba:5069::3d/128 1679608317 9238972 141261976 off +bpvh8xyCKBZ5wjQRTKI7UxAWAFYXscV2ffsDhWyB+Ek= 1ZWVE8w0bk6q6Md4jhNmp8kDaadN+QBZZ8pZSZd/JjY= 37.129.171.116:54772 10.0.0.62/32,fdd0:438e:19ba:5069::3e/128 1679602156 25262308 758835208 off +YESGUd6GzN4sldhXn80Nz3UeX/Tv1nANyTW98gHQTEY= tXKZHknajC+JeytG8BIPjyir0D+fyctLz9CVG7FwZGU= 5.232.199.164:18016 10.0.0.64/32,fdd0:438e:19ba:5069::40/128 1679609091 53371808 828476932 off +pw8kkrzQ762vv3fp3z0DSMpNtmLwRYj0m/iyzVMaYXM= m3CxYl7p2snW00B+VIx/vLDG/le93DwfWMPnPC9EpL4= 83.122.106.35:33994 10.0.0.65/32,fdd0:438e:19ba:5069::41/128 1679620210 54243332 1026011872 off +538nb9hYbOk+idgQ6qfY4qigMY4lKRZyDKkcaQ1E0mw= StqYgoU+2ckQvK4AVVQYHQA5GJvj/KbMRbQM6P62I1A= 5.124.60.6:59688 10.0.0.67/32,fdd0:438e:19ba:5069::43/128 1679622348 38488680 694107612 off +Zc7k5xA70Nfbo2nLJziFFBVY/2OZu5HTsGLQH+KUBRU= bMO4+pL4mnQjLpPSgXNDUUMhjtXmQAyHqc1sZadb+S0= (none) 10.0.0.68/32,fdd0:438e:19ba:5069::44/128 0 0 0 off +t4LWLF/J7BpPtS8p0WX8+oGpQoIO31eE2Uwm2uCoOBo= OpTNAEpQHp+C0SM6AMSr3/keabLhdq3vI9KWZNU8Htc= (none) 10.0.0.69/32,fdd0:438e:19ba:5069::45/128 0 0 0 off +KgIUwSPETcpI5f1tL6Zcsvvpr/sCbaCVVXeWgdlupAM= RU+OfbsJnOqzLeQkwmdrMYgmkv43XKJ52bhsYK7yXdU= (none) 10.0.0.70/32,fdd0:438e:19ba:5069::46/128 0 0 0 off +Ik8ivyv+wRAagtGRJP/kjBIfj8Gp0HKH4YW1c886THw= tSWKQWD3632XTURXkqRLt18iTz2RxK2L617S04b08Mg= (none) 10.0.0.71/32,fdd0:438e:19ba:5069::47/128 0 0 0 off +9chK4Nm6kokH7DKLBkwDtiu1ohUi2IKjHvUQ7ryYilQ= 8qCqx2Jc8b41bqJgsUbceIFPZK+tiLscjtDoE7wQ7WA= (none) 10.0.0.72/32,fdd0:438e:19ba:5069::48/128 0 0 0 off +3uXe1350QPqYkCFti5P1p8QNb9JG5Q1aiMWTvCqSai0= OnFaqQUryKomwWEUU4YNxMshiZTY006VzS/8HSahEaE= (none) 10.0.0.73/32,fdd0:438e:19ba:5069::49/128 0 0 0 off +htNim8Y//7BuWZcMW9RmEDl4jrQrfx3/l4GOmp6vLk4= eW+z9QOsTGzrIQG8mnqUw54j0C5mWkY7LqgvwIbvkDM= (none) 10.0.0.74/32,fdd0:438e:19ba:5069::4a/128 0 0 0 off +4rBI2mkO/tXLLbAgerKzEllf5AnmKQO0Yaz9NZBnwE4= Xw1IoyiR1q9g9jgkgRR+0AgDQbYnjCuOfxjz/b4uLdQ= 2.181.107.248:10574 10.0.0.76/32,fdd0:438e:19ba:5069::4c/128 1679620078 60474420 1988225048 off +qvbdv+lPHuGDWUjrojSJSIHY/tjg8e3N3NTFAPp7zDY= RktzPDRjLXNdytEPSXy0cVme2K2u0GPzzVU8SbzXrDU= (none) 10.0.0.78/32,fdd0:438e:19ba:5069::4e/128 0 0 0 off +QZKGqgNRF2cS8g5qS3wcx+/CUpMQOCjAqsOKRaoiNS4= kjZdEwHD4eV8je1EdypnRTs1ipZxiseYQe2cpE/SzdM= 5.125.118.44:7585 10.0.0.79/32,fdd0:438e:19ba:5069::4f/128 1679612230 58485180 549983952 off +tcaDGgjy0/MHmnNaBpYV10vEb+HIE8A66y1q63XtKD8= qxXReWqCnJOfEreDRSiggrUBAsvSL8wEgnZPiV4Lmhk= 5.125.241.99:14167 10.0.0.80/32,fdd0:438e:19ba:5069::50/128 1679621917 91208368 1594288628 off +SxSm/S+Sr/VSptW0O2WTCRLjz6XUFvC6KvAWdHwjMB4= Es9JNg13wuC5YvEchEvT6bS281ozKM8nzdnUu3XBOkU= (none) 10.0.0.81/32,fdd0:438e:19ba:5069::51/128 0 0 0 off +XWMRIDUA0SjDF6pUFPOpa4ffgkCW2luMGGnK2nuL3X8= ZLU0MTM7of2f244My92DY8LfCLyg0BMQQSa+992NlZM= (none) 10.0.0.82/32,fdd0:438e:19ba:5069::52/128 0 0 0 off +Ol/ET8HoIOqK6DvisCbPDUS2WWXlBarKmpZ0y+dsCys= fh8U59aCSPtj7+rXT87hbnQjwRiRKpip0DIfXEFFL9I= 83.123.96.52:49644 10.0.0.83/32,fdd0:438e:19ba:5069::53/128 1679602712 57556000 864674044 off +9llEVDcvyhKrsKUoKkfXJuigLBcRl4U/tPtcUfVMdyU= PfZX2T/KiVC2fRiZ+JzvXUppa50UQ/6mZiA8W27mIgg= (none) 10.0.0.84/32,fdd0:438e:19ba:5069::54/128 0 0 0 off +L7Cmw+UIFKbT/ATkhQjPTrZzp4yphHuv+0oYW9EZdHk= /eM2JPyOP5rEejZYP8iXZesrnlkD7S5Gc9MQuW9Lit8= 5.124.231.78:17780 10.0.0.85/32,fdd0:438e:19ba:5069::55/128 1679622312 96118376 2048227908 off +HQGRHBO8qFMwRs6R4VIcM7zrNRJH/hO19vFu3gWVyk8= d+O833BjLsxc2WwBmGsdcB8ogDtBYGaXn57xTrJGaAw= (none) 10.0.0.86/32,fdd0:438e:19ba:5069::56/128 0 0 0 off +sPgKlh9EnLc1UKG93ibfpIAovwpn8YjYlOBbQT/sjSM= 6qsQOM1KgdWLrFOY5u6QPep8TfUHcInsc3jv6FyIuGE= 5.126.94.232:59749 10.0.0.87/32,fdd0:438e:19ba:5069::57/128 1679609810 46650644 320625632 off ++cOve1HTMMq42V4HBb3MIIH4paGdmBT2AJ/ejV0ZcH4= NWmkGO1bQJ7q894n24JG+PMv9Wq9sHJ63vocV7LAIdo= (none) 10.0.0.88/32,fdd0:438e:19ba:5069::58/128 0 0 0 off +4ATnN6sLBFrmnFbEDDGLdaxtokPX3kSJ4dI+NWDGozA= WVlJrNwx76dcr4SH94ircmCR/LfPGRdNwG2n3Mvq2CE= 5.125.220.175:17516 10.0.0.89/32,fdd0:438e:19ba:5069::59/128 1679608542 95543172 328747492 off +k//CV10qBAipId1soU4e8+J+lsBY47+g9oeuyYQf1UQ= WxEXQe2/HyV+mAXKzOzUFBBLRrbXRvCY7mNKYRvAcos= 83.123.33.104:58065 10.0.0.90/32,fdd0:438e:19ba:5069::5a/128 1679601734 17100408 193644292 off +a5H1GKKO+9dYr1EjvI+bMKSsNfQz9ghO22oBLbCV1W8= St9drSEDjTXMkBRFPVYXDOa6vmJ4f7dtM5A6zCbaF9M= (none) 10.0.0.91/32,fdd0:438e:19ba:5069::5b/128 0 0 0 off +dZd8wgMH+AdaYiAbb/emUsieNYSu3VLh+R5I8FGmCCE= Wf8tdgYDJ6R6BsyAptSwtoPDgdaFykexjPRdX/7TZJQ= 172.80.252.9:40934 10.0.0.92/32,fdd0:438e:19ba:5069::5c/128 1679607825 38538968 479650036 off +WSxAe8GiFWTRwA+NZXYHG2fy5CVSCyt6H00iE4gb/QM= R1wPt+WT/66PR3zGv59EjbCvwDMLQvTXEAuhFEh86Y4= (none) 10.0.0.93/32,fdd0:438e:19ba:5069::5d/128 0 0 0 off +vf/YD28YgyaitJiGyOFw9P9dEj3m4XI7NBEhmsQtuS4= OMKwZuXGTrTPmIIBkA9leNTXiceeTBXYtmM3E2dDHFM= 83.122.177.202:63738 10.0.0.94/32,fdd0:438e:19ba:5069::5e/128 1679622119 214899792 403954636 off +Fgyh9YI3H0BMXpIwuzGsPNuqZBpNX6MHjL9bn2Ao2mc= HPk6anM0vNx7NBle4LqoJip2yMd8RmQzJMDyG7gBOtU= (none) 10.0.0.95/32,fdd0:438e:19ba:5069::5f/128 0 0 0 off +S/VgSky31TeNufxjySvoX71kKhJ6c20gTx3qedTa7Hc= Ad/h2P5vN2A5D3irqe+a6OoZp9+feGkxdsbZv06z0J4= (none) 10.0.0.96/32,fdd0:438e:19ba:5069::60/128 0 0 0 off +UM0khRt0dMUguG+S/mPoqY0QlGnPpFWtpPo6z7BGQWc= GjqKzSvi+1M86YTuZuDwjZ/kN4UHk4m1JqNkbRUpmGk= 95.162.238.226:15423 10.0.0.99/32,fdd0:438e:19ba:5069::63/128 1679622287 200415180 1479932240 off +oxN6Cn4EPglTKBCwgyTiHZ1bU540zD/h8li1laXCbj0= eS7dvTHZF5T60vF1AQ8S/u8jauj6uVz+HMEFOfK85zs= 5.126.57.129:51519 10.0.0.100/32,fdd0:438e:19ba:5069::64/128 1679613535 61072720 1065353052 off +4xcn/eRHubjW9Nytc0AwoN6WSRaTKtjPesz/7ImuCzA= yhWvanfIx4spsiCXH5HT27rvlmL6fjjm2VgxQ4V7/m4= 37.129.11.127:32809 10.0.0.101/32,fdd0:438e:19ba:5069::65/128 1679619234 27569848 539436024 off +nkAGr/kNARuXwSHSKCm7XdpMhrimPLzeWHcsKJcko1g= NPT8zU1f+zm9OTeIODh7ba9JGOoOmpE2un6OPBYz5Xw= (none) 10.0.0.102/32,fdd0:438e:19ba:5069::66/128 0 0 0 off +zMsFGX2HA5AVK2NzOjPXZz0tL6cb70Uwt41YxLEogTA= Xx9kbd2Vtzm6INWM4irctZjdCA3vXwPJfEkj4DpCX9Y= (none) 10.0.0.103/32,fdd0:438e:19ba:5069::67/128 0 0 0 off +WhIPaOV1QuE2Wqgf3kKIzAS/W9rX81cBwFbMFATv7Sg= R01ZFMDTCvB2n+2YCySVowy8v0kXAxSScUi1/ELuVok= 5.119.170.37:61162 10.0.0.104/32,fdd0:438e:19ba:5069::68/128 1679617644 21586800 286196660 off +tCXkXzYf1FXFXNOUP7J0gGEbLYa2+OtmN3xGMjHvn2Y= 02znarJ3rTH03+7ZGMORYkfWtMNpDUXv/39be7QQxBY= 5.114.158.124:5085 10.0.0.105/32,fdd0:438e:19ba:5069::69/128 1679610971 92314284 1539250464 off +pyoJ/0UqULHxtXDVbVJTQsOxj0bkrhcxv5QbiAo/2yA= oE5g05yXJhiY19WMCyorKURphqG7/0vfIYTu21bqdGU= (none) 10.0.0.106/32,fdd0:438e:19ba:5069::6a/128 0 0 0 off +2VWoryzJr403WxW/Q0g7d+akXniBOdH7xMzSBkBz4gk= iO61JNmmi4BdL2MWwpIsVBzgO/YuEOXn/5VhIOGQTfE= (none) 10.0.0.107/32,fdd0:438e:19ba:5069::6b/128 0 0 0 off +NehSfEfBgEIMmMWKJc6dNe+Kk+eowsokeUTJlioPOWM= h/76X+Cuwo123DQETPWEDqVTZwiZXHM0zCS9b000WaM= 83.122.28.199:49330 10.0.0.108/32,fdd0:438e:19ba:5069::6c/128 1679599088 28062836 626552728 off +1wgfj6gR/fhrfW909Ejnu9xPS+gCMDhvqCQFzH8tP0Y= Eqekgl1wqY4Gkf8VaApuXVw0354BWbCelcpZv9WzYHc= (none) 10.0.0.109/32,fdd0:438e:19ba:5069::6d/128 0 0 0 off +WtNgBST4LflxP2IR/oaNkULvQ5909BhZaizrtODXFQs= zoR7N5mGIR1/A9PNC08DK2eOFHCBdG+SapPNyXyNGIo= (none) 10.0.0.110/32,fdd0:438e:19ba:5069::6e/128 0 0 0 off +kPuHjkT58MsNP55oQFJszXhpuN6ukG++WUDsR4DJQjo= G4Z57Ihjk3Et+O68iFj8I3CT2SgbjMDnpa+/lFuH6gU= (none) 10.0.0.111/32,fdd0:438e:19ba:5069::6f/128 0 0 0 off +4ejeXAAOX6UrBnaAivqQ11JSanLzXxBpVd+A1Aozl3A= 77EuIYC1+S1i4LP5j8TibwL8didMy+mI+5bOfqKC+sA= 5.232.204.179:10610 10.0.0.112/32,fdd0:438e:19ba:5069::70/128 1679607401 20695096 349602820 off +RFrjTKnZWr/CmI/XC+zgPeGJzbDGQhA9eOBb+ge90wA= uox3nW4W81djWoJEN/lgdrvjqtLnl36lhkJZcbSkwOM= 5.232.204.179:10611 10.0.0.113/32,fdd0:438e:19ba:5069::71/128 1672307401 10695096 349602820 10 \ No newline at end of file diff --git a/readme.md b/readme.md index 495ee38..e901e62 100644 --- a/readme.md +++ b/readme.md @@ -6,10 +6,11 @@ ![Latest Tag](https://img.shields.io/github/v/tag/xeptore/wireuse?label=Latest%20Tag&sort=semver) ![Latest Release](https://img.shields.io/github/v/release/xeptore/wireuse?display_name=tag&label=Latest%20Release&sort=semver) ![Code Size](https://img.shields.io/github/languages/code-size/xeptore/wireuse?label=Code%20Size) +[![Go Report Card](https://goreportcard.com/badge/github.com/xeptore/wireuse)](https://goreportcard.com/report/github.com/xeptore/wireuse) ![Latest Release Downloads](https://img.shields.io/github/downloads/xeptore/wireuse/latest/total?label=Downloads%40Latest) ![License](https://img.shields.io/github/license/xeptore/wireuse?label=License) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fxeptore%2Fwireuse.svg?type=small)](https://app.fossa.com/projects/git%2Bgithub.com%2Fxeptore%2Fwireuse?ref=badge_small) -[![codecov](https://codecov.io/gh/xeptore/wireuse/branch/main/graph/badge.svg?token=tNKcOjlxLo)](https://codecov.io/gh/xeptore/wireuse) +[![codecov](https://codecov.io/gh/xeptore/wireuse/branch/main/graph/badge.svg?token=9RUG1B8DWZ)](https://codecov.io/gh/xeptore/wireuse) ![Linux](https://img.shields.io/badge/Linux-FCC624?style=flat&logo=linux&logoColor=black) Missing WireGuard monitoring part