From 6986db8527bf8dac8cb6efb25195fcbcd86abeb0 Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 15:17:58 +0100 Subject: [PATCH 01/14] add the version/build_info stuff --- Makefile | 37 +++++++++++++++++++------- main.go | 28 +++++++++++++++++++- pkg/version/version.go | 60 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 pkg/version/version.go diff --git a/Makefile b/Makefile index ccd14c5..bcf7c79 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,32 @@ -.PHONY: clean run +## +# +.DEFAULT_GOAL := help -linux: - GOOS=linux GOARCH=amd64 go build -o output/mystrom-exporter_linux-amd64 -mac: - GOOS=darwin GOARCH=amd64 go build -o output/mystrom-exporter_mac-amd64 +version := $(shell git describe --tags --always) +revision := $(shell git rev-parse HEAD) +branch := $(shell git rev-parse --abbrev-ref HEAD) +builduser := $(shell whoami) +builddate := $(shell date '+%FT%T_%Z') + +versionPkgPrefix := mystrom-exporter/pkg/version + +LDFLAGS := -w -s \ + -X $(versionPkgPrefix).Version=${version} \ + -X $(versionPkgPrefix).Revision=${revision} \ + -X $(versionPkgPrefix).Branch=${branch} \ + -X $(versionPkgPrefix).BuildUser=${builduser} \ + -X $(versionPkgPrefix).BuildDate=${builddate} +GOFLAGS := -v + +linux: ## builds the linux version of the exporter + GOOS=linux GOARCH=amd64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' +mac: ## builds the macos version of the exporter + GOOS=darwin GOARCH=amd64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' arm64: - GOOS=linux GOARCH=arm64 go build -o output/mystrom-exporter_linux-arm64 + GOOS=linux GOARCH=arm64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' arm: - GOOS=linux GOARCH=arm go build -o output/mystrom-exporter_linux-arm + GOOS=linux GOARCH=arm go build $(GOFLAGS) -ldflags '$(LDFLAGS)' - -all: linux mac arm64 arm \ No newline at end of file +# -- +help: + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/main.go b/main.go index f7f11ec..2379284 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + + "mystrom-exporter/pkg/version" ) type switchReport struct { @@ -30,6 +32,8 @@ var ( "Path under which to expose metrics") switchIP = flag.String("switch.ip-address", "", "IP address of the switch you try to monitor") + showVersion = flag.Bool("version", false, + "Show version information.") up = prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "up"), @@ -169,14 +173,36 @@ func main() { flag.Parse() + // Show version information + if *showVersion { + v, err := version.Print("mystrom_exporter") + if err != nil { + log.Fatalf("Failed to print version information: %#v", err) + } + + fmt.Fprintln(os.Stdout, v) + os.Exit(0) + } + if *switchIP == "" { flag.Usage() fmt.Println("\nNo switch.ip-address provided") os.Exit(1) } + // make the build information is available through a metric + buildInfo := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "scripts", + Name: "build_info", + Help: "A metric with a constant '1' value labeled by build information.", + }, + []string{"version", "revision", "branch", "goversion", "builddate", "builduser"}, + ) + buildInfo.WithLabelValues(version.Version, version.Revision, version.Branch, version.GoVersion, version.BuildDate, version.BuildUser).Set(1) + exporter := NewExporter(*switchIP) - prometheus.MustRegister(exporter) + prometheus.MustRegister(exporter, buildInfo) http.Handle(*metricsPath, promhttp.Handler()) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 0000000..5d52c04 --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,60 @@ +package version + +import ( + "bytes" + "fmt" + "runtime" + "strings" + "text/template" +) + +// Build information. Populated at build-time. +var ( + Version string + Revision string + Branch string + BuildUser string + BuildDate string + GoVersion = runtime.Version() +) + +// versionInfoTmpl contains the template used by Print. +var versionInfoTmpl = ` +{{.program}}, version {{.version}} (branch: {{.branch}}, revision: {{.revision}}) + build user: {{.buildUser}} + build date: {{.buildDate}} + go version: {{.goVersion}} +` + +// Print returns version information. +func Print(program string) (string, error) { + m := map[string]string{ + "program": program, + "version": Version, + "revision": Revision, + "branch": Branch, + "buildUser": BuildUser, + "buildDate": BuildDate, + "goVersion": GoVersion, + } + t, err := template.New("version").Parse(versionInfoTmpl) + if err != nil { + return "", err + } + + var buf bytes.Buffer + if err := t.ExecuteTemplate(&buf, "version", m); err != nil { + return "", err + } + return strings.TrimSpace(buf.String()), nil +} + +// Info returns version, branch and revision information. +func Info() string { + return fmt.Sprintf("(version=%s, branch=%s, revision=%s)", Version, Branch, Revision) +} + +// BuildContext returns goVersion, buildUser and buildDate information. +func BuildContext() string { + return fmt.Sprintf("(go=%s, user=%s, date=%s)", GoVersion, BuildUser, BuildDate) +} From 77bd05cd5be7ecc6aa67bc78fc719333d59f8f01 Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 15:25:59 +0100 Subject: [PATCH 02/14] switch to prometheus logging lib and write startup msg --- go.mod | 5 ++++- go.sum | 5 +++++ main.go | 14 +++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 1fdb4d1..7a1da03 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module mystrom-exporter go 1.15 -require github.com/prometheus/client_golang v1.10.0 +require ( + github.com/prometheus/client_golang v1.10.0 + github.com/prometheus/common v0.18.0 // indirect +) diff --git a/go.sum b/go.sum index a8c158b..d82c337 100644 --- a/go.sum +++ b/go.sum @@ -7,9 +7,11 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -138,6 +140,7 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -237,6 +240,7 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -376,6 +380,7 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main.go b/main.go index 2379284..087c823 100644 --- a/main.go +++ b/main.go @@ -5,13 +5,13 @@ import ( "flag" "fmt" "io/ioutil" - "log" "net/http" "os" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/common/log" "mystrom-exporter/pkg/version" ) @@ -96,7 +96,7 @@ func (e *Exporter) FetchSwitchMetrics(switchIP string, ch chan<- prometheus.Metr report, err := FetchReport(switchIP) if err != nil { - log.Printf("Error occured, while fetching metrics: %s", err) + log.Infof("Error occured, while fetching metrics: %s", err) ch <- prometheus.MustNewConstMetric( up, prometheus.GaugeValue, 0, ) @@ -128,7 +128,7 @@ func (e *Exporter) FetchSwitchMetrics(switchIP string, ch chan<- prometheus.Metr } func FetchReport(switchIP string) (*switchReport, error) { - log.Printf("Trying to connect to switch at: %s\n", switchIP) + log.Infof("Trying to connect to switch at: %s", switchIP) url := "http://" + switchIP + "/report" switchClient := http.Client{ @@ -144,7 +144,7 @@ func FetchReport(switchIP string) (*switchReport, error) { res, getErr := switchClient.Do(req) if getErr != nil { - log.Printf("Error while trying to connect to switch: %s\n", getErr) + log.Infof("Error while trying to connect to switch: %s", getErr) return nil, getErr } @@ -155,14 +155,14 @@ func FetchReport(switchIP string) (*switchReport, error) { body, readErr := ioutil.ReadAll(res.Body) if readErr != nil { - log.Printf("Error while reading body: %s\n", readErr) + log.Infof("Error while reading body: %s", readErr) return nil, readErr } report := switchReport{} err = json.Unmarshal(body, &report) if err != nil { - log.Printf("Error while unmarshaling report: %s\n", err) + log.Infof("Error while unmarshaling report: %s", err) return nil, err } @@ -220,6 +220,6 @@ func main() { log.Fatalf("Switch at address %s couldn't be reached. Ensure it is reachable before starting the exporter", *switchIP) } - log.Printf("Starting listener on %s\n", *listenAddress) + log.Infoln("Listening on address " + *listenAddress) log.Fatal(http.ListenAndServe(*listenAddress, nil)) } From b0dd938b5decd178bf3630d6659e272b23fe8f8f Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 15:35:28 +0100 Subject: [PATCH 03/14] fix definition of type tags --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 087c823..39daae0 100644 --- a/main.go +++ b/main.go @@ -19,8 +19,8 @@ import ( type switchReport struct { Power float64 `json:"power"` WattPerSec float64 `json:"Ws"` - Relay bool `json:relay` - Temperature float64 `json:"temperature` + Relay bool `json:"relay"` + Temperature float64 `json:"temperature"` } const namespace = "mystrom" From e2639a5638426a745cfb3907bbf70680a1b4031a Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 16:36:04 +0100 Subject: [PATCH 04/14] put the scraping in own package --- Makefile | 2 + README.md | 27 ++++- main.go | 227 ++++++++++++----------------------------- pkg/mystrom/mystrom.go | 153 +++++++++++++++++++++++++++ 4 files changed, 247 insertions(+), 162 deletions(-) create mode 100644 pkg/mystrom/mystrom.go diff --git a/Makefile b/Makefile index bcf7c79..365589e 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,8 @@ linux: ## builds the linux version of the exporter GOOS=linux GOARCH=amd64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' mac: ## builds the macos version of the exporter GOOS=darwin GOARCH=amd64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' +mac-arm: ## builds the macos (m1) version of the exporter + GOOS=darwin GOARCH=arm64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' arm64: GOOS=linux GOARCH=arm64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' arm: diff --git a/README.md b/README.md index 1d28069..52a4561 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,31 @@ $ ./mystrom-exporter --help ``` | Flag | Description | Default | | ---- | ----------- | ------- | -| switch.ip-address | IP address of the switch you try to monitor | `` | | web.listen-address | Address to listen on | `:9452` | -| web.metrics-path | Path under which to expose metrics | `/metrics` | +| web.metrics-path | Path under which to expose exporters own metrics | `/metrics` | +| web.device-path | Path under which the metrics of the devices are fetched | `/device` | + +## Prometheus configuration +A enhancement has been made to have only one exporter which can scrape multiple devices. This is configured in +Prometheus as follows assuming we have 4 mystrom devices and the exporter is running locally on the smae machine as +the Prometheus. +```yaml + - job_name: mystrom + scrape_interval: 30s + metrics_path: /device + honor_labels: true + static_configs: + - targets: + - '192.168.105.11' + - '192.168.105.12' + - '192.168.105.13' + - '192.168.105.14' + relabel_configs: + - source_labels: [__address__] + target_label: __param_target + - target_label: __address__ + replacement: 127.0.0.1:9452 +``` ## Supported architectures Using the make file, you can easily build for the following architectures, those can also be considered the tested ones: @@ -40,6 +62,7 @@ Using the make file, you can easily build for the following architectures, those | Linux | arm64 | | Linux | arm | | Mac | amd64 | +| Mac | arm64 | Since go is cross compatible with windows, and mac arm as well, you should be able to build the binary for those as well, but they aren't tested. The docker image is only built & tested for amd64. diff --git a/main.go b/main.go index 39daae0..fd58ca8 100644 --- a/main.go +++ b/main.go @@ -1,18 +1,18 @@ package main import ( - "encoding/json" "flag" "fmt" - "io/ioutil" "net/http" "os" + "strings" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/log" + "mystrom-exporter/pkg/mystrom" "mystrom-exporter/pkg/version" ) @@ -29,151 +29,22 @@ var ( listenAddress = flag.String("web.listen-address", ":9452", "Address to listen on") metricsPath = flag.String("web.metrics-path", "/metrics", - "Path under which to expose metrics") - switchIP = flag.String("switch.ip-address", "", - "IP address of the switch you try to monitor") + "Path under which to expose exporters own metrics") + devicePath = flag.String("web.device-path", "/device", + "Path under which the metrics of the devices are fetched") showVersion = flag.Bool("version", false, "Show version information.") - - up = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "up"), - "Was the last myStrom query successful.", - nil, nil, - ) - myStromPower = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_power"), - "The current power consumed by devices attached to the switch", - nil, nil, - ) - - myStromRelay = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_relay"), - "The current state of the relay (wether or not the relay is currently turned on)", - nil, nil, - ) - - myStromTemperature = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_temperatur"), - "The currently measured temperature by the switch. (Might initially be wrong, but will automatically correct itself over the span of a few hours)", - nil, nil, - ) - - myStromWattPerSec = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_watt_per_sec"), - "The average of energy consumed per second from last call this request", - nil, nil, - ) ) - -type Exporter struct { - myStromSwitchIp string -} - -func NewExporter(myStromSwitchIp string) *Exporter { - return &Exporter{ - myStromSwitchIp: myStromSwitchIp, - } -} - -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - ch <- up - ch <- myStromPower - ch <- myStromRelay - ch <- myStromTemperature - ch <- myStromWattPerSec -} - -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - ch <- prometheus.MustNewConstMetric( - up, prometheus.GaugeValue, 1, - ) - - e.FetchSwitchMetrics(e.myStromSwitchIp, ch) -} - -func (e *Exporter) FetchSwitchMetrics(switchIP string, ch chan<- prometheus.Metric) { - - report, err := FetchReport(switchIP) - - if err != nil { - log.Infof("Error occured, while fetching metrics: %s", err) - ch <- prometheus.MustNewConstMetric( - up, prometheus.GaugeValue, 0, - ) - return - } - - ch <- prometheus.MustNewConstMetric( - myStromPower, prometheus.GaugeValue, report.Power, - ) - - if report.Relay { - ch <- prometheus.MustNewConstMetric( - myStromRelay, prometheus.GaugeValue, 1, - ) - } else { - ch <- prometheus.MustNewConstMetric( - myStromRelay, prometheus.GaugeValue, 0, - ) - } - - ch <- prometheus.MustNewConstMetric( - myStromWattPerSec, prometheus.GaugeValue, report.WattPerSec, - ) - - ch <- prometheus.MustNewConstMetric( - myStromTemperature, prometheus.GaugeValue, report.Temperature, - ) - -} - -func FetchReport(switchIP string) (*switchReport, error) { - log.Infof("Trying to connect to switch at: %s", switchIP) - url := "http://" + switchIP + "/report" - - switchClient := http.Client{ - Timeout: time.Second * 5, // 3 second timeout, might need to be increased - } - - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return nil, err - } - - req.Header.Set("User-Agent", "myStrom-exporter") - - res, getErr := switchClient.Do(req) - if getErr != nil { - log.Infof("Error while trying to connect to switch: %s", getErr) - return nil, getErr - - } - - if res.Body != nil { - defer res.Body.Close() - } - - body, readErr := ioutil.ReadAll(res.Body) - if readErr != nil { - log.Infof("Error while reading body: %s", readErr) - return nil, readErr - } - - report := switchReport{} - err = json.Unmarshal(body, &report) - if err != nil { - log.Infof("Error while unmarshaling report: %s", err) - return nil, err - } - - return &report, nil -} +var ( + mystromDurationCounterVec *prometheus.CounterVec + mystromRequestsCounterVec *prometheus.CounterVec +) func main() { flag.Parse() - // Show version information + // -- show version information if *showVersion { v, err := version.Print("mystrom_exporter") if err != nil { @@ -184,13 +55,18 @@ func main() { os.Exit(0) } - if *switchIP == "" { - flag.Usage() - fmt.Println("\nNo switch.ip-address provided") - os.Exit(1) - } + // -- create a new registry for the exporter telemetry + telemetryRegistry := prometheus.NewRegistry() + telemetryRegistry.MustRegister(prometheus.NewGoCollector()) + telemetryRegistry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{})) - // make the build information is available through a metric + mystromDurationCounterVec = prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "mystrom_request_duration_seconds_total", + Help: "Total duration of mystrom successful requests by target in seconds", + }, []string{"target"}) + telemetryRegistry.MustRegister(mystromDurationCounterVec) + + // -- make the build information is available through a metric buildInfo := prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: "scripts", @@ -200,26 +76,57 @@ func main() { []string{"version", "revision", "branch", "goversion", "builddate", "builduser"}, ) buildInfo.WithLabelValues(version.Version, version.Revision, version.Branch, version.GoVersion, version.BuildDate, version.BuildUser).Set(1) + telemetryRegistry.MustRegister(buildInfo) - exporter := NewExporter(*switchIP) - prometheus.MustRegister(exporter, buildInfo) + exporter := mystrom.NewExporter() + // prometheus.MustRegister(exporter) - http.Handle(*metricsPath, promhttp.Handler()) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + router := http.NewServeMux() + router.Handle(*metricsPath, promhttp.HandlerFor(telemetryRegistry, promhttp.HandlerOpts{})) + router.Handle(*devicePath, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + scrapeHandler(exporter, w, r) + }), + ) + router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` - myStrom switch report Exporter - -

