From e1864377c2f8fdcd9f3f58b60f956c05ee0bc913 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Mon, 17 May 2021 14:00:11 +0300 Subject: [PATCH 01/12] Update golangci-lint to 1.40.1 --- .github/workflows/ci.yaml | 2 +- Makefile | 2 +- internal/cli/access.go | 8 +-- internal/config/config.go | 64 +++++++++---------- internal/config/type_prefer_ip.go | 15 ++++- internal/config/type_statsd_tag_format.go | 10 ++- mtglib/internal/faketls/client_hello_test.go | 4 +- mtglib/internal/faketls/init.go | 26 ++++++-- mtglib/internal/faketls/record/init.go | 20 +++++- .../client-hello-bad-fa2e46cdb33e2a1b.json | 4 +- .../client-hello-ok-19dfe38384b9884b.json | 6 +- .../client-hello-ok-48f8a72a56f3174a.json | 6 +- .../client-hello-ok-651054256093c6cd.json | 6 +- .../client-hello-ok-79d01ef18a9d2621.json | 6 +- .../client-hello-ok-7a5569f05b118145.json | 6 +- .../internal/obfuscated2/handshake_frame.go | 2 + mtglib/proxy_test.go | 2 +- mtglib/secret.go | 2 +- network/init.go | 10 ++- network/network_test.go | 4 +- stats/init.go | 16 ++++- 21 files changed, 146 insertions(+), 75 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 34d76a425..3590ed397 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -70,7 +70,7 @@ jobs: - name: Run linter uses: golangci/golangci-lint-action@v2 with: - version: v1.39.0 + version: v1.40.1 docker: name: Docker diff --git a/Makefile b/Makefile index edba1fea9..145596186 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) IMAGE_NAME := mtg APP_NAME := $(IMAGE_NAME) -GOLANGCI_LINT_VERSION := v1.39.0 +GOLANGCI_LINT_VERSION := v1.40.1 VERSION_GO := $(shell go version) VERSION_DATE := $(shell date -Ru) diff --git a/internal/cli/access.go b/internal/cli/access.go index 57083ccf3..15e6bfaa1 100644 --- a/internal/cli/access.go +++ b/internal/cli/access.go @@ -26,10 +26,10 @@ type accessResponse struct { type accessResponseURLs struct { IP net.IP `json:"ip"` Port uint `json:"port"` - TgURL string `json:"tg_url"` - TgQrCode string `json:"tg_qrcode"` - TmeURL string `json:"tme_url"` - TmeQrCode string `json:"tme_qrcode"` + TgURL string `json:"tg_url"` // nolint: tagliatelle + TgQrCode string `json:"tg_qrcode"` // nolint: tagliatelle + TmeURL string `json:"tme_url"` // nolint: tagliatelle + TmeQrCode string `json:"tme_qrcode"` // nolint: tagliatelle } type Access struct { diff --git a/internal/config/config.go b/internal/config/config.go index a634c7b48..c8e88d318 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -12,23 +12,23 @@ import ( type Config struct { Debug bool `json:"debug"` Secret mtglib.Secret `json:"secret"` - BindTo TypeHostPort `json:"bind-to"` - TCPBuffer TypeBytes `json:"tcp-buffer"` - PreferIP TypePreferIP `json:"prefer-ip"` - DomainFrontingPort TypePort `json:"domain-fronting-port"` - TolerateTimeSkewness TypeDuration `json:"tolerate-time-skewness"` + BindTo TypeHostPort `json:"bindTo"` + TCPBuffer TypeBytes `json:"tcpBuffer"` + PreferIP TypePreferIP `json:"preferIp"` + DomainFrontingPort TypePort `json:"domainFrontingPort"` + TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"` Concurrency uint `json:"concurrency"` Defense struct { AntiReplay struct { Enabled bool `json:"enabled"` - MaxSize TypeBytes `json:"max-size"` - ErrorRate TypeErrorRate `json:"error-rate"` - } `json:"anti-replay"` + MaxSize TypeBytes `json:"maxSize"` + ErrorRate TypeErrorRate `json:"errorRate"` + } `json:"antiReplay"` Blocklist struct { Enabled bool `json:"enabled"` - DownloadConcurrency uint `json:"download-concurrency"` + DownloadConcurrency uint `json:"downloadConcurrency"` URLs []TypeBlocklistURI `json:"urls"` - UpdateEach TypeDuration `json:"update-each"` + UpdateEach TypeDuration `json:"updateEach"` } `json:"blocklist"` } `json:"defense"` Network struct { @@ -37,21 +37,21 @@ type Config struct { HTTP TypeDuration `json:"http"` Idle TypeDuration `json:"idle"` } `json:"timeout"` - DOHIP TypeIP `json:"doh-ip"` + DOHIP TypeIP `json:"dohIp"` Proxies []TypeURL `json:"proxies"` } `json:"network"` Stats struct { StatsD struct { Enabled bool `json:"enabled"` Address TypeHostPort `json:"address"` - MetricPrefix TypeMetricPrefix `json:"metric-prefix"` - TagFormat TypeStatsdTagFormat `json:"tag-format"` + MetricPrefix TypeMetricPrefix `json:"metricPrefix"` + TagFormat TypeStatsdTagFormat `json:"tagFormat"` } `json:"statsd"` Prometheus struct { Enabled bool `json:"enabled"` - BindTo TypeHostPort `json:"bind-to"` - HTTPPath TypeHTTPPath `json:"http-path"` - MetricPrefix TypeMetricPrefix `json:"metric-prefix"` + BindTo TypeHostPort `json:"bindTo"` + HTTPPath TypeHTTPPath `json:"httpPath"` + MetricPrefix TypeMetricPrefix `json:"metricPrefix"` } `json:"prometheus"` } `json:"stats"` } @@ -84,23 +84,23 @@ func (c *Config) String() string { type configRaw struct { Debug bool `toml:"debug" json:"debug,omitempty"` Secret string `toml:"secret" json:"secret"` - BindTo string `toml:"bind-to" json:"bind-to"` - TCPBuffer string `toml:"tcp-buffer" json:"tcp-buffer,omitempty"` - PreferIP string `toml:"prefer-ip" json:"prefer-ip,omitempty"` - DomainFrontingPort uint `toml:"domain-fronting-port" json:"domain-fronting-port,omitempty"` - TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerate-time-skewness,omitempty"` + BindTo string `toml:"bind-to" json:"bindTo"` + TCPBuffer string `toml:"tcp-buffer" json:"tcpBuffer,omitempty"` + PreferIP string `toml:"prefer-ip" json:"preferIp,omitempty"` + DomainFrontingPort uint `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"` + TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"` Concurrency uint `toml:"concurrency" json:"concurrency,omitempty"` Defense struct { AntiReplay struct { Enabled bool `toml:"enabled" json:"enabled,omitempty"` - MaxSize string `toml:"max-size" json:"max-size,omitempty"` - ErrorRate float64 `toml:"error-rate" json:"error-rate,omitempty"` - } `toml:"anti-replay" json:"anti-replay,omitempty"` + MaxSize string `toml:"max-size" json:"maxSize,omitempty"` + ErrorRate float64 `toml:"error-rate" json:"errorRate,omitempty"` + } `toml:"anti-replay" json:"antiReplay,omitempty"` Blocklist struct { Enabled bool `toml:"enabled" json:"enabled,omitempty"` - DownloadConcurrency uint `toml:"download-concurrency" json:"download-concurrency,omitempty"` + DownloadConcurrency uint `toml:"download-concurrency" json:"downloadConcurrency,omitempty"` URLs []string `toml:"urls" json:"urls,omitempty"` - UpdateEach string `toml:"update-each" json:"update-each,omitempty"` + UpdateEach string `toml:"update-each" json:"updateEach,omitempty"` } `toml:"blocklist" json:"blocklist,omitempty"` } `toml:"defense" json:"defense,omitempty"` Network struct { @@ -109,21 +109,21 @@ type configRaw struct { HTTP string `toml:"http" json:"http,omitempty"` Idle string `toml:"idle" json:"idle,omitempty"` } `toml:"timeout" json:"timeout,omitempty"` - DOHIP string `toml:"doh-ip" json:"doh-ip,omitempty"` + DOHIP string `toml:"doh-ip" json:"dohIp,omitempty"` Proxies []string `toml:"proxies" json:"proxies,omitempty"` } `toml:"network" json:"network,omitempty"` Stats struct { StatsD struct { Enabled bool `toml:"enabled" json:"enabled,omitempty"` Address string `toml:"address" json:"address,omitempty"` - MetricPrefix string `toml:"metric-prefix" json:"metric-prefix,omitempty"` - TagFormat string `toml:"tag-format" json:"tag-format,omitempty"` + MetricPrefix string `toml:"metric-prefix" json:"metricPrefix,omitempty"` + TagFormat string `toml:"tag-format" json:"tagFormat,omitempty"` } `toml:"statsd" json:"statsd,omitempty"` Prometheus struct { Enabled bool `toml:"enabled" json:"enabled,omitempty"` - BindTo string `toml:"bind-to" json:"bind-to,omitempty"` - HTTPPath string `toml:"http-path" json:"http-path,omitempty"` - MetricPrefix string `toml:"metric-prefix" json:"metric-prefix,omitempty"` + BindTo string `toml:"bind-to" json:"bindTo,omitempty"` + HTTPPath string `toml:"http-path" json:"httpPath,omitempty"` + MetricPrefix string `toml:"metric-prefix" json:"metricPrefix,omitempty"` } `toml:"prometheus" json:"prometheus,omitempty"` } `toml:"stats" json:"stats,omitempty"` } diff --git a/internal/config/type_prefer_ip.go b/internal/config/type_prefer_ip.go index 3d150d474..0b30e82b4 100644 --- a/internal/config/type_prefer_ip.go +++ b/internal/config/type_prefer_ip.go @@ -6,10 +6,21 @@ import ( ) const ( + // TypePreferIPPreferIPv4 states that you prefer to use IPv4 addresses + // but IPv6 is also possible. TypePreferIPPreferIPv4 = "prefer-ipv4" + + // TypePreferIPPreferIPv6 states that you prefer to use IPv6 addresses + // but IPv4 is also possible. TypePreferIPPreferIPv6 = "prefer-ipv6" - TypePreferOnlyIPv4 = "only-ipv4" - TypePreferOnlyIPv6 = "only-ipv6" + + // TypePreferOnlyIPv4 states that you prefer to use IPv4 addresses + // only. + TypePreferOnlyIPv4 = "only-ipv4" + + // TypePreferOnlyIPv6 states that you prefer to use IPv6 addresses + // only. + TypePreferOnlyIPv6 = "only-ipv6" ) type TypePreferIP struct { diff --git a/internal/config/type_statsd_tag_format.go b/internal/config/type_statsd_tag_format.go index 7903b0cab..51ed34b48 100644 --- a/internal/config/type_statsd_tag_format.go +++ b/internal/config/type_statsd_tag_format.go @@ -6,8 +6,16 @@ import ( ) const ( + // TypeStatsdTagFormatInfluxdb defines a tag format compatible with + // InfluxDB. TypeStatsdTagFormatInfluxdb = "influxdb" - TypeStatsdTagFormatDatadog = "datadog" + + // TypeStatsdTagFormatDatadog defines a tag format compatible with + // DataDog. + TypeStatsdTagFormatDatadog = "datadog" + + // TypeStatsdTagFormatGraphite defines a tag format compatible with + // Graphite. TypeStatsdTagFormatGraphite = "graphite" ) diff --git a/mtglib/internal/faketls/client_hello_test.go b/mtglib/internal/faketls/client_hello_test.go index 4d5ad53ec..7c9446e48 100644 --- a/mtglib/internal/faketls/client_hello_test.go +++ b/mtglib/internal/faketls/client_hello_test.go @@ -18,9 +18,9 @@ import ( type ClientHelloSnapshot struct { Time int `json:"time"` Random string `json:"random"` - SessionID string `json:"session-id"` + SessionID string `json:"sessionId"` Host string `json:"host"` - CipherSuite int `json:"cipher-suite"` + CipherSuite int `json:"cipherSuite"` Full string `json:"full"` } diff --git a/mtglib/internal/faketls/init.go b/mtglib/internal/faketls/init.go index 5bbf6f3b3..3a305a57e 100644 --- a/mtglib/internal/faketls/init.go +++ b/mtglib/internal/faketls/init.go @@ -6,25 +6,43 @@ import ( ) const ( + // RandomLen defines a size of the random digest in TLS Hellos. RandomLen = 32 - ClientHelloRandomOffset = 6 + // ClientHelloRandomOffset is an offset in ClientHello record where + // random digest is started. + ClientHelloRandomOffset = 6 + + // ClientHelloSessionIDOffset is an offset in ClientHello record where + // SessionID is started. ClientHelloSessionIDOffset = ClientHelloRandomOffset + RandomLen - ClientHelloMinLen = 4 + // ClientHelloMinLen is a minimal possible length of + // ClientHello record. + ClientHelloMinLen = 4 + + // WelcomePacketRandomOffset is an offset of random in ServerHello + // packet (including record envelope). WelcomePacketRandomOffset = 11 + // HandshakeTypeClient is a value representing a client handshake. HandshakeTypeClient = 0x01 + + // HandshakeTypeServer is a value representing a server handshake. HandshakeTypeServer = 0x02 + // ChangeCipherValue is a value representing a change cipher + // specification record. ChangeCipherValue = 0x01 + // ExtensionSNI is a value for TLS extension 'SNI'. ExtensionSNI = 0x00 ) var ( - ErrBadDigest = errors.New("bad digest") - ErrAntiReplayAttack = errors.New("antireplay attack was detected") + // ErrBadDigest is returned if given TLS Client Hello mismatches with a + // derived one. + ErrBadDigest = errors.New("bad digest") serverHelloSuffix = []byte{ 0x00, // no compression diff --git a/mtglib/internal/faketls/record/init.go b/mtglib/internal/faketls/record/init.go index 0ea3b5fbf..91951136b 100644 --- a/mtglib/internal/faketls/record/init.go +++ b/mtglib/internal/faketls/record/init.go @@ -7,9 +7,18 @@ const TLSMaxRecordSize = 65535 // max uint16 type Type uint8 const ( + // TypeChangeCipherSpec defines a byte value of the TLS record when a + // peer wants to change a specifications of the chosen cipher. TypeChangeCipherSpec Type = 0x14 - TypeHandshake Type = 0x16 - TypeApplicationData Type = 0x17 + + // TypeHandshake defines a byte value of the TLS record when a peer + // initiates a new TLS connection and wants to make a handshake + // ceremony. + TypeHandshake Type = 0x16 + + // TypeApplicationData defines a byte value of the TLS record when a + // peer sends an user data, not a control frames. + TypeApplicationData Type = 0x17 ) func (t Type) String() string { @@ -37,9 +46,16 @@ func (t Type) Valid() error { type Version uint16 const ( + // Version10 defines a TLS1.0. Version10 Version = 769 // 0x03 0x01 + + // Version11 defines a TLS1.1. Version11 Version = 770 // 0x03 0x02 + + // Version12 defines a TLS1.2. Version12 Version = 771 // 0x03 0x03 + + // Version13 defines a TLS1.3. Version13 Version = 772 // 0x03 0x04 ) diff --git a/mtglib/internal/faketls/testdata/client-hello-bad-fa2e46cdb33e2a1b.json b/mtglib/internal/faketls/testdata/client-hello-bad-fa2e46cdb33e2a1b.json index 046a12286..68bfd3819 100644 --- a/mtglib/internal/faketls/testdata/client-hello-bad-fa2e46cdb33e2a1b.json +++ b/mtglib/internal/faketls/testdata/client-hello-bad-fa2e46cdb33e2a1b.json @@ -1,8 +1,8 @@ { "time": 1617181365, "random": "XvCPc3aAbHbhRLv0kUmy6BfPZOGvsused5/HNsKXEPs=", - "session-id": "St2BZ2uHMFn3B2trD1jfdtpjoJOOg6JBeLhFcyCMCq4=", + "sessionId": "St2BZ2uHMFn3B2trD1jfdtpjoJOOg6JBeLhFcyCMCq4=", "host": "storage.googleapis.com", - "cipher-suite": 4867, + "cipherSuite": 4867, "full": "AQAB/AMDXvCPc3aAbHbhRLv0kUmy6BfPZOGvsused5/HNsKXEPsgSt2BZ2uHMFn3B2trD1jfdtpjoJOOg6JBeLhFcyCACq4ANBMDEwETAsAswCvAJMAjwArACcypwDDAL8AowCfAFMATzKgAnQCcAD0APAA1AC/ACMASAAoBAAF//wEAAQAAAAAbABkAABZzdG9yYWdlLmdvb2dsZWFwaXMuY29tABcAAAANABgAFgQDCAQEAQUDAgMIBQgFBQEIBgYBAgEABQAFANAAAAAzdAAAABIAAAAQADAALgJoMgVoMi0xNgVoMi0xNQVoMi0xNAhzcGR5LzMuMQZzcGR5LzMIaHR0cC8xLjEACwACAQAAMwAmACQAHQAgB/7oLx9JElIALsLJS91H2QNyU1H0osKwIUelVndsLyIALQACAQEAKwAJCAMEAwMDAgMBAAoACgAIAB0AFwAYABkAFQChAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" } diff --git a/mtglib/internal/faketls/testdata/client-hello-ok-19dfe38384b9884b.json b/mtglib/internal/faketls/testdata/client-hello-ok-19dfe38384b9884b.json index 0a47b926c..097df60d6 100644 --- a/mtglib/internal/faketls/testdata/client-hello-ok-19dfe38384b9884b.json +++ b/mtglib/internal/faketls/testdata/client-hello-ok-19dfe38384b9884b.json @@ -1,8 +1,8 @@ { "time": 1617181365, "random": "XvCPc3aAbHbhRLv0kUmy6BfPZOGvsused5/HNsKXEPs=", - "session-id": "St2BZ2uHMFn3B2trD1jfdtpjoJOOg6JBeLhFcyCMCq4=", + "sessionId": "St2BZ2uHMFn3B2trD1jfdtpjoJOOg6JBeLhFcyCMCq4=", "host": "storage.googleapis.com", - "cipher-suite": 4867, + "cipherSuite": 4867, "full": "AQAB/AMDXvCPc3aAbHbhRLv0kUmy6BfPZOGvsused5/HNsKXEPsgSt2BZ2uHMFn3B2trD1jfdtpjoJOOg6JBeLhFcyCMCq4ANBMDEwETAsAswCvAJMAjwArACcypwDDAL8AowCfAFMATzKgAnQCcAD0APAA1AC/ACMASAAoBAAF//wEAAQAAAAAbABkAABZzdG9yYWdlLmdvb2dsZWFwaXMuY29tABcAAAANABgAFgQDCAQEAQUDAgMIBQgFBQEIBgYBAgEABQAFAQAAAAAzdAAAABIAAAAQADAALgJoMgVoMi0xNgVoMi0xNQVoMi0xNAhzcGR5LzMuMQZzcGR5LzMIaHR0cC8xLjEACwACAQAAMwAmACQAHQAgB/7oLx9JElIALsLJS91H2QNyU1H0osKwIUelVndsLyIALQACAQEAKwAJCAMEAwMDAgMBAAoACgAIAB0AFwAYABkAFQChAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" -} \ No newline at end of file +} diff --git a/mtglib/internal/faketls/testdata/client-hello-ok-48f8a72a56f3174a.json b/mtglib/internal/faketls/testdata/client-hello-ok-48f8a72a56f3174a.json index adc7cc735..f4fe585c8 100644 --- a/mtglib/internal/faketls/testdata/client-hello-ok-48f8a72a56f3174a.json +++ b/mtglib/internal/faketls/testdata/client-hello-ok-48f8a72a56f3174a.json @@ -1,8 +1,8 @@ { "time": 1617181352, "random": "oYEu33jl+zQbUKMtQbV1OHB0gXIM2y2aq9iY0QX12os=", - "session-id": "FGqA3ZFYrSlj//xl7lammNn64K9/MK2mQ3HJUGvP+8g=", + "sessionId": "FGqA3ZFYrSlj//xl7lammNn64K9/MK2mQ3HJUGvP+8g=", "host": "storage.googleapis.com", - "cipher-suite": 4867, + "cipherSuite": 4867, "full": "AQAB/AMDoYEu33jl+zQbUKMtQbV1OHB0gXIM2y2aq9iY0QX12osgFGqA3ZFYrSlj//xl7lammNn64K9/MK2mQ3HJUGvP+8gANBMDEwETAsAswCvAJMAjwArACcypwDDAL8AowCfAFMATzKgAnQCcAD0APAA1AC/ACMASAAoBAAF//wEAAQAAAAAbABkAABZzdG9yYWdlLmdvb2dsZWFwaXMuY29tABcAAAANABgAFgQDCAQEAQUDAgMIBQgFBQEIBgYBAgEABQAFAQAAAAAzdAAAABIAAAAQADAALgJoMgVoMi0xNgVoMi0xNQVoMi0xNAhzcGR5LzMuMQZzcGR5LzMIaHR0cC8xLjEACwACAQAAMwAmACQAHQAga6CocpFP8Qd4YCFR9pkaCr97po2ALj0P5nI9Nnb3UWMALQACAQEAKwAJCAMEAwMDAgMBAAoACgAIAB0AFwAYABkAFQChAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" -} \ No newline at end of file +} diff --git a/mtglib/internal/faketls/testdata/client-hello-ok-651054256093c6cd.json b/mtglib/internal/faketls/testdata/client-hello-ok-651054256093c6cd.json index c5165fb25..432f17007 100644 --- a/mtglib/internal/faketls/testdata/client-hello-ok-651054256093c6cd.json +++ b/mtglib/internal/faketls/testdata/client-hello-ok-651054256093c6cd.json @@ -1,8 +1,8 @@ { "time": 1617181352, "random": "5V5sSprk/tFIgy+x1BeKNGhLlFkqfggLpgN7GYOA1ro=", - "session-id": "jxr4d6PXPDk+Lwx3WUp9wvj8TGlOxEdrRJ0ydyJ9+H8=", + "sessionId": "jxr4d6PXPDk+Lwx3WUp9wvj8TGlOxEdrRJ0ydyJ9+H8=", "host": "storage.googleapis.com", - "cipher-suite": 4867, + "cipherSuite": 4867, "full": "AQAB/AMD5V5sSprk/tFIgy+x1BeKNGhLlFkqfggLpgN7GYOA1rogjxr4d6PXPDk+Lwx3WUp9wvj8TGlOxEdrRJ0ydyJ9+H8ANBMDEwETAsAswCvAJMAjwArACcypwDDAL8AowCfAFMATzKgAnQCcAD0APAA1AC/ACMASAAoBAAF//wEAAQAAAAAbABkAABZzdG9yYWdlLmdvb2dsZWFwaXMuY29tABcAAAANABgAFgQDCAQEAQUDAgMIBQgFBQEIBgYBAgEABQAFAQAAAAAzdAAAABIAAAAQADAALgJoMgVoMi0xNgVoMi0xNQVoMi0xNAhzcGR5LzMuMQZzcGR5LzMIaHR0cC8xLjEACwACAQAAMwAmACQAHQAgrulAaqUdKeVYM0F+pu6on/h6LBpOyzOKG4xFIKcoFk4ALQACAQEAKwAJCAMEAwMDAgMBAAoACgAIAB0AFwAYABkAFQChAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" -} \ No newline at end of file +} diff --git a/mtglib/internal/faketls/testdata/client-hello-ok-79d01ef18a9d2621.json b/mtglib/internal/faketls/testdata/client-hello-ok-79d01ef18a9d2621.json index a642fec21..0faafd89b 100644 --- a/mtglib/internal/faketls/testdata/client-hello-ok-79d01ef18a9d2621.json +++ b/mtglib/internal/faketls/testdata/client-hello-ok-79d01ef18a9d2621.json @@ -1,8 +1,8 @@ { "time": 1617181365, "random": "8xljlOhkDlkafEF5vu3e1r3fWvh8AX548wC3hLZ3szQ=", - "session-id": "00uvDYKnFyZFKyf3HlLwWGCOyeHsPFiU5UZ+Fs5pDAU=", + "sessionId": "00uvDYKnFyZFKyf3HlLwWGCOyeHsPFiU5UZ+Fs5pDAU=", "host": "storage.googleapis.com", - "cipher-suite": 4867, + "cipherSuite": 4867, "full": "AQAB/AMD8xljlOhkDlkafEF5vu3e1r3fWvh8AX548wC3hLZ3szQg00uvDYKnFyZFKyf3HlLwWGCOyeHsPFiU5UZ+Fs5pDAUANBMDEwETAsAswCvAJMAjwArACcypwDDAL8AowCfAFMATzKgAnQCcAD0APAA1AC/ACMASAAoBAAF//wEAAQAAAAAbABkAABZzdG9yYWdlLmdvb2dsZWFwaXMuY29tABcAAAANABgAFgQDCAQEAQUDAgMIBQgFBQEIBgYBAgEABQAFAQAAAAAzdAAAABIAAAAQADAALgJoMgVoMi0xNgVoMi0xNQVoMi0xNAhzcGR5LzMuMQZzcGR5LzMIaHR0cC8xLjEACwACAQAAMwAmACQAHQAg/9P7140NtKzjyDwBf99mOy1+FjRPAPHTNQ9WxHOKpV4ALQACAQEAKwAJCAMEAwMDAgMBAAoACgAIAB0AFwAYABkAFQChAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" -} \ No newline at end of file +} diff --git a/mtglib/internal/faketls/testdata/client-hello-ok-7a5569f05b118145.json b/mtglib/internal/faketls/testdata/client-hello-ok-7a5569f05b118145.json index 5b803da5c..7bd1122dd 100644 --- a/mtglib/internal/faketls/testdata/client-hello-ok-7a5569f05b118145.json +++ b/mtglib/internal/faketls/testdata/client-hello-ok-7a5569f05b118145.json @@ -1,8 +1,8 @@ { "time": 1617181352, "random": "zja3MLZ8WGSfsQRtPV75+tY6gbK3zKPi1Sy7SBBafg4=", - "session-id": "qPut2yMqXa9zGLII/872SQ3d4Tfqo0uoDb7tpkRfBnA=", + "sessionId": "qPut2yMqXa9zGLII/872SQ3d4Tfqo0uoDb7tpkRfBnA=", "host": "storage.googleapis.com", - "cipher-suite": 4867, + "cipherSuite": 4867, "full": "AQAB/AMDzja3MLZ8WGSfsQRtPV75+tY6gbK3zKPi1Sy7SBBafg4gqPut2yMqXa9zGLII/872SQ3d4Tfqo0uoDb7tpkRfBnAANBMDEwETAsAswCvAJMAjwArACcypwDDAL8AowCfAFMATzKgAnQCcAD0APAA1AC/ACMASAAoBAAF//wEAAQAAAAAbABkAABZzdG9yYWdlLmdvb2dsZWFwaXMuY29tABcAAAANABgAFgQDCAQEAQUDAgMIBQgFBQEIBgYBAgEABQAFAQAAAAAzdAAAABIAAAAQADAALgJoMgVoMi0xNgVoMi0xNQVoMi0xNAhzcGR5LzMuMQZzcGR5LzMIaHR0cC8xLjEACwACAQAAMwAmACQAHQAgXviLRAqAYJ8xOLdlcsUhldI4Xl0g/s9+y2Qrd8raPEgALQACAQEAKwAJCAMEAwMDAgMBAAoACgAIAB0AFwAYABkAFQChAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" -} \ No newline at end of file +} diff --git a/mtglib/internal/obfuscated2/handshake_frame.go b/mtglib/internal/obfuscated2/handshake_frame.go index fff548faf..4a8e15659 100644 --- a/mtglib/internal/obfuscated2/handshake_frame.go +++ b/mtglib/internal/obfuscated2/handshake_frame.go @@ -1,6 +1,8 @@ package obfuscated2 const ( + // DefaultDC defines a number of the default DC to use. This value used + // only if a value from obfuscated2 handshake frame is 0 (default). DefaultDC = 2 handshakeFrameLen = 64 diff --git a/mtglib/proxy_test.go b/mtglib/proxy_test.go index d97b8e701..59963c6a8 100644 --- a/mtglib/proxy_test.go +++ b/mtglib/proxy_test.go @@ -166,7 +166,7 @@ func (suite *ProxyTestSuite) TestHTTPSRequest() { jsonStruct := struct { Headers struct { - TraceID string `json:"X-Amzn-Trace-Id"` + TraceID string `json:"X-Amzn-Trace-Id"` // nolint: tagliatelle } `json:"headers"` }{} diff --git a/mtglib/secret.go b/mtglib/secret.go index d9c1ff36d..49fd106a8 100644 --- a/mtglib/secret.go +++ b/mtglib/secret.go @@ -56,7 +56,7 @@ func (s Secret) MarshalText() ([]byte, error) { return nil, nil } -// MarshalText is to support text.Unmarshaller interface. +// UnmarshalText is to support text.Unmarshaller interface. func (s *Secret) UnmarshalText(data []byte) error { text := string(data) if text == "" { diff --git a/network/init.go b/network/init.go index 0dc966061..b53f37c95 100644 --- a/network/init.go +++ b/network/init.go @@ -53,8 +53,14 @@ const ( // // When circuit breaker is closed, it clears an error states each // ProxyDialerResetFailuresTimeout. - ProxyDialerOpenThreshold = 5 - ProxyDialerHalfOpenTimeout = time.Minute + ProxyDialerOpenThreshold = 5 + + // ProxyDialerHalfOpenTimeout defines a halfopen timeout for circuit + // breaker. + ProxyDialerHalfOpenTimeout = time.Minute + + // ProxyDialerResetFailuresTimeout defines a timeout for resetting a + // failure. ProxyDialerResetFailuresTimeout = 10 * time.Second // DefaultDOHHostname defines a default IP address for DOH host. diff --git a/network/network_test.go b/network/network_test.go index 86b5f0ce6..891c614f6 100644 --- a/network/network_test.go +++ b/network/network_test.go @@ -42,7 +42,7 @@ func (suite *NetworkTestSuite) TestLocalHTTPRequest() { jsonStruct := struct { Headers struct { - UserAgent []string `json:"User-Agent"` + UserAgent []string `json:"User-Agent"` // nolint: tagliatelle } `json:"headers"` }{} @@ -67,7 +67,7 @@ func (suite *NetworkTestSuite) TestRealHTTPRequest() { jsonStruct := struct { Headers struct { - UserAgent string `json:"User-Agent"` + UserAgent string `json:"User-Agent"` // nolint: tagliatelle } `json:"headers"` }{} diff --git a/stats/init.go b/stats/init.go index 3a2bae710..2154c605b 100644 --- a/stats/init.go +++ b/stats/init.go @@ -90,8 +90,12 @@ const ( MetricReplayAttacks = "replay_attacks" // TagIPFamily defines a name of the 'ip_family' tag and all values. - TagIPFamily = "ip_family" + TagIPFamily = "ip_family" + + // TagIPFamilyIPv4 defines a value of 'ip_family' of IPv4. TagIPFamilyIPv4 = "ipv4" + + // TagIPFamilyIPv6 defines a value of 'ip_family' of IPv6. TagIPFamilyIPv6 = "ipv6" // TagTelegramIP defines a name of the 'telegram_ip' tag. @@ -101,7 +105,13 @@ const ( TagDC = "dc" // TagDirection defines a name of the 'direction' tag. - TagDirection = "direction" - TagDirectionToClient = "to_client" + TagDirection = "direction" + + // TagDirectionToClient defines that traffic is sent from Telegram to a + // client. + TagDirectionToClient = "to_client" + + // TagDirectionFromClient defines that traffic is sent from a client to + // Telegram. TagDirectionFromClient = "from_client" ) From ec4f0656fb7cce2de8b57e848b1eb03c7b520014 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Thu, 22 Jul 2021 13:32:25 +0300 Subject: [PATCH 02/12] Add updated version of config --- internal/config2/config.go | 81 +++++++++++++ internal/config2/config_test.go | 54 +++++++++ internal/config2/parse.go | 80 +++++++++++++ internal/config2/testdata/broken.toml | 1 + internal/config2/testdata/minimal.toml | 2 + internal/config2/testdata/only_secret.toml | 1 + internal/config2/type_blocklist_uri.go | 77 ++++++++++++ internal/config2/type_blocklist_uri_test.go | 113 ++++++++++++++++++ internal/config2/type_bool.go | 40 +++++++ internal/config2/type_bool_test.go | 113 ++++++++++++++++++ internal/config2/type_bytes.go | 55 +++++++++ internal/config2/type_bytes_test.go | 86 +++++++++++++ internal/config2/type_concurrency.go | 45 +++++++ internal/config2/type_concurrency_test.go | 73 +++++++++++ internal/config2/type_duration.go | 53 ++++++++ internal/config2/type_duration_test.go | 110 +++++++++++++++++ internal/config2/type_error_rate.go | 47 ++++++++ internal/config2/type_error_rate_test.go | 94 +++++++++++++++ internal/config2/type_hostport.go | 59 +++++++++ internal/config2/type_hostport_test.go | 88 ++++++++++++++ internal/config2/type_http_path.go | 33 +++++ internal/config2/type_http_path_test.go | 67 +++++++++++ internal/config2/type_ip.go | 45 +++++++ internal/config2/type_ip_test.go | 104 ++++++++++++++++ internal/config2/type_metric_prefix.go | 40 +++++++ internal/config2/type_metric_prefix_test.go | 70 +++++++++++ internal/config2/type_port.go | 45 +++++++ internal/config2/type_port_test.go | 71 +++++++++++ internal/config2/type_prefer_ip.go | 62 ++++++++++ internal/config2/type_prefer_ip_test.go | 113 ++++++++++++++++++ internal/config2/type_proxy_url.go | 61 ++++++++++ internal/config2/type_proxy_url_test.go | 95 +++++++++++++++ internal/config2/type_statsd_tag_format.go | 58 +++++++++ .../config2/type_statsd_tag_format_test.go | 108 +++++++++++++++++ 34 files changed, 2244 insertions(+) create mode 100644 internal/config2/config.go create mode 100644 internal/config2/config_test.go create mode 100644 internal/config2/parse.go create mode 100644 internal/config2/testdata/broken.toml create mode 100644 internal/config2/testdata/minimal.toml create mode 100644 internal/config2/testdata/only_secret.toml create mode 100644 internal/config2/type_blocklist_uri.go create mode 100644 internal/config2/type_blocklist_uri_test.go create mode 100644 internal/config2/type_bool.go create mode 100644 internal/config2/type_bool_test.go create mode 100644 internal/config2/type_bytes.go create mode 100644 internal/config2/type_bytes_test.go create mode 100644 internal/config2/type_concurrency.go create mode 100644 internal/config2/type_concurrency_test.go create mode 100644 internal/config2/type_duration.go create mode 100644 internal/config2/type_duration_test.go create mode 100644 internal/config2/type_error_rate.go create mode 100644 internal/config2/type_error_rate_test.go create mode 100644 internal/config2/type_hostport.go create mode 100644 internal/config2/type_hostport_test.go create mode 100644 internal/config2/type_http_path.go create mode 100644 internal/config2/type_http_path_test.go create mode 100644 internal/config2/type_ip.go create mode 100644 internal/config2/type_ip_test.go create mode 100644 internal/config2/type_metric_prefix.go create mode 100644 internal/config2/type_metric_prefix_test.go create mode 100644 internal/config2/type_port.go create mode 100644 internal/config2/type_port_test.go create mode 100644 internal/config2/type_prefer_ip.go create mode 100644 internal/config2/type_prefer_ip_test.go create mode 100644 internal/config2/type_proxy_url.go create mode 100644 internal/config2/type_proxy_url_test.go create mode 100644 internal/config2/type_statsd_tag_format.go create mode 100644 internal/config2/type_statsd_tag_format_test.go diff --git a/internal/config2/config.go b/internal/config2/config.go new file mode 100644 index 000000000..1b89c78c1 --- /dev/null +++ b/internal/config2/config.go @@ -0,0 +1,81 @@ +package config2 + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/9seconds/mtg/v2/mtglib" +) + +type Config struct { + Debug TypeBool `json:"debug"` + Secret mtglib.Secret `json:"secret"` + BindTo TypeHostPort `json:"bindTo"` + TCPBuffer TypeBytes `json:"tcpBuffer"` + PreferIP TypePreferIP `json:"preferIp"` + DomainFrontingPort TypePort `json:"domainFrontingPort"` + TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"` + Concurrency TypeConcurrency `json:"concurrency"` + Defense struct { + AntiReplay struct { + Enabled TypeBool `json:"enabled"` + MaxSize TypeBytes `json:"maxSize"` + ErrorRate TypeErrorRate `json:"errorRate"` + } `json:"antiReplay"` + Blocklist struct { + Enabled TypeBool `json:"enabled"` + DownloadConcurrency TypeConcurrency `json:"downloadConcurrency"` + URLs []TypeBlocklistURI `json:"urls"` + UpdateEach TypeDuration `json:"updateEach"` + } `json:"blocklist"` + } `json:"defense"` + Network struct { + Timeout struct { + TCP TypeDuration `json:"tcp"` + HTTP TypeDuration `json:"http"` + Idle TypeDuration `json:"idle"` + } `json:"timeout"` + DOHIP TypeIP `json:"dohIp"` + Proxies []TypeProxyURL `json:"proxies"` + } `json:"network"` + Stats struct { + StatsD struct { + Enabled TypeBool `json:"enabled"` + Address TypeHostPort `json:"address"` + MetricPrefix TypeMetricPrefix `json:"metricPrefix"` + TagFormat TypeStatsdTagFormat `json:"tagFormat"` + } `json:"statsd"` + Prometheus struct { + Enabled TypeBool `json:"enabled"` + BindTo TypeHostPort `json:"bindTo"` + HTTPPath TypeHTTPPath `json:"httpPath"` + MetricPrefix TypeMetricPrefix `json:"metricPrefix"` + } `json:"prometheus"` + } `json:"stats"` +} + +func (c *Config) Validate() error { + if !c.Secret.Valid() { + return fmt.Errorf("invalid secret %s", c.Secret.String()) + } + + if c.BindTo.Get("") == "" { + return fmt.Errorf("incorrect bind-to parameter %s", c.BindTo.String()) + } + + return nil +} + +func (c *Config) String() string { + buf := &bytes.Buffer{} + encoder := json.NewEncoder(buf) + + encoder.SetEscapeHTML(false) + + if err := encoder.Encode(c); err != nil { + panic(err) + } + + return buf.String() +} diff --git a/internal/config2/config_test.go b/internal/config2/config_test.go new file mode 100644 index 000000000..76d32e725 --- /dev/null +++ b/internal/config2/config_test.go @@ -0,0 +1,54 @@ +package config2_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/suite" +) + +type ConfigTestSuite struct { + suite.Suite +} + +func (suite *ConfigTestSuite) ReadConfig(filename string) []byte { + data, err := os.ReadFile(filepath.Join("testdata", filename)) + suite.NoError(err) + + return data +} + +func (suite *ConfigTestSuite) TestParseEmpty() { + _, err := config2.Parse([]byte{}) + suite.Error(err) +} + +func (suite *ConfigTestSuite) TestParseBrokenToml() { + _, err := config2.Parse(suite.ReadConfig("broken.toml")) + suite.Error(err) +} + +func (suite *ConfigTestSuite) TestParseOnlySecret() { + _, err := config2.Parse(suite.ReadConfig("only_secret.toml")) + suite.Error(err) +} + +func (suite *ConfigTestSuite) TestParseMinimalConfig() { + conf, err := config2.Parse(suite.ReadConfig("minimal.toml")) + suite.NoError(err) + suite.Equal("7oe1GqLy6TBc38CV3jx7q09nb29nbGUuY29t", conf.Secret.Base64()) + suite.Equal("0.0.0.0:3128", conf.BindTo.String()) +} + +func (suite *ConfigTestSuite) TestString() { + conf, err := config2.Parse(suite.ReadConfig("minimal.toml")) + suite.NoError(err) + suite.NotEmpty(conf.String()) +} + +func TestConfig(t *testing.T) { + t.Parallel() + suite.Run(t, &ConfigTestSuite{}) +} diff --git a/internal/config2/parse.go b/internal/config2/parse.go new file mode 100644 index 000000000..c63c76230 --- /dev/null +++ b/internal/config2/parse.go @@ -0,0 +1,80 @@ +package config2 + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/pelletier/go-toml" +) + +type tomlConfig struct { + Debug bool `toml:"debug" json:"debug,omitempty"` + Secret string `toml:"secret" json:"secret"` + BindTo string `toml:"bind-to" json:"bindTo"` + TCPBuffer string `toml:"tcp-buffer" json:"tcpBuffer,omitempty"` + PreferIP string `toml:"prefer-ip" json:"preferIp,omitempty"` + DomainFrontingPort uint `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"` + TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"` + Concurrency uint `toml:"concurrency" json:"concurrency,omitempty"` + Defense struct { + AntiReplay struct { + Enabled bool `toml:"enabled" json:"enabled,omitempty"` + MaxSize string `toml:"max-size" json:"maxSize,omitempty"` + ErrorRate float64 `toml:"error-rate" json:"errorRate,omitempty"` + } `toml:"anti-replay" json:"antiReplay,omitempty"` + Blocklist struct { + Enabled bool `toml:"enabled" json:"enabled,omitempty"` + DownloadConcurrency uint `toml:"download-concurrency" json:"downloadConcurrency,omitempty"` + URLs []string `toml:"urls" json:"urls,omitempty"` + UpdateEach string `toml:"update-each" json:"updateEach,omitempty"` + } `toml:"blocklist" json:"blocklist,omitempty"` + } `toml:"defense" json:"defense,omitempty"` + Network struct { + Timeout struct { + TCP string `toml:"tcp" json:"tcp,omitempty"` + HTTP string `toml:"http" json:"http,omitempty"` + Idle string `toml:"idle" json:"idle,omitempty"` + } `toml:"timeout" json:"timeout,omitempty"` + DOHIP string `toml:"doh-ip" json:"dohIp,omitempty"` + Proxies []string `toml:"proxies" json:"proxies,omitempty"` + } `toml:"network" json:"network,omitempty"` + Stats struct { + StatsD struct { + Enabled bool `toml:"enabled" json:"enabled,omitempty"` + Address string `toml:"address" json:"address,omitempty"` + MetricPrefix string `toml:"metric-prefix" json:"metricPrefix,omitempty"` + TagFormat string `toml:"tag-format" json:"tagFormat,omitempty"` + } `toml:"statsd" json:"statsd,omitempty"` + Prometheus struct { + Enabled bool `toml:"enabled" json:"enabled,omitempty"` + BindTo string `toml:"bind-to" json:"bindTo,omitempty"` + HTTPPath string `toml:"http-path" json:"httpPath,omitempty"` + MetricPrefix string `toml:"metric-prefix" json:"metricPrefix,omitempty"` + } `toml:"prometheus" json:"prometheus,omitempty"` + } `toml:"stats" json:"stats,omitempty"` +} + +func Parse(rawData []byte) (*Config, error) { + tomlConf := &tomlConfig{} + jsonBuf := &bytes.Buffer{} + conf := &Config{} + + jsonEncoder := json.NewEncoder(jsonBuf) + jsonEncoder.SetEscapeHTML(false) + jsonEncoder.SetIndent("", "") + + if err := toml.Unmarshal(rawData, tomlConf); err != nil { + return nil, fmt.Errorf("cannot parse toml config: %w", err) + } + + if err := jsonEncoder.Encode(tomlConf); err != nil { + panic(err) + } + + if err := json.NewDecoder(jsonBuf).Decode(conf); err != nil { + return nil, fmt.Errorf("cannot parse a config: %w", err) + } + + return conf, nil +} diff --git a/internal/config2/testdata/broken.toml b/internal/config2/testdata/broken.toml new file mode 100644 index 000000000..d95f791f5 --- /dev/null +++ b/internal/config2/testdata/broken.toml @@ -0,0 +1 @@ +s = sdfsdfds diff --git a/internal/config2/testdata/minimal.toml b/internal/config2/testdata/minimal.toml new file mode 100644 index 000000000..9d0961a33 --- /dev/null +++ b/internal/config2/testdata/minimal.toml @@ -0,0 +1,2 @@ +secret = "7oe1GqLy6TBc38CV3jx7q09nb29nbGUuY29t" +bind-to = "0.0.0.0:3128" diff --git a/internal/config2/testdata/only_secret.toml b/internal/config2/testdata/only_secret.toml new file mode 100644 index 000000000..f6b0bee57 --- /dev/null +++ b/internal/config2/testdata/only_secret.toml @@ -0,0 +1 @@ +secret = "7oe1GqLy6TBc38CV3jx7q09nb29nbGUuY29t" diff --git a/internal/config2/type_blocklist_uri.go b/internal/config2/type_blocklist_uri.go new file mode 100644 index 000000000..f73ee3c0e --- /dev/null +++ b/internal/config2/type_blocklist_uri.go @@ -0,0 +1,77 @@ +package config2 + +import ( + "fmt" + "net/url" + "os" + "path/filepath" +) + +type TypeBlocklistURI struct { + Value string +} + +func (t *TypeBlocklistURI) Set(value string) error { + if stat, err := os.Stat(value); err == nil || os.IsExist(err) { + switch { + case stat.IsDir(): + return fmt.Errorf("value is correct filepath but directory") + case stat.Mode().Perm() & 0o400 == 0: + return fmt.Errorf("value is correct filepath but not readable") + } + + value, err = filepath.Abs(value) + if err != nil { + return fmt.Errorf( + "value is correct filepath but cannot resolve absolute (%s): %w", + value, err) + } + + t.Value = value + + return nil + } + + parsedURL, err := url.Parse(value) + if err != nil { + return fmt.Errorf("incorrect url (%s): %w", value, err) + } + + switch parsedURL.Scheme { + case "http", "https": + default: + return fmt.Errorf("unknown schema %s (%s)", parsedURL.Scheme, value) + } + + if parsedURL.Host == "" { + return fmt.Errorf("incorrect url %s", value) + } + + t.Value = parsedURL.String() + + return nil +} + +func (t TypeBlocklistURI) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value +} + +func (t TypeBlocklistURI) IsRemote() bool { + return !filepath.IsAbs(t.Value) +} + +func (t *TypeBlocklistURI) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeBlocklistURI) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeBlocklistURI) String() string { + return t.Value +} diff --git a/internal/config2/type_blocklist_uri_test.go b/internal/config2/type_blocklist_uri_test.go new file mode 100644 index 000000000..cfecc8a9b --- /dev/null +++ b/internal/config2/type_blocklist_uri_test.go @@ -0,0 +1,113 @@ +package config2_test + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeBlocklistURITestStruct struct { + Value config2.TypeBlocklistURI `json:"value"` +} + +type TypeBlocklistURITestSuite struct { + suite.Suite + + directory string + absDirectory string +} + +func (suite *TypeBlocklistURITestSuite) SetupSuite() { + dir, _ := os.Getwd() + absDir, _ := filepath.Abs(dir) + + suite.directory = dir + suite.absDirectory = absDir +} + +func (suite *TypeBlocklistURITestSuite) TestUnmarshalFail() { + testData := []string{ + "gopher://lalala", + "https:///paths", + "h:/=", + filepath.Join(suite.directory, "___"), + filepath.Join(suite.absDirectory, "___"), + suite.directory, + suite.absDirectory, + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeBlocklistURITestStruct{})) + }) + } +} + +func (suite *TypeBlocklistURITestSuite) TestUnmarshalOk() { + testData := []string{ + "http://lalala", + "https://lalala", + "https://lalala/path", + filepath.Join(suite.directory, "config.go"), + filepath.Join(suite.absDirectory, "config.go"), + } + + for _, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + testStruct := &typeBlocklistURITestStruct{} + + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.EqualValues(t, value, testStruct.Value.Get("")) + + if strings.HasPrefix(value, "http") { + assert.True(t, testStruct.Value.IsRemote()) + } else { + assert.False(t, testStruct.Value.IsRemote()) + } + }) + } +} + +func (suite *TypeBlocklistURITestSuite) TestMarshalOk() { + testStruct := &typeBlocklistURITestStruct{ + Value: config2.TypeBlocklistURI{ + Value: "http://some.url/with/path", + }, + } + + data, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value": "http://some.url/with/path"}`, string(data)) +} + +func (suite *TypeBlocklistURITestSuite) TestGet() { + value := config2.TypeBlocklistURI{} + suite.Equal("/path", value.Get("/path")) + + suite.NoError(value.Set("http://lalala.ru")) + suite.Equal("http://lalala.ru", value.Get("/path")) + suite.Equal("http://lalala.ru", value.Get("")) +} + +func TestTypeBlocklistURI(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeBlocklistURITestSuite{}) +} diff --git a/internal/config2/type_bool.go b/internal/config2/type_bool.go new file mode 100644 index 000000000..490d610a9 --- /dev/null +++ b/internal/config2/type_bool.go @@ -0,0 +1,40 @@ +package config2 + +import ( + "fmt" + "strconv" + "strings" +) + +type TypeBool struct { + Value bool +} + +func (t *TypeBool) Set(data string) error { + switch strings.ToLower(data) { + case "1", "y", "yes", "enabled", "true": + t.Value = true + case "0", "n", "no", "disabled", "false": + t.Value = false + default: + return fmt.Errorf("incorrect bool value %s", data) + } + + return nil +} + +func (t TypeBool) Get(defaultValue bool) bool { + return t.Value || defaultValue +} + +func (t *TypeBool) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeBool) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeBool) String() string { + return strconv.FormatBool(t.Value) +} diff --git a/internal/config2/type_bool_test.go b/internal/config2/type_bool_test.go new file mode 100644 index 000000000..0fa3b091a --- /dev/null +++ b/internal/config2/type_bool_test.go @@ -0,0 +1,113 @@ +package config2_test + +import ( + "encoding/json" + "fmt" + "strconv" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeBoolTestStruct struct { + Value config2.TypeBool `json:"value"` +} + +type TypeBoolTestSuite struct { + suite.Suite +} + +func (suite *TypeBoolTestSuite) TestUnmarshalFail() { + testData := []string{ + "", + "np", + "нет", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeBoolTestStruct{})) + }) + } +} + +func (suite *TypeBoolTestSuite) TestUnmarshalOk() { + testData := map[string]bool{ + "0": false, + "N": false, + "nO": false, + "no": false, + "dISAbLEd": false, + "False": false, + "false": false, + + "1": true, + "y": true, + "Yes": true, + "yes": true, + "enABLED": true, + "True": true, + "TRUE": true, + "true": true, + } + + for k, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": k, + }) + suite.NoError(err) + + suite.T().Run(k, func(t *testing.T) { + testStruct := &typeBoolTestStruct{} + assert.NoError(t, json.Unmarshal(data, testStruct)) + + if value { + assert.True(t, testStruct.Value.Value) + } else { + assert.False(t, testStruct.Value.Value) + } + }) + } +} + +func (suite *TypeBoolTestSuite) TestMarshalOk() { + for _, v := range []bool{true, false} { + name := strconv.FormatBool(v) + + suite.T().Run(name, func(t *testing.T) { + testStruct := typeBoolTestStruct{ + Value: config2.TypeBool{ + Value: v, + }, + } + + data, err := json.Marshal(testStruct) + assert.NoError(t, err) + assert.JSONEq(t, fmt.Sprintf(`{"value": "%s"}`, name), string(data)) + }) + } +} + +func (suite *TypeBoolTestSuite) TestGet() { + value := config2.TypeBool{} + suite.False(value.Get(false)) + suite.True(value.Get(true)) + + value.Value = true + suite.True(value.Get(false)) + suite.True(value.Get(true)) +} + +func TestTypeBool(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeBoolTestSuite{}) +} diff --git a/internal/config2/type_bytes.go b/internal/config2/type_bytes.go new file mode 100644 index 000000000..789ab5448 --- /dev/null +++ b/internal/config2/type_bytes.go @@ -0,0 +1,55 @@ +package config2 + +import ( + "fmt" + "strings" + + "github.com/alecthomas/units" +) + +var typeBytesStringCleaner = strings.NewReplacer(" ", "", "\t", "", "IB", "iB") + +type TypeBytes struct { + Value units.Base2Bytes +} + +func (t *TypeBytes) Set(value string) error { + normalizedValue := typeBytesStringCleaner.Replace(strings.ToUpper(value)) + + parsedValue, err := units.ParseBase2Bytes(normalizedValue) + if err != nil { + return fmt.Errorf("incorrect bytes value (%v): %w", value, err) + } + + if parsedValue < 0 { + return fmt.Errorf("bytes should be positive (%s)", value) + } + + t.Value = parsedValue + + return nil +} + +func (t TypeBytes) Get(defaultValue uint) uint { + if t.Value == 0 { + return defaultValue + } + + return uint(t.Value) +} + +func (t *TypeBytes) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeBytes) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeBytes) String() string { + if t.Value == 0 { + return "" + } + + return strings.ToLower(t.Value.String()) +} diff --git a/internal/config2/type_bytes_test.go b/internal/config2/type_bytes_test.go new file mode 100644 index 000000000..c153fb2f0 --- /dev/null +++ b/internal/config2/type_bytes_test.go @@ -0,0 +1,86 @@ +package config2_test + +import ( + "encoding/json" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeBytesTestStruct struct { + Value config2.TypeBytes `json:"value"` +} + +type TypeBytesTestSuite struct { + suite.Suite +} + +func (suite *TypeBytesTestSuite) TestUnmarshalFail() { + testData := []string{ + "1m", + "1", + "-1kb", + "-1kib", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeBytesTestStruct{})) + }) + } +} + +func (suite *TypeBytesTestSuite) TestUnmarshalOk() { + testData := map[string]uint{ + "1b": 1, + "1kb": 1024, + "1kib": 1024, + "2mb": 2 * 1024 * 1024, + "2mib": 2 * 1024 * 1024, + } + + for k, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": k, + }) + suite.NoError(err) + + suite.T().Run(k, func(t *testing.T) { + testStruct := &typeBytesTestStruct{} + + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.EqualValues(t, value, testStruct.Value.Get(0)) + }) + } +} + +func (suite *TypeBytesTestSuite) TestMarshalOk() { + value := typeBytesTestStruct{} + suite.NoError(value.Value.Set("1kib")) + + data, err := json.Marshal(value) + suite.NoError(err) + suite.JSONEq(`{"value": "1kib"}`, string(data)) +} + +func (suite *TypeBytesTestSuite) TestGet() { + value := config2.TypeBytes{} + suite.EqualValues(1000, value.Get(1000)) + + suite.NoError(value.Set("1mib")) + suite.EqualValues(1048576, value.Get(1000)) +} + +func TestTypeBytes(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeBytesTestSuite{}) +} diff --git a/internal/config2/type_concurrency.go b/internal/config2/type_concurrency.go new file mode 100644 index 000000000..e54f8ad4f --- /dev/null +++ b/internal/config2/type_concurrency.go @@ -0,0 +1,45 @@ +package config2 + +import ( + "fmt" + "strconv" +) + +type TypeConcurrency struct { + Value uint +} + +func (t *TypeConcurrency) Set(value string) error { + concurrencyValue, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return fmt.Errorf("Value is not uint (%s): %w", value, err) + } + + if concurrencyValue == 0 { + return fmt.Errorf("Value should be >0 (%s)", value) + } + + t.Value = uint(concurrencyValue) + + return nil +} + +func (t TypeConcurrency) Get(defaultValue uint) uint { + if t.Value == 0 { + return defaultValue + } + + return t.Value +} + +func (t *TypeConcurrency) UnmarshalJSON(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeConcurrency) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeConcurrency) String() string { + return strconv.FormatUint(uint64(t.Value), 10) +} diff --git a/internal/config2/type_concurrency_test.go b/internal/config2/type_concurrency_test.go new file mode 100644 index 000000000..f7227a5b1 --- /dev/null +++ b/internal/config2/type_concurrency_test.go @@ -0,0 +1,73 @@ +package config2_test + +import ( + "encoding/json" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeConcurrencyTestStruct struct { + Value config2.TypeConcurrency `json:"value"` +} + +type TypeConcurrencyTestSuite struct { + suite.Suite +} + +func (suite *TypeConcurrencyTestSuite) TestUnmarshalFail() { + testData := []string{ + "-1", + "0", + "0.0", + "1.0", + "1.1", + ".", + "some_value", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeConcurrencyTestStruct{})) + }) + } +} + +func (suite *TypeConcurrencyTestSuite) TestUnmarshalOk() { + testStruct := &typeConcurrencyTestStruct{} + + suite.NoError(json.Unmarshal([]byte(`{"value": 1}`), testStruct)) + suite.EqualValues(1, testStruct.Value.Get(2)) +} + +func (suite *TypeConcurrencyTestSuite) TestMarshalOk() { + testStruct := &typeConcurrencyTestStruct{ + Value: config2.TypeConcurrency{ + Value: 2, + }, + } + + data, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value": 2}`, string(data)) +} + +func (suite *TypeConcurrencyTestSuite) TestGet() { + value := config2.TypeConcurrency{} + suite.EqualValues(1, value.Get(1)) + + value.Value = 3 + suite.EqualValues(3, value.Get(1)) +} + +func TestTypeConcurrency(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeConcurrencyTestSuite{}) +} diff --git a/internal/config2/type_duration.go b/internal/config2/type_duration.go new file mode 100644 index 000000000..85db148eb --- /dev/null +++ b/internal/config2/type_duration.go @@ -0,0 +1,53 @@ +package config2 + +import ( + "fmt" + "strings" + "time" +) + +var typeDurationStringCleaner = strings.NewReplacer(" ", "", "\t", "") + +type TypeDuration struct { + Value time.Duration +} + +func (t *TypeDuration) Set(value string) error { + parsedValue, err := time.ParseDuration( + typeDurationStringCleaner.Replace(strings.ToLower(value))) + if err != nil { + return fmt.Errorf("incorrect duration (%s): %w", value, err) + } + + if parsedValue < 0 { + return fmt.Errorf("duration has to be a positive: %s", value) + } + + t.Value = parsedValue + + return nil +} + +func (t TypeDuration) Get(defaultValue time.Duration) time.Duration { + if t.Value == 0 { + return defaultValue + } + + return t.Value +} + +func (t *TypeDuration) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeDuration) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeDuration) String() string { + if t.Value == 0 { + return "" + } + + return t.Value.String() +} diff --git a/internal/config2/type_duration_test.go b/internal/config2/type_duration_test.go new file mode 100644 index 000000000..3f9b21ea4 --- /dev/null +++ b/internal/config2/type_duration_test.go @@ -0,0 +1,110 @@ +package config2_test + +import ( + "encoding/json" + "testing" + "time" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeDurationTestStruct struct { + Value config2.TypeDuration `json:"value"` +} + +type TypeDurationTestSuite struct { + suite.Suite +} + +func (suite *TypeDurationTestSuite) TestUnmarshalFail() { + testData := []string{ + "-1s", + "1 seconds ago", + "1s ago", + "", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeDurationTestStruct{})) + }) + } +} + +func (suite *TypeDurationTestSuite) TestUnmarshalOk() { + testData := map[string]time.Duration{ + "1s": time.Second, + "0": 0 * time.Second, + "0s": 0 * time.Second, + "1\tM": time.Minute, + "1H": time.Hour, + "1 h": time.Hour, + } + + for k, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": k, + }) + suite.NoError(err) + + suite.T().Run(k, func(t *testing.T) { + testStruct := &typeDurationTestStruct{} + + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.Equal(t, value, testStruct.Value.Value) + }) + } +} + +func (suite *TypeDurationTestSuite) TestMarshalOk() { + testData := map[string]string{ + "1s": "1s", + "0": "", + "0s": "", + "0ms": "", + "1 H": "1h0m0s", + } + + for k, v := range testData { + value := k + expected := v + + suite.T().Run(value, func(t *testing.T) { + testStruct := &typeDurationTestStruct{} + + assert.NoError(t, testStruct.Value.Set(value)) + + data, err := json.Marshal(testStruct) + assert.NoError(t, err) + + expectedJson, err := json.Marshal(map[string]string{ + "value": expected, + }) + assert.NoError(t, err) + + assert.JSONEq(t, string(expectedJson), string(data)) + }) + } +} + +func (suite *TypeDurationTestSuite) TestGet() { + value := config2.TypeDuration{} + suite.Equal(time.Second, value.Get(time.Second)) + + value.Value = 3 * time.Second + suite.Equal(3*time.Second, value.Get(time.Hour)) +} + +func TestTypeDuration(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeDurationTestSuite{}) +} diff --git a/internal/config2/type_error_rate.go b/internal/config2/type_error_rate.go new file mode 100644 index 000000000..1ce8ecc3f --- /dev/null +++ b/internal/config2/type_error_rate.go @@ -0,0 +1,47 @@ +package config2 + +import ( + "fmt" + "strconv" +) + +const typeErrorRateIgnoreLess = 1e-8 + +type TypeErrorRate struct { + Value float64 +} + +func (t *TypeErrorRate) Set(value string) error { + parsedValue, err := strconv.ParseFloat(value, 64) + if err != nil { + return fmt.Errorf("Value is not a float (%s): %w", value, err) + } + + if parsedValue <= 0.0 || parsedValue >= 100.0 { + return fmt.Errorf("Value should be 0 < x < 100 (%s)", value) + } + + t.Value = parsedValue + + return nil +} + +func (t TypeErrorRate) Get(defaultValue float64) float64 { + if t.Value < typeErrorRateIgnoreLess { + return defaultValue + } + + return t.Value +} + +func (t *TypeErrorRate) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeErrorRate) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeErrorRate) String() string { + return strconv.FormatFloat(t.Value, 'f', -1, 64) +} diff --git a/internal/config2/type_error_rate_test.go b/internal/config2/type_error_rate_test.go new file mode 100644 index 000000000..5d8590aae --- /dev/null +++ b/internal/config2/type_error_rate_test.go @@ -0,0 +1,94 @@ +package config2_test + +import ( + "encoding/json" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeErrorRateTestStruct struct { + Value config2.TypeErrorRate `json:"value"` +} + +type TypeErrorRateTestSuite struct { + suite.Suite +} + +func (suite *TypeErrorRateTestSuite) TestUnmarshalFail() { + testData := []string{ + "", + "1s", + "1,", + "1,2", + ".", + "3.4.5", + "3.5.", + ".3.5", + "some word", + "1e2", + "-1.0", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeErrorRateTestStruct{})) + }) + } +} + +func (suite *TypeErrorRateTestSuite) TestUnmarshalOk() { + testData := map[string]float64{ + "1": 1.0, + "1.0": 1.0, + "0.5": 0.5, + ".5": 0.5, + } + + for k, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": k, + }) + suite.NoError(err) + + suite.T().Run(k, func(t *testing.T) { + testStruct := &typeErrorRateTestStruct{} + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.InEpsilon(t, value, testStruct.Value.Value, 1e-10) + }) + } +} + +func (suite *TypeErrorRateTestSuite) TestMarshalOk() { + testStruct := typeErrorRateTestStruct{ + Value: config2.TypeErrorRate{ + Value: 1.01, + }, + } + + encodedJson, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value": "1.01"}`, string(encodedJson)) +} + +func (suite *TypeErrorRateTestSuite) TestGet() { + value := config2.TypeErrorRate{} + suite.InEpsilon(1.0, value.Get(1.0), 1e-10) + + value.Value = 5.0 + suite.InEpsilon(5.0, value.Get(1.0), 1e-10) +} + +func TestTypeErrorRate(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeErrorRateTestSuite{}) +} diff --git a/internal/config2/type_hostport.go b/internal/config2/type_hostport.go new file mode 100644 index 000000000..73d45c06a --- /dev/null +++ b/internal/config2/type_hostport.go @@ -0,0 +1,59 @@ +package config2 + +import ( + "fmt" + "net" + "strconv" +) + +type TypeHostPort struct { + Value string +} + +func (t *TypeHostPort) Set(value string) error { + host, port, err := net.SplitHostPort(value) + if err != nil { + return fmt.Errorf("incorrect host:port value (%v): %w", value, err) + } + + portValue, err := strconv.ParseUint(port, 10, 16) + if err != nil { + return fmt.Errorf("incorrect port number (%v): %w", value, err) + } + + if portValue == 0 { + return fmt.Errorf("incorrect port number (%s)", value) + } + + if host == "" { + return fmt.Errorf("empty host: %s", value) + } + + if net.ParseIP(host) == nil { + return fmt.Errorf("host is not an IP address: %s", value) + } + + t.Value = net.JoinHostPort(host, port) + + return nil +} + +func (t TypeHostPort) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value +} + +func (t *TypeHostPort) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeHostPort) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeHostPort) String() string { + return t.Value +} diff --git a/internal/config2/type_hostport_test.go b/internal/config2/type_hostport_test.go new file mode 100644 index 000000000..dfaf395f6 --- /dev/null +++ b/internal/config2/type_hostport_test.go @@ -0,0 +1,88 @@ +package config2_test + +import ( + "encoding/json" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeHostPortTestStruct struct { + Value config2.TypeHostPort `json:"value"` +} + +type TypeHostPortTestSuite struct { + suite.Suite +} + +func (suite *TypeHostPortTestSuite) TestUnmarshalFail() { + testData := []string{ + ":", + ":800", + "127.0.0.1:8000000", + "12...:80", + "", + "localhost", + "google.com:", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeHostPortTestStruct{})) + }) + } +} + +func (suite *TypeHostPortTestSuite) TestUnmarshalOk() { + testData := []string{ + "127.0.0.1:80", + "10.0.0.10:6553", + } + + for _, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + testStruct := &typeHostPortTestStruct{} + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.Equal(t, value, testStruct.Value.Value) + }) + } +} + +func (suite *TypeHostPortTestSuite) TestMarshalOk() { + testStruct := typeHostPortTestStruct{ + Value: config2.TypeHostPort{ + Value: "127.0.0.1:8000", + }, + } + + data, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value": "127.0.0.1:8000"}`, string(data)) +} + +func (suite *TypeHostPortTestSuite) TestGet() { + value := config2.TypeHostPort{} + suite.Equal("127.0.0.1:9000", value.Get("127.0.0.1:9000")) + + value.Value = "127.0.0.1:80" + suite.Equal("127.0.0.1:80", value.Get("127.0.0.1:9000")) +} + +func TestTypeHostPort(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeHostPortTestSuite{}) +} diff --git a/internal/config2/type_http_path.go b/internal/config2/type_http_path.go new file mode 100644 index 000000000..d31ac57cb --- /dev/null +++ b/internal/config2/type_http_path.go @@ -0,0 +1,33 @@ +package config2 + +import "strings" + +type TypeHTTPPath struct { + Value string +} + +func (t *TypeHTTPPath) Set(value string) error { + t.Value = "/" + strings.Trim(value, "/") + + return nil +} + +func (t TypeHTTPPath) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value +} + +func (t *TypeHTTPPath) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeHTTPPath) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeHTTPPath) String() string { + return t.Value +} diff --git a/internal/config2/type_http_path_test.go b/internal/config2/type_http_path_test.go new file mode 100644 index 000000000..014907e39 --- /dev/null +++ b/internal/config2/type_http_path_test.go @@ -0,0 +1,67 @@ +package config2_test + +import ( + "encoding/json" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeHTTPPathTestStruct struct { + Value config2.TypeHTTPPath `json:"value"` +} + +type TypeHTTPPathTestSuite struct { + suite.Suite +} + +func (suite *TypeHTTPPathTestSuite) TestUnmarshalOk() { + testData := map[string]string{ + "": "/", + "/": "/", + "/path": "/path", + "path": "/path", + } + + for k, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": k, + }) + suite.NoError(err) + + suite.T().Run(k, func(t *testing.T) { + testStruct := &typeHTTPPathTestStruct{} + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.Equal(t, value, testStruct.Value.Get("")) + }) + } +} + +func (suite *TypeHTTPPathTestSuite) TestMarshalOk() { + value := typeHTTPPathTestStruct{ + Value: config2.TypeHTTPPath{ + Value: "/path", + }, + } + + data, err := json.Marshal(value) + suite.NoError(err) + suite.JSONEq(`{"value": "/path"}`, string(data)) +} + +func (suite *TypeHTTPPathTestSuite) TestGet() { + value := config2.TypeHTTPPath{} + suite.Equal("/hello", value.Get("/hello")) + + suite.NoError(value.Set("/lalala")) + suite.Equal("/lalala", value.Get("/hello")) +} + +func TestTypeHTTPPath(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeHTTPPathTestSuite{}) +} diff --git a/internal/config2/type_ip.go b/internal/config2/type_ip.go new file mode 100644 index 000000000..207d22eb8 --- /dev/null +++ b/internal/config2/type_ip.go @@ -0,0 +1,45 @@ +package config2 + +import ( + "fmt" + "net" +) + +type TypeIP struct { + Value net.IP +} + +func (t *TypeIP) Set(value string) error { + ip := net.ParseIP(value) + if ip == nil { + return fmt.Errorf("incorret ip %s", value) + } + + t.Value = ip + + return nil +} + +func (t *TypeIP) Get(defaultValue net.IP) net.IP { + if len(t.Value) == 0 { + return defaultValue + } + + return t.Value +} + +func (t *TypeIP) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeIP) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeIP) String() string { + if len(t.Value) == 0 { + return "" + } + + return t.Value.String() +} diff --git a/internal/config2/type_ip_test.go b/internal/config2/type_ip_test.go new file mode 100644 index 000000000..4659cb0b8 --- /dev/null +++ b/internal/config2/type_ip_test.go @@ -0,0 +1,104 @@ +package config2_test + +import ( + "encoding/json" + "net" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeIPTestStruct struct { + Value config2.TypeIP `json:"value"` +} + +type TypeIPTestSuite struct { + suite.Suite +} + +func (suite *TypeIPTestSuite) TestUnmarshalFail() { + testData := []string{ + "", + "....", + "0...", + "300.200.200.800", + "[]", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeIPTestStruct{})) + }) + } +} + +func (suite *TypeIPTestSuite) TestUnmarshalOk() { + testData := map[string]string{ + "2001:0db8:85a3:0000:0000:8a2e:0370:7334": "2001:db8:85a3::8a2e:370:7334", + "127.0.0.1": "127.0.0.1", + } + + for k, v := range testData { + expected := v + + data, err := json.Marshal(map[string]string{ + "value": k, + }) + suite.NoError(err) + + suite.T().Run(k, func(t *testing.T) { + testStruct := &typeIPTestStruct{} + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.Equal(t, expected, testStruct.Value.Get(nil).String()) + }) + } +} + +func (suite *TypeIPTestSuite) TestMarshalOk() { + testData := []string{ + "2001:db8:85a3::8a2e:370:7334", + "127.0.0.1", + } + + for _, v := range testData { + value := v + + suite.T().Run(v, func(t *testing.T) { + testStruct := &typeIPTestStruct{ + Value: config2.TypeIP{ + Value: net.ParseIP(value), + }, + } + + encodedJSON, err := json.Marshal(testStruct) + assert.NoError(t, err) + + expectedJSON, err := json.Marshal(map[string]string{ + "value": value, + }) + assert.NoError(t, err) + + assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) + }) + } +} + +func (suite *TypeIPTestSuite) TestGet() { + value := config2.TypeIP{} + suite.Equal("127.0.0.1", value.Get(net.ParseIP("127.0.0.1")).String()) + + suite.NoError(value.Set("127.0.0.2")) + suite.Equal("127.0.0.2", value.Get(net.ParseIP("127.0.0.1")).String()) +} + +func TestTypeIP(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeIPTestSuite{}) +} diff --git a/internal/config2/type_metric_prefix.go b/internal/config2/type_metric_prefix.go new file mode 100644 index 000000000..d8507def5 --- /dev/null +++ b/internal/config2/type_metric_prefix.go @@ -0,0 +1,40 @@ +package config2 + +import ( + "fmt" + "regexp" +) + +type TypeMetricPrefix struct { + Value string +} + +func (t *TypeMetricPrefix) Set(value string) error { + if ok, err := regexp.MatchString("^[a-z0-9]+$", value); !ok || err != nil { + return fmt.Errorf("incorrect metric prefix %s: %w", value, err) + } + + t.Value = value + + return nil +} + +func (t TypeMetricPrefix) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value +} + +func (t *TypeMetricPrefix) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeMetricPrefix) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeMetricPrefix) String() string { + return t.Value +} diff --git a/internal/config2/type_metric_prefix_test.go b/internal/config2/type_metric_prefix_test.go new file mode 100644 index 000000000..bed649753 --- /dev/null +++ b/internal/config2/type_metric_prefix_test.go @@ -0,0 +1,70 @@ +package config2_test + +import ( + "encoding/json" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeMetricPrefixTestStruct struct { + Value config2.TypeMetricPrefix `json:"value"` +} + +type TypeMetricPrefixTestSuite struct { + suite.Suite +} + +func (suite *TypeMetricPrefixTestSuite) TestUnmarshalFail() { + testData := []string{ + "", + "-", + "hello/world", + "lala*", + "++sdf++", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeMetricPrefixTestStruct{})) + }) + } +} + +func (suite *TypeMetricPrefixTestSuite) TestUnmarshalOk() { + testStruct := &typeMetricPrefixTestStruct{} + suite.NoError(json.Unmarshal([]byte(`{"value": "mtg"}`), testStruct)) + suite.Equal("mtg", testStruct.Value.Get("lalala")) +} + +func (suite *TypeMetricPrefixTestSuite) TestMarshalOk() { + testStruct := &typeMetricPrefixTestStruct{ + Value: config2.TypeMetricPrefix{ + Value: "mtg", + }, + } + + data, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value": "mtg"}`, string(data)) +} + +func (suite *TypeMetricPrefixTestSuite) TestGet() { + value := config2.TypeMetricPrefix{} + suite.Equal("lalala", value.Get("lalala")) + + value.Value = "mtg" + suite.Equal("mtg", value.Get("lalala")) +} + +func TestTypeMetricPrefix(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeMetricPrefixTestSuite{}) +} diff --git a/internal/config2/type_port.go b/internal/config2/type_port.go new file mode 100644 index 000000000..3a307d9e9 --- /dev/null +++ b/internal/config2/type_port.go @@ -0,0 +1,45 @@ +package config2 + +import ( + "fmt" + "strconv" +) + +type TypePort struct { + Value uint16 +} + +func (t *TypePort) Set(value string) error { + portValue, err := strconv.ParseUint(value, 10, 16) + if err != nil { + return fmt.Errorf("incorrect port number (%v): %w", value, err) + } + + if portValue == 0 { + return fmt.Errorf("incorrect port number (%s)", value) + } + + t.Value = uint16(portValue) + + return nil +} + +func (t TypePort) Get(defaultValue uint16) uint16 { + if t.Value == 0 { + return defaultValue + } + + return t.Value +} + +func (t *TypePort) UnmarshalJSON(data []byte) error { + return t.Set(string(data)) +} + +func (t TypePort) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypePort) String() string { + return strconv.Itoa(int(t.Value)) +} diff --git a/internal/config2/type_port_test.go b/internal/config2/type_port_test.go new file mode 100644 index 000000000..544a2e974 --- /dev/null +++ b/internal/config2/type_port_test.go @@ -0,0 +1,71 @@ +package config2_test + +import ( + "encoding/json" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typePortTestStruct struct { + Value config2.TypePort `json:"value"` +} + +type TypePortTestSuite struct { + suite.Suite +} + +func (suite *TypePortTestSuite) TestUnmarshalFail() { + testData := []string{ + "", + "port", + "0", + "-1", + "1.5", + "70000", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typePortTestStruct{})) + }) + } +} + +func (suite *TypePortTestSuite) TestUnmarshalOk() { + testStruct := &typePortTestStruct{} + suite.NoError(json.Unmarshal([]byte(`{"value": 5}`), testStruct)) + suite.EqualValues(5, testStruct.Value.Value) +} + +func (suite *TypePortTestSuite) TestMarshalOk() { + testStruct := &typePortTestStruct{ + Value: config2.TypePort{ + Value: 10, + }, + } + + data, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value":10}`, string(data)) +} + +func (suite *TypePortTestSuite) TestGet() { + value := config2.TypePort{} + suite.EqualValues(10, value.Get(10)) + + value.Value = 100 + suite.EqualValues(100, value.Get(10)) +} + +func TestTypePort(t *testing.T) { + t.Parallel() + suite.Run(t, &TypePortTestSuite{}) +} diff --git a/internal/config2/type_prefer_ip.go b/internal/config2/type_prefer_ip.go new file mode 100644 index 000000000..3370a3a69 --- /dev/null +++ b/internal/config2/type_prefer_ip.go @@ -0,0 +1,62 @@ +package config2 + +import ( + "fmt" + "strings" +) + +const ( + // TypePreferIPPreferIPv4 states that you prefer to use IPv4 addresses + // but IPv6 is also possible. + TypePreferIPPreferIPv4 = "prefer-ipv4" + + // TypePreferIPPreferIPv6 states that you prefer to use IPv6 addresses + // but IPv4 is also possible. + TypePreferIPPreferIPv6 = "prefer-ipv6" + + // TypePreferOnlyIPv4 states that you prefer to use IPv4 addresses + // only. + TypePreferOnlyIPv4 = "only-ipv4" + + // TypePreferOnlyIPv6 states that you prefer to use IPv6 addresses + // only. + TypePreferOnlyIPv6 = "only-ipv6" +) + +type TypePreferIP struct { + Value string +} + +func (t *TypePreferIP) Set(value string) error { + value = strings.ToLower(value) + + switch value { + case TypePreferIPPreferIPv4, TypePreferIPPreferIPv6, + TypePreferOnlyIPv4, TypePreferOnlyIPv6: + t.Value = value + + return nil + default: + return fmt.Errorf("unsupported ip preference: %s", value) + } +} + +func (t *TypePreferIP) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value +} + +func (t *TypePreferIP) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypePreferIP) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypePreferIP) String() string { + return t.Value +} diff --git a/internal/config2/type_prefer_ip_test.go b/internal/config2/type_prefer_ip_test.go new file mode 100644 index 000000000..420ac1369 --- /dev/null +++ b/internal/config2/type_prefer_ip_test.go @@ -0,0 +1,113 @@ +package config2_test + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typePreferIPTestStruct struct { + Value config2.TypePreferIP `json:"value"` +} + +type TypePreferIPTestSuite struct { + suite.Suite +} + +func (suite *TypePreferIPTestSuite) TestUnmarshalFail() { + testData := []string{ + "", + "prefer", + "preferipv4", + config2.TypePreferIPPreferIPv4 + "_", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typePreferIPTestStruct{})) + }) + } +} + +func (suite *TypePreferIPTestSuite) TestUnmarshalOk() { + testData := []string{ + config2.TypePreferIPPreferIPv4, + config2.TypePreferIPPreferIPv6, + config2.TypePreferOnlyIPv4, + config2.TypePreferOnlyIPv6, + strings.ToTitle(config2.TypePreferOnlyIPv4), + strings.ToTitle(config2.TypePreferOnlyIPv6), + strings.ToTitle(config2.TypePreferIPPreferIPv4), + strings.ToTitle(config2.TypePreferIPPreferIPv6), + } + + for _, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + testStruct := &typePreferIPTestStruct{} + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.Equal(t, strings.ToLower(value), testStruct.Value.Value) + }) + } +} + +func (suite *TypePreferIPTestSuite) TestMarshalOk() { + testData := []string{ + config2.TypePreferIPPreferIPv4, + config2.TypePreferIPPreferIPv6, + config2.TypePreferOnlyIPv4, + config2.TypePreferOnlyIPv6, + } + + for _, v := range testData { + value := v + + suite.T().Run(v, func(t *testing.T) { + testStruct := &typePreferIPTestStruct{ + Value: config2.TypePreferIP{ + Value: value, + }, + } + + encodedJSON, err := json.Marshal(testStruct) + assert.NoError(t, err) + + expectedJSON, err := json.Marshal(map[string]string{ + "value": value, + }) + assert.NoError(t, err) + + assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) + }) + } +} + +func (suite *TypePreferIPTestSuite) TestGet() { + value := config2.TypePreferIP{} + suite.Equal(config2.TypePreferIPPreferIPv4, + value.Get(config2.TypePreferIPPreferIPv4)) + + suite.NoError(value.Set(config2.TypePreferIPPreferIPv6)) + suite.Equal(config2.TypePreferIPPreferIPv6, + value.Get(config2.TypePreferIPPreferIPv4)) +} + +func TestTypePreferIP(t *testing.T) { + t.Parallel() + suite.Run(t, &TypePreferIPTestSuite{}) +} diff --git a/internal/config2/type_proxy_url.go b/internal/config2/type_proxy_url.go new file mode 100644 index 000000000..7336968b6 --- /dev/null +++ b/internal/config2/type_proxy_url.go @@ -0,0 +1,61 @@ +package config2 + +import ( + "fmt" + "net" + "net/url" +) + +const typeProxyURLDefaultSOCKS5Port = "1080" + +type TypeProxyURL struct { + Value *url.URL +} + +func (t *TypeProxyURL) Set(value string) error { + parsedURL, err := url.Parse(value) + if err != nil { + return fmt.Errorf("Value is not corect URL (%s): %w", value, err) + } + + if parsedURL.Host == "" { + return fmt.Errorf("url has to have a schema: %s", value) + } + + if parsedURL.Scheme != "socks5" { + return fmt.Errorf("unsupported schema: %s", parsedURL.Scheme) + } + + if _, _, err := net.SplitHostPort(parsedURL.Host); err != nil { + parsedURL.Host = net.JoinHostPort(parsedURL.Host, + typeProxyURLDefaultSOCKS5Port) + } + + t.Value = parsedURL + + return nil +} + +func (t *TypeProxyURL) Get(defaultValue *url.URL) *url.URL { + if t.Value == nil { + return defaultValue + } + + return t.Value +} + +func (t *TypeProxyURL) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeProxyURL) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeProxyURL) String() string { + if t.Value == nil { + return "" + } + + return t.Value.String() +} diff --git a/internal/config2/type_proxy_url_test.go b/internal/config2/type_proxy_url_test.go new file mode 100644 index 000000000..f0992311a --- /dev/null +++ b/internal/config2/type_proxy_url_test.go @@ -0,0 +1,95 @@ +package config2_test + +import ( + "encoding/json" + "net/url" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeProxyURLTestStruct struct { + Value config2.TypeProxyURL `json:"value"` +} + +type ProxyURLTestSuite struct { + suite.Suite +} + +func (suite *ProxyURLTestSuite) TestUnmarshalFail() { + testData := []string{ + "", + "socks5://", + "://lala", + "/path", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeProxyURLTestStruct{})) + }) + } +} + +func (suite *ProxyURLTestSuite) TestUnmarshalOk() { + testData := map[string]string{ + "socks5://127.0.0.1/?open_threshold=1": "socks5://127.0.0.1:1080/?open_threshold=1", + "socks5://127.0.0.1:80": "socks5://127.0.0.1:80", + } + + for k, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": k, + }) + suite.NoError(err) + + suite.T().Run(k, func(t *testing.T) { + testStruct := &typeProxyURLTestStruct{} + assert.NoError(t, json.Unmarshal(data, testStruct)) + + parsed, _ := url.Parse(value) + + assert.Equal(t, parsed.Scheme, testStruct.Value.Get(nil).Scheme) + assert.Equal(t, parsed.Host, testStruct.Value.Get(nil).Host) + assert.Equal(t, parsed.RawQuery, testStruct.Value.Get(nil).RawQuery) + assert.Equal(t, parsed.Path, testStruct.Value.Get(nil).Path) + }) + } +} + +func (suite *ProxyURLTestSuite) TestMarshalOk() { + parsed, _ := url.Parse("socks5://127.0.0.1:1080?open_threshold=1") + testStruct := &typeProxyURLTestStruct{ + Value: config2.TypeProxyURL{ + Value: parsed, + }, + } + + encodedJSON, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value": "socks5://127.0.0.1:1080?open_threshold=1"}`, string(encodedJSON)) +} + +func (suite *ProxyURLTestSuite) TestGet() { + emptyURL := &url.URL{} + + value := config2.TypeProxyURL{} + suite.Equal(emptyURL, value.Get(emptyURL)) + + value.Value = &url.URL{} + suite.Equal(value.Value, value.Get(emptyURL)) +} + +func TestTypeProxyURL(t *testing.T) { + t.Parallel() + suite.Run(t, &ProxyURLTestSuite{}) +} diff --git a/internal/config2/type_statsd_tag_format.go b/internal/config2/type_statsd_tag_format.go new file mode 100644 index 000000000..fb4f2674a --- /dev/null +++ b/internal/config2/type_statsd_tag_format.go @@ -0,0 +1,58 @@ +package config2 + +import ( + "fmt" + "strings" +) + +const ( + // TypeStatsdTagFormatInfluxdb defines a tag format compatible with + // InfluxDB. + TypeStatsdTagFormatInfluxdb = "influxdb" + + // TypeStatsdTagFormatDatadog defines a tag format compatible with + // DataDog. + TypeStatsdTagFormatDatadog = "datadog" + + // TypeStatsdTagFormatGraphite defines a tag format compatible with + // Graphite. + TypeStatsdTagFormatGraphite = "graphite" +) + +type TypeStatsdTagFormat struct { + Value string +} + +func (t *TypeStatsdTagFormat) Set(value string) error { + lowercasedValue := strings.ToLower(value) + + switch lowercasedValue { + case TypeStatsdTagFormatDatadog, TypeStatsdTagFormatInfluxdb, + TypeStatsdTagFormatGraphite: + t.Value = lowercasedValue + + return nil + default: + return fmt.Errorf("unknown tag format %s", value) + } +} + +func (t TypeStatsdTagFormat) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value +} + +func (t *TypeStatsdTagFormat) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t *TypeStatsdTagFormat) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t *TypeStatsdTagFormat) String() string { + return t.Value +} diff --git a/internal/config2/type_statsd_tag_format_test.go b/internal/config2/type_statsd_tag_format_test.go new file mode 100644 index 000000000..fb75b26a8 --- /dev/null +++ b/internal/config2/type_statsd_tag_format_test.go @@ -0,0 +1,108 @@ +package config2_test + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeStatsdTagFormatTestStruct struct { + Value config2.TypeStatsdTagFormat `json:"value"` +} + +type StatsdTagFormatTestSuite struct { + suite.Suite +} + +func (suite *StatsdTagFormatTestSuite) TestUnmarshalFail() { + testData := []string{ + "", + "dogdog", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeStatsdTagFormatTestStruct{})) + }) + } +} + +func (suite *StatsdTagFormatTestSuite) TestUnmarshalOk() { + testData := []string{ + config2.TypeStatsdTagFormatInfluxdb, + config2.TypeStatsdTagFormatGraphite, + config2.TypeStatsdTagFormatDatadog, + strings.ToUpper(config2.TypeStatsdTagFormatInfluxdb), + strings.ToUpper(config2.TypeStatsdTagFormatGraphite), + strings.ToUpper(config2.TypeStatsdTagFormatDatadog), + } + + for _, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + testStruct := &typeStatsdTagFormatTestStruct{} + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.Equal(t, strings.ToLower(value), testStruct.Value.Value) + }) + } +} + +func (suite *StatsdTagFormatTestSuite) TestMarshalOk() { + testData := []string{ + config2.TypeStatsdTagFormatInfluxdb, + config2.TypeStatsdTagFormatGraphite, + config2.TypeStatsdTagFormatDatadog, + } + + for _, v := range testData { + value := v + + suite.T().Run(v, func(t *testing.T) { + testStruct := &typeStatsdTagFormatTestStruct{ + Value: config2.TypeStatsdTagFormat{ + Value: value, + }, + } + + encodedJSON, err := json.Marshal(testStruct) + assert.NoError(t, err) + + expectedJSON, err := json.Marshal(map[string]string{ + "value": value, + }) + assert.NoError(t, err) + + assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) + }) + } +} + +func (suite *StatsdTagFormatTestSuite) TestGet() { + value := config2.TypeStatsdTagFormat{} + suite.Equal(config2.TypeStatsdTagFormatDatadog, + value.Get(config2.TypeStatsdTagFormatDatadog)) + + suite.NoError(value.Set(config2.TypeStatsdTagFormatInfluxdb)) + suite.Equal(config2.TypeStatsdTagFormatInfluxdb, + value.Get(config2.TypeStatsdTagFormatDatadog)) +} + +func TestTypeStatsdTagFormat(t *testing.T) { + t.Parallel() + suite.Run(t, &StatsdTagFormatTestSuite{}) +} From 87ed1d1aa7dcdc97a837641bd258672ef53ce449 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Thu, 29 Jul 2021 16:21:22 +0300 Subject: [PATCH 03/12] Move config2 into config --- internal/cli/utils.go | 66 +++++++++ internal/config/config.go | 108 ++------------ internal/config/config_test.go | 2 +- internal/{config2 => config}/parse.go | 2 +- internal/config/type_blocklist_uri.go | 65 +++++---- internal/config/type_blocklist_uri_test.go | 138 +++++------------- internal/{config2 => config}/type_bool.go | 2 +- .../{config2 => config}/type_bool_test.go | 10 +- internal/config/type_bytes.go | 49 ++++--- internal/config/type_bytes_test.go | 56 ++----- .../{config2 => config}/type_concurrency.go | 2 +- .../type_concurrency_test.go | 10 +- internal/config/type_duration.go | 45 +++--- internal/config/type_duration_test.go | 74 +++++----- internal/config/type_error_rate.go | 36 +++-- internal/config/type_error_rate_test.go | 103 +++++-------- internal/config/type_hostport.go | 60 ++++---- internal/config/type_hostport_test.go | 69 +++------ internal/config/type_http_path.go | 34 ++--- internal/config/type_http_path_test.go | 72 +++------ internal/config/type_ip.go | 40 ++--- internal/config/type_ip_test.go | 77 +++++----- internal/config/type_metric_prefix.go | 37 +++-- internal/config/type_metric_prefix_test.go | 85 +++-------- internal/config/type_port.go | 40 ++--- internal/config/type_port_test.go | 94 +++--------- internal/config/type_prefer_ip.go | 43 +++--- internal/config/type_prefer_ip_test.go | 85 ++++------- .../{config2 => config}/type_proxy_url.go | 2 +- .../type_proxy_url_test.go | 13 +- internal/config/type_statsd_tag_format.go | 43 +++--- .../config/type_statsd_tag_format_test.go | 88 ++++------- internal/config/type_url.go | 71 --------- internal/config/type_url_test.go | 107 -------------- internal/config2/config.go | 81 ---------- internal/config2/config_test.go | 54 ------- internal/config2/testdata/broken.toml | 1 - internal/config2/testdata/minimal.toml | 2 - internal/config2/testdata/only_secret.toml | 1 - internal/config2/type_blocklist_uri.go | 77 ---------- internal/config2/type_blocklist_uri_test.go | 113 -------------- internal/config2/type_bytes.go | 55 ------- internal/config2/type_bytes_test.go | 86 ----------- internal/config2/type_duration.go | 53 ------- internal/config2/type_duration_test.go | 110 -------------- internal/config2/type_error_rate.go | 47 ------ internal/config2/type_error_rate_test.go | 94 ------------ internal/config2/type_hostport.go | 59 -------- internal/config2/type_hostport_test.go | 88 ----------- internal/config2/type_http_path.go | 33 ----- internal/config2/type_http_path_test.go | 67 --------- internal/config2/type_ip.go | 45 ------ internal/config2/type_ip_test.go | 104 ------------- internal/config2/type_metric_prefix.go | 40 ----- internal/config2/type_metric_prefix_test.go | 70 --------- internal/config2/type_port.go | 45 ------ internal/config2/type_port_test.go | 71 --------- internal/config2/type_prefer_ip.go | 62 -------- internal/config2/type_prefer_ip_test.go | 113 -------------- internal/config2/type_statsd_tag_format.go | 58 -------- .../config2/type_statsd_tag_format_test.go | 108 -------------- 61 files changed, 653 insertions(+), 2912 deletions(-) create mode 100644 internal/cli/utils.go rename internal/{config2 => config}/parse.go (99%) rename internal/{config2 => config}/type_bool.go (97%) rename internal/{config2 => config}/type_bool_test.go (92%) rename internal/{config2 => config}/type_concurrency.go (98%) rename internal/{config2 => config}/type_concurrency_test.go (87%) rename internal/{config2 => config}/type_proxy_url.go (98%) rename internal/{config2 => config}/type_proxy_url_test.go (90%) delete mode 100644 internal/config/type_url.go delete mode 100644 internal/config/type_url_test.go delete mode 100644 internal/config2/config.go delete mode 100644 internal/config2/config_test.go delete mode 100644 internal/config2/testdata/broken.toml delete mode 100644 internal/config2/testdata/minimal.toml delete mode 100644 internal/config2/testdata/only_secret.toml delete mode 100644 internal/config2/type_blocklist_uri.go delete mode 100644 internal/config2/type_blocklist_uri_test.go delete mode 100644 internal/config2/type_bytes.go delete mode 100644 internal/config2/type_bytes_test.go delete mode 100644 internal/config2/type_duration.go delete mode 100644 internal/config2/type_duration_test.go delete mode 100644 internal/config2/type_error_rate.go delete mode 100644 internal/config2/type_error_rate_test.go delete mode 100644 internal/config2/type_hostport.go delete mode 100644 internal/config2/type_hostport_test.go delete mode 100644 internal/config2/type_http_path.go delete mode 100644 internal/config2/type_http_path_test.go delete mode 100644 internal/config2/type_ip.go delete mode 100644 internal/config2/type_ip_test.go delete mode 100644 internal/config2/type_metric_prefix.go delete mode 100644 internal/config2/type_metric_prefix_test.go delete mode 100644 internal/config2/type_port.go delete mode 100644 internal/config2/type_port_test.go delete mode 100644 internal/config2/type_prefer_ip.go delete mode 100644 internal/config2/type_prefer_ip_test.go delete mode 100644 internal/config2/type_statsd_tag_format.go delete mode 100644 internal/config2/type_statsd_tag_format_test.go diff --git a/internal/cli/utils.go b/internal/cli/utils.go new file mode 100644 index 000000000..d07f04782 --- /dev/null +++ b/internal/cli/utils.go @@ -0,0 +1,66 @@ +package cli + +import ( + "fmt" + "net" + "net/url" + "os" + + "github.com/9seconds/mtg/v2/internal/config2" + "github.com/9seconds/mtg/v2/mtglib" + "github.com/9seconds/mtg/v2/network" +) + +func readTOMLConfig(path string) (*config2.Config, error) { + content, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("cannot read config file: %w", err) + } + + conf, err := config2.Parse(content) + if err != nil { + return nil, fmt.Errorf("cannot parse config: %w", err) + } + + return conf, nil +} + +func makeNetwork(conf *config2.Config, version string) (mtglib.Network, error) { + tcpTimeout := conf.Network.Timeout.TCP.Get(network.DefaultTimeout) + httpTimeout := conf.Network.Timeout.HTTP.Get(network.DefaultHTTPTimeout) + dohIP := conf.Network.DOHIP.Get(net.ParseIP(network.DefaultDOHHostname)).String() + bufferSize := conf.TCPBuffer.Get(network.DefaultBufferSize) + userAgent := "mtg/" + version + + baseDialer, err := network.NewDefaultDialer(tcpTimeout, int(bufferSize)) + if err != nil { + return nil, fmt.Errorf("cannot build a default dialer: %w", err) + } + + if len(conf.Network.Proxies) == 0 { + return network.NewNetwork(baseDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck + } + + proxyURLs := make([]*url.URL, 0, len(conf.Network.Proxies)) + for _, v := range conf.Network.Proxies { + if value := v.Get(nil); value != nil { + proxyURLs = append(proxyURLs, value) + } + } + + if len(proxyURLs) == 1 { + socksDialer, err := network.NewSocks5Dialer(baseDialer, proxyURLs[0]) + if err != nil { + return nil, fmt.Errorf("cannot build socks5 dialer: %w", err) + } + + return network.NewNetwork(socksDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck + } + + socksDialer, err := network.NewLoadBalancedSocks5Dialer(baseDialer, proxyURLs) + if err != nil { + return nil, fmt.Errorf("cannot build socks5 dialer: %w", err) + } + + return network.NewNetwork(socksDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck +} diff --git a/internal/config/config.go b/internal/config/config.go index c8e88d318..c05d33f6f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,27 +6,26 @@ import ( "fmt" "github.com/9seconds/mtg/v2/mtglib" - "github.com/pelletier/go-toml" ) type Config struct { - Debug bool `json:"debug"` - Secret mtglib.Secret `json:"secret"` - BindTo TypeHostPort `json:"bindTo"` - TCPBuffer TypeBytes `json:"tcpBuffer"` - PreferIP TypePreferIP `json:"preferIp"` - DomainFrontingPort TypePort `json:"domainFrontingPort"` - TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"` - Concurrency uint `json:"concurrency"` + Debug TypeBool `json:"debug"` + Secret mtglib.Secret `json:"secret"` + BindTo TypeHostPort `json:"bindTo"` + TCPBuffer TypeBytes `json:"tcpBuffer"` + PreferIP TypePreferIP `json:"preferIp"` + DomainFrontingPort TypePort `json:"domainFrontingPort"` + TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"` + Concurrency TypeConcurrency `json:"concurrency"` Defense struct { AntiReplay struct { - Enabled bool `json:"enabled"` + Enabled TypeBool `json:"enabled"` MaxSize TypeBytes `json:"maxSize"` ErrorRate TypeErrorRate `json:"errorRate"` } `json:"antiReplay"` Blocklist struct { - Enabled bool `json:"enabled"` - DownloadConcurrency uint `json:"downloadConcurrency"` + Enabled TypeBool `json:"enabled"` + DownloadConcurrency TypeConcurrency `json:"downloadConcurrency"` URLs []TypeBlocklistURI `json:"urls"` UpdateEach TypeDuration `json:"updateEach"` } `json:"blocklist"` @@ -37,18 +36,18 @@ type Config struct { HTTP TypeDuration `json:"http"` Idle TypeDuration `json:"idle"` } `json:"timeout"` - DOHIP TypeIP `json:"dohIp"` - Proxies []TypeURL `json:"proxies"` + DOHIP TypeIP `json:"dohIp"` + Proxies []TypeProxyURL `json:"proxies"` } `json:"network"` Stats struct { StatsD struct { - Enabled bool `json:"enabled"` + Enabled TypeBool `json:"enabled"` Address TypeHostPort `json:"address"` MetricPrefix TypeMetricPrefix `json:"metricPrefix"` TagFormat TypeStatsdTagFormat `json:"tagFormat"` } `json:"statsd"` Prometheus struct { - Enabled bool `json:"enabled"` + Enabled TypeBool `json:"enabled"` BindTo TypeHostPort `json:"bindTo"` HTTPPath TypeHTTPPath `json:"httpPath"` MetricPrefix TypeMetricPrefix `json:"metricPrefix"` @@ -61,7 +60,7 @@ func (c *Config) Validate() error { return fmt.Errorf("invalid secret %s", c.Secret.String()) } - if len(c.BindTo.HostValue(nil)) == 0 || c.BindTo.PortValue(0) == 0 { + if c.BindTo.Get("") == "" { return fmt.Errorf("incorrect bind-to parameter %s", c.BindTo.String()) } @@ -80,78 +79,3 @@ func (c *Config) String() string { return buf.String() } - -type configRaw struct { - Debug bool `toml:"debug" json:"debug,omitempty"` - Secret string `toml:"secret" json:"secret"` - BindTo string `toml:"bind-to" json:"bindTo"` - TCPBuffer string `toml:"tcp-buffer" json:"tcpBuffer,omitempty"` - PreferIP string `toml:"prefer-ip" json:"preferIp,omitempty"` - DomainFrontingPort uint `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"` - TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"` - Concurrency uint `toml:"concurrency" json:"concurrency,omitempty"` - Defense struct { - AntiReplay struct { - Enabled bool `toml:"enabled" json:"enabled,omitempty"` - MaxSize string `toml:"max-size" json:"maxSize,omitempty"` - ErrorRate float64 `toml:"error-rate" json:"errorRate,omitempty"` - } `toml:"anti-replay" json:"antiReplay,omitempty"` - Blocklist struct { - Enabled bool `toml:"enabled" json:"enabled,omitempty"` - DownloadConcurrency uint `toml:"download-concurrency" json:"downloadConcurrency,omitempty"` - URLs []string `toml:"urls" json:"urls,omitempty"` - UpdateEach string `toml:"update-each" json:"updateEach,omitempty"` - } `toml:"blocklist" json:"blocklist,omitempty"` - } `toml:"defense" json:"defense,omitempty"` - Network struct { - Timeout struct { - TCP string `toml:"tcp" json:"tcp,omitempty"` - HTTP string `toml:"http" json:"http,omitempty"` - Idle string `toml:"idle" json:"idle,omitempty"` - } `toml:"timeout" json:"timeout,omitempty"` - DOHIP string `toml:"doh-ip" json:"dohIp,omitempty"` - Proxies []string `toml:"proxies" json:"proxies,omitempty"` - } `toml:"network" json:"network,omitempty"` - Stats struct { - StatsD struct { - Enabled bool `toml:"enabled" json:"enabled,omitempty"` - Address string `toml:"address" json:"address,omitempty"` - MetricPrefix string `toml:"metric-prefix" json:"metricPrefix,omitempty"` - TagFormat string `toml:"tag-format" json:"tagFormat,omitempty"` - } `toml:"statsd" json:"statsd,omitempty"` - Prometheus struct { - Enabled bool `toml:"enabled" json:"enabled,omitempty"` - BindTo string `toml:"bind-to" json:"bindTo,omitempty"` - HTTPPath string `toml:"http-path" json:"httpPath,omitempty"` - MetricPrefix string `toml:"metric-prefix" json:"metricPrefix,omitempty"` - } `toml:"prometheus" json:"prometheus,omitempty"` - } `toml:"stats" json:"stats,omitempty"` -} - -func Parse(rawData []byte) (*Config, error) { - rawConf := &configRaw{} - jsonBuf := &bytes.Buffer{} - conf := &Config{} - - jsonEncoder := json.NewEncoder(jsonBuf) - jsonEncoder.SetEscapeHTML(false) - jsonEncoder.SetIndent("", "") - - if err := toml.Unmarshal(rawData, rawConf); err != nil { - return nil, fmt.Errorf("cannot parse toml config: %w", err) - } - - if err := jsonEncoder.Encode(rawConf); err != nil { - panic(err) - } - - if err := json.NewDecoder(jsonBuf).Decode(conf); err != nil { - return nil, fmt.Errorf("cannot parse a config: %w", err) - } - - if err := conf.Validate(); err != nil { - return nil, fmt.Errorf("cannot validate config: %w", err) - } - - return conf, nil -} diff --git a/internal/config/config_test.go b/internal/config/config_test.go index e6b19e060..f191221bb 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -10,7 +10,7 @@ import ( ) type ConfigTestSuite struct { - suite.Suite + suite.Suite } func (suite *ConfigTestSuite) ReadConfig(filename string) []byte { diff --git a/internal/config2/parse.go b/internal/config/parse.go similarity index 99% rename from internal/config2/parse.go rename to internal/config/parse.go index c63c76230..f4e7a0575 100644 --- a/internal/config2/parse.go +++ b/internal/config/parse.go @@ -1,4 +1,4 @@ -package config2 +package config import ( "bytes" diff --git a/internal/config/type_blocklist_uri.go b/internal/config/type_blocklist_uri.go index 6bcf6b5e6..8bd36778e 100644 --- a/internal/config/type_blocklist_uri.go +++ b/internal/config/type_blocklist_uri.go @@ -8,61 +8,70 @@ import ( ) type TypeBlocklistURI struct { - value string + Value string } -func (c *TypeBlocklistURI) UnmarshalText(data []byte) error { - if len(data) == 0 { - return nil - } - - text := string(data) - if filepath.IsAbs(text) { - if _, err := os.Stat(text); os.IsNotExist(err) { - return fmt.Errorf("filepath %s does not exist", text) +func (t *TypeBlocklistURI) Set(value string) error { + if stat, err := os.Stat(value); err == nil || os.IsExist(err) { + switch { + case stat.IsDir(): + return fmt.Errorf("value is correct filepath but directory") + case stat.Mode().Perm() & 0o400 == 0: + return fmt.Errorf("value is correct filepath but not readable") + } + + value, err = filepath.Abs(value) + if err != nil { + return fmt.Errorf( + "value is correct filepath but cannot resolve absolute (%s): %w", + value, err) } - c.value = text + t.Value = value return nil } - parsedURL, err := url.Parse(text) + parsedURL, err := url.Parse(value) if err != nil { - return fmt.Errorf("incorrect url: %w", err) + return fmt.Errorf("incorrect url (%s): %w", value, err) } switch parsedURL.Scheme { - case "http", "https": // nolint: goconst + case "http", "https": default: - return fmt.Errorf("unknown schema %s", parsedURL.Scheme) + return fmt.Errorf("unknown schema %s (%s)", parsedURL.Scheme, value) } if parsedURL.Host == "" { - return fmt.Errorf("incorrect url %s", text) + return fmt.Errorf("incorrect url %s", value) } - c.value = parsedURL.String() + t.Value = parsedURL.String() return nil } -func (c TypeBlocklistURI) MarshalText() ([]byte, error) { - return []byte(c.value), nil +func (t TypeBlocklistURI) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value } -func (c TypeBlocklistURI) String() string { - return c.value +func (t TypeBlocklistURI) IsRemote() bool { + return !filepath.IsAbs(t.Value) } -func (c TypeBlocklistURI) IsRemote() bool { - return !filepath.IsAbs(c.value) +func (t *TypeBlocklistURI) UnmarshalText(data []byte) error { + return t.Set(string(data)) } -func (c TypeBlocklistURI) Value(defaultValue string) string { - if c.value == "" { - return defaultValue - } +func (t TypeBlocklistURI) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} - return c.value +func (t TypeBlocklistURI) String() string { + return t.Value } diff --git a/internal/config/type_blocklist_uri_test.go b/internal/config/type_blocklist_uri_test.go index cdba55ac3..407bedafd 100644 --- a/internal/config/type_blocklist_uri_test.go +++ b/internal/config/type_blocklist_uri_test.go @@ -1,12 +1,10 @@ package config_test import ( - "crypto/rand" - "encoding/base64" "encoding/json" "os" "path/filepath" - "strconv" + "strings" "testing" "github.com/9seconds/mtg/v2/internal/config" @@ -20,42 +18,28 @@ type typeBlocklistURITestStruct struct { type TypeBlocklistURITestSuite struct { suite.Suite -} - -func (suite *TypeBlocklistURITestSuite) TestUnmarshalNil() { - typ := &config.TypeBlocklistURI{} - suite.NoError(typ.UnmarshalText(nil)) - suite.Empty(typ.String()) -} -func (suite *TypeBlocklistURITestSuite) TestUnknownSchema() { - typ := &config.TypeBlocklistURI{} - suite.Error(typ.UnmarshalText([]byte("gopher://lalala"))) + directory string + absDirectory string } -func (suite *TypeBlocklistURITestSuite) TestEmptyHost() { - typ := &config.TypeBlocklistURI{} - suite.Error(typ.UnmarshalText([]byte("https:///path"))) -} +func (suite *TypeBlocklistURITestSuite) SetupSuite() { + dir, _ := os.Getwd() + absDir, _ := filepath.Abs(dir) -func (suite *TypeBlocklistURITestSuite) TestIncorrectURL() { - typ := &config.TypeBlocklistURI{} - suite.Error(typ.UnmarshalText([]byte("h:/--"))) + suite.directory = dir + suite.absDirectory = absDir } func (suite *TypeBlocklistURITestSuite) TestUnmarshalFail() { - rnd := make([]byte, 48) - - rand.Read(rnd) // nolint: errcheck - - unknownPath := base64.StdEncoding.EncodeToString(rnd) - testData := []string{ - "1", - unknownPath, - "/" + unknownPath, - "http:/", - "gopher://lalalal", + "gopher://lalala", + "https:///paths", + "h:/=", + filepath.Join(suite.directory, "___"), + filepath.Join(suite.absDirectory, "___"), + suite.directory, + suite.absDirectory, } for _, v := range testData { @@ -71,13 +55,12 @@ func (suite *TypeBlocklistURITestSuite) TestUnmarshalFail() { } func (suite *TypeBlocklistURITestSuite) TestUnmarshalOk() { - dir, _ := os.Getwd() - dir, _ = filepath.Abs(dir) - testData := []string{ "http://lalala", - filepath.Join(dir, "config.go"), "https://lalala", + "https://lalala/path", + filepath.Join(suite.directory, "config.go"), + filepath.Join(suite.absDirectory, "config.go"), } for _, v := range testData { @@ -92,83 +75,36 @@ func (suite *TypeBlocklistURITestSuite) TestUnmarshalOk() { testStruct := &typeBlocklistURITestStruct{} assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.EqualValues(t, value, testStruct.Value.Value("")) + assert.EqualValues(t, value, testStruct.Value.Get("")) + + if strings.HasPrefix(value, "http") { + assert.True(t, testStruct.Value.IsRemote()) + } else { + assert.False(t, testStruct.Value.IsRemote()) + } }) } } func (suite *TypeBlocklistURITestSuite) TestMarshalOk() { - dir, _ := os.Getwd() - dir, _ = filepath.Abs(dir) - - testData := []string{ - "http://lalalal", - filepath.Join(dir, "config.go"), + testStruct := &typeBlocklistURITestStruct{ + Value: config.TypeBlocklistURI{ + Value: "http://some.url/with/path", + }, } - for _, v := range testData { - name := v - - data, err := json.Marshal(map[string]string{ - "value": name, - }) - suite.NoError(err) - - suite.T().Run(name, func(t *testing.T) { - testStruct := &typeBlocklistURITestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, name, testStruct.Value.String()) - - marshalled, err := testStruct.Value.MarshalText() - assert.NoError(t, err) - assert.Equal(t, name, string(marshalled)) - }) - } -} - -func (suite *TypeBlocklistURITestSuite) TestValue() { - testStruct := &typeBlocklistURITestStruct{} - - suite.Equal("http://lalala", testStruct.Value.Value("http://lalala")) - - data, err := json.Marshal(map[string]string{ - "value": "http://blablabla", - }) + data, err := json.Marshal(testStruct) suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) - - suite.Equal("http://blablabla", testStruct.Value.Value("")) + suite.JSONEq(`{"value": "http://some.url/with/path"}`, string(data)) } -func (suite *TypeBlocklistURITestSuite) TestIsRemote() { - dir, _ := os.Getwd() - dir, _ = filepath.Abs(dir) - - testData := map[bool]string{ - true: "http://lalalal", - false: filepath.Join(dir, "config.go"), - } - - for k, v := range testData { - ok := k - - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(strconv.FormatBool(ok), func(t *testing.T) { - testStruct := &typeBlocklistURITestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) +func (suite *TypeBlocklistURITestSuite) TestGet() { + value := config.TypeBlocklistURI{} + suite.Equal("/path", value.Get("/path")) - if ok { - assert.True(t, testStruct.Value.IsRemote()) - } else { - assert.False(t, testStruct.Value.IsRemote()) - } - }) - } + suite.NoError(value.Set("http://lalala.ru")) + suite.Equal("http://lalala.ru", value.Get("/path")) + suite.Equal("http://lalala.ru", value.Get("")) } func TestTypeBlocklistURI(t *testing.T) { diff --git a/internal/config2/type_bool.go b/internal/config/type_bool.go similarity index 97% rename from internal/config2/type_bool.go rename to internal/config/type_bool.go index 490d610a9..233a5a664 100644 --- a/internal/config2/type_bool.go +++ b/internal/config/type_bool.go @@ -1,4 +1,4 @@ -package config2 +package config import ( "fmt" diff --git a/internal/config2/type_bool_test.go b/internal/config/type_bool_test.go similarity index 92% rename from internal/config2/type_bool_test.go rename to internal/config/type_bool_test.go index 0fa3b091a..2b9028598 100644 --- a/internal/config2/type_bool_test.go +++ b/internal/config/type_bool_test.go @@ -1,4 +1,4 @@ -package config2_test +package config_test import ( "encoding/json" @@ -6,13 +6,13 @@ import ( "strconv" "testing" - "github.com/9seconds/mtg/v2/internal/config2" + "github.com/9seconds/mtg/v2/internal/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) type typeBoolTestStruct struct { - Value config2.TypeBool `json:"value"` + Value config.TypeBool `json:"value"` } type TypeBoolTestSuite struct { @@ -85,7 +85,7 @@ func (suite *TypeBoolTestSuite) TestMarshalOk() { suite.T().Run(name, func(t *testing.T) { testStruct := typeBoolTestStruct{ - Value: config2.TypeBool{ + Value: config.TypeBool{ Value: v, }, } @@ -98,7 +98,7 @@ func (suite *TypeBoolTestSuite) TestMarshalOk() { } func (suite *TypeBoolTestSuite) TestGet() { - value := config2.TypeBool{} + value := config.TypeBool{} suite.False(value.Get(false)) suite.True(value.Get(true)) diff --git a/internal/config/type_bytes.go b/internal/config/type_bytes.go index 4c7d7d6ff..412f0197a 100644 --- a/internal/config/type_bytes.go +++ b/internal/config/type_bytes.go @@ -7,48 +7,49 @@ import ( "github.com/alecthomas/units" ) +var typeBytesStringCleaner = strings.NewReplacer(" ", "", "\t", "", "IB", "iB") + type TypeBytes struct { - value units.Base2Bytes + Value units.Base2Bytes } -func (c *TypeBytes) UnmarshalText(data []byte) error { - if len(data) == 0 { - return nil - } - - normalizedData := strings.ToUpper(string(data)) - normalizedData = strings.ReplaceAll(normalizedData, "IB", "iB") +func (t *TypeBytes) Set(value string) error { + normalizedValue := typeBytesStringCleaner.Replace(strings.ToUpper(value)) - value, err := units.ParseBase2Bytes(normalizedData) + parsedValue, err := units.ParseBase2Bytes(normalizedValue) if err != nil { - return fmt.Errorf("incorrect bytes value: %w", err) + return fmt.Errorf("incorrect bytes value (%v): %w", value, err) } - if value < 0 { - return fmt.Errorf("%d should be positive number", value) + if parsedValue < 0 { + return fmt.Errorf("bytes should be positive (%s)", value) } - c.value = value + t.Value = parsedValue return nil } -func (c TypeBytes) MarshalText() ([]byte, error) { - return []byte(c.String()), nil +func (t TypeBytes) Get(defaultValue uint) uint { + if t.Value == 0 { + return defaultValue + } + + return uint(t.Value) } -func (c TypeBytes) String() string { - if c.value == 0 { - return "" - } +func (t *TypeBytes) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} - return strings.ToLower(c.value.String()) +func (t TypeBytes) MarshalText() ([]byte, error) { + return []byte(t.String()), nil } -func (c TypeBytes) Value(defaultValue uint) uint { - if c.value == 0 { - return defaultValue +func (t TypeBytes) String() string { + if t.Value == 0 { + return "" } - return uint(c.value) + return strings.ToLower(t.Value.String()) } diff --git a/internal/config/type_bytes_test.go b/internal/config/type_bytes_test.go index e3952740a..08874f43f 100644 --- a/internal/config/type_bytes_test.go +++ b/internal/config/type_bytes_test.go @@ -17,19 +17,12 @@ type TypeBytesTestSuite struct { suite.Suite } -func (suite *TypeBytesTestSuite) TestUnmarshalNil() { - typ := &config.TypeBytes{} - suite.NoError(typ.UnmarshalText(nil)) - suite.Empty(typ.String()) -} - func (suite *TypeBytesTestSuite) TestUnmarshalFail() { testData := []string{ "1m", "1", "-1kb", "-1kib", - "-1QB", } for _, v := range testData { @@ -65,53 +58,26 @@ func (suite *TypeBytesTestSuite) TestUnmarshalOk() { testStruct := &typeBytesTestStruct{} assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.EqualValues(t, value, testStruct.Value.Value(0)) + assert.EqualValues(t, value, testStruct.Value.Get(0)) }) } } func (suite *TypeBytesTestSuite) TestMarshalOk() { - testData := []string{ - "1b", - "1kib", - "2mib", - } - - for _, v := range testData { - name := v - - data, err := json.Marshal(map[string]string{ - "value": name, - }) - suite.NoError(err) - - suite.T().Run(name, func(t *testing.T) { - testStruct := &typeBytesTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, name, testStruct.Value.String()) + value := typeBytesTestStruct{} + suite.NoError(value.Value.Set("1kib")) - marshalled, err := testStruct.Value.MarshalText() - assert.NoError(t, err) - assert.Equal(t, name, string(marshalled)) - }) - } + data, err := json.Marshal(value) + suite.NoError(err) + suite.JSONEq(`{"value": "1kib"}`, string(data)) } -func (suite *TypeBytesTestSuite) TestValue() { - testStruct := &typeBytesTestStruct{} - - suite.EqualValues(0, testStruct.Value.Value(0)) - suite.EqualValues(1, testStruct.Value.Value(1)) - - data, err := json.Marshal(map[string]string{ - "value": "1kb", - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) +func (suite *TypeBytesTestSuite) TestGet() { + value := config.TypeBytes{} + suite.EqualValues(1000, value.Get(1000)) - suite.EqualValues(1024, testStruct.Value.Value(0)) - suite.EqualValues(1024, testStruct.Value.Value(1)) + suite.NoError(value.Set("1mib")) + suite.EqualValues(1048576, value.Get(1000)) } func TestTypeBytes(t *testing.T) { diff --git a/internal/config2/type_concurrency.go b/internal/config/type_concurrency.go similarity index 98% rename from internal/config2/type_concurrency.go rename to internal/config/type_concurrency.go index e54f8ad4f..1c172d71b 100644 --- a/internal/config2/type_concurrency.go +++ b/internal/config/type_concurrency.go @@ -1,4 +1,4 @@ -package config2 +package config import ( "fmt" diff --git a/internal/config2/type_concurrency_test.go b/internal/config/type_concurrency_test.go similarity index 87% rename from internal/config2/type_concurrency_test.go rename to internal/config/type_concurrency_test.go index f7227a5b1..cf18024be 100644 --- a/internal/config2/type_concurrency_test.go +++ b/internal/config/type_concurrency_test.go @@ -1,16 +1,16 @@ -package config2_test +package config_test import ( "encoding/json" "testing" - "github.com/9seconds/mtg/v2/internal/config2" + "github.com/9seconds/mtg/v2/internal/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) type typeConcurrencyTestStruct struct { - Value config2.TypeConcurrency `json:"value"` + Value config.TypeConcurrency `json:"value"` } type TypeConcurrencyTestSuite struct { @@ -49,7 +49,7 @@ func (suite *TypeConcurrencyTestSuite) TestUnmarshalOk() { func (suite *TypeConcurrencyTestSuite) TestMarshalOk() { testStruct := &typeConcurrencyTestStruct{ - Value: config2.TypeConcurrency{ + Value: config.TypeConcurrency{ Value: 2, }, } @@ -60,7 +60,7 @@ func (suite *TypeConcurrencyTestSuite) TestMarshalOk() { } func (suite *TypeConcurrencyTestSuite) TestGet() { - value := config2.TypeConcurrency{} + value := config.TypeConcurrency{} suite.EqualValues(1, value.Get(1)) value.Value = 3 diff --git a/internal/config/type_duration.go b/internal/config/type_duration.go index 8971a25a7..676f5cbd8 100644 --- a/internal/config/type_duration.go +++ b/internal/config/type_duration.go @@ -6,41 +6,48 @@ import ( "time" ) +var typeDurationStringCleaner = strings.NewReplacer(" ", "", "\t", "") + type TypeDuration struct { - value time.Duration + Value time.Duration } -func (c *TypeDuration) UnmarshalText(data []byte) error { - if len(data) == 0 { - return nil - } - - dur, err := time.ParseDuration(strings.ToLower(string(data))) +func (t *TypeDuration) Set(value string) error { + parsedValue, err := time.ParseDuration( + typeDurationStringCleaner.Replace(strings.ToLower(value))) if err != nil { - return fmt.Errorf("incorrect duration: %w", err) + return fmt.Errorf("incorrect duration (%s): %w", value, err) } - if dur < 0 { - return fmt.Errorf("%s should be positive duration", dur) + if parsedValue < 0 { + return fmt.Errorf("duration has to be a positive: %s", value) } - c.value = dur + t.Value = parsedValue return nil } -func (c TypeDuration) MarshalText() ([]byte, error) { - return []byte(c.value.String()), nil +func (t TypeDuration) Get(defaultValue time.Duration) time.Duration { + if t.Value == 0 { + return defaultValue + } + + return t.Value } -func (c TypeDuration) String() string { - return c.value.String() +func (t *TypeDuration) UnmarshalText(data []byte) error { + return t.Set(string(data)) } -func (c TypeDuration) Value(defaultValue time.Duration) time.Duration { - if c.value == 0 { - return defaultValue +func (t TypeDuration) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +func (t TypeDuration) String() string { + if t.Value == 0 { + return "" } - return c.value + return t.Value.String() } diff --git a/internal/config/type_duration_test.go b/internal/config/type_duration_test.go index ad2dd4e7b..49d674aa9 100644 --- a/internal/config/type_duration_test.go +++ b/internal/config/type_duration_test.go @@ -18,18 +18,12 @@ type TypeDurationTestSuite struct { suite.Suite } -func (suite *TypeDurationTestSuite) TestUnmarshalNil() { - typ := &config.TypeDuration{} - suite.NoError(typ.UnmarshalText(nil)) - suite.EqualValues(0, typ.Value(0)) -} - func (suite *TypeDurationTestSuite) TestUnmarshalFail() { testData := []string{ - "1t", - "1", "-1s", - "-1h", + "1 seconds ago", + "1s ago", + "", } for _, v := range testData { @@ -47,8 +41,11 @@ func (suite *TypeDurationTestSuite) TestUnmarshalFail() { func (suite *TypeDurationTestSuite) TestUnmarshalOk() { testData := map[string]time.Duration{ "1s": time.Second, - "1m": time.Minute, - "2h1s": 2*time.Hour + time.Second, + "0": 0 * time.Second, + "0s": 0 * time.Second, + "1\tM": time.Minute, + "1H": time.Hour, + "1 h": time.Hour, } for k, v := range testData { @@ -63,53 +60,48 @@ func (suite *TypeDurationTestSuite) TestUnmarshalOk() { testStruct := &typeDurationTestStruct{} assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, value, testStruct.Value.Value(0)) + assert.Equal(t, value, testStruct.Value.Value) }) } } func (suite *TypeDurationTestSuite) TestMarshalOk() { - testData := []string{ - "1s", - "1m0s", - "2h0m1s", + testData := map[string]string{ + "1s": "1s", + "0": "", + "0s": "", + "0ms": "", + "1 H": "1h0m0s", } - for _, v := range testData { - name := v - - data, err := json.Marshal(map[string]string{ - "value": name, - }) - suite.NoError(err) + for k, v := range testData { + value := k + expected := v - suite.T().Run(name, func(t *testing.T) { + suite.T().Run(value, func(t *testing.T) { testStruct := &typeDurationTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, name, testStruct.Value.String()) + assert.NoError(t, testStruct.Value.Set(value)) + + data, err := json.Marshal(testStruct) + assert.NoError(t, err) - marshalled, err := testStruct.Value.MarshalText() + expectedJson, err := json.Marshal(map[string]string{ + "value": expected, + }) assert.NoError(t, err) - assert.Equal(t, name, string(marshalled)) + + assert.JSONEq(t, string(expectedJson), string(data)) }) } } -func (suite *TypeDurationTestSuite) TestValue() { - testStruct := &typeDurationTestStruct{} - - suite.EqualValues(0, testStruct.Value.Value(0)) - suite.Equal(time.Second, testStruct.Value.Value(time.Second)) - - data, err := json.Marshal(map[string]string{ - "value": "1s", - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) +func (suite *TypeDurationTestSuite) TestGet() { + value := config.TypeDuration{} + suite.Equal(time.Second, value.Get(time.Second)) - suite.Equal(time.Second, testStruct.Value.Value(0)) - suite.Equal(time.Second, testStruct.Value.Value(time.Minute)) + value.Value = 3 * time.Second + suite.Equal(3*time.Second, value.Get(time.Hour)) } func TestTypeDuration(t *testing.T) { diff --git a/internal/config/type_error_rate.go b/internal/config/type_error_rate.go index 0214956b7..cfe6ebcf9 100644 --- a/internal/config/type_error_rate.go +++ b/internal/config/type_error_rate.go @@ -8,36 +8,40 @@ import ( const typeErrorRateIgnoreLess = 1e-8 type TypeErrorRate struct { - value float64 + Value float64 } -func (c *TypeErrorRate) UnmarshalJSON(data []byte) error { - value, err := strconv.ParseFloat(string(data), 64) +func (t *TypeErrorRate) Set(value string) error { + parsedValue, err := strconv.ParseFloat(value, 64) if err != nil { - return fmt.Errorf("incorrect float value: %w", err) + return fmt.Errorf("Value is not a float (%s): %w", value, err) } - if value <= 0 || value >= 100 { - return fmt.Errorf("%f should be 0 < x < 100", value) + if parsedValue <= 0.0 || parsedValue >= 100.0 { + return fmt.Errorf("Value should be 0 < x < 100 (%s)", value) } - c.value = value + t.Value = parsedValue return nil } -func (c *TypeErrorRate) MarshalText() ([]byte, error) { - return []byte(c.String()), nil +func (t TypeErrorRate) Get(defaultValue float64) float64 { + if t.Value < typeErrorRateIgnoreLess { + return defaultValue + } + + return t.Value } -func (c TypeErrorRate) String() string { - return strconv.FormatFloat(c.value, 'f', -1, 64) +func (t *TypeErrorRate) UnmarshalText(data []byte) error { + return t.Set(string(data)) } -func (c TypeErrorRate) Value(defaultValue float64) float64 { - if c.value < typeErrorRateIgnoreLess { - return defaultValue - } +func (t TypeErrorRate) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} - return c.value +func (t TypeErrorRate) String() string { + return strconv.FormatFloat(t.Value, 'f', -1, 64) } diff --git a/internal/config/type_error_rate_test.go b/internal/config/type_error_rate_test.go index de99f0999..aebe8f017 100644 --- a/internal/config/type_error_rate_test.go +++ b/internal/config/type_error_rate_test.go @@ -2,7 +2,6 @@ package config_test import ( "encoding/json" - "strconv" "testing" "github.com/9seconds/mtg/v2/internal/config" @@ -19,104 +18,74 @@ type TypeErrorRateTestSuite struct { } func (suite *TypeErrorRateTestSuite) TestUnmarshalFail() { - testData := []float64{ - 1000, - -100, - -0.0001, + testData := []string{ + "", + "1s", + "1,", + "1,2", + ".", + "3.4.5", + "3.5.", + ".3.5", + "some word", + "1e2", + "-1.0", } for _, v := range testData { - data, err := json.Marshal(map[string]float64{ + data, err := json.Marshal(map[string]string{ "value": v, }) suite.NoError(err) - suite.T().Run(strconv.FormatFloat(v, 'f', -1, 64), func(t *testing.T) { + suite.T().Run(v, func(t *testing.T) { assert.Error(t, json.Unmarshal(data, &typeErrorRateTestStruct{})) }) } - - data, err := json.Marshal(map[string]string{ - "value": "hello", - }) - suite.NoError(err) - suite.Error(json.Unmarshal(data, &typeErrorRateTestStruct{})) } func (suite *TypeErrorRateTestSuite) TestUnmarshalOk() { - testData := []float64{ - 1, - 55.5, - 0.0001, - 1e-6, + testData := map[string]float64{ + "1": 1.0, + "1.0": 1.0, + "0.5": 0.5, + ".5": 0.5, } - for _, v := range testData { + for k, v := range testData { value := v - data, err := json.Marshal(map[string]float64{ - "value": v, + data, err := json.Marshal(map[string]string{ + "value": k, }) suite.NoError(err) - suite.T().Run(strconv.FormatFloat(v, 'f', -1, 64), func(t *testing.T) { + suite.T().Run(k, func(t *testing.T) { testStruct := &typeErrorRateTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.InEpsilon(t, value, testStruct.Value.Value(0), 1e-10) + assert.InEpsilon(t, value, testStruct.Value.Value, 1e-10) }) } } func (suite *TypeErrorRateTestSuite) TestMarshalOk() { - testData := []float64{ - 1, - 55.5, - 0.0001, - 1e-6, + testStruct := typeErrorRateTestStruct{ + Value: config.TypeErrorRate{ + Value: 1.01, + }, } - for _, v := range testData { - value := v - - data, err := json.Marshal(map[string]float64{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(strconv.FormatFloat(v, 'f', -1, 64), func(t *testing.T) { - testStruct := &typeErrorRateTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - - parsed, err := strconv.ParseFloat(testStruct.Value.String(), 64) - assert.NoError(t, err) - assert.InEpsilon(t, value, parsed, 1e-10) - - marshalled, err := testStruct.Value.MarshalText() - assert.NoError(t, err) - - parsed, err = strconv.ParseFloat(string(marshalled), 64) - assert.NoError(t, err) - assert.InEpsilon(t, value, parsed, 1e-10) - }) - } + encodedJson, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value": "1.01"}`, string(encodedJson)) } -func (suite *TypeErrorRateTestSuite) TestValue() { - testStruct := &typeErrorRateTestStruct{} - - suite.InEpsilon(1, testStruct.Value.Value(1), 1e-10) - suite.InEpsilon(2, testStruct.Value.Value(2), 1e-10) - - data, err := json.Marshal(map[string]float64{ - "value": 1, - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) +func (suite *TypeErrorRateTestSuite) TestGet() { + value := config.TypeErrorRate{} + suite.InEpsilon(1.0, value.Get(1.0), 1e-10) - suite.InEpsilon(1, testStruct.Value.Value(2), 1e-10) - suite.InEpsilon(1, testStruct.Value.Value(3), 1e-10) + value.Value = 5.0 + suite.InEpsilon(5.0, value.Get(1.0), 1e-10) } func TestTypeErrorRate(t *testing.T) { diff --git a/internal/config/type_hostport.go b/internal/config/type_hostport.go index 4bc58f181..9f006a73f 100644 --- a/internal/config/type_hostport.go +++ b/internal/config/type_hostport.go @@ -7,61 +7,53 @@ import ( ) type TypeHostPort struct { - host TypeIP - port TypePort + Value string } -func (c *TypeHostPort) UnmarshalText(data []byte) error { - if len(data) == 0 { - return nil +func (t *TypeHostPort) Set(value string) error { + host, port, err := net.SplitHostPort(value) + if err != nil { + return fmt.Errorf("incorrect host:port value (%v): %w", value, err) } - text := string(data) - - host, port, err := net.SplitHostPort(text) + portValue, err := strconv.ParseUint(port, 10, 16) if err != nil { - return fmt.Errorf("incorrect host:port syntax: %w", err) + return fmt.Errorf("incorrect port number (%v): %w", value, err) } - if port == "" { - return fmt.Errorf("port in %s host:port pair cannot be empty", text) + if portValue == 0 { + return fmt.Errorf("incorrect port number (%s)", value) } - if err := c.port.UnmarshalJSON([]byte(port)); err != nil { - return fmt.Errorf("incorrect port in host:port: %w", err) + if host == "" { + return fmt.Errorf("empty host: %s", value) } - if err := c.host.UnmarshalText([]byte(host)); err != nil { - return fmt.Errorf("incorrect host: %w", err) + if net.ParseIP(host) == nil { + return fmt.Errorf("host is not an IP address: %s", value) } + t.Value = net.JoinHostPort(host, port) + return nil } -func (c TypeHostPort) MarshalText() ([]byte, error) { - return []byte(c.String()), nil -} +func (t TypeHostPort) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } -func (c TypeHostPort) String() string { - return c.Value(net.IP{}, 0) + return t.Value } -func (c TypeHostPort) HostValue(defaultValue net.IP) net.IP { - return c.host.Value(defaultValue) +func (t *TypeHostPort) UnmarshalText(data []byte) error { + return t.Set(string(data)) } -func (c TypeHostPort) PortValue(defaultValue uint) uint { - return c.port.Value(defaultValue) +func (t TypeHostPort) MarshalText() ([]byte, error) { + return []byte(t.String()), nil } -func (c TypeHostPort) Value(defaultHostValue net.IP, defaultPortValue uint) string { - host := c.HostValue(defaultHostValue) - port := c.PortValue(defaultPortValue) - - hostStr := "" - if len(host) > 0 { - hostStr = host.String() - } - - return net.JoinHostPort(hostStr, strconv.Itoa(int(port))) +func (t TypeHostPort) String() string { + return t.Value } diff --git a/internal/config/type_hostport_test.go b/internal/config/type_hostport_test.go index 6972e1b19..5fc24ec37 100644 --- a/internal/config/type_hostport_test.go +++ b/internal/config/type_hostport_test.go @@ -2,7 +2,6 @@ package config_test import ( "encoding/json" - "net" "testing" "github.com/9seconds/mtg/v2/internal/config" @@ -20,11 +19,13 @@ type TypeHostPortTestSuite struct { func (suite *TypeHostPortTestSuite) TestUnmarshalFail() { testData := []string{ - "10.0.0.10:aaa", - "10.0.0.10:", ":", - "xxx", - "xxx:80", + ":800", + "127.0.0.1:8000000", + "12...:80", + "", + "localhost", + "google.com:", } for _, v := range testData { @@ -41,9 +42,8 @@ func (suite *TypeHostPortTestSuite) TestUnmarshalFail() { func (suite *TypeHostPortTestSuite) TestUnmarshalOk() { testData := []string{ - "10.0.0.10:80", - "0.0.0.0:80", - ":8000", + "127.0.0.1:80", + "10.0.0.10:6553", } for _, v := range testData { @@ -56,57 +56,30 @@ func (suite *TypeHostPortTestSuite) TestUnmarshalOk() { suite.T().Run(v, func(t *testing.T) { testStruct := &typeHostPortTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.EqualValues(t, value, testStruct.Value.Value(nil, 0)) + assert.Equal(t, value, testStruct.Value.Value) }) } } func (suite *TypeHostPortTestSuite) TestMarshalOk() { - testData := []string{ - "10.0.0.10:80", - "0.0.0.0:80", - ":8000", + testStruct := typeHostPortTestStruct{ + Value: config.TypeHostPort{ + Value: "127.0.0.1:8000", + }, } - for _, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeHostPortTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, value, testStruct.Value.String()) - - marshalled, err := testStruct.Value.MarshalText() - assert.NoError(t, err) - assert.Equal(t, value, string(marshalled)) - }) - } + data, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value": "127.0.0.1:8000"}`, string(data)) } -func (suite *TypeHostPortTestSuite) TestValue() { - testStruct := &typeHostPortTestStruct{} - - suite.EqualValues("127.0.0.1:80", - testStruct.Value.Value(net.ParseIP("127.0.0.1"), 80)) - suite.EqualValues("127.1.0.1:80", - testStruct.Value.Value(net.ParseIP("127.1.0.1"), 80)) - - data, err := json.Marshal(map[string]string{ - "value": "127.0.0.1:80", - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) +func (suite *TypeHostPortTestSuite) TestGet() { + value := config.TypeHostPort{} + suite.Equal("127.0.0.1:9000", value.Get("127.0.0.1:9000")) - suite.EqualValues("127.0.0.1:80", testStruct.Value.Value(nil, 0)) - suite.EqualValues("127.0.0.1:80", testStruct.Value.Value(net.ParseIP("10.0.0.10"), 3000)) + value.Value = "127.0.0.1:80" + suite.Equal("127.0.0.1:80", value.Get("127.0.0.1:9000")) } func TestTypeHostPort(t *testing.T) { diff --git a/internal/config/type_http_path.go b/internal/config/type_http_path.go index 56a7a4e25..abd77f09f 100644 --- a/internal/config/type_http_path.go +++ b/internal/config/type_http_path.go @@ -3,33 +3,31 @@ package config import "strings" type TypeHTTPPath struct { - value string + Value string } -func (c *TypeHTTPPath) UnmarshalText(data []byte) error { - if len(data) > 0 { - c.value = "/" + strings.Trim(string(data), "/") - } +func (t *TypeHTTPPath) Set(value string) error { + t.Value = "/" + strings.Trim(value, "/") return nil } -func (c TypeHTTPPath) MarshalText() ([]byte, error) { - return []byte(c.String()), nil -} - -func (c TypeHTTPPath) String() string { - if c.value == "" { - return "/" +func (t TypeHTTPPath) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue } - return c.value + return t.Value } -func (c TypeHTTPPath) Value(defaultValue string) string { - if c.value == "" { - return defaultValue - } +func (t *TypeHTTPPath) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} + +func (t TypeHTTPPath) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} - return c.value +func (t TypeHTTPPath) String() string { + return t.Value } diff --git a/internal/config/type_http_path_test.go b/internal/config/type_http_path_test.go index 01843db8f..ee764ccfd 100644 --- a/internal/config/type_http_path_test.go +++ b/internal/config/type_http_path_test.go @@ -17,72 +17,48 @@ type TypeHTTPPathTestSuite struct { suite.Suite } -func (suite *TypeHTTPPathTestSuite) TestUnmarshal() { - testData := []string{ - "/hello", - "hello", - "hello/", - "/hello/", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeHTTPPathTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, "/hello", testStruct.Value.Value("")) - }) - } -} - -func (suite *TypeHTTPPathTestSuite) TestMarshalOk() { +func (suite *TypeHTTPPathTestSuite) TestUnmarshalOk() { testData := map[string]string{ - "": "/", - "/hello": "/hello", - "/hello/": "/hello", - "hello/": "/hello", - "hello": "/hello", + "": "/", + "/": "/", + "/path": "/path", + "path": "/path", } for k, v := range testData { - toPass := k - compareWith := v + value := v data, err := json.Marshal(map[string]string{ - "value": toPass, + "value": k, }) suite.NoError(err) - suite.T().Run(toPass, func(t *testing.T) { + suite.T().Run(k, func(t *testing.T) { testStruct := &typeHTTPPathTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, compareWith, testStruct.Value.String()) - - marshalled, err := testStruct.Value.MarshalText() - assert.NoError(t, err) - assert.Equal(t, compareWith, string(marshalled)) + assert.Equal(t, value, testStruct.Value.Get("")) }) } } -func (suite *TypeHTTPPathTestSuite) TestValue() { - testStruct := &typeHTTPPathTestStruct{} - - suite.Equal("/hello", testStruct.Value.Value("/hello")) +func (suite *TypeHTTPPathTestSuite) TestMarshalOk() { + value := typeHTTPPathTestStruct{ + Value: config.TypeHTTPPath{ + Value: "/path", + }, + } - data, err := json.Marshal(map[string]string{ - "value": "/map", - }) + data, err := json.Marshal(value) suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) + suite.JSONEq(`{"value": "/path"}`, string(data)) +} + +func (suite *TypeHTTPPathTestSuite) TestGet() { + value := config.TypeHTTPPath{} + suite.Equal("/hello", value.Get("/hello")) - suite.Equal("/map", testStruct.Value.Value("/hello")) + suite.NoError(value.Set("/lalala")) + suite.Equal("/lalala", value.Get("/hello")) } func TestTypeHTTPPath(t *testing.T) { diff --git a/internal/config/type_ip.go b/internal/config/type_ip.go index 0fe61e735..03a2f20c3 100644 --- a/internal/config/type_ip.go +++ b/internal/config/type_ip.go @@ -6,40 +6,40 @@ import ( ) type TypeIP struct { - value net.IP + Value net.IP } -func (c *TypeIP) UnmarshalText(data []byte) error { - if len(data) == 0 { - return nil - } - - ip := net.ParseIP(string(data)) +func (t *TypeIP) Set(value string) error { + ip := net.ParseIP(value) if ip == nil { - return fmt.Errorf("incorrect ip address: %s", string(data)) + return fmt.Errorf("incorret ip %s", value) } - c.value = ip + t.Value = ip return nil } -func (c *TypeIP) MarshalText() ([]byte, error) { - return []byte(c.String()), nil +func (t *TypeIP) Get(defaultValue net.IP) net.IP { + if len(t.Value) == 0 { + return defaultValue + } + + return t.Value } -func (c TypeIP) String() string { - if len(c.value) > 0 { - return c.value.String() - } +func (t *TypeIP) UnmarshalText(data []byte) error { + return t.Set(string(data)) +} - return "" +func (t TypeIP) MarshalText() ([]byte, error) { + return []byte(t.String()), nil } -func (c TypeIP) Value(defaultValue net.IP) net.IP { - if c.value == nil { - return defaultValue +func (t TypeIP) String() string { + if len(t.Value) == 0 { + return "" } - return c.value + return t.Value.String() } diff --git a/internal/config/type_ip_test.go b/internal/config/type_ip_test.go index 86f14ed40..342c195b4 100644 --- a/internal/config/type_ip_test.go +++ b/internal/config/type_ip_test.go @@ -20,10 +20,11 @@ type TypeIPTestSuite struct { func (suite *TypeIPTestSuite) TestUnmarshalFail() { testData := []string{ - "0.0.10", - "10.0.0.10:", - "xxx:80", - "2001:0db8:85a3:0000:0000:8a2e:4", + "", + "....", + "0...", + "300.200.200.800", + "[]", } for _, v := range testData { @@ -39,74 +40,62 @@ func (suite *TypeIPTestSuite) TestUnmarshalFail() { } func (suite *TypeIPTestSuite) TestUnmarshalOk() { - testData := []string{ - "0.0.0.0", - "10.0.0.10", - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + testData := map[string]string{ + "2001:0db8:85a3:0000:0000:8a2e:0370:7334": "2001:db8:85a3::8a2e:370:7334", + "127.0.0.1": "127.0.0.1", } - for _, v := range testData { - value := v + for k, v := range testData { + expected := v data, err := json.Marshal(map[string]string{ - "value": v, + "value": k, }) suite.NoError(err) - suite.T().Run(v, func(t *testing.T) { + suite.T().Run(k, func(t *testing.T) { testStruct := &typeIPTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, - net.ParseIP(value).String(), - testStruct.Value.Value(nil).String()) + assert.Equal(t, expected, testStruct.Value.Get(nil).String()) }) } } func (suite *TypeIPTestSuite) TestMarshalOk() { testData := []string{ - "0.0.0.0", - "10.0.0.10", - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "2001:db8:85a3::8a2e:370:7334", + "127.0.0.1", } for _, v := range testData { - value := net.ParseIP(v).String() - - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) + value := v suite.T().Run(v, func(t *testing.T) { - testStruct := &typeIPTestStruct{} + testStruct := &typeIPTestStruct{ + Value: config.TypeIP{ + Value: net.ParseIP(value), + }, + } - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, value, testStruct.Value.String()) + encodedJSON, err := json.Marshal(testStruct) + assert.NoError(t, err) - marshalled, err := testStruct.Value.MarshalText() + expectedJSON, err := json.Marshal(map[string]string{ + "value": value, + }) assert.NoError(t, err) - assert.Equal(t, value, string(marshalled)) + + assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) }) } } -func (suite *TypeIPTestSuite) TestValue() { - testStruct := &typeIPTestStruct{} - suite.Empty(testStruct.Value.String()) - - suite.Nil(testStruct.Value.Value(nil)) - suite.Equal("127.1.0.1", testStruct.Value.Value(net.ParseIP("127.1.0.1")).String()) - - data, err := json.Marshal(map[string]string{ - "value": "127.0.0.1", - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) +func (suite *TypeIPTestSuite) TestGet() { + value := config.TypeIP{} + suite.Equal("127.0.0.1", value.Get(net.ParseIP("127.0.0.1")).String()) - suite.Equal("127.0.0.1", testStruct.Value.Value(nil).String()) - suite.Equal("127.0.0.1", testStruct.Value.Value(net.ParseIP("10.0.0.10")).String()) + suite.NoError(value.Set("127.0.0.2")) + suite.Equal("127.0.0.2", value.Get(net.ParseIP("127.0.0.1")).String()) } func TestTypeIP(t *testing.T) { diff --git a/internal/config/type_metric_prefix.go b/internal/config/type_metric_prefix.go index 64b5f5b4c..fcd951ea8 100644 --- a/internal/config/type_metric_prefix.go +++ b/internal/config/type_metric_prefix.go @@ -6,36 +6,35 @@ import ( ) type TypeMetricPrefix struct { - value string + Value string } -func (c *TypeMetricPrefix) UnmarshalText(data []byte) error { - if len(data) == 0 { - return nil +func (t *TypeMetricPrefix) Set(value string) error { + if ok, err := regexp.MatchString("^[a-z0-9]+$", value); !ok || err != nil { + return fmt.Errorf("incorrect metric prefix %s: %w", value, err) } - prefix := string(data) - if ok, err := regexp.MatchString("^[a-z0-9]+$", prefix); !ok || err != nil { - return fmt.Errorf("incorrect metric prefix: %s", prefix) - } - - c.value = prefix + t.Value = value return nil } -func (c TypeMetricPrefix) MarshalText() ([]byte, error) { - return []byte(c.String()), nil +func (t TypeMetricPrefix) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value } -func (c TypeMetricPrefix) String() string { - return c.value +func (t *TypeMetricPrefix) UnmarshalText(data []byte) error { + return t.Set(string(data)) } -func (c TypeMetricPrefix) Value(defaultValue string) string { - if c.value == "" { - return defaultValue - } +func (t TypeMetricPrefix) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} - return c.value +func (t TypeMetricPrefix) String() string { + return t.Value } diff --git a/internal/config/type_metric_prefix_test.go b/internal/config/type_metric_prefix_test.go index 0c3727d16..eb6022995 100644 --- a/internal/config/type_metric_prefix_test.go +++ b/internal/config/type_metric_prefix_test.go @@ -17,18 +17,13 @@ type TypeMetricPrefixTestSuite struct { suite.Suite } -func (suite *TypeMetricPrefixTestSuite) TestUnmarshalNil() { - typ := &config.TypeMetricPrefix{} - suite.NoError(typ.UnmarshalText(nil)) - suite.Empty(typ.String()) -} - func (suite *TypeMetricPrefixTestSuite) TestUnmarshalFail() { testData := []string{ - "aaa.aaa", - "aaa-bbb", - "aaa:ccc", - "metric prefix", + "", + "-", + "hello/world", + "lala*", + "++sdf++", } for _, v := range testData { @@ -44,69 +39,29 @@ func (suite *TypeMetricPrefixTestSuite) TestUnmarshalFail() { } func (suite *TypeMetricPrefixTestSuite) TestUnmarshalOk() { - testData := []string{ - "mtg", - "mtg111", - } - - for _, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeMetricPrefixTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, value, testStruct.Value.Value("")) - }) - } + testStruct := &typeMetricPrefixTestStruct{} + suite.NoError(json.Unmarshal([]byte(`{"value": "mtg"}`), testStruct)) + suite.Equal("mtg", testStruct.Value.Get("lalala")) } func (suite *TypeMetricPrefixTestSuite) TestMarshalOk() { - testData := []string{ - "mtg", - "mtg111", + testStruct := &typeMetricPrefixTestStruct{ + Value: config.TypeMetricPrefix{ + Value: "mtg", + }, } - for _, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeMetricPrefixTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, value, testStruct.Value.String()) - - marshalled, err := testStruct.Value.MarshalText() - assert.NoError(t, err) - assert.Equal(t, value, string(marshalled)) - }) - } + data, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value": "mtg"}`, string(data)) } -func (suite *TypeMetricPrefixTestSuite) TestValue() { - testStruct := &typeMetricPrefixTestStruct{} - - suite.Equal("mtg", testStruct.Value.Value("mtg")) - suite.Equal("vvv", testStruct.Value.Value("vvv")) - - data, err := json.Marshal(map[string]string{ - "value": "aaa", - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) +func (suite *TypeMetricPrefixTestSuite) TestGet() { + value := config.TypeMetricPrefix{} + suite.Equal("lalala", value.Get("lalala")) - suite.Equal("aaa", testStruct.Value.Value("mtg")) - suite.Equal("aaa", testStruct.Value.Value("vvv")) + value.Value = "mtg" + suite.Equal("mtg", value.Get("lalala")) } func TestTypeMetricPrefix(t *testing.T) { diff --git a/internal/config/type_port.go b/internal/config/type_port.go index 892558ecc..f53d77e5b 100644 --- a/internal/config/type_port.go +++ b/internal/config/type_port.go @@ -6,40 +6,40 @@ import ( ) type TypePort struct { - value uint + Value uint16 } -func (c *TypePort) UnmarshalJSON(data []byte) error { - if len(data) == 0 { - return nil - } - - intValue, err := strconv.ParseUint(string(data), 10, 64) +func (t *TypePort) Set(value string) error { + portValue, err := strconv.ParseUint(value, 10, 16) if err != nil { - return fmt.Errorf("port number is not a number: %w", err) + return fmt.Errorf("incorrect port number (%v): %w", value, err) } - if intValue == 0 || intValue >= 65536 { - return fmt.Errorf("port number should be 0 < portNo < 65536: %d", intValue) + if portValue == 0 { + return fmt.Errorf("incorrect port number (%s)", value) } - c.value = uint(intValue) + t.Value = uint16(portValue) return nil } -func (c *TypePort) MarshalJSON() ([]byte, error) { - return []byte(c.String()), nil +func (t TypePort) Get(defaultValue uint16) uint16 { + if t.Value == 0 { + return defaultValue + } + + return t.Value } -func (c TypePort) String() string { - return strconv.Itoa(int(c.value)) +func (t *TypePort) UnmarshalJSON(data []byte) error { + return t.Set(string(data)) } -func (c TypePort) Value(defaultValue uint) uint { - if c.value == 0 { - return defaultValue - } +func (t TypePort) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} - return c.value +func (t TypePort) String() string { + return strconv.Itoa(int(t.Value)) } diff --git a/internal/config/type_port_test.go b/internal/config/type_port_test.go index 75fc920f0..9b7c239a3 100644 --- a/internal/config/type_port_test.go +++ b/internal/config/type_port_test.go @@ -2,7 +2,6 @@ package config_test import ( "encoding/json" - "strconv" "testing" "github.com/9seconds/mtg/v2/internal/config" @@ -18,97 +17,52 @@ type TypePortTestSuite struct { suite.Suite } -func (suite *TypePortTestSuite) TestUnmarshalNil() { - typ := &config.TypePort{} - suite.NoError(typ.UnmarshalJSON(nil)) - suite.Equal("0", typ.String()) -} - func (suite *TypePortTestSuite) TestUnmarshalFail() { - testData := []int{ - -1, - 1_000_000, + testData := []string{ + "", + "port", + "0", + "-1", + "1.5", + "70000", } for _, v := range testData { - data, err := json.Marshal(map[string]int{ + data, err := json.Marshal(map[string]string{ "value": v, }) suite.NoError(err) - suite.T().Run(strconv.Itoa(v), func(t *testing.T) { + suite.T().Run(v, func(t *testing.T) { assert.Error(t, json.Unmarshal(data, &typePortTestStruct{})) }) } } func (suite *TypePortTestSuite) TestUnmarshalOk() { - testData := []int{ - 1, - 1_000, - 65535, - } - - for _, v := range testData { - value := v - - data, err := json.Marshal(map[string]int{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(strconv.Itoa(v), func(t *testing.T) { - testStruct := &typePortTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.EqualValues(t, value, testStruct.Value.Value(0)) - }) - } + testStruct := &typePortTestStruct{} + suite.NoError(json.Unmarshal([]byte(`{"value": 5}`), testStruct)) + suite.EqualValues(5, testStruct.Value.Value) } func (suite *TypePortTestSuite) TestMarshalOk() { - testData := map[string]int{ - "1": 1, - "1000": 1000, - "65535": 65535, + testStruct := &typePortTestStruct{ + Value: config.TypePort{ + Value: 10, + }, } - for k, v := range testData { - name := k - value := v - - data, err := json.Marshal(map[string]int{ - "value": value, - }) - suite.NoError(err) - - suite.T().Run(name, func(t *testing.T) { - testStruct := &typePortTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, name, testStruct.Value.String()) - - marshalled, err := testStruct.Value.MarshalJSON() - assert.NoError(t, err) - assert.Equal(t, name, string(marshalled)) - }) - } + data, err := json.Marshal(testStruct) + suite.NoError(err) + suite.JSONEq(`{"value":10}`, string(data)) } -func (suite *TypePortTestSuite) TestValue() { - testStruct := &typePortTestStruct{} - - suite.EqualValues(0, testStruct.Value.Value(0)) - suite.EqualValues(1, testStruct.Value.Value(1)) - - data, err := json.Marshal(map[string]int{ - "value": 5, - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) +func (suite *TypePortTestSuite) TestGet() { + value := config.TypePort{} + suite.EqualValues(10, value.Get(10)) - suite.EqualValues(5, testStruct.Value.Value(0)) - suite.EqualValues(5, testStruct.Value.Value(1)) + value.Value = 100 + suite.EqualValues(100, value.Get(10)) } func TestTypePort(t *testing.T) { diff --git a/internal/config/type_prefer_ip.go b/internal/config/type_prefer_ip.go index 0b30e82b4..b28a33f29 100644 --- a/internal/config/type_prefer_ip.go +++ b/internal/config/type_prefer_ip.go @@ -24,38 +24,39 @@ const ( ) type TypePreferIP struct { - value string + Value string } -func (c *TypePreferIP) UnmarshalText(data []byte) error { - if len(data) == 0 { - return nil - } +func (t *TypePreferIP) Set(value string) error { + value = strings.ToLower(value) - text := strings.ToLower(string(data)) + switch value { + case TypePreferIPPreferIPv4, TypePreferIPPreferIPv6, + TypePreferOnlyIPv4, TypePreferOnlyIPv6: + t.Value = value - switch text { - case TypePreferIPPreferIPv4, TypePreferIPPreferIPv6, TypePreferOnlyIPv4, TypePreferOnlyIPv6: - c.value = text + return nil default: - return fmt.Errorf("incorrect prefer-ip value: %s", string(data)) + return fmt.Errorf("unsupported ip preference: %s", value) } - - return nil } -func (c TypePreferIP) MarshalText() ([]byte, error) { - return []byte(c.value), nil +func (t *TypePreferIP) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value } -func (c *TypePreferIP) String() string { - return c.value +func (t *TypePreferIP) UnmarshalText(data []byte) error { + return t.Set(string(data)) } -func (c *TypePreferIP) Value(defaultValue string) string { - if c.value == "" { - return defaultValue - } +func (t TypePreferIP) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} - return c.value +func (t TypePreferIP) String() string { + return t.Value } diff --git a/internal/config/type_prefer_ip_test.go b/internal/config/type_prefer_ip_test.go index 917b91883..005123742 100644 --- a/internal/config/type_prefer_ip_test.go +++ b/internal/config/type_prefer_ip_test.go @@ -18,18 +18,12 @@ type TypePreferIPTestSuite struct { suite.Suite } -func (suite *TypePreferIPTestSuite) TestUnmarshalNil() { - typ := &config.TypePreferIP{} - suite.NoError(typ.UnmarshalText(nil)) - suite.Empty(typ.String()) -} - func (suite *TypePreferIPTestSuite) TestUnmarshalFail() { testData := []string{ - "p", - "ipv4", - "onlyipv4", - "ipv6prefer", + "", + "prefer", + "preferipv4", + config.TypePreferIPPreferIPv4 + "_", } for _, v := range testData { @@ -50,14 +44,10 @@ func (suite *TypePreferIPTestSuite) TestUnmarshalOk() { config.TypePreferIPPreferIPv6, config.TypePreferOnlyIPv4, config.TypePreferOnlyIPv6, - strings.ToUpper(config.TypePreferIPPreferIPv4), - strings.ToUpper(config.TypePreferIPPreferIPv6), - strings.ToUpper(config.TypePreferOnlyIPv4), - strings.ToUpper(config.TypePreferOnlyIPv6), - strings.ToLower(config.TypePreferIPPreferIPv4), - strings.ToLower(config.TypePreferIPPreferIPv6), - strings.ToLower(config.TypePreferOnlyIPv4), - strings.ToLower(config.TypePreferOnlyIPv6), + strings.ToTitle(config.TypePreferOnlyIPv4), + strings.ToTitle(config.TypePreferOnlyIPv6), + strings.ToTitle(config.TypePreferIPPreferIPv4), + strings.ToTitle(config.TypePreferIPPreferIPv6), } for _, v := range testData { @@ -70,11 +60,8 @@ func (suite *TypePreferIPTestSuite) TestUnmarshalOk() { suite.T().Run(v, func(t *testing.T) { testStruct := &typePreferIPTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.EqualValues(t, - strings.ToLower(value), - testStruct.Value.Value(config.TypePreferIPPreferIPv4)) + assert.Equal(t, strings.ToLower(value), testStruct.Value.Value) }) } } @@ -85,55 +72,39 @@ func (suite *TypePreferIPTestSuite) TestMarshalOk() { config.TypePreferIPPreferIPv6, config.TypePreferOnlyIPv4, config.TypePreferOnlyIPv6, - strings.ToUpper(config.TypePreferIPPreferIPv4), - strings.ToUpper(config.TypePreferIPPreferIPv6), - strings.ToUpper(config.TypePreferOnlyIPv4), - strings.ToUpper(config.TypePreferOnlyIPv6), - strings.ToLower(config.TypePreferIPPreferIPv4), - strings.ToLower(config.TypePreferIPPreferIPv6), - strings.ToLower(config.TypePreferOnlyIPv4), - strings.ToLower(config.TypePreferOnlyIPv6), } for _, v := range testData { value := v - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - suite.T().Run(v, func(t *testing.T) { - testStruct := &typePreferIPTestStruct{} + testStruct := &typePreferIPTestStruct{ + Value: config.TypePreferIP{ + Value: value, + }, + } - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, strings.ToLower(value), testStruct.Value.String()) + encodedJSON, err := json.Marshal(testStruct) + assert.NoError(t, err) - marshalled, err := testStruct.Value.MarshalText() + expectedJSON, err := json.Marshal(map[string]string{ + "value": value, + }) assert.NoError(t, err) - assert.Equal(t, strings.ToLower(value), string(marshalled)) + + assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) }) } } -func (suite *TypePreferIPTestSuite) TestValue() { - testStruct := &typePreferIPTestStruct{} - - suite.EqualValues(config.TypePreferIPPreferIPv4, - testStruct.Value.Value(config.TypePreferIPPreferIPv4)) - suite.EqualValues(config.TypePreferIPPreferIPv6, - testStruct.Value.Value(config.TypePreferIPPreferIPv6)) - - data, err := json.Marshal(map[string]string{ - "value": config.TypePreferOnlyIPv4, - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) +func (suite *TypePreferIPTestSuite) TestGet() { + value := config.TypePreferIP{} + suite.Equal(config.TypePreferIPPreferIPv4, + value.Get(config.TypePreferIPPreferIPv4)) - suite.EqualValues(config.TypePreferOnlyIPv4, - testStruct.Value.Value(config.TypePreferOnlyIPv6)) - suite.EqualValues(config.TypePreferOnlyIPv4, - testStruct.Value.Value(config.TypePreferIPPreferIPv6)) + suite.NoError(value.Set(config.TypePreferIPPreferIPv6)) + suite.Equal(config.TypePreferIPPreferIPv6, + value.Get(config.TypePreferIPPreferIPv4)) } func TestTypePreferIP(t *testing.T) { diff --git a/internal/config2/type_proxy_url.go b/internal/config/type_proxy_url.go similarity index 98% rename from internal/config2/type_proxy_url.go rename to internal/config/type_proxy_url.go index 7336968b6..f27d0af92 100644 --- a/internal/config2/type_proxy_url.go +++ b/internal/config/type_proxy_url.go @@ -1,4 +1,4 @@ -package config2 +package config import ( "fmt" diff --git a/internal/config2/type_proxy_url_test.go b/internal/config/type_proxy_url_test.go similarity index 90% rename from internal/config2/type_proxy_url_test.go rename to internal/config/type_proxy_url_test.go index f0992311a..4bbe042aa 100644 --- a/internal/config2/type_proxy_url_test.go +++ b/internal/config/type_proxy_url_test.go @@ -1,17 +1,17 @@ -package config2_test +package config_test import ( "encoding/json" "net/url" "testing" - "github.com/9seconds/mtg/v2/internal/config2" + "github.com/9seconds/mtg/v2/internal/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) type typeProxyURLTestStruct struct { - Value config2.TypeProxyURL `json:"value"` + Value config.TypeProxyURL `json:"value"` } type ProxyURLTestSuite struct { @@ -69,20 +69,21 @@ func (suite *ProxyURLTestSuite) TestUnmarshalOk() { func (suite *ProxyURLTestSuite) TestMarshalOk() { parsed, _ := url.Parse("socks5://127.0.0.1:1080?open_threshold=1") testStruct := &typeProxyURLTestStruct{ - Value: config2.TypeProxyURL{ + Value: config.TypeProxyURL{ Value: parsed, }, } encodedJSON, err := json.Marshal(testStruct) suite.NoError(err) - suite.JSONEq(`{"value": "socks5://127.0.0.1:1080?open_threshold=1"}`, string(encodedJSON)) + suite.JSONEq(`{"value": "socks5://127.0.0.1:1080?open_threshold=1"}`, + string(encodedJSON)) } func (suite *ProxyURLTestSuite) TestGet() { emptyURL := &url.URL{} - value := config2.TypeProxyURL{} + value := config.TypeProxyURL{} suite.Equal(emptyURL, value.Get(emptyURL)) value.Value = &url.URL{} diff --git a/internal/config/type_statsd_tag_format.go b/internal/config/type_statsd_tag_format.go index 51ed34b48..e449b9ff5 100644 --- a/internal/config/type_statsd_tag_format.go +++ b/internal/config/type_statsd_tag_format.go @@ -20,38 +20,39 @@ const ( ) type TypeStatsdTagFormat struct { - value string + Value string } -func (c *TypeStatsdTagFormat) UnmarshalText(data []byte) error { - if len(data) == 0 { - return nil - } +func (t *TypeStatsdTagFormat) Set(value string) error { + lowercasedValue := strings.ToLower(value) - text := strings.ToLower(string(data)) + switch lowercasedValue { + case TypeStatsdTagFormatDatadog, TypeStatsdTagFormatInfluxdb, + TypeStatsdTagFormatGraphite: + t.Value = lowercasedValue - switch text { - case TypeStatsdTagFormatInfluxdb, TypeStatsdTagFormatDatadog, TypeStatsdTagFormatGraphite: - c.value = text + return nil default: - return fmt.Errorf("incorrect tag format value: %s", string(data)) + return fmt.Errorf("unknown tag format %s", value) } - - return nil } -func (c TypeStatsdTagFormat) MarshalText() ([]byte, error) { - return []byte(c.value), nil +func (t TypeStatsdTagFormat) Get(defaultValue string) string { + if t.Value == "" { + return defaultValue + } + + return t.Value } -func (c *TypeStatsdTagFormat) String() string { - return c.value +func (t *TypeStatsdTagFormat) UnmarshalText(data []byte) error { + return t.Set(string(data)) } -func (c *TypeStatsdTagFormat) Value(defaultValue string) string { - if c.value == "" { - return defaultValue - } +func (t *TypeStatsdTagFormat) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} - return c.value +func (t *TypeStatsdTagFormat) String() string { + return t.Value } diff --git a/internal/config/type_statsd_tag_format_test.go b/internal/config/type_statsd_tag_format_test.go index b639ed462..455d90f54 100644 --- a/internal/config/type_statsd_tag_format_test.go +++ b/internal/config/type_statsd_tag_format_test.go @@ -14,22 +14,14 @@ type typeStatsdTagFormatTestStruct struct { Value config.TypeStatsdTagFormat `json:"value"` } -type TypeStatsdTagFormatTestSuite struct { +type StatsdTagFormatTestSuite struct { suite.Suite } -func (suite *TypeStatsdTagFormatTestSuite) TestUnmarshalNil() { - typ := &config.TypeStatsdTagFormat{} - suite.NoError(typ.UnmarshalText(nil)) - suite.Equal("lalala", typ.Value("lalala")) -} - -func (suite *TypeStatsdTagFormatTestSuite) TestUnmarshalFail() { +func (suite *StatsdTagFormatTestSuite) TestUnmarshalFail() { testData := []string{ - "p", - "ipv4", - "onlyipv4", - "ipv6prefer", + "", + "dogdog", } for _, v := range testData { @@ -44,17 +36,14 @@ func (suite *TypeStatsdTagFormatTestSuite) TestUnmarshalFail() { } } -func (suite *TypeStatsdTagFormatTestSuite) TestUnmarshalOk() { +func (suite *StatsdTagFormatTestSuite) TestUnmarshalOk() { testData := []string{ - config.TypeStatsdTagFormatDatadog, config.TypeStatsdTagFormatInfluxdb, config.TypeStatsdTagFormatGraphite, - strings.ToUpper(config.TypeStatsdTagFormatDatadog), + config.TypeStatsdTagFormatDatadog, strings.ToUpper(config.TypeStatsdTagFormatInfluxdb), strings.ToUpper(config.TypeStatsdTagFormatGraphite), - strings.ToLower(config.TypeStatsdTagFormatDatadog), - strings.ToLower(config.TypeStatsdTagFormatInfluxdb), - strings.ToLower(config.TypeStatsdTagFormatGraphite), + strings.ToUpper(config.TypeStatsdTagFormatDatadog), } for _, v := range testData { @@ -67,70 +56,53 @@ func (suite *TypeStatsdTagFormatTestSuite) TestUnmarshalOk() { suite.T().Run(v, func(t *testing.T) { testStruct := &typeStatsdTagFormatTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.EqualValues(t, - strings.ToLower(value), - testStruct.Value.Value(config.TypeStatsdTagFormatDatadog)) + assert.Equal(t, strings.ToLower(value), testStruct.Value.Value) }) } } -func (suite *TypeStatsdTagFormatTestSuite) TestMarshalOk() { +func (suite *StatsdTagFormatTestSuite) TestMarshalOk() { testData := []string{ - config.TypeStatsdTagFormatDatadog, config.TypeStatsdTagFormatInfluxdb, config.TypeStatsdTagFormatGraphite, - strings.ToUpper(config.TypeStatsdTagFormatDatadog), - strings.ToUpper(config.TypeStatsdTagFormatInfluxdb), - strings.ToUpper(config.TypeStatsdTagFormatGraphite), - strings.ToLower(config.TypeStatsdTagFormatDatadog), - strings.ToLower(config.TypeStatsdTagFormatInfluxdb), - strings.ToLower(config.TypeStatsdTagFormatGraphite), + config.TypeStatsdTagFormatDatadog, } for _, v := range testData { value := v - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeStatsdTagFormatTestStruct{} + testStruct := &typeStatsdTagFormatTestStruct{ + Value: config.TypeStatsdTagFormat{ + Value: value, + }, + } - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, strings.ToLower(value), testStruct.Value.String()) + encodedJSON, err := json.Marshal(testStruct) + assert.NoError(t, err) - marshalled, err := testStruct.Value.MarshalText() + expectedJSON, err := json.Marshal(map[string]string{ + "value": value, + }) assert.NoError(t, err) - assert.Equal(t, strings.ToLower(value), string(marshalled)) + + assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) }) } } -func (suite *TypeStatsdTagFormatTestSuite) TestValue() { - testStruct := &typePreferIPTestStruct{} - - suite.EqualValues(config.TypePreferIPPreferIPv4, - testStruct.Value.Value(config.TypePreferIPPreferIPv4)) - suite.EqualValues(config.TypePreferIPPreferIPv6, - testStruct.Value.Value(config.TypePreferIPPreferIPv6)) - - data, err := json.Marshal(map[string]string{ - "value": config.TypePreferOnlyIPv4, - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) +func (suite *StatsdTagFormatTestSuite) TestGet() { + value := config.TypeStatsdTagFormat{} + suite.Equal(config.TypeStatsdTagFormatDatadog, + value.Get(config.TypeStatsdTagFormatDatadog)) - suite.EqualValues(config.TypePreferOnlyIPv4, - testStruct.Value.Value(config.TypePreferOnlyIPv6)) - suite.EqualValues(config.TypePreferOnlyIPv4, - testStruct.Value.Value(config.TypePreferIPPreferIPv6)) + suite.NoError(value.Set(config.TypeStatsdTagFormatInfluxdb)) + suite.Equal(config.TypeStatsdTagFormatInfluxdb, + value.Get(config.TypeStatsdTagFormatDatadog)) } func TestTypeStatsdTagFormat(t *testing.T) { t.Parallel() - suite.Run(t, &TypeStatsdTagFormatTestSuite{}) + suite.Run(t, &StatsdTagFormatTestSuite{}) } diff --git a/internal/config/type_url.go b/internal/config/type_url.go deleted file mode 100644 index 925049287..000000000 --- a/internal/config/type_url.go +++ /dev/null @@ -1,71 +0,0 @@ -package config - -import ( - "fmt" - "net" - "net/url" -) - -type TypeURL struct { - value *url.URL -} - -func (c *TypeURL) UnmarshalText(data []byte) error { // nolint: cyclop - if len(data) == 0 { - return nil - } - - value, err := url.Parse(string(data)) - if err != nil { - return fmt.Errorf("incorrect URL: %w", err) - } - - switch value.Scheme { - case "http", "https", "socks5": - case "": - return fmt.Errorf("url %s has to have a schema", value) - default: - return fmt.Errorf("unsupported schema %s", value.Scheme) - } - - if value.Host == "" { - return fmt.Errorf("url %s has to have a host", value) - } - - if _, _, err := net.SplitHostPort(value.Host); err != nil { - switch value.Scheme { - case "http": - value.Host = net.JoinHostPort(value.Host, "80") - case "https": - value.Host = net.JoinHostPort(value.Host, "443") - case "socks5": - value.Host = net.JoinHostPort(value.Host, "1080") - default: - return fmt.Errorf("cannot set a default port for %s", value) - } - } - - c.value = value - - return nil -} - -func (c *TypeURL) MarshalText() ([]byte, error) { - return []byte(c.String()), nil -} - -func (c TypeURL) String() string { - if c.value == nil { - return "" - } - - return c.value.String() -} - -func (c TypeURL) Value(defaultValue *url.URL) *url.URL { - if c.value == nil { - return defaultValue - } - - return c.value -} diff --git a/internal/config/type_url_test.go b/internal/config/type_url_test.go deleted file mode 100644 index 428817277..000000000 --- a/internal/config/type_url_test.go +++ /dev/null @@ -1,107 +0,0 @@ -package config_test - -import ( - "encoding/json" - "net/url" - "testing" - - "github.com/9seconds/mtg/v2/internal/config" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeURLTestStruct struct { - Value config.TypeURL `json:"value"` -} - -type TypeURLTestSuite struct { - suite.Suite -} - -func (suite *TypeURLTestSuite) TestUnmarshalNil() { - u, _ := url.Parse("https://google.com") - - typ := &config.TypeURL{} - suite.NoError(typ.UnmarshalText(nil)) - suite.Empty(typ.String()) - suite.Equal("https://google.com", typ.Value(u).String()) -} - -func (suite *TypeURLTestSuite) TestUnmarshalFail() { - testData := []string{ - "http:/aaa.com", - "ipv4", - "111", - "://111", - "http://aaa.com:xxx", - "gopher://aaa.com:888", - "gopher://aaa.com", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typeURLTestStruct{})) - }) - } -} - -func (suite *TypeURLTestSuite) TestUnmarshalOk() { - testData := map[string]string{ - "https://10.0.0.10:80": "https://10.0.0.10:80", - "https://10.0.0.10:443": "https://10.0.0.10", - "http://10.0.0.10:8": "http://10.0.0.10:8", - "http://10.0.0.10:80": "http://10.0.0.10", - "socks5://10.0.0.10:1080": "socks5://10.0.0.10", - "socks5://10.0.0.10:888": "socks5://10.0.0.10:888", - } - - for k, v := range testData { - expected := k - actual := v - - data, err := json.Marshal(map[string]string{ - "value": actual, - }) - suite.NoError(err) - - suite.T().Run(actual, func(t *testing.T) { - testStruct := &typeURLTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, expected, testStruct.Value.Value(nil).String()) - - marshalled, err := testStruct.Value.MarshalText() - assert.NoError(t, err) - assert.Equal(t, expected, string(marshalled)) - }) - } -} - -func (suite *TypeURLTestSuite) TestValue() { - testStruct := &typeURLTestStruct{} - - u1, _ := url.Parse("https://10.0.0.10:80") - u2, _ := url.Parse("https://10.1.0.10:80") - - suite.Equal("https://10.0.0.10:80", testStruct.Value.Value(u1).String()) - suite.Equal("https://10.1.0.10:80", testStruct.Value.Value(u2).String()) - - data, err := json.Marshal(map[string]string{ - "value": "http://127.0.0.1:80", - }) - suite.NoError(err) - suite.NoError(json.Unmarshal(data, testStruct)) - - suite.Equal("http://127.0.0.1:80", testStruct.Value.Value(u1).String()) - suite.Equal("http://127.0.0.1:80", testStruct.Value.Value(u2).String()) -} - -func TestTypeURL(t *testing.T) { - t.Parallel() - suite.Run(t, &TypeURLTestSuite{}) -} diff --git a/internal/config2/config.go b/internal/config2/config.go deleted file mode 100644 index 1b89c78c1..000000000 --- a/internal/config2/config.go +++ /dev/null @@ -1,81 +0,0 @@ -package config2 - -import ( - "bytes" - "encoding/json" - "fmt" - - "github.com/9seconds/mtg/v2/mtglib" -) - -type Config struct { - Debug TypeBool `json:"debug"` - Secret mtglib.Secret `json:"secret"` - BindTo TypeHostPort `json:"bindTo"` - TCPBuffer TypeBytes `json:"tcpBuffer"` - PreferIP TypePreferIP `json:"preferIp"` - DomainFrontingPort TypePort `json:"domainFrontingPort"` - TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"` - Concurrency TypeConcurrency `json:"concurrency"` - Defense struct { - AntiReplay struct { - Enabled TypeBool `json:"enabled"` - MaxSize TypeBytes `json:"maxSize"` - ErrorRate TypeErrorRate `json:"errorRate"` - } `json:"antiReplay"` - Blocklist struct { - Enabled TypeBool `json:"enabled"` - DownloadConcurrency TypeConcurrency `json:"downloadConcurrency"` - URLs []TypeBlocklistURI `json:"urls"` - UpdateEach TypeDuration `json:"updateEach"` - } `json:"blocklist"` - } `json:"defense"` - Network struct { - Timeout struct { - TCP TypeDuration `json:"tcp"` - HTTP TypeDuration `json:"http"` - Idle TypeDuration `json:"idle"` - } `json:"timeout"` - DOHIP TypeIP `json:"dohIp"` - Proxies []TypeProxyURL `json:"proxies"` - } `json:"network"` - Stats struct { - StatsD struct { - Enabled TypeBool `json:"enabled"` - Address TypeHostPort `json:"address"` - MetricPrefix TypeMetricPrefix `json:"metricPrefix"` - TagFormat TypeStatsdTagFormat `json:"tagFormat"` - } `json:"statsd"` - Prometheus struct { - Enabled TypeBool `json:"enabled"` - BindTo TypeHostPort `json:"bindTo"` - HTTPPath TypeHTTPPath `json:"httpPath"` - MetricPrefix TypeMetricPrefix `json:"metricPrefix"` - } `json:"prometheus"` - } `json:"stats"` -} - -func (c *Config) Validate() error { - if !c.Secret.Valid() { - return fmt.Errorf("invalid secret %s", c.Secret.String()) - } - - if c.BindTo.Get("") == "" { - return fmt.Errorf("incorrect bind-to parameter %s", c.BindTo.String()) - } - - return nil -} - -func (c *Config) String() string { - buf := &bytes.Buffer{} - encoder := json.NewEncoder(buf) - - encoder.SetEscapeHTML(false) - - if err := encoder.Encode(c); err != nil { - panic(err) - } - - return buf.String() -} diff --git a/internal/config2/config_test.go b/internal/config2/config_test.go deleted file mode 100644 index 76d32e725..000000000 --- a/internal/config2/config_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package config2_test - -import ( - "os" - "path/filepath" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/suite" -) - -type ConfigTestSuite struct { - suite.Suite -} - -func (suite *ConfigTestSuite) ReadConfig(filename string) []byte { - data, err := os.ReadFile(filepath.Join("testdata", filename)) - suite.NoError(err) - - return data -} - -func (suite *ConfigTestSuite) TestParseEmpty() { - _, err := config2.Parse([]byte{}) - suite.Error(err) -} - -func (suite *ConfigTestSuite) TestParseBrokenToml() { - _, err := config2.Parse(suite.ReadConfig("broken.toml")) - suite.Error(err) -} - -func (suite *ConfigTestSuite) TestParseOnlySecret() { - _, err := config2.Parse(suite.ReadConfig("only_secret.toml")) - suite.Error(err) -} - -func (suite *ConfigTestSuite) TestParseMinimalConfig() { - conf, err := config2.Parse(suite.ReadConfig("minimal.toml")) - suite.NoError(err) - suite.Equal("7oe1GqLy6TBc38CV3jx7q09nb29nbGUuY29t", conf.Secret.Base64()) - suite.Equal("0.0.0.0:3128", conf.BindTo.String()) -} - -func (suite *ConfigTestSuite) TestString() { - conf, err := config2.Parse(suite.ReadConfig("minimal.toml")) - suite.NoError(err) - suite.NotEmpty(conf.String()) -} - -func TestConfig(t *testing.T) { - t.Parallel() - suite.Run(t, &ConfigTestSuite{}) -} diff --git a/internal/config2/testdata/broken.toml b/internal/config2/testdata/broken.toml deleted file mode 100644 index d95f791f5..000000000 --- a/internal/config2/testdata/broken.toml +++ /dev/null @@ -1 +0,0 @@ -s = sdfsdfds diff --git a/internal/config2/testdata/minimal.toml b/internal/config2/testdata/minimal.toml deleted file mode 100644 index 9d0961a33..000000000 --- a/internal/config2/testdata/minimal.toml +++ /dev/null @@ -1,2 +0,0 @@ -secret = "7oe1GqLy6TBc38CV3jx7q09nb29nbGUuY29t" -bind-to = "0.0.0.0:3128" diff --git a/internal/config2/testdata/only_secret.toml b/internal/config2/testdata/only_secret.toml deleted file mode 100644 index f6b0bee57..000000000 --- a/internal/config2/testdata/only_secret.toml +++ /dev/null @@ -1 +0,0 @@ -secret = "7oe1GqLy6TBc38CV3jx7q09nb29nbGUuY29t" diff --git a/internal/config2/type_blocklist_uri.go b/internal/config2/type_blocklist_uri.go deleted file mode 100644 index f73ee3c0e..000000000 --- a/internal/config2/type_blocklist_uri.go +++ /dev/null @@ -1,77 +0,0 @@ -package config2 - -import ( - "fmt" - "net/url" - "os" - "path/filepath" -) - -type TypeBlocklistURI struct { - Value string -} - -func (t *TypeBlocklistURI) Set(value string) error { - if stat, err := os.Stat(value); err == nil || os.IsExist(err) { - switch { - case stat.IsDir(): - return fmt.Errorf("value is correct filepath but directory") - case stat.Mode().Perm() & 0o400 == 0: - return fmt.Errorf("value is correct filepath but not readable") - } - - value, err = filepath.Abs(value) - if err != nil { - return fmt.Errorf( - "value is correct filepath but cannot resolve absolute (%s): %w", - value, err) - } - - t.Value = value - - return nil - } - - parsedURL, err := url.Parse(value) - if err != nil { - return fmt.Errorf("incorrect url (%s): %w", value, err) - } - - switch parsedURL.Scheme { - case "http", "https": - default: - return fmt.Errorf("unknown schema %s (%s)", parsedURL.Scheme, value) - } - - if parsedURL.Host == "" { - return fmt.Errorf("incorrect url %s", value) - } - - t.Value = parsedURL.String() - - return nil -} - -func (t TypeBlocklistURI) Get(defaultValue string) string { - if t.Value == "" { - return defaultValue - } - - return t.Value -} - -func (t TypeBlocklistURI) IsRemote() bool { - return !filepath.IsAbs(t.Value) -} - -func (t *TypeBlocklistURI) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t TypeBlocklistURI) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypeBlocklistURI) String() string { - return t.Value -} diff --git a/internal/config2/type_blocklist_uri_test.go b/internal/config2/type_blocklist_uri_test.go deleted file mode 100644 index cfecc8a9b..000000000 --- a/internal/config2/type_blocklist_uri_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeBlocklistURITestStruct struct { - Value config2.TypeBlocklistURI `json:"value"` -} - -type TypeBlocklistURITestSuite struct { - suite.Suite - - directory string - absDirectory string -} - -func (suite *TypeBlocklistURITestSuite) SetupSuite() { - dir, _ := os.Getwd() - absDir, _ := filepath.Abs(dir) - - suite.directory = dir - suite.absDirectory = absDir -} - -func (suite *TypeBlocklistURITestSuite) TestUnmarshalFail() { - testData := []string{ - "gopher://lalala", - "https:///paths", - "h:/=", - filepath.Join(suite.directory, "___"), - filepath.Join(suite.absDirectory, "___"), - suite.directory, - suite.absDirectory, - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typeBlocklistURITestStruct{})) - }) - } -} - -func (suite *TypeBlocklistURITestSuite) TestUnmarshalOk() { - testData := []string{ - "http://lalala", - "https://lalala", - "https://lalala/path", - filepath.Join(suite.directory, "config.go"), - filepath.Join(suite.absDirectory, "config.go"), - } - - for _, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeBlocklistURITestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.EqualValues(t, value, testStruct.Value.Get("")) - - if strings.HasPrefix(value, "http") { - assert.True(t, testStruct.Value.IsRemote()) - } else { - assert.False(t, testStruct.Value.IsRemote()) - } - }) - } -} - -func (suite *TypeBlocklistURITestSuite) TestMarshalOk() { - testStruct := &typeBlocklistURITestStruct{ - Value: config2.TypeBlocklistURI{ - Value: "http://some.url/with/path", - }, - } - - data, err := json.Marshal(testStruct) - suite.NoError(err) - suite.JSONEq(`{"value": "http://some.url/with/path"}`, string(data)) -} - -func (suite *TypeBlocklistURITestSuite) TestGet() { - value := config2.TypeBlocklistURI{} - suite.Equal("/path", value.Get("/path")) - - suite.NoError(value.Set("http://lalala.ru")) - suite.Equal("http://lalala.ru", value.Get("/path")) - suite.Equal("http://lalala.ru", value.Get("")) -} - -func TestTypeBlocklistURI(t *testing.T) { - t.Parallel() - suite.Run(t, &TypeBlocklistURITestSuite{}) -} diff --git a/internal/config2/type_bytes.go b/internal/config2/type_bytes.go deleted file mode 100644 index 789ab5448..000000000 --- a/internal/config2/type_bytes.go +++ /dev/null @@ -1,55 +0,0 @@ -package config2 - -import ( - "fmt" - "strings" - - "github.com/alecthomas/units" -) - -var typeBytesStringCleaner = strings.NewReplacer(" ", "", "\t", "", "IB", "iB") - -type TypeBytes struct { - Value units.Base2Bytes -} - -func (t *TypeBytes) Set(value string) error { - normalizedValue := typeBytesStringCleaner.Replace(strings.ToUpper(value)) - - parsedValue, err := units.ParseBase2Bytes(normalizedValue) - if err != nil { - return fmt.Errorf("incorrect bytes value (%v): %w", value, err) - } - - if parsedValue < 0 { - return fmt.Errorf("bytes should be positive (%s)", value) - } - - t.Value = parsedValue - - return nil -} - -func (t TypeBytes) Get(defaultValue uint) uint { - if t.Value == 0 { - return defaultValue - } - - return uint(t.Value) -} - -func (t *TypeBytes) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t TypeBytes) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypeBytes) String() string { - if t.Value == 0 { - return "" - } - - return strings.ToLower(t.Value.String()) -} diff --git a/internal/config2/type_bytes_test.go b/internal/config2/type_bytes_test.go deleted file mode 100644 index c153fb2f0..000000000 --- a/internal/config2/type_bytes_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeBytesTestStruct struct { - Value config2.TypeBytes `json:"value"` -} - -type TypeBytesTestSuite struct { - suite.Suite -} - -func (suite *TypeBytesTestSuite) TestUnmarshalFail() { - testData := []string{ - "1m", - "1", - "-1kb", - "-1kib", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typeBytesTestStruct{})) - }) - } -} - -func (suite *TypeBytesTestSuite) TestUnmarshalOk() { - testData := map[string]uint{ - "1b": 1, - "1kb": 1024, - "1kib": 1024, - "2mb": 2 * 1024 * 1024, - "2mib": 2 * 1024 * 1024, - } - - for k, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": k, - }) - suite.NoError(err) - - suite.T().Run(k, func(t *testing.T) { - testStruct := &typeBytesTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.EqualValues(t, value, testStruct.Value.Get(0)) - }) - } -} - -func (suite *TypeBytesTestSuite) TestMarshalOk() { - value := typeBytesTestStruct{} - suite.NoError(value.Value.Set("1kib")) - - data, err := json.Marshal(value) - suite.NoError(err) - suite.JSONEq(`{"value": "1kib"}`, string(data)) -} - -func (suite *TypeBytesTestSuite) TestGet() { - value := config2.TypeBytes{} - suite.EqualValues(1000, value.Get(1000)) - - suite.NoError(value.Set("1mib")) - suite.EqualValues(1048576, value.Get(1000)) -} - -func TestTypeBytes(t *testing.T) { - t.Parallel() - suite.Run(t, &TypeBytesTestSuite{}) -} diff --git a/internal/config2/type_duration.go b/internal/config2/type_duration.go deleted file mode 100644 index 85db148eb..000000000 --- a/internal/config2/type_duration.go +++ /dev/null @@ -1,53 +0,0 @@ -package config2 - -import ( - "fmt" - "strings" - "time" -) - -var typeDurationStringCleaner = strings.NewReplacer(" ", "", "\t", "") - -type TypeDuration struct { - Value time.Duration -} - -func (t *TypeDuration) Set(value string) error { - parsedValue, err := time.ParseDuration( - typeDurationStringCleaner.Replace(strings.ToLower(value))) - if err != nil { - return fmt.Errorf("incorrect duration (%s): %w", value, err) - } - - if parsedValue < 0 { - return fmt.Errorf("duration has to be a positive: %s", value) - } - - t.Value = parsedValue - - return nil -} - -func (t TypeDuration) Get(defaultValue time.Duration) time.Duration { - if t.Value == 0 { - return defaultValue - } - - return t.Value -} - -func (t *TypeDuration) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t TypeDuration) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypeDuration) String() string { - if t.Value == 0 { - return "" - } - - return t.Value.String() -} diff --git a/internal/config2/type_duration_test.go b/internal/config2/type_duration_test.go deleted file mode 100644 index 3f9b21ea4..000000000 --- a/internal/config2/type_duration_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "testing" - "time" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeDurationTestStruct struct { - Value config2.TypeDuration `json:"value"` -} - -type TypeDurationTestSuite struct { - suite.Suite -} - -func (suite *TypeDurationTestSuite) TestUnmarshalFail() { - testData := []string{ - "-1s", - "1 seconds ago", - "1s ago", - "", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typeDurationTestStruct{})) - }) - } -} - -func (suite *TypeDurationTestSuite) TestUnmarshalOk() { - testData := map[string]time.Duration{ - "1s": time.Second, - "0": 0 * time.Second, - "0s": 0 * time.Second, - "1\tM": time.Minute, - "1H": time.Hour, - "1 h": time.Hour, - } - - for k, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": k, - }) - suite.NoError(err) - - suite.T().Run(k, func(t *testing.T) { - testStruct := &typeDurationTestStruct{} - - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, value, testStruct.Value.Value) - }) - } -} - -func (suite *TypeDurationTestSuite) TestMarshalOk() { - testData := map[string]string{ - "1s": "1s", - "0": "", - "0s": "", - "0ms": "", - "1 H": "1h0m0s", - } - - for k, v := range testData { - value := k - expected := v - - suite.T().Run(value, func(t *testing.T) { - testStruct := &typeDurationTestStruct{} - - assert.NoError(t, testStruct.Value.Set(value)) - - data, err := json.Marshal(testStruct) - assert.NoError(t, err) - - expectedJson, err := json.Marshal(map[string]string{ - "value": expected, - }) - assert.NoError(t, err) - - assert.JSONEq(t, string(expectedJson), string(data)) - }) - } -} - -func (suite *TypeDurationTestSuite) TestGet() { - value := config2.TypeDuration{} - suite.Equal(time.Second, value.Get(time.Second)) - - value.Value = 3 * time.Second - suite.Equal(3*time.Second, value.Get(time.Hour)) -} - -func TestTypeDuration(t *testing.T) { - t.Parallel() - suite.Run(t, &TypeDurationTestSuite{}) -} diff --git a/internal/config2/type_error_rate.go b/internal/config2/type_error_rate.go deleted file mode 100644 index 1ce8ecc3f..000000000 --- a/internal/config2/type_error_rate.go +++ /dev/null @@ -1,47 +0,0 @@ -package config2 - -import ( - "fmt" - "strconv" -) - -const typeErrorRateIgnoreLess = 1e-8 - -type TypeErrorRate struct { - Value float64 -} - -func (t *TypeErrorRate) Set(value string) error { - parsedValue, err := strconv.ParseFloat(value, 64) - if err != nil { - return fmt.Errorf("Value is not a float (%s): %w", value, err) - } - - if parsedValue <= 0.0 || parsedValue >= 100.0 { - return fmt.Errorf("Value should be 0 < x < 100 (%s)", value) - } - - t.Value = parsedValue - - return nil -} - -func (t TypeErrorRate) Get(defaultValue float64) float64 { - if t.Value < typeErrorRateIgnoreLess { - return defaultValue - } - - return t.Value -} - -func (t *TypeErrorRate) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t TypeErrorRate) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypeErrorRate) String() string { - return strconv.FormatFloat(t.Value, 'f', -1, 64) -} diff --git a/internal/config2/type_error_rate_test.go b/internal/config2/type_error_rate_test.go deleted file mode 100644 index 5d8590aae..000000000 --- a/internal/config2/type_error_rate_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeErrorRateTestStruct struct { - Value config2.TypeErrorRate `json:"value"` -} - -type TypeErrorRateTestSuite struct { - suite.Suite -} - -func (suite *TypeErrorRateTestSuite) TestUnmarshalFail() { - testData := []string{ - "", - "1s", - "1,", - "1,2", - ".", - "3.4.5", - "3.5.", - ".3.5", - "some word", - "1e2", - "-1.0", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typeErrorRateTestStruct{})) - }) - } -} - -func (suite *TypeErrorRateTestSuite) TestUnmarshalOk() { - testData := map[string]float64{ - "1": 1.0, - "1.0": 1.0, - "0.5": 0.5, - ".5": 0.5, - } - - for k, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": k, - }) - suite.NoError(err) - - suite.T().Run(k, func(t *testing.T) { - testStruct := &typeErrorRateTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.InEpsilon(t, value, testStruct.Value.Value, 1e-10) - }) - } -} - -func (suite *TypeErrorRateTestSuite) TestMarshalOk() { - testStruct := typeErrorRateTestStruct{ - Value: config2.TypeErrorRate{ - Value: 1.01, - }, - } - - encodedJson, err := json.Marshal(testStruct) - suite.NoError(err) - suite.JSONEq(`{"value": "1.01"}`, string(encodedJson)) -} - -func (suite *TypeErrorRateTestSuite) TestGet() { - value := config2.TypeErrorRate{} - suite.InEpsilon(1.0, value.Get(1.0), 1e-10) - - value.Value = 5.0 - suite.InEpsilon(5.0, value.Get(1.0), 1e-10) -} - -func TestTypeErrorRate(t *testing.T) { - t.Parallel() - suite.Run(t, &TypeErrorRateTestSuite{}) -} diff --git a/internal/config2/type_hostport.go b/internal/config2/type_hostport.go deleted file mode 100644 index 73d45c06a..000000000 --- a/internal/config2/type_hostport.go +++ /dev/null @@ -1,59 +0,0 @@ -package config2 - -import ( - "fmt" - "net" - "strconv" -) - -type TypeHostPort struct { - Value string -} - -func (t *TypeHostPort) Set(value string) error { - host, port, err := net.SplitHostPort(value) - if err != nil { - return fmt.Errorf("incorrect host:port value (%v): %w", value, err) - } - - portValue, err := strconv.ParseUint(port, 10, 16) - if err != nil { - return fmt.Errorf("incorrect port number (%v): %w", value, err) - } - - if portValue == 0 { - return fmt.Errorf("incorrect port number (%s)", value) - } - - if host == "" { - return fmt.Errorf("empty host: %s", value) - } - - if net.ParseIP(host) == nil { - return fmt.Errorf("host is not an IP address: %s", value) - } - - t.Value = net.JoinHostPort(host, port) - - return nil -} - -func (t TypeHostPort) Get(defaultValue string) string { - if t.Value == "" { - return defaultValue - } - - return t.Value -} - -func (t *TypeHostPort) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t TypeHostPort) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypeHostPort) String() string { - return t.Value -} diff --git a/internal/config2/type_hostport_test.go b/internal/config2/type_hostport_test.go deleted file mode 100644 index dfaf395f6..000000000 --- a/internal/config2/type_hostport_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeHostPortTestStruct struct { - Value config2.TypeHostPort `json:"value"` -} - -type TypeHostPortTestSuite struct { - suite.Suite -} - -func (suite *TypeHostPortTestSuite) TestUnmarshalFail() { - testData := []string{ - ":", - ":800", - "127.0.0.1:8000000", - "12...:80", - "", - "localhost", - "google.com:", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typeHostPortTestStruct{})) - }) - } -} - -func (suite *TypeHostPortTestSuite) TestUnmarshalOk() { - testData := []string{ - "127.0.0.1:80", - "10.0.0.10:6553", - } - - for _, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeHostPortTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, value, testStruct.Value.Value) - }) - } -} - -func (suite *TypeHostPortTestSuite) TestMarshalOk() { - testStruct := typeHostPortTestStruct{ - Value: config2.TypeHostPort{ - Value: "127.0.0.1:8000", - }, - } - - data, err := json.Marshal(testStruct) - suite.NoError(err) - suite.JSONEq(`{"value": "127.0.0.1:8000"}`, string(data)) -} - -func (suite *TypeHostPortTestSuite) TestGet() { - value := config2.TypeHostPort{} - suite.Equal("127.0.0.1:9000", value.Get("127.0.0.1:9000")) - - value.Value = "127.0.0.1:80" - suite.Equal("127.0.0.1:80", value.Get("127.0.0.1:9000")) -} - -func TestTypeHostPort(t *testing.T) { - t.Parallel() - suite.Run(t, &TypeHostPortTestSuite{}) -} diff --git a/internal/config2/type_http_path.go b/internal/config2/type_http_path.go deleted file mode 100644 index d31ac57cb..000000000 --- a/internal/config2/type_http_path.go +++ /dev/null @@ -1,33 +0,0 @@ -package config2 - -import "strings" - -type TypeHTTPPath struct { - Value string -} - -func (t *TypeHTTPPath) Set(value string) error { - t.Value = "/" + strings.Trim(value, "/") - - return nil -} - -func (t TypeHTTPPath) Get(defaultValue string) string { - if t.Value == "" { - return defaultValue - } - - return t.Value -} - -func (t *TypeHTTPPath) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t TypeHTTPPath) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypeHTTPPath) String() string { - return t.Value -} diff --git a/internal/config2/type_http_path_test.go b/internal/config2/type_http_path_test.go deleted file mode 100644 index 014907e39..000000000 --- a/internal/config2/type_http_path_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeHTTPPathTestStruct struct { - Value config2.TypeHTTPPath `json:"value"` -} - -type TypeHTTPPathTestSuite struct { - suite.Suite -} - -func (suite *TypeHTTPPathTestSuite) TestUnmarshalOk() { - testData := map[string]string{ - "": "/", - "/": "/", - "/path": "/path", - "path": "/path", - } - - for k, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": k, - }) - suite.NoError(err) - - suite.T().Run(k, func(t *testing.T) { - testStruct := &typeHTTPPathTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, value, testStruct.Value.Get("")) - }) - } -} - -func (suite *TypeHTTPPathTestSuite) TestMarshalOk() { - value := typeHTTPPathTestStruct{ - Value: config2.TypeHTTPPath{ - Value: "/path", - }, - } - - data, err := json.Marshal(value) - suite.NoError(err) - suite.JSONEq(`{"value": "/path"}`, string(data)) -} - -func (suite *TypeHTTPPathTestSuite) TestGet() { - value := config2.TypeHTTPPath{} - suite.Equal("/hello", value.Get("/hello")) - - suite.NoError(value.Set("/lalala")) - suite.Equal("/lalala", value.Get("/hello")) -} - -func TestTypeHTTPPath(t *testing.T) { - t.Parallel() - suite.Run(t, &TypeHTTPPathTestSuite{}) -} diff --git a/internal/config2/type_ip.go b/internal/config2/type_ip.go deleted file mode 100644 index 207d22eb8..000000000 --- a/internal/config2/type_ip.go +++ /dev/null @@ -1,45 +0,0 @@ -package config2 - -import ( - "fmt" - "net" -) - -type TypeIP struct { - Value net.IP -} - -func (t *TypeIP) Set(value string) error { - ip := net.ParseIP(value) - if ip == nil { - return fmt.Errorf("incorret ip %s", value) - } - - t.Value = ip - - return nil -} - -func (t *TypeIP) Get(defaultValue net.IP) net.IP { - if len(t.Value) == 0 { - return defaultValue - } - - return t.Value -} - -func (t *TypeIP) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t TypeIP) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypeIP) String() string { - if len(t.Value) == 0 { - return "" - } - - return t.Value.String() -} diff --git a/internal/config2/type_ip_test.go b/internal/config2/type_ip_test.go deleted file mode 100644 index 4659cb0b8..000000000 --- a/internal/config2/type_ip_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "net" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeIPTestStruct struct { - Value config2.TypeIP `json:"value"` -} - -type TypeIPTestSuite struct { - suite.Suite -} - -func (suite *TypeIPTestSuite) TestUnmarshalFail() { - testData := []string{ - "", - "....", - "0...", - "300.200.200.800", - "[]", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typeIPTestStruct{})) - }) - } -} - -func (suite *TypeIPTestSuite) TestUnmarshalOk() { - testData := map[string]string{ - "2001:0db8:85a3:0000:0000:8a2e:0370:7334": "2001:db8:85a3::8a2e:370:7334", - "127.0.0.1": "127.0.0.1", - } - - for k, v := range testData { - expected := v - - data, err := json.Marshal(map[string]string{ - "value": k, - }) - suite.NoError(err) - - suite.T().Run(k, func(t *testing.T) { - testStruct := &typeIPTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, expected, testStruct.Value.Get(nil).String()) - }) - } -} - -func (suite *TypeIPTestSuite) TestMarshalOk() { - testData := []string{ - "2001:db8:85a3::8a2e:370:7334", - "127.0.0.1", - } - - for _, v := range testData { - value := v - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeIPTestStruct{ - Value: config2.TypeIP{ - Value: net.ParseIP(value), - }, - } - - encodedJSON, err := json.Marshal(testStruct) - assert.NoError(t, err) - - expectedJSON, err := json.Marshal(map[string]string{ - "value": value, - }) - assert.NoError(t, err) - - assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) - }) - } -} - -func (suite *TypeIPTestSuite) TestGet() { - value := config2.TypeIP{} - suite.Equal("127.0.0.1", value.Get(net.ParseIP("127.0.0.1")).String()) - - suite.NoError(value.Set("127.0.0.2")) - suite.Equal("127.0.0.2", value.Get(net.ParseIP("127.0.0.1")).String()) -} - -func TestTypeIP(t *testing.T) { - t.Parallel() - suite.Run(t, &TypeIPTestSuite{}) -} diff --git a/internal/config2/type_metric_prefix.go b/internal/config2/type_metric_prefix.go deleted file mode 100644 index d8507def5..000000000 --- a/internal/config2/type_metric_prefix.go +++ /dev/null @@ -1,40 +0,0 @@ -package config2 - -import ( - "fmt" - "regexp" -) - -type TypeMetricPrefix struct { - Value string -} - -func (t *TypeMetricPrefix) Set(value string) error { - if ok, err := regexp.MatchString("^[a-z0-9]+$", value); !ok || err != nil { - return fmt.Errorf("incorrect metric prefix %s: %w", value, err) - } - - t.Value = value - - return nil -} - -func (t TypeMetricPrefix) Get(defaultValue string) string { - if t.Value == "" { - return defaultValue - } - - return t.Value -} - -func (t *TypeMetricPrefix) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t TypeMetricPrefix) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypeMetricPrefix) String() string { - return t.Value -} diff --git a/internal/config2/type_metric_prefix_test.go b/internal/config2/type_metric_prefix_test.go deleted file mode 100644 index bed649753..000000000 --- a/internal/config2/type_metric_prefix_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeMetricPrefixTestStruct struct { - Value config2.TypeMetricPrefix `json:"value"` -} - -type TypeMetricPrefixTestSuite struct { - suite.Suite -} - -func (suite *TypeMetricPrefixTestSuite) TestUnmarshalFail() { - testData := []string{ - "", - "-", - "hello/world", - "lala*", - "++sdf++", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typeMetricPrefixTestStruct{})) - }) - } -} - -func (suite *TypeMetricPrefixTestSuite) TestUnmarshalOk() { - testStruct := &typeMetricPrefixTestStruct{} - suite.NoError(json.Unmarshal([]byte(`{"value": "mtg"}`), testStruct)) - suite.Equal("mtg", testStruct.Value.Get("lalala")) -} - -func (suite *TypeMetricPrefixTestSuite) TestMarshalOk() { - testStruct := &typeMetricPrefixTestStruct{ - Value: config2.TypeMetricPrefix{ - Value: "mtg", - }, - } - - data, err := json.Marshal(testStruct) - suite.NoError(err) - suite.JSONEq(`{"value": "mtg"}`, string(data)) -} - -func (suite *TypeMetricPrefixTestSuite) TestGet() { - value := config2.TypeMetricPrefix{} - suite.Equal("lalala", value.Get("lalala")) - - value.Value = "mtg" - suite.Equal("mtg", value.Get("lalala")) -} - -func TestTypeMetricPrefix(t *testing.T) { - t.Parallel() - suite.Run(t, &TypeMetricPrefixTestSuite{}) -} diff --git a/internal/config2/type_port.go b/internal/config2/type_port.go deleted file mode 100644 index 3a307d9e9..000000000 --- a/internal/config2/type_port.go +++ /dev/null @@ -1,45 +0,0 @@ -package config2 - -import ( - "fmt" - "strconv" -) - -type TypePort struct { - Value uint16 -} - -func (t *TypePort) Set(value string) error { - portValue, err := strconv.ParseUint(value, 10, 16) - if err != nil { - return fmt.Errorf("incorrect port number (%v): %w", value, err) - } - - if portValue == 0 { - return fmt.Errorf("incorrect port number (%s)", value) - } - - t.Value = uint16(portValue) - - return nil -} - -func (t TypePort) Get(defaultValue uint16) uint16 { - if t.Value == 0 { - return defaultValue - } - - return t.Value -} - -func (t *TypePort) UnmarshalJSON(data []byte) error { - return t.Set(string(data)) -} - -func (t TypePort) MarshalJSON() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypePort) String() string { - return strconv.Itoa(int(t.Value)) -} diff --git a/internal/config2/type_port_test.go b/internal/config2/type_port_test.go deleted file mode 100644 index 544a2e974..000000000 --- a/internal/config2/type_port_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typePortTestStruct struct { - Value config2.TypePort `json:"value"` -} - -type TypePortTestSuite struct { - suite.Suite -} - -func (suite *TypePortTestSuite) TestUnmarshalFail() { - testData := []string{ - "", - "port", - "0", - "-1", - "1.5", - "70000", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typePortTestStruct{})) - }) - } -} - -func (suite *TypePortTestSuite) TestUnmarshalOk() { - testStruct := &typePortTestStruct{} - suite.NoError(json.Unmarshal([]byte(`{"value": 5}`), testStruct)) - suite.EqualValues(5, testStruct.Value.Value) -} - -func (suite *TypePortTestSuite) TestMarshalOk() { - testStruct := &typePortTestStruct{ - Value: config2.TypePort{ - Value: 10, - }, - } - - data, err := json.Marshal(testStruct) - suite.NoError(err) - suite.JSONEq(`{"value":10}`, string(data)) -} - -func (suite *TypePortTestSuite) TestGet() { - value := config2.TypePort{} - suite.EqualValues(10, value.Get(10)) - - value.Value = 100 - suite.EqualValues(100, value.Get(10)) -} - -func TestTypePort(t *testing.T) { - t.Parallel() - suite.Run(t, &TypePortTestSuite{}) -} diff --git a/internal/config2/type_prefer_ip.go b/internal/config2/type_prefer_ip.go deleted file mode 100644 index 3370a3a69..000000000 --- a/internal/config2/type_prefer_ip.go +++ /dev/null @@ -1,62 +0,0 @@ -package config2 - -import ( - "fmt" - "strings" -) - -const ( - // TypePreferIPPreferIPv4 states that you prefer to use IPv4 addresses - // but IPv6 is also possible. - TypePreferIPPreferIPv4 = "prefer-ipv4" - - // TypePreferIPPreferIPv6 states that you prefer to use IPv6 addresses - // but IPv4 is also possible. - TypePreferIPPreferIPv6 = "prefer-ipv6" - - // TypePreferOnlyIPv4 states that you prefer to use IPv4 addresses - // only. - TypePreferOnlyIPv4 = "only-ipv4" - - // TypePreferOnlyIPv6 states that you prefer to use IPv6 addresses - // only. - TypePreferOnlyIPv6 = "only-ipv6" -) - -type TypePreferIP struct { - Value string -} - -func (t *TypePreferIP) Set(value string) error { - value = strings.ToLower(value) - - switch value { - case TypePreferIPPreferIPv4, TypePreferIPPreferIPv6, - TypePreferOnlyIPv4, TypePreferOnlyIPv6: - t.Value = value - - return nil - default: - return fmt.Errorf("unsupported ip preference: %s", value) - } -} - -func (t *TypePreferIP) Get(defaultValue string) string { - if t.Value == "" { - return defaultValue - } - - return t.Value -} - -func (t *TypePreferIP) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t TypePreferIP) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t TypePreferIP) String() string { - return t.Value -} diff --git a/internal/config2/type_prefer_ip_test.go b/internal/config2/type_prefer_ip_test.go deleted file mode 100644 index 420ac1369..000000000 --- a/internal/config2/type_prefer_ip_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "strings" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typePreferIPTestStruct struct { - Value config2.TypePreferIP `json:"value"` -} - -type TypePreferIPTestSuite struct { - suite.Suite -} - -func (suite *TypePreferIPTestSuite) TestUnmarshalFail() { - testData := []string{ - "", - "prefer", - "preferipv4", - config2.TypePreferIPPreferIPv4 + "_", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typePreferIPTestStruct{})) - }) - } -} - -func (suite *TypePreferIPTestSuite) TestUnmarshalOk() { - testData := []string{ - config2.TypePreferIPPreferIPv4, - config2.TypePreferIPPreferIPv6, - config2.TypePreferOnlyIPv4, - config2.TypePreferOnlyIPv6, - strings.ToTitle(config2.TypePreferOnlyIPv4), - strings.ToTitle(config2.TypePreferOnlyIPv6), - strings.ToTitle(config2.TypePreferIPPreferIPv4), - strings.ToTitle(config2.TypePreferIPPreferIPv6), - } - - for _, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typePreferIPTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, strings.ToLower(value), testStruct.Value.Value) - }) - } -} - -func (suite *TypePreferIPTestSuite) TestMarshalOk() { - testData := []string{ - config2.TypePreferIPPreferIPv4, - config2.TypePreferIPPreferIPv6, - config2.TypePreferOnlyIPv4, - config2.TypePreferOnlyIPv6, - } - - for _, v := range testData { - value := v - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typePreferIPTestStruct{ - Value: config2.TypePreferIP{ - Value: value, - }, - } - - encodedJSON, err := json.Marshal(testStruct) - assert.NoError(t, err) - - expectedJSON, err := json.Marshal(map[string]string{ - "value": value, - }) - assert.NoError(t, err) - - assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) - }) - } -} - -func (suite *TypePreferIPTestSuite) TestGet() { - value := config2.TypePreferIP{} - suite.Equal(config2.TypePreferIPPreferIPv4, - value.Get(config2.TypePreferIPPreferIPv4)) - - suite.NoError(value.Set(config2.TypePreferIPPreferIPv6)) - suite.Equal(config2.TypePreferIPPreferIPv6, - value.Get(config2.TypePreferIPPreferIPv4)) -} - -func TestTypePreferIP(t *testing.T) { - t.Parallel() - suite.Run(t, &TypePreferIPTestSuite{}) -} diff --git a/internal/config2/type_statsd_tag_format.go b/internal/config2/type_statsd_tag_format.go deleted file mode 100644 index fb4f2674a..000000000 --- a/internal/config2/type_statsd_tag_format.go +++ /dev/null @@ -1,58 +0,0 @@ -package config2 - -import ( - "fmt" - "strings" -) - -const ( - // TypeStatsdTagFormatInfluxdb defines a tag format compatible with - // InfluxDB. - TypeStatsdTagFormatInfluxdb = "influxdb" - - // TypeStatsdTagFormatDatadog defines a tag format compatible with - // DataDog. - TypeStatsdTagFormatDatadog = "datadog" - - // TypeStatsdTagFormatGraphite defines a tag format compatible with - // Graphite. - TypeStatsdTagFormatGraphite = "graphite" -) - -type TypeStatsdTagFormat struct { - Value string -} - -func (t *TypeStatsdTagFormat) Set(value string) error { - lowercasedValue := strings.ToLower(value) - - switch lowercasedValue { - case TypeStatsdTagFormatDatadog, TypeStatsdTagFormatInfluxdb, - TypeStatsdTagFormatGraphite: - t.Value = lowercasedValue - - return nil - default: - return fmt.Errorf("unknown tag format %s", value) - } -} - -func (t TypeStatsdTagFormat) Get(defaultValue string) string { - if t.Value == "" { - return defaultValue - } - - return t.Value -} - -func (t *TypeStatsdTagFormat) UnmarshalText(data []byte) error { - return t.Set(string(data)) -} - -func (t *TypeStatsdTagFormat) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t *TypeStatsdTagFormat) String() string { - return t.Value -} diff --git a/internal/config2/type_statsd_tag_format_test.go b/internal/config2/type_statsd_tag_format_test.go deleted file mode 100644 index fb75b26a8..000000000 --- a/internal/config2/type_statsd_tag_format_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package config2_test - -import ( - "encoding/json" - "strings" - "testing" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type typeStatsdTagFormatTestStruct struct { - Value config2.TypeStatsdTagFormat `json:"value"` -} - -type StatsdTagFormatTestSuite struct { - suite.Suite -} - -func (suite *StatsdTagFormatTestSuite) TestUnmarshalFail() { - testData := []string{ - "", - "dogdog", - } - - for _, v := range testData { - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - assert.Error(t, json.Unmarshal(data, &typeStatsdTagFormatTestStruct{})) - }) - } -} - -func (suite *StatsdTagFormatTestSuite) TestUnmarshalOk() { - testData := []string{ - config2.TypeStatsdTagFormatInfluxdb, - config2.TypeStatsdTagFormatGraphite, - config2.TypeStatsdTagFormatDatadog, - strings.ToUpper(config2.TypeStatsdTagFormatInfluxdb), - strings.ToUpper(config2.TypeStatsdTagFormatGraphite), - strings.ToUpper(config2.TypeStatsdTagFormatDatadog), - } - - for _, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": v, - }) - suite.NoError(err) - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeStatsdTagFormatTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.Equal(t, strings.ToLower(value), testStruct.Value.Value) - }) - } -} - -func (suite *StatsdTagFormatTestSuite) TestMarshalOk() { - testData := []string{ - config2.TypeStatsdTagFormatInfluxdb, - config2.TypeStatsdTagFormatGraphite, - config2.TypeStatsdTagFormatDatadog, - } - - for _, v := range testData { - value := v - - suite.T().Run(v, func(t *testing.T) { - testStruct := &typeStatsdTagFormatTestStruct{ - Value: config2.TypeStatsdTagFormat{ - Value: value, - }, - } - - encodedJSON, err := json.Marshal(testStruct) - assert.NoError(t, err) - - expectedJSON, err := json.Marshal(map[string]string{ - "value": value, - }) - assert.NoError(t, err) - - assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) - }) - } -} - -func (suite *StatsdTagFormatTestSuite) TestGet() { - value := config2.TypeStatsdTagFormat{} - suite.Equal(config2.TypeStatsdTagFormatDatadog, - value.Get(config2.TypeStatsdTagFormatDatadog)) - - suite.NoError(value.Set(config2.TypeStatsdTagFormatInfluxdb)) - suite.Equal(config2.TypeStatsdTagFormatInfluxdb, - value.Get(config2.TypeStatsdTagFormatDatadog)) -} - -func TestTypeStatsdTagFormat(t *testing.T) { - t.Parallel() - suite.Run(t, &StatsdTagFormatTestSuite{}) -} From 3fd5e9eb19d7e0ad9aca40e08fd7218523abbf49 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Thu, 29 Jul 2021 18:33:58 +0300 Subject: [PATCH 04/12] Rework cli --- internal/cli/access.go | 71 ++++---- internal/cli/access_test.go | 197 ---------------------- internal/cli/base.go | 81 --------- internal/cli/base_internal_test.go | 33 ---- internal/cli/generate_secret_test.go | 51 ------ internal/cli/init_test.go | 37 ----- internal/cli/proxy.go | 155 +----------------- internal/cli/run_proxy.go | 208 ++++++++++++++++++++++++ internal/cli/utils.go | 66 -------- internal/config/type_bool.go | 17 +- internal/config/type_bool_test.go | 52 +++--- internal/config/type_error_rate.go | 4 +- internal/config/type_error_rate_test.go | 29 +--- internal/config/type_hostport.go | 4 + internal/utils/make_qr_code_url.go | 19 +++ internal/utils/read_config.go | 26 +++ mtglib/proxy_opts.go | 2 +- 17 files changed, 333 insertions(+), 719 deletions(-) delete mode 100644 internal/cli/access_test.go delete mode 100644 internal/cli/base.go delete mode 100644 internal/cli/base_internal_test.go delete mode 100644 internal/cli/generate_secret_test.go delete mode 100644 internal/cli/init_test.go create mode 100644 internal/cli/run_proxy.go delete mode 100644 internal/cli/utils.go create mode 100644 internal/utils/make_qr_code_url.go create mode 100644 internal/utils/read_config.go diff --git a/internal/cli/access.go b/internal/cli/access.go index 15e6bfaa1..66282690c 100644 --- a/internal/cli/access.go +++ b/internal/cli/access.go @@ -12,6 +12,10 @@ import ( "strconv" "strings" "sync" + + "github.com/9seconds/mtg/v2/internal/config" + "github.com/9seconds/mtg/v2/internal/utils" + "github.com/9seconds/mtg/v2/mtglib" ) type accessResponse struct { @@ -33,8 +37,7 @@ type accessResponseURLs struct { } type Access struct { - base - + ConfigPath string `kong:"arg,required,type='existingfile',help='Path to the configuration file.',name='config-path'"` // nolint: lll PublicIPv4 net.IP `kong:"help='Public IPv4 address for proxy. By default it is resolved via remote website',name='ipv4',short='i'"` // nolint: lll PublicIPv6 net.IP `kong:"help='Public IPv6 address for proxy. By default it is resolved via remote website',name='ipv6',short='I'"` // nolint: lll Port uint `kong:"help='Port number. Default port is taken from configuration file, bind-to parameter',type:'uint',short='p'"` // nolint: lll @@ -42,17 +45,19 @@ type Access struct { } func (c *Access) Run(cli *CLI, version string) error { - if err := c.ReadConfig(version); err != nil { + conf, err := utils.ReadConfig(c.ConfigPath) + if err != nil { return fmt.Errorf("cannot init config: %w", err) } - return c.Execute(cli) -} - -func (c *Access) Execute(cli *CLI) error { resp := &accessResponse{} - resp.Secret.Base64 = c.Config.Secret.Base64() - resp.Secret.Hex = c.Config.Secret.Hex() + resp.Secret.Base64 = conf.Secret.Base64() + resp.Secret.Hex = conf.Secret.Hex() + + ntw, err := makeNetwork(conf, version) + if err != nil { + return fmt.Errorf("cannot init network: %w", err) + } wg := &sync.WaitGroup{} wg.Add(2) // nolint: gomnd @@ -60,31 +65,31 @@ func (c *Access) Execute(cli *CLI) error { go func() { defer wg.Done() - ip := cli.Access.PublicIPv4 + ip := c.PublicIPv4 if ip == nil { - ip = c.getIP("tcp4") + ip = c.getIP(ntw, "tcp4") } if ip != nil { ip = ip.To4() } - resp.IPv4 = c.makeURLs(ip, cli) + resp.IPv4 = c.makeURLs(conf, ip) }() go func() { defer wg.Done() - ip := cli.Access.PublicIPv6 + ip := c.PublicIPv6 if ip == nil { - ip = c.getIP("tcp6") + ip = c.getIP(ntw, "tcp6") } if ip != nil { ip = ip.To16() } - resp.IPv6 = c.makeURLs(ip, cli) + resp.IPv6 = c.makeURLs(conf, ip) }() wg.Wait() @@ -100,9 +105,9 @@ func (c *Access) Execute(cli *CLI) error { return nil } -func (c *Access) getIP(protocol string) net.IP { - client := c.Network.MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error) { - return c.Network.DialContext(ctx, protocol, address) // nolint: wrapcheck +func (c *Access) getIP(ntw mtglib.Network, protocol string) net.IP { + client := ntw.MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error) { + return ntw.DialContext(ctx, protocol, address) // nolint: wrapcheck }) req, err := http.NewRequest(http.MethodGet, "https://ifconfig.co", nil) // nolint: noctx @@ -134,24 +139,24 @@ func (c *Access) getIP(protocol string) net.IP { return net.ParseIP(strings.TrimSpace(string(data))) } -func (c *Access) makeURLs(ip net.IP, cli *CLI) *accessResponseURLs { +func (c *Access) makeURLs(conf *config.Config, ip net.IP) *accessResponseURLs { if ip == nil { return nil } - portNo := cli.Access.Port + portNo := c.Port if portNo == 0 { - portNo = c.Config.BindTo.PortValue(0) + portNo = conf.BindTo.Port } values := url.Values{} values.Set("server", ip.String()) values.Set("port", strconv.Itoa(int(portNo))) - if cli.Access.Hex { - values.Set("secret", c.Config.Secret.Hex()) + if c.Hex { + values.Set("secret", conf.Secret.Hex()) } else { - values.Set("secret", c.Config.Secret.Base64()) + values.Set("secret", conf.Secret.Base64()) } urlQuery := values.Encode() @@ -171,22 +176,8 @@ func (c *Access) makeURLs(ip net.IP, cli *CLI) *accessResponseURLs { RawQuery: urlQuery, }).String(), } - rv.TgQrCode = c.makeQRCode(rv.TgURL) - rv.TmeQrCode = c.makeQRCode(rv.TmeURL) + rv.TgQrCode = utils.MakeQRCodeURL(rv.TgURL) + rv.TmeQrCode = utils.MakeQRCodeURL(rv.TmeURL) return rv } - -func (c *Access) makeQRCode(data string) string { - values := url.Values{} - values.Set("qzone", "4") - values.Set("format", "svg") - values.Set("data", data) - - return (&url.URL{ - Scheme: "https", - Host: "api.qrserver.com", - Path: "v1/create-qr-code", - RawQuery: values.Encode(), - }).String() -} diff --git a/internal/cli/access_test.go b/internal/cli/access_test.go deleted file mode 100644 index b55d40afa..000000000 --- a/internal/cli/access_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package cli_test - -import ( - "net" - "net/http" - "testing" - - "github.com/9seconds/mtg/v2/internal/config" - "github.com/9seconds/mtg/v2/internal/testlib" - "github.com/9seconds/mtg/v2/mtglib" - "github.com/jarcoal/httpmock" - "github.com/stretchr/testify/suite" - "github.com/xeipuuv/gojsonschema" -) - -var accressResponseJSONSchema = func() *gojsonschema.Schema { - schema, err := gojsonschema.NewSchema(gojsonschema.NewStringLoader(` -{ - "type": "object", - "required": ["secret"], - "additionalProperties": true, - "properties": { - "secret": { - "type": "object", - "required": [ - "hex", - "base64" - ], - "additionalProperties": false, - "properties": { - "hex": { - "type": "string", - "minLength": 34 - }, - "base64": { - "type": "string", - "minLength": 10 - } - } - }, - "ipv4": { - "$ref": "#/definitions/ip" - }, - "ipv6": { - "$ref": "#/definitions/ip" - } - }, - "definitions": { - "ip": { - "type": "object", - "required": [ - "ip", - "port", - "tg_url", - "tg_qrcode", - "tme_url", - "tme_qrcode" - ], - "additionalProperties": false, - "properties": { - "ip": { - "type": "string", - "minLength": 1, - "anyOf": [ - { - "format": "ipv4" - }, - { - "format": "ipv6" - } - ] - }, - "port": { - "type": "integer", - "multipleOf": 1.0, - "exclusiveMinimum": 0, - "exclusiveMaximum": 65536 - }, - "tg_url": { - "type": "string", - "minLength": 1, - "format": "uri" - }, - "tg_qrcode": { - "type": "string", - "minLength": 1, - "format": "uri" - }, - "tme_url": { - "type": "string", - "minLength": 1, - "format": "uri" - }, - "tme_qrcode": { - "type": "string", - "minLength": 1, - "format": "uri" - } - } - } - } -} - `)) - if err != nil { - panic(err) - } - - return schema -}() - -type AccessTestSuite struct { - CommonTestSuite -} - -func (suite *AccessTestSuite) SetupTest() { - suite.CommonTestSuite.SetupTest() - - suite.cli.Access.Config = &config.Config{} - suite.cli.Access.Config.Secret = mtglib.GenerateSecret("google.com") - suite.cli.Access.Network = suite.networkMock - - suite.NoError( - suite.cli.Access.Config.BindTo.UnmarshalText([]byte("0.0.0.0:80"))) -} - -func (suite *AccessTestSuite) TestGenerateNoCalls() { - suite.cli.Access.PublicIPv4 = net.ParseIP("10.0.0.10") - suite.cli.Access.PublicIPv6 = net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") - - output := testlib.CaptureStdout(func() { - suite.NoError(suite.cli.Access.Execute(suite.cli)) - }) - - validated, err := accressResponseJSONSchema.Validate( - gojsonschema.NewStringLoader(output)) - suite.NoError(err) - suite.Empty(validated.Errors()) - suite.True(validated.Valid()) - - suite.Contains(output, "10.0.0.10") - suite.Contains(output, "2001:db8:85a3::8a2e:370:7334") - suite.Contains(output, "ipv4") - suite.Contains(output, "ipv6") - suite.Contains(output, suite.cli.Access.Config.Secret.Base64()) - suite.Contains(output, suite.cli.Access.Config.Secret.Hex()) -} - -func (suite *AccessTestSuite) TestGenerateIPv4Call() { - suite.cli.Access.PublicIPv6 = net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") - - httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co", - httpmock.NewStringResponder(http.StatusOK, "10.11.12.13")) - - output := testlib.CaptureStdout(func() { - suite.NoError(suite.cli.Access.Execute(suite.cli)) - }) - - validated, err := accressResponseJSONSchema.Validate( - gojsonschema.NewStringLoader(output)) - suite.NoError(err) - suite.Empty(validated.Errors()) - suite.True(validated.Valid()) - - suite.Contains(output, "10.11.12.13") - suite.Contains(output, "2001:db8:85a3::8a2e:370:7334") - suite.Contains(output, "ipv4") - suite.Contains(output, "ipv6") - suite.Contains(output, suite.cli.Access.Config.Secret.Base64()) - suite.Contains(output, suite.cli.Access.Config.Secret.Hex()) -} - -func (suite *AccessTestSuite) TestIPv4CallFail() { - suite.cli.Access.PublicIPv6 = net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") - - httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co", - httpmock.NewStringResponder(http.StatusForbidden, "")) - - output := testlib.CaptureStdout(func() { - suite.NoError(suite.cli.Access.Execute(suite.cli)) - }) - - validated, err := accressResponseJSONSchema.Validate( - gojsonschema.NewStringLoader(output)) - suite.NoError(err) - suite.Empty(validated.Errors()) - suite.True(validated.Valid()) - - suite.Contains(output, "2001:db8:85a3::8a2e:370:7334") - suite.NotContains(output, "ipv4") - suite.Contains(output, "ipv6") - suite.Contains(output, suite.cli.Access.Config.Secret.Base64()) - suite.Contains(output, suite.cli.Access.Config.Secret.Hex()) -} - -func TestAccess(t *testing.T) { // nolint: paralleltest - suite.Run(t, &AccessTestSuite{}) -} diff --git a/internal/cli/base.go b/internal/cli/base.go deleted file mode 100644 index 0bdc93be2..000000000 --- a/internal/cli/base.go +++ /dev/null @@ -1,81 +0,0 @@ -package cli - -import ( - "fmt" - "net" - "net/url" - "os" - - "github.com/9seconds/mtg/v2/internal/config" - "github.com/9seconds/mtg/v2/mtglib" - "github.com/9seconds/mtg/v2/network" -) - -type base struct { - ConfigPath string `kong:"arg,required,type='existingfile',help='Path to the configuration file.',name='config-path'"` // nolint: lll - - Network mtglib.Network `kong:"-"` - Config *config.Config `kong:"-"` -} - -func (b *base) ReadConfig(version string) error { - content, err := os.ReadFile(b.ConfigPath) - if err != nil { - return fmt.Errorf("cannot read config file: %w", err) - } - - conf, err := config.Parse(content) - if err != nil { - return fmt.Errorf("cannot parse config: %w", err) - } - - ntw, err := b.makeNetwork(conf, version) - if err != nil { - return fmt.Errorf("cannot build a network: %w", err) - } - - b.Config = conf - b.Network = ntw - - return nil -} - -func (b *base) makeNetwork(conf *config.Config, version string) (mtglib.Network, error) { - tcpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultTimeout) - httpTimeout := conf.Network.Timeout.HTTP.Value(network.DefaultHTTPTimeout) - dohIP := conf.Network.DOHIP.Value(net.ParseIP(network.DefaultDOHHostname)).String() - bufferSize := conf.TCPBuffer.Value(network.DefaultBufferSize) - userAgent := "mtg/" + version - - baseDialer, err := network.NewDefaultDialer(tcpTimeout, int(bufferSize)) - if err != nil { - return nil, fmt.Errorf("cannot build a default dialer: %w", err) - } - - proxyURLs := make([]*url.URL, 0, len(conf.Network.Proxies)) - - for _, v := range conf.Network.Proxies { - if value := v.Value(nil); value != nil { - proxyURLs = append(proxyURLs, v.Value(nil)) - } - } - - switch len(proxyURLs) { - case 0: - return network.NewNetwork(baseDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck - case 1: - socksDialer, err := network.NewSocks5Dialer(baseDialer, proxyURLs[0]) - if err != nil { - return nil, fmt.Errorf("cannot build socks5 dialer: %w", err) - } - - return network.NewNetwork(socksDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck - } - - socksDialer, err := network.NewLoadBalancedSocks5Dialer(baseDialer, proxyURLs) - if err != nil { - return nil, fmt.Errorf("cannot build socks5 dialer: %w", err) - } - - return network.NewNetwork(socksDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck -} diff --git a/internal/cli/base_internal_test.go b/internal/cli/base_internal_test.go deleted file mode 100644 index 51909d4bc..000000000 --- a/internal/cli/base_internal_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package cli - -import ( - "path/filepath" - "testing" - - "github.com/stretchr/testify/suite" -) - -type BaseTestSuite struct { - suite.Suite - - b base -} - -func (suite *BaseTestSuite) SetupTest() { - suite.b = base{} -} - -func (suite *BaseTestSuite) TestReadConfigNok() { - suite.b.ConfigPath = filepath.Join("testdata", "unknown") - suite.Error(suite.b.ReadConfig("dev")) -} - -func (suite *BaseTestSuite) TestReadConfig() { - suite.b.ConfigPath = filepath.Join("testdata", "minimal.toml") - suite.NoError(suite.b.ReadConfig("dev")) -} - -func TestBase(t *testing.T) { - t.Parallel() - suite.Run(t, &BaseTestSuite{}) -} diff --git a/internal/cli/generate_secret_test.go b/internal/cli/generate_secret_test.go deleted file mode 100644 index 64c600274..000000000 --- a/internal/cli/generate_secret_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package cli_test - -import ( - "strings" - "testing" - - "github.com/9seconds/mtg/v2/internal/testlib" - "github.com/9seconds/mtg/v2/mtglib" - "github.com/stretchr/testify/suite" -) - -type GenerateSecretTestSuite struct { - CommonTestSuite -} - -func (suite *GenerateSecretTestSuite) SetupTest() { - suite.CommonTestSuite.SetupTest() - - suite.cli.GenerateSecret.HostName = "google.com" -} - -func (suite *GenerateSecretTestSuite) TestDefault() { - output := testlib.CaptureStdout(func() { - suite.NoError(suite.cli.GenerateSecret.Run(suite.cli, "dev")) - }) - suite.True(strings.HasPrefix(output, "7")) - - secret, err := mtglib.ParseSecret(output) - suite.NoError(err) - suite.True(secret.Valid()) - suite.Equal("google.com", secret.Host) -} - -func (suite *GenerateSecretTestSuite) TestHex() { - suite.cli.GenerateSecret.Hex = true - - output := testlib.CaptureStdout(func() { - suite.NoError(suite.cli.GenerateSecret.Run(suite.cli, "dev")) - }) - suite.True(strings.HasPrefix(output, "ee")) - - secret, err := mtglib.ParseSecret(output) - suite.NoError(err) - suite.True(secret.Valid()) - suite.Equal("google.com", secret.Host) -} - -func TestGenerateSecret(t *testing.T) { - t.Parallel() - suite.Run(t, &GenerateSecretTestSuite{}) -} diff --git a/internal/cli/init_test.go b/internal/cli/init_test.go deleted file mode 100644 index e94e829fd..000000000 --- a/internal/cli/init_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package cli_test - -import ( - "net/http" - - "github.com/9seconds/mtg/v2/internal/cli" - "github.com/9seconds/mtg/v2/internal/testlib" - "github.com/jarcoal/httpmock" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" -) - -type CommonTestSuite struct { - suite.Suite - - cli *cli.CLI - networkMock *testlib.MtglibNetworkMock - httpClient *http.Client -} - -func (suite *CommonTestSuite) SetupTest() { - suite.networkMock = &testlib.MtglibNetworkMock{} - suite.httpClient = &http.Client{} - suite.cli = &cli.CLI{} - - httpmock.ActivateNonDefault(suite.httpClient) - - suite.networkMock. - On("MakeHTTPClient", mock.Anything). - Maybe(). - Return(suite.httpClient) -} - -func (suite *CommonTestSuite) TearDownTest() { - suite.networkMock.AssertExpectations(suite.T()) - httpmock.DeactivateAndReset() -} diff --git a/internal/cli/proxy.go b/internal/cli/proxy.go index b20b39d03..199018549 100644 --- a/internal/cli/proxy.go +++ b/internal/cli/proxy.go @@ -2,166 +2,19 @@ package cli import ( "fmt" - "net" - "os" - "github.com/9seconds/mtg/v2/antireplay" - "github.com/9seconds/mtg/v2/events" "github.com/9seconds/mtg/v2/internal/utils" - "github.com/9seconds/mtg/v2/ipblocklist" - "github.com/9seconds/mtg/v2/logger" - "github.com/9seconds/mtg/v2/mtglib" - "github.com/9seconds/mtg/v2/stats" - "github.com/rs/zerolog" ) type Proxy struct { - base + ConfigPath string `kong:"arg,required,type='existingfile',help='Path to the configuration file.',name='config-path'"` // nolint: lll } func (c *Proxy) Run(cli *CLI, version string) error { - if err := c.ReadConfig(version); err != nil { - return fmt.Errorf("cannot init config: %w", err) - } - - return c.Execute() -} - -func (c *Proxy) Execute() error { - zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs - zerolog.TimestampFieldName = "timestamp" - zerolog.LevelFieldName = "level" - - if c.Config.Debug { - zerolog.SetGlobalLevel(zerolog.DebugLevel) - } else { - zerolog.SetGlobalLevel(zerolog.WarnLevel) - } - - ctx := utils.RootContext() - opts := mtglib.ProxyOpts{ - Logger: logger.NewZeroLogger(zerolog.New(os.Stdout).With().Timestamp().Logger()), - Network: c.Network, - AntiReplayCache: antireplay.NewNoop(), - IPBlocklist: ipblocklist.NewNoop(), - EventStream: events.NewNoopStream(), - - Secret: c.Config.Secret, - BufferSize: c.Config.TCPBuffer.Value(mtglib.DefaultBufferSize), - DomainFrontingPort: c.Config.DomainFrontingPort.Value(mtglib.DefaultDomainFrontingPort), - IdleTimeout: c.Config.Network.Timeout.Idle.Value(mtglib.DefaultIdleTimeout), - PreferIP: c.Config.PreferIP.Value(mtglib.DefaultPreferIP), - } - - opts.Logger.BindStr("configuration", c.Config.String()).Debug("configuration") - - c.setupAntiReplayCache(&opts) - - if err := c.setupIPBlocklist(&opts); err != nil { - return fmt.Errorf("cannot setup ipblocklist: %w", err) - } - - if err := c.setupEventStream(&opts); err != nil { - return fmt.Errorf("cannot setup event stream: %w", err) - } - - proxy, err := mtglib.NewProxy(opts) - if err != nil { - return fmt.Errorf("cannot create a proxy: %w", err) - } - - listener, err := net.Listen("tcp", c.Config.BindTo.String()) - if err != nil { - return fmt.Errorf("cannot start proxy: %w", err) - } - - go proxy.Serve(listener) // nolint: errcheck - - <-ctx.Done() - listener.Close() - proxy.Shutdown() - - return nil -} - -func (c *Proxy) setupAntiReplayCache(opts *mtglib.ProxyOpts) { - if !c.Config.Defense.AntiReplay.Enabled { - return - } - - opts.AntiReplayCache = antireplay.NewStableBloomFilter( - c.Config.Defense.AntiReplay.MaxSize.Value(antireplay.DefaultStableBloomFilterMaxSize), - c.Config.Defense.AntiReplay.ErrorRate.Value(antireplay.DefaultStableBloomFilterErrorRate), - ) -} - -func (c *Proxy) setupIPBlocklist(opts *mtglib.ProxyOpts) error { - if !c.Config.Defense.Blocklist.Enabled { - return nil - } - - remoteURLs := []string{} - localFiles := []string{} - - for _, v := range c.Config.Defense.Blocklist.URLs { - if v.IsRemote() { - remoteURLs = append(remoteURLs, v.String()) - } else { - localFiles = append(localFiles, v.String()) - } - } - - firehol, err := ipblocklist.NewFirehol(opts.Logger.Named("ipblockist"), - c.Network, - c.Config.Defense.Blocklist.DownloadConcurrency, - remoteURLs, - localFiles) + conf, err := utils.ReadConfig(c.ConfigPath) if err != nil { - return err // nolint: wrapcheck - } - - go firehol.Run(c.Config.Defense.Blocklist.UpdateEach.Value(ipblocklist.DefaultFireholUpdateEach)) - - opts.IPBlocklist = firehol - - return nil -} - -func (c *Proxy) setupEventStream(opts *mtglib.ProxyOpts) error { - factories := make([]events.ObserverFactory, 0, 2) - - if c.Config.Stats.StatsD.Enabled { - statsdFactory, err := stats.NewStatsd( - c.Config.Stats.StatsD.Address.String(), - opts.Logger.Named("statsd"), - c.Config.Stats.StatsD.MetricPrefix.Value(stats.DefaultStatsdMetricPrefix), - c.Config.Stats.StatsD.TagFormat.Value(stats.DefaultStatsdTagFormat)) - if err != nil { - return fmt.Errorf("cannot build statsd observer: %w", err) - } - - factories = append(factories, statsdFactory.Make) - } - - if c.Config.Stats.Prometheus.Enabled { - prometheus := stats.NewPrometheus( - c.Config.Stats.Prometheus.MetricPrefix.Value(stats.DefaultMetricPrefix), - c.Config.Stats.Prometheus.HTTPPath.Value("/"), - ) - - listener, err := net.Listen("tcp", c.Config.Stats.Prometheus.BindTo.String()) - if err != nil { - return fmt.Errorf("cannot start a listener for prometheus: %w", err) - } - - go prometheus.Serve(listener) // nolint: errcheck - - factories = append(factories, prometheus.Make) - } - - if len(factories) > 0 { - opts.EventStream = events.NewEventStream(factories) + return fmt.Errorf("cannot init config: %w", err) } - return nil + return runProxy(conf, version) } diff --git a/internal/cli/run_proxy.go b/internal/cli/run_proxy.go new file mode 100644 index 000000000..4716bc1aa --- /dev/null +++ b/internal/cli/run_proxy.go @@ -0,0 +1,208 @@ +package cli + +import ( + "fmt" + "net" + "net/url" + "os" + + "github.com/9seconds/mtg/v2/antireplay" + "github.com/9seconds/mtg/v2/events" + "github.com/9seconds/mtg/v2/internal/config" + "github.com/9seconds/mtg/v2/internal/utils" + "github.com/9seconds/mtg/v2/ipblocklist" + "github.com/9seconds/mtg/v2/logger" + "github.com/9seconds/mtg/v2/mtglib" + "github.com/9seconds/mtg/v2/network" + "github.com/9seconds/mtg/v2/stats" + "github.com/rs/zerolog" +) + +func makeLogger(conf *config.Config) mtglib.Logger { + zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs + zerolog.TimestampFieldName = "timestamp" + zerolog.LevelFieldName = "level" + + if conf.Debug.Get(false) { + zerolog.SetGlobalLevel(zerolog.DebugLevel) + } else { + zerolog.SetGlobalLevel(zerolog.WarnLevel) + } + + baseLogger := zerolog.New(os.Stdout).With().Timestamp().Logger() + + return logger.NewZeroLogger(baseLogger) +} + +func makeNetwork(conf *config.Config, version string) (mtglib.Network, error) { + tcpTimeout := conf.Network.Timeout.TCP.Get(network.DefaultTimeout) + httpTimeout := conf.Network.Timeout.HTTP.Get(network.DefaultHTTPTimeout) + dohIP := conf.Network.DOHIP.Get(net.ParseIP(network.DefaultDOHHostname)).String() + bufferSize := conf.TCPBuffer.Get(network.DefaultBufferSize) + userAgent := "mtg/" + version + + baseDialer, err := network.NewDefaultDialer(tcpTimeout, int(bufferSize)) + if err != nil { + return nil, fmt.Errorf("cannot build a default dialer: %w", err) + } + + if len(conf.Network.Proxies) == 0 { + return network.NewNetwork(baseDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck + } + + proxyURLs := make([]*url.URL, 0, len(conf.Network.Proxies)) + for _, v := range conf.Network.Proxies { + if value := v.Get(nil); value != nil { + proxyURLs = append(proxyURLs, value) + } + } + + if len(proxyURLs) == 1 { + socksDialer, err := network.NewSocks5Dialer(baseDialer, proxyURLs[0]) + if err != nil { + return nil, fmt.Errorf("cannot build socks5 dialer: %w", err) + } + + return network.NewNetwork(socksDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck + } + + socksDialer, err := network.NewLoadBalancedSocks5Dialer(baseDialer, proxyURLs) + if err != nil { + return nil, fmt.Errorf("cannot build socks5 dialer: %w", err) + } + + return network.NewNetwork(socksDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck +} + +func makeAntiReplayCache(conf *config.Config) mtglib.AntiReplayCache { + if !conf.Defense.AntiReplay.Enabled.Get(false) { + return antireplay.NewNoop() + } + + return antireplay.NewStableBloomFilter( + conf.Defense.AntiReplay.MaxSize.Get(antireplay.DefaultStableBloomFilterMaxSize), + conf.Defense.AntiReplay.ErrorRate.Get(antireplay.DefaultStableBloomFilterErrorRate), + ) +} + +func makeIPBlocklist(conf *config.Config, logger mtglib.Logger, ntw mtglib.Network) (mtglib.IPBlocklist, error) { + if !conf.Defense.Blocklist.Enabled.Get(false) { + return ipblocklist.NewNoop(), nil + } + + remoteURLs := []string{} + localFiles := []string{} + + for _, v := range conf.Defense.Blocklist.URLs { + if v.IsRemote() { + remoteURLs = append(remoteURLs, v.String()) + } else { + localFiles = append(localFiles, v.String()) + } + } + + firehol, err := ipblocklist.NewFirehol(logger.Named("ipblockist"), + ntw, + conf.Defense.Blocklist.DownloadConcurrency.Get(1), + remoteURLs, + localFiles) + if err != nil { + return nil, fmt.Errorf("incorrect parameters for firehol: %w", err) + } + + return firehol, nil +} + +func makeEventStream(conf *config.Config, logger mtglib.Logger) (mtglib.EventStream, error) { + factories := make([]events.ObserverFactory, 0, 2) + + if conf.Stats.StatsD.Enabled.Get(false) { + statsdFactory, err := stats.NewStatsd( + conf.Stats.StatsD.Address.Get(""), + logger.Named("statsd"), + conf.Stats.StatsD.MetricPrefix.Get(stats.DefaultStatsdMetricPrefix), + conf.Stats.StatsD.TagFormat.Get(stats.DefaultStatsdTagFormat)) + if err != nil { + return nil, fmt.Errorf("cannot build statsd observer: %w", err) + } + + factories = append(factories, statsdFactory.Make) + } + + if conf.Stats.Prometheus.Enabled.Get(false) { + prometheus := stats.NewPrometheus( + conf.Stats.Prometheus.MetricPrefix.Get(stats.DefaultMetricPrefix), + conf.Stats.Prometheus.HTTPPath.Get("/"), + ) + + listener, err := net.Listen("tcp", conf.Stats.Prometheus.BindTo.Get("")) + if err != nil { + return nil, fmt.Errorf("cannot start a listener for prometheus: %w", err) + } + + go prometheus.Serve(listener) // nolint: errcheck + + factories = append(factories, prometheus.Make) + } + + if len(factories) > 0 { + return events.NewEventStream(factories), nil + } + + return events.NewNoopStream(), nil +} + +func runProxy(conf *config.Config, version string) error { + logger := makeLogger(conf) + + logger.BindStr("configuration", conf.String()).Debug("configuration") + + ntw, err := makeNetwork(conf, version) + if err != nil { + return fmt.Errorf("cannot build network: %w", err) + } + + blocklist, err := makeIPBlocklist(conf, logger, ntw) + if err != nil { + return fmt.Errorf("cannot build ip blocklist: %w", err) + } + + eventStream, err := makeEventStream(conf, logger) + if err != nil { + return fmt.Errorf("cannot build event stream: %w", err) + } + + opts := mtglib.ProxyOpts{ + Logger: logger, + Network: ntw, + AntiReplayCache: makeAntiReplayCache(conf), + IPBlocklist: blocklist, + EventStream: eventStream, + + Secret: conf.Secret, + BufferSize: conf.TCPBuffer.Get(mtglib.DefaultBufferSize), + DomainFrontingPort: conf.DomainFrontingPort.Get(mtglib.DefaultDomainFrontingPort), + IdleTimeout: conf.Network.Timeout.Idle.Get(mtglib.DefaultIdleTimeout), + PreferIP: conf.PreferIP.Get(mtglib.DefaultPreferIP), + } + + proxy, err := mtglib.NewProxy(opts) + if err != nil { + return fmt.Errorf("cannot create a proxy: %w", err) + } + + listener, err := net.Listen("tcp", conf.BindTo.Get("")) + if err != nil { + return fmt.Errorf("cannot start proxy: %w", err) + } + + ctx := utils.RootContext() + + go proxy.Serve(listener) // nolint: errcheck + + <-ctx.Done() + listener.Close() + proxy.Shutdown() + + return nil +} diff --git a/internal/cli/utils.go b/internal/cli/utils.go deleted file mode 100644 index d07f04782..000000000 --- a/internal/cli/utils.go +++ /dev/null @@ -1,66 +0,0 @@ -package cli - -import ( - "fmt" - "net" - "net/url" - "os" - - "github.com/9seconds/mtg/v2/internal/config2" - "github.com/9seconds/mtg/v2/mtglib" - "github.com/9seconds/mtg/v2/network" -) - -func readTOMLConfig(path string) (*config2.Config, error) { - content, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("cannot read config file: %w", err) - } - - conf, err := config2.Parse(content) - if err != nil { - return nil, fmt.Errorf("cannot parse config: %w", err) - } - - return conf, nil -} - -func makeNetwork(conf *config2.Config, version string) (mtglib.Network, error) { - tcpTimeout := conf.Network.Timeout.TCP.Get(network.DefaultTimeout) - httpTimeout := conf.Network.Timeout.HTTP.Get(network.DefaultHTTPTimeout) - dohIP := conf.Network.DOHIP.Get(net.ParseIP(network.DefaultDOHHostname)).String() - bufferSize := conf.TCPBuffer.Get(network.DefaultBufferSize) - userAgent := "mtg/" + version - - baseDialer, err := network.NewDefaultDialer(tcpTimeout, int(bufferSize)) - if err != nil { - return nil, fmt.Errorf("cannot build a default dialer: %w", err) - } - - if len(conf.Network.Proxies) == 0 { - return network.NewNetwork(baseDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck - } - - proxyURLs := make([]*url.URL, 0, len(conf.Network.Proxies)) - for _, v := range conf.Network.Proxies { - if value := v.Get(nil); value != nil { - proxyURLs = append(proxyURLs, value) - } - } - - if len(proxyURLs) == 1 { - socksDialer, err := network.NewSocks5Dialer(baseDialer, proxyURLs[0]) - if err != nil { - return nil, fmt.Errorf("cannot build socks5 dialer: %w", err) - } - - return network.NewNetwork(socksDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck - } - - socksDialer, err := network.NewLoadBalancedSocks5Dialer(baseDialer, proxyURLs) - if err != nil { - return nil, fmt.Errorf("cannot build socks5 dialer: %w", err) - } - - return network.NewNetwork(socksDialer, userAgent, dohIP, httpTimeout) // nolint: wrapcheck -} diff --git a/internal/config/type_bool.go b/internal/config/type_bool.go index 233a5a664..f1ec9d588 100644 --- a/internal/config/type_bool.go +++ b/internal/config/type_bool.go @@ -3,7 +3,6 @@ package config import ( "fmt" "strconv" - "strings" ) type TypeBool struct { @@ -11,15 +10,13 @@ type TypeBool struct { } func (t *TypeBool) Set(data string) error { - switch strings.ToLower(data) { - case "1", "y", "yes", "enabled", "true": - t.Value = true - case "0", "n", "no", "disabled", "false": - t.Value = false - default: - return fmt.Errorf("incorrect bool value %s", data) + parsed, err := strconv.ParseBool(data) + if err != nil { + return fmt.Errorf("incorrect bool value: %s", data) } + t.Value = parsed + return nil } @@ -27,11 +24,11 @@ func (t TypeBool) Get(defaultValue bool) bool { return t.Value || defaultValue } -func (t *TypeBool) UnmarshalText(data []byte) error { +func (t *TypeBool) UnmarshalJSON(data []byte) error { return t.Set(string(data)) } -func (t TypeBool) MarshalText() ([]byte, error) { +func (t TypeBool) MarshalJSON() ([]byte, error) { return []byte(t.String()), nil } diff --git a/internal/config/type_bool_test.go b/internal/config/type_bool_test.go index 2b9028598..99d0488f5 100644 --- a/internal/config/type_bool_test.go +++ b/internal/config/type_bool_test.go @@ -20,53 +20,41 @@ type TypeBoolTestSuite struct { } func (suite *TypeBoolTestSuite) TestUnmarshalFail() { - testData := []string{ + testData := []interface{}{ "", "np", "нет", + int(10), + []int{}, } for _, v := range testData { - data, err := json.Marshal(map[string]string{ + data, err := json.Marshal(map[string]interface{}{ "value": v, }) suite.NoError(err) - suite.T().Run(v, func(t *testing.T) { + suite.T().Run(fmt.Sprintf("%v", v), func(t *testing.T) { assert.Error(t, json.Unmarshal(data, &typeBoolTestStruct{})) }) } } func (suite *TypeBoolTestSuite) TestUnmarshalOk() { - testData := map[string]bool{ - "0": false, - "N": false, - "nO": false, - "no": false, - "dISAbLEd": false, - "False": false, - "false": false, - - "1": true, - "y": true, - "Yes": true, - "yes": true, - "enABLED": true, - "True": true, - "TRUE": true, - "true": true, + testData := []bool{ + true, + false, } - for k, v := range testData { + for _, v := range testData { value := v - data, err := json.Marshal(map[string]string{ - "value": k, + data, err := json.Marshal(map[string]bool{ + "value": v, }) suite.NoError(err) - suite.T().Run(k, func(t *testing.T) { + suite.T().Run(strconv.FormatBool(v), func(t *testing.T) { testStruct := &typeBoolTestStruct{} assert.NoError(t, json.Unmarshal(data, testStruct)) @@ -81,18 +69,24 @@ func (suite *TypeBoolTestSuite) TestUnmarshalOk() { func (suite *TypeBoolTestSuite) TestMarshalOk() { for _, v := range []bool{true, false} { - name := strconv.FormatBool(v) + value := v - suite.T().Run(name, func(t *testing.T) { + suite.T().Run(strconv.FormatBool(v), func(t *testing.T) { testStruct := typeBoolTestStruct{ Value: config.TypeBool{ - Value: v, + Value: value, }, } - data, err := json.Marshal(testStruct) + encodedJSON, err := json.Marshal(testStruct) assert.NoError(t, err) - assert.JSONEq(t, fmt.Sprintf(`{"value": "%s"}`, name), string(data)) + + expectedJSON, err := json.Marshal(map[string]bool{ + "value": value, + }) + assert.NoError(t, err) + + assert.JSONEq(t, string(expectedJSON), string(encodedJSON)) }) } } diff --git a/internal/config/type_error_rate.go b/internal/config/type_error_rate.go index cfe6ebcf9..e950424d7 100644 --- a/internal/config/type_error_rate.go +++ b/internal/config/type_error_rate.go @@ -34,11 +34,11 @@ func (t TypeErrorRate) Get(defaultValue float64) float64 { return t.Value } -func (t *TypeErrorRate) UnmarshalText(data []byte) error { +func (t *TypeErrorRate) UnmarshalJSON(data []byte) error { return t.Set(string(data)) } -func (t TypeErrorRate) MarshalText() ([]byte, error) { +func (t TypeErrorRate) MarshalJSON() ([]byte, error) { return []byte(t.String()), nil } diff --git a/internal/config/type_error_rate_test.go b/internal/config/type_error_rate_test.go index aebe8f017..c45ffa77d 100644 --- a/internal/config/type_error_rate_test.go +++ b/internal/config/type_error_rate_test.go @@ -45,27 +45,14 @@ func (suite *TypeErrorRateTestSuite) TestUnmarshalFail() { } func (suite *TypeErrorRateTestSuite) TestUnmarshalOk() { - testData := map[string]float64{ - "1": 1.0, - "1.0": 1.0, - "0.5": 0.5, - ".5": 0.5, - } - - for k, v := range testData { - value := v - - data, err := json.Marshal(map[string]string{ - "value": k, - }) - suite.NoError(err) + data, err := json.Marshal(map[string]float64{ + "value": 1.0, + }) + suite.NoError(err) - suite.T().Run(k, func(t *testing.T) { - testStruct := &typeErrorRateTestStruct{} - assert.NoError(t, json.Unmarshal(data, testStruct)) - assert.InEpsilon(t, value, testStruct.Value.Value, 1e-10) - }) - } + testStruct := &typeErrorRateTestStruct{} + suite.NoError(json.Unmarshal(data, testStruct)) + suite.InEpsilon(1.0, testStruct.Value.Value, 1e-10) } func (suite *TypeErrorRateTestSuite) TestMarshalOk() { @@ -77,7 +64,7 @@ func (suite *TypeErrorRateTestSuite) TestMarshalOk() { encodedJson, err := json.Marshal(testStruct) suite.NoError(err) - suite.JSONEq(`{"value": "1.01"}`, string(encodedJson)) + suite.JSONEq(`{"value": 1.01}`, string(encodedJson)) } func (suite *TypeErrorRateTestSuite) TestGet() { diff --git a/internal/config/type_hostport.go b/internal/config/type_hostport.go index 9f006a73f..665ba1015 100644 --- a/internal/config/type_hostport.go +++ b/internal/config/type_hostport.go @@ -8,6 +8,8 @@ import ( type TypeHostPort struct { Value string + Host string + Port uint } func (t *TypeHostPort) Set(value string) error { @@ -34,6 +36,8 @@ func (t *TypeHostPort) Set(value string) error { } t.Value = net.JoinHostPort(host, port) + t.Port = uint(portValue) + t.Host = host return nil } diff --git a/internal/utils/make_qr_code_url.go b/internal/utils/make_qr_code_url.go new file mode 100644 index 000000000..752986fce --- /dev/null +++ b/internal/utils/make_qr_code_url.go @@ -0,0 +1,19 @@ +package utils + +import "net/url" + +func MakeQRCodeURL(data string) string { + values := url.Values{} + values.Set("qzone", "4") + values.Set("format", "svg") + values.Set("data", data) + + rv := url.URL{ + Scheme: "https", + Host: "api.qrserver.com", + Path: "v1/create-qr-code", + RawQuery: values.Encode(), + } + + return rv.String() +} diff --git a/internal/utils/read_config.go b/internal/utils/read_config.go new file mode 100644 index 000000000..eee7b981a --- /dev/null +++ b/internal/utils/read_config.go @@ -0,0 +1,26 @@ +package utils + +import ( + "fmt" + "os" + + "github.com/9seconds/mtg/v2/internal/config" +) + +func ReadConfig(path string) (*config.Config, error) { + content, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("cannot read config file: %w", err) + } + + conf, err := config.Parse(content) + if err != nil { + return nil, fmt.Errorf("cannot parse config: %w", err) + } + + if err := conf.Validate(); err != nil { + return nil, fmt.Errorf("invalid config: %w", err) + } + + return conf, nil +} diff --git a/mtglib/proxy_opts.go b/mtglib/proxy_opts.go index dc9b57b73..d74728626 100644 --- a/mtglib/proxy_opts.go +++ b/mtglib/proxy_opts.go @@ -62,7 +62,7 @@ type ProxyOpts struct { // specifies a hostname only. // // This is an optional setting. - DomainFrontingPort uint + DomainFrontingPort uint16 // IdleTimeout is a timeout for relay when we have to break a // stream. From ed91290e4742081ca44fc4394524fdce4ff8161a Mon Sep 17 00:00:00 2001 From: 9seconds Date: Fri, 30 Jul 2021 15:07:35 +0300 Subject: [PATCH 05/12] Add test for reading config --- internal/utils/read_config_test.go | 55 +++++++++++++++++++ internal/utils/testdata/broken.toml | 1 + internal/utils/testdata/empty.toml | 0 internal/{cli => utils}/testdata/minimal.toml | 0 internal/utils/testdata/missed-bindto.toml | 2 + internal/utils/testdata/missed-secret.toml | 1 + 6 files changed, 59 insertions(+) create mode 100644 internal/utils/read_config_test.go create mode 100644 internal/utils/testdata/broken.toml create mode 100644 internal/utils/testdata/empty.toml rename internal/{cli => utils}/testdata/minimal.toml (100%) create mode 100644 internal/utils/testdata/missed-bindto.toml create mode 100644 internal/utils/testdata/missed-secret.toml diff --git a/internal/utils/read_config_test.go b/internal/utils/read_config_test.go new file mode 100644 index 000000000..7ba06b052 --- /dev/null +++ b/internal/utils/read_config_test.go @@ -0,0 +1,55 @@ +package utils_test + +import ( + "path/filepath" + "testing" + + "github.com/9seconds/mtg/v2/internal/utils" + "github.com/stretchr/testify/suite" +) + +type ReadConfigTestSuite struct { + suite.Suite +} + +func (suite *ReadConfigTestSuite) GetConfigPath(filename string) string { + return filepath.Join("testdata", filename) +} + +func (suite *ReadConfigTestSuite) TestReadMinimal() { + conf, err := utils.ReadConfig(suite.GetConfigPath("minimal.toml")) + suite.NoError(err) + suite.NoError(conf.Validate()) + suite.Equal("0.0.0.0:80", conf.BindTo.Get("")) + suite.Equal("7mqFMMq3P2Tvvt_rPx5qhmFnb29nbGUuY29t", conf.Secret.Base64()) +} + +func (suite *ReadConfigTestSuite) TestReadAbsentFile() { + _, err := utils.ReadConfig(suite.GetConfigPath("unknown.file")) + suite.Error(err) +} + +func (suite *ReadConfigTestSuite) TestBrokenFile() { + _, err := utils.ReadConfig(suite.GetConfigPath("broken.toml")) + suite.Error(err) +} + +func (suite *ReadConfigTestSuite) TestMissedBindTo() { + _, err := utils.ReadConfig(suite.GetConfigPath("missed-bindto.toml")) + suite.Error(err) +} + +func (suite *ReadConfigTestSuite) TestMissedSecret() { + _, err := utils.ReadConfig(suite.GetConfigPath("missed-secret.toml")) + suite.Error(err) +} + +func (suite *ReadConfigTestSuite) TestEmpty() { + _, err := utils.ReadConfig(suite.GetConfigPath("empty.toml")) + suite.Error(err) +} + +func TestReadConfig(t *testing.T) { + t.Parallel() + suite.Run(t, &ReadConfigTestSuite{}) +} diff --git a/internal/utils/testdata/broken.toml b/internal/utils/testdata/broken.toml new file mode 100644 index 000000000..b4de39476 --- /dev/null +++ b/internal/utils/testdata/broken.toml @@ -0,0 +1 @@ +11 diff --git a/internal/utils/testdata/empty.toml b/internal/utils/testdata/empty.toml new file mode 100644 index 000000000..e69de29bb diff --git a/internal/cli/testdata/minimal.toml b/internal/utils/testdata/minimal.toml similarity index 100% rename from internal/cli/testdata/minimal.toml rename to internal/utils/testdata/minimal.toml diff --git a/internal/utils/testdata/missed-bindto.toml b/internal/utils/testdata/missed-bindto.toml new file mode 100644 index 000000000..82cce1b9a --- /dev/null +++ b/internal/utils/testdata/missed-bindto.toml @@ -0,0 +1,2 @@ + +secret = "7mqFMMq3P2Tvvt_rPx5qhmFnb29nbGUuY29t" diff --git a/internal/utils/testdata/missed-secret.toml b/internal/utils/testdata/missed-secret.toml new file mode 100644 index 000000000..90e9bb053 --- /dev/null +++ b/internal/utils/testdata/missed-secret.toml @@ -0,0 +1 @@ +bind-to = "0.0.0.0:80" From c53364d9520fadf544db5ea063c9c83360540964 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Fri, 30 Jul 2021 15:18:26 +0300 Subject: [PATCH 06/12] Add test for making QR code url --- internal/utils/make_qr_code_url.go | 2 +- internal/utils/make_qr_code_url_test.go | 31 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 internal/utils/make_qr_code_url_test.go diff --git a/internal/utils/make_qr_code_url.go b/internal/utils/make_qr_code_url.go index 752986fce..fd140fbfe 100644 --- a/internal/utils/make_qr_code_url.go +++ b/internal/utils/make_qr_code_url.go @@ -11,7 +11,7 @@ func MakeQRCodeURL(data string) string { rv := url.URL{ Scheme: "https", Host: "api.qrserver.com", - Path: "v1/create-qr-code", + Path: "/v1/create-qr-code", RawQuery: values.Encode(), } diff --git a/internal/utils/make_qr_code_url_test.go b/internal/utils/make_qr_code_url_test.go new file mode 100644 index 000000000..cea1f91cc --- /dev/null +++ b/internal/utils/make_qr_code_url_test.go @@ -0,0 +1,31 @@ +package utils_test + +import ( + "net/url" + "strings" + "testing" + + "github.com/9seconds/mtg/v2/internal/utils" + "github.com/stretchr/testify/suite" +) + +type MakeQRCodeURLTestSuite struct { + suite.Suite +} + +func (suite *MakeQRCodeURLTestSuite) TestSomeData() { + value := utils.MakeQRCodeURL("some data") + + parsed, err := url.Parse(value) + suite.NoError(err) + + suite.Equal("some data", parsed.Query().Get("data")) + suite.Equal("svg", parsed.Query().Get("format")) + suite.Equal("api.qrserver.com", strings.TrimPrefix(parsed.Host, "www.")) + suite.Equal("v1/create-qr-code", strings.Trim(parsed.Path, "/")) +} + +func TestMakeQRCodeURL(t *testing.T) { + t.Parallel() + suite.Run(t, &MakeQRCodeURLTestSuite{}) +} From c85c88efd67acd0689872c3d706c9b8b944c582a Mon Sep 17 00:00:00 2001 From: 9seconds Date: Fri, 30 Jul 2021 16:14:58 +0300 Subject: [PATCH 07/12] Add simple-run command --- internal/cli/access.go | 24 ++++----- internal/cli/cli.go | 3 +- internal/cli/generate_secret.go | 4 +- internal/cli/{proxy.go => run.go} | 6 +-- internal/cli/simple_run.go | 84 +++++++++++++++++++++++++++++++ mtglib/secret.go | 5 +- 6 files changed, 107 insertions(+), 19 deletions(-) rename internal/cli/{proxy.go => run.go} (73%) create mode 100644 internal/cli/simple_run.go diff --git a/internal/cli/access.go b/internal/cli/access.go index 66282690c..23a47ce3e 100644 --- a/internal/cli/access.go +++ b/internal/cli/access.go @@ -44,8 +44,8 @@ type Access struct { Hex bool `kong:"help='Print secret in hex encoding.',short='x'"` } -func (c *Access) Run(cli *CLI, version string) error { - conf, err := utils.ReadConfig(c.ConfigPath) +func (a *Access) Run(cli *CLI, version string) error { + conf, err := utils.ReadConfig(a.ConfigPath) if err != nil { return fmt.Errorf("cannot init config: %w", err) } @@ -65,31 +65,31 @@ func (c *Access) Run(cli *CLI, version string) error { go func() { defer wg.Done() - ip := c.PublicIPv4 + ip := a.PublicIPv4 if ip == nil { - ip = c.getIP(ntw, "tcp4") + ip = a.getIP(ntw, "tcp4") } if ip != nil { ip = ip.To4() } - resp.IPv4 = c.makeURLs(conf, ip) + resp.IPv4 = a.makeURLs(conf, ip) }() go func() { defer wg.Done() - ip := c.PublicIPv6 + ip := a.PublicIPv6 if ip == nil { - ip = c.getIP(ntw, "tcp6") + ip = a.getIP(ntw, "tcp6") } if ip != nil { ip = ip.To16() } - resp.IPv6 = c.makeURLs(conf, ip) + resp.IPv6 = a.makeURLs(conf, ip) }() wg.Wait() @@ -105,7 +105,7 @@ func (c *Access) Run(cli *CLI, version string) error { return nil } -func (c *Access) getIP(ntw mtglib.Network, protocol string) net.IP { +func (a *Access) getIP(ntw mtglib.Network, protocol string) net.IP { client := ntw.MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error) { return ntw.DialContext(ctx, protocol, address) // nolint: wrapcheck }) @@ -139,12 +139,12 @@ func (c *Access) getIP(ntw mtglib.Network, protocol string) net.IP { return net.ParseIP(strings.TrimSpace(string(data))) } -func (c *Access) makeURLs(conf *config.Config, ip net.IP) *accessResponseURLs { +func (a *Access) makeURLs(conf *config.Config, ip net.IP) *accessResponseURLs { if ip == nil { return nil } - portNo := c.Port + portNo := a.Port if portNo == 0 { portNo = conf.BindTo.Port } @@ -153,7 +153,7 @@ func (c *Access) makeURLs(conf *config.Config, ip net.IP) *accessResponseURLs { values.Set("server", ip.String()) values.Set("port", strconv.Itoa(int(portNo))) - if c.Hex { + if a.Hex { values.Set("secret", conf.Secret.Hex()) } else { values.Set("secret", conf.Secret.Base64()) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index d2d813cec..f287ad8ff 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -5,6 +5,7 @@ import "github.com/alecthomas/kong" type CLI struct { GenerateSecret GenerateSecret `kong:"cmd,help='Generate new proxy secret'"` Access Access `kong:"cmd,help='Print access information.'"` - Run Proxy `kong:"cmd,help='Run proxy.'"` + Run Run `kong:"cmd,help='Run proxy.'"` + SimpleRun SimpleRun `kong:"cmd,help='Run proxy without config file.'"` Version kong.VersionFlag `kong:"help='Print version.',short='v'"` } diff --git a/internal/cli/generate_secret.go b/internal/cli/generate_secret.go index e3fb0e82d..17b3b0985 100644 --- a/internal/cli/generate_secret.go +++ b/internal/cli/generate_secret.go @@ -11,10 +11,10 @@ type GenerateSecret struct { Hex bool `kong:"help='Print secret in hex encoding.',short='x'"` } -func (c *GenerateSecret) Run(cli *CLI, _ string) error { +func (g *GenerateSecret) Run(cli *CLI, _ string) error { secret := mtglib.GenerateSecret(cli.GenerateSecret.HostName) - if cli.GenerateSecret.Hex { + if g.Hex { fmt.Println(secret.Hex()) // nolint: forbidigo } else { fmt.Println(secret.Base64()) // nolint: forbidigo diff --git a/internal/cli/proxy.go b/internal/cli/run.go similarity index 73% rename from internal/cli/proxy.go rename to internal/cli/run.go index 199018549..644f34931 100644 --- a/internal/cli/proxy.go +++ b/internal/cli/run.go @@ -6,12 +6,12 @@ import ( "github.com/9seconds/mtg/v2/internal/utils" ) -type Proxy struct { +type Run struct { ConfigPath string `kong:"arg,required,type='existingfile',help='Path to the configuration file.',name='config-path'"` // nolint: lll } -func (c *Proxy) Run(cli *CLI, version string) error { - conf, err := utils.ReadConfig(c.ConfigPath) +func (r *Run) Run(cli *CLI, version string) error { + conf, err := utils.ReadConfig(r.ConfigPath) if err != nil { return fmt.Errorf("cannot init config: %w", err) } diff --git a/internal/cli/simple_run.go b/internal/cli/simple_run.go new file mode 100644 index 000000000..167b9c4a5 --- /dev/null +++ b/internal/cli/simple_run.go @@ -0,0 +1,84 @@ +package cli + +import ( + "fmt" + "net" + "strconv" + "time" + + "github.com/9seconds/mtg/v2/internal/config" +) + +type SimpleRun struct { + BindTo string `kong:"arg,required,name='bind-to',help='A host:port to bind proxy to.'"` + Secret string `kong:"arg,required,name='secret',help='Proxy secret.'"` + + Debug bool `kong:"name='debug',short='d',help='Run in debug mode.'"` + Concurrency uint64 `kong:"name='concurrency',short='c',default='8192',help='Max number of concurrent connection to proxy.'"` + TCPBuffer string `kong:"name='tcp-buffer',short='b',default='4KB',help='Size of TCP buffer to use.'"` + PreferIP string `kong:"name='prefer-ip',short='i',default='prefer-ipv6',help='IP preference. By default we prefer IPv6 with fallback to IPv4.'"` + DomainFrontingPort uint64 `kong:"name='domain-fronting-port',short='p',default='443',help='A port to access for domain fronting.'"` + DOHIP net.IP `kong:"name='doh-ip',short='d',default='9.9.9.9',help='IP address of DNS-over-HTTP to use.'"` + Timeout time.Duration `kong:"name='timeout',short='t',default='10s',help='Network timeout to use'"` + AntiReplayCacheSize string `kong:"name='antireplay-cache-size',short='a',default='1MB',help='A size of anti-replay cache to use.'"` +} + +func (s *SimpleRun) Run(cli *CLI, version string) error { + conf := &config.Config{} + + if err := conf.BindTo.Set(s.BindTo); err != nil { + return fmt.Errorf("incorrect bind-to parameter: %w", err) + } + + if err := conf.Secret.Set(s.Secret); err != nil { + return fmt.Errorf("incorrect secret: %w", err) + } + + if err := conf.Concurrency.Set(strconv.FormatUint(s.Concurrency, 10)); err != nil { + return fmt.Errorf("incorrect concurrency: %w", err) + } + + if err := conf.TCPBuffer.Set(s.TCPBuffer); err != nil { + return fmt.Errorf("incorrect tcp-buffer: %w", err) + } + + if err := conf.PreferIP.Set(s.PreferIP); err != nil { + return fmt.Errorf("incorrect prefer-ip: %w", err) + } + + if err := conf.DomainFrontingPort.Set(strconv.FormatUint(s.DomainFrontingPort, 10)); err != nil { + return fmt.Errorf("incorrect domain-fronting-port: %w", err) + } + + if err := conf.Network.DOHIP.Set(s.DOHIP.String()); err != nil { + return fmt.Errorf("incorrect doh-ip: %w", err) + } + + if err := conf.Network.Timeout.TCP.Set(s.Timeout.String()); err != nil { + return fmt.Errorf("incorrect timeout: %w", err) + } + + if err := conf.Network.Timeout.HTTP.Set(s.Timeout.String()); err != nil { + return fmt.Errorf("incorrect timeout: %w", err) + } + + if err := conf.Network.Timeout.Idle.Set(s.Timeout.String()); err != nil { + return fmt.Errorf("incorrect timeout: %w", err) + } + + if err := conf.Defense.AntiReplay.MaxSize.Set(s.AntiReplayCacheSize); err != nil { + return fmt.Errorf("incorrect antireplay-cache-size: %w", err) + } + + conf.Debug.Value = s.Debug + conf.Defense.AntiReplay.Enabled.Value = true + conf.Defense.Blocklist.Enabled.Value = false + conf.Stats.StatsD.Enabled.Value = false + conf.Stats.Prometheus.Enabled.Value = false + + if err := conf.Validate(); err != nil { + return fmt.Errorf("invalid result configuration: %w", err) + } + + return runProxy(conf, version) +} diff --git a/mtglib/secret.go b/mtglib/secret.go index 49fd106a8..eced18146 100644 --- a/mtglib/secret.go +++ b/mtglib/secret.go @@ -58,7 +58,10 @@ func (s Secret) MarshalText() ([]byte, error) { // UnmarshalText is to support text.Unmarshaller interface. func (s *Secret) UnmarshalText(data []byte) error { - text := string(data) + return s.Set(string(data)) +} + +func (s *Secret) Set(text string) error { if text == "" { return ErrSecretEmpty } From 1050ca0b97af9c2ae7d59be79d3826c8bb623e90 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Fri, 30 Jul 2021 16:27:28 +0300 Subject: [PATCH 08/12] Fix lint issues --- internal/cli/run.go | 2 +- internal/cli/run_proxy.go | 1 + internal/cli/simple_run.go | 18 +++++++++--------- internal/config/config.go | 2 +- internal/config/config_test.go | 2 +- internal/config/type_blocklist_uri.go | 12 ++++++------ internal/config/type_blocklist_uri_test.go | 6 +++--- internal/config/type_bytes.go | 2 +- internal/config/type_bytes_test.go | 18 +++++++++--------- internal/config/type_concurrency.go | 4 ++-- internal/config/type_duration_test.go | 4 ++-- internal/config/type_error_rate.go | 4 ++-- internal/config/type_error_rate_test.go | 4 ++-- internal/config/type_ip.go | 2 +- internal/config/type_metric_prefix.go | 4 ++-- internal/config/type_port.go | 6 +++--- internal/config/type_prefer_ip.go | 2 +- internal/config/type_proxy_url.go | 2 +- internal/config/type_statsd_tag_format.go | 2 +- mtglib/proxy_opts.go | 18 +++++++++--------- 20 files changed, 58 insertions(+), 57 deletions(-) diff --git a/internal/cli/run.go b/internal/cli/run.go index 644f34931..390f30ab8 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -16,5 +16,5 @@ func (r *Run) Run(cli *CLI, version string) error { return fmt.Errorf("cannot init config: %w", err) } - return runProxy(conf, version) + return runProxy(conf, version) } diff --git a/internal/cli/run_proxy.go b/internal/cli/run_proxy.go index 4716bc1aa..745319fa4 100644 --- a/internal/cli/run_proxy.go +++ b/internal/cli/run_proxy.go @@ -51,6 +51,7 @@ func makeNetwork(conf *config.Config, version string) (mtglib.Network, error) { } proxyURLs := make([]*url.URL, 0, len(conf.Network.Proxies)) + for _, v := range conf.Network.Proxies { if value := v.Get(nil); value != nil { proxyURLs = append(proxyURLs, value) diff --git a/internal/cli/simple_run.go b/internal/cli/simple_run.go index 167b9c4a5..4d1726a1f 100644 --- a/internal/cli/simple_run.go +++ b/internal/cli/simple_run.go @@ -13,17 +13,17 @@ type SimpleRun struct { BindTo string `kong:"arg,required,name='bind-to',help='A host:port to bind proxy to.'"` Secret string `kong:"arg,required,name='secret',help='Proxy secret.'"` - Debug bool `kong:"name='debug',short='d',help='Run in debug mode.'"` - Concurrency uint64 `kong:"name='concurrency',short='c',default='8192',help='Max number of concurrent connection to proxy.'"` - TCPBuffer string `kong:"name='tcp-buffer',short='b',default='4KB',help='Size of TCP buffer to use.'"` - PreferIP string `kong:"name='prefer-ip',short='i',default='prefer-ipv6',help='IP preference. By default we prefer IPv6 with fallback to IPv4.'"` - DomainFrontingPort uint64 `kong:"name='domain-fronting-port',short='p',default='443',help='A port to access for domain fronting.'"` - DOHIP net.IP `kong:"name='doh-ip',short='d',default='9.9.9.9',help='IP address of DNS-over-HTTP to use.'"` - Timeout time.Duration `kong:"name='timeout',short='t',default='10s',help='Network timeout to use'"` - AntiReplayCacheSize string `kong:"name='antireplay-cache-size',short='a',default='1MB',help='A size of anti-replay cache to use.'"` + Debug bool `kong:"name='debug',short='d',help='Run in debug mode.'"` // nolint: lll + Concurrency uint64 `kong:"name='concurrency',short='c',default='8192',help='Max number of concurrent connection to proxy.'"` // nolint: lll + TCPBuffer string `kong:"name='tcp-buffer',short='b',default='4KB',help='Size of TCP buffer to use.'"` // nolint: lll + PreferIP string `kong:"name='prefer-ip',short='i',default='prefer-ipv6',help='IP preference. By default we prefer IPv6 with fallback to IPv4.'"` // nolint: lll + DomainFrontingPort uint64 `kong:"name='domain-fronting-port',short='p',default='443',help='A port to access for domain fronting.'"` // nolint: lll + DOHIP net.IP `kong:"name='doh-ip',short='n',default='9.9.9.9',help='IP address of DNS-over-HTTP to use.'"` // nolint: lll + Timeout time.Duration `kong:"name='timeout',short='t',default='10s',help='Network timeout to use'"` // nolint: lll + AntiReplayCacheSize string `kong:"name='antireplay-cache-size',short='a',default='1MB',help='A size of anti-replay cache to use.'"` // nolint: lll } -func (s *SimpleRun) Run(cli *CLI, version string) error { +func (s *SimpleRun) Run(cli *CLI, version string) error { // nolint: cyclop conf := &config.Config{} if err := conf.BindTo.Set(s.BindTo); err != nil { diff --git a/internal/config/config.go b/internal/config/config.go index c05d33f6f..623075882 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -60,7 +60,7 @@ func (c *Config) Validate() error { return fmt.Errorf("invalid secret %s", c.Secret.String()) } - if c.BindTo.Get("") == "" { + if c.BindTo.Get("") == "" { return fmt.Errorf("incorrect bind-to parameter %s", c.BindTo.String()) } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index f191221bb..e6b19e060 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -10,7 +10,7 @@ import ( ) type ConfigTestSuite struct { - suite.Suite + suite.Suite } func (suite *ConfigTestSuite) ReadConfig(filename string) []byte { diff --git a/internal/config/type_blocklist_uri.go b/internal/config/type_blocklist_uri.go index 8bd36778e..620ea51fd 100644 --- a/internal/config/type_blocklist_uri.go +++ b/internal/config/type_blocklist_uri.go @@ -13,12 +13,12 @@ type TypeBlocklistURI struct { func (t *TypeBlocklistURI) Set(value string) error { if stat, err := os.Stat(value); err == nil || os.IsExist(err) { - switch { - case stat.IsDir(): - return fmt.Errorf("value is correct filepath but directory") - case stat.Mode().Perm() & 0o400 == 0: - return fmt.Errorf("value is correct filepath but not readable") - } + switch { + case stat.IsDir(): + return fmt.Errorf("value is correct filepath but directory") + case stat.Mode().Perm()&0o400 == 0: + return fmt.Errorf("value is correct filepath but not readable") + } value, err = filepath.Abs(value) if err != nil { diff --git a/internal/config/type_blocklist_uri_test.go b/internal/config/type_blocklist_uri_test.go index 407bedafd..3cf76bce4 100644 --- a/internal/config/type_blocklist_uri_test.go +++ b/internal/config/type_blocklist_uri_test.go @@ -102,9 +102,9 @@ func (suite *TypeBlocklistURITestSuite) TestGet() { value := config.TypeBlocklistURI{} suite.Equal("/path", value.Get("/path")) - suite.NoError(value.Set("http://lalala.ru")) - suite.Equal("http://lalala.ru", value.Get("/path")) - suite.Equal("http://lalala.ru", value.Get("")) + suite.NoError(value.Set("http://lalala.ru")) + suite.Equal("http://lalala.ru", value.Get("/path")) + suite.Equal("http://lalala.ru", value.Get("")) } func TestTypeBlocklistURI(t *testing.T) { diff --git a/internal/config/type_bytes.go b/internal/config/type_bytes.go index 412f0197a..254d556a2 100644 --- a/internal/config/type_bytes.go +++ b/internal/config/type_bytes.go @@ -14,7 +14,7 @@ type TypeBytes struct { } func (t *TypeBytes) Set(value string) error { - normalizedValue := typeBytesStringCleaner.Replace(strings.ToUpper(value)) + normalizedValue := typeBytesStringCleaner.Replace(strings.ToUpper(value)) parsedValue, err := units.ParseBase2Bytes(normalizedValue) if err != nil { diff --git a/internal/config/type_bytes_test.go b/internal/config/type_bytes_test.go index 08874f43f..bee09228a 100644 --- a/internal/config/type_bytes_test.go +++ b/internal/config/type_bytes_test.go @@ -64,20 +64,20 @@ func (suite *TypeBytesTestSuite) TestUnmarshalOk() { } func (suite *TypeBytesTestSuite) TestMarshalOk() { - value := typeBytesTestStruct{} - suite.NoError(value.Value.Set("1kib")) + value := typeBytesTestStruct{} + suite.NoError(value.Value.Set("1kib")) - data, err := json.Marshal(value) - suite.NoError(err) - suite.JSONEq(`{"value": "1kib"}`, string(data)) + data, err := json.Marshal(value) + suite.NoError(err) + suite.JSONEq(`{"value": "1kib"}`, string(data)) } func (suite *TypeBytesTestSuite) TestGet() { - value := config.TypeBytes{} - suite.EqualValues(1000, value.Get(1000)) + value := config.TypeBytes{} + suite.EqualValues(1000, value.Get(1000)) - suite.NoError(value.Set("1mib")) - suite.EqualValues(1048576, value.Get(1000)) + suite.NoError(value.Set("1mib")) + suite.EqualValues(1048576, value.Get(1000)) } func TestTypeBytes(t *testing.T) { diff --git a/internal/config/type_concurrency.go b/internal/config/type_concurrency.go index 1c172d71b..4f39b3b8a 100644 --- a/internal/config/type_concurrency.go +++ b/internal/config/type_concurrency.go @@ -12,11 +12,11 @@ type TypeConcurrency struct { func (t *TypeConcurrency) Set(value string) error { concurrencyValue, err := strconv.ParseUint(value, 10, 64) if err != nil { - return fmt.Errorf("Value is not uint (%s): %w", value, err) + return fmt.Errorf("value is not uint (%s): %w", value, err) } if concurrencyValue == 0 { - return fmt.Errorf("Value should be >0 (%s)", value) + return fmt.Errorf("value should be >0 (%s)", value) } t.Value = uint(concurrencyValue) diff --git a/internal/config/type_duration_test.go b/internal/config/type_duration_test.go index 49d674aa9..5d0e204b2 100644 --- a/internal/config/type_duration_test.go +++ b/internal/config/type_duration_test.go @@ -86,12 +86,12 @@ func (suite *TypeDurationTestSuite) TestMarshalOk() { data, err := json.Marshal(testStruct) assert.NoError(t, err) - expectedJson, err := json.Marshal(map[string]string{ + expectedJSON, err := json.Marshal(map[string]string{ "value": expected, }) assert.NoError(t, err) - assert.JSONEq(t, string(expectedJson), string(data)) + assert.JSONEq(t, string(expectedJSON), string(data)) }) } } diff --git a/internal/config/type_error_rate.go b/internal/config/type_error_rate.go index e950424d7..4e30e098a 100644 --- a/internal/config/type_error_rate.go +++ b/internal/config/type_error_rate.go @@ -14,11 +14,11 @@ type TypeErrorRate struct { func (t *TypeErrorRate) Set(value string) error { parsedValue, err := strconv.ParseFloat(value, 64) if err != nil { - return fmt.Errorf("Value is not a float (%s): %w", value, err) + return fmt.Errorf("value is not a float (%s): %w", value, err) } if parsedValue <= 0.0 || parsedValue >= 100.0 { - return fmt.Errorf("Value should be 0 < x < 100 (%s)", value) + return fmt.Errorf("value should be 0 < x < 100 (%s)", value) } t.Value = parsedValue diff --git a/internal/config/type_error_rate_test.go b/internal/config/type_error_rate_test.go index c45ffa77d..44d17c279 100644 --- a/internal/config/type_error_rate_test.go +++ b/internal/config/type_error_rate_test.go @@ -62,9 +62,9 @@ func (suite *TypeErrorRateTestSuite) TestMarshalOk() { }, } - encodedJson, err := json.Marshal(testStruct) + encodedJSON, err := json.Marshal(testStruct) suite.NoError(err) - suite.JSONEq(`{"value": 1.01}`, string(encodedJson)) + suite.JSONEq(`{"value": 1.01}`, string(encodedJSON)) } func (suite *TypeErrorRateTestSuite) TestGet() { diff --git a/internal/config/type_ip.go b/internal/config/type_ip.go index 03a2f20c3..c6371815a 100644 --- a/internal/config/type_ip.go +++ b/internal/config/type_ip.go @@ -15,7 +15,7 @@ func (t *TypeIP) Set(value string) error { return fmt.Errorf("incorret ip %s", value) } - t.Value = ip + t.Value = ip return nil } diff --git a/internal/config/type_metric_prefix.go b/internal/config/type_metric_prefix.go index fcd951ea8..e55438c0a 100644 --- a/internal/config/type_metric_prefix.go +++ b/internal/config/type_metric_prefix.go @@ -11,10 +11,10 @@ type TypeMetricPrefix struct { func (t *TypeMetricPrefix) Set(value string) error { if ok, err := regexp.MatchString("^[a-z0-9]+$", value); !ok || err != nil { - return fmt.Errorf("incorrect metric prefix %s: %w", value, err) + return fmt.Errorf("incorrect metric prefix %s: %w", value, err) } - t.Value = value + t.Value = value return nil } diff --git a/internal/config/type_port.go b/internal/config/type_port.go index f53d77e5b..cdab9ee5c 100644 --- a/internal/config/type_port.go +++ b/internal/config/type_port.go @@ -6,7 +6,7 @@ import ( ) type TypePort struct { - Value uint16 + Value uint } func (t *TypePort) Set(value string) error { @@ -19,12 +19,12 @@ func (t *TypePort) Set(value string) error { return fmt.Errorf("incorrect port number (%s)", value) } - t.Value = uint16(portValue) + t.Value = uint(portValue) return nil } -func (t TypePort) Get(defaultValue uint16) uint16 { +func (t TypePort) Get(defaultValue uint) uint { if t.Value == 0 { return defaultValue } diff --git a/internal/config/type_prefer_ip.go b/internal/config/type_prefer_ip.go index b28a33f29..8a07721b5 100644 --- a/internal/config/type_prefer_ip.go +++ b/internal/config/type_prefer_ip.go @@ -33,7 +33,7 @@ func (t *TypePreferIP) Set(value string) error { switch value { case TypePreferIPPreferIPv4, TypePreferIPPreferIPv6, TypePreferOnlyIPv4, TypePreferOnlyIPv6: - t.Value = value + t.Value = value return nil default: diff --git a/internal/config/type_proxy_url.go b/internal/config/type_proxy_url.go index f27d0af92..f3d0231ae 100644 --- a/internal/config/type_proxy_url.go +++ b/internal/config/type_proxy_url.go @@ -15,7 +15,7 @@ type TypeProxyURL struct { func (t *TypeProxyURL) Set(value string) error { parsedURL, err := url.Parse(value) if err != nil { - return fmt.Errorf("Value is not corect URL (%s): %w", value, err) + return fmt.Errorf("value is not corect URL (%s): %w", value, err) } if parsedURL.Host == "" { diff --git a/internal/config/type_statsd_tag_format.go b/internal/config/type_statsd_tag_format.go index e449b9ff5..530190389 100644 --- a/internal/config/type_statsd_tag_format.go +++ b/internal/config/type_statsd_tag_format.go @@ -29,7 +29,7 @@ func (t *TypeStatsdTagFormat) Set(value string) error { switch lowercasedValue { case TypeStatsdTagFormatDatadog, TypeStatsdTagFormatInfluxdb, TypeStatsdTagFormatGraphite: - t.Value = lowercasedValue + t.Value = lowercasedValue return nil default: diff --git a/mtglib/proxy_opts.go b/mtglib/proxy_opts.go index d74728626..2eac74ecb 100644 --- a/mtglib/proxy_opts.go +++ b/mtglib/proxy_opts.go @@ -55,15 +55,6 @@ type ProxyOpts struct { // This is an optional setting. Concurrency uint - // DomainFrontingPort is a port we use to connect to a fronting - // domain. - // - // This is required because secret does not specify a port. It - // specifies a hostname only. - // - // This is an optional setting. - DomainFrontingPort uint16 - // IdleTimeout is a timeout for relay when we have to break a // stream. // @@ -90,6 +81,15 @@ type ProxyOpts struct { // This is an optional setting. PreferIP string + // DomainFrontingPort is a port we use to connect to a fronting + // domain. + // + // This is required because secret does not specify a port. It + // specifies a hostname only. + // + // This is an optional setting. + DomainFrontingPort uint + // UseTestDCs defines if we have to connect to production or to staging // DCs of Telegram. // From dcbbb4960790c15d47585cf85efc360a0a18814d Mon Sep 17 00:00:00 2001 From: 9seconds Date: Fri, 30 Jul 2021 16:34:09 +0300 Subject: [PATCH 09/12] Update README --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.md b/README.md index bcc583489..f271d61b6 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,50 @@ For example, you've bought a VPS from [Digital Ocean](https://www.digitalocean.com/). Then it might be a good idea to generate a secret for _digitalocean.com_ then. + +### Simple run mode + +mtg supports 2 modes: simple and normal. Simple mode allows starting +proxy with a small subset of configuration options you usually want to +modify. This is quite good for oneliners that you can copy-paste and do +not bother about external files whatsoever. + +Let's take a look: + +```console +Usage: mtg simple-run + +Run proxy without config file. + +Arguments: + A host:port to bind proxy to. + Proxy secret. + +Flags: + -h, --help Show context-sensitive help. + -v, --version Print version. + + -d, --debug Run in debug mode. + -c, --concurrency=8192 Max number of concurrent connection to proxy. + -b, --tcp-buffer="4KB" Size of TCP buffer to use. + -i, --prefer-ip="prefer-ipv6" IP preference. By default we prefer IPv6 with fallback to IPv4. + -p, --domain-fronting-port=443 A port to access for domain fronting. + -n, --doh-ip=9.9.9.9 IP address of DNS-over-HTTP to use. + -t, --timeout=10s Network timeout to use + -a, --antireplay-cache-size="1MB" A size of anti-replay cache to use. +``` + +So, if you want to startup a proxy with CLI only, you can do something like + +```console +$ mtg simple-run -n 1.1.1.1 -t 30s -a 512kib 127.0.0.1:3128 7hBO-dCS4EBzenlKbdLFxyNnb29nbGUuY29t +``` + +The rest of the configuration will be taken from default values. But +a simple run is fine if you do not have any special requirements or +granular tuning. If you want it, please checkout the configuration +files. + ### Prepare a configuration file Please checkout an example configuration file. All options except of From dc99dd165e3a81d9bd36fc24c43ee4d0d62f1521 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Sat, 31 Jul 2021 11:29:30 +0300 Subject: [PATCH 10/12] Update golangci-lint to 1.41.1 --- .github/workflows/ci.yaml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3590ed397..7a12ddb17 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -70,7 +70,7 @@ jobs: - name: Run linter uses: golangci/golangci-lint-action@v2 with: - version: v1.40.1 + version: v1.41.1 docker: name: Docker diff --git a/Makefile b/Makefile index 145596186..137c11196 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) IMAGE_NAME := mtg APP_NAME := $(IMAGE_NAME) -GOLANGCI_LINT_VERSION := v1.40.1 +GOLANGCI_LINT_VERSION := v1.41.1 VERSION_GO := $(shell go version) VERSION_DATE := $(shell date -Ru) From eaa757b6d0eee218f3c4d4c5013c27583eab8006 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Sat, 31 Jul 2021 11:35:05 +0300 Subject: [PATCH 11/12] Update code to the latest golangci-lint --- internal/cli/run_proxy.go | 2 +- internal/cli/simple_run.go | 7 ++----- internal/config/type_concurrency.go | 4 ++-- internal/config/type_error_rate.go | 4 ++-- internal/config/type_hostport.go | 2 +- internal/config/type_port.go | 2 +- ipblocklist/firehol.go | 4 ++-- network/proxy_dialer.go | 2 +- stats/prometheus.go | 5 ++--- 9 files changed, 14 insertions(+), 18 deletions(-) diff --git a/internal/cli/run_proxy.go b/internal/cli/run_proxy.go index 745319fa4..95507e4bf 100644 --- a/internal/cli/run_proxy.go +++ b/internal/cli/run_proxy.go @@ -115,7 +115,7 @@ func makeIPBlocklist(conf *config.Config, logger mtglib.Logger, ntw mtglib.Netwo } func makeEventStream(conf *config.Config, logger mtglib.Logger) (mtglib.EventStream, error) { - factories := make([]events.ObserverFactory, 0, 2) + factories := make([]events.ObserverFactory, 0, 2) // nolint: gomnd if conf.Stats.StatsD.Enabled.Get(false) { statsdFactory, err := stats.NewStatsd( diff --git a/internal/cli/simple_run.go b/internal/cli/simple_run.go index 4d1726a1f..5a4bb0e62 100644 --- a/internal/cli/simple_run.go +++ b/internal/cli/simple_run.go @@ -34,7 +34,7 @@ func (s *SimpleRun) Run(cli *CLI, version string) error { // nolint: cyclop return fmt.Errorf("incorrect secret: %w", err) } - if err := conf.Concurrency.Set(strconv.FormatUint(s.Concurrency, 10)); err != nil { + if err := conf.Concurrency.Set(strconv.FormatUint(s.Concurrency, 10)); err != nil { // nolint: gomnd return fmt.Errorf("incorrect concurrency: %w", err) } @@ -46,7 +46,7 @@ func (s *SimpleRun) Run(cli *CLI, version string) error { // nolint: cyclop return fmt.Errorf("incorrect prefer-ip: %w", err) } - if err := conf.DomainFrontingPort.Set(strconv.FormatUint(s.DomainFrontingPort, 10)); err != nil { + if err := conf.DomainFrontingPort.Set(strconv.FormatUint(s.DomainFrontingPort, 10)); err != nil { // nolint: gomnd return fmt.Errorf("incorrect domain-fronting-port: %w", err) } @@ -72,9 +72,6 @@ func (s *SimpleRun) Run(cli *CLI, version string) error { // nolint: cyclop conf.Debug.Value = s.Debug conf.Defense.AntiReplay.Enabled.Value = true - conf.Defense.Blocklist.Enabled.Value = false - conf.Stats.StatsD.Enabled.Value = false - conf.Stats.Prometheus.Enabled.Value = false if err := conf.Validate(); err != nil { return fmt.Errorf("invalid result configuration: %w", err) diff --git a/internal/config/type_concurrency.go b/internal/config/type_concurrency.go index 4f39b3b8a..e2ccd6734 100644 --- a/internal/config/type_concurrency.go +++ b/internal/config/type_concurrency.go @@ -10,7 +10,7 @@ type TypeConcurrency struct { } func (t *TypeConcurrency) Set(value string) error { - concurrencyValue, err := strconv.ParseUint(value, 10, 64) + concurrencyValue, err := strconv.ParseUint(value, 10, 16) // nolint: gomnd if err != nil { return fmt.Errorf("value is not uint (%s): %w", value, err) } @@ -41,5 +41,5 @@ func (t TypeConcurrency) MarshalJSON() ([]byte, error) { } func (t TypeConcurrency) String() string { - return strconv.FormatUint(uint64(t.Value), 10) + return strconv.FormatUint(uint64(t.Value), 10) // nolint: gomnd } diff --git a/internal/config/type_error_rate.go b/internal/config/type_error_rate.go index 4e30e098a..92465ded1 100644 --- a/internal/config/type_error_rate.go +++ b/internal/config/type_error_rate.go @@ -12,7 +12,7 @@ type TypeErrorRate struct { } func (t *TypeErrorRate) Set(value string) error { - parsedValue, err := strconv.ParseFloat(value, 64) + parsedValue, err := strconv.ParseFloat(value, 64) // nolint: gomnd if err != nil { return fmt.Errorf("value is not a float (%s): %w", value, err) } @@ -43,5 +43,5 @@ func (t TypeErrorRate) MarshalJSON() ([]byte, error) { } func (t TypeErrorRate) String() string { - return strconv.FormatFloat(t.Value, 'f', -1, 64) + return strconv.FormatFloat(t.Value, 'f', -1, 64) // nolint: gomnd } diff --git a/internal/config/type_hostport.go b/internal/config/type_hostport.go index 665ba1015..8dcb254a8 100644 --- a/internal/config/type_hostport.go +++ b/internal/config/type_hostport.go @@ -18,7 +18,7 @@ func (t *TypeHostPort) Set(value string) error { return fmt.Errorf("incorrect host:port value (%v): %w", value, err) } - portValue, err := strconv.ParseUint(port, 10, 16) + portValue, err := strconv.ParseUint(port, 10, 16) // nolint: gomnd if err != nil { return fmt.Errorf("incorrect port number (%v): %w", value, err) } diff --git a/internal/config/type_port.go b/internal/config/type_port.go index cdab9ee5c..3965fb7a9 100644 --- a/internal/config/type_port.go +++ b/internal/config/type_port.go @@ -10,7 +10,7 @@ type TypePort struct { } func (t *TypePort) Set(value string) error { - portValue, err := strconv.ParseUint(value, 10, 16) + portValue, err := strconv.ParseUint(value, 10, 16) // nolint: gomnd if err != nil { return fmt.Errorf("incorrect port number (%v): %w", value, err) } diff --git a/ipblocklist/firehol.go b/ipblocklist/firehol.go index 953aebaa8..52f7a91f8 100644 --- a/ipblocklist/firehol.go +++ b/ipblocklist/firehol.go @@ -119,7 +119,7 @@ func (f *Firehol) Run(updateEach time.Duration) { } func (f *Firehol) containsIPv4(addr net.IP) bool { - ip := patricia.NewIPv4AddressFromBytes(addr, 32) + ip := patricia.NewIPv4AddressFromBytes(addr, 32) // nolint: gomnd if ok, _, err := f.treeV4.FindDeepestTag(ip); ok && err == nil { return true @@ -129,7 +129,7 @@ func (f *Firehol) containsIPv4(addr net.IP) bool { } func (f *Firehol) containsIPv6(addr net.IP) bool { - ip := patricia.NewIPv6Address(addr, 128) + ip := patricia.NewIPv6Address(addr, 128) // nolint: gomnd if ok, _, err := f.treeV6.FindDeepestTag(ip); ok && err == nil { return true diff --git a/network/proxy_dialer.go b/network/proxy_dialer.go index 9c3f0f7cd..941949979 100644 --- a/network/proxy_dialer.go +++ b/network/proxy_dialer.go @@ -16,7 +16,7 @@ func newProxyDialer(baseDialer Dialer, proxyURL *url.URL) Dialer { ) if param := params.Get("open_threshold"); param != "" { - if intNum, err := strconv.ParseUint(param, 10, 32); err == nil { + if intNum, err := strconv.ParseUint(param, 10, 32); err == nil { // nolint: gomnd openThreshold = uint32(intNum) } } diff --git a/stats/prometheus.go b/stats/prometheus.go index 7a2277748..97c6e973a 100644 --- a/stats/prometheus.go +++ b/stats/prometheus.go @@ -119,11 +119,10 @@ func (p prometheusProcessor) EventReplayAttack(_ mtglib.EventReplayAttack) { } func (p prometheusProcessor) Shutdown() { - for _, v := range p.streams { + for k, v := range p.streams { releaseStreamInfo(v) + delete(p.streams, k) } - - p.streams = make(map[string]*streamInfo) } // PrometheusFactory is a factory of events.Observers which collect From 6cd91600b5b960f9196e19b9c92f29fae3a38211 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Sat, 31 Jul 2021 11:44:58 +0300 Subject: [PATCH 12/12] Update dependencies --- go.mod | 21 +-- go.sum | 510 +++++++++++++++++++++++++++++++-------------------------- 2 files changed, 285 insertions(+), 246 deletions(-) diff --git a/go.mod b/go.mod index 17569d92f..7bb69b7a7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/OneOfOne/xxhash v1.2.8 - github.com/alecthomas/kong v0.2.16 + github.com/alecthomas/kong v0.2.17 github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 @@ -15,17 +15,18 @@ require ( github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1 github.com/libp2p/go-reuseport v0.0.2 github.com/mccutchen/go-httpbin v1.1.1 - github.com/panjf2000/ants/v2 v2.4.4 - github.com/pelletier/go-toml v1.9.1 - github.com/prometheus/client_golang v1.10.0 - github.com/prometheus/common v0.24.0 // indirect - github.com/rs/zerolog v1.22.0 + github.com/panjf2000/ants/v2 v2.4.6 + github.com/pelletier/go-toml v1.9.3 + github.com/prometheus/client_golang v1.11.0 + github.com/prometheus/common v0.30.0 // indirect + github.com/prometheus/procfs v0.7.1 // indirect + github.com/rs/zerolog v1.23.0 github.com/smira/go-statsd v1.3.2 github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/testify v1.7.0 github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43 - github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a - golang.org/x/net v0.0.0-20210510120150-4163338589ed - golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 + golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c + google.golang.org/protobuf v1.27.1 // indirect ) diff --git a/go.sum b/go.sum index 386a1f133..ff2bf9221 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,44 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/alecthomas/kong v0.2.16 h1:F232CiYSn54Tnl1sJGTeHmx4vJDNLVP2b9yCVMOQwHQ= -github.com/alecthomas/kong v0.2.16/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= +github.com/alecthomas/kong v0.2.17 h1:URDISCI96MIgcIlQyoCAlhOmrSw6pZScBNkctg8r0W0= +github.com/alecthomas/kong v0.2.17/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ= 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= @@ -21,109 +48,100 @@ github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRY github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc= github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +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/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= 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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +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/gotd/getdoc v0.6.2/go.mod h1:6Ul8cyaq+Aw0gVyuU+TKZPCmNjdlylzSIWVxjzI5t/U= github.com/gotd/ige v0.1.5 h1:qITql4hZpqPM/DSeO5IVlxzXJxrs4ZZiKRwPif2K76U= github.com/gotd/ige v0.1.5/go.mod h1:LbvqjUuGELVuHcKjfJZ2NPNzegICQU4eqrPAkXy0nTs= @@ -135,50 +153,23 @@ github.com/gotd/tl v0.4.0/go.mod h1:CMIcjPWFS4qxxJ+1Ce7U/ilbtPrkoVo/t8uhN5Y/D7c= github.com/gotd/xor v0.1.0/go.mod h1:ZTmdgqf6SOHder8/MFp9CNkXIadzID5lIiaZxRZICH0= github.com/gotd/xor v0.1.1 h1:LSPEeuf7noTo4fi4PrEsAaWXOSwjsY2e+IINPiR+c7s= github.com/gotd/xor v0.1.1/go.mod h1:ZTmdgqf6SOHder8/MFp9CNkXIadzID5lIiaZxRZICH0= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 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/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v2.4.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1 h1:D7qhJP3R49ZjUzpzKQ6B2H3lgejPs6DTO5gRomhhOpE= github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1/go.mod h1:2OfLA+0esiUJpwMjrH39pEk79cb8MvGTBS9YlZpejJ4= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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= @@ -190,133 +181,65 @@ 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/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU= github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 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/mccutchen/go-httpbin v1.1.1 h1:aEws49HEJEyXHLDnshQVswfUlCVoS8g6h9YaDyaW7RE= github.com/mccutchen/go-httpbin v1.1.1/go.mod h1:fhpOYavp5g2K74XDl/ao2y4KvhqVtKlkg1e+0UaQv7I= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 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/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/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/panjf2000/ants/v2 v2.4.4 h1:kebk2KSiXHGeiYS6b+w2RqNN5+IKoqlBNd7cuC7MvQI= -github.com/panjf2000/ants/v2 v2.4.4/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.9.1 h1:a6qW1EVNZWH9WGI6CsYdD8WAylkoXBS5yv0XHlh17Tc= -github.com/pelletier/go-toml v1.9.1/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/panjf2000/ants/v2 v2.4.6 h1:drmj9mcygn2gawZ155dRbo+NfXEfAssjZNU1qoIb4gQ= +github.com/panjf2000/ants/v2 v2.4.6/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= +github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.24.0 h1:aIycr3wRFxPUq8XlLQlGQ9aNXV3dFi5y62pe/SB262k= -github.com/prometheus/common v0.24.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.1 h1:TlEtJq5GvGqMykEwWzbZWjjztF86swFhsPix1i0bkgA= +github.com/prometheus/procfs v0.7.1/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/quasilyte/go-ruleguard/dsl v0.3.2/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.22.0 h1:XrVUjV4K+izZpKXZHlPrYQiDtmdGiCylnT4i43AAWxg= -github.com/rs/zerolog v1.22.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/rs/zerolog v1.23.0 h1:UskrK+saS9P9Y789yNNulYKdARjPZuS35B8gJF2x60g= +github.com/rs/zerolog v1.23.0/go.mod h1:6c7hFfxPOy7TacJc4Fcdi24/J0NKYGzjG8FWRI916Qo= github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smira/go-statsd v1.3.2 h1:1EeuzxNZ/TD9apbTOFSM9nulqfcsQFmT4u1A2DREabI= github.com/smira/go-statsd v1.3.2/go.mod h1:1srXJ9/pbnN04G8f4F1jUzsGOnwkPKXciyqpewGlkC4= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 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.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= @@ -329,148 +252,214 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43 h1:QEePdg0ty2r0t1+qwfZmQ4OOl/MB2UXIeJSpIZv56lg= github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43/go.mod h1:OYRfF6eb5wY9VRFkXJH8FFBi3plw2v+giaIu7P054pM= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +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.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +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.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/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-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/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= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= -golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= 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= @@ -478,59 +467,108 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=