Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
fb34d0d53e
|
|||
|
abe6734a46
|
|||
|
c32d99ea65
|
|||
|
edfbd5a90d
|
|||
|
10d761f07d
|
|||
|
ddf3ff9240
|
|||
|
25d8b2776c
|
|||
|
cdbb3b7089
|
|||
|
614526b16d
|
|||
|
f1418a7a31
|
@@ -3,17 +3,10 @@ run:
|
|||||||
linters:
|
linters:
|
||||||
enable-all: true
|
enable-all: true
|
||||||
disable:
|
disable:
|
||||||
# Because globals might exist, but according to our codestyle they
|
|
||||||
# should be lowercased and considered as unexported.
|
|
||||||
- gochecknoglobals
|
|
||||||
# While it might be useful it'll create more problems that will solve.
|
# While it might be useful it'll create more problems that will solve.
|
||||||
- gocritic
|
- gocritic
|
||||||
# Complains about main() lengths, which isn't an issue.
|
|
||||||
- funlen
|
|
||||||
# Magic numbers might be everywhere. Disabled for now.
|
# Magic numbers might be everywhere. Disabled for now.
|
||||||
- gomnd
|
- gomnd
|
||||||
# ToDos everywhere
|
|
||||||
- godox
|
|
||||||
# Why? WHY? WHY _test???
|
# Why? WHY? WHY _test???
|
||||||
- testpackage
|
- testpackage
|
||||||
linters-settings:
|
linters-settings:
|
||||||
@@ -23,3 +16,13 @@ linters-settings:
|
|||||||
min-complexity: 40
|
min-complexity: 40
|
||||||
gocognit:
|
gocognit:
|
||||||
min-complexity: 40
|
min-complexity: 40
|
||||||
|
funlen:
|
||||||
|
lines: 200
|
||||||
|
statements: 100
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
# There will be some ToDos.
|
||||||
|
- linters:
|
||||||
|
- godox
|
||||||
|
text: "TODO"
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ ARG CI_COMMIT_TAG
|
|||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
RUN apk add bash git make
|
RUN apk add bash git make
|
||||||
RUN make metricatord-build
|
RUN make metricatord-build
|
||||||
|
RUN make metricator-client-build
|
||||||
|
|
||||||
FROM registry.gitlab.pztrn.name/containers/mirror/golang:1.15.5-alpine
|
FROM registry.gitlab.pztrn.name/containers/mirror/golang:1.15.5-alpine
|
||||||
LABEL maintainer="Stanislav N. <pztrn@pztrn.name>"
|
LABEL maintainer="Stanislav N. <pztrn@pztrn.name>"
|
||||||
|
|
||||||
COPY --from=build /go/src/gitlab.pztrn.name/pztrn/metricator/._bin/metricatord /usr/local/bin/metricatord
|
COPY --from=build /go/src/gitlab.pztrn.name/pztrn/metricator/._bin/metricatord /usr/local/bin/metricatord
|
||||||
|
COPY --from=build /go/src/gitlab.pztrn.name/pztrn/metricator/._bin/metricator-client /usr/local/bin/metricator-client
|
||||||
|
|
||||||
RUN apk add tzdata
|
RUN apk add tzdata
|
||||||
|
|
||||||
|
|||||||
11
Makefile
11
Makefile
@@ -14,11 +14,20 @@ help: Makefile
|
|||||||
check-build-dir:
|
check-build-dir:
|
||||||
@if [ ! -d "._bin" ]; then mkdir ._bin; fi
|
@if [ ! -d "._bin" ]; then mkdir ._bin; fi
|
||||||
|
|
||||||
|
## metricator-client-build: builds metricator client and places into ${PWD}/._bin.
|
||||||
|
metricator-client-build: check-build-dir
|
||||||
|
@if [ -f ./._bin/metricator-client ]; then rm ./._bin/metricator-client; fi
|
||||||
|
@scripts/build.sh metricator-client
|
||||||
|
|
||||||
## metricatord-build: builds metricator daemon and places into ${PWD}/._bin.
|
## metricatord-build: builds metricator daemon and places into ${PWD}/._bin.
|
||||||
metricatord-build: check-build-dir
|
metricatord-build: check-build-dir
|
||||||
@rm ./._bin/metricatord || true
|
@if [ -f ./._bin/metricatord ]; then rm ./._bin/metricatord; fi
|
||||||
@scripts/build.sh metricatord
|
@scripts/build.sh metricatord
|
||||||
|
|
||||||
|
## metricator-client-run: starts metricator client. Use ARGS to supply args.
|
||||||
|
metricator-client-run: metricator-client-build
|
||||||
|
@./._bin/metricator-client -config ${CONFIG} $(ARGS)
|
||||||
|
|
||||||
## metricatord-run: starts metricator daemon.
|
## metricatord-run: starts metricator daemon.
|
||||||
metricatord-run: metricatord-build
|
metricatord-run: metricatord-build
|
||||||
./._bin/metricatord -config ${CONFIG}
|
./._bin/metricatord -config ${CONFIG}
|
||||||
|
|||||||
156
cmd/metricator-client/main.go
Normal file
156
cmd/metricator-client/main.go
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"go.dev.pztrn.name/metricator/internal/common"
|
||||||
|
"go.dev.pztrn.name/metricator/internal/configuration"
|
||||||
|
"go.dev.pztrn.name/metricator/internal/logger"
|
||||||
|
"go.dev.pztrn.name/metricator/pkg/client"
|
||||||
|
"go.dev.pztrn.name/metricator/pkg/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// nolint:gochecknoglobals
|
||||||
|
var (
|
||||||
|
application = flag.String("application", "", "Application to query.")
|
||||||
|
appsList = flag.Bool("apps-list", false, "Show application's list registered at Metricator.")
|
||||||
|
metricatorHost = flag.String("metricator-host", "", "IP address or domain on which Metricator is available")
|
||||||
|
metricatorTimeout = flag.Int("metricator-timeout", 5, "Timeout for requests sent to Metricator.")
|
||||||
|
metricsList = flag.Bool("metrics-list", false, "Show metrics list. Requires 'application' parameter.")
|
||||||
|
metric = flag.String("metric", "", "Metric data to retrieve. Requires 'application' parameter.")
|
||||||
|
output = flag.String("output", "json", "Output format. Can be 'json' or 'plain-by-line'.")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config := configuration.NewConfig()
|
||||||
|
|
||||||
|
// Parse configuration.
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
err := config.Parse()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to parse configuration:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := logger.NewLogger(config.Logger)
|
||||||
|
|
||||||
|
logger.Debugf("Starting Metricator client, version %s from branch %s (build #%s, commit hash %s)\n",
|
||||||
|
common.Version,
|
||||||
|
common.Branch,
|
||||||
|
common.Build,
|
||||||
|
common.CommitHash,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check configuration.
|
||||||
|
// We cannot work at all if host isn't defined.
|
||||||
|
if *metricatorHost == "" {
|
||||||
|
logger.Infoln("Host isn't defined.")
|
||||||
|
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If nothing is requested - show error message.
|
||||||
|
if !*appsList && !*metricsList && *metric == "" {
|
||||||
|
logger.Infoln("No action specified.")
|
||||||
|
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When asking to metrics list we need application to be defined.
|
||||||
|
if *metricsList && *application == "" {
|
||||||
|
logger.Infoln("Getting metrics list requires 'application' parameter to be filled.")
|
||||||
|
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When asking for specific metric we need application to be defined.
|
||||||
|
if *metric != "" && *application == "" {
|
||||||
|
logger.Infoln("Getting metric data requires 'application' parameter to be filled.")
|
||||||
|
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConfig := &client.Config{
|
||||||
|
Host: *metricatorHost,
|
||||||
|
Timeout: *metricatorTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
c := client.NewClient(clientConfig, logger)
|
||||||
|
|
||||||
|
var data interface{}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case *appsList:
|
||||||
|
data = c.GetAppsList()
|
||||||
|
case *metricsList:
|
||||||
|
data = c.GetMetricsList(*application)
|
||||||
|
case *metric != "":
|
||||||
|
data = c.GetMetric(*application, *metric)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch *output {
|
||||||
|
case "json":
|
||||||
|
dataAsBytes, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
logger.Infoln("Failed to marshal data from Metricator:", err.Error())
|
||||||
|
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(dataAsBytes))
|
||||||
|
case "plain-by-lines":
|
||||||
|
// For plain mode if we request metric - we should just print it and exit.
|
||||||
|
if *metric != "" {
|
||||||
|
fmt.Println(data)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
dataToPrint := []string{}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case *appsList:
|
||||||
|
appsListData, ok := data.(schema.AppsList)
|
||||||
|
if !ok {
|
||||||
|
logger.Infoln("Failed to cast parsed data into schema.AppsList!")
|
||||||
|
|
||||||
|
os.Exit(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, app := range appsListData {
|
||||||
|
dataToPrint = append(dataToPrint, app)
|
||||||
|
}
|
||||||
|
case *metric != "":
|
||||||
|
metricData, ok := data.(string)
|
||||||
|
if !ok {
|
||||||
|
logger.Infoln("Failed to cast parsed data into string!")
|
||||||
|
|
||||||
|
os.Exit(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
dataToPrint = append(dataToPrint, metricData)
|
||||||
|
case *metricsList:
|
||||||
|
metricsData, ok := data.(schema.Metrics)
|
||||||
|
if !ok {
|
||||||
|
logger.Infoln("Failed to cast parsed data into schema.Metrics!")
|
||||||
|
|
||||||
|
os.Exit(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, metric := range metricsData {
|
||||||
|
dataToPrint = append(dataToPrint, metric.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range dataToPrint {
|
||||||
|
fmt.Println(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
docs/CLIENT.md
Normal file
53
docs/CLIENT.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Metricator Client
|
||||||
|
|
||||||
|
Metricator client was created to help with Metricator daemon communication. It is able to produce different output based on selected format.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Description |
|
||||||
|
| --------- | ---- | ----------- |
|
||||||
|
| `-application` | string | Name of application to query. |
|
||||||
|
| `-apps-list` | bool | Request type: applications list. |
|
||||||
|
| `-config` | string | Path to configuration file. **MANDATORY** |
|
||||||
|
| `-metric` | string | Request type: single metric. Name of metric to request. |
|
||||||
|
| `-metricator-host` | URL | URL to Metricator daemon. (e.g. `http://127.0.0.1:34421`). **MANDATORY** |
|
||||||
|
| `-metricator-timeout` | integer | Timeout in seconds for Metricator Client's HTTP requests. By default - 5 seconds. |
|
||||||
|
| `-metrics-list` | bool | Request type: list of metrics. **Requires `-application` parameter to be filled.** |
|
||||||
|
| `-output` | string | Type of output to produce (see below). |
|
||||||
|
|
||||||
|
## Request types
|
||||||
|
|
||||||
|
One of following parameters should be defined:
|
||||||
|
|
||||||
|
* `-apps-list` to get listing of applications that is registered at Metricator daemon.
|
||||||
|
* `-metrics-list` with `-application` parameters to get list of metrics for application which Metricator know.
|
||||||
|
* `-metric` with name of metric and `-application` (with a name of application) parameters to get specific metric for application which Metricator know.
|
||||||
|
|
||||||
|
See Examples section below.
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
Currently Metricator client is able to produce JSON and "Plain By Line" outputs. Their meanings:
|
||||||
|
|
||||||
|
* When `-output=json` is specified (or `-output` wasn't specified at all) Metricator Client will just dump response from Metricator Daemon.
|
||||||
|
* When `-output=plain-by-line` is specified Metricator Client will transform received data into line-by-line output, e.g. every application name on separate line, every metric name on separate line, etc.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
* Get list of applications registered at Metricator daemon line-by-line (for later use with metrics autodiscovery helper):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
metricator-client -config=./metricator.yaml -metricator-host http://127.0.0.1:34421 -output plain-by-line -apps-list
|
||||||
|
```
|
||||||
|
|
||||||
|
* Get list of metrics for application `test` in JSON format:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
metricator-client -config ./metricator.yaml -metricator-host http://127.0.0.1:34421 -application test -metrics-list
|
||||||
|
```
|
||||||
|
|
||||||
|
* Get specific metric for application `test`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
metricator-client -config ./metricator.yaml -metricator-host http://127.0.0.1:34421 -application test -metric mymegametric
|
||||||
|
```
|
||||||
@@ -31,3 +31,4 @@ dnsdist_frontend_responses/frontend:127.0.0.1:53/proto:UDP/thread:0
|
|||||||
* [Installation](INSTALL.md)
|
* [Installation](INSTALL.md)
|
||||||
* [Configuration](CONFIGURE.md)
|
* [Configuration](CONFIGURE.md)
|
||||||
* [API](API.md)
|
* [API](API.md)
|
||||||
|
* [Client](CLIENT.md)
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package application
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.dev.pztrn.name/metricator/internal/models"
|
"go.dev.pztrn.name/metricator/pkg/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parses passed body and returns a map suitable for pushing into storage.
|
// Parses passed body and returns a map suitable for pushing into storage.
|
||||||
func (a *Application) parse(body string) map[string]models.Metric {
|
func (a *Application) parse(body string) map[string]schema.Metric {
|
||||||
data := make(map[string]models.Metric)
|
data := make(map[string]schema.Metric)
|
||||||
|
|
||||||
// ToDo: switch to bytes buffer and maybe do not read body in caller?
|
// ToDo: switch to bytes buffer and maybe do not read body in caller?
|
||||||
splittedBody := strings.Split(body, "\n")
|
splittedBody := strings.Split(body, "\n")
|
||||||
@@ -35,7 +35,7 @@ func (a *Application) parse(body string) map[string]models.Metric {
|
|||||||
if !found {
|
if !found {
|
||||||
a.logger.Debugln("Metric wasn't yet created, creating new structure")
|
a.logger.Debugln("Metric wasn't yet created, creating new structure")
|
||||||
|
|
||||||
metric = models.NewMetric(name, "", "", nil)
|
metric = schema.NewMetric(name, "", "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.logger.Debugf("Got metric to use: %+v\n", metric)
|
a.logger.Debugf("Got metric to use: %+v\n", metric)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
|
// nolint:gochecknoglobals
|
||||||
var (
|
var (
|
||||||
Branch string
|
Branch string
|
||||||
Build string
|
Build string
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"go.dev.pztrn.name/metricator/internal/models"
|
"go.dev.pztrn.name/metricator/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// nolint:gochecknoglobals
|
||||||
var (
|
var (
|
||||||
errInvalidAPIVersion = errors.New("invalid API version")
|
errInvalidAPIVersion = errors.New("invalid API version")
|
||||||
errInvalidApplication = errors.New("invalid application")
|
errInvalidApplication = errors.New("invalid application")
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import "go.dev.pztrn.name/metricator/internal/models"
|
import "go.dev.pztrn.name/metricator/pkg/schema"
|
||||||
|
|
||||||
// GenericStorage describes interface every other storage should embed
|
// GenericStorage describes interface every other storage should embed
|
||||||
// and conform to as it contains essential things like context handling.
|
// and conform to as it contains essential things like context handling.
|
||||||
type GenericStorage interface {
|
type GenericStorage interface {
|
||||||
// Get returns data from storage by key.
|
// Get returns data from storage by key.
|
||||||
Get(string) (models.Metric, error)
|
Get(string) (schema.Metric, error)
|
||||||
// GetAsSlice returns all data from storage as slice.
|
// GetAsSlice returns all data from storage as slice.
|
||||||
GetAsSlice() []models.Metric
|
GetAsSlice() []schema.Metric
|
||||||
// GetDoneChan returns a channel which should be used to block execution
|
// GetDoneChan returns a channel which should be used to block execution
|
||||||
// until storage's routines are completed.
|
// until storage's routines are completed.
|
||||||
GetDoneChan() chan struct{}
|
GetDoneChan() chan struct{}
|
||||||
// Put puts passed data into storage.
|
// Put puts passed data into storage.
|
||||||
Put(map[string]models.Metric)
|
Put(map[string]schema.Metric)
|
||||||
// Start starts asynchronous things if needed.
|
// Start starts asynchronous things if needed.
|
||||||
Start()
|
Start()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"go.dev.pztrn.name/metricator/internal/logger"
|
"go.dev.pztrn.name/metricator/internal/logger"
|
||||||
"go.dev.pztrn.name/metricator/internal/models"
|
"go.dev.pztrn.name/metricator/pkg/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMetricNotFound appears if requested metric wasn't found in storage.
|
// ErrMetricNotFound appears if requested metric wasn't found in storage.
|
||||||
@@ -17,7 +17,7 @@ type Storage struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
doneChan chan struct{}
|
doneChan chan struct{}
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
data map[string]models.Metric
|
data map[string]schema.Metric
|
||||||
name string
|
name string
|
||||||
dataMutex sync.RWMutex
|
dataMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ func NewStorage(ctx context.Context, name string, logger *logger.Logger) (*Stora
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get returns data from storage by key.
|
// Get returns data from storage by key.
|
||||||
func (s *Storage) Get(key string) (models.Metric, error) {
|
func (s *Storage) Get(key string) (schema.Metric, error) {
|
||||||
s.logger.Debugln("Retrieving data for", key, "key from storage...")
|
s.logger.Debugln("Retrieving data for", key, "key from storage...")
|
||||||
|
|
||||||
s.dataMutex.RLock()
|
s.dataMutex.RLock()
|
||||||
@@ -46,7 +46,7 @@ func (s *Storage) Get(key string) (models.Metric, error) {
|
|||||||
if !found {
|
if !found {
|
||||||
s.logger.Infoln("Key", key, "not found in storage!")
|
s.logger.Infoln("Key", key, "not found in storage!")
|
||||||
|
|
||||||
return models.NewMetric("", "", "", nil), ErrMetricNotFound
|
return schema.NewMetric("", "", "", nil), ErrMetricNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Debugf("Key %s found: %+v\n", key, data)
|
s.logger.Debugf("Key %s found: %+v\n", key, data)
|
||||||
@@ -55,10 +55,10 @@ func (s *Storage) Get(key string) (models.Metric, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAsSlice returns all data from storage as slice.
|
// GetAsSlice returns all data from storage as slice.
|
||||||
func (s *Storage) GetAsSlice() []models.Metric {
|
func (s *Storage) GetAsSlice() []schema.Metric {
|
||||||
s.logger.Debugln("Returning all stored metrics as slice...")
|
s.logger.Debugln("Returning all stored metrics as slice...")
|
||||||
|
|
||||||
metrics := make([]models.Metric, 0, len(s.data))
|
metrics := make([]schema.Metric, 0, len(s.data))
|
||||||
|
|
||||||
for _, metric := range s.data {
|
for _, metric := range s.data {
|
||||||
metrics = append(metrics, metric)
|
metrics = append(metrics, metric)
|
||||||
@@ -75,11 +75,11 @@ func (s *Storage) GetDoneChan() chan struct{} {
|
|||||||
|
|
||||||
// Initializes internal things.
|
// Initializes internal things.
|
||||||
func (s *Storage) initialize() {
|
func (s *Storage) initialize() {
|
||||||
s.data = make(map[string]models.Metric)
|
s.data = make(map[string]schema.Metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put puts passed data into storage.
|
// Put puts passed data into storage.
|
||||||
func (s *Storage) Put(data map[string]models.Metric) {
|
func (s *Storage) Put(data map[string]schema.Metric) {
|
||||||
s.dataMutex.Lock()
|
s.dataMutex.Lock()
|
||||||
defer s.dataMutex.Unlock()
|
defer s.dataMutex.Unlock()
|
||||||
|
|
||||||
|
|||||||
146
pkg/client/client.go
Normal file
146
pkg/client/client.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.dev.pztrn.name/metricator/internal/logger"
|
||||||
|
"go.dev.pztrn.name/metricator/pkg/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is a Metricator client that is ready to be used in other applications
|
||||||
|
// or libraries.
|
||||||
|
type Client struct {
|
||||||
|
config *Config
|
||||||
|
logger *logger.Logger
|
||||||
|
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient creates new Metricator client.
|
||||||
|
func NewClient(config *Config, logger *logger.Logger) *Client {
|
||||||
|
c := &Client{
|
||||||
|
config: config,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
c.initialize()
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Executes request and parses it's contents.
|
||||||
|
func (c *Client) executeAndParse(req *http.Request, dest interface{}) error {
|
||||||
|
c.logger.Debugf("Executing HTTP request to %s%s", c.config.Host, req.URL.RequestURI())
|
||||||
|
|
||||||
|
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*time.Duration(c.config.Timeout))
|
||||||
|
defer cancelFunc()
|
||||||
|
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
response, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Infoln("Failed to execute request to Metricator:", err.Error())
|
||||||
|
|
||||||
|
return fmt.Errorf("metricator client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
respData, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Infoln("Failed to read response body:", err.Error())
|
||||||
|
|
||||||
|
return fmt.Errorf("metricator client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(respData, dest)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Infoln("Failed to parse response:", err.Error())
|
||||||
|
|
||||||
|
return fmt.Errorf("metricator client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAppsList returns a slice with applications that was registered at Metricator.
|
||||||
|
func (c *Client) GetAppsList() schema.AppsList {
|
||||||
|
address := fmt.Sprintf("%s/api/v1/apps_list", c.config.Host)
|
||||||
|
|
||||||
|
// Request's context sets in c.executeAndParse, so:
|
||||||
|
// nolint:noctx
|
||||||
|
req, err := http.NewRequest("GET", address, nil)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Infoln("Failed to create HTTP request:", err.Error())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
appsList := make(schema.AppsList, 0)
|
||||||
|
|
||||||
|
err = c.executeAndParse(req, &appsList)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return appsList
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetric returns value for metric.
|
||||||
|
func (c *Client) GetMetric(appName, metricName string) interface{} {
|
||||||
|
address := fmt.Sprintf("%s/api/v1/metrics/%s/%s", c.config.Host, appName, metricName)
|
||||||
|
|
||||||
|
// Request's context sets in c.executeAndParse, so:
|
||||||
|
// nolint:noctx
|
||||||
|
req, err := http.NewRequest("GET", address, nil)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Infoln("Failed to create HTTP request:", err.Error())
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var data interface{}
|
||||||
|
|
||||||
|
err = c.executeAndParse(req, &data)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetricsList returns a slice with metrics names for passed application.
|
||||||
|
func (c *Client) GetMetricsList(appName string) schema.Metrics {
|
||||||
|
address := fmt.Sprintf("%s/api/v1/metrics/%s", c.config.Host, appName)
|
||||||
|
|
||||||
|
// Request's context sets in c.executeAndParse, so:
|
||||||
|
// nolint:noctx
|
||||||
|
req, err := http.NewRequest("GET", address, nil)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Infoln("Failed to create HTTP request:", err.Error())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make(schema.Metrics, 0)
|
||||||
|
|
||||||
|
err = c.executeAndParse(req, &data)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializes internal states and storages.
|
||||||
|
func (c *Client) initialize() {
|
||||||
|
// We do not need to set everything for client actually, so:
|
||||||
|
// nolint:exhaustivestruct
|
||||||
|
c.httpClient = &http.Client{
|
||||||
|
Timeout: time.Second * time.Duration(c.config.Timeout),
|
||||||
|
}
|
||||||
|
}
|
||||||
9
pkg/client/config.go
Normal file
9
pkg/client/config.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
// Config is a Metricator client configuration.
|
||||||
|
type Config struct {
|
||||||
|
// Host is a host where Metricator is available for requests.
|
||||||
|
Host string
|
||||||
|
// Timeout specifies HTTP client timeout.
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
9
pkg/schema/apps_list.go
Normal file
9
pkg/schema/apps_list.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
// AppsList represents applications list structure from Metricator's API.
|
||||||
|
type AppsList []string
|
||||||
|
|
||||||
|
// IsEmpty returns true if returned applications list is empty.
|
||||||
|
func (a AppsList) IsEmpty() bool {
|
||||||
|
return len(a) == 0
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package models
|
package schema
|
||||||
|
|
||||||
// Metric is a generic metric structure.
|
// Metric is a generic metric structure. Used in HTTP responses and data storage.
|
||||||
type Metric struct {
|
type Metric struct {
|
||||||
// BaseName is a metric's base name, used for constructing name.
|
// BaseName is a metric's base name, used for constructing name.
|
||||||
BaseName string
|
BaseName string
|
||||||
9
pkg/schema/metrics.go
Normal file
9
pkg/schema/metrics.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
// Metrics is a metrics collection response.
|
||||||
|
type Metrics []*Metric
|
||||||
|
|
||||||
|
// IsEmpty returns true if returned applications list is empty.
|
||||||
|
func (m Metrics) IsEmpty() bool {
|
||||||
|
return len(m) == 0
|
||||||
|
}
|
||||||
@@ -2,7 +2,13 @@
|
|||||||
|
|
||||||
# Metricator build script.
|
# Metricator build script.
|
||||||
|
|
||||||
|
GO=${GO:=$(which go)}
|
||||||
|
|
||||||
|
if [ -d .git ]; then
|
||||||
source ./scripts/shell_helpers/get_git_data.sh
|
source ./scripts/shell_helpers/get_git_data.sh
|
||||||
|
else
|
||||||
|
source ./scripts/shell_helpers/get_release_data.sh
|
||||||
|
fi
|
||||||
|
|
||||||
WHATTOBUILD=$1
|
WHATTOBUILD=$1
|
||||||
|
|
||||||
@@ -13,5 +19,7 @@ LINKERFLAGS="\
|
|||||||
-X go.dev.pztrn.name/metricator/internal/common.Version=${VERSION}"
|
-X go.dev.pztrn.name/metricator/internal/common.Version=${VERSION}"
|
||||||
|
|
||||||
|
|
||||||
|
echo "Using $(go version) at ${GO}"
|
||||||
|
|
||||||
cd cmd/${WHATTOBUILD}
|
cd cmd/${WHATTOBUILD}
|
||||||
go build -tags netgo -ldflags "${LINKERFLAGS} -w -extldflags '-static'" -o ../../._bin/${WHATTOBUILD}
|
${GO} build -tags netgo -ldflags "${LINKERFLAGS} -w -extldflags '-static'" -o ../../._bin/${WHATTOBUILD}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# Gets git data.
|
# Gets git data.
|
||||||
# Should be sourced where neccessary.
|
# Should be sourced where neccessary.
|
||||||
|
|
||||||
export BRANCHNAME=${BRANCHNAME:=$(git branch --no-color --show-current)}
|
export BRANCHNAME=${BRANCHNAME:=$(git branch --no-color --show-current)}
|
||||||
export BUILDID=${BUILDID:=$(git rev-list HEAD --count)}
|
export BUILDID=${BUILDID:=$(git rev-list HEAD --count)}
|
||||||
export COMMITHASH=${COMMITHASH:=$(git rev-parse --verify HEAD)}
|
export COMMITHASH=${COMMITHASH:=$(git rev-parse --verify HEAD)}
|
||||||
export VERSION=${VERSION:="0.1.0-dev"}
|
export VERSION=${VERSION:=$(cat VERSION)}
|
||||||
|
|||||||
6
scripts/shell_helpers/get_release_data.sh
Normal file
6
scripts/shell_helpers/get_release_data.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Overrides some variables for release.
|
||||||
|
|
||||||
|
export VERSION=$(cat VERSION)
|
||||||
|
export BRANCHNAME="release/${VERSION}"
|
||||||
|
export COMMITHASH="none"
|
||||||
|
export BUILDID="0"
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Showing git data in console.
|
# Showing git data in console.
|
||||||
|
if [ -d .git ]; then
|
||||||
source ./scripts/shell_helpers/get_git_data.sh
|
source ./scripts/shell_helpers/get_git_data.sh
|
||||||
|
else
|
||||||
|
source ./scripts/shell_helpers/get_release_data.sh
|
||||||
|
fi
|
||||||
|
|
||||||
echo "* Branch: ${BRANCHNAME}"
|
echo "* Branch: ${BRANCHNAME}"
|
||||||
echo "* Build ID: ${BUILDID}"
|
echo "* Build ID: ${BUILDID}"
|
||||||
|
|||||||
Reference in New Issue
Block a user