myStrom Exporter

-

Metrics

- - `)) + myStrom switch report Exporter + +

myStrom Exporter

+

Metrics

+ + `)) }) + log.Infoln("Listening on address " + *listenAddress) + log.Fatal(http.ListenAndServe(*listenAddress, router)) +} - _, err := FetchReport(*switchIP) - if err != nil { - log.Fatalf("Switch at address %s couldn't be reached. Ensure it is reachable before starting the exporter", *switchIP) +func scrapeHandler(e *mystrom.Exporter, w http.ResponseWriter, r *http.Request) { + target := r.URL.Query().Get("target") + if target == "" { + http.Error(w, "'target' parameter must be specified", http.StatusBadRequest) + return } - log.Infoln("Listening on address " + *listenAddress) - log.Fatal(http.ListenAndServe(*listenAddress, nil)) + log.Infof("got scrape request for target '%v'", target) + + start := time.Now() + gatherer, err := e.Scrape(target) + duration := time.Since(start).Seconds() + if err != nil { + if strings.Contains(fmt.Sprintf("%v", err), "unable to connect with target") { + } else if strings.Contains(fmt.Sprintf("%v", err), "i/o timeout") { + } + http.Error( + w, + fmt.Sprintf("failed to scrape target '%v': %v", target, err), + http.StatusInternalServerError, + ) + log.Error(err) + return + } + mystromDurationCounterVec.WithLabelValues(target).Add(duration) + + promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}).ServeHTTP(w, r) + } diff --git a/pkg/mystrom/mystrom.go b/pkg/mystrom/mystrom.go new file mode 100644 index 0000000..8b00aea --- /dev/null +++ b/pkg/mystrom/mystrom.go @@ -0,0 +1,153 @@ +package mystrom + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" + + "github.com/prometheus/client_golang/prometheus" +) + +const namespace = "mystrom" + +type switchReport struct { + Power float64 `json:"power"` + WattPerSec float64 `json:"Ws"` + Relay bool `json:relay` + Temperature float64 `json:"temperature` +} + +type Exporter struct { + myStromSwitchIp string +} + +var ( + up = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "up"), + "Was the last myStrom query successful.", + nil, nil, + ) + myStromPower = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "report_power"), + "The current power consumed by devices attached to the switch", + nil, nil, + ) + + myStromRelay = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "report_relay"), + "The current state of the relay (wether or not the relay is currently turned on)", + nil, nil, + ) + + myStromTemperature = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "report_temperatur"), + "The currently measured temperature by the switch. (Might initially be wrong, but will automatically correct itself over the span of a few hours)", + nil, nil, + ) + + myStromWattPerSec = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "report_watt_per_sec"), + "The average of energy consumed per second from last call this request", + nil, nil, + ) +) + +func NewExporter() *Exporter { + return &Exporter{ + // myStromSwitchIp: myStromSwitchIp, + } +} + +func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { + ch <- up + ch <- myStromPower + ch <- myStromRelay + ch <- myStromTemperature + ch <- myStromWattPerSec +} + +func (e *Exporter) Collect(ch chan<- prometheus.Metric) { + ch <- prometheus.MustNewConstMetric( + up, prometheus.GaugeValue, 1, + ) + + e.FetchSwitchMetrics(e.myStromSwitchIp, ch) +} + +func (e *Exporter) FetchSwitchMetrics(switchIP string, ch chan<- prometheus.Metric) { + + url := "http://" + switchIP + "/report" + + switchClient := http.Client{ + Timeout: time.Second * 5, // 3 second timeout, might need to be increased + } + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + ch <- prometheus.MustNewConstMetric( + up, prometheus.GaugeValue, 0, + ) + } + + req.Header.Set("User-Agent", "myStrom-exporter") + + res, getErr := switchClient.Do(req) + if getErr != nil { + ch <- prometheus.MustNewConstMetric( + up, prometheus.GaugeValue, 0, + ) + + } + + if res.Body != nil { + defer res.Body.Close() + } + + body, readErr := ioutil.ReadAll(res.Body) + if readErr != nil { + ch <- prometheus.MustNewConstMetric( + up, prometheus.GaugeValue, 0, + ) + } + + report := switchReport{} + err = json.Unmarshal(body, &report) + if err != nil { + fmt.Println(err) + ch <- prometheus.MustNewConstMetric( + up, prometheus.GaugeValue, 0, + ) + return + } + + ch <- prometheus.MustNewConstMetric( + myStromPower, prometheus.GaugeValue, report.Power, + ) + + if report.Relay { + ch <- prometheus.MustNewConstMetric( + myStromRelay, prometheus.GaugeValue, 1, + ) + } else { + ch <- prometheus.MustNewConstMetric( + myStromRelay, prometheus.GaugeValue, 0, + ) + } + + ch <- prometheus.MustNewConstMetric( + myStromWattPerSec, prometheus.GaugeValue, report.WattPerSec, + ) + + ch <- prometheus.MustNewConstMetric( + myStromTemperature, prometheus.GaugeValue, report.Temperature, + ) + +} + +func (e *Exporter) Scrape(targetAddress string) (prometheus.Gatherer, error) { + reg := prometheus.NewRegistry() + + return reg, nil +} From 7d8ac22bb17fed78f15adf8b6de1bf246ef28892 Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 16:46:18 +0100 Subject: [PATCH 05/14] use stringer to generate enum type --- Makefile | 18 +++++++++++++----- main.go | 27 ++++++++++++++++++++------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 365589e..3d3d494 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ ## # .DEFAULT_GOAL := help +.PHONY: generate version := $(shell git describe --tags --always) revision := $(shell git rev-parse HEAD) @@ -18,17 +19,24 @@ LDFLAGS := -w -s \ -X $(versionPkgPrefix).BuildDate=${builddate} GOFLAGS := -v -linux: ## builds the linux version of the exporter +linux: generate ## builds the linux version of the exporter GOOS=linux GOARCH=amd64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -mac: ## builds the macos version of the exporter +mac: generate ## builds the macos version of the exporter GOOS=darwin GOARCH=amd64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -mac-arm: ## builds the macos (m1) version of the exporter +mac-arm: generate ## builds the macos (m1) version of the exporter GOOS=darwin GOARCH=arm64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -arm64: +arm64: generate GOOS=linux GOARCH=arm64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -arm: +arm: generate GOOS=linux GOARCH=arm go build $(GOFLAGS) -ldflags '$(LDFLAGS)' +# -- see more info on https://pkg.go.dev/golang.org/x/tools/cmd/stringer +generate: $(GOPATH)/bin/stringer + go generate ./... + +$(GOPATH)/bin/stringer: + go install golang.org/x/tools/cmd/stringer@latest + # -- help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/main.go b/main.go index fd58ca8..dc8ae82 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,4 @@ +//go:generate stringer -type MystromReqStatus main.go package main import ( @@ -16,14 +17,15 @@ import ( "mystrom-exporter/pkg/version" ) -type switchReport struct { - Power float64 `json:"power"` - WattPerSec float64 `json:"Ws"` - Relay bool `json:"relay"` - Temperature float64 `json:"temperature"` -} +// -- MystromRequestStatusType represents the request to MyStrom device status +type MystromReqStatus uint32 -const namespace = "mystrom" +const ( + OK MystromReqStatus = iota + ERROR_SOCKET + ERROR_TIMEOUT + ERROR_PARSING_VALUE +) var ( listenAddress = flag.String("web.listen-address", ":9452", @@ -66,6 +68,12 @@ func main() { }, []string{"target"}) telemetryRegistry.MustRegister(mystromDurationCounterVec) + mystromRequestsCounterVec = prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "mystrom_requests_total", + Help: "Number of mystrom request by status and target", + }, []string{"target", "status"}) + telemetryRegistry.MustRegister(mystromRequestsCounterVec) + // -- make the build information is available through a metric buildInfo := prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -115,7 +123,11 @@ func scrapeHandler(e *mystrom.Exporter, w http.ResponseWriter, r *http.Request) duration := time.Since(start).Seconds() if err != nil { if strings.Contains(fmt.Sprintf("%v", err), "unable to connect with target") { + mystromRequestsCounterVec.WithLabelValues(target, ERROR_SOCKET.String()).Inc() } else if strings.Contains(fmt.Sprintf("%v", err), "i/o timeout") { + mystromRequestsCounterVec.WithLabelValues(target, ERROR_TIMEOUT.String()).Inc() + } else { + mystromRequestsCounterVec.WithLabelValues(target, ERROR_PARSING_VALUE.String()).Inc() } http.Error( w, @@ -126,6 +138,7 @@ func scrapeHandler(e *mystrom.Exporter, w http.ResponseWriter, r *http.Request) return } mystromDurationCounterVec.WithLabelValues(target).Add(duration) + mystromRequestsCounterVec.WithLabelValues(target, OK.String()).Inc() promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}).ServeHTTP(w, r) From c49231374e6917c609a2e971a1a2699794154bd8 Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 16:46:56 +0100 Subject: [PATCH 06/14] update modules with go mod tidy --- go.mod | 2 +- go.sum | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 7a1da03..629a3bf 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,5 @@ go 1.15 require ( github.com/prometheus/client_golang v1.10.0 - github.com/prometheus/common v0.18.0 // indirect + github.com/prometheus/common v0.18.0 ) diff --git a/go.sum b/go.sum index d82c337..7a50d30 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -143,8 +144,10 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= @@ -201,6 +204,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -255,6 +259,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -354,6 +359,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -384,6 +390,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -397,6 +404,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From f5914673543279e3e06396a32f4141b3d4f717bb Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 16:48:29 +0100 Subject: [PATCH 07/14] extract the landingpage out from the code to a variable --- main.go | 15 ++++++++------- pkg/mystrom/mystrom.go | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index dc8ae82..4b71a50 100644 --- a/main.go +++ b/main.go @@ -41,6 +41,13 @@ var ( mystromDurationCounterVec *prometheus.CounterVec mystromRequestsCounterVec *prometheus.CounterVec ) +var landingPage = []byte(` +myStrom switch report Exporter + +

