diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6211506..237cba5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,17 +8,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version-file: go.mod - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v4 + uses: goreleaser/goreleaser-action@v6 with: version: latest args: release --clean diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e6c9afa..d4ec3a1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,19 +14,13 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: golangci/golangci-lint-action@v3 + - uses: actions/checkout@v4 + - uses: golangci/golangci-lint-action@v6 test: runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: "1.19.x" - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - run: make deps-test test + go-version-file: go.mod + - run: make test diff --git a/Makefile b/Makefile index 2238ea8..704cf34 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,6 @@ all: build deps-lint: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.50.1 -deps-test: - go install github.com/tenntenn/testtime/cmd/testtime@latest - fmt: go fmt ./... @@ -18,5 +15,5 @@ build: go build test: - go test -overlay=`testtime` -v ./... + go test -v ./... diff --git a/collector/collector.go b/collector/collector.go index f54af5c..a267277 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -71,9 +71,6 @@ func do(ctx context.Context, snmpClient snmpClientImpl, c *config.Config) ([]Met metrics = append(metrics, MetricsDutum{IfIndex: ifIndex, Mib: mibName, IfName: ifName, Value: value}) } } - if c.Debug { - debugPrint(metrics) - } return metrics, nil } diff --git a/collector/debugprint.go b/collector/debugprint.go deleted file mode 100644 index 0401b43..0000000 --- a/collector/debugprint.go +++ /dev/null @@ -1,22 +0,0 @@ -package collector - -import ( - "fmt" - "sort" - "strings" - "time" - - "github.com/maruel/natural" -) - -func debugPrint(dutum []MetricsDutum) { - var dutumStr []string - for i := range dutum { - dutumStr = append(dutumStr, dutum[i].String()) - } - sort.Sort(natural.StringSlice(dutumStr)) - // debug print. - fmt.Print("\033[H\033[2J") - fmt.Println(time.Now().Format(time.ANSIC)) - fmt.Println(strings.Join(dutumStr, "\n")) -} diff --git a/collector/debugprint_test.go b/collector/debugprint_test.go deleted file mode 100644 index 6cc0ea6..0000000 --- a/collector/debugprint_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package collector - -import ( - "context" - "flag" - "io" - "os" - "testing" - "time" - - "github.com/tenntenn/golden" - "github.com/tenntenn/testtime" - "github.com/yseto/switch-traffic-to-mackerel/config" -) - -var ( - flagUpdate bool - goldenDir string = "./testdata/" -) - -func init() { - flag.BoolVar(&flagUpdate, "update", true, "update golden files") -} - -func TestDebugPrint(t *testing.T) { - ctx := context.Background() - - got := capture(func() { - testtime.SetTime(t, time.Unix(1, 0)) - - c := &config.Config{ - MIBs: []string{"ifHCInOctets", "ifHCOutOctets"}, - Debug: true, - } - _, err := do(ctx, &mockSnmpClient{}, c) - if err != nil { - t.Error("invalid raised error") - } - }) - - if diff := golden.Check(t, flagUpdate, goldenDir, t.Name(), got); diff != "" { - t.Errorf("mismatch (-want +got):\n%s", diff) - } -} - -func capture(f func()) string { - writer := os.Stdout - defer func() { - os.Stdout = writer - }() - - r, w, _ := os.Pipe() - os.Stdout = w - - f() - - w.Close() - out, _ := io.ReadAll(r) - return string(out) -} diff --git a/collector/testdata/TestDebugPrint.golden b/collector/testdata/TestDebugPrint.golden deleted file mode 100644 index 4ca50b3..0000000 --- a/collector/testdata/TestDebugPrint.golden +++ /dev/null @@ -1,9 +0,0 @@ -Thu Jan 1 00:00:01 1970 -1 lo0 ifHCInOctets 60 -1 lo0 ifHCOutOctets 120 -2 eth0 ifHCInOctets 60 -2 eth0 ifHCOutOctets 120 -3 eth1 ifHCInOctets 60 -3 eth1 ifHCOutOctets 120 -4 eth2 ifHCInOctets 60 -4 eth2 ifHCOutOctets 120 diff --git a/config/config.go b/config/config.go index 579c448..f28b527 100644 --- a/config/config.go +++ b/config/config.go @@ -60,10 +60,10 @@ func Init(filename string) (*Config, error) { } if t.Community == "" { - return nil, fmt.Errorf("community is needed.") + return nil, fmt.Errorf("community is needed") } if t.Target == "" { - return nil, fmt.Errorf("target is needed.") + return nil, fmt.Errorf("target is needed") } c := &Config{ @@ -76,7 +76,7 @@ func Init(filename string) (*Config, error) { if t.Interface != nil { if t.Interface.Include != nil && t.Interface.Exclude != nil { - return nil, fmt.Errorf("Interface.Exclude, Interface.Include is exclusive control.") + return nil, fmt.Errorf("Interface.Exclude, Interface.Include is exclusive control") } if t.Interface.Include != nil { c.IncludeRegexp, err = regexp.Compile(*t.Interface.Include) diff --git a/go.mod b/go.mod index e883c7a..5260d06 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,12 @@ module github.com/yseto/switch-traffic-to-mackerel -go 1.20 +go 1.22.4 require ( github.com/google/go-cmp v0.5.9 github.com/gosnmp/gosnmp v1.35.0 github.com/mackerelio/mackerel-client-go v0.24.0 - github.com/maruel/natural v1.1.0 - github.com/tenntenn/golden v0.4.0 - github.com/tenntenn/testtime v0.2.2 gopkg.in/yaml.v3 v3.0.1 ) -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/josharian/mapfs v0.0.0-20210615234106-095c008854e6 // indirect - github.com/josharian/txtarfs v0.0.0-20210615234325-77aca6df5bca // indirect - golang.org/x/tools v0.1.5 // indirect -) +require github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 3bb52f2..12f3c32 100644 --- a/go.sum +++ b/go.sum @@ -4,45 +4,14 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gosnmp/gosnmp v1.35.0 h1:EuWWNPxTCdAUx2/NbQcSa3WdNxjzpy4Phv57b4MWpJM= github.com/gosnmp/gosnmp v1.35.0/go.mod h1:2AvKZ3n9aEl5TJEo/fFmf/FGO4Nj4cVeEc5yuk88CYc= -github.com/josharian/mapfs v0.0.0-20210615234106-095c008854e6 h1:c+ctPFdISggaSNCfU1IueNBAsqetJSvMcpQlT+0OVdY= -github.com/josharian/mapfs v0.0.0-20210615234106-095c008854e6/go.mod h1:Rv/momJI8DgrWnBZip+SgagpcgORIZQE5SERlxNb8LY= -github.com/josharian/txtarfs v0.0.0-20210615234325-77aca6df5bca h1:a8xeK4GsWLE4LYo5VI4u1Cn7ZvT1NtXouXR3DdKLB8Q= -github.com/josharian/txtarfs v0.0.0-20210615234325-77aca6df5bca/go.mod h1:UbC32ft9G/jG+sZI8wLbIBNIrYr7vp/yqMDa9SxVBNA= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mackerelio/mackerel-client-go v0.24.0 h1:Y9FeTgrQlDdtLU7FtMM6hd5mL5T4TvGCVUY0LH+bceQ= github.com/mackerelio/mackerel-client-go v0.24.0/go.mod h1:b4qVMQi+w4rxtKQIFycLWXNBtIi9d0r571RzYmg/aXo= -github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ= -github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ= 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/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/tenntenn/golden v0.4.0 h1:ghZvhG1A3zp3JKBBywTt4GRJ4ieCtZi6Xw20kt8fpy0= -github.com/tenntenn/golden v0.4.0/go.mod h1:0xI/4lpoHR65AUTmd1RKR9S1Uv0JR3yR2Q1Ob2bKqQA= -github.com/tenntenn/testtime v0.2.2 h1:y6K00BUNg7cRE9WpkBX/Bn+WgmV5/a3hsw7xGNyF2p0= -github.com/tenntenn/testtime v0.2.2/go.mod h1:gXZpxnMoBEV+JZwooprQ65lIbR2Kzk5PpP/deHMn+Is= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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= diff --git a/mackerel/mackerel.go b/mackerel/mackerel.go index fbe6257..7bcbf44 100644 --- a/mackerel/mackerel.go +++ b/mackerel/mackerel.go @@ -1,11 +1,10 @@ package mackerel import ( - "container/list" + "cmp" "context" "log" - "sync" - "time" + "os" mackerel "github.com/mackerelio/mackerel-client-go" @@ -19,33 +18,28 @@ type mackerelClient interface { PostHostMetricValuesByHostID(hostID string, metricValues []*mackerel.MetricValue) error } -type Queue struct { - sync.Mutex - - buffers *list.List - Snapshot []collector.MetricsDutum - client mackerelClient - +type Mackerel struct { + client mackerelClient hostID string targetAddr string name string } -type QueueArg struct { +type Arg struct { Apikey string HostID string TargetAddr string Name string - Snapshot []collector.MetricsDutum } -func NewQueue(qa *QueueArg) *Queue { - client := mackerel.NewClient(qa.Apikey) +func New(qa *Arg) *Mackerel { + baseURL := cmp.Or(os.Getenv("MACKEREL_APIBASE"), "https://api.mackerelio.com/") + apikey := cmp.Or(os.Getenv("MACKEREL_APIKEY"), qa.Apikey) - return &Queue{ - buffers: list.New(), + client, _ := mackerel.NewClientWithOptions(apikey, baseURL, false) + + return &Mackerel{ client: client, - Snapshot: qa.Snapshot, hostID: qa.HostID, targetAddr: qa.TargetAddr, name: qa.Name, @@ -53,8 +47,8 @@ func NewQueue(qa *QueueArg) *Queue { } // return host ID when create. -func (q *Queue) Init(ifs []collector.Interface) (*string, error) { - log.Println("init queue") +func (m *Mackerel) Init(ifs []collector.Interface) (*string, error) { + log.Println("init mackerel") var interfaces []mackerel.Interface @@ -62,7 +56,7 @@ func (q *Queue) Init(ifs []collector.Interface) (*string, error) { interfaces = []mackerel.Interface{ { Name: "main", - IPv4Addresses: []string{q.targetAddr}, + IPv4Addresses: []string{m.targetAddr}, }, } } else { @@ -77,61 +71,32 @@ func (q *Queue) Init(ifs []collector.Interface) (*string, error) { var newHostID *string var err error - if q.hostID != "" { - _, err = q.client.UpdateHost(q.hostID, &mackerel.UpdateHostParam{ - Name: q.name, + if m.hostID != "" { + _, err = m.client.UpdateHost(m.hostID, &mackerel.UpdateHostParam{ + Name: m.name, Interfaces: interfaces, }) } else { - q.hostID, err = q.client.CreateHost(&mackerel.CreateHostParam{ - Name: q.name, + m.hostID, err = m.client.CreateHost(&mackerel.CreateHostParam{ + Name: m.name, Interfaces: interfaces, }) - newHostID = &q.hostID + newHostID = &m.hostID } if err != nil { return nil, err } - err = q.client.CreateGraphDefs(graphDefs) - if err != nil { + if err = m.CreateGraphDefs(graphDefs); err != nil { return nil, err } return newHostID, nil } -func (q *Queue) SendTicker(ctx context.Context, wg *sync.WaitGroup) { - t := time.NewTicker(500 * time.Millisecond) - - defer func() { - t.Stop() - wg.Done() - }() - - for { - select { - case <-t.C: - q.sendToMackerel(ctx) - - case <-ctx.Done(): - log.Println("cancellation from context:", ctx.Err()) - return - } - } +func (m *Mackerel) CreateGraphDefs(d []*mackerel.GraphDefsParam) error { + return m.client.CreateGraphDefs(d) } -func (q *Queue) sendToMackerel(ctx context.Context) { - if q.buffers.Len() == 0 { - return - } - - e := q.buffers.Front() - err := q.client.PostHostMetricValuesByHostID(q.hostID, e.Value.([](*mackerel.MetricValue))) - if err != nil { - log.Println(err) - return - } - q.Lock() - q.buffers.Remove(e) - q.Unlock() +func (m *Mackerel) Send(ctx context.Context, value []*mackerel.MetricValue) error { + return m.client.PostHostMetricValuesByHostID(m.hostID, value) } diff --git a/mackerel/mackerel_test.go b/mackerel/mackerel_test.go index 6248cf6..6727da0 100644 --- a/mackerel/mackerel_test.go +++ b/mackerel/mackerel_test.go @@ -1,7 +1,6 @@ package mackerel import ( - "container/list" "context" "errors" "reflect" @@ -71,15 +70,14 @@ func TestInit(t *testing.T) { expectedGraphDef []*mackerel.GraphDefsParam hostID string returnHostID *string - queue *Queue + queue *Mackerel mock *mackerelClientMock interfaces []collector.Interface }{ { name: "create host when hostID is empty", expectedCreateParam: createHost, - queue: &Queue{ - buffers: list.New(), + queue: &Mackerel{ name: "hostname", targetAddr: "192.0.2.1", }, @@ -92,8 +90,7 @@ func TestInit(t *testing.T) { { name: "update host when hostID is exist", expectedUpdateParam: updateHost, - queue: &Queue{ - buffers: list.New(), + queue: &Mackerel{ name: "hostname", targetAddr: "192.0.2.2", hostID: "0987654321", @@ -105,8 +102,7 @@ func TestInit(t *testing.T) { name: "create host is error", expectedCreateParam: createHost, expectedError: e, - queue: &Queue{ - buffers: list.New(), + queue: &Mackerel{ name: "hostname", targetAddr: "192.0.2.1", }, @@ -119,8 +115,7 @@ func TestInit(t *testing.T) { name: "update host is error", expectedUpdateParam: updateHost, expectedError: e, - queue: &Queue{ - buffers: list.New(), + queue: &Mackerel{ name: "hostname", targetAddr: "192.0.2.2", hostID: "0987654321", @@ -134,8 +129,7 @@ func TestInit(t *testing.T) { name: "createGraphDef is error", expectedUpdateParam: updateHost, expectedError: e, - queue: &Queue{ - buffers: list.New(), + queue: &Mackerel{ name: "hostname", targetAddr: "192.0.2.2", hostID: "0987654321", @@ -160,8 +154,7 @@ func TestInit(t *testing.T) { }, }, }, - queue: &Queue{ - buffers: list.New(), + queue: &Mackerel{ name: "hostname", targetAddr: "192.0.2.1", }, @@ -207,77 +200,21 @@ func TestInit(t *testing.T) { } -func TestSendToMackerel(t *testing.T) { +func TestSend(t *testing.T) { ctx := context.Background() - t.Run("when empty queue", func(t *testing.T) { - mock := &mackerelClientMock{} - queue := &Queue{ - buffers: list.New(), - hostID: "0987654321", - client: mock, - } - - queue.sendToMackerel(ctx) - - if mock.hostID != "" { - t.Error("invalid get hostID") - } - }) - - t.Run("when queue length = 1", func(t *testing.T) { - mock := &mackerelClientMock{} - queue := &Queue{ - buffers: list.New(), - hostID: "0987654321", - client: mock, - } - - queue.buffers.PushBack([]*mackerel.MetricValue{ - { - Name: "foo", - }, - }) - - queue.sendToMackerel(ctx) - - if mock.hostID == "" { - t.Error("invalid need hostID") - } - - if queue.buffers.Len() != 0 { - t.Error("invalid queue length") - } - }) - - t.Run("when queue length = 2", func(t *testing.T) { - mock := &mackerelClientMock{} - queue := &Queue{ - buffers: list.New(), - hostID: "0987654321", - client: mock, - } - - queue.buffers.PushBack([]*mackerel.MetricValue{ - { - Name: "foo", - }, - }) - queue.buffers.PushBack([]*mackerel.MetricValue{ - { - Name: "foo", - }, - }) - - queue.sendToMackerel(ctx) + mock := &mackerelClientMock{} + mc := &Mackerel{ + hostID: "0987654321", + client: mock, + } - if mock.hostID == "" { - t.Error("invalid need hostID") - } + if err := mc.Send(ctx, nil); err != nil { + t.Errorf("occur error %v", err) + } - if queue.buffers.Len() != 1 { - t.Error("invalid queue length") - } - }) + if mock.hostID == "" { + t.Error("invalid need hostID") + } } diff --git a/main.go b/main.go index 736aec6..2b8b218 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "cmp" "context" "flag" "log" @@ -12,6 +13,7 @@ import ( "github.com/yseto/switch-traffic-to-mackerel/collector" "github.com/yseto/switch-traffic-to-mackerel/config" "github.com/yseto/switch-traffic-to-mackerel/mackerel" + "github.com/yseto/switch-traffic-to-mackerel/queue" ) func main() { @@ -42,55 +44,53 @@ func main() { log.Fatal(err) } - qa := &mackerel.QueueArg{ - TargetAddr: c.Target, - Snapshot: snapshot, - } + var mClient *mackerel.Mackerel if c.Mackerel != nil { - qa.Apikey = c.Mackerel.ApiKey - qa.HostID = c.Mackerel.HostID - qa.Name = c.Mackerel.Name - if qa.Name == "" { - qa.Name = c.Target - } - } - queue := mackerel.NewQueue(qa) - - wg := &sync.WaitGroup{} - wg.Add(1) - go ticker(ctx, wg, c, queue) + mClient = mackerel.New(&mackerel.Arg{ + TargetAddr: c.Target, + Apikey: c.Mackerel.ApiKey, + HostID: c.Mackerel.HostID, + Name: cmp.Or(c.Mackerel.Name, c.Target), + }) - if c.DryRun { - wg.Wait() - return - } + var interfaces []collector.Interface + if !c.Mackerel.IgnoreNetworkInfo { + interfaces, err = collector.DoInterfaceIPAddress(ctx, c) + if err != nil { + log.Println("HINT: try mackerel > ignore-network-info: true") + log.Fatal(err) + } + } - var interfaces []collector.Interface - if !c.Mackerel.IgnoreNetworkInfo { - interfaces, err = collector.DoInterfaceIPAddress(ctx, c) + newHostID, err := mClient.Init(interfaces) if err != nil { - log.Println("HINT: try mackerel > ignore-network-info: true") log.Fatal(err) } - } - - newHostID, err := queue.Init(interfaces) - if err != nil { - log.Fatal(err) - } - if newHostID != nil { - log.Println("save HostID") - if err = c.Save(*newHostID); err != nil { - log.Fatal(err) + if newHostID != nil { + log.Println("save HostID") + if err = c.Save(*newHostID); err != nil { + log.Fatal(err) + } } } + queueHandler := queue.New(queue.Arg{ + SendFunc: mClient, + Debug: c.Debug, + DryRun: c.DryRun, + Snapshot: snapshot, + }) + + wg := &sync.WaitGroup{} + wg.Add(1) + go collectTicker(ctx, wg, c, queueHandler) + wg.Add(1) - go queue.SendTicker(ctx, wg) + go sendTicker(ctx, wg, queueHandler) wg.Wait() } -func ticker(ctx context.Context, wg *sync.WaitGroup, c *config.Config, queue *mackerel.Queue) { +func collectTicker(ctx context.Context, wg *sync.WaitGroup, c *config.Config, queueHandler *queue.Queue) { t := time.NewTicker(1 * time.Minute) defer func() { t.Stop() @@ -105,9 +105,32 @@ func ticker(ctx context.Context, wg *sync.WaitGroup, c *config.Config, queue *ma log.Println(err.Error()) continue } - if !c.DryRun { - queue.Enqueue(rawMetrics) - } + queueHandler.Enqueue(rawMetrics) + + case <-ctx.Done(): + log.Println("cancellation from context:", ctx.Err()) + return + } + } +} + +type sendTickerFunc interface { + Tick(context.Context) +} + +func sendTicker(ctx context.Context, wg *sync.WaitGroup, f sendTickerFunc) { + t := time.NewTicker(500 * time.Millisecond) + + defer func() { + t.Stop() + wg.Done() + }() + + for { + select { + case <-t.C: + f.Tick(ctx) + case <-ctx.Done(): log.Println("cancellation from context:", ctx.Err()) return diff --git a/mib/mib.go b/mib/mib.go index 82d63ec..13e4e77 100644 --- a/mib/mib.go +++ b/mib/mib.go @@ -30,7 +30,7 @@ func Validate(rawMibs []string) ([]string, error) { for _, name := range rawMibs { if _, exists := Oidmapping[name]; !exists { - return nil, fmt.Errorf("mib %s is not supported.", name) + return nil, fmt.Errorf("mib %s is not supported", name) } parseMibs = append(parseMibs, name) } diff --git a/mackerel/enqueue.go b/queue/enqueue.go similarity index 96% rename from mackerel/enqueue.go rename to queue/enqueue.go index afb11fd..9842472 100644 --- a/mackerel/enqueue.go +++ b/queue/enqueue.go @@ -1,4 +1,4 @@ -package mackerel +package queue import ( "fmt" @@ -48,8 +48,8 @@ func calcurateDiff(a, b, overflow uint64) uint64 { } func (q *Queue) Enqueue(rawMetrics []collector.MetricsDutum) { - prevSnapshot := q.Snapshot - q.Snapshot = rawMetrics + prevSnapshot := q.snapshot + q.snapshot = rawMetrics now := time.Now().Unix() diff --git a/mackerel/enqueue_test.go b/queue/enqueue_test.go similarity index 94% rename from mackerel/enqueue_test.go rename to queue/enqueue_test.go index 0769e02..fb704db 100644 --- a/mackerel/enqueue_test.go +++ b/queue/enqueue_test.go @@ -1,4 +1,4 @@ -package mackerel +package queue import ( "container/list" @@ -36,7 +36,7 @@ func TestEnqueue(t *testing.T) { t.Run("replace Snapshot", func(t *testing.T) { queue := &Queue{ buffers: list.New(), - Snapshot: []collector.MetricsDutum{ + snapshot: []collector.MetricsDutum{ { IfIndex: 1, Mib: "", @@ -55,7 +55,7 @@ func TestEnqueue(t *testing.T) { } queue.Enqueue(newSnapshot) - if !reflect.DeepEqual(queue.Snapshot, newSnapshot) { + if !reflect.DeepEqual(queue.snapshot, newSnapshot) { t.Error("replace Snapshot is invalid") } }) @@ -63,7 +63,7 @@ func TestEnqueue(t *testing.T) { t.Run("calcurate", func(t *testing.T) { queue := &Queue{ buffers: list.New(), - Snapshot: []collector.MetricsDutum{ + snapshot: []collector.MetricsDutum{ { IfIndex: 1, Mib: "ifHCInOctets", diff --git a/queue/queue.go b/queue/queue.go new file mode 100644 index 0000000..ec7e637 --- /dev/null +++ b/queue/queue.go @@ -0,0 +1,85 @@ +package queue + +import ( + "container/list" + "context" + "fmt" + "log" + "sync" + + "github.com/mackerelio/mackerel-client-go" + "github.com/yseto/switch-traffic-to-mackerel/collector" +) + +type SendInterface interface { + Send(context.Context, []*mackerel.MetricValue) error +} + +type Queue struct { + sync.Mutex + buffers *list.List + + sendFunc SendInterface + + debug bool + dryrun bool + + snapshot []collector.MetricsDutum +} + +type Arg struct { + SendFunc SendInterface + + Debug bool + DryRun bool + + Snapshot []collector.MetricsDutum +} + +type noopSendFunc struct{} + +func (noopSendFunc) Send(_ context.Context, _ []*mackerel.MetricValue) error { + return nil +} + +func New(qa Arg) *Queue { + if qa.SendFunc == nil { + qa.SendFunc = &noopSendFunc{} + } + return &Queue{ + buffers: list.New(), + + sendFunc: qa.SendFunc, + debug: qa.Debug, + dryrun: qa.DryRun, + + snapshot: qa.Snapshot, + } +} + +func (q *Queue) Tick(ctx context.Context) { + if q.buffers.Len() == 0 { + return + } + + e := q.buffers.Front() + value := e.Value.([](*mackerel.MetricValue)) + + if q.debug { + for idx := range value { + fmt.Printf("%d\t%s\t%v\n", value[idx].Time, value[idx].Name, value[idx].Value) + } + } + + if !q.dryrun { + err := q.sendFunc.Send(ctx, value) + if err != nil { + log.Println(err) + return + } + } + + q.Lock() + q.buffers.Remove(e) + q.Unlock() +} diff --git a/queue/queue_test.go b/queue/queue_test.go new file mode 100644 index 0000000..03b5ed7 --- /dev/null +++ b/queue/queue_test.go @@ -0,0 +1,11 @@ +package queue + +import ( + "context" + "testing" +) + +func TestNew(t *testing.T) { + q := New(Arg{}) + q.sendFunc.Send(context.TODO(), nil) // nolint +} diff --git a/snmp/snmp.go b/snmp/snmp.go index bdc24a2..41390fc 100644 --- a/snmp/snmp.go +++ b/snmp/snmp.go @@ -40,7 +40,7 @@ var ( errGetInterfaceNumber = errors.New("cant get interface number") errParseInterfaceName = errors.New("cant parse interface name") errParseInterfacePhyAddress = errors.New("cant parse phy address") - errParseError = errors.New("cant parse value.") + errParseError = errors.New("cant parse value") ) func (s *SNMP) GetInterfaceNumber() (uint64, error) {