diff --git a/.gitignore b/.gitignore index 8d062ab..cd3d5bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ .idea/ *.geany *~ +.project* .go-build/ +dist/ main sbercdn-exporter diff --git a/.goreleaser.yml b/.goreleaser.yml index 98fb698..b23ddad 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -48,13 +48,10 @@ changelog: - typo dockers: - - dockerfile: Dockerfile + - dockerfile: GoReleaserDockerfile image_templates: - "ghcr.io/rabotaru/sbercdn-exporter:{{ .Version }}" - "ghcr.io/rabotaru/sbercdn-exporter:latest" - - - "rabotaru/sbercdn-exporter:{{ .Version }}" - - "rabotaru/sbercdn-exporter:latest" build_flag_templates: - "--pull" - "--label=org.opencontainers.image.created={{.Date}}" diff --git a/Dockerfile b/Dockerfile index 1e1f93a..789e1f5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ARG GOLANG_VERSION=1.17 -ARG VERSION=0.3.0 +ARG VERSION=0.3.0rc1 FROM golang:${GOLANG_VERSION} AS builder ARG VERSION diff --git a/GoReleaserDockerfile b/GoReleaserDockerfile new file mode 100644 index 0000000..734af63 --- /dev/null +++ b/GoReleaserDockerfile @@ -0,0 +1,8 @@ +ARG GOLANG_VERSION=1.17 +ARG VERSION=0.3.0rc1 + +FROM alpine +LABEL maintainer="o.marin@rabota.ru" +COPY sbercdn-exporter /bin/ +EXPOSE 9921/tcp +ENTRYPOINT ["/bin/sbercdn-exporter"] diff --git a/README.md b/README.md index 2d207c2..ba134f7 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,10 @@ At this moment service may be configured from config and/or from env. There is only one cli flag now `-config` to get settings from YAML formatted config file for eg.: ``` listen: - address: ":9921" # mandatory, address to listen on + address: ":9921" # optional, address to listen on defaults to :9921 cert_file: # optional, path certificate file for endpoint encryption privkey_file: # optional, path to unencrypted private key file for endpoint encryption + # if both cert_file and privkey_file are not empty exporter will serve metrics through HTTPS api: url: "https://api.cdn.sber.cloud" # optional, API URL, defaults to https://api.cdn.sber.cloud @@ -29,6 +30,12 @@ yaml params full path in upper case with "SCE" prefix joined with "_" for eg. op ``` SCE_LISTEN_ADDRESS=":9921" SCE_API_URL="https://api.cdn.sber.cloud" -SCE_API_AUTH_USERNAME="username@example.com" -SCE_API_AUTH_PASSWORD="password" +SCE_API_USERNAME="username@example.com" +SCE_API_PASSWORD="password" +``` + +## Running in docker container + +``` +docker run --rm -e SCE_API_USERNAME="username@example.com" -e SCE_API_PASSWORD="password" -p 9921:9921/tcp ghcr.io/rabotaru/sbercdn-exporter:latest ``` diff --git a/apiclient/certificates.go b/apiclient/certificates.go index 3c42480..dfbe66a 100644 --- a/apiclient/certificates.go +++ b/apiclient/certificates.go @@ -7,7 +7,7 @@ import ( "log" "net/url" - cmn "git.rabota.space/infrastructure/sbercdn-exporter/common" + cmn "github.com/RabotaRu/sbercdn-exporter/common" ) type CertItem struct { diff --git a/apiclient/common.go b/apiclient/common.go index 20ac593..803d0b4 100644 --- a/apiclient/common.go +++ b/apiclient/common.go @@ -12,7 +12,7 @@ import ( "strings" "time" - cmn "git.rabota.space/infrastructure/sbercdn-exporter/common" + cmn "github.com/RabotaRu/sbercdn-exporter/common" ) const ( @@ -83,9 +83,12 @@ func (c *SberCdnApiClient) FindActiveAccounts(ctx context.Context) (accounts []s active_accs = append(active_accs, accs[i]["name"]) } } - c.active_accs = active_accs - t := time.Now() - c.accs_update_time = &t + if fmt.Sprintf("%v", c.active_accs) != fmt.Sprintf("%v", active_accs) { + log.Println("active accounts now:", active_accs) + c.active_accs = active_accs + t := time.Now() + c.accs_update_time = &t + } } return c.active_accs } @@ -93,8 +96,8 @@ func (c *SberCdnApiClient) FindActiveAccounts(ctx context.Context) (accounts []s func (c *SberCdnApiClient) auth(ctx context.Context) (auth_token string, err error) { defer func() { if r := recover(); r != nil { - log.Println("failed to update auth token:", r) auth_token = "" + log.Println("failed to update auth token:", r) } }() if c.auth_token != "" && time.Since(c.auth_token_time) < (c.TokenLifetime-c.MaxQueryTime) { @@ -112,20 +115,25 @@ func (c *SberCdnApiClient) auth(ctx context.Context) (auth_token string, err err ), ) if err != nil { - log.Panicln("Error creating new auth request") + err = fmt.Errorf("Error creating new auth request") + log.Panicln(err) } resp, err := c.hc.Do(req) if err != nil { - log.Panicln("Error sending auth request") + err = fmt.Errorf("Error sending auth request") + log.Panicln(err) } defer resp.Body.Close() - if resp.StatusCode != 200 { - log.Panicln(fmt.Errorf("auth response status code %v", resp.Status)) - } body, err := io.ReadAll(resp.Body) if err != nil { log.Panicln("Error reading auth response body") } + if resp.StatusCode != 200 { + reqbody, _ := io.ReadAll(req.Body) + err = fmt.Errorf("auth response status code %v", resp.Status) + log.Println(string(reqbody)) + log.Panicln(err) + } var data map[string]interface{} err = json.Unmarshal(body, &data) @@ -136,7 +144,7 @@ func (c *SberCdnApiClient) auth(ctx context.Context) (auth_token string, err err if auth_token, ok := data["token"].(string); ok { c.auth_token = auth_token c.auth_token_time = time.Now() - log.Println("Authorized successfully!") + log.Println("Authorized successfully on", c.Url) } else { err := fmt.Errorf("token is not a string") log.Panicln("%w", err) diff --git a/collector/cert.go b/collector/cert.go index c74e3d3..301ec9b 100644 --- a/collector/cert.go +++ b/collector/cert.go @@ -5,8 +5,8 @@ import ( "log" "sync" - "git.rabota.space/infrastructure/sbercdn-exporter/apiclient" - cmn "git.rabota.space/infrastructure/sbercdn-exporter/common" + "github.com/RabotaRu/sbercdn-exporter/apiclient" + cmn "github.com/RabotaRu/sbercdn-exporter/common" "github.com/prometheus/client_golang/prometheus" ) @@ -17,18 +17,19 @@ type SberCdnCertCollector struct { } func NewSberCdnCertCollector(client *apiclient.SberCdnApiClient) *SberCdnCertCollector { + labels := []string{"account", "comment", "cn"} return &SberCdnCertCollector{ client, map[string]*prometheus.Desc{ "cert_start": prometheus.NewDesc( "sbercdn_certificate_valid_since", "UNIX time in seconds since EPOCH since which certificate is valid", - []string{"account", "cn"}, + labels, nil), "cert_end": prometheus.NewDesc( "sbercdn_certificate_valid_till", "UNIX time in seconds since EPOCH till which certificate is valid", - []string{"account", "cn"}, + labels, nil), }, "certList", @@ -66,11 +67,12 @@ func (c *SberCdnCertCollector) Collect(mch chan<- prometheus.Metric) { for i := 0; i < len(certs); i++ { ci := &certs[i] mch <- prometheus.MustNewConstMetric(c.descriptions["cert_start"], prometheus.CounterValue, - ci.Start, account, ci.Cn) + ci.Start, account, ci.Comment, ci.Cn) mch <- prometheus.MustNewConstMetric(c.descriptions["cert_end"], prometheus.CounterValue, - ci.End, account, ci.Cn) + ci.End, account, ci.Comment, ci.Cn) } } + for _, acc := range c.client.FindActiveAccounts(ctx_root) { wag.Add(1) getCertMetrics(context.WithValue(ctx_root, cmn.CtxKey("account"), acc)) diff --git a/collector/collector.go b/collector/collector.go index a9fa3f8..b43cc33 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -7,8 +7,8 @@ import ( "sync" "time" - "git.rabota.space/infrastructure/sbercdn-exporter/apiclient" - cmn "git.rabota.space/infrastructure/sbercdn-exporter/common" + "github.com/RabotaRu/sbercdn-exporter/apiclient" + cmn "github.com/RabotaRu/sbercdn-exporter/common" "github.com/prometheus/client_golang/prometheus" ) diff --git a/go.mod b/go.mod index 44edab1..35e9225 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module git.rabota.space/infrastructure/sbercdn-exporter +module github.com/RabotaRu/sbercdn-exporter go 1.16 diff --git a/main.go b/main.go index 1d6fb4e..2da14dd 100644 --- a/main.go +++ b/main.go @@ -12,9 +12,9 @@ import ( "syscall" "time" - "git.rabota.space/infrastructure/sbercdn-exporter/apiclient" - col "git.rabota.space/infrastructure/sbercdn-exporter/collector" - cmn "git.rabota.space/infrastructure/sbercdn-exporter/common" + "github.com/RabotaRu/sbercdn-exporter/apiclient" + col "github.com/RabotaRu/sbercdn-exporter/collector" + cmn "github.com/RabotaRu/sbercdn-exporter/common" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -53,6 +53,10 @@ func main() { config.Listen.Address = "0.0.0.0" + config.Listen.Address } + if config.Client.Username == "" || config.Client.Username == "" { + log.Fatalln("API username or password is missing or empty") + } + apiClient, err := apiclient.NewSberCdnApiClient(&config.Client) if err != nil { log.Fatalf("failed to start api client: %v", err) diff --git a/sbercdn_grafana_dashboard.json b/sbercdn_grafana_dashboard.json new file mode 100644 index 0000000..dd1880d --- /dev/null +++ b/sbercdn_grafana_dashboard.json @@ -0,0 +1,744 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 384, + "links": [], + "liveNow": false, + "panels": [ + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 1, + "mappings": [], + "max": 31536000, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "#EAB839", + "value": 691200 + }, + { + "color": "green", + "value": 2678400 + } + ] + }, + "unit": "dtdhms" + }, + "overrides": [] + }, + "gridPos": { + "h": 24, + "w": 5, + "x": 0, + "y": 0 + }, + "id": 21, + "options": { + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "8.3.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "(sbercdn_certificate_valid_till - time())", + "interval": "", + "legendFormat": "{{cn}}", + "refId": "A" + } + ], + "title": "Certificates valid days", + "transparent": true, + "type": "gauge" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 15, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 19, + "x": 5, + "y": 0 + }, + "id": 10, + "interval": "1m", + "options": { + "legend": { + "calcs": [ + "min", + "mean", + "max", + "sum" + ], + "displayMode": "table", + "placement": "right" + }, + "tooltip": { + "mode": "multi" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_traffic", + "interval": "", + "legendFormat": "Totall", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_resource_traffic", + "hide": false, + "interval": "", + "legendFormat": "{{resource}}", + "refId": "B" + } + ], + "title": "Traffic by resource", + "type": "timeseries" + }, + { + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 19, + "x": 5, + "y": 6 + }, + "id": 3, + "interval": "1m", + "options": { + "legend": { + "calcs": [ + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "right" + }, + "tooltip": { + "mode": "multi" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_bandwidth", + "interval": "", + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_resource_bandwidth", + "hide": false, + "interval": "", + "legendFormat": "{{resource}}", + "refId": "B" + } + ], + "title": "Bandwidth by resource", + "type": "timeseries" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 19, + "x": 5, + "y": 12 + }, + "id": 19, + "interval": "1m", + "options": { + "legend": { + "calcs": [ + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "right" + }, + "tooltip": { + "mode": "multi" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_cache_ratio", + "interval": "", + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_resource_cache_ratio", + "hide": false, + "interval": "", + "legendFormat": "{{resource}}", + "refId": "B" + } + ], + "title": "Cache ratio by resource", + "type": "timeseries" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 19, + "x": 5, + "y": 18 + }, + "id": 16, + "interval": "1m", + "options": { + "legend": { + "calcs": [ + "sum", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "right", + "sortBy": "Total", + "sortDesc": true + }, + "tooltip": { + "mode": "multi" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_hits", + "interval": "", + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_resource_hits", + "hide": false, + "interval": "", + "legendFormat": "{{resource}}", + "refId": "B" + } + ], + "title": "Hits by resource", + "type": "timeseries" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 18, + "interval": "1m", + "options": { + "legend": { + "calcs": [ + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "right" + }, + "tooltip": { + "mode": "multi" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_cache_ratio", + "interval": "", + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_code_cache_ratio", + "hide": false, + "interval": "", + "legendFormat": "{{code}}", + "refId": "B" + } + ], + "title": "Cache ratio by code", + "type": "timeseries" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 25, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 14, + "interval": "1m", + "options": { + "legend": { + "calcs": [ + "min", + "mean", + "max", + "sum" + ], + "displayMode": "table", + "placement": "right" + }, + "tooltip": { + "mode": "multi" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_hits", + "interval": "", + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "exemplar": true, + "expr": "sbercdn_code_hits", + "hide": false, + "interval": "", + "legendFormat": "{{code}}", + "refId": "B" + } + ], + "title": "Hits by code", + "type": "timeseries" + } + ], + "refresh": false, + "schemaVersion": 34, + "style": "dark", + "tags": [ + "CDN" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "nowDelay": "5m" + }, + "timezone": "", + "title": "SberCDN", + "uid": "UebgIcx7k", + "version": 23, + "weekStart": "" +}