myStrom Exporter

+

Metrics

+ +`) func main() { @@ -97,13 +104,7 @@ func main() { }), ) router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(` - myStrom switch report Exporter - -

myStrom Exporter

-

Metrics

- - `)) + w.Write(landingPage) }) log.Infoln("Listening on address " + *listenAddress) log.Fatal(http.ListenAndServe(*listenAddress, router)) diff --git a/pkg/mystrom/mystrom.go b/pkg/mystrom/mystrom.go index 8b00aea..e54c53b 100644 --- a/pkg/mystrom/mystrom.go +++ b/pkg/mystrom/mystrom.go @@ -15,8 +15,8 @@ const namespace = "mystrom" type switchReport struct { Power float64 `json:"power"` WattPerSec float64 `json:"Ws"` - Relay bool `json:relay` - Temperature float64 `json:"temperature` + Relay bool `json:"relay"` + Temperature float64 `json:"temperature"` } type Exporter struct { From 23b598736495e8fc54d8f04cfe05185c47546e6c Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 16:49:57 +0100 Subject: [PATCH 08/14] change to multidevice scrape and adding info --- main.go | 84 +++++++------- pkg/mystrom/mystrom.go | 245 +++++++++++++++++++++++++---------------- 2 files changed, 199 insertions(+), 130 deletions(-) diff --git a/main.go b/main.go index 4b71a50..e7e8bd7 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,8 @@ const ( ERROR_PARSING_VALUE ) +const namespace = "mystrom_exporter" + var ( listenAddress = flag.String("web.listen-address", ":9452", "Address to listen on") @@ -65,44 +67,11 @@ func main() { } // -- create a new registry for the exporter telemetry - telemetryRegistry := prometheus.NewRegistry() - telemetryRegistry.MustRegister(prometheus.NewGoCollector()) - telemetryRegistry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{})) - - mystromDurationCounterVec = prometheus.NewCounterVec(prometheus.CounterOpts{ - Name: "mystrom_request_duration_seconds_total", - Help: "Total duration of mystrom successful requests by target in seconds", - }, []string{"target"}) - telemetryRegistry.MustRegister(mystromDurationCounterVec) - - mystromRequestsCounterVec = prometheus.NewCounterVec(prometheus.CounterOpts{ - Name: "mystrom_requests_total", - Help: "Number of mystrom request by status and target", - }, []string{"target", "status"}) - telemetryRegistry.MustRegister(mystromRequestsCounterVec) - - // -- make the build information is available through a metric - buildInfo := prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: "scripts", - Name: "build_info", - Help: "A metric with a constant '1' value labeled by build information.", - }, - []string{"version", "revision", "branch", "goversion", "builddate", "builduser"}, - ) - buildInfo.WithLabelValues(version.Version, version.Revision, version.Branch, version.GoVersion, version.BuildDate, version.BuildUser).Set(1) - telemetryRegistry.MustRegister(buildInfo) - - exporter := mystrom.NewExporter() - // prometheus.MustRegister(exporter) + telemetryRegistry := setupMetrics() router := http.NewServeMux() router.Handle(*metricsPath, promhttp.HandlerFor(telemetryRegistry, promhttp.HandlerOpts{})) - router.Handle(*devicePath, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - scrapeHandler(exporter, w, r) - }), - ) + router.HandleFunc(*devicePath, scrapeHandler) router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write(landingPage) }) @@ -110,7 +79,7 @@ func main() { log.Fatal(http.ListenAndServe(*listenAddress, router)) } -func scrapeHandler(e *mystrom.Exporter, w http.ResponseWriter, r *http.Request) { +func scrapeHandler(w http.ResponseWriter, r *http.Request) { target := r.URL.Query().Get("target") if target == "" { http.Error(w, "'target' parameter must be specified", http.StatusBadRequest) @@ -118,9 +87,10 @@ func scrapeHandler(e *mystrom.Exporter, w http.ResponseWriter, r *http.Request) } log.Infof("got scrape request for target '%v'", target) + exporter := mystrom.NewExporter(target) start := time.Now() - gatherer, err := e.Scrape(target) + gatherer, err := exporter.Scrape() duration := time.Since(start).Seconds() if err != nil { if strings.Contains(fmt.Sprintf("%v", err), "unable to connect with target") { @@ -142,5 +112,43 @@ func scrapeHandler(e *mystrom.Exporter, w http.ResponseWriter, r *http.Request) mystromRequestsCounterVec.WithLabelValues(target, OK.String()).Inc() promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}).ServeHTTP(w, r) - +} + +// -- setupMetrics creates a new registry for the exporter telemetry +func setupMetrics() *prometheus.Registry { + registry := prometheus.NewRegistry() + registry.MustRegister(prometheus.NewGoCollector()) + registry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{})) + + mystromDurationCounterVec = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: namespace, + Name: "request_duration_seconds_total", + Help: "Total duration of mystrom successful requests by target in seconds", + }, + []string{"target"}) + registry.MustRegister(mystromDurationCounterVec) + + mystromRequestsCounterVec = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: namespace, + Name: "requests_total", + Help: "Number of mystrom request by status and target", + }, + []string{"target", "status"}) + registry.MustRegister(mystromRequestsCounterVec) + + // -- make the build information is available through a metric + buildInfo := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "build_info", + Help: "A metric with a constant '1' value labeled by build information.", + }, + []string{"version", "revision", "branch", "goversion", "builddate", "builduser"}, + ) + buildInfo.WithLabelValues(version.Version, version.Revision, version.Branch, version.GoVersion, version.BuildDate, version.BuildUser).Set(1) + registry.MustRegister(buildInfo) + + return registry } diff --git a/pkg/mystrom/mystrom.go b/pkg/mystrom/mystrom.go index e54c53b..097b702 100644 --- a/pkg/mystrom/mystrom.go +++ b/pkg/mystrom/mystrom.go @@ -7,11 +7,16 @@ import ( "net/http" "time" + "github.com/prometheus/common/log" + "github.com/prometheus/client_golang/prometheus" ) const namespace = "mystrom" +// 5 second timeout, might need to be increased +const reqTimeout = time.Second * 5 + type switchReport struct { Power float64 `json:"power"` WattPerSec float64 `json:"Ws"` @@ -19,135 +24,191 @@ type switchReport struct { Temperature float64 `json:"temperature"` } -type Exporter struct { - myStromSwitchIp string +type switchInfo struct { + Version string `json:"version"` + Mac string `json:"mac"` + SwType float64 `json:"type"` + SSID string `json:"ssid"` + Static bool `json:"static"` + Connected bool `json:"connected"` } -var ( - up = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "up"), - "Was the last myStrom query successful.", - nil, nil, - ) - myStromPower = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_power"), - "The current power consumed by devices attached to the switch", - nil, nil, - ) +// Exporter -- +type Exporter struct { + myStromSwitchIp string + switchType float64 +} - myStromRelay = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_relay"), - "The current state of the relay (wether or not the relay is currently turned on)", - nil, nil, - ) - - myStromTemperature = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_temperatur"), - "The currently measured temperature by the switch. (Might initially be wrong, but will automatically correct itself over the span of a few hours)", - nil, nil, - ) - - myStromWattPerSec = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_watt_per_sec"), - "The average of energy consumed per second from last call this request", - nil, nil, - ) -) - -func NewExporter() *Exporter { +// NewExporter -- +func NewExporter(switchIP string) *Exporter { return &Exporter{ - // myStromSwitchIp: myStromSwitchIp, + myStromSwitchIp: switchIP, } } -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - ch <- up - ch <- myStromPower - ch <- myStromRelay - ch <- myStromTemperature - ch <- myStromWattPerSec +// Scrape -- +func (e *Exporter) Scrape() (prometheus.Gatherer, error) { + reg := prometheus.NewRegistry() + + // -- + bodyInfo, err := e.fetchData("/api/v1/info") + if err != nil { + } + + info := switchInfo{} + err = json.Unmarshal(bodyInfo, &info) + if err != nil { + // fmt.Println(err) + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) + return reg, fmt.Errorf("unable to decode switchReport: %v", err.Error()) + } + log.Debugf("info: %#v", info) + e.switchType = info.SwType + + if err := registerInfoMetrics(reg, info, e.myStromSwitchIp); err != nil { + return nil, fmt.Errorf("failed to register metrics : %v", err.Error()) + } + + // -- + bodyData, err := e.fetchData("/report") + if err != nil { + } + + report := switchReport{} + err = json.Unmarshal(bodyData, &report) + if err != nil { + // fmt.Println(err) + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) + return reg, fmt.Errorf("unable to decode switchReport: %v", err.Error()) + } + log.Debugf("report: %#v", report) + + if err := registerMetrics(reg, report, e.myStromSwitchIp, e.switchType); err != nil { + return nil, fmt.Errorf("failed to register metrics : %v", err.Error()) + } + + return reg, nil } -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - ch <- prometheus.MustNewConstMetric( - up, prometheus.GaugeValue, 1, - ) - - e.FetchSwitchMetrics(e.myStromSwitchIp, ch) -} - -func (e *Exporter) FetchSwitchMetrics(switchIP string, ch chan<- prometheus.Metric) { - - url := "http://" + switchIP + "/report" +// fetchData -- get the data from the switch under the given path +func (e *Exporter) fetchData(urlpath string) ([]byte, error) { + url := "http://" + e.myStromSwitchIp + urlpath switchClient := http.Client{ - Timeout: time.Second * 5, // 3 second timeout, might need to be increased + Timeout: reqTimeout, + Transport: &http.Transport{ + DisableCompression: true, + }, } req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { - ch <- prometheus.MustNewConstMetric( - up, prometheus.GaugeValue, 0, - ) + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) } - req.Header.Set("User-Agent", "myStrom-exporter") res, getErr := switchClient.Do(req) if getErr != nil { - ch <- prometheus.MustNewConstMetric( - up, prometheus.GaugeValue, 0, - ) - + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) } - if res.Body != nil { defer res.Body.Close() } body, readErr := ioutil.ReadAll(res.Body) if readErr != nil { - ch <- prometheus.MustNewConstMetric( - up, prometheus.GaugeValue, 0, - ) + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) + return []byte{}, fmt.Errorf("unable to read body: %v", readErr.Error()) } - report := switchReport{} - err = json.Unmarshal(body, &report) - if err != nil { - fmt.Println(err) - ch <- prometheus.MustNewConstMetric( - up, prometheus.GaugeValue, 0, - ) - return + return body, nil +} + +// registerMetrics -- +func registerMetrics(reg prometheus.Registerer, data switchReport, target string, st float64) error { + + // -- + collectorRelay := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "relay", + Help: "The current state of the relay (wether or not the relay is currently turned on)", + }, + []string{"target"}) + + if err := reg.Register(collectorRelay); err != nil { + return fmt.Errorf("failed to register metric %v: %v", "relay", err.Error()) } - ch <- prometheus.MustNewConstMetric( - myStromPower, prometheus.GaugeValue, report.Power, - ) - - if report.Relay { - ch <- prometheus.MustNewConstMetric( - myStromRelay, prometheus.GaugeValue, 1, - ) + if data.Relay { + collectorRelay.WithLabelValues(target).Set(1) } else { - ch <- prometheus.MustNewConstMetric( - myStromRelay, prometheus.GaugeValue, 0, - ) + collectorRelay.WithLabelValues(target).Set(0) } - ch <- prometheus.MustNewConstMetric( - myStromWattPerSec, prometheus.GaugeValue, report.WattPerSec, - ) + if st != 114 { + // -- + collectorPower := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "power", + Help: "The current power consumed by devices attached to the switch", + }, + []string{"target"}) - ch <- prometheus.MustNewConstMetric( - myStromTemperature, prometheus.GaugeValue, report.Temperature, - ) + if err := reg.Register(collectorPower); err != nil { + return fmt.Errorf("failed to register metric %v: %v", "power", err.Error()) + } + collectorPower.WithLabelValues(target).Set(data.Power) + + // -- + collectorTemperature := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "temperature", + Help: "The currently measured temperature by the switch. (Might initially be wrong, but will automatically correct itself over the span of a few hours)", + }, + []string{"target"}) + + if err := reg.Register(collectorTemperature); err != nil { + return fmt.Errorf("failed to register metric %v: %v", "temperature", err.Error()) + } + + collectorTemperature.WithLabelValues(target).Set(data.Temperature) + + } + + return nil } -func (e *Exporter) Scrape(targetAddress string) (prometheus.Gatherer, error) { - reg := prometheus.NewRegistry() +// registerMetrics -- +func registerInfoMetrics(reg prometheus.Registerer, data switchInfo, target string) error { - return reg, nil + // -- + collectorInfo := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "info", + Help: "", + }, + []string{"target", "version", "mac", "type", "ssid"}) + + if err := reg.Register(collectorInfo); err != nil { + return fmt.Errorf("failed to register metric %v: %v", "info", err.Error()) + } + + collectorInfo.WithLabelValues(target, data.Version, data.Mac, fmt.Sprintf("%v", data.SwType), data.SSID).Set(1) + + return nil } From 7c2157ff5358795eb9e8aca5e292af3b2f214d14 Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 16:50:40 +0100 Subject: [PATCH 09/14] adapt the labeling --- pkg/mystrom/mystrom.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/mystrom/mystrom.go b/pkg/mystrom/mystrom.go index 097b702..3d789f1 100644 --- a/pkg/mystrom/mystrom.go +++ b/pkg/mystrom/mystrom.go @@ -144,7 +144,7 @@ func registerMetrics(reg prometheus.Registerer, data switchReport, target string Name: "relay", Help: "The current state of the relay (wether or not the relay is currently turned on)", }, - []string{"target"}) + []string{"instance"}) if err := reg.Register(collectorRelay); err != nil { return fmt.Errorf("failed to register metric %v: %v", "relay", err.Error()) @@ -164,7 +164,7 @@ func registerMetrics(reg prometheus.Registerer, data switchReport, target string Name: "power", Help: "The current power consumed by devices attached to the switch", }, - []string{"target"}) + []string{"instance"}) if err := reg.Register(collectorPower); err != nil { return fmt.Errorf("failed to register metric %v: %v", "power", err.Error()) @@ -179,7 +179,7 @@ func registerMetrics(reg prometheus.Registerer, data switchReport, target string Name: "temperature", Help: "The currently measured temperature by the switch. (Might initially be wrong, but will automatically correct itself over the span of a few hours)", }, - []string{"target"}) + []string{"instance"}) if err := reg.Register(collectorTemperature); err != nil { return fmt.Errorf("failed to register metric %v: %v", "temperature", err.Error()) @@ -200,9 +200,9 @@ func registerInfoMetrics(reg prometheus.Registerer, data switchInfo, target stri prometheus.GaugeOpts{ Namespace: namespace, Name: "info", - Help: "", + Help: "general information about the device", }, - []string{"target", "version", "mac", "type", "ssid"}) + []string{"instance", "version", "mac", "type", "ssid"}) if err := reg.Register(collectorInfo); err != nil { return fmt.Errorf("failed to register metric %v: %v", "info", err.Error()) From 5aeb0808da010ab6ba064162cf69184b03c05ebf Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 16:54:45 +0100 Subject: [PATCH 10/14] change http router to gorilla/mux --- .gitignore | 3 ++- go.mod | 1 + go.sum | 1 + main.go | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index b72222b..745881e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ mystrom-exporter output/* +*_string.go # Created by https://www.toptal.com/developers/gitignore/api/osx,intellij+all,windows # Edit at https://www.toptal.com/developers/gitignore?templates=osx,intellij+all,windows @@ -148,4 +149,4 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.toptal.com/developers/gitignore/api/osx,intellij+all,windows \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/osx,intellij+all,windows diff --git a/go.mod b/go.mod index 629a3bf..9570836 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module mystrom-exporter go 1.15 require ( + github.com/gorilla/mux v1.7.3 github.com/prometheus/client_golang v1.10.0 github.com/prometheus/common v0.18.0 ) diff --git a/go.sum b/go.sum index 7a50d30..ce3fb9d 100644 --- a/go.sum +++ b/go.sum @@ -99,6 +99,7 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= diff --git a/main.go b/main.go index e7e8bd7..eae08f9 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/log" @@ -69,7 +70,7 @@ func main() { // -- create a new registry for the exporter telemetry telemetryRegistry := setupMetrics() - router := http.NewServeMux() + router := mux.NewRouter() router.Handle(*metricsPath, promhttp.HandlerFor(telemetryRegistry, promhttp.HandlerOpts{})) router.HandleFunc(*devicePath, scrapeHandler) router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { From c784c8f7c799cbf93f9474f9da569cacabb1ef20 Mon Sep 17 00:00:00 2001 From: Andre Kully Date: Thu, 23 Dec 2021 17:43:20 +0100 Subject: [PATCH 11/14] optimize build target --- Makefile | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 3d3d494..daa18af 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ ## # .DEFAULT_GOAL := help -.PHONY: generate +.PHONY: generate go-tools version := $(shell git describe --tags --always) revision := $(shell git rev-parse HEAD) @@ -11,6 +11,9 @@ builddate := $(shell date '+%FT%T_%Z') versionPkgPrefix := mystrom-exporter/pkg/version +BINDIR := $(CURDIR)/output +GO ?= go +GOPATH ?= $(shell $(GO) env GOPATH) LDFLAGS := -w -s \ -X $(versionPkgPrefix).Version=${version} \ -X $(versionPkgPrefix).Revision=${revision} \ @@ -18,25 +21,36 @@ LDFLAGS := -w -s \ -X $(versionPkgPrefix).BuildUser=${builduser} \ -X $(versionPkgPrefix).BuildDate=${builddate} GOFLAGS := -v +GOX_FLAGS := -mod=vendor +GO_BUILD_FLAGS := -v +export GO111MODULE := on -linux: generate ## builds the linux version of the exporter - GOOS=linux GOARCH=amd64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -mac: generate ## builds the macos version of the exporter - GOOS=darwin GOARCH=amd64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -mac-arm: generate ## builds the macos (m1) version of the exporter - GOOS=darwin GOARCH=arm64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -arm64: generate - GOOS=linux GOARCH=arm64 go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -arm: generate - GOOS=linux GOARCH=arm go build $(GOFLAGS) -ldflags '$(LDFLAGS)' +build: go-tools generate ## builds the all platform binaries of the exporter + $(GOPATH)/bin/gox \ + -os="darwin linux" \ + -arch="amd64 arm arm64" \ + -osarch="!darwin/arm" \ + -output "${BINDIR}/{{.Dir}}-{{.OS}}-{{.Arch}}" \ + -gcflags "$(GO_BUILD_FLAGS)" \ + -ldflags '$(LDFLAGS)' \ + -tags '$(TAGS)' \ + ./... + +run: + ${BINDIR}/mystrom-exporter-$(shell $(GO) env GOOS)-$(shell $(GO) env GOARCH) + + +generate: go-tools + $(GO) generate ./... + +go-tools: $(GOPATH)/bin/stringer $(GOPATH)/bin/gox # -- see more info on https://pkg.go.dev/golang.org/x/tools/cmd/stringer -generate: $(GOPATH)/bin/stringer - go generate ./... - $(GOPATH)/bin/stringer: - go install golang.org/x/tools/cmd/stringer@latest + $(GO) install golang.org/x/tools/cmd/stringer@latest +$(GOPATH)/bin/gox: + $(GO) install github.com/mitchellh/gox@latest # -- help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' From 5f6427d146c7dbd06fd7248830c32270316f5842 Mon Sep 17 00:00:00 2001 From: Mathias Petermann Date: Mon, 29 Aug 2022 20:39:07 +0200 Subject: [PATCH 12/14] Add gotools dependency --- go.mod | 2 ++ go.sum | 22 +++++++++++++++++++++- tools.go | 7 +++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tools.go diff --git a/go.mod b/go.mod index 9570836..5cf895f 100644 --- a/go.mod +++ b/go.mod @@ -6,4 +6,6 @@ require ( github.com/gorilla/mux v1.7.3 github.com/prometheus/client_golang v1.10.0 github.com/prometheus/common v0.18.0 + golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect + golang.org/x/tools v0.1.12 ) diff --git a/go.sum b/go.sum index ce3fb9d..042dbb0 100644 --- a/go.sum +++ b/go.sum @@ -92,6 +92,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -266,6 +267,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -285,6 +287,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -293,6 +296,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -310,6 +315,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -319,6 +326,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -338,11 +346,20 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -357,7 +374,10 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= diff --git a/tools.go b/tools.go new file mode 100644 index 0000000..ace5f40 --- /dev/null +++ b/tools.go @@ -0,0 +1,7 @@ +// +build tools + +package main + +import ( + _ "golang.org/x/tools/cmd/stringer" +) \ No newline at end of file From c05cc378f0cebe11d7b4950a573062828bfcf9d4 Mon Sep 17 00:00:00 2001 From: Mathias Petermann Date: Mon, 29 Aug 2022 20:43:40 +0200 Subject: [PATCH 13/14] Run generate explicitly --- .github/workflows/go.yml | 3 +++ Dockerfile | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index f8eae93..18d3311 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -19,6 +19,9 @@ jobs: with: go-version: 1.17 + - name: Generate + run: go generate ./... + - name: Build run: go build -v ./... diff --git a/Dockerfile b/Dockerfile index 21f9383..5aadd63 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM golang:alpine as build WORKDIR /root COPY . /root -RUN go build . +RUN go generate ./... && go build . FROM alpine:latest From aa584845392eb295171339f177ee115674f506f0 Mon Sep 17 00:00:00 2001 From: Mathias Petermann Date: Mon, 29 Aug 2022 20:51:43 +0200 Subject: [PATCH 14/14] Install stringer explicitly --- .github/workflows/go.yml | 7 +++++-- Dockerfile | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 18d3311..9af3c4a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,13 +12,16 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: 1.17 + - name: install stringer + run: go install golang.org/x/tools/cmd/stringer@latest + - name: Generate run: go generate ./... diff --git a/Dockerfile b/Dockerfile index 5aadd63..7a797cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM golang:alpine as build WORKDIR /root COPY . /root -RUN go generate ./... && go build . +RUN go install golang.org/x/tools/cmd/stringer@latest && go generate ./... && go build . FROM alpine:latest