From db9e48a45e1fe3bd27d3a9c28d73cce2981ca2b9 Mon Sep 17 00:00:00 2001 From: Eugene Shuvalov Date: Thu, 7 Nov 2024 21:09:45 +0300 Subject: [PATCH 1/8] added rsa encryption --- .gitignore | 2 + README.md | 9 ++ example_key.pem | 28 ++++ example_key.pub.pem | 9 ++ internal/agent/agent.go | 51 ++++-- internal/agent/exporter/batch_exporter.go | 25 ++- internal/agent/exporter/exporter_test.go | 20 +-- internal/agent/exporter/limited_exporter.go | 27 ++-- internal/entities/errors.go | 2 + internal/entities/filepath.go | 32 ++++ internal/httpserver/handlers_test.go | 2 +- internal/httpserver/router.go | 6 + internal/middleware/security.go | 27 +++- internal/middleware/security_test.go | 4 +- internal/security/crypto.go | 152 ++++++++++++++++++ internal/security/crypto_test.go | 1 + internal/{services => security}/signer.go | 2 +- .../{services => security}/signer_test.go | 2 +- internal/server/server.go | 37 ++++- 19 files changed, 380 insertions(+), 58 deletions(-) create mode 100644 example_key.pem create mode 100644 example_key.pub.pem create mode 100644 internal/entities/filepath.go create mode 100644 internal/security/crypto.go create mode 100644 internal/security/crypto_test.go rename internal/{services => security}/signer.go (98%) rename internal/{services => security}/signer_test.go (98%) diff --git a/.gitignore b/.gitignore index 85f8162..5ebbaf6 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ vendor/ .vscode .DS_Store + +*.prof \ No newline at end of file diff --git a/README.md b/README.md index f15d8b1..1169fe4 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,12 @@ git fetch template && git checkout template/main .github При мёрже ветки с инкрементом в основную ветку `main` будут запускаться все автотесты. Подробнее про локальный и автоматический запуск читайте в [README автотестов](https://github.com/Yandex-Practicum/go-autotests). + + + + +openssl genpkey -algorithm RSA -out example_key.pem -pkeyopt rsa_keygen_bits:2048 +openssl rsa -in example_key.pem -pubout -out example_key.pub.pem + + +go run cmd/server/main.go --crypto-key=/Users/ex0rcist/projects/go/yp/metflix/key_example \ No newline at end of file diff --git a/example_key.pem b/example_key.pem new file mode 100644 index 0000000..ce429e5 --- /dev/null +++ b/example_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAc/6Js4iWRfLp +I/7cshsxxxU9cYeGZubuW+wmt/NjRJaSRU/NvJ3h8tYAOWmn/U7umEBcDtoXC2zl +cInj867KklrouXmlwiWhvomLTUJh/jg+jtuUbmhJckuzHwzXJoDqBbGP359g/uUu +qsr870xqTaNGFN224t39M+3PDOWX6jkkbztIOwXBoP4Z49j6JphbZRUAW6Q/jgvP +q1OjfDM/7qoBlD5Y7o0/gafo8wVTQjhBcR6ueSyQmRE760xZ6BguJhaAdXz7pw58 +DTzxWLxM6pRJfN+fslRr5FPh4DBIE6Ck+GIG+4Jqt10++r133mKO3eSOnuRPQOUT +Vs40K3IJAgMBAAECggEABUbo8tOmaiBu4a+n/Fsx3bKMxYuIYMoPiPeiSQmANfea +fVZDMuv6FEj5TzZDzGRotqgcE6oglsMscDoevc5fu5Q5w+naCQ6Cw6Wa1eP/bvpu +hmCaar6AQ4tLwhSW4Itgu4n+bWh/UUSL+jSarkbNfJi2YmSOE8ayuY+j9Kpw3TJ1 +rAQ/TVJSBTsL7uP76928OReP4azoOZabQiejMNunNSpFy3Ds36Dy2eGpyLZFL7+s +jmog+Q4PYiho9fJB1XJb3sCop9Vb6vNJ6h2ji0cNzhz2q84DHxav7hQEwGGaaHeR +0WotC+Hiy6YNZxHwbLu7EuhKfhgbx/E4rbt+8yyiEQKBgQD7lAxvEJtVer50lqTZ +FbKBR903GVmRDCXlqlIXrNWXGHhHL/qrQSqeJhLLTkkfcA22nxFTAWEKUeG+MGLW +njp4RAUKNxi4EBvPlH+iKbq3bOpJrtfPb/2rjJ4I9+4B85n41dAgybbMzo9U/cNY +xMVhFTz5ZSV+w1ZnzGjXDFQDlQKBgQDD1erxmmbw4AT9Mukfii7XkFsjY4xGamuX +AgiNhbPZcAxm2TCt8FRR2RxIZIVxWlI8q9JHKZlsuGf1HQZIeR9DKE8BGsam9aEP +sVby1OxcLdErAtPxZpR2DCgdjgJPFfBIpfy9K8FoUhlaAlfNvTCueQG78/dZSawA +6pdAtjfXpQKBgQDOtR5iQ2reGTRT51Mba1B4GOwSUaneG/Und4JsIX5934U0OJ/m +Bm1V9EKM+Y3rXs5ltt0tLnzlVdH9yxPq86dnGbxqYErXMaUoMgOM4mygOcVnNVns +/z+WrLtM8ymEQPvKESQpTJP+Q9pRIxBW55al5iVrZUghl65vKu0ZhO+i4QKBgHQ4 +yjwkdB9T8+IIis3LBk2sQEpaU++eTMfLzyOeS8QpeVK4ZulzjbcdcGmOLpu9DL0x +A5R0HbemIRwY45ezOlhy3aA8MdT4RWO4zW9D6hoynsY14W6MgGMmh9AybTBeRsH2 +sG3cP+YXgzAl39IcC34gFmVjPlJhJJFOqAEhozZ5AoGBAIjlG3DQfmisZ9ODIRJw +8sEfNy+BggsQpDeV4XR2s10wFnfs8YBOpe3hltwBWnC3MHxvJ95hhtXDZALQc8AS +8jzyzWUQUIwlqragywvEr5nl3zALw8OYLfy5UhbHpBg4nuwilKgsBVR+kOlwOPro +UdDRvssb7IK2GY83KTzBBKwg +-----END PRIVATE KEY----- diff --git a/example_key.pub.pem b/example_key.pub.pem new file mode 100644 index 0000000..54456c7 --- /dev/null +++ b/example_key.pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwHP+ibOIlkXy6SP+3LIb +MccVPXGHhmbm7lvsJrfzY0SWkkVPzbyd4fLWADlpp/1O7phAXA7aFwts5XCJ4/Ou +ypJa6Ll5pcIlob6Ji01CYf44Po7blG5oSXJLsx8M1yaA6gWxj9+fYP7lLqrK/O9M +ak2jRhTdtuLd/TPtzwzll+o5JG87SDsFwaD+GePY+iaYW2UVAFukP44Lz6tTo3wz +P+6qAZQ+WO6NP4Gn6PMFU0I4QXEernkskJkRO+tMWegYLiYWgHV8+6cOfA088Vi8 +TOqUSXzfn7JUa+RT4eAwSBOgpPhiBvuCarddPvq9d95ijt3kjp7kT0DlE1bONCty +CQIDAQAB +-----END PUBLIC KEY----- diff --git a/internal/agent/agent.go b/internal/agent/agent.go index bc7aa2b..4d6a214 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -11,7 +11,7 @@ import ( "github.com/ex0rcist/metflix/internal/agent/exporter" "github.com/ex0rcist/metflix/internal/entities" "github.com/ex0rcist/metflix/internal/logging" - "github.com/ex0rcist/metflix/internal/services" + "github.com/ex0rcist/metflix/internal/security" "github.com/ex0rcist/metflix/internal/utils" "github.com/spf13/pflag" ) @@ -22,16 +22,19 @@ type Agent struct { Stats *Stats Exporter exporter.Exporter + publicKey security.PublicKey + wg sync.WaitGroup } // Agent config. type Config struct { - Address entities.Address `env:"ADDRESS"` - PollInterval int `env:"POLL_INTERVAL"` - ReportInterval int `env:"REPORT_INTERVAL"` - RateLimit int `env:"RATE_LIMIT"` - Secret entities.Secret `env:"KEY"` + Address entities.Address `env:"ADDRESS"` + PollInterval int `env:"POLL_INTERVAL"` + ReportInterval int `env:"REPORT_INTERVAL"` + RateLimit int `env:"RATE_LIMIT"` + Secret entities.Secret `env:"KEY"` + PublicKeyPath entities.FilePath `env:"CRYPTO_KEY"` } // Constructor. @@ -48,15 +51,24 @@ func New() (*Agent, error) { return nil, err } - exporter, err := newMetricsExporter(config) + var key security.PublicKey + if len(config.PublicKeyPath) != 0 { + key, err = security.NewPublicKey(config.PublicKeyPath) + if err != nil { + return nil, err + } + } + + exporter, err := newMetricsExporter(config, key) if err != nil { return nil, err } return &Agent{ - Config: config, - Stats: NewStats(), - Exporter: exporter, + Config: config, + Stats: NewStats(), + Exporter: exporter, + publicKey: key, }, nil } @@ -171,6 +183,10 @@ func (c Config) String() string { str = append(str, fmt.Sprintf("secret=%v", c.Secret)) } + if len(c.PublicKeyPath) > 0 { + str = append(str, fmt.Sprintf("public-key=%v", c.PublicKeyPath)) + } + return "agent config: " + strings.Join(str, "; ") } @@ -187,22 +203,22 @@ func detectExporterKind(c *Config) string { return ek } -func newMetricsExporter(config *Config) (exporter.Exporter, error) { +func newMetricsExporter(config *Config, publicKey security.PublicKey) (exporter.Exporter, error) { var exp exporter.Exporter - var signer services.Signer + var signer security.Signer var err error if len(config.Secret) > 0 { - signer = services.NewSignerService(config.Secret) + signer = security.NewSignerService(config.Secret) } exporterKind := detectExporterKind(config) switch exporterKind { case exporter.KindLimited: - exp = exporter.NewLimitedExporter(&config.Address, signer, config.RateLimit) + exp = exporter.NewLimitedExporter(&config.Address, signer, config.RateLimit, publicKey) case exporter.KindBatch: - exp = exporter.NewBatchExporter(&config.Address, signer) + exp = exporter.NewBatchExporter(&config.Address, signer, publicKey) default: exp, err = nil, fmt.Errorf("unknown exporter type") } @@ -217,6 +233,9 @@ func parseConfig(config *Config) error { secret := config.Secret pflag.VarP(&secret, "secret", "k", "a key to sign outgoing data") + publicKeyPath := config.PublicKeyPath + pflag.VarP(&publicKeyPath, "crypto-key", "", "path to public key to encrypt agent -> server communications") + pflag.IntVarP(&config.PollInterval, "poll-interval", "p", config.PollInterval, "interval (s) for polling stats") pflag.IntVarP(&config.ReportInterval, "report-interval", "r", config.ReportInterval, "interval (s) for polling stats") pflag.IntVarP(&config.RateLimit, "rate-limit", "l", config.RateLimit, "number of max simultaneous requests to server") @@ -230,6 +249,8 @@ func parseConfig(config *Config) error { config.Address = address case "secret": config.Secret = secret + case "crypto-key": + config.PublicKeyPath = publicKeyPath } }) diff --git a/internal/agent/exporter/batch_exporter.go b/internal/agent/exporter/batch_exporter.go index 650bde1..6484bb7 100644 --- a/internal/agent/exporter/batch_exporter.go +++ b/internal/agent/exporter/batch_exporter.go @@ -12,7 +12,7 @@ import ( "github.com/ex0rcist/metflix/internal/entities" "github.com/ex0rcist/metflix/internal/logging" "github.com/ex0rcist/metflix/internal/retrier" - "github.com/ex0rcist/metflix/internal/services" + "github.com/ex0rcist/metflix/internal/security" "github.com/ex0rcist/metflix/internal/utils" "github.com/ex0rcist/metflix/pkg/metrics" ) @@ -21,24 +21,26 @@ var _ Exporter = (*BatchExporter)(nil) // Batch exporter to optimize requests count. type BatchExporter struct { - baseURL *entities.Address - client *http.Client - signer services.Signer + baseURL *entities.Address + client *http.Client + signer security.Signer + publicKey security.PublicKey buffer []metrics.MetricExchange err error } // Constructor. -func NewBatchExporter(baseURL *entities.Address, signer services.Signer) *BatchExporter { +func NewBatchExporter(baseURL *entities.Address, signer security.Signer, publicKey security.PublicKey) *BatchExporter { client := &http.Client{ Timeout: 2 * time.Second, } return &BatchExporter{ - baseURL: baseURL, - client: client, - signer: signer, + baseURL: baseURL, + client: client, + signer: signer, + publicKey: publicKey, } } @@ -123,6 +125,13 @@ func (e *BatchExporter) doSend() error { return err } + if e.publicKey != nil { + payload, err = security.Encrypt(io.Reader(payload), e.publicKey) + if err != nil { + return err + } + } + url := "http://" + e.baseURL.String() + "/updates" req, err := http.NewRequest(http.MethodPost, url, payload) if err != nil { diff --git a/internal/agent/exporter/exporter_test.go b/internal/agent/exporter/exporter_test.go index c57aada..918e2e3 100644 --- a/internal/agent/exporter/exporter_test.go +++ b/internal/agent/exporter/exporter_test.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ex0rcist/metflix/internal/entities" - "github.com/ex0rcist/metflix/internal/services" + "github.com/ex0rcist/metflix/internal/security" "github.com/ex0rcist/metflix/pkg/metrics" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -31,7 +31,7 @@ func (m *MockSigner) VerifySignature(data []byte, hash string) (bool, error) { return args.Bool(0), args.Error(1) } -func mockSigner(signature string) services.Signer { +func mockSigner(signature string) security.Signer { signer := new(MockSigner) signer.On("CalculateSignature", mock.Anything).Return(signature, nil) @@ -56,7 +56,7 @@ func TestNewLimitedExporter(t *testing.T) { signer := mockSigner("mocked_sign") - exporter := NewLimitedExporter(&baseURL, signer, 3) + exporter := NewLimitedExporter(&baseURL, signer, 3, nil) assert.NotNil(t, exporter) assert.Equal(t, baseURL, *exporter.baseURL) @@ -69,7 +69,7 @@ func TestAdd(t *testing.T) { signer := mockSigner("mocked_sign") - exporter := NewLimitedExporter(&baseURL, signer, 3) + exporter := NewLimitedExporter(&baseURL, signer, 3, nil) counter := metrics.Counter(10) exporter.Add("test_counter", counter) @@ -95,7 +95,7 @@ func TestLimitedSend(t *testing.T) { })) defer server.Close() - exporter := NewLimitedExporter(&baseURL, signer, 1) + exporter := NewLimitedExporter(&baseURL, signer, 1, nil) exporter.Add("test_counter", metrics.Counter(10)) err := exporter.Send() @@ -121,7 +121,7 @@ func TestBatchSend(t *testing.T) { })) defer server.Close() - exporter := NewBatchExporter(&baseURL, signer) + exporter := NewBatchExporter(&baseURL, signer, nil) exporter.Add("test_counter", metrics.Counter(10)) err := exporter.Send() @@ -136,7 +136,7 @@ func TestSendEmptyBuffer(t *testing.T) { signer := mockSigner("mocked_sign") baseURL := entities.Address("localhost:8080") - exporter := NewLimitedExporter(&baseURL, signer, 1) + exporter := NewLimitedExporter(&baseURL, signer, 1, nil) err := exporter.Send() @@ -148,7 +148,7 @@ func TestError(t *testing.T) { signer := mockSigner("mocked_sign") baseURL := entities.Address("localhost:8080") - exporter := NewLimitedExporter(&baseURL, signer, 1) + exporter := NewLimitedExporter(&baseURL, signer, 1, nil) exporter.err = errors.New("test error") assert.Equal(t, "metrics export failed: test error", exporter.Error().Error()) @@ -158,7 +158,7 @@ func TestReset(t *testing.T) { signer := mockSigner("mocked_sign") baseURL := entities.Address("localhost:8080") - exporter := NewLimitedExporter(&baseURL, signer, 1) + exporter := NewLimitedExporter(&baseURL, signer, 1, nil) exporter.Add("test_counter", metrics.Counter(10)) exporter.Reset() @@ -171,7 +171,7 @@ func TestWorker(t *testing.T) { signer := mockSigner("mocked_sign") baseURL := entities.Address("localhost:8080") - exporter := NewLimitedExporter(&baseURL, signer, 1) + exporter := NewLimitedExporter(&baseURL, signer, 1, nil) exporter.Add("test_counter", metrics.Counter(10)) err := exporter.Send() diff --git a/internal/agent/exporter/limited_exporter.go b/internal/agent/exporter/limited_exporter.go index 48aa49b..022e495 100644 --- a/internal/agent/exporter/limited_exporter.go +++ b/internal/agent/exporter/limited_exporter.go @@ -12,7 +12,7 @@ import ( "github.com/ex0rcist/metflix/internal/entities" "github.com/ex0rcist/metflix/internal/logging" "github.com/ex0rcist/metflix/internal/retrier" - "github.com/ex0rcist/metflix/internal/services" + "github.com/ex0rcist/metflix/internal/security" "github.com/ex0rcist/metflix/internal/utils" "github.com/ex0rcist/metflix/pkg/metrics" ) @@ -21,9 +21,10 @@ var _ Exporter = (*LimitedExporter)(nil) // An exporter to send metrics one-by-one in parallel. type LimitedExporter struct { - baseURL *entities.Address - client *http.Client - signer services.Signer + baseURL *entities.Address + client *http.Client + signer security.Signer + publicKey security.PublicKey buffer []metrics.MetricExchange jobs chan metrics.MetricExchange @@ -31,16 +32,17 @@ type LimitedExporter struct { } // Constructor. -func NewLimitedExporter(baseURL *entities.Address, signer services.Signer, numWorkers int) *LimitedExporter { +func NewLimitedExporter(baseURL *entities.Address, signer security.Signer, numWorkers int, publicKey security.PublicKey) *LimitedExporter { client := &http.Client{ Timeout: 2 * time.Second, } exporter := &LimitedExporter{ - baseURL: baseURL, - client: client, - signer: signer, - jobs: make(chan metrics.MetricExchange, 30), + baseURL: baseURL, + client: client, + signer: signer, + publicKey: publicKey, + jobs: make(chan metrics.MetricExchange, 30), } exporter.spawnWorkers(numWorkers) @@ -155,6 +157,13 @@ func (e *LimitedExporter) doSend(mex metrics.MetricExchange) error { return err } + if e.publicKey != nil { + payload, err = security.Encrypt(io.Reader(payload), e.publicKey) + if err != nil { + return err + } + } + url := "http://" + e.baseURL.String() + "/update" req, err := http.NewRequest(http.MethodPost, url, payload) diff --git a/internal/entities/errors.go b/internal/entities/errors.go index cbc1cee..037e274 100644 --- a/internal/entities/errors.go +++ b/internal/entities/errors.go @@ -28,6 +28,8 @@ var ( ErrNoSignature = errors.New("no signature provided") + ErrBadRSAKey = errors.New("bad RSA key") + ErrUnexpected = errors.New("unexpected error") ) diff --git a/internal/entities/filepath.go b/internal/entities/filepath.go new file mode 100644 index 0000000..6300ade --- /dev/null +++ b/internal/entities/filepath.go @@ -0,0 +1,32 @@ +package entities + +import ( + "fmt" + "os" +) + +// FilePath is a path to a file on local filesystem. +type FilePath string + +// Set validates that path exists and assigns it to FilePath. +// Required by pflags interface. +func (p *FilePath) Set(src string) error { + if _, err := os.Stat(src); err != nil { + return fmt.Errorf("invalid file path: %w", err) + } + + *p = FilePath(src) + + return nil +} + +// Returns string representation of stored path. +// Required by pflags interface. +func (p FilePath) String() string { + return string(p) +} + +// Required by pflags interface. +func (p FilePath) Type() string { + return "string" +} diff --git a/internal/httpserver/handlers_test.go b/internal/httpserver/handlers_test.go index 9bb7772..49e7440 100644 --- a/internal/httpserver/handlers_test.go +++ b/internal/httpserver/handlers_test.go @@ -48,7 +48,7 @@ func createTestRouter() (http.Handler, *storage.ServiceMock, *services.PingerMoc sm := &storage.ServiceMock{} pm := &services.PingerMock{} - router := NewRouter(sm, pm, entities.Secret("")) + router := NewRouter(sm, pm, entities.Secret(""), nil) return router, sm, pm } diff --git a/internal/httpserver/router.go b/internal/httpserver/router.go index a92128d..7a61a70 100644 --- a/internal/httpserver/router.go +++ b/internal/httpserver/router.go @@ -25,6 +25,7 @@ import ( "github.com/ex0rcist/metflix/internal/entities" "github.com/ex0rcist/metflix/internal/middleware" + "github.com/ex0rcist/metflix/internal/security" "github.com/ex0rcist/metflix/internal/services" "github.com/ex0rcist/metflix/internal/storage" ) @@ -34,6 +35,7 @@ func NewRouter( storageService storage.StorageService, pingerService services.Pinger, secret entities.Secret, + privateKey security.PrivateKey, ) http.Handler { router := chi.NewRouter() @@ -46,6 +48,10 @@ func NewRouter( return middleware.CheckSignedRequest(next, secret) }) + router.Use(func(next http.Handler) http.Handler { + return middleware.DecryptRequest(next, privateKey) + }) + router.Use(middleware.DecompressRequest) router.Use(middleware.CompressResponse) diff --git a/internal/middleware/security.go b/internal/middleware/security.go index bcac322..2231e9e 100644 --- a/internal/middleware/security.go +++ b/internal/middleware/security.go @@ -8,7 +8,7 @@ import ( "github.com/ex0rcist/metflix/internal/entities" "github.com/ex0rcist/metflix/internal/logging" - "github.com/ex0rcist/metflix/internal/services" + "github.com/ex0rcist/metflix/internal/security" "github.com/go-chi/chi/middleware" ) @@ -45,7 +45,7 @@ func SignResponse(next http.Handler, secret entities.Secret) http.Handler { // pass the custom ResponseWriter to the next handler next.ServeHTTP(crw, r) - signer := services.NewSignerService(secret) + signer := security.NewSignerService(secret) signature, _ := signer.CalculateSignature(bodyBuffer.Bytes()) w.Header().Set("HashSHA256", signature) @@ -96,7 +96,7 @@ func CheckSignedRequest(next http.Handler, secret entities.Secret) http.Handler return } - signer := services.NewSignerService(secret) + signer := security.NewSignerService(secret) ok, _ := signer.VerifySignature(bodyBytes, hash) if !ok { logging.LogErrorCtx(ctx, fmt.Errorf("failed to verify request signature")) @@ -110,3 +110,24 @@ func CheckSignedRequest(next http.Handler, secret entities.Secret) http.Handler next.ServeHTTP(w, r) }) } + +// Decrypt request body using RSA. +func DecryptRequest(next http.Handler, key security.PrivateKey) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if key == nil { // skip middleware entirely + next.ServeHTTP(w, r) + return + } + + msg, err := security.Decrypt(r.Body, key) + if err != nil { + logging.LogError(err, "error decoding request") + http.Error(w, "decrypt failed", http.StatusBadRequest) + + return + } + + r.Body = io.NopCloser(bytes.NewReader(msg.Bytes())) + next.ServeHTTP(w, r) + }) +} diff --git a/internal/middleware/security_test.go b/internal/middleware/security_test.go index e3d78b7..a298083 100644 --- a/internal/middleware/security_test.go +++ b/internal/middleware/security_test.go @@ -9,7 +9,7 @@ import ( "github.com/ex0rcist/metflix/internal/entities" "github.com/ex0rcist/metflix/internal/logging" - "github.com/ex0rcist/metflix/internal/services" + "github.com/ex0rcist/metflix/internal/security" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -91,7 +91,7 @@ func TestSignResponseMiddlewareWithoutSecret(t *testing.T) { func TestCheckSignedRequestMiddleware(t *testing.T) { secret := entities.Secret("my-secret-key") - signer := services.NewSignerService(secret) + signer := security.NewSignerService(secret) body := []byte("test request") signature, err := signer.CalculateSignature(body) diff --git a/internal/security/crypto.go b/internal/security/crypto.go new file mode 100644 index 0000000..41ed342 --- /dev/null +++ b/internal/security/crypto.go @@ -0,0 +1,152 @@ +package security + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "fmt" + "io" + "os" + + "github.com/ex0rcist/metflix/internal/entities" +) + +// RSA key to encrypt data +type PublicKey *rsa.PublicKey + +// RSA key to decrypt data +type PrivateKey *rsa.PrivateKey + +// NewPrivateKey reads RSA public key from file +func NewPrivateKey(path entities.FilePath) (PrivateKey, error) { + pemBlock, err := readKey(path) + if err != nil { + return nil, err + } + + var rsaKey PrivateKey + switch pemBlock.Type { + case "RSA PRIVATE KEY": + rsaKey, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + if err != nil { + return nil, fmt.Errorf("security.NewPrivateKey - x509.ParsePKCS1PrivateKey: %w", err) + + } + case "PRIVATE KEY": + key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes) + if err != nil { + return nil, fmt.Errorf("security.NewPrivateKey - x509.ParsePKCS8PrivateKey: %w", err) + } + + key, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("security.NewPrivateKey - key.(*rsa.PrivateKey): %w", err) + } + + rsaKey = key.(*rsa.PrivateKey) + default: + return nil, fmt.Errorf("unknown key type %s", pemBlock.Type) + } + + return rsaKey, nil +} + +// NewPublicKey reads RSA public key from file +func NewPublicKey(path entities.FilePath) (PublicKey, error) { + block, err := readKey(path) + if err != nil { + return nil, err + } + + rawKey, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("security.NewPublicKey - x509.ParsePKIXPublicKey: %w", err) + } + + key, ok := rawKey.(*rsa.PublicKey) + if !ok { + return nil, fmt.Errorf("security.NewPublicKey - .(*rsa.PublicKey): %w", entities.ErrBadRSAKey) + } + + return key, nil +} + +// Encrypt message with RSA using PublicKey +func Encrypt(src io.Reader, key PublicKey) (*bytes.Buffer, error) { + msg := new(bytes.Buffer) + + chunkSize := (*rsa.PublicKey)(key).Size() - 2*sha256.New().Size() - 2 + chunk := make([]byte, chunkSize) + + for { + n, err := src.Read(chunk) + + if n > 0 { + encryptedChunk, encErr := rsa.EncryptOAEP(sha256.New(), rand.Reader, key, chunk, nil) + if encErr != nil { + return nil, fmt.Errorf("security.Encrypt - rsa.EncryptOAEP: %w", encErr) + } + + msg.Write(encryptedChunk) + } + + if err == io.EOF { + break + } + + if err != nil { + return nil, fmt.Errorf("security.Encrypt - reader.Read: %w", err) + } + } + + return msg, nil +} + +// Decrypt RSA-encoded message using PrivateKey +func Decrypt(src io.Reader, key PrivateKey) (*bytes.Buffer, error) { + msg := new(bytes.Buffer) + + chunkSize := key.PublicKey.Size() + chunk := make([]byte, chunkSize) + + for { + n, err := src.Read(chunk) + + if n > 0 { + decryptedChunk, decErr := rsa.DecryptOAEP(sha256.New(), rand.Reader, key, chunk, nil) + if decErr != nil { + return nil, fmt.Errorf("security.Decrypt - rsa.DecryptOAEP: %w", decErr) + } + + msg.Write(decryptedChunk) + } + + if err == io.EOF { + break + } + + if err != nil { + return nil, fmt.Errorf("security.Decrypt - src.Read: %w", err) + } + } + + return msg, nil +} + +// Read key in PEM format from file +func readKey(path entities.FilePath) (*pem.Block, error) { + rawKey, err := os.ReadFile(path.String()) + if err != nil { + return nil, fmt.Errorf("security.readKey - os.ReadFile: %w", err) + } + + key, _ := pem.Decode(rawKey) + if key == nil { + return nil, fmt.Errorf("security.readKey - pem.Decode: %w", entities.ErrBadRSAKey) + } + + return key, nil +} diff --git a/internal/security/crypto_test.go b/internal/security/crypto_test.go new file mode 100644 index 0000000..3c8a4b4 --- /dev/null +++ b/internal/security/crypto_test.go @@ -0,0 +1 @@ +package security diff --git a/internal/services/signer.go b/internal/security/signer.go similarity index 98% rename from internal/services/signer.go rename to internal/security/signer.go index 75cc05e..e5adf77 100644 --- a/internal/services/signer.go +++ b/internal/security/signer.go @@ -1,4 +1,4 @@ -package services +package security import ( "crypto/hmac" diff --git a/internal/services/signer_test.go b/internal/security/signer_test.go similarity index 98% rename from internal/services/signer_test.go rename to internal/security/signer_test.go index 13fbe67..84aebd7 100644 --- a/internal/services/signer_test.go +++ b/internal/security/signer_test.go @@ -1,4 +1,4 @@ -package services +package security import ( "testing" diff --git a/internal/server/server.go b/internal/server/server.go index 3d8117e..bac1e55 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -14,6 +14,7 @@ import ( "github.com/ex0rcist/metflix/internal/entities" "github.com/ex0rcist/metflix/internal/httpserver" "github.com/ex0rcist/metflix/internal/logging" + "github.com/ex0rcist/metflix/internal/security" "github.com/ex0rcist/metflix/internal/services" "github.com/ex0rcist/metflix/internal/storage" "github.com/spf13/pflag" @@ -28,17 +29,19 @@ type Server struct { profiler *ProfilerServer storage storage.MetricsStorage router http.Handler + privateKey security.PrivateKey } // Backend config type Config struct { - Address entities.Address `env:"ADDRESS"` - StoreInterval int `env:"STORE_INTERVAL"` - StorePath string `env:"FILE_STORAGE_PATH"` - RestoreOnStart bool `env:"RESTORE"` - DatabaseDSN string `env:"DATABASE_DSN"` - Secret entities.Secret `env:"KEY"` - ProfilerAddress entities.Address `env:"PROFILER_ADDRESS"` + Address entities.Address `env:"ADDRESS"` + StoreInterval int `env:"STORE_INTERVAL"` + StorePath string `env:"FILE_STORAGE_PATH"` + RestoreOnStart bool `env:"RESTORE"` + DatabaseDSN string `env:"DATABASE_DSN"` + Secret entities.Secret `env:"KEY"` + ProfilerAddress entities.Address `env:"PROFILER_ADDRESS"` + PrivateKeyPath entities.FilePath `env:"CRYPTO_KEY"` } // Server constructor @@ -60,9 +63,17 @@ func New() (*Server, error) { return nil, err } + var privateKey security.PrivateKey + if len(config.PrivateKeyPath) != 0 { + privateKey, err = security.NewPrivateKey(config.PrivateKeyPath) + if err != nil { + return nil, fmt.Errorf("server - New - security.NewPrivateKey: %w", err) + } + } + storageService := storage.NewService(dataStorage) pingerService := services.NewPingerService(dataStorage) - router := httpserver.NewRouter(storageService, pingerService, config.Secret) + router := httpserver.NewRouter(storageService, pingerService, config.Secret, privateKey) httpServer := httpserver.New(router, config.Address) pprofiler := NewProfilerServer(config.ProfilerAddress) @@ -73,6 +84,7 @@ func New() (*Server, error) { storage: dataStorage, router: router, profiler: pprofiler, + privateKey: privateKey, }, nil } @@ -139,6 +151,10 @@ func (s *Server) String() string { str = append(str, fmt.Sprintf("secret=%s", s.config.Secret)) } + if len(s.config.PrivateKeyPath) > 0 { + str = append(str, fmt.Sprintf("private-key=%v", s.config.PrivateKeyPath)) + } + return "server config: " + strings.Join(str, "; ") } @@ -182,6 +198,9 @@ func parseFlags(config *Config, progname string, args []string) error { secret := config.Secret flags.VarP(&secret, "secret", "k", "a key to sign outgoing data") + privateKeyPath := config.PrivateKeyPath + flags.VarP(&privateKeyPath, "crypto-key", "", "path to public key to encrypt agent -> server communications") + // define flags flags.IntVarP(&config.StoreInterval, "store-interval", "i", config.StoreInterval, "interval (s) for dumping metrics to the disk, zero value means saving after each request") flags.StringVarP(&config.StorePath, "store-file", "f", config.StorePath, "path to file to store metrics") @@ -200,6 +219,8 @@ func parseFlags(config *Config, progname string, args []string) error { config.Address = address case "secret": config.Secret = secret + case "crypto-key": + config.PrivateKeyPath = privateKeyPath } }) From d19331a1b0b2332c866dfa0eb523fc44cb056796 Mon Sep 17 00:00:00 2001 From: Eugene Shuvalov Date: Thu, 7 Nov 2024 22:09:44 +0300 Subject: [PATCH 2/8] added json configuration --- config/agent.example.json | 6 ++++++ config/server.example.json | 8 ++++++++ internal/agent/agent.go | 36 +++++++++++++++++++++++++++++------ internal/server/server.go | 39 ++++++++++++++++++++++++++++++-------- 4 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 config/agent.example.json create mode 100644 config/server.example.json diff --git a/config/agent.example.json b/config/agent.example.json new file mode 100644 index 0000000..ad03fd9 --- /dev/null +++ b/config/agent.example.json @@ -0,0 +1,6 @@ +{ + "address": "localhost:8080", + "report_interval": 3, + "poll_interval": 1, + "crypto_key": "./example_key.pub.pem" +} \ No newline at end of file diff --git a/config/server.example.json b/config/server.example.json new file mode 100644 index 0000000..445f278 --- /dev/null +++ b/config/server.example.json @@ -0,0 +1,8 @@ +{ + "address": "localhost:8080", + "restore": true, + "store_interval": 1, + "store_file": "./metflix.db", + "database_dsn": "postgres://cm:cm@localhost:5432/metflix?sslmode=disable", + "crypto_key": "./example_key.pem" +} \ No newline at end of file diff --git a/internal/agent/agent.go b/internal/agent/agent.go index 4d6a214..6242cfa 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -2,7 +2,9 @@ package agent import ( "context" + "encoding/json" "fmt" + "os" "strings" "sync" "time" @@ -29,12 +31,12 @@ type Agent struct { // Agent config. type Config struct { - Address entities.Address `env:"ADDRESS"` - PollInterval int `env:"POLL_INTERVAL"` - ReportInterval int `env:"REPORT_INTERVAL"` - RateLimit int `env:"RATE_LIMIT"` - Secret entities.Secret `env:"KEY"` - PublicKeyPath entities.FilePath `env:"CRYPTO_KEY"` + Address entities.Address `env:"ADDRESS" json:"address"` + PollInterval int `env:"POLL_INTERVAL" json:"poll_interval"` + ReportInterval int `env:"REPORT_INTERVAL" json:"report_interval"` + RateLimit int `env:"RATE_LIMIT" json:"-"` + Secret entities.Secret `env:"KEY" json:"key"` + PublicKeyPath entities.FilePath `env:"CRYPTO_KEY" json:"crypto_key"` } // Constructor. @@ -236,12 +238,21 @@ func parseConfig(config *Config) error { publicKeyPath := config.PublicKeyPath pflag.VarP(&publicKeyPath, "crypto-key", "", "path to public key to encrypt agent -> server communications") + configPath := entities.FilePath("") + pflag.VarP(&configPath, "config", "c", "path to configuration file in JSON format") + pflag.IntVarP(&config.PollInterval, "poll-interval", "p", config.PollInterval, "interval (s) for polling stats") pflag.IntVarP(&config.ReportInterval, "report-interval", "r", config.ReportInterval, "interval (s) for polling stats") pflag.IntVarP(&config.RateLimit, "rate-limit", "l", config.RateLimit, "number of max simultaneous requests to server") pflag.Parse() + if len(configPath) != 0 { + if err := loadConfigFromFile(configPath, config); err != nil { + return err + } + } + // because VarP gets non-pointer value, set it manually pflag.Visit(func(f *pflag.Flag) { switch f.Name { @@ -260,3 +271,16 @@ func parseConfig(config *Config) error { return nil } + +func loadConfigFromFile(src entities.FilePath, dst *Config) error { + data, err := os.ReadFile(src.String()) + if err != nil { + return fmt.Errorf("agent.loadConfigFromFile - os.ReadFile: %w", err) + } + + if err := json.Unmarshal(data, dst); err != nil { + return fmt.Errorf("agent.loadConfigFromFile - json.Unmarshal: %w", err) + } + + return nil +} diff --git a/internal/server/server.go b/internal/server/server.go index bac1e55..1c5cb7b 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -2,6 +2,7 @@ package server import ( "context" + "encoding/json" "fmt" "net/http" "os" @@ -34,14 +35,14 @@ type Server struct { // Backend config type Config struct { - Address entities.Address `env:"ADDRESS"` - StoreInterval int `env:"STORE_INTERVAL"` - StorePath string `env:"FILE_STORAGE_PATH"` - RestoreOnStart bool `env:"RESTORE"` - DatabaseDSN string `env:"DATABASE_DSN"` - Secret entities.Secret `env:"KEY"` - ProfilerAddress entities.Address `env:"PROFILER_ADDRESS"` - PrivateKeyPath entities.FilePath `env:"CRYPTO_KEY"` + Address entities.Address `env:"ADDRESS" json:"address"` + StoreInterval int `env:"STORE_INTERVAL" json:"store_interval"` + StorePath string `env:"FILE_STORAGE_PATH" json:"store_file"` + RestoreOnStart bool `env:"RESTORE" json:"restore"` + DatabaseDSN string `env:"DATABASE_DSN" json:"database_dsn"` + Secret entities.Secret `env:"KEY" json:"key"` + ProfilerAddress entities.Address `env:"PROFILER_ADDRESS" json:"profiler_address"` + PrivateKeyPath entities.FilePath `env:"CRYPTO_KEY" json:"crypto_key"` } // Server constructor @@ -201,6 +202,9 @@ func parseFlags(config *Config, progname string, args []string) error { privateKeyPath := config.PrivateKeyPath flags.VarP(&privateKeyPath, "crypto-key", "", "path to public key to encrypt agent -> server communications") + configPath := entities.FilePath("") + flags.VarP(&configPath, "config", "c", "path to configuration file in JSON format") + // define flags flags.IntVarP(&config.StoreInterval, "store-interval", "i", config.StoreInterval, "interval (s) for dumping metrics to the disk, zero value means saving after each request") flags.StringVarP(&config.StorePath, "store-file", "f", config.StorePath, "path to file to store metrics") @@ -212,6 +216,12 @@ func parseFlags(config *Config, progname string, args []string) error { return err } + if len(configPath) != 0 { + if err := loadConfigFromFile(configPath, config); err != nil { + return err + } + } + // fill values flags.Visit(func(f *pflag.Flag) { switch f.Name { @@ -264,3 +274,16 @@ func newDataStorage(config *Config) (storage.MetricsStorage, error) { return nil, fmt.Errorf("unknown storage type") } } + +func loadConfigFromFile(src entities.FilePath, dst *Config) error { + data, err := os.ReadFile(src.String()) + if err != nil { + return fmt.Errorf("server.loadConfigFromFile - os.ReadFile: %w", err) + } + + if err := json.Unmarshal(data, dst); err != nil { + return fmt.Errorf("server.loadConfigFromFile - json.Unmarshal: %w", err) + } + + return nil +} From 22cc9e4ef41c14bc61e8eb24c3ae1111ce08d1e4 Mon Sep 17 00:00:00 2001 From: Eugene Shuvalov Date: Thu, 7 Nov 2024 22:39:11 +0300 Subject: [PATCH 3/8] fixed json configuration --- .gitignore | 1 + config/server.example.json | 2 +- coverage.out.tmp | 835 ------------------------------------- internal/agent/agent.go | 33 +- internal/server/server.go | 38 +- 5 files changed, 55 insertions(+), 854 deletions(-) delete mode 100644 coverage.out.tmp diff --git a/.gitignore b/.gitignore index 5ebbaf6..9dc4825 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +*.out.tmp # Binaries cmd/agent/main diff --git a/config/server.example.json b/config/server.example.json index 445f278..abed86f 100644 --- a/config/server.example.json +++ b/config/server.example.json @@ -3,6 +3,6 @@ "restore": true, "store_interval": 1, "store_file": "./metflix.db", - "database_dsn": "postgres://cm:cm@localhost:5432/metflix?sslmode=disable", + "crypto_key": "./example_key.pem" } \ No newline at end of file diff --git a/coverage.out.tmp b/coverage.out.tmp deleted file mode 100644 index ad1b5f8..0000000 --- a/coverage.out.tmp +++ /dev/null @@ -1,835 +0,0 @@ -mode: atomic -github.com/ex0rcist/metflix/cmd/agent/main.go:14.13,20.16 4 0 -github.com/ex0rcist/metflix/cmd/agent/main.go:20.16,22.3 1 0 -github.com/ex0rcist/metflix/cmd/agent/main.go:24.2,24.12 1 0 -github.com/ex0rcist/metflix/cmd/server/main.go:16.13,21.16 4 0 -github.com/ex0rcist/metflix/cmd/server/main.go:21.16,23.3 1 0 -github.com/ex0rcist/metflix/cmd/server/main.go:25.2,25.13 1 0 -github.com/ex0rcist/metflix/docs/api/docs.go:372.13,374.2 1 0 -github.com/ex0rcist/metflix/cmd/staticlint/main.go:5.13,8.2 2 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:26.30,27.17 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:27.17,29.3 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:30.2,30.25 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:34.40,37.16 2 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:37.16,39.3 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:41.2,45.16 4 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:45.16,46.13 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:49.2,49.15 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:49.15,51.17 2 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:51.17,53.4 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:56.2,57.50 2 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:57.50,58.13 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:61.2,61.54 1 0 -github.com/ex0rcist/metflix/internal/entities/address.go:14.34,16.2 1 3 -github.com/ex0rcist/metflix/internal/entities/address.go:19.41,21.22 2 9 -github.com/ex0rcist/metflix/internal/entities/address.go:21.22,23.3 1 5 -github.com/ex0rcist/metflix/internal/entities/address.go:25.2,27.46 2 4 -github.com/ex0rcist/metflix/internal/entities/address.go:27.46,29.3 1 1 -github.com/ex0rcist/metflix/internal/entities/address.go:31.2,33.12 2 3 -github.com/ex0rcist/metflix/internal/entities/address.go:37.32,39.2 1 1 -github.com/ex0rcist/metflix/internal/entities/errors.go:35.37,37.2 1 0 -github.com/ex0rcist/metflix/internal/entities/errors.go:48.40,50.2 1 0 -github.com/ex0rcist/metflix/internal/entities/secret.go:13.40,14.27 1 0 -github.com/ex0rcist/metflix/internal/entities/secret.go:14.27,16.3 1 0 -github.com/ex0rcist/metflix/internal/entities/secret.go:18.2,20.12 2 0 -github.com/ex0rcist/metflix/internal/entities/secret.go:24.31,26.2 1 0 -github.com/ex0rcist/metflix/internal/entities/secret.go:29.33,30.17 1 0 -github.com/ex0rcist/metflix/internal/entities/secret.go:30.17,32.3 1 0 -github.com/ex0rcist/metflix/internal/entities/secret.go:34.2,35.52 2 0 -github.com/ex0rcist/metflix/internal/middleware/compression.go:14.56,15.71 1 4 -github.com/ex0rcist/metflix/internal/middleware/compression.go:15.71,19.25 3 4 -github.com/ex0rcist/metflix/internal/middleware/compression.go:19.25,22.4 2 1 -github.com/ex0rcist/metflix/internal/middleware/compression.go:24.3,28.17 4 3 -github.com/ex0rcist/metflix/internal/middleware/compression.go:28.17,29.11 1 2 -github.com/ex0rcist/metflix/internal/middleware/compression.go:30.57,32.11 2 1 -github.com/ex0rcist/metflix/internal/middleware/compression.go:33.54,35.11 2 1 -github.com/ex0rcist/metflix/internal/middleware/compression.go:39.3,39.23 1 1 -github.com/ex0rcist/metflix/internal/middleware/compression.go:44.55,45.71 1 2 -github.com/ex0rcist/metflix/internal/middleware/compression.go:45.71,48.27 2 2 -github.com/ex0rcist/metflix/internal/middleware/compression.go:48.27,53.4 3 1 -github.com/ex0rcist/metflix/internal/middleware/compression.go:55.3,58.32 3 1 -github.com/ex0rcist/metflix/internal/middleware/compression.go:62.45,63.47 1 4 -github.com/ex0rcist/metflix/internal/middleware/compression.go:63.47,65.3 1 2 -github.com/ex0rcist/metflix/internal/middleware/compression.go:67.2,67.62 1 2 -github.com/ex0rcist/metflix/internal/middleware/compression.go:67.62,68.41 1 2 -github.com/ex0rcist/metflix/internal/middleware/compression.go:68.41,70.4 1 2 -github.com/ex0rcist/metflix/internal/middleware/compression.go:73.2,73.14 1 0 -github.com/ex0rcist/metflix/internal/middleware/request_logger.go:13.53,14.71 1 0 -github.com/ex0rcist/metflix/internal/middleware/request_logger.go:14.71,47.3 10 0 -github.com/ex0rcist/metflix/internal/middleware/request_logger.go:50.52,53.21 2 2 -github.com/ex0rcist/metflix/internal/middleware/request_logger.go:53.21,55.3 1 1 -github.com/ex0rcist/metflix/internal/middleware/request_logger.go:57.2,57.18 1 2 -github.com/ex0rcist/metflix/internal/middleware/security.go:22.61,24.2 1 1 -github.com/ex0rcist/metflix/internal/middleware/security.go:27.75,28.71 1 2 -github.com/ex0rcist/metflix/internal/middleware/security.go:28.71,31.23 2 2 -github.com/ex0rcist/metflix/internal/middleware/security.go:31.23,34.4 2 1 -github.com/ex0rcist/metflix/internal/middleware/security.go:37.3,55.17 9 1 -github.com/ex0rcist/metflix/internal/middleware/security.go:55.17,58.4 2 0 -github.com/ex0rcist/metflix/internal/middleware/security.go:63.81,64.71 1 3 -github.com/ex0rcist/metflix/internal/middleware/security.go:64.71,67.23 2 3 -github.com/ex0rcist/metflix/internal/middleware/security.go:67.23,70.4 2 1 -github.com/ex0rcist/metflix/internal/middleware/security.go:72.3,73.40 2 2 -github.com/ex0rcist/metflix/internal/middleware/security.go:73.40,77.4 3 0 -github.com/ex0rcist/metflix/internal/middleware/security.go:79.3,80.21 2 2 -github.com/ex0rcist/metflix/internal/middleware/security.go:80.21,84.4 2 0 -github.com/ex0rcist/metflix/internal/middleware/security.go:86.3,87.16 2 2 -github.com/ex0rcist/metflix/internal/middleware/security.go:87.16,88.51 1 2 -github.com/ex0rcist/metflix/internal/middleware/security.go:88.51,90.5 1 0 -github.com/ex0rcist/metflix/internal/middleware/security.go:93.3,93.17 1 2 -github.com/ex0rcist/metflix/internal/middleware/security.go:93.17,97.4 3 0 -github.com/ex0rcist/metflix/internal/middleware/security.go:99.3,101.10 3 2 -github.com/ex0rcist/metflix/internal/middleware/security.go:101.10,105.4 3 1 -github.com/ex0rcist/metflix/internal/middleware/security.go:107.3,110.23 3 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:38.28,47.16 3 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:47.16,49.3 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:51.2,52.16 2 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:52.16,54.3 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:56.2,60.8 1 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:64.23,76.2 7 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:78.51,81.6 2 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:81.6,83.17 2 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:83.17,85.4 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:87.3,87.57 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:91.34,94.6 2 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:94.6,98.3 2 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:101.31,146.51 7 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:146.51,148.3 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:150.2,151.16 2 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:151.16,153.3 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:155.2,158.41 2 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:162.33,170.23 2 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:170.23,172.3 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:174.2,174.51 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:177.43,180.9 2 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:181.23,182.28 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:183.10,184.26 1 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:187.2,187.11 1 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:190.68,195.28 4 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:195.28,197.3 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:199.2,201.22 2 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:202.28,203.79 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:204.26,205.59 1 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:206.10,207.54 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:210.2,210.17 1 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:213.40,227.34 9 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:227.34,228.17 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:229.18,230.28 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:231.17,232.26 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:236.2,236.42 1 1 -github.com/ex0rcist/metflix/internal/agent/agent.go:236.42,238.3 1 0 -github.com/ex0rcist/metflix/internal/agent/agent.go:240.2,240.12 1 1 -github.com/ex0rcist/metflix/internal/agent/runtime.go:41.31,72.2 29 5 -github.com/ex0rcist/metflix/internal/agent/stats.go:25.24,28.2 2 2 -github.com/ex0rcist/metflix/internal/agent/stats.go:31.49,39.20 5 3 -github.com/ex0rcist/metflix/internal/agent/stats.go:39.20,42.3 2 3 -github.com/ex0rcist/metflix/internal/agent/stats.go:44.2,44.20 1 3 -github.com/ex0rcist/metflix/internal/agent/stats.go:44.20,46.3 1 3 -github.com/ex0rcist/metflix/internal/agent/stats.go:48.2,48.33 1 3 -github.com/ex0rcist/metflix/internal/agent/stats.go:48.33,50.3 1 0 -github.com/ex0rcist/metflix/internal/agent/stats.go:52.2,52.12 1 3 -github.com/ex0rcist/metflix/internal/agent/system.go:21.55,23.16 2 3 -github.com/ex0rcist/metflix/internal/agent/system.go:23.16,25.3 1 0 -github.com/ex0rcist/metflix/internal/agent/system.go:27.2,31.16 4 3 -github.com/ex0rcist/metflix/internal/agent/system.go:31.16,33.3 1 0 -github.com/ex0rcist/metflix/internal/agent/system.go:35.2,36.32 2 3 -github.com/ex0rcist/metflix/internal/agent/system.go:36.32,38.3 1 24 -github.com/ex0rcist/metflix/internal/agent/system.go:40.2,40.12 1 3 -github.com/ex0rcist/metflix/internal/compression/compression.go:11.47,15.16 3 1 -github.com/ex0rcist/metflix/internal/compression/compression.go:15.16,17.3 1 0 -github.com/ex0rcist/metflix/internal/compression/compression.go:19.2,19.46 1 1 -github.com/ex0rcist/metflix/internal/compression/compression.go:19.46,21.3 1 0 -github.com/ex0rcist/metflix/internal/compression/compression.go:23.2,23.39 1 1 -github.com/ex0rcist/metflix/internal/compression/compression.go:23.39,25.3 1 0 -github.com/ex0rcist/metflix/internal/compression/compression.go:27.2,27.16 1 1 -github.com/ex0rcist/metflix/internal/compression/compressor.go:22.76,33.2 2 3 -github.com/ex0rcist/metflix/internal/compression/compressor.go:36.54,39.51 2 3 -github.com/ex0rcist/metflix/internal/compression/compressor.go:39.51,42.3 2 1 -github.com/ex0rcist/metflix/internal/compression/compressor.go:44.2,44.22 1 2 -github.com/ex0rcist/metflix/internal/compression/compressor.go:44.22,46.17 2 2 -github.com/ex0rcist/metflix/internal/compression/compressor.go:46.17,49.4 2 0 -github.com/ex0rcist/metflix/internal/compression/compressor.go:50.3,50.22 1 2 -github.com/ex0rcist/metflix/internal/compression/compressor.go:53.2,55.30 2 2 -github.com/ex0rcist/metflix/internal/compression/compressor.go:59.30,60.22 1 2 -github.com/ex0rcist/metflix/internal/compression/compressor.go:60.22,62.3 1 0 -github.com/ex0rcist/metflix/internal/compression/compressor.go:64.2,64.42 1 2 -github.com/ex0rcist/metflix/internal/compression/compressor.go:64.42,66.3 1 0 -github.com/ex0rcist/metflix/internal/compression/compressor.go:68.2,68.17 1 2 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:22.76,32.2 2 5 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:35.43,38.24 2 5 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:38.24,41.3 2 1 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:43.2,45.50 2 4 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:45.50,50.3 3 1 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:52.2,52.21 1 3 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:52.21,54.17 2 3 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:54.17,59.4 3 1 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:61.3,61.20 1 2 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:64.2,66.12 2 2 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:70.32,71.21 1 3 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:71.21,73.3 1 1 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:75.2,75.41 1 2 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:75.41,77.3 1 0 -github.com/ex0rcist/metflix/internal/compression/decompressor.go:79.2,79.16 1 2 -github.com/ex0rcist/metflix/docs/api/docs.go:372.13,374.2 1 1 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:13.68,16.69 2 20 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:16.69,18.3 1 8 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:20.2,20.19 1 12 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:21.27,22.23 1 7 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:22.23,24.4 1 1 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:26.3,26.59 1 6 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:27.25,28.23 1 5 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:28.23,30.4 1 1 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:32.3,32.59 1 4 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:33.10,34.43 1 0 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:37.2,37.20 1 10 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:40.79,43.29 2 6 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:44.27,46.21 2 3 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:48.25,50.21 2 3 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:53.2,53.17 1 6 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:56.88,59.33 2 1 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:59.33,61.17 2 2 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:61.17,63.4 1 0 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:65.3,65.18 1 2 -github.com/ex0rcist/metflix/internal/httpserver/converters.go:68.2,68.20 1 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:26.78,30.2 1 40 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:32.90,36.2 2 26 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:46.77,52.16 4 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:52.16,55.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:56.2,56.22 1 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:56.22,59.34 2 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:59.34,61.4 1 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:64.2,67.16 3 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:67.16,69.3 1 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:84.81,94.19 4 9 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:95.27,97.17 2 5 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:97.17,100.4 2 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:102.3,102.21 1 4 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:103.25,105.17 2 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:105.17,108.4 2 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:110.3,110.21 1 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:113.2,114.16 2 7 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:114.16,117.3 2 4 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:119.2,120.16 2 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:120.16,123.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:125.2,127.71 2 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:127.71,130.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:143.85,147.62 3 6 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:147.62,148.20 1 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:148.20,150.4 1 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:152.3,153.9 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:156.2,157.16 2 6 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:157.16,160.3 2 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:162.2,163.16 2 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:163.16,166.3 2 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:168.2,169.16 2 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:169.16,172.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:174.2,176.56 2 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:176.56,179.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:192.87,196.16 3 6 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:196.16,197.20 1 4 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:197.20,199.4 1 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:201.3,202.9 2 4 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:205.2,206.16 2 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:206.16,209.3 2 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:211.2,212.16 2 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:212.16,215.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:217.2,219.57 2 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:219.57,222.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:224.2,224.44 1 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:239.78,245.74 4 5 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:245.74,248.3 2 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:250.2,252.16 3 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:252.16,255.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:257.2,262.16 4 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:262.16,265.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:280.82,284.62 3 8 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:284.62,287.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:289.2,289.69 1 8 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:289.69,292.3 2 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:294.2,295.16 2 5 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:295.16,298.3 2 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:300.2,301.16 2 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:301.16,304.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:306.2,308.56 2 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:308.56,311.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:314.70,316.61 2 6 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:316.61,318.3 1 0 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:320.2,322.21 2 6 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:322.21,324.17 2 7 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:324.17,326.4 1 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:328.3,328.22 1 4 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:331.2,331.23 1 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:331.23,333.3 1 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:335.2,335.21 1 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:344.63,348.2 1 40 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:358.71,362.16 3 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:362.16,364.3 1 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:366.2,366.51 1 2 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:366.51,369.3 2 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:371.2,371.65 1 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:374.33,375.13 1 18 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:376.65,377.29 1 3 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:381.34,383.31 1 14 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:384.30,385.40 1 1 -github.com/ex0rcist/metflix/internal/httpserver/handlers.go:386.10,387.40 1 0 -github.com/ex0rcist/metflix/internal/httpserver/router.go:37.16,45.50 5 40 -github.com/ex0rcist/metflix/internal/httpserver/router.go:45.50,47.3 1 40 -github.com/ex0rcist/metflix/internal/httpserver/router.go:49.2,52.50 3 40 -github.com/ex0rcist/metflix/internal/httpserver/router.go:52.50,54.3 1 40 -github.com/ex0rcist/metflix/internal/httpserver/router.go:56.2,56.80 1 40 -github.com/ex0rcist/metflix/internal/httpserver/router.go:56.80,58.3 1 1 -github.com/ex0rcist/metflix/internal/httpserver/router.go:60.2,65.15 4 40 -github.com/ex0rcist/metflix/internal/httpserver/router.go:68.87,79.2 7 40 -github.com/ex0rcist/metflix/internal/httpserver/router.go:81.77,85.2 2 40 -github.com/ex0rcist/metflix/internal/httpserver/server.go:17.66,29.2 3 0 -github.com/ex0rcist/metflix/internal/httpserver/server.go:32.26,33.12 1 0 -github.com/ex0rcist/metflix/internal/httpserver/server.go:33.12,36.3 2 0 -github.com/ex0rcist/metflix/internal/httpserver/server.go:40.40,42.2 1 0 -github.com/ex0rcist/metflix/internal/httpserver/server.go:45.54,46.21 1 0 -github.com/ex0rcist/metflix/internal/httpserver/server.go:46.21,48.3 1 0 -github.com/ex0rcist/metflix/internal/httpserver/server.go:50.2,50.31 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:26.30,27.17 1 1 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:27.17,29.3 1 1 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:30.2,30.25 1 1 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:34.40,37.16 2 1 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:37.16,39.3 1 1 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:41.2,45.16 4 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:45.16,46.13 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:49.2,49.15 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:49.15,51.17 2 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:51.17,53.4 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:56.2,57.50 2 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:57.50,58.13 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:61.2,61.54 1 0 -github.com/ex0rcist/metflix/internal/logging/debug.go:12.44,15.2 2 0 -github.com/ex0rcist/metflix/internal/logging/debug.go:18.35,20.2 1 0 -github.com/ex0rcist/metflix/internal/logging/debug.go:23.59,26.2 2 0 -github.com/ex0rcist/metflix/internal/logging/debug.go:28.59,31.2 2 0 -github.com/ex0rcist/metflix/internal/logging/error.go:12.42,15.2 2 0 -github.com/ex0rcist/metflix/internal/logging/error.go:18.46,20.2 1 0 -github.com/ex0rcist/metflix/internal/logging/error.go:23.70,26.2 2 0 -github.com/ex0rcist/metflix/internal/logging/error.go:28.70,31.20 2 0 -github.com/ex0rcist/metflix/internal/logging/error.go:31.20,33.3 1 0 -github.com/ex0rcist/metflix/internal/logging/error.go:33.8,35.3 1 0 -github.com/ex0rcist/metflix/internal/logging/fatal.go:12.42,15.2 2 0 -github.com/ex0rcist/metflix/internal/logging/fatal.go:18.46,20.2 1 0 -github.com/ex0rcist/metflix/internal/logging/fatal.go:23.70,26.2 2 0 -github.com/ex0rcist/metflix/internal/logging/fatal.go:28.70,31.20 2 0 -github.com/ex0rcist/metflix/internal/logging/fatal.go:31.20,33.3 1 0 -github.com/ex0rcist/metflix/internal/logging/fatal.go:33.8,35.3 1 0 -github.com/ex0rcist/metflix/internal/logging/info.go:12.43,15.2 2 0 -github.com/ex0rcist/metflix/internal/logging/info.go:18.34,20.2 1 0 -github.com/ex0rcist/metflix/internal/logging/info.go:23.58,26.2 2 0 -github.com/ex0rcist/metflix/internal/logging/info.go:28.58,31.2 2 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:22.14,28.17 4 1 -github.com/ex0rcist/metflix/internal/logging/logging.go:29.17,33.79 3 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:34.21,37.79 2 1 -github.com/ex0rcist/metflix/internal/logging/logging.go:38.20,41.21 2 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:44.2,45.9 2 1 -github.com/ex0rcist/metflix/internal/logging/logging.go:46.22,47.39 1 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:48.10,49.30 1 1 -github.com/ex0rcist/metflix/internal/logging/logging.go:52.2,53.40 2 1 -github.com/ex0rcist/metflix/internal/logging/logging.go:56.27,58.40 2 1 -github.com/ex0rcist/metflix/internal/logging/logging.go:58.40,60.3 1 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:62.2,62.12 1 1 -github.com/ex0rcist/metflix/internal/logging/logging.go:65.52,66.24 1 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:66.24,68.3 1 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:71.2,72.31 2 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:72.31,73.16 1 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:73.16,75.4 1 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:78.2,78.35 1 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:81.61,83.2 1 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:85.26,87.2 1 0 -github.com/ex0rcist/metflix/internal/logging/logging.go:89.26,91.2 1 1 -github.com/ex0rcist/metflix/internal/logging/warn.go:12.43,15.2 2 0 -github.com/ex0rcist/metflix/internal/logging/warn.go:18.34,20.2 1 0 -github.com/ex0rcist/metflix/internal/logging/warn.go:23.58,26.2 2 0 -github.com/ex0rcist/metflix/internal/logging/warn.go:28.58,31.2 2 0 -github.com/ex0rcist/metflix/models/metrics_metric_exchange.go:34.73,36.2 1 0 -github.com/ex0rcist/metflix/models/metrics_metric_exchange.go:39.101,41.2 1 0 -github.com/ex0rcist/metflix/models/metrics_metric_exchange.go:44.65,45.14 1 0 -github.com/ex0rcist/metflix/models/metrics_metric_exchange.go:45.14,47.3 1 0 -github.com/ex0rcist/metflix/models/metrics_metric_exchange.go:48.2,48.26 1 0 -github.com/ex0rcist/metflix/models/metrics_metric_exchange.go:52.65,54.47 2 0 -github.com/ex0rcist/metflix/models/metrics_metric_exchange.go:54.47,56.3 1 0 -github.com/ex0rcist/metflix/models/metrics_metric_exchange.go:57.2,58.12 2 0 -github.com/ex0rcist/metflix/internal/retrier/retrier.go:21.30,25.79 1 3 -github.com/ex0rcist/metflix/internal/retrier/retrier.go:25.79,28.4 2 4 -github.com/ex0rcist/metflix/internal/retrier/retrier.go:34.53,35.26 1 3 -github.com/ex0rcist/metflix/internal/retrier/retrier.go:35.26,37.3 1 3 -github.com/ex0rcist/metflix/internal/retrier/retrier.go:41.96,47.27 2 3 -github.com/ex0rcist/metflix/internal/retrier/retrier.go:47.27,49.3 1 3 -github.com/ex0rcist/metflix/internal/retrier/retrier.go:51.2,51.10 1 3 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:33.89,43.2 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:46.73,47.18 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:47.18,49.3 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:51.2,52.22 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:53.27,54.67 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:56.25,57.63 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:59.10,61.11 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:64.2,66.10 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:70.38,71.18 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:71.18,73.3 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:75.2,75.24 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:75.24,77.3 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:79.2,82.16 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:82.16,82.37 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:83.24,86.4 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:90.2,92.12 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:96.33,99.2 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:102.39,103.18 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:103.18,105.3 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:107.2,107.55 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:110.40,115.16 4 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:115.16,118.3 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:120.2,121.16 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:121.16,124.3 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:126.2,128.16 3 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:128.16,131.3 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:133.2,137.21 4 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:137.21,139.21 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:139.21,142.4 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:144.3,144.42 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:147.2,150.16 3 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:150.16,153.3 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:155.2,155.15 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:155.15,156.53 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:156.53,158.4 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:161.2,162.16 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:162.16,165.3 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:167.2,169.38 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:169.38,173.3 3 0 -github.com/ex0rcist/metflix/internal/agent/exporter/batch_exporter.go:175.2,175.12 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/exporter.go:29.55,38.2 3 3 -github.com/ex0rcist/metflix/internal/agent/exporter/exporter.go:40.84,43.2 2 3 -github.com/ex0rcist/metflix/internal/agent/exporter/exporter.go:45.77,47.2 1 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:34.109,49.2 4 7 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:52.75,53.18 1 4 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:53.18,55.3 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:57.2,58.22 2 4 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:59.27,60.67 1 4 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:62.25,63.63 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:65.10,69.11 3 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:72.2,74.10 2 4 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:78.40,79.18 1 3 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:79.18,81.3 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:83.2,83.24 1 3 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:83.24,85.3 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:87.2,89.31 2 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:89.31,91.3 1 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:93.2,95.12 2 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:99.35,102.2 2 3 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:105.41,106.18 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:106.18,108.3 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:110.2,110.55 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:113.56,114.35 1 7 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:114.35,116.3 1 11 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:119.42,122.26 2 11 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:122.26,126.17 2 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:126.17,126.41 1 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:127.25,130.5 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:134.3,134.17 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:134.17,136.4 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:138.3,138.48 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:142.68,147.16 4 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:147.16,150.3 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:152.2,153.16 2 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:153.16,156.3 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:158.2,161.16 3 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:161.16,164.3 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:166.2,170.21 4 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:170.21,172.21 2 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:172.21,175.4 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:177.3,177.42 1 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:180.2,183.16 3 2 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:183.16,186.3 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:188.2,188.15 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:188.15,189.53 1 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:189.53,191.4 1 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:194.2,195.16 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:195.16,198.3 2 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:200.2,202.38 2 1 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:202.38,206.3 3 0 -github.com/ex0rcist/metflix/internal/agent/exporter/limited_exporter.go:208.2,208.12 1 1 -github.com/ex0rcist/metflix/internal/services/pinger.go:25.69,27.2 1 3 -github.com/ex0rcist/metflix/internal/services/pinger.go:30.56,35.9 4 3 -github.com/ex0rcist/metflix/internal/services/pinger.go:35.9,37.3 1 1 -github.com/ex0rcist/metflix/internal/services/pinger.go:39.2,39.39 1 2 -github.com/ex0rcist/metflix/internal/services/pinger.go:39.39,41.3 1 1 -github.com/ex0rcist/metflix/internal/services/pinger.go:43.2,43.12 1 1 -github.com/ex0rcist/metflix/internal/services/pinger_mock.go:17.54,20.2 2 0 -github.com/ex0rcist/metflix/internal/services/signer.go:25.61,27.2 1 3 -github.com/ex0rcist/metflix/internal/services/signer.go:30.72,34.16 3 3 -github.com/ex0rcist/metflix/internal/services/signer.go:34.16,36.3 1 0 -github.com/ex0rcist/metflix/internal/services/signer.go:37.2,39.40 2 3 -github.com/ex0rcist/metflix/internal/services/signer.go:43.80,44.20 1 2 -github.com/ex0rcist/metflix/internal/services/signer.go:44.20,46.3 1 0 -github.com/ex0rcist/metflix/internal/services/signer.go:48.2,49.16 2 2 -github.com/ex0rcist/metflix/internal/services/signer.go:49.16,51.3 1 0 -github.com/ex0rcist/metflix/internal/services/signer.go:53.2,53.30 1 2 -github.com/ex0rcist/metflix/pkg/staticlint/staticlint.go:101.23,145.4 2 0 -github.com/ex0rcist/metflix/pkg/staticlint/staticlint.go:145.4,146.32 1 0 -github.com/ex0rcist/metflix/pkg/staticlint/staticlint.go:146.32,147.62 1 0 -github.com/ex0rcist/metflix/pkg/staticlint/staticlint.go:147.62,148.13 1 0 -github.com/ex0rcist/metflix/pkg/staticlint/staticlint.go:151.4,151.43 1 0 -github.com/ex0rcist/metflix/pkg/staticlint/staticlint.go:156.2,163.29 5 0 -github.com/ex0rcist/metflix/pkg/staticlint/staticlint.go:167.27,169.2 1 0 -github.com/ex0rcist/metflix/docs/api/docs.go:372.13,374.2 1 1 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:26.30,27.17 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:27.17,29.3 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:30.2,30.25 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:34.40,37.16 2 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:37.16,39.3 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:41.2,45.16 4 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:45.16,46.13 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:49.2,49.15 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:49.15,51.17 2 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:51.17,53.4 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:56.2,57.50 2 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:57.50,58.13 1 0 -github.com/ex0rcist/metflix/internal/profiler/profiler.go:61.2,61.54 1 0 -github.com/ex0rcist/metflix/internal/server/profile_server.go:16.66,27.2 6 2 -github.com/ex0rcist/metflix/internal/server/server.go:45.29,54.16 3 2 -github.com/ex0rcist/metflix/internal/server/server.go:54.16,56.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:58.2,59.16 2 2 -github.com/ex0rcist/metflix/internal/server/server.go:59.16,61.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:63.2,76.8 6 2 -github.com/ex0rcist/metflix/internal/server/server.go:80.26,90.9 7 0 -github.com/ex0rcist/metflix/internal/server/server.go:91.24,92.53 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:93.38,94.68 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:95.36,96.65 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:99.2,105.12 5 0 -github.com/ex0rcist/metflix/internal/server/server.go:105.12,108.3 2 0 -github.com/ex0rcist/metflix/internal/server/server.go:110.2,110.9 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:111.17,112.48 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:114.24,115.47 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:120.34,128.30 3 1 -github.com/ex0rcist/metflix/internal/server/server.go:128.30,132.3 3 0 -github.com/ex0rcist/metflix/internal/server/server.go:134.2,134.34 1 1 -github.com/ex0rcist/metflix/internal/server/server.go:134.34,136.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:138.2,138.30 1 1 -github.com/ex0rcist/metflix/internal/server/server.go:138.30,140.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:142.2,142.52 1 1 -github.com/ex0rcist/metflix/internal/server/server.go:145.48,147.51 2 0 -github.com/ex0rcist/metflix/internal/server/server.go:147.51,149.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:151.2,152.45 2 0 -github.com/ex0rcist/metflix/internal/server/server.go:152.45,154.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:156.2,157.49 2 0 -github.com/ex0rcist/metflix/internal/server/server.go:157.49,159.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:162.40,164.16 2 2 -github.com/ex0rcist/metflix/internal/server/server.go:164.16,166.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:168.2,169.16 2 2 -github.com/ex0rcist/metflix/internal/server/server.go:169.16,171.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:173.2,173.12 1 2 -github.com/ex0rcist/metflix/internal/server/server.go:176.71,192.16 11 4 -github.com/ex0rcist/metflix/internal/server/server.go:192.16,194.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:197.2,197.34 1 4 -github.com/ex0rcist/metflix/internal/server/server.go:197.34,198.17 1 2 -github.com/ex0rcist/metflix/internal/server/server.go:199.18,200.28 1 2 -github.com/ex0rcist/metflix/internal/server/server.go:201.17,202.26 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:206.2,206.12 1 4 -github.com/ex0rcist/metflix/internal/server/server.go:209.37,210.42 1 2 -github.com/ex0rcist/metflix/internal/server/server.go:210.42,212.3 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:214.2,214.12 1 2 -github.com/ex0rcist/metflix/internal/server/server.go:217.42,220.9 2 3 -github.com/ex0rcist/metflix/internal/server/server.go:221.27,222.28 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:223.25,224.24 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:225.10,226.26 1 3 -github.com/ex0rcist/metflix/internal/server/server.go:229.2,229.11 1 3 -github.com/ex0rcist/metflix/internal/server/server.go:232.69,235.21 2 2 -github.com/ex0rcist/metflix/internal/server/server.go:236.26,237.38 1 2 -github.com/ex0rcist/metflix/internal/server/server.go:238.24,239.95 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:240.28,241.56 1 0 -github.com/ex0rcist/metflix/internal/server/server.go:242.10,243.49 1 0 -github.com/ex0rcist/metflix/internal/utils/http.go:11.47,14.36 2 4 -github.com/ex0rcist/metflix/internal/utils/http.go:14.36,15.32 1 4 -github.com/ex0rcist/metflix/internal/utils/http.go:15.32,17.4 1 5 -github.com/ex0rcist/metflix/internal/utils/http.go:20.2,20.47 1 4 -github.com/ex0rcist/metflix/internal/utils/http.go:20.47,22.3 1 2 -github.com/ex0rcist/metflix/internal/utils/http.go:24.2,24.41 1 4 -github.com/ex0rcist/metflix/internal/utils/request_id.go:8.33,10.2 1 1 -github.com/ex0rcist/metflix/internal/utils/time.go:6.41,8.2 1 5 -github.com/ex0rcist/metflix/internal/validators/validators.go:13.46,14.49 1 7 -github.com/ex0rcist/metflix/internal/validators/validators.go:14.49,16.3 1 4 -github.com/ex0rcist/metflix/internal/validators/validators.go:18.2,18.49 1 3 -github.com/ex0rcist/metflix/internal/validators/validators.go:18.49,20.3 1 1 -github.com/ex0rcist/metflix/internal/validators/validators.go:22.2,22.12 1 2 -github.com/ex0rcist/metflix/internal/validators/validators.go:25.44,26.20 1 7 -github.com/ex0rcist/metflix/internal/validators/validators.go:26.20,28.3 1 2 -github.com/ex0rcist/metflix/internal/validators/validators.go:30.2,30.35 1 5 -github.com/ex0rcist/metflix/internal/validators/validators.go:30.35,32.3 1 2 -github.com/ex0rcist/metflix/internal/validators/validators.go:34.2,34.12 1 3 -github.com/ex0rcist/metflix/internal/validators/validators.go:37.44,38.14 1 3 -github.com/ex0rcist/metflix/internal/validators/validators.go:39.46,40.13 1 2 -github.com/ex0rcist/metflix/internal/validators/validators.go:42.10,43.35 1 1 -github.com/ex0rcist/metflix/pkg/metrics/metrics.go:5.69,7.2 1 0 -github.com/ex0rcist/metflix/pkg/metrics/metrics.go:10.65,12.2 1 0 -github.com/ex0rcist/metflix/pkg/metrics/metrics.go:15.51,17.2 1 0 -github.com/ex0rcist/metflix/pkg/metrics/metrics.go:20.49,22.2 1 0 -github.com/ex0rcist/metflix/pkg/metrics/types.go:25.32,27.2 1 1 -github.com/ex0rcist/metflix/pkg/metrics/types.go:30.34,32.2 1 1 -github.com/ex0rcist/metflix/pkg/metrics/types.go:38.30,40.2 1 1 -github.com/ex0rcist/metflix/pkg/metrics/types.go:43.32,45.2 1 2 -github.com/ex0rcist/metflix/pkg/metrics/types.go:48.47,50.16 2 0 -github.com/ex0rcist/metflix/pkg/metrics/types.go:50.16,52.3 1 0 -github.com/ex0rcist/metflix/pkg/metrics/types.go:54.2,54.31 1 0 -github.com/ex0rcist/metflix/pkg/metrics/types.go:58.43,60.16 2 0 -github.com/ex0rcist/metflix/pkg/metrics/types.go:60.16,62.3 1 0 -github.com/ex0rcist/metflix/pkg/metrics/types.go:64.2,64.29 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:29.126,32.2 2 0 -github.com/ex0rcist/metflix/internal/storage/database.go:35.110,37.2 0 0 -github.com/ex0rcist/metflix/internal/storage/database.go:40.63,43.39 2 0 -github.com/ex0rcist/metflix/internal/storage/database.go:43.39,45.3 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:47.2,48.16 2 0 -github.com/ex0rcist/metflix/internal/storage/database.go:48.16,50.3 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:52.2,56.16 4 0 -github.com/ex0rcist/metflix/internal/storage/database.go:56.16,58.3 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:60.2,60.42 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:64.85,66.16 2 1 -github.com/ex0rcist/metflix/internal/storage/database.go:66.16,68.3 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:70.2,72.16 3 1 -github.com/ex0rcist/metflix/internal/storage/database.go:72.16,74.18 2 0 -github.com/ex0rcist/metflix/internal/storage/database.go:74.18,76.4 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:77.3,77.66 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:80.2,80.23 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:84.86,87.31 3 1 -github.com/ex0rcist/metflix/internal/storage/database.go:87.31,89.3 1 2 -github.com/ex0rcist/metflix/internal/storage/database.go:91.2,92.15 2 1 -github.com/ex0rcist/metflix/internal/storage/database.go:92.15,93.43 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:93.43,95.4 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:98.2,98.33 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:98.33,99.45 1 2 -github.com/ex0rcist/metflix/internal/storage/database.go:99.45,101.4 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:104.2,104.12 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:108.79,120.16 4 1 -github.com/ex0rcist/metflix/internal/storage/database.go:120.16,121.36 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:121.36,123.4 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:125.3,125.63 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:128.2,128.14 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:129.27,130.71 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:131.25,132.69 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:133.10,134.55 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:137.2,137.20 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:141.70,143.16 2 1 -github.com/ex0rcist/metflix/internal/storage/database.go:143.16,145.3 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:147.2,147.41 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:147.41,149.3 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:151.2,160.74 4 1 -github.com/ex0rcist/metflix/internal/storage/database.go:160.74,161.15 1 2 -github.com/ex0rcist/metflix/internal/storage/database.go:162.28,164.14 2 1 -github.com/ex0rcist/metflix/internal/storage/database.go:166.26,168.14 2 1 -github.com/ex0rcist/metflix/internal/storage/database.go:170.11,171.57 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:175.2,175.16 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:175.16,177.3 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:179.2,179.20 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:183.58,184.41 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:184.41,186.3 1 0 -github.com/ex0rcist/metflix/internal/storage/database.go:188.2,188.12 1 1 -github.com/ex0rcist/metflix/internal/storage/database.go:192.59,195.2 2 1 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:26.83,28.2 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:31.39,35.16 2 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:35.16,41.18 4 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:41.18,43.5 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:45.4,45.14 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:47.24,49.4 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:53.2,53.18 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:53.18,55.3 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:57.2,59.15 2 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:59.15,62.20 2 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:62.20,64.4 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:66.3,66.19 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:66.19,68.4 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:71.2,71.18 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:71.18,74.3 2 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:76.2,76.43 1 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:76.43,79.3 2 0 -github.com/ex0rcist/metflix/internal/storage/database_migrator.go:81.2,81.14 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:29.101,37.23 2 5 -github.com/ex0rcist/metflix/internal/storage/file.go:37.23,38.38 1 1 -github.com/ex0rcist/metflix/internal/storage/file.go:38.38,40.4 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:43.2,43.26 1 5 -github.com/ex0rcist/metflix/internal/storage/file.go:43.26,46.3 2 1 -github.com/ex0rcist/metflix/internal/storage/file.go:48.2,48.16 1 5 -github.com/ex0rcist/metflix/internal/storage/file.go:52.81,53.59 1 3 -github.com/ex0rcist/metflix/internal/storage/file.go:53.59,55.3 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:57.2,57.26 1 3 -github.com/ex0rcist/metflix/internal/storage/file.go:57.26,59.3 1 2 -github.com/ex0rcist/metflix/internal/storage/file.go:61.2,61.12 1 1 -github.com/ex0rcist/metflix/internal/storage/file.go:65.83,66.57 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:66.57,68.3 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:70.2,70.26 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:70.26,72.3 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:74.2,74.12 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:78.54,79.25 1 1 -github.com/ex0rcist/metflix/internal/storage/file.go:79.25,81.3 1 1 -github.com/ex0rcist/metflix/internal/storage/file.go:83.2,83.17 1 1 -github.com/ex0rcist/metflix/internal/storage/file.go:86.42,93.16 5 4 -github.com/ex0rcist/metflix/internal/storage/file.go:93.16,95.3 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:97.2,97.15 1 4 -github.com/ex0rcist/metflix/internal/storage/file.go:97.15,98.62 1 4 -github.com/ex0rcist/metflix/internal/storage/file.go:98.62,100.4 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:103.2,106.49 3 4 -github.com/ex0rcist/metflix/internal/storage/file.go:106.49,108.3 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:110.2,110.12 1 4 -github.com/ex0rcist/metflix/internal/storage/file.go:113.45,120.16 5 1 -github.com/ex0rcist/metflix/internal/storage/file.go:120.16,121.25 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:121.25,124.4 2 0 -github.com/ex0rcist/metflix/internal/storage/file.go:126.3,126.77 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:129.2,129.15 1 1 -github.com/ex0rcist/metflix/internal/storage/file.go:129.15,130.62 1 1 -github.com/ex0rcist/metflix/internal/storage/file.go:130.62,132.4 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:135.2,136.53 2 1 -github.com/ex0rcist/metflix/internal/storage/file.go:136.53,138.3 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:140.2,142.12 2 1 -github.com/ex0rcist/metflix/internal/storage/file.go:145.64,148.6 2 1 -github.com/ex0rcist/metflix/internal/storage/file.go:148.6,150.10 2 2 -github.com/ex0rcist/metflix/internal/storage/file.go:150.10,151.9 1 0 -github.com/ex0rcist/metflix/internal/storage/file.go:154.3,154.34 1 1 -github.com/ex0rcist/metflix/internal/storage/file.go:154.34,156.4 1 0 -github.com/ex0rcist/metflix/internal/storage/memory.go:19.34,23.2 1 11 -github.com/ex0rcist/metflix/internal/storage/memory.go:26.78,33.2 4 10 -github.com/ex0rcist/metflix/internal/storage/memory.go:36.80,40.31 3 1 -github.com/ex0rcist/metflix/internal/storage/memory.go:40.31,42.3 1 2 -github.com/ex0rcist/metflix/internal/storage/memory.go:44.2,44.12 1 1 -github.com/ex0rcist/metflix/internal/storage/memory.go:48.72,53.9 4 11 -github.com/ex0rcist/metflix/internal/storage/memory.go:53.9,55.3 1 1 -github.com/ex0rcist/metflix/internal/storage/memory.go:57.2,57.20 1 10 -github.com/ex0rcist/metflix/internal/storage/memory.go:61.64,68.32 5 1 -github.com/ex0rcist/metflix/internal/storage/memory.go:68.32,71.3 2 2 -github.com/ex0rcist/metflix/internal/storage/memory.go:73.2,73.17 1 1 -github.com/ex0rcist/metflix/internal/storage/memory.go:77.45,83.27 4 4 -github.com/ex0rcist/metflix/internal/storage/memory.go:83.27,85.3 1 4 -github.com/ex0rcist/metflix/internal/storage/memory.go:87.2,87.36 1 4 -github.com/ex0rcist/metflix/internal/storage/memory.go:91.53,93.2 1 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:20.36,22.2 1 6 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:25.66,28.2 2 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:31.81,34.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:37.101,40.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:43.90,46.2 2 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:49.55,52.2 2 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:55.93,58.2 2 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:61.86,64.2 2 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:67.31,69.2 1 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:79.65,82.2 2 2 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:85.57,88.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:91.50,94.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:97.45,100.2 2 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:110.46,113.2 2 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:123.31,125.2 1 2 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:128.35,131.2 2 3 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:134.47,137.2 2 2 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:140.35,143.2 2 2 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:146.54,149.2 2 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:152.69,155.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:158.47,161.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:164.44,167.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:170.40,173.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:183.64,186.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:189.55,192.2 2 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:195.57,198.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:201.141,204.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:207.83,210.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:213.53,216.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:219.106,222.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:225.114,229.2 3 1 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:232.91,235.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:238.84,241.2 2 0 -github.com/ex0rcist/metflix/internal/storage/pgx_pool_mock.go:244.38,247.2 2 0 -github.com/ex0rcist/metflix/internal/storage/record.go:18.50,19.38 1 44 -github.com/ex0rcist/metflix/internal/storage/record.go:19.38,21.3 1 5 -github.com/ex0rcist/metflix/internal/storage/record.go:23.2,23.26 1 39 -github.com/ex0rcist/metflix/internal/storage/record.go:27.44,29.2 1 38 -github.com/ex0rcist/metflix/internal/storage/record.go:32.47,39.16 2 6 -github.com/ex0rcist/metflix/internal/storage/record.go:39.16,41.3 1 0 -github.com/ex0rcist/metflix/internal/storage/record.go:43.2,43.16 1 6 -github.com/ex0rcist/metflix/internal/storage/record.go:47.50,50.51 2 9 -github.com/ex0rcist/metflix/internal/storage/record.go:50.51,52.3 1 1 -github.com/ex0rcist/metflix/internal/storage/record.go:54.2,56.22 2 8 -github.com/ex0rcist/metflix/internal/storage/record.go:57.27,59.17 2 5 -github.com/ex0rcist/metflix/internal/storage/record.go:59.17,61.4 1 1 -github.com/ex0rcist/metflix/internal/storage/record.go:63.3,63.18 1 4 -github.com/ex0rcist/metflix/internal/storage/record.go:64.25,66.17 2 2 -github.com/ex0rcist/metflix/internal/storage/record.go:66.17,68.4 1 1 -github.com/ex0rcist/metflix/internal/storage/record.go:70.3,70.18 1 1 -github.com/ex0rcist/metflix/internal/storage/record.go:71.10,72.81 1 1 -github.com/ex0rcist/metflix/internal/storage/record.go:75.2,75.12 1 5 -github.com/ex0rcist/metflix/internal/storage/service.go:29.49,31.2 1 17 -github.com/ex0rcist/metflix/internal/storage/service.go:34.78,38.16 3 2 -github.com/ex0rcist/metflix/internal/storage/service.go:38.16,40.3 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:42.2,42.20 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:46.75,48.16 2 7 -github.com/ex0rcist/metflix/internal/storage/service.go:48.16,50.3 1 2 -github.com/ex0rcist/metflix/internal/storage/service.go:52.2,55.16 3 5 -github.com/ex0rcist/metflix/internal/storage/service.go:55.16,57.3 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:59.2,59.20 1 4 -github.com/ex0rcist/metflix/internal/storage/service.go:63.84,66.33 2 1 -github.com/ex0rcist/metflix/internal/storage/service.go:66.33,69.31 2 3 -github.com/ex0rcist/metflix/internal/storage/service.go:69.31,70.50 1 0 -github.com/ex0rcist/metflix/internal/storage/service.go:70.50,72.5 1 0 -github.com/ex0rcist/metflix/internal/storage/service.go:74.4,76.12 2 0 -github.com/ex0rcist/metflix/internal/storage/service.go:79.3,80.17 2 3 -github.com/ex0rcist/metflix/internal/storage/service.go:80.17,82.4 1 0 -github.com/ex0rcist/metflix/internal/storage/service.go:84.3,85.20 2 3 -github.com/ex0rcist/metflix/internal/storage/service.go:88.2,88.54 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:88.54,90.3 1 0 -github.com/ex0rcist/metflix/internal/storage/service.go:92.2,93.25 2 1 -github.com/ex0rcist/metflix/internal/storage/service.go:93.25,95.3 1 3 -github.com/ex0rcist/metflix/internal/storage/service.go:97.2,97.41 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:97.41,99.3 1 3 -github.com/ex0rcist/metflix/internal/storage/service.go:101.2,101.20 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:105.62,107.16 2 2 -github.com/ex0rcist/metflix/internal/storage/service.go:107.16,109.3 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:111.2,111.42 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:111.42,113.3 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:115.2,115.21 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:118.96,119.48 1 15 -github.com/ex0rcist/metflix/internal/storage/service.go:119.48,121.3 1 6 -github.com/ex0rcist/metflix/internal/storage/service.go:123.2,124.14 2 9 -github.com/ex0rcist/metflix/internal/storage/service.go:124.14,126.3 1 1 -github.com/ex0rcist/metflix/internal/storage/service.go:128.2,129.48 2 8 -github.com/ex0rcist/metflix/internal/storage/service.go:129.48,131.3 1 3 -github.com/ex0rcist/metflix/internal/storage/service.go:131.8,131.23 1 5 -github.com/ex0rcist/metflix/internal/storage/service.go:131.23,133.3 1 2 -github.com/ex0rcist/metflix/internal/storage/service.go:135.2,135.83 1 3 -github.com/ex0rcist/metflix/internal/storage/service_mock.go:17.83,20.2 2 0 -github.com/ex0rcist/metflix/internal/storage/service_mock.go:23.80,26.2 2 0 -github.com/ex0rcist/metflix/internal/storage/service_mock.go:29.89,32.2 2 0 -github.com/ex0rcist/metflix/internal/storage/service_mock.go:35.67,38.24 2 0 -github.com/ex0rcist/metflix/internal/storage/service_mock.go:38.24,40.3 1 0 -github.com/ex0rcist/metflix/internal/storage/service_mock.go:42.2,42.46 1 0 -github.com/ex0rcist/metflix/internal/storage/storage_mock.go:17.75,20.2 2 10 -github.com/ex0rcist/metflix/internal/storage/storage_mock.go:23.81,26.2 2 5 -github.com/ex0rcist/metflix/internal/storage/storage_mock.go:29.83,32.2 2 1 -github.com/ex0rcist/metflix/internal/storage/storage_mock.go:35.67,38.24 2 2 -github.com/ex0rcist/metflix/internal/storage/storage_mock.go:38.24,40.3 1 0 -github.com/ex0rcist/metflix/internal/storage/storage_mock.go:42.2,42.46 1 2 -github.com/ex0rcist/metflix/internal/storage/storage_mock.go:46.56,50.2 2 0 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:20.42,21.46 1 7 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:21.46,23.3 1 0 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:25.2,27.52 2 7 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:30.59,32.9 2 12 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:32.9,34.3 1 2 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:36.2,37.9 2 10 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:37.9,39.3 1 2 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:41.2,42.44 2 8 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:42.44,44.3 1 8 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:46.2,46.52 1 8 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:46.52,49.3 2 7 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:52.64,54.2 1 7 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:56.60,57.22 1 1 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:57.22,59.3 1 1 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:62.66,63.22 1 4 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:63.22,65.3 1 4 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:68.52,69.34 1 10 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:69.34,72.85 2 9 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:72.85,73.12 1 2 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:76.3,76.51 1 7 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:76.51,77.12 1 1 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:80.3,80.46 1 6 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:80.46,81.28 1 334 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:82.23,83.25 1 7 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:85.23,86.30 1 7 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:88.21,89.28 1 1 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:91.24,92.31 1 4 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:94.22,95.17 1 1 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:98.4,98.15 1 326 -github.com/ex0rcist/metflix/pkg/noexit/analyzer.go:102.2,102.17 1 10 diff --git a/internal/agent/agent.go b/internal/agent/agent.go index 6242cfa..aa9a92d 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -229,6 +229,11 @@ func newMetricsExporter(config *Config, publicKey security.PublicKey) (exporter. } func parseConfig(config *Config) error { + err := tryLoadJSONConfig(config) + if err != nil { + return err + } + address := config.Address pflag.VarP(&address, "address", "a", "address:port for HTTP API requests") @@ -238,7 +243,7 @@ func parseConfig(config *Config) error { publicKeyPath := config.PublicKeyPath pflag.VarP(&publicKeyPath, "crypto-key", "", "path to public key to encrypt agent -> server communications") - configPath := entities.FilePath("") + configPath := entities.FilePath("") // register var for compatibility pflag.VarP(&configPath, "config", "c", "path to configuration file in JSON format") pflag.IntVarP(&config.PollInterval, "poll-interval", "p", config.PollInterval, "interval (s) for polling stats") @@ -247,13 +252,6 @@ func parseConfig(config *Config) error { pflag.Parse() - if len(configPath) != 0 { - if err := loadConfigFromFile(configPath, config); err != nil { - return err - } - } - - // because VarP gets non-pointer value, set it manually pflag.Visit(func(f *pflag.Flag) { switch f.Name { case "address": @@ -272,6 +270,25 @@ func parseConfig(config *Config) error { return nil } +func tryLoadJSONConfig(dst *Config) error { + var configArg string + for i, arg := range os.Args { + if (arg == "-c" || arg == "--config") && i+1 < len(os.Args) { + configArg = os.Args[i+1] + break + } + } + + if len(configArg) > 0 { + err := loadConfigFromFile(entities.FilePath(configArg), dst) + if err != nil { + return err + } + } + + return nil +} + func loadConfigFromFile(src entities.FilePath, dst *Config) error { data, err := os.ReadFile(src.String()) if err != nil { diff --git a/internal/server/server.go b/internal/server/server.go index 1c5cb7b..7442d76 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -191,6 +191,11 @@ func parseConfig(config *Config) error { } func parseFlags(config *Config, progname string, args []string) error { + err := tryLoadJSONConfig(config) + if err != nil { + return err + } + flags := pflag.NewFlagSet(progname, pflag.ContinueOnError) address := config.Address @@ -202,7 +207,7 @@ func parseFlags(config *Config, progname string, args []string) error { privateKeyPath := config.PrivateKeyPath flags.VarP(&privateKeyPath, "crypto-key", "", "path to public key to encrypt agent -> server communications") - configPath := entities.FilePath("") + configPath := entities.FilePath("") // register var for compatibility flags.VarP(&configPath, "config", "c", "path to configuration file in JSON format") // define flags @@ -211,15 +216,9 @@ func parseFlags(config *Config, progname string, args []string) error { flags.BoolVarP(&config.RestoreOnStart, "restore", "r", config.RestoreOnStart, "whether to restore state on startup") flags.StringVarP(&config.DatabaseDSN, "database", "d", config.DatabaseDSN, "PostgreSQL database DSN") - err := flags.Parse(args) - if err != nil { - return err - } - - if len(configPath) != 0 { - if err := loadConfigFromFile(configPath, config); err != nil { - return err - } + pErr := flags.Parse(args) + if pErr != nil { + return pErr } // fill values @@ -275,6 +274,25 @@ func newDataStorage(config *Config) (storage.MetricsStorage, error) { } } +func tryLoadJSONConfig(dst *Config) error { + var configArg string + for i, arg := range os.Args { + if (arg == "-c" || arg == "--config") && i+1 < len(os.Args) { + configArg = os.Args[i+1] + break + } + } + + if len(configArg) > 0 { + err := loadConfigFromFile(entities.FilePath(configArg), dst) + if err != nil { + return err + } + } + + return nil +} + func loadConfigFromFile(src entities.FilePath, dst *Config) error { data, err := os.ReadFile(src.String()) if err != nil { From 757cb7d1ae725aab90a8dd625784a30faf99f5c0 Mon Sep 17 00:00:00 2001 From: Eugene Shuvalov Date: Sun, 10 Nov 2024 17:50:19 +0300 Subject: [PATCH 4/8] added graceful agent shutdown --- cmd/agent/main.go | 5 +- coverage.html | 897 +++++++++++++++----- internal/agent/agent.go | 123 ++- internal/agent/exporter/batch_exporter.go | 7 +- internal/agent/exporter/exporter_test.go | 17 +- internal/agent/exporter/limited_exporter.go | 13 +- internal/retrier/retrier.go | 4 +- internal/retrier/retrier_test.go | 7 +- internal/storage/database_migrator.go | 3 +- 9 files changed, 788 insertions(+), 288 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 85215d8..1223b61 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -22,5 +22,8 @@ func main() { logging.LogFatal(err) } - agnt.Run() + err = agnt.Run() + if err != nil { + logging.LogFatal(err) + } } diff --git a/coverage.html b/coverage.html index 79b61b1..65c5fdd 100644 --- a/coverage.html +++ b/coverage.html @@ -63,13 +63,13 @@ - + - + - + @@ -87,75 +87,79 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + @@ -181,6 +185,8 @@ @@ -623,20 +631,26 @@ import ( "context" + "encoding/json" "fmt" + "os" + "os/signal" "strings" "sync" + "syscall" "time" "github.com/caarlos0/env/v11" "github.com/ex0rcist/metflix/internal/agent/exporter" "github.com/ex0rcist/metflix/internal/entities" "github.com/ex0rcist/metflix/internal/logging" - "github.com/ex0rcist/metflix/internal/services" + "github.com/ex0rcist/metflix/internal/security" "github.com/ex0rcist/metflix/internal/utils" "github.com/spf13/pflag" ) +const shutdownTimeout = 60 * time.Second + // Metric collecting agent (mr. Bond?). type Agent struct { Config *Config @@ -648,15 +662,16 @@ // Agent config. type Config struct { - Address entities.Address `env:"ADDRESS"` - PollInterval int `env:"POLL_INTERVAL"` - ReportInterval int `env:"REPORT_INTERVAL"` - RateLimit int `env:"RATE_LIMIT"` - Secret entities.Secret `env:"KEY"` + Address entities.Address `env:"ADDRESS" json:"address"` + PollInterval int `env:"POLL_INTERVAL" json:"poll_interval"` + ReportInterval int `env:"REPORT_INTERVAL" json:"report_interval"` + RateLimit int `env:"RATE_LIMIT" json:"-"` + Secret entities.Secret `env:"KEY" json:"key"` + PublicKeyPath entities.FilePath `env:"CRYPTO_KEY" json:"crypto_key"` } // Constructor. -func New() (*Agent, error) { +func New() (*Agent, error) { config := &Config{ Address: "0.0.0.0:8080", PollInterval: 2, @@ -669,54 +684,102 @@ return nil, err } - exporter, err := newMetricsExporter(config) - if err != nil { - return nil, err - } - - return &Agent{ - Config: config, - Stats: NewStats(), - Exporter: exporter, + return &Agent{ + Config: config, + Stats: NewStats(), }, nil } // Run agent. -func (a *Agent) Run() { +func (a *Agent) Run() error { logging.LogInfo(a.Config.String()) logging.LogInfo("agent ready") - ctx := context.Background() + ctx, cancelBackgroundTasks := context.WithCancel(context.Background()) + defer cancelBackgroundTasks() + + exporter, err := newMetricsExporter(ctx, a.Config) + if err != nil { + return err + } + a.Exporter = exporter + + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) a.wg.Add(2) - go a.startPolling(ctx) - go a.startReporting() + go func() { + defer a.wg.Done() + a.startPolling(ctx) + }() - a.wg.Wait() -} + go func() { + defer a.wg.Done() + a.startReporting(ctx) + }() + + <-interrupt + + logging.LogInfo("shutting down agent...") + cancelBackgroundTasks() + + stopped := make(chan struct{}) + + stopCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) + defer cancel() + + go func() { + defer close(stopped) + a.wg.Wait() + }() + + select { + case <-stopped: + logging.LogInfo("agent shutdown successful") + + case <-stopCtx.Done(): + logging.LogWarn("exceeded shutdown timeout, force exit") + } + + return nil +} func (a *Agent) startPolling(ctx context.Context) { - defer a.wg.Done() + ticker := time.NewTicker(utils.IntToDuration(a.Config.PollInterval)) + defer ticker.Stop() for { - err := a.Stats.Poll(ctx) - if err != nil { - logging.LogError(err) - } - - time.Sleep(utils.IntToDuration(a.Config.PollInterval)) + select { + case <-ticker.C: + func() { + err := a.Stats.Poll(ctx) + if err != nil { + logging.LogError(err) + } + }() + + case <-ctx.Done(): + logging.LogInfo("shutting down metrics polling") + return + } } } -func (a *Agent) startReporting() { - defer a.wg.Done() +func (a *Agent) startReporting(ctx context.Context) { + ticker := time.NewTicker(utils.IntToDuration(a.Config.ReportInterval)) + defer ticker.Stop() for { - time.Sleep(utils.IntToDuration(a.Config.ReportInterval)) + select { + case <-ticker.C: + a.reportStats() - a.reportStats() - } + case <-ctx.Done(): + logging.LogInfo("shutting down metrics reporting") + return + } + } } func (a *Agent) reportStats() { @@ -792,79 +855,136 @@ str = append(str, fmt.Sprintf("secret=%v", c.Secret)) } + if len(c.PublicKeyPath) > 0 { + str = append(str, fmt.Sprintf("public-key=%v", c.PublicKeyPath)) + } + return "agent config: " + strings.Join(str, "; ") } -func detectExporterKind(c *Config) string { +func detectExporterKind(c *Config) string { var ek string switch { case c.RateLimit > 0: ek = exporter.KindLimited - default: + default: ek = exporter.KindBatch } - return ek + return ek } -func newMetricsExporter(config *Config) (exporter.Exporter, error) { +func newMetricsExporter(ctx context.Context, config *Config) (exporter.Exporter, error) { var exp exporter.Exporter - var signer services.Signer + var signer security.Signer var err error if len(config.Secret) > 0 { - signer = services.NewSignerService(config.Secret) + signer = security.NewSignerService(config.Secret) } - exporterKind := detectExporterKind(config) + var publicKey security.PublicKey + if len(config.PublicKeyPath) != 0 { + publicKey, err = security.NewPublicKey(config.PublicKeyPath) + if err != nil { + return nil, err + } + } + + exporterKind := detectExporterKind(config) switch exporterKind { case exporter.KindLimited: - exp = exporter.NewLimitedExporter(&config.Address, signer, config.RateLimit) - case exporter.KindBatch: - exp = exporter.NewBatchExporter(&config.Address, signer) + exp = exporter.NewLimitedExporter(ctx, &config.Address, signer, config.RateLimit, publicKey) + case exporter.KindBatch: + exp = exporter.NewBatchExporter(ctx, &config.Address, signer, publicKey) default: exp, err = nil, fmt.Errorf("unknown exporter type") } - return exp, err + return exp, err } -func parseConfig(config *Config) error { - address := config.Address +func parseConfig(config *Config) error { + err := tryLoadJSONConfig(config) + if err != nil { + return err + } + + address := config.Address pflag.VarP(&address, "address", "a", "address:port for HTTP API requests") secret := config.Secret pflag.VarP(&secret, "secret", "k", "a key to sign outgoing data") + publicKeyPath := config.PublicKeyPath + pflag.VarP(&publicKeyPath, "crypto-key", "", "path to public key to encrypt agent -> server communications") + + configPath := entities.FilePath("") // register var for compatibility + pflag.VarP(&configPath, "config", "c", "path to configuration file in JSON format") + pflag.IntVarP(&config.PollInterval, "poll-interval", "p", config.PollInterval, "interval (s) for polling stats") pflag.IntVarP(&config.ReportInterval, "report-interval", "r", config.ReportInterval, "interval (s) for polling stats") pflag.IntVarP(&config.RateLimit, "rate-limit", "l", config.RateLimit, "number of max simultaneous requests to server") pflag.Parse() - // because VarP gets non-pointer value, set it manually pflag.Visit(func(f *pflag.Flag) { switch f.Name { case "address": config.Address = address case "secret": config.Secret = secret + case "crypto-key": + config.PublicKeyPath = publicKeyPath } }) - if err := env.Parse(config); err != nil { + if err := env.Parse(config); err != nil { return err } - return nil + return nil +} + +func tryLoadJSONConfig(dst *Config) error { + var configArg string + for i, arg := range os.Args { + if (arg == "-c" || arg == "--config") && i+1 < len(os.Args) { + configArg = os.Args[i+1] + break + } + } + + if len(configArg) > 0 { + err := loadConfigFromFile(entities.FilePath(configArg), dst) + if err != nil { + return err + } + } + + return nil +} + +func loadConfigFromFile(src entities.FilePath, dst *Config) error { + data, err := os.ReadFile(src.String()) + if err != nil { + return fmt.Errorf("agent.loadConfigFromFile - os.ReadFile: %w", err) + } + + if err := json.Unmarshal(data, dst); err != nil { + return fmt.Errorf("agent.loadConfigFromFile - json.Unmarshal: %w", err) + } + + return nil }