Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Инкремент №4 #4

Merged
merged 8 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
linters-settings:
custom:
statictest:
path: ./plugin/statictest.so
3 changes: 0 additions & 3 deletions cmd/agent/README.md

This file was deleted.

32 changes: 31 additions & 1 deletion cmd/agent/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
package main

func main() {}
import (
"time"

"github.com/ex0rcist/metflix/internal/agent"
"github.com/ex0rcist/metflix/internal/logging"
"github.com/rs/zerolog/log"
)

func main() {
logging.Setup()

log.Info().Msg("starting agent...")

agnt := agent.New()

err := agnt.ParseFlags()
if err != nil {
panic(err)
}

log.Info().Msgf("agent args: a: %v, p: %v, r: %v", agnt.Config.Address, agnt.Config.PollInterval, agnt.Config.ReportInterval)

agnt.Run()

log.Info().Msgf("agent ready")

for {
// fixme: tmp hack for goroutine
time.Sleep(time.Second * 1)
}
}
3 changes: 0 additions & 3 deletions cmd/server/README.md

This file was deleted.

30 changes: 29 additions & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
package main

func main() {}
import (
"github.com/ex0rcist/metflix/internal/logging"
"github.com/ex0rcist/metflix/internal/server"
"github.com/rs/zerolog/log"
)

func main() {
logging.Setup()

log.Info().Msg("starting server...")

srv, err := server.New()
if err != nil {
panic(err)
}

err = srv.ParseFlags()
if err != nil {
panic(err)
}

log.Info().Msgf("server flags: address=%v", srv.Config.Address)
log.Info().Msg("server ready") // TODO: must be after run?

err2 := srv.Run()
if err2 != nil {
panic(err2)
}
}
19 changes: 19 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module github.com/ex0rcist/metflix

go 1.22.4

