initial commit
This commit is contained in:
commit
05ad0f1f88
20 changed files with 1073 additions and 0 deletions
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
# vscode Go debugging files
|
||||||
|
__debug_*
|
||||||
|
template-exporter
|
||||||
|
bin/
|
||||||
|
|
||||||
|
# vscode editor config
|
||||||
|
.vscode/
|
||||||
|
|
43
.woodpecker/docker-deploy.yml
Normal file
43
.woodpecker/docker-deploy.yml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
when:
|
||||||
|
- event: [pull_request, tag]
|
||||||
|
|
||||||
|
# Change this when using template
|
||||||
|
variables:
|
||||||
|
- &DOCKER_USERNAME 'adoralaura'
|
||||||
|
- &DOCKER_REPOSITORY 'adoralaura/template-exporter'
|
||||||
|
- &DOCKER_REGISTRY 'https://index.docker.io/v1/'
|
||||||
|
|
||||||
|
# Necessary Secrets:
|
||||||
|
# DOCKERHUB_TOKEN: https://hub.docker.com/settings/security -> New Access-Token -> Read, Write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
docker-deploy-pr:
|
||||||
|
when:
|
||||||
|
- event: pull_request
|
||||||
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
platforms: linux/arm/v7,linux/arm64/v8,linux/amd64
|
||||||
|
repo: *DOCKER_REPOSITORY
|
||||||
|
registry: *DOCKER_REGISTRY
|
||||||
|
tag: pr-${CI_COMMIT_PULL_REQUEST}
|
||||||
|
username: *DOCKER_USERNAME
|
||||||
|
password:
|
||||||
|
from_secret: dockerhub_token
|
||||||
|
|
||||||
|
|
||||||
|
docker-deploy-tag:
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
platforms: linux/arm/v7,linux/arm64/v8,linux/amd64
|
||||||
|
repo: *DOCKER_REPOSITORY
|
||||||
|
registry: *DOCKER_REGISTRY
|
||||||
|
auto_tag: true
|
||||||
|
username: *DOCKER_USERNAME
|
||||||
|
password:
|
||||||
|
from_secret: dockerhub_token
|
||||||
|
|
||||||
|
|
9
.woodpecker/golang-test.yml
Normal file
9
.woodpecker/golang-test.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
|
||||||
|
steps:
|
||||||
|
test:
|
||||||
|
image: golang:1.22-bullseye
|
||||||
|
commands:
|
||||||
|
- go mod download
|
||||||
|
- go test -v ./...
|
6
.woodpecker/readme.md
Normal file
6
.woodpecker/readme.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Woodpecker CI Directory
|
||||||
|
Please lint the woodpecker configs in this directory before committing!
|
||||||
|
|
||||||
|
```shell
|
||||||
|
woodpecker-cli lint .woodpecker/*.yml
|
||||||
|
```
|
35
CHANGELOG.md
Normal file
35
CHANGELOG.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
### Changed
|
||||||
|
- asd
|
||||||
|
|
||||||
|
## [0.2.0] - 1970-01-01
|
||||||
|
### ⚠️ Breaking Changes
|
||||||
|
- asd
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- asd
|
||||||
|
|
||||||
|
## [0.1.1] - 1970-01-01
|
||||||
|
### Fixed
|
||||||
|
- asd
|
||||||
|
|
||||||
|
## [0.1.0] - 1970-01-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- This CHANGELOG file to document all significant changes
|
||||||
|
- LICENSE
|
||||||
|
- CONTRIBUTING guidelines
|
||||||
|
- MAINTAINERS file
|
||||||
|
- minimal implementation of the exporter
|
||||||
|
|
||||||
|
[unreleased]: https://code.lila.network/adoralaura/template-exporter/compare/0.2.0...HEAD
|
||||||
|
[0.2.0]: https://code.lila.network/adoralaura/template-exporter/compare/0.1.1...0.2.0
|
||||||
|
[0.1.1]: https://code.lila.network/adoralaura/template-exporter/compare/0.1.0...0.1.1
|
||||||
|
[0.1.0]: https://code.lila.network/adoralaura/template-exporter/releases/tag/0.1.0
|
17
CONTRIBUTING.md
Normal file
17
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
I use my own [Forgejo Instance](https://code.lila.network) to manage issues and pull requests.
|
||||||
|
|
||||||
|
* If you have a trivial fix or improvement, go ahead and create a pull request,
|
||||||
|
addressing (with `@...`) the maintainer of this repository (see
|
||||||
|
[MAINTAINERS.md](MAINTAINERS.md)) in the description of the pull request.
|
||||||
|
|
||||||
|
* If you plan to do something more involved, first please [send me a mail]( mailto:adora@lila.network?subject=%5Btemplate-exporter%5D).
|
||||||
|
|
||||||
|
# What to contribute
|
||||||
|
|
||||||
|
The best way to help without speaking a lot of Go would be to share your
|
||||||
|
configuration, alerts, dashboards, and recording rules. If you have something
|
||||||
|
that works and is not in the repository, please pay it forward and
|
||||||
|
share what works.
|
||||||
|
|
31
Dockerfile
Normal file
31
Dockerfile
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
FROM golang:1.22-bullseye AS dev
|
||||||
|
|
||||||
|
COPY . /var/app
|
||||||
|
WORKDIR /var/app
|
||||||
|
|
||||||
|
|
||||||
|
ENV GO111MODULE="on" \
|
||||||
|
CGO_ENABLED=0 \
|
||||||
|
GOOS=linux
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
ENTRYPOINT ["sh"]
|
||||||
|
|
||||||
|
|
||||||
|
FROM dev as build
|
||||||
|
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends --assume-yes ca-certificates
|
||||||
|
RUN go mod download && go mod verify
|
||||||
|
RUN go build -o template-exporter main.go
|
||||||
|
RUN chmod +x template-exporter
|
||||||
|
|
||||||
|
|
||||||
|
FROM gcr.io/distroless/static-debian12 AS prod
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /var/app/template-exporter /bin/
|
||||||
|
|
||||||
|
LABEL maintainer="Adora Laura Kalb <adora@lila.network>"
|
||||||
|
|
||||||
|
EXPOSE 43609
|
||||||
|
ENTRYPOINT ["/bin/template-exporter"]
|
201
LICENSE
Normal file
201
LICENSE
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
1
MAINTAINERS.md
Normal file
1
MAINTAINERS.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* Adora Laura Kalb <adora@lila.network> @adoralaura
|
3
Makefile
Normal file
3
Makefile
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test ./...
|
68
README.md
Normal file
68
README.md
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Template Exporter
|
||||||
|
|
||||||
|
This repository is a template repository for @adoralaura. It's used to easily implement (small) Prometheus Exporters.
|
||||||
|
|
||||||
|
If you run into issues you may inform @adoralaura, but know that you use this template on your own risk!
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pre-built docker images can be found at [Docker Hub](https://hub.docker.com/r/lauralani/template-exporter). A sample `docker-compose.yml` can be [found here](docker-compose.yml).
|
||||||
|
|
||||||
|
Binaries can be built by cloning this repository and run `go build main.go` with [Golang](https://go.dev/) installed.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
Start `template-exporter` as a daemon or from CLI:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./template-exporter
|
||||||
|
```
|
||||||
|
|
||||||
|
Visit <http://localhost:43609/metrics?target=203.0.113.8> where `203.0.113.8` is the IP
|
||||||
|
of the NTP server you want to get the score from.
|
||||||
|
|
||||||
|
## Prometheus Configuration
|
||||||
|
|
||||||
|
The URL parameter `target` can be controlled through relabelling.
|
||||||
|
|
||||||
|
Example config:
|
||||||
|
```YAML
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'template'
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
- 203.0.113.8 # change this
|
||||||
|
- 2003:db8::1 # change this
|
||||||
|
relabel_configs:
|
||||||
|
- source_labels: [__address__]
|
||||||
|
target_label: __param_target
|
||||||
|
- source_labels: [__param_target]
|
||||||
|
target_label: instance
|
||||||
|
- target_label: __address__
|
||||||
|
replacement: 127.0.0.1:43609 # The template exporter's real hostname:port.
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly to [blackbox_exporter](https://github.com/prometheus/blackbox_exporter),
|
||||||
|
`template-exporter` is meant to run on a few central machines and can be thought of
|
||||||
|
like a "Prometheus proxy".
|
||||||
|
|
||||||
|
### TLS and basic authentication
|
||||||
|
|
||||||
|
The Template Exporter does **not** support TLS and basic authentication yet.
|
||||||
|
This may be added in a future release.
|
||||||
|
|
||||||
|
# Once you have it running
|
||||||
|
|
||||||
|
It can be opaque to get started with all this. To make it
|
||||||
|
easier for others, please consider contributing back your configurations to
|
||||||
|
us.
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
If you have any ideas, enhancements, bug reports or general questions, please don't hesitate to [create a new issue](https://gitlab.com/adoralaura/template-exporter/-/issues/new).
|
||||||
|
|
||||||
|
If you want to contribute to this project, please read the [Contribution Guidelines](CONTRIBUTING.md)
|
||||||
|
|
||||||
|
# License
|
||||||
|
This project is available under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0). See the LICENSE file for more info.
|
93
cache/cache.go
vendored
Normal file
93
cache/cache.go
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 Adora Laura Kalb <adora@lila.network>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
GlobalScoreCache *ScoreCache
|
||||||
|
cacheExpiredAfter = 5 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerScore struct {
|
||||||
|
expiresAt time.Time
|
||||||
|
Score float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScoreCache struct {
|
||||||
|
stop chan struct{}
|
||||||
|
|
||||||
|
mu sync.RWMutex
|
||||||
|
|
||||||
|
scores map[netip.Addr]ServerScore
|
||||||
|
}
|
||||||
|
|
||||||
|
type CacheMissError struct{}
|
||||||
|
|
||||||
|
func (m *CacheMissError) Error() string {
|
||||||
|
return "User is not in cache!"
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCacheMissError() *CacheMissError {
|
||||||
|
return &CacheMissError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScoreCache() *ScoreCache {
|
||||||
|
lc := &ScoreCache{
|
||||||
|
scores: make(map[netip.Addr]ServerScore),
|
||||||
|
stop: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return lc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *ScoreCache) Add(score float64, ip netip.Addr, ts time.Time) {
|
||||||
|
ssc := ServerScore{Score: score, expiresAt: ts.Add(cacheExpiredAfter)}
|
||||||
|
sc.mu.Lock()
|
||||||
|
sc.scores[ip] = ssc
|
||||||
|
sc.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lc *ScoreCache) Get(ip netip.Addr) (ServerScore, error) {
|
||||||
|
now := time.Now()
|
||||||
|
lc.mu.RLock()
|
||||||
|
|
||||||
|
cachedScore, ok := lc.scores[ip]
|
||||||
|
if !ok {
|
||||||
|
lc.mu.RUnlock()
|
||||||
|
return ServerScore{}, newCacheMissError()
|
||||||
|
}
|
||||||
|
lc.mu.RUnlock()
|
||||||
|
|
||||||
|
if now.After(cachedScore.expiresAt) {
|
||||||
|
lc.delete(ip)
|
||||||
|
return ServerScore{}, newCacheMissError()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedScore, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lc *ScoreCache) delete(ip netip.Addr) {
|
||||||
|
lc.mu.Lock()
|
||||||
|
|
||||||
|
delete(lc.scores, ip)
|
||||||
|
lc.mu.Unlock()
|
||||||
|
}
|
49
cache/cache_test.go
vendored
Normal file
49
cache/cache_test.go
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 Adora Laura Kalb <adora@lila.network>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCacheGet(t *testing.T) {
|
||||||
|
const testFloat = 1.23456
|
||||||
|
cache := NewScoreCache()
|
||||||
|
cache.Add(testFloat, netip.MustParseAddr("1.2.3.4"), time.Now().Add(5*time.Minute))
|
||||||
|
|
||||||
|
score, _ := cache.Get(netip.MustParseAddr("1.2.3.4"))
|
||||||
|
if score.Score != testFloat {
|
||||||
|
t.Fatalf("cache.Get(\"1.2.3.4\") = %f, want %f", score.Score, testFloat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheMiss(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
const testFloat = 1.23456
|
||||||
|
|
||||||
|
expiredTime := now.Add(-(time.Minute * 10))
|
||||||
|
|
||||||
|
cache := NewScoreCache()
|
||||||
|
cache.Add(testFloat, netip.MustParseAddr("1.2.3.4"), expiredTime)
|
||||||
|
|
||||||
|
_, err := cache.Get(netip.MustParseAddr("1.2.3.4"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("cache.Get(\"1.2.3.4\") = got nil, want error")
|
||||||
|
}
|
||||||
|
}
|
138
collector/collector.go
Normal file
138
collector/collector.go
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 Adora Laura Kalb <adora@lila.network>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-kit/log"
|
||||||
|
"github.com/go-kit/log/level"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"golang.adora.codes/template-exporter/cache"
|
||||||
|
"golang.adora.codes/template-exporter/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Metrics struct {
|
||||||
|
NtpppolServerScore prometheus.Gauge
|
||||||
|
}
|
||||||
|
|
||||||
|
type Collector struct {
|
||||||
|
ctx context.Context
|
||||||
|
target netip.Addr
|
||||||
|
logger log.Logger
|
||||||
|
metrics Metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, target netip.Addr, logger log.Logger, metrics Metrics) *Collector {
|
||||||
|
return &Collector{ctx: ctx, target: target, logger: logger, metrics: metrics}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe implements Prometheus.Collector.
|
||||||
|
func (c Collector) Describe(ch chan<- *prometheus.Desc) {
|
||||||
|
ch <- prometheus.NewDesc("dummy", "dummy", nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect implements Prometheus.Collector.
|
||||||
|
func (c Collector) Collect(ch chan<- prometheus.Metric) {
|
||||||
|
logger := log.With(c.logger, "module", "scraper")
|
||||||
|
level.Debug(logger).Log("msg", "Starting scrape")
|
||||||
|
start := time.Now()
|
||||||
|
c.collect(ch, logger)
|
||||||
|
duration := time.Since(start).Seconds()
|
||||||
|
level.Debug(logger).Log("msg", "Finished scrape", "duration_seconds", duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Collector) collect(ch chan<- prometheus.Metric, logger log.Logger) {
|
||||||
|
score, err := cache.GlobalScoreCache.Get(c.target)
|
||||||
|
if err == nil {
|
||||||
|
level.Debug(logger).Log("msg", "Serving score from cache",
|
||||||
|
"server", c.target.String(), "score", score.Score)
|
||||||
|
|
||||||
|
serverScoreMetric := prometheus.NewDesc("template_server_score",
|
||||||
|
"Shows the server score currently assigned at template.org",
|
||||||
|
nil, nil)
|
||||||
|
|
||||||
|
m1 := prometheus.MustNewConstMetric(serverScoreMetric, prometheus.GaugeValue, score.Score)
|
||||||
|
m1 = prometheus.NewMetricWithTimestamp(time.Now(), m1)
|
||||||
|
ch <- m1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
level.Debug(logger).Log("msg", "Cache miss, querying API",
|
||||||
|
"server", c.target.String())
|
||||||
|
|
||||||
|
var serverApiScore float64 = 0
|
||||||
|
const apiEndpoint = "https://www.template.org/scores/"
|
||||||
|
const apiQuery = "/json?limit=1&monitor=24"
|
||||||
|
|
||||||
|
url := apiEndpoint + c.target.String() + apiQuery
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error sending HTTP request", "url", url, "message", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error in HTTP response", "error", err)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error reading HTTP response body", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var response models.ApiResponse
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &response)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error unmarshaling JSON body", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serverApiScore = response.History[0].Score
|
||||||
|
|
||||||
|
cache.GlobalScoreCache.Add(serverApiScore, c.target, time.Now())
|
||||||
|
level.Debug(logger).Log("msg", "Added score to cache",
|
||||||
|
"server", c.target.String(), "score", score.Score)
|
||||||
|
|
||||||
|
// TODO: Test or delete
|
||||||
|
serverScoreMetric := prometheus.NewDesc("template_server_score",
|
||||||
|
"Shows the server score currently assigned at template.org",
|
||||||
|
nil, nil)
|
||||||
|
|
||||||
|
// TODO: Test or delete
|
||||||
|
//c.metrics.NtpppolServerScore.Add(serverApiScore)
|
||||||
|
|
||||||
|
// TODO: Test or delete
|
||||||
|
//Write latest value for each metric in the prometheus metric channel.
|
||||||
|
//Note that you can pass CounterValue, GaugeValue, or UntypedValue types here.
|
||||||
|
m1 := prometheus.MustNewConstMetric(serverScoreMetric, prometheus.GaugeValue, serverApiScore)
|
||||||
|
m1 = prometheus.NewMetricWithTimestamp(time.Now(), m1)
|
||||||
|
ch <- m1
|
||||||
|
}
|
8
docker-compose.yml
Normal file
8
docker-compose.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
services:
|
||||||
|
template_exporter:
|
||||||
|
image: lauralani/template-exporter:latest
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- '127.0.0.1:43609:43609'
|
||||||
|
# Add this to enable debug logging:
|
||||||
|
# command: --log.level=debug
|
33
go.mod
Normal file
33
go.mod
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
module golang.adora.codes/template-exporter
|
||||||
|
|
||||||
|
go 1.22.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||||
|
github.com/go-kit/log v0.2.1
|
||||||
|
github.com/prometheus/client_golang v1.19.1
|
||||||
|
github.com/prometheus/common v0.55.0
|
||||||
|
github.com/prometheus/exporter-toolkit v0.11.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||||
|
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||||
|
github.com/jpillora/backoff v1.0.0 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
|
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||||
|
golang.org/x/crypto v0.25.0 // indirect
|
||||||
|
golang.org/x/net v0.27.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.21.0 // indirect
|
||||||
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
|
golang.org/x/text v0.16.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
)
|
77
go.sum
Normal file
77
go.sum
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
|
||||||
|
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
||||||
|
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg=
|
||||||
|
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
|
||||||
|
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.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
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/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/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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
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/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
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/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.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||||
|
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||||
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
|
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||||
|
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||||
|
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
|
||||||
|
github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q=
|
||||||
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||||
|
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||||
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
|
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||||
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
|
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||||
|
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
|
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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
25
helpers/helpers.go
Normal file
25
helpers/helpers.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 Adora Laura Kalb <adora@lila.network>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UnixToTime(i int64) time.Time {
|
||||||
|
return time.Unix(i, 0)
|
||||||
|
}
|
165
main.go
Normal file
165
main.go
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 Adora Laura Kalb <adora@lila.network>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alecthomas/kingpin/v2"
|
||||||
|
"github.com/go-kit/log"
|
||||||
|
"github.com/go-kit/log/level"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"github.com/prometheus/common/promlog"
|
||||||
|
"github.com/prometheus/common/promlog/flag"
|
||||||
|
"github.com/prometheus/common/version"
|
||||||
|
"github.com/prometheus/exporter-toolkit/web"
|
||||||
|
webflag "github.com/prometheus/exporter-toolkit/web/kingpinflag"
|
||||||
|
"golang.adora.codes/template-exporter/cache"
|
||||||
|
"golang.adora.codes/template-exporter/collector"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
namespace = "template"
|
||||||
|
appVersion = "0.2.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
metricsPath = kingpin.Flag(
|
||||||
|
"web.telemetry-path",
|
||||||
|
"Path under which to expose metrics.",
|
||||||
|
).Default("/metrics").String()
|
||||||
|
//appListenIP = kingpin.Flag("web.listen-address", "Addresses on which to expose metrics and web interface").Default(":43609").String()
|
||||||
|
toolkitFlags = webflag.AddFlags(kingpin.CommandLine, ":43609")
|
||||||
|
|
||||||
|
// Metrics about the SNMP exporter itself.
|
||||||
|
templateRequestErrors = promauto.NewCounter(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "request_errors_total",
|
||||||
|
Help: "Errors in requests to the SNMP exporter",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request, logger log.Logger, exporterMetrics collector.Metrics) {
|
||||||
|
|
||||||
|
// /metrics&target=2003%3Adb8%3A%3A1
|
||||||
|
// /metrics&target=1.2.3.4
|
||||||
|
query := r.URL.Query()
|
||||||
|
|
||||||
|
target := query.Get("target")
|
||||||
|
if len(query["target"]) != 1 || target == "" {
|
||||||
|
http.Error(w, "'target' parameter must be specified once", http.StatusBadRequest)
|
||||||
|
templateRequestErrors.Inc()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetIP, ipError := netip.ParseAddr(target)
|
||||||
|
if ipError != nil {
|
||||||
|
http.Error(w, "'target' parameter must be a valid IPv4/IPv6 address", http.StatusBadRequest)
|
||||||
|
templateRequestErrors.Inc()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = log.With(logger, "target", target)
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
c := collector.New(r.Context(), targetIP, logger, exporterMetrics)
|
||||||
|
registry.MustRegister(c)
|
||||||
|
// Delegate http serving to Prometheus client library, which will call collector.Collect.
|
||||||
|
h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
version.Version = appVersion
|
||||||
|
promlogConfig := &promlog.Config{}
|
||||||
|
flag.AddFlags(kingpin.CommandLine, promlogConfig)
|
||||||
|
kingpin.Version(version.Print("template_exporter"))
|
||||||
|
kingpin.HelpFlag.Short('h')
|
||||||
|
kingpin.Parse()
|
||||||
|
logger := promlog.New(promlogConfig)
|
||||||
|
|
||||||
|
cache.GlobalScoreCache = cache.NewScoreCache()
|
||||||
|
|
||||||
|
level.Info(logger).Log("msg", "Starting template_exporter", "version", appVersion)
|
||||||
|
level.Info(logger).Log("build_context", version.BuildContext())
|
||||||
|
|
||||||
|
exporterMetrics := collector.Metrics{
|
||||||
|
NtpppolServerScore: promauto.NewGauge(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "server_score",
|
||||||
|
Help: "Current template server score",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc(*metricsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
handler(w, r, logger, exporterMetrics)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsPathValue := *metricsPath
|
||||||
|
|
||||||
|
if *metricsPath != "/" && *metricsPath != "" {
|
||||||
|
landingConfig := web.LandingConfig{
|
||||||
|
Name: "template Exporter",
|
||||||
|
Description: "Prometheus Exporter for SNMP targets",
|
||||||
|
Version: version.Info(),
|
||||||
|
Form: web.LandingForm{
|
||||||
|
Action: metricsPathValue,
|
||||||
|
Inputs: []web.LandingFormInput{
|
||||||
|
{
|
||||||
|
Label: "Target",
|
||||||
|
Type: "text",
|
||||||
|
Name: "target",
|
||||||
|
Placeholder: "X.X.X.X/[::X]",
|
||||||
|
Value: "::1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Links: []web.LandingLinks{
|
||||||
|
{
|
||||||
|
Address: *metricsPath,
|
||||||
|
Text: "Metrics",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
landingPage, err := web.NewLandingPage(landingConfig)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("err", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
http.Handle("/", landingPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
ReadHeaderTimeout: 15 * time.Second,
|
||||||
|
ReadTimeout: 15 * time.Second,
|
||||||
|
WriteTimeout: 10 * time.Second,
|
||||||
|
IdleTimeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
if err := web.ListenAndServe(srv, toolkitFlags, logger); err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error starting HTTP server", "err", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
41
models/api_response.go
Normal file
41
models/api_response.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 Adora Laura Kalb <adora@lila.network>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type ApiResponse struct {
|
||||||
|
History []ApiResponseHistory `json:"history"`
|
||||||
|
Monitors []struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Timestamp time.Time `json:"ts"`
|
||||||
|
Score float64 `json:"score"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
} `json:"monitors"`
|
||||||
|
Server struct {
|
||||||
|
IP string `json:"ip"`
|
||||||
|
} `json:"server"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiResponseHistory struct {
|
||||||
|
TimestampInt int64 `json:"ts"`
|
||||||
|
Step int `json:"step"`
|
||||||
|
Score float64 `json:"score"`
|
||||||
|
MonitorID int `json:"monitor_id"`
|
||||||
|
}
|
Loading…
Reference in a new issue