From 4df736467e71758a70218ecb12b066e0e2156d11 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Fri, 3 Feb 2023 20:24:56 +0100 Subject: [PATCH] refacto app using kilnfi/go-utils/app --- cmd/root.go | 25 ++ cmd/run.go | 54 ++++ collector/collector.go | 302 ------------------ go.mod | 46 ++- go.sum | 269 +++++++++------- main.go | 50 +-- pkg/app/app.go | 30 ++ pkg/app/config.go | 8 + pkg/app/exporter.go | 204 ++++++++++++ pkg/app/metrics.go | 181 +++++++++++ {collector => pkg/app}/utils.go | 2 +- pkg/near/block.go | 72 +++++ pkg/near/call_function.go | 27 ++ {api => pkg/near}/client.go | 24 +- .../near/protocol_config.go | 16 +- pkg/near/query.go | 96 ++++++ {api => pkg/near}/status.go | 17 +- pkg/near/tx.go | 79 +++++ {api => pkg/near}/validators.go | 15 +- 19 files changed, 1014 insertions(+), 503 deletions(-) create mode 100644 cmd/root.go create mode 100644 cmd/run.go delete mode 100644 collector/collector.go create mode 100644 pkg/app/app.go create mode 100644 pkg/app/config.go create mode 100644 pkg/app/exporter.go create mode 100644 pkg/app/metrics.go rename {collector => pkg/app}/utils.go (96%) create mode 100644 pkg/near/block.go create mode 100644 pkg/near/call_function.go rename {api => pkg/near}/client.go (75%) rename api/protocolconfig.go => pkg/near/protocol_config.go (97%) create mode 100644 pkg/near/query.go rename {api => pkg/near}/status.go (75%) create mode 100644 pkg/near/tx.go rename {api => pkg/near}/validators.go (79%) diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..7ff3bb9 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,25 @@ +package cmd + +import ( + "context" + + kilnutils "github.com/kilnfi/go-utils/cmd/utils" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func NewRootCommand() *cobra.Command { + v := viper.New() + + ctx := context.Background() + ctx = kilnutils.WithViper(ctx, v) + + cmd := &cobra.Command{ + Use: "near-exporter", + } + + cmd.SetContext(ctx) + cmd.AddCommand(NewRunCommand()) + + return cmd +} diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 0000000..2ed3b11 --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,54 @@ +package cmd + +import ( + "fmt" + "net/http" + "os" + "strings" + "time" + + "github.com/kilnfi/near-exporter/pkg/app" + "github.com/kilnfi/near-exporter/pkg/near" + "github.com/spf13/cobra" +) + +func NewRunCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "run", + Short: "Run exporter", + RunE: func(cmd *cobra.Command, args []string) error { + rpcAddr := os.Getenv("NEAR_EXPORTER_RPC_ADDR") + if rpcAddr == "" { + return fmt.Errorf("missing env var NEAR_EXPORTER_RPC_ADDR") + } + trackedAccounts := strings.Split( + os.Getenv("NEAR_EXPORTER_TRACKED_ACCOUNTS"), ",", + ) + + client := near.NewClient(rpcAddr, &http.Client{ + Timeout: time.Duration(10 * time.Second), + }) + + config := &app.Config{ + TrackedAccounts: trackedAccounts, + RefreshRate: 15 * time.Second, + } + + app, err := app.NewApp(config, client) + if err != nil { + return err + } + + app.Logger().SetOutput(cmd.OutOrStderr()) + + app.Logger(). + WithField("rpc_addr", rpcAddr). + WithField("tracked_account", trackedAccounts). + Info("config") + + return app.Run() + }, + } + + return cmd +} diff --git a/collector/collector.go b/collector/collector.go deleted file mode 100644 index 968de5b..0000000 --- a/collector/collector.go +++ /dev/null @@ -1,302 +0,0 @@ -package collector - -import ( - "encoding/json" - "strconv" - - "github.com/kilnfi/near-exporter/api" - "github.com/prometheus/client_golang/prometheus" -) - -const MetricPrefix = "near_exporter_" - -type Collector struct { - client *api.Client - trackedAccounts []string - epochBlockBroducedDesc *prometheus.Desc - epochBlockExpectedDesc *prometheus.Desc - seatPriceDesc *prometheus.Desc - currentStakeDesc *prometheus.Desc - epochStartHeightDesc *prometheus.Desc - blockNumberDesc *prometheus.Desc - syncingDesc *prometheus.Desc - versionBuildDesc *prometheus.Desc - validatorStakeDesc *prometheus.Desc - validatorSlashedDesc *prometheus.Desc - validatorExpectedBlocksDesc *prometheus.Desc - validatorProducedBlocksDesc *prometheus.Desc - validatorExpectedChunksDesc *prometheus.Desc - validatorProducedChunksDesc *prometheus.Desc - nextValidatorStakeDesc *prometheus.Desc - prevEpochKickoutDesc *prometheus.Desc - currentProposalsDesc *prometheus.Desc - epochLength *prometheus.Desc - chainId *prometheus.Desc - protocolVersion *prometheus.Desc -} - -func NewCollector(client *api.Client) *Collector { - return &Collector{ - client: client, - epochBlockBroducedDesc: prometheus.NewDesc( - MetricPrefix+"epoch_block_produced_number", - "The number of block produced in epoch", - nil, - nil, - ), - epochBlockExpectedDesc: prometheus.NewDesc( - MetricPrefix+"epoch_block_expected_number", - "The number of block expected in epoch", - nil, - nil, - ), - seatPriceDesc: prometheus.NewDesc( - MetricPrefix+"seat_price", - "Validator seat price", - nil, - nil, - ), - currentStakeDesc: prometheus.NewDesc( - MetricPrefix+"current_stake", - "Current stake of a given account id", - nil, - nil, - ), - epochStartHeightDesc: prometheus.NewDesc( - MetricPrefix+"epoch_start_height", - "Near epoch start height", - nil, - nil, - ), - blockNumberDesc: prometheus.NewDesc( - MetricPrefix+"block_number", - "The number of most recent block", - nil, - nil, - ), - syncingDesc: prometheus.NewDesc( - MetricPrefix+"sync_state", - "Sync state", - nil, - nil, - ), - versionBuildDesc: prometheus.NewDesc( - MetricPrefix+"version_build", - "The Near node version build", - []string{"version", "build"}, - nil, - ), - validatorStakeDesc: prometheus.NewDesc( - MetricPrefix+"validator_stake", - "Current amount of validator stake", - []string{"account_id", "public_key", "epoch_start_height", "tracked"}, - nil, - ), - validatorSlashedDesc: prometheus.NewDesc( - MetricPrefix+"validator_slashed", - "Validators slashed", - []string{"account_id", "public_key", "epoch_start_height", "tracked"}, - nil, - ), - validatorExpectedBlocksDesc: prometheus.NewDesc( - MetricPrefix+"validator_blocks_expected", - "Current amount of validator expected blocks", - []string{"account_id", "public_key", "epoch_start_height", "tracked"}, - nil, - ), - validatorProducedBlocksDesc: prometheus.NewDesc( - MetricPrefix+"validator_blocks_produced", - "Current amount of validator produced blocks", - []string{"account_id", "public_key", "epoch_start_height", "tracked"}, - nil, - ), - validatorExpectedChunksDesc: prometheus.NewDesc( - MetricPrefix+"validator_chunks_expected", - "Current amount of validator expected chunks", - []string{"account_id", "public_key", "epoch_start_height", "tracked"}, - nil, - ), - validatorProducedChunksDesc: prometheus.NewDesc( - MetricPrefix+"validator_chunks_produced", - "Current amount of validator produced chunks", - []string{"account_id", "public_key", "epoch_start_height", "tracked"}, - nil, - ), - nextValidatorStakeDesc: prometheus.NewDesc( - MetricPrefix+"next_validator_stake", - "The next validators", - []string{"account_id", "public_key", "epoch_start_height", "tracked"}, - nil, - ), - currentProposalsDesc: prometheus.NewDesc( - MetricPrefix+"current_proposals_stake", - "Current proposals", - []string{"account_id", "public_key", "epoch_start_height", "tracked"}, - nil, - ), - prevEpochKickoutDesc: prometheus.NewDesc( - MetricPrefix+"prev_epoch_kickout", - "Near previous epoch kicked out validators", - []string{"account_id", "reason", "epoch_start_height", "tracked"}, - nil, - ), - epochLength: prometheus.NewDesc( - MetricPrefix+"epoch_length", - "Near epoch length as specified in the protocol", - nil, - nil, - ), - chainId: prometheus.NewDesc( - MetricPrefix+"chain_id", - "Near chain id", - []string{"chain_id"}, - nil, - ), - protocolVersion: prometheus.NewDesc( - MetricPrefix+"protocol_version", - "Current protocol version deployed to the blockchain", - nil, - nil, - ), - } -} - -func (c *Collector) Describe(ch chan<- *prometheus.Desc) { - ch <- c.seatPriceDesc - ch <- c.epochStartHeightDesc - ch <- c.blockNumberDesc - ch <- c.syncingDesc - ch <- c.versionBuildDesc - ch <- c.validatorStakeDesc - ch <- c.validatorSlashedDesc - ch <- c.nextValidatorStakeDesc - ch <- c.currentProposalsDesc - ch <- c.prevEpochKickoutDesc - ch <- c.validatorExpectedBlocksDesc - ch <- c.validatorProducedBlocksDesc - ch <- c.validatorExpectedChunksDesc - ch <- c.validatorProducedChunksDesc - ch <- c.epochLength - ch <- c.chainId - ch <- c.protocolVersion -} - -func (c *Collector) WithTrackedAccounts(trackedAccounts []string) *Collector { - c.trackedAccounts = trackedAccounts - return c -} - -func (c *Collector) Collect(ch chan<- prometheus.Metric) { - c.collectStatus(ch) - c.collectValidators(ch) - c.collectProtocolConfig(ch) -} - -func (c *Collector) isTracked(accountId string) string { - for _, t := range c.trackedAccounts { - if accountId == t { - return "1" - } - } - return "0" -} - -// Collect info from Status method -func (c *Collector) collectStatus(ch chan<- prometheus.Metric) { - status, err := c.client.Status() - if err != nil { - ch <- prometheus.NewInvalidMetric(c.blockNumberDesc, err) - ch <- prometheus.NewInvalidMetric(c.syncingDesc, err) - ch <- prometheus.NewInvalidMetric(c.versionBuildDesc, err) - return - } - - blockHeight := status.SyncInfo.LatestBlockHeight - ch <- prometheus.MustNewConstMetric(c.blockNumberDesc, prometheus.GaugeValue, float64(blockHeight)) - - isSyncing := 0 - if status.SyncInfo.Syncing { - isSyncing = 1 - } - ch <- prometheus.MustNewConstMetric(c.syncingDesc, prometheus.GaugeValue, float64(isSyncing)) - - versionBuildInt := HashString(status.Version.Build) - ch <- prometheus.MustNewConstMetric(c.versionBuildDesc, prometheus.GaugeValue, float64(versionBuildInt), status.Version.Version, status.Version.Build) -} - -// Collect info from Validators method -func (c *Collector) collectValidators(ch chan<- prometheus.Metric) { - validators, err := c.client.Validators() - if err != nil { - ch <- prometheus.NewInvalidMetric(c.seatPriceDesc, err) - ch <- prometheus.NewInvalidMetric(c.epochStartHeightDesc, err) - ch <- prometheus.NewInvalidMetric(c.syncingDesc, err) - ch <- prometheus.NewInvalidMetric(c.validatorStakeDesc, err) - ch <- prometheus.NewInvalidMetric(c.validatorSlashedDesc, err) - ch <- prometheus.NewInvalidMetric(c.validatorExpectedBlocksDesc, err) - ch <- prometheus.NewInvalidMetric(c.validatorExpectedChunksDesc, err) - ch <- prometheus.NewInvalidMetric(c.validatorProducedBlocksDesc, err) - ch <- prometheus.NewInvalidMetric(c.validatorProducedChunksDesc, err) - ch <- prometheus.NewInvalidMetric(c.nextValidatorStakeDesc, err) - ch <- prometheus.NewInvalidMetric(c.currentProposalsDesc, err) - ch <- prometheus.NewInvalidMetric(c.prevEpochKickoutDesc, err) - return - } - - ch <- prometheus.MustNewConstMetric(c.epochStartHeightDesc, prometheus.GaugeValue, float64(validators.EpochStartHeight)) - - var seatPrice float64 - for _, v := range validators.CurrentValidators { - isTracked := c.isTracked(v.AccountId) - - ch <- prometheus.MustNewConstMetric(c.validatorExpectedChunksDesc, prometheus.GaugeValue, float64(v.NumExpectedChunks), v.AccountId, v.PublicKey, strconv.FormatInt(validators.EpochStartHeight, 10), isTracked) - ch <- prometheus.MustNewConstMetric(c.validatorProducedChunksDesc, prometheus.GaugeValue, float64(v.NumProducedChunks), v.AccountId, v.PublicKey, strconv.FormatInt(validators.EpochStartHeight, 10), isTracked) - ch <- prometheus.MustNewConstMetric(c.validatorExpectedBlocksDesc, prometheus.GaugeValue, float64(v.NumExpectedBlocks), v.AccountId, v.PublicKey, strconv.FormatInt(validators.EpochStartHeight, 10), isTracked) - ch <- prometheus.MustNewConstMetric(c.validatorProducedBlocksDesc, prometheus.GaugeValue, float64(v.NumProducedBlocks), v.AccountId, v.PublicKey, strconv.FormatInt(validators.EpochStartHeight, 10), isTracked) - ch <- prometheus.MustNewConstMetric(c.validatorStakeDesc, prometheus.GaugeValue, float64(GetStakeFromString(v.Stake)), v.AccountId, v.PublicKey, strconv.FormatInt(validators.EpochStartHeight, 10), isTracked) - - isSlashed := 0 - if v.IsSlashed { - isSlashed = 1 - } - ch <- prometheus.MustNewConstMetric(c.validatorSlashedDesc, prometheus.GaugeValue, float64(isSlashed), v.AccountId, v.PublicKey, strconv.FormatInt(validators.EpochStartHeight, 10), isTracked) - - t := GetStakeFromString(v.Stake) - if seatPrice == 0 { - seatPrice = t - } - if seatPrice > t { - seatPrice = t - } - } - - ch <- prometheus.MustNewConstMetric(c.seatPriceDesc, prometheus.GaugeValue, seatPrice) - - for _, v := range validators.NextValidators { - ch <- prometheus.MustNewConstMetric(c.nextValidatorStakeDesc, prometheus.GaugeValue, float64(GetStakeFromString(v.Stake)), v.AccountId, v.PublicKey, strconv.FormatInt(validators.EpochStartHeight, 10), c.isTracked(v.AccountId)) - } - - for _, v := range validators.CurrentProposals { - ch <- prometheus.MustNewConstMetric(c.currentProposalsDesc, prometheus.GaugeValue, float64(GetStakeFromString(v.Stake)), v.AccountId, v.PublicKey, strconv.FormatInt(validators.EpochStartHeight, 10), c.isTracked(v.AccountId)) - } - - for _, v := range validators.PrevEpochKickOut { - reason, _ := json.Marshal(v.Reason) - ch <- prometheus.MustNewConstMetric(c.prevEpochKickoutDesc, prometheus.GaugeValue, 0, v.AccountId, string(reason), strconv.FormatInt(validators.EpochStartHeight, 10), c.isTracked(v.AccountId)) - } -} - -// Collect info from ProtocolConfig method -func (c *Collector) collectProtocolConfig(ch chan<- prometheus.Metric) { - config, err := c.client.ProtocolConfig() - if err != nil { - ch <- prometheus.NewInvalidMetric(c.epochLength, err) - ch <- prometheus.NewInvalidMetric(c.chainId, err) - ch <- prometheus.NewInvalidMetric(c.protocolVersion, err) - return - } - - ch <- prometheus.MustNewConstMetric(c.chainId, prometheus.GaugeValue, float64(HashString(config.ChainID)), config.ChainID) - ch <- prometheus.MustNewConstMetric(c.epochLength, prometheus.GaugeValue, float64(config.EpochLength)) - ch <- prometheus.MustNewConstMetric(c.protocolVersion, prometheus.GaugeValue, float64(config.ProtocolVersion)) -} diff --git a/go.mod b/go.mod index b822426..10e0866 100644 --- a/go.mod +++ b/go.mod @@ -2,16 +2,46 @@ module github.com/kilnfi/near-exporter go 1.18 -require github.com/prometheus/client_golang v1.12.2 +require ( + github.com/cenkalti/backoff/v4 v4.2.0 + github.com/kilnfi/go-utils v0.3.6 + github.com/prometheus/client_golang v1.14.0 + github.com/sirupsen/logrus v1.9.0 + github.com/spf13/cobra v1.6.1 + github.com/spf13/viper v1.15.0 + golang.org/x/sync v0.1.0 +) require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.35.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect - google.golang.org/protobuf v1.28.0 // indirect + github.com/gorilla/handlers v1.5.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hellofresh/health-go/v4 v4.7.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/justinas/alice v1.2.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.39.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/spf13/afero v1.9.3 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + go.opentelemetry.io/otel v1.12.0 // indirect + go.opentelemetry.io/otel/trace v1.12.0 // indirect + golang.org/x/net v0.5.0 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index be9640f..fc8d4d4 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -13,6 +14,9 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -30,46 +34,43 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -107,12 +108,13 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -120,90 +122,115 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hellofresh/health-go/v4 v4.7.0 h1:D+0gCkG9oEpUewIkIKxTmalxkM+0QoRDfJelJrG3sFU= +github.com/hellofresh/health-go/v4 v4.7.0/go.mod h1:XyFAB5J9wAUq7PGN3om2g68bNyWIqKIrMytAT8IMJ4Y= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= +github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= +github.com/kilnfi/go-utils v0.3.6 h1:3JWTY92VfGYE1iZcM1klp+atcUfRAkSiG7dhn10/ofw= +github.com/kilnfi/go-utils v0.3.6/go.mod h1:OTNsPq+NT2c7ZzzY5oYfj5pWl4prWEqiBRy2tSYre+Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.35.0 h1:Eyr+Pw2VymWejHqCugNaQXkAi6KayVNxaHeu6khmFBE= -github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opentelemetry.io/otel v1.12.0 h1:IgfC7kqQrRccIKuB7Cl+SRUmsKbEwSGPr0Eu+/ht1SQ= +go.opentelemetry.io/otel v1.12.0/go.mod h1:geaoz0L0r1BEOR81k7/n9W4TCXYCJ7bPO7K374jQHG0= +go.opentelemetry.io/otel/trace v1.12.0 h1:p28in++7Kd0r2d8gSt931O57fdjUyWxkVbESuILAeUc= +go.opentelemetry.io/otel/trace v1.12.0/go.mod h1:pHlgBynn6s25qJ2szD+Bv+iwKJttjHSI3lUAyf0GNuQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -226,6 +253,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -234,9 +262,10 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -244,7 +273,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -262,16 +290,22 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -280,14 +314,14 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -296,7 +330,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -309,28 +342,29 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -374,10 +408,16 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -395,12 +435,16 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -430,6 +474,13 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -442,6 +493,10 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -453,21 +508,19 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index e075f0f..456e9a8 100644 --- a/main.go +++ b/main.go @@ -1,53 +1,13 @@ package main import ( - "fmt" - "log" - "net/http" - "os" - "strings" - - "github.com/kilnfi/near-exporter/api" - "github.com/kilnfi/near-exporter/collector" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/kilnfi/near-exporter/cmd" + log "github.com/sirupsen/logrus" ) func main() { - rpcAddr := os.Getenv("NEAR_EXPORTER_RPC_ADDR") - if rpcAddr == "" { - rpcAddr = "https://rpc.testnet.near.org" - } - - port := os.Getenv("NEAR_EXPORTER_PORT") - if port == "" { - port = "8080" + command := cmd.NewRootCommand() + if err := command.Execute(); err != nil { + log.WithError(err).Fatalf("main: execution failed") } - - trackedAccounts := strings.Split( - os.Getenv("NEAR_EXPORTER_TRACKED_ACCOUNTS"), ",", - ) - - client := api.NewClient(rpcAddr) - - registry := prometheus.NewPedanticRegistry() - registry.MustRegister( - collector. - NewCollector(client). - WithTrackedAccounts(trackedAccounts), - ) - - handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{ - ErrorLog: log.New(os.Stderr, log.Prefix(), log.Flags()), - ErrorHandling: promhttp.ContinueOnError, - }) - - fmt.Println("Listening on port " + port + "...") - - http.Handle("/metrics", handler) - http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNoContent) - }) - - log.Fatal(http.ListenAndServe(":"+port, nil)) } diff --git a/pkg/app/app.go b/pkg/app/app.go new file mode 100644 index 0000000..4edc670 --- /dev/null +++ b/pkg/app/app.go @@ -0,0 +1,30 @@ +package app + +import ( + kilnapp "github.com/kilnfi/go-utils/app" + "github.com/kilnfi/go-utils/log" + "github.com/kilnfi/near-exporter/pkg/near" +) + +func NewApp(config *Config, client *near.Client) (*kilnapp.App, error) { + cfg := &kilnapp.Config{ + Logger: &log.Config{ + Level: "info", + Format: "json", + }, + } + + app, err := kilnapp.New(cfg.SetDefault()) + if err != nil { + return nil, err + } + + exporter := NewExporter( + config, + client, + ) + + app.RegisterService(exporter) + + return app, nil +} diff --git a/pkg/app/config.go b/pkg/app/config.go new file mode 100644 index 0000000..719ba8c --- /dev/null +++ b/pkg/app/config.go @@ -0,0 +1,8 @@ +package app + +import "time" + +type Config struct { + TrackedAccounts []string + RefreshRate time.Duration +} diff --git a/pkg/app/exporter.go b/pkg/app/exporter.go new file mode 100644 index 0000000..454ab17 --- /dev/null +++ b/pkg/app/exporter.go @@ -0,0 +1,204 @@ +package app + +import ( + "context" + "encoding/json" + "strconv" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/kilnfi/near-exporter/pkg/near" + "github.com/prometheus/client_golang/prometheus" + "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" +) + +type Exporter struct { + config *Config + client *near.Client + + logger logrus.FieldLogger + metrics *Metrics + + errg *errgroup.Group + cancel context.CancelFunc +} + +func NewExporter(config *Config, client *near.Client) *Exporter { + return &Exporter{ + config: config, + client: client, + metrics: NewMetrics(), + logger: logrus.StandardLogger(), + } +} + +func (e *Exporter) SetLogger(logger logrus.FieldLogger) { + e.logger = logger.WithField("service", "exporter") +} + +func (e *Exporter) RegisterMetrics(reg prometheus.Registerer) error { + return e.metrics.RegisterMetrics(reg) +} + +func (e *Exporter) Start(ctx context.Context) error { + ctx, e.cancel = context.WithCancel(context.Background()) + e.errg, ctx = errgroup.WithContext(ctx) + + e.errg.Go(func() error { + ticker := time.NewTicker(e.config.RefreshRate) + + for { + tick := func() error { + return e.tick(ctx) + } + + backOff := backoff.WithMaxRetries( + backoff.NewExponentialBackOff(), + 3, + ) + notify := func(err error, d time.Duration) { + e.logger.WithError(err).Error("tick failed, retrying...") + } + + if err := backoff.RetryNotify(tick, backOff, notify); err != nil { + e.logger.WithError(err).Error("tick run failed") + } + + select { + case <-ctx.Done(): + return nil + case <-ticker.C: + continue + } + } + }) + + return nil +} + +func (e *Exporter) Stop(ctx context.Context) error { + e.cancel() + return e.errg.Wait() +} + +func (e *Exporter) tick(ctx context.Context) error { + e.logger.Info("collect near data") + + var errg *errgroup.Group + + errg, ctx = errgroup.WithContext(ctx) + + errg.Go(func() error { + return e.collectProtocolConfig(ctx) + }) + errg.Go(func() error { + return e.collectStatus(ctx) + }) + errg.Go(func() error { + return e.collectValidators(ctx) + }) + + return errg.Wait() +} + +func (e *Exporter) collectProtocolConfig(ctx context.Context) error { + config, err := e.client.ProtocolConfig(ctx) + if err != nil { + return err + } + + e.metrics.EpochLength.Set(float64(config.EpochLength)) + e.metrics.ProtocolVersion.Set(float64(config.ProtocolVersion)) + + return nil +} + +func (e *Exporter) collectStatus(ctx context.Context) error { + status, err := e.client.Status(ctx) + if err != nil { + return err + } + + isSyncing := 0 + if status.SyncInfo.Syncing { + isSyncing = 1 + } + + e.metrics.ChainID.WithLabelValues(status.ChainID).Set(float64(HashString(status.ChainID))) + e.metrics.VersionBuild.WithLabelValues(status.Version.Version, status.Version.Build).Set(float64(HashString(status.Version.Build))) + e.metrics.BlockNumber.Set(float64(status.SyncInfo.LatestBlockHeight)) + e.metrics.SyncingDesc.Set(float64(isSyncing)) + + return nil +} + +func (e *Exporter) collectValidators(ctx context.Context) error { + validators, err := e.client.Validators(ctx, "latest") + if err != nil { + return err + } + + labelEpochStartHeight := strconv.FormatInt(validators.EpochStartHeight, 10) + + e.metrics.EpochStartHeight.Set(float64(validators.EpochStartHeight)) + + var seatPrice float64 + for _, v := range validators.CurrentValidators { + isSlashed := 0 + if v.IsSlashed { + isSlashed = 1 + } + + labels := []string{v.AccountId, v.PublicKey, labelEpochStartHeight, e.isTracked(v.AccountId)} + + e.metrics.ValidatorExpectedBlocks.WithLabelValues(labels...).Set(float64(v.NumExpectedBlocks)) + e.metrics.ValidatorExpectedChunks.WithLabelValues(labels...).Set(float64(v.NumExpectedChunks)) + e.metrics.ValidatorProducedBlocks.WithLabelValues(labels...).Set(float64(v.NumProducedBlocks)) + e.metrics.ValidatorProducedChunks.WithLabelValues(labels...).Set(float64(v.NumProducedChunks)) + + e.metrics.ValidatorSlashed.WithLabelValues(labels...).Set(float64(isSlashed)) + e.metrics.ValidatorStake.WithLabelValues(labels...).Set(float64(GetStakeFromString(v.Stake))) + + t := GetStakeFromString(v.Stake) + if seatPrice == 0 { + seatPrice = t + } + if seatPrice > t { + seatPrice = t + } + } + + e.metrics.SeatPriceDesc.Set(seatPrice) + + for _, v := range validators.NextValidators { + e.metrics.NextValidatorStake. + WithLabelValues(v.AccountId, v.PublicKey, labelEpochStartHeight, e.isTracked(v.AccountId)). + Set(float64(GetStakeFromString(v.Stake))) + } + + for _, v := range validators.CurrentProposals { + e.metrics.CurrentProposals. + WithLabelValues(v.AccountId, v.PublicKey, labelEpochStartHeight, e.isTracked(v.AccountId)). + Set(float64(GetStakeFromString(v.Stake))) + } + + for _, v := range validators.PrevEpochKickOut { + reason, _ := json.Marshal(v.Reason) + + e.metrics.PrevEpochKickout. + WithLabelValues(v.AccountId, string(reason), labelEpochStartHeight, e.isTracked(v.AccountId)). + Set(1) + } + + return nil +} + +func (e *Exporter) isTracked(accountId string) string { + for _, t := range e.config.TrackedAccounts { + if accountId == t { + return "1" + } + } + return "0" +} diff --git a/pkg/app/metrics.go b/pkg/app/metrics.go new file mode 100644 index 0000000..c248cd3 --- /dev/null +++ b/pkg/app/metrics.go @@ -0,0 +1,181 @@ +package app + +import "github.com/prometheus/client_golang/prometheus" + +const namespace = "near_exporter" + +type Metrics struct { + BlockNumber prometheus.Gauge + ChainID *prometheus.GaugeVec + CurrentProposals *prometheus.GaugeVec + EpochLength prometheus.Gauge + EpochStartHeight prometheus.Gauge + NextValidatorStake *prometheus.GaugeVec + PrevEpochKickout *prometheus.GaugeVec + ProtocolVersion prometheus.Gauge + SeatPriceDesc prometheus.Gauge + SyncingDesc prometheus.Gauge + ValidatorExpectedBlocks *prometheus.GaugeVec + ValidatorExpectedChunks *prometheus.GaugeVec + ValidatorProducedBlocks *prometheus.GaugeVec + ValidatorProducedChunks *prometheus.GaugeVec + ValidatorSlashed *prometheus.GaugeVec + ValidatorStake *prometheus.GaugeVec + VersionBuild *prometheus.GaugeVec +} + +func NewMetrics() *Metrics { + return &Metrics{ + BlockNumber: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "block_number", + Help: "The number of most recent block", + }), + ChainID: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "chain_id", + Help: "Near chain id"}, + []string{"chain_id"}, + ), + CurrentProposals: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "current_proposals_stake", + Help: "Current proposals"}, + []string{"account_id", "public_key", "epoch_start_height", "tracked"}, + ), + EpochLength: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "epoch_length", + Help: "Near epoch length as specified in the protocol", + }), + EpochStartHeight: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "epoch_start_height", + Help: "Near epoch start height", + }), + NextValidatorStake: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "next_validator_stake", + Help: "The next validators"}, + []string{"account_id", "public_key", "epoch_start_height", "tracked"}, + ), + PrevEpochKickout: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "prev_epoch_kickout", + Help: "Near previous epoch kicked out validators"}, + []string{"account_id", "public_key", "epoch_start_height", "tracked"}, + ), + ProtocolVersion: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "protocol_version", + Help: "Current protocol version deployed to the blockchain", + }), + SeatPriceDesc: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "seat_price", + Help: "Validator seat price", + }), + SyncingDesc: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "sync_state", + Help: "Sync state", + }), + ValidatorExpectedBlocks: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "validator_blocks_expected", + Help: "Current amount of validator expected blocks"}, + []string{"account_id", "public_key", "epoch_start_height", "tracked"}, + ), + ValidatorExpectedChunks: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "validator_chunks_expected", + Help: "Current amount of validator expected chunks"}, + []string{"account_id", "public_key", "epoch_start_height", "tracked"}, + ), + ValidatorProducedBlocks: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "validator_blocks_produced", + Help: "Current amount of validator produced blocks"}, + []string{"account_id", "public_key", "epoch_start_height", "tracked"}, + ), + ValidatorProducedChunks: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "validator_chunks_produced", + Help: "Current amount of validator produced chunks"}, + []string{"account_id", "public_key", "epoch_start_height", "tracked"}, + ), + ValidatorSlashed: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "validator_slashed", + Help: "Validators slashed"}, + []string{"account_id", "public_key", "epoch_start_height", "tracked"}, + ), + ValidatorStake: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "validator_stake", + Help: "Current amount of validator stake"}, + []string{"account_id", "public_key", "epoch_start_height", "tracked"}, + ), + VersionBuild: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "version_build", + Help: "The Near node version build"}, + []string{"version", "build"}, + ), + } +} + +func (m *Metrics) RegisterMetrics(reg prometheus.Registerer) error { + if err := reg.Register(m.BlockNumber); err != nil { + return err + } + if err := reg.Register(m.ChainID); err != nil { + return err + } + if err := reg.Register(m.CurrentProposals); err != nil { + return err + } + if err := reg.Register(m.EpochLength); err != nil { + return err + } + if err := reg.Register(m.EpochStartHeight); err != nil { + return err + } + if err := reg.Register(m.NextValidatorStake); err != nil { + return err + } + if err := reg.Register(m.PrevEpochKickout); err != nil { + return err + } + if err := reg.Register(m.ProtocolVersion); err != nil { + return err + } + if err := reg.Register(m.SeatPriceDesc); err != nil { + return err + } + if err := reg.Register(m.SyncingDesc); err != nil { + return err + } + if err := reg.Register(m.ValidatorExpectedBlocks); err != nil { + return err + } + if err := reg.Register(m.ValidatorExpectedChunks); err != nil { + return err + } + if err := reg.Register(m.ValidatorProducedBlocks); err != nil { + return err + } + if err := reg.Register(m.ValidatorProducedChunks); err != nil { + return err + } + if err := reg.Register(m.ValidatorSlashed); err != nil { + return err + } + if err := reg.Register(m.ValidatorStake); err != nil { + return err + } + if err := reg.Register(m.VersionBuild); err != nil { + return err + } + return nil +} diff --git a/collector/utils.go b/pkg/app/utils.go similarity index 96% rename from collector/utils.go rename to pkg/app/utils.go index f3e534c..66935f3 100644 --- a/collector/utils.go +++ b/pkg/app/utils.go @@ -1,4 +1,4 @@ -package collector +package app import ( "fmt" diff --git a/pkg/near/block.go b/pkg/near/block.go new file mode 100644 index 0000000..19ae90e --- /dev/null +++ b/pkg/near/block.go @@ -0,0 +1,72 @@ +package near + +import "context" + +type BlockRequest struct { + Finality string `json:"finality,omitempty"` + BlockID interface{} `json:"block_id,omitempty"` +} + +type BlockResponse struct { + Author string `json:"author"` + Header struct { + Height int `json:"height"` + EpochID string `json:"epoch_id"` + NextEpochID string `json:"next_epoch_id"` + Hash string `json:"hash"` + PrevHash string `json:"prev_hash"` + PrevStateRoot string `json:"prev_state_root"` + ChunkReceiptsRoot string `json:"chunk_receipts_root"` + ChunkHeadersRoot string `json:"chunk_headers_root"` + ChunkTxRoot string `json:"chunk_tx_root"` + OutcomeRoot string `json:"outcome_root"` + ChunksIncluded int `json:"chunks_included"` + ChallengesRoot string `json:"challenges_root"` + Timestamp int64 `json:"timestamp"` + TimestampNanosec string `json:"timestamp_nanosec"` + RandomValue string `json:"random_value"` + ValidatorProposals []interface{} `json:"validator_proposals"` + ChunkMask []bool `json:"chunk_mask"` + GasPrice string `json:"gas_price"` + RentPaid string `json:"rent_paid"` + ValidatorReward string `json:"validator_reward"` + TotalSupply string `json:"total_supply"` + ChallengesResult []interface{} `json:"challenges_result"` + LastFinalBlock string `json:"last_final_block"` + LastDsFinalBlock string `json:"last_ds_final_block"` + NextBpHash string `json:"next_bp_hash"` + BlockMerkleRoot string `json:"block_merkle_root"` + Approvals []interface{} `json:"approvals"` + Signature string `json:"signature"` + LatestProtocolVersion int `json:"latest_protocol_version"` + } `json:"header"` + Chunks []struct { + ChunkHash string `json:"chunk_hash"` + PrevBlockHash string `json:"prev_block_hash"` + OutcomeRoot string `json:"outcome_root"` + PrevStateRoot string `json:"prev_state_root"` + EncodedMerkleRoot string `json:"encoded_merkle_root"` + EncodedLength int `json:"encoded_length"` + HeightCreated int `json:"height_created"` + HeightIncluded int `json:"height_included"` + ShardID int `json:"shard_id"` + GasUsed int `json:"gas_used"` + GasLimit int64 `json:"gas_limit"` + RentPaid string `json:"rent_paid"` + ValidatorReward string `json:"validator_reward"` + BalanceBurnt string `json:"balance_burnt"` + OutgoingReceiptsRoot string `json:"outgoing_receipts_root"` + TxRoot string `json:"tx_root"` + ValidatorProposals []interface{} `json:"validator_proposals"` + Signature string `json:"signature"` + } `json:"chunks"` +} + +func (c *Client) Block(ctx context.Context, blockID uint64) (BlockResponse, error) { + var req BlockRequest + var resp BlockResponse + + req.BlockID = blockID + err := c.call(ctx, "block", req, &resp) + return resp, err +} diff --git a/pkg/near/call_function.go b/pkg/near/call_function.go new file mode 100644 index 0000000..d6fead0 --- /dev/null +++ b/pkg/near/call_function.go @@ -0,0 +1,27 @@ +package near + +import ( + "context" +) + +// CallFunctionResponse holds information about the result of a function call. +type CallFunctionResponse struct { + QueryResponse + Logs []string `json:"logs"` + Result []byte `json:"result"` +} + +func (c *Client) CallFunction( + ctx context.Context, + accountID string, + methodName string, + opts ...QueryOption, +) (CallFunctionResponse, error) { + var resp CallFunctionResponse + req, err := NewQueryRequest("call_function", accountID, methodName, opts...) + if err != nil { + return resp, err + } + err = c.call(ctx, "query", req, &resp) + return resp, err +} diff --git a/api/client.go b/pkg/near/client.go similarity index 75% rename from api/client.go rename to pkg/near/client.go index 7ac8a3d..173925d 100644 --- a/api/client.go +++ b/pkg/near/client.go @@ -1,13 +1,13 @@ -package api +package near import ( "bytes" + "context" "encoding/json" "fmt" "io/ioutil" "log" "net/http" - "time" ) type Client struct { @@ -31,23 +31,21 @@ type Response struct { Code int `json:"code"` Message string `json:"message"` Data string `json:"data"` + Cause struct { + Info interface{} `json:"info"` + Name string `json:"name"` + } `json:"cause"` } `json:"error"` } -func NewClient(endpoint string) *Client { - timeout := time.Duration(10 * time.Second) - - httpClient := &http.Client{ - Timeout: timeout, - } - +func NewClient(endpoint string, httpClient *http.Client) *Client { return &Client{ Endpoint: endpoint, httpClient: httpClient, } } -func (c *Client) Request(method string, params interface{}) (*Response, error) { +func (c *Client) Request(ctx context.Context, method string, params interface{}) (*Response, error) { payload, err := json.Marshal(map[string]string{ "query": method, }) @@ -69,7 +67,7 @@ func (c *Client) Request(method string, params interface{}) (*Response, error) { } } - req, err := http.NewRequest("POST", c.Endpoint, bytes.NewBuffer(payload)) + req, err := http.NewRequestWithContext(ctx, "POST", c.Endpoint, bytes.NewBuffer(payload)) req.Header.Set("Content-Type", "application/json") if err != nil { return nil, err @@ -96,8 +94,8 @@ func (c *Client) Request(method string, params interface{}) (*Response, error) { return resp, nil } -func (c *Client) do(method string, params interface{}, result interface{}) error { - resp, err := c.Request(method, params) +func (c *Client) call(ctx context.Context, method string, params interface{}, result interface{}) error { + resp, err := c.Request(ctx, method, params) if err != nil { return err } diff --git a/api/protocolconfig.go b/pkg/near/protocol_config.go similarity index 97% rename from api/protocolconfig.go rename to pkg/near/protocol_config.go index aecd07c..d0cb032 100644 --- a/api/protocolconfig.go +++ b/pkg/near/protocol_config.go @@ -1,12 +1,11 @@ -package api +package near import ( + "context" "time" ) -const ProtocolConfigMethod = "EXPERIMENTAL_protocol_config" - -type ProtocolConfigResult struct { +type ProtocolConfigResponse struct { ProtocolVersion int `json:"protocol_version"` GenesisTime time.Time `json:"genesis_time"` ChainID string `json:"chain_id"` @@ -213,9 +212,8 @@ type ProtocolConfigResult struct { MinimumStakeDivisor int `json:"minimum_stake_divisor"` } -func (c *Client) ProtocolConfig() (*ProtocolConfigResult, error) { - var result *ProtocolConfigResult - err := c.do(ProtocolConfigMethod, map[string]string{"finality": "final"}, &result) - - return result, err +func (c *Client) ProtocolConfig(ctx context.Context) (ProtocolConfigResponse, error) { + var resp ProtocolConfigResponse + err := c.call(ctx, "EXPERIMENTAL_protocol_config", map[string]string{"finality": "final"}, &resp) + return resp, err } diff --git a/pkg/near/query.go b/pkg/near/query.go new file mode 100644 index 0000000..fdec3ef --- /dev/null +++ b/pkg/near/query.go @@ -0,0 +1,96 @@ +package near + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" +) + +// QueryRequest is used for RPC query requests. +type QueryRequest struct { + RequestType string `json:"request_type"` + Finality string `json:"finality,omitempty"` + BlockID interface{} `json:"block_id,omitempty"` + AccountID string `json:"account_id,omitempty"` + PrefixBase64 string `json:"prefix_base64"` + MethodName string `json:"method_name,omitempty"` + ArgsBase64 string `json:"args_base64"` + PublicKey string `json:"public_key,omitempty"` +} + +func NewQueryRequest(requestType, accountID, methodName string, opts ...QueryOption) (QueryRequest, error) { + req := QueryRequest{ + RequestType: requestType, + AccountID: accountID, + MethodName: methodName, + } + + for _, opt := range opts { + if err := opt(&req); err != nil { + return req, err + } + } + return req, nil +} + +// QueryResponse is a base type used for responses to query requests. +type QueryResponse struct { + BlockHash string `json:"block_hash"` + BlockHeight int `json:"block_height"` + // TODO: this property is undocumented, but appears in some API responses. Is this the right place for it? + Error string `json:"error"` +} + +// QueryOption controls the behavior when calling CallFunction. +type QueryOption func(*QueryRequest) error + +// QueryWithFinality specifies the finality to be used when calling the function. +func QueryWithFinality(finality string) QueryOption { + return func(qr *QueryRequest) error { + qr.Finality = finality + return nil + } +} + +// QueryWithBlockHeight specifies the block height to call the function for. +func QueryWithBlockHeight(blockHeight int64) QueryOption { + return func(qr *QueryRequest) error { + qr.BlockID = blockHeight + return nil + } +} + +// QueryWithBlockHash specifies the block hash to call the function for. +func QueryWithBlockHash(blockHash string) QueryOption { + return func(qr *QueryRequest) error { + qr.BlockID = blockHash + return nil + } +} + +// QueryWithArgs specified the args to call the function with. +// Should be a JSON encodable object. +func QueryWithArgs(args interface{}) QueryOption { + return func(qr *QueryRequest) error { + if args == nil { + args = make(map[string]interface{}) + } + bytes, err := json.Marshal(args) + if err != nil { + return err + } + qr.ArgsBase64 = base64.StdEncoding.EncodeToString(bytes) + return nil + } +} + +func (c *Client) Query(ctx context.Context, req QueryRequest, resp interface{}) error { + if req.BlockID == nil && req.Finality == "" { + return fmt.Errorf("missing block_id or finality") + } + if req.BlockID != nil && req.Finality != "" { + return fmt.Errorf("you can't use both block_id and finality") + } + return c.call(ctx, "query", req, &resp) +} diff --git a/api/status.go b/pkg/near/status.go similarity index 75% rename from api/status.go rename to pkg/near/status.go index 575f3db..9b920c3 100644 --- a/api/status.go +++ b/pkg/near/status.go @@ -1,9 +1,9 @@ -package api +package near -const StatusMethod = "status" +import "context" -type StatusResult struct { - ChainId string `json:"chain_id"` +type StatusResponse struct { + ChainID string `json:"chain_id"` LatestProtocolVersion int `json:"latest_protocol_version"` ProtocolVersion int `json:"protocol_version"` RpcAddr string `json:"rpc_addr"` @@ -24,9 +24,8 @@ type StatusResult struct { } `json:"version"` } -func (c *Client) Status() (*StatusResult, error) { - var result *StatusResult - err := c.do(StatusMethod, nil, &result) - - return result, err +func (c *Client) Status(ctx context.Context) (StatusResponse, error) { + var resp StatusResponse + err := c.call(ctx, "status", nil, &resp) + return resp, err } diff --git a/pkg/near/tx.go b/pkg/near/tx.go new file mode 100644 index 0000000..57ce46b --- /dev/null +++ b/pkg/near/tx.go @@ -0,0 +1,79 @@ +package near + +import "context" + +type TxResponse struct { + ReceiptsOutcome []struct { + BlockHash string `json:"block_hash"` + ID string `json:"id"` + Outcome struct { + ExecutorID string `json:"executor_id"` + GasBurnt int64 `json:"gas_burnt"` + Logs []string `json:"logs"` + Metadata struct { + GasProfile []struct { + Cost string `json:"cost"` + CostCategory string `json:"cost_category"` + GasUsed string `json:"gas_used"` + } `json:"gas_profile"` + Version int `json:"version"` + } `json:"metadata"` + ReceiptIds []string `json:"receipt_ids"` + Status struct { + SuccessValue string `json:"SuccessValue"` + } `json:"status"` + TokensBurnt string `json:"tokens_burnt"` + } `json:"outcome"` + Proof []struct { + Direction string `json:"direction"` + Hash string `json:"hash"` + } `json:"proof"` + } `json:"receipts_outcome"` + Status struct { + SuccessValue string `json:"SuccessValue"` + } `json:"status"` + Transaction struct { + Actions []struct { + FunctionCall struct { + Args string `json:"args"` + Deposit string `json:"deposit"` + Gas int64 `json:"gas"` + MethodName string `json:"method_name"` + } `json:"FunctionCall"` + } `json:"actions"` + Hash string `json:"hash"` + Nonce int64 `json:"nonce"` + PublicKey string `json:"public_key"` + ReceiverID string `json:"receiver_id"` + Signature string `json:"signature"` + SignerID string `json:"signer_id"` + } `json:"transaction"` + TransactionOutcome struct { + BlockHash string `json:"block_hash"` + ID string `json:"id"` + Outcome struct { + ExecutorID string `json:"executor_id"` + GasBurnt int64 `json:"gas_burnt"` + Logs []interface{} `json:"logs"` + Metadata struct { + GasProfile interface{} `json:"gas_profile"` + Version int `json:"version"` + } `json:"metadata"` + ReceiptIds []string `json:"receipt_ids"` + Status struct { + SuccessReceiptID string `json:"SuccessReceiptId"` + } `json:"status"` + TokensBurnt string `json:"tokens_burnt"` + } `json:"outcome"` + Proof []struct { + Direction string `json:"direction"` + Hash string `json:"hash"` + } `json:"proof"` + } `json:"transaction_outcome"` +} + +func (c *Client) Tx(ctx context.Context, params interface{}) (TxResponse, error) { + var resp TxResponse + err := c.call(ctx, "tx", params, &resp) + return resp, err +} diff --git a/api/validators.go b/pkg/near/validators.go similarity index 79% rename from api/validators.go rename to pkg/near/validators.go index 5cb4bc3..6bc721a 100644 --- a/api/validators.go +++ b/pkg/near/validators.go @@ -1,8 +1,8 @@ -package api +package near -const ValidatorsMethod = "validators" +import "context" -type ValidatorsResult struct { +type ValidatorsResponse struct { CurrentValidators []struct { Validator IsSlashed bool `json:"is_slashed"` @@ -34,9 +34,8 @@ type Validator struct { Stake string `json:"stake"` } -func (c *Client) Validators() (*ValidatorsResult, error) { - var result *ValidatorsResult - err := c.do(ValidatorsMethod, "latest", &result) - - return result, err +func (c *Client) Validators(ctx context.Context, params interface{}) (ValidatorsResponse, error) { + var resp ValidatorsResponse + err := c.call(ctx, "validators", params, &resp) + return resp, err }