diff --git a/changelog/unreleased/file-secrets.md b/changelog/unreleased/file-secrets.md new file mode 100644 index 0000000..03e0fe3 --- /dev/null +++ b/changelog/unreleased/file-secrets.md @@ -0,0 +1,8 @@ +Change: Read secrets form files + +We have added proper support to load secrets like the password from files or +from base64-encoded strings. Just provide the flags or environment variables +for token or private key with a DSN formatted string like `file://path/to/file` +or `base64://Zm9vYmFy`. + +https://github.com/promhippie/hetzner_exporter/pulls/ diff --git a/changelog/unreleased/pprof-profiler.md b/changelog/unreleased/pprof-profiler.md new file mode 100644 index 0000000..650c310 --- /dev/null +++ b/changelog/unreleased/pprof-profiler.md @@ -0,0 +1,8 @@ +Enhancement: Integrate option pprof profiling + +We have added an option to enable a pprof endpoint for proper profiling support +with the help of tools like Parca. The endpoint `/debug/pprof` can now +optionally be enabled to get the profiling details for catching potential memory +leaks. + +https://github.com/promhippie/hetzner_exporter/pulls/ diff --git a/changelog/unreleased/web-config.md b/changelog/unreleased/web-config.md new file mode 100644 index 0000000..66e34f6 --- /dev/null +++ b/changelog/unreleased/web-config.md @@ -0,0 +1,7 @@ +Change: Integrate standard web config + +We integrated the new web config from the Prometheus toolkit which provides a +configuration for TLS support and also some basic builtin authentication. For +the detailed configuration you can check out the documentation. + +https://github.com/promhippie/hetzner_exporter/pulls/ diff --git a/docs/content/kubernetes.md b/docs/content/kubernetes.md index ba661bf..9faa6f3 100644 --- a/docs/content/kubernetes.md +++ b/docs/content/kubernetes.md @@ -64,7 +64,7 @@ helm install hetzner-exporter promhippie/hetzner-exporter {{< / highlight >}} You can also watch that available values and generally the details of the chart -provided by us within our [chart][chart] repository. +provided by us within our [chart][chart] repository or on [Artifacthub][ahub]. After applying this manifest the exporter should be directly visible within your Prometheus instance depending on your installation if you enabled the @@ -72,4 +72,5 @@ annotations or the service monitor. [kustomize]: https://github.com/kubernetes-sigs/kustomize [helm]: https://helm.sh -[chart]: https://github.com/promhippie/charts/tree/master/charts/hetzner-exporter +[chart]: https://github.com/promhippie/charts/tree/master/stable/hetzner-exporter +[ahub]: https://artifacthub.io/packages/helm/promhippie/hetzner-exporter diff --git a/docs/content/usage.md b/docs/content/usage.md index df5f6ad..0683dbb 100644 --- a/docs/content/usage.md +++ b/docs/content/usage.md @@ -111,6 +111,22 @@ support for it, for details about the config format look at the - HETZNER_EXPORTER_LOG_PRETTY=true {{< / highlight >}} +If you want to provide the required secrets from a file it's also possible. For +this use case you can write the secret to a file on any path and reference it +with the following format: + +{{< highlight diff >}} + hetzner_exporter: + image: promhippie/hetzner-exporter:latest + restart: always + environment: +- - HETZNER_EXPORTER_USERNAME=#ws+qOeMD4UP +- - HETZNER_EXPORTER_PASSWORD=CNFPCgivAAqWu613 ++ - HETZNER_EXPORTER_USERNAME=file://path/to/secret/file/with/username ++ - HETZNER_EXPORTER_PASSWORD=file://path/to/secret/file/with/password + - HETZNER_EXPORTER_LOG_PRETTY=true +{{< / highlight >}} + Finally the exporter should be configured fine, let's start this stack with [docker-compose][compose], you just need to execute `docker-compose up` within the directory where you have stored the `prometheus.yml` and diff --git a/docs/layouts/partials/style.html b/docs/layouts/partials/style.html index cba6e12..6834422 100644 --- a/docs/layouts/partials/style.html +++ b/docs/layouts/partials/style.html @@ -68,7 +68,7 @@ line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; color: #242424; - max-width: 800px; + max-width: 900px; margin: 5% auto; } @@ -115,6 +115,7 @@ h3 { font-size: 18px; margin-bottom: 0.3em; + margin-top: 1.0em; } h1 small a { @@ -215,6 +216,11 @@ dl dt { font-weight: bold; + margin-top: 10px; + } + + dl dt:first-child { + margin-top: 0px; } dl dd { diff --git a/docs/partials/envvars.md b/docs/partials/envvars.md index 69b3794..096ea9a 100644 --- a/docs/partials/envvars.md +++ b/docs/partials/envvars.md @@ -10,6 +10,15 @@ HETZNER_EXPORTER_WEB_ADDRESS HETZNER_EXPORTER_WEB_PATH : Path to bind the metrics server, defaults to `/metrics` +HETZNER_EXPORTER_WEB_PPROF +: Enable pprof debugging for server, defaults to `false` + +HETZNER_EXPORTER_WEB_TIMEOUT +: Server metrics endpoint timeout, defaults to `10s` + +HETZNER_EXPORTER_WEB_CONFIG +: Path to web-config file + HETZNER_EXPORTER_REQUEST_TIMEOUT : Request timeout as duration, defaults to `5s` diff --git a/docs/partials/metrics.md b/docs/partials/metrics.md index b952ead..1e51a22 100644 --- a/docs/partials/metrics.md +++ b/docs/partials/metrics.md @@ -4,56 +4,56 @@ hetzner_request_duration_seconds{collector} hetzner_request_failures_total{collector} : Total number of failed requests to the api per collector -hetzner_server_cancelled{, , } +hetzner_server_cancelled{id, name, datacenter} : If 1 the server have been cancelled, 0 otherwise -hetzner_server_flatrate{, , } +hetzner_server_flatrate{id, name, datacenter} : If 1 the server got a flatrate enabled, 0 otherwise -hetzner_server_paid_timestamp{, , } +hetzner_server_paid_timestamp{id, name, datacenter} : Timestamp of the date until server is paid -hetzner_server_running{, , } +hetzner_server_running{id, name, datacenter} : If 1 the server is running, 0 otherwise -hetzner_server_traffic_bytes{, , } +hetzner_server_traffic_bytes{id, name, datacenter} : Amount of included traffic for the server -hetzner_ssh_key{, , , } +hetzner_ssh_key{name, type, size, fingerprint} : Information about SSH keys in your Hetzner robot -hetzner_storagebox_cancelled{, , , } +hetzner_storagebox_cancelled{id, name, location, login} : If 1 the storagebox have been cancelled, 0 otherwise -hetzner_storagebox_data{, , , } +hetzner_storagebox_data{id, name, location, login} : Used storage by files for the storagebox in MB -hetzner_storagebox_external{, , , } +hetzner_storagebox_external{id, name, location, login} : If 1 the storagebox can be accessed from external, 0 otherwise -hetzner_storagebox_locked{, , , } +hetzner_storagebox_locked{id, name, location, login} : If 1 the storagebox have been locked, 0 otherwise -hetzner_storagebox_paid{, , , } +hetzner_storagebox_paid{id, name, location, login} : Timestamp of the date until storagebox is paid -hetzner_storagebox_quota{, , , } +hetzner_storagebox_quota{id, name, location, login} : Available storage for the storagebox in MB -hetzner_storagebox_samba{, , , } +hetzner_storagebox_samba{id, name, location, login} : If 1 the storagebox can be accessed via samba, 0 otherwise -hetzner_storagebox_snapshots{, , , } +hetzner_storagebox_snapshots{id, name, location, login} : Used storage by snapshots for the storagebox in MB -hetzner_storagebox_ssh{, , , } +hetzner_storagebox_ssh{id, name, location, login} : If 1 the storagebox can be accessed via ssh, 0 otherwise -hetzner_storagebox_usage{, , , } +hetzner_storagebox_usage{id, name, location, login} : Used storage for the storagebox in MB -hetzner_storagebox_webdav{, , , } +hetzner_storagebox_webdav{id, name, location, login} : If 1 the storagebox can be accessed via webdav, 0 otherwise -hetzner_storagebox_zfs{, , , } +hetzner_storagebox_zfs{id, name, location, login} : If 1 the zfs directory is visible, 0 otherwise diff --git a/go.mod b/go.mod index 569bea3..58bd45d 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/joho/godotenv v1.5.1 github.com/oklog/run v1.1.0 github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/exporter-toolkit v0.10.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.25.7 ) @@ -16,19 +17,29 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/jpillora/backoff v1.0.0 // indirect github.com/kr/text v0.2.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.13.0 // indirect + golang.org/x/sync v0.4.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 2cc521d..0539984 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -15,31 +17,38 @@ github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 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/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/exporter-toolkit v0.10.0 h1:yOAzZTi4M22ZzVxD+fhy1URTuNRj/36uQJJ5S8IPza8= +github.com/prometheus/exporter-toolkit v0.10.0/go.mod h1:+sVFzuvV5JDyw+Ih6p3zFxZNVnKQa3x5qPmDSiPu4ZY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -49,15 +58,52 @@ github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hack/generate-metrics-docs.go b/hack/generate-metrics-docs.go index be585b2..d3fabcc 100644 --- a/hack/generate-metrics-docs.go +++ b/hack/generate-metrics-docs.go @@ -60,7 +60,9 @@ func main() { Labels: make([]string, 0), } - labels := reflect.ValueOf(desc).Elem().FieldByName("variableLabels") + labels := reflect.Indirect( + reflect.ValueOf(desc).Elem().FieldByName("variableLabels"), + ).FieldByName("names") for i := 0; i < labels.Len(); i++ { m.Labels = append(m.Labels, labels.Index(i).String()) diff --git a/pkg/action/helper.go b/pkg/action/helper.go new file mode 100644 index 0000000..f6fa442 --- /dev/null +++ b/pkg/action/helper.go @@ -0,0 +1,16 @@ +package action + +// boolP returns a boolean pointer. +func boolP(i bool) *bool { + return &i +} + +// stringP returns a string pointer. +func stringP(i string) *string { + return &i +} + +// slceP returns a slice pointer. +func sliceP(i []string) *[]string { + return &i +} diff --git a/pkg/action/server.go b/pkg/action/server.go index 2da97d7..19d3165 100644 --- a/pkg/action/server.go +++ b/pkg/action/server.go @@ -13,6 +13,7 @@ import ( "github.com/go-kit/log/level" "github.com/oklog/run" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/exporter-toolkit/web" "github.com/promhippie/hetzner_exporter/pkg/config" "github.com/promhippie/hetzner_exporter/pkg/exporter" "github.com/promhippie/hetzner_exporter/pkg/internal/hetzner" @@ -30,9 +31,31 @@ func Server(cfg *config.Config, logger log.Logger) error { "go", version.Go, ) + username, err := config.Value(cfg.Target.Username) + + if err != nil { + level.Error(logger).Log( + "msg", "Failed to load username from file", + "err", err, + ) + + return err + } + + password, err := config.Value(cfg.Target.Password) + + if err != nil { + level.Error(logger).Log( + "msg", "Failed to load password from file", + "err", err, + ) + + return err + } + client := hetzner.NewClient( - hetzner.WithUsername(cfg.Target.Username), - hetzner.WithPassword(cfg.Target.Password), + hetzner.WithUsername(username), + hetzner.WithPassword(password), ) var gr run.Group @@ -51,7 +74,15 @@ func Server(cfg *config.Config, logger log.Logger) error { "addr", cfg.Server.Addr, ) - return server.ListenAndServe() + return web.ListenAndServe( + server, + &web.FlagConfig{ + WebListenAddresses: sliceP([]string{cfg.Server.Addr}), + WebSystemdSocket: boolP(false), + WebConfigFile: stringP(cfg.Server.Web), + }, + logger, + ) }, func(reason error) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -96,6 +127,10 @@ func handler(cfg *config.Config, logger log.Logger, client *hetzner.Client) *chi mux.Use(middleware.Timeout) mux.Use(middleware.Cache) + if cfg.Server.Pprof { + mux.Mount("/debug", middleware.Profiler()) + } + if cfg.Collector.Servers { level.Debug(logger).Log( "msg", "Server collector registered", diff --git a/pkg/command/command.go b/pkg/command/command.go index 0aca866..9155cc0 100644 --- a/pkg/command/command.go +++ b/pkg/command/command.go @@ -101,6 +101,27 @@ func RootFlags(cfg *config.Config) []cli.Flag { EnvVars: []string{"HETZNER_EXPORTER_WEB_PATH"}, Destination: &cfg.Server.Path, }, + &cli.BoolFlag{ + Name: "web.debug", + Value: false, + Usage: "Enable pprof debugging for server", + EnvVars: []string{"HETZNER_EXPORTER_WEB_PPROF"}, + Destination: &cfg.Server.Pprof, + }, + &cli.DurationFlag{ + Name: "web.timeout", + Value: 10 * time.Second, + Usage: "Server metrics endpoint timeout", + EnvVars: []string{"HETZNER_EXPORTER_WEB_TIMEOUT"}, + Destination: &cfg.Server.Timeout, + }, + &cli.StringFlag{ + Name: "web.config", + Value: "", + Usage: "Path to web-config file", + EnvVars: []string{"HETZNER_EXPORTER_WEB_CONFIG"}, + Destination: &cfg.Server.Web, + }, &cli.DurationFlag{ Name: "request.timeout", Value: 5 * time.Second, diff --git a/pkg/config/config.go b/pkg/config/config.go index a8d5b9e..914c09f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,6 +1,10 @@ package config import ( + "encoding/base64" + "fmt" + "os" + "strings" "time" ) @@ -9,6 +13,8 @@ type Server struct { Addr string Path string Timeout time.Duration + Web string + Pprof bool } // Logs defines the level and color for log configuration. @@ -43,3 +49,32 @@ type Config struct { func Load() *Config { return &Config{} } + +// Value returns the config value based on a DSN. +func Value(val string) (string, error) { + if strings.HasPrefix(val, "file://") { + content, err := os.ReadFile( + strings.TrimPrefix(val, "file://"), + ) + + if err != nil { + return "", fmt.Errorf("failed to parse secret file: %w", err) + } + + return string(content), nil + } + + if strings.HasPrefix(val, "base64://") { + content, err := base64.StdEncoding.DecodeString( + strings.TrimPrefix(val, "base64://"), + ) + + if err != nil { + return "", fmt.Errorf("failed to parse base64 value: %w", err) + } + + return string(content), nil + } + + return val, nil +} diff --git a/pkg/middleware/profiler.go b/pkg/middleware/profiler.go new file mode 100644 index 0000000..62b8cf6 --- /dev/null +++ b/pkg/middleware/profiler.go @@ -0,0 +1,12 @@ +package middleware + +import ( + "net/http" + + "github.com/go-chi/chi/v5/middleware" +) + +// Profiler just wraps the go-chi profiler middleware. +func Profiler() http.Handler { + return middleware.Profiler() +}