require (
github.com/go-chi/chi/v5 v5.1.0
github.com/rs/zerolog v1.33.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
30 changes: 30 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
137 changes: 137 additions & 0 deletions internal/agent/agent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package agent

import (
"time"

"github.com/ex0rcist/metflix/internal/entities"
"github.com/rs/zerolog/log"
"github.com/spf13/pflag"
)

type Agent struct {
Config *Config
Stats *Stats
API *API
}

type Config struct {
Address entities.Address
PollInterval time.Duration
ReportInterval time.Duration
}

func New() *Agent {
config := &Config{
Address: "0.0.0.0:8080",
PollInterval: 2 * time.Second,
ReportInterval: 10 * time.Second,
}

stats := NewStats()
api := NewAPI(&config.Address, nil)

return &Agent{
Config: config,
Stats: stats,
API: api,
}
}

func (app *Agent) ParseFlags() error {
address := app.Config.Address

pflag.VarP(&address, "address", "a", "address:port for HTTP API requests") // HELP: "&"" because Set() has pointer receiver?

// Task requires us to receive intervals in seconds, not duration, so we have to do it dirty
pollFlag := pflag.IntP("poll-interval", "p", durationToInt(app.Config.PollInterval), "interval (s) for polling stats")
reportFlag := pflag.IntP("report-interval", "r", durationToInt(app.Config.ReportInterval), "interval (s) for polling stats")

pflag.Parse()

app.Config.PollInterval = intToDuration(*pollFlag)
app.Config.ReportInterval = intToDuration(*reportFlag)

// because VarP gets non-pointer value, set it manually
pflag.Visit(func(f *pflag.Flag) {
switch f.Name {
case "address":
app.Config.Address = address
}
})

return nil
}

func (app *Agent) Run() {
go app.startPolling()
go app.startReporting()
}

func (app *Agent) startPolling() {
for {
err := app.Stats.Poll()
if err != nil {
return // todo: handle errors
}

time.Sleep(app.Config.PollInterval)
}
}

func (app *Agent) startReporting() {
for {
time.Sleep(app.Config.ReportInterval)

app.reportStats() // todo: handle errors
}
}

func (app *Agent) reportStats() {
log.Info().Msg("reporting stats ... ")

// agent continues polling while report is in progress, take snapshot?
snapshot := *app.Stats

app.API.
Report("Alloc", snapshot.Runtime.Alloc).
Report("BuckHashSys", snapshot.Runtime.BuckHashSys).
Report("Frees", snapshot.Runtime.Frees).
Report("GCCPUFraction", snapshot.Runtime.GCCPUFraction).
Report("GCSys", snapshot.Runtime.GCSys).
Report("HeapAlloc", snapshot.Runtime.HeapAlloc).
Report("HeapIdle", snapshot.Runtime.HeapIdle).
Report("HeapInuse", snapshot.Runtime.HeapInuse).
Report("HeapObjects", snapshot.Runtime.HeapObjects).
Report("HeapReleased", snapshot.Runtime.HeapReleased).
Report("HeapSys", snapshot.Runtime.HeapSys).
Report("LastGC", snapshot.Runtime.LastGC).
Report("Lookups", snapshot.Runtime.Lookups).
Report("MCacheInuse", snapshot.Runtime.MCacheInuse).
Report("MCacheSys", snapshot.Runtime.MCacheSys).
Report("MSpanInuse", snapshot.Runtime.MSpanInuse).
Report("MSpanSys", snapshot.Runtime.MSpanSys).
Report("Mallocs", snapshot.Runtime.Mallocs).
Report("NextGC", snapshot.Runtime.NextGC).
Report("NumForcedGC", snapshot.Runtime.NumForcedGC).
Report("NumGC", snapshot.Runtime.NumGC).
Report("OtherSys", snapshot.Runtime.OtherSys).
Report("PauseTotalNs", snapshot.Runtime.PauseTotalNs).
Report("StackInuse", snapshot.Runtime.StackInuse).
Report("StackSys", snapshot.Runtime.StackSys).
Report("Sys", snapshot.Runtime.Sys).
Report("TotalAlloc", snapshot.Runtime.TotalAlloc)

app.API.
Report("RandomValue", snapshot.RandomValue)

app.API.
Report("PollCount", snapshot.PollCount)
}

func intToDuration(s int) time.Duration {
return time.Duration(s) * time.Second
}

func durationToInt(d time.Duration) int {
return int(d.Seconds())
}
16 changes: 16 additions & 0 deletions internal/agent/agent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package agent

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestRun(t *testing.T) {
agnt := New()
require.NotPanics(t, agnt.Run)
}

func TestReportStats(t *testing.T) {
// HELP: как тестировать приватные?
}
68 changes: 68 additions & 0 deletions internal/agent/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package agent

import (
"fmt"
"io"
"net/http"
"time"

"github.com/ex0rcist/metflix/internal/entities"
"github.com/ex0rcist/metflix/internal/metrics"
"github.com/rs/zerolog/log"
)

type API struct {
address *entities.Address
httpClient *http.Client
err error
}

func NewAPI(address *entities.Address, httpTransport http.RoundTripper) *API {
if httpTransport == nil {
httpTransport = http.DefaultTransport
}

client := &http.Client{
Timeout: 2 * time.Second,
Transport: httpTransport,
}

return &API{
address: address,
httpClient: client,
err: nil,
}
}

func (c *API) Report(name string, metric metrics.Metric) *API {
// todo: another transport?
url := "http://" + c.address.String() + fmt.Sprintf("/update/%s/%s/%s", metric.Kind(), name, metric)

req, err := http.NewRequest(http.MethodPost, url, http.NoBody)
if err != nil {
panic(err)
}

req.Header.Set("Content-Type", "text/plain")

log.Info().Msg(fmt.Sprintf("sending POST to %v", url))

resp, err := c.httpClient.Do(req)
if err != nil {
panic(err)
//c.err = err
}

defer resp.Body.Close()

respBody, err := io.ReadAll(resp.Body) // нужно прочитать ответ для keepalive?
if err != nil {
c.err = err
}

if resp.StatusCode != http.StatusOK {
fmt.Println(respBody) // todo: log
}

return c
}
Loading
Loading