Merge branch 'main' into beorn7/sparsehistogram

This commit is contained in:
beorn7 2022-06-09 20:50:30 +02:00
commit 40ad5e284a
144 changed files with 3193 additions and 1647 deletions

View file

@ -38,6 +38,7 @@ jobs:
GO111MODULE: "on"
- run: go test ./tsdb/ -test.tsdb-isolation=false
- run: make -C documentation/examples/remote_storage
- run: make -C documentation/examples
- prometheus/check_proto:
version: "3.15.8"
- prometheus/store_artifact:

2
.github/CODEOWNERS vendored
View file

@ -4,4 +4,4 @@
/discovery/kubernetes @brancz
/tsdb @codesome
/promql @codesome @roidelapluie
/cmd/promtool @jessicagreben @dgl
/cmd/promtool @dgl

View file

@ -4,6 +4,10 @@ updates:
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "gomod"
directory: "/documentation/examples/remote_storage"
schedule:
interval: "monthly"
- package-ecosystem: "npm"
directory: "/web/ui"
schedule:

View file

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: bufbuild/buf-setup-action@v1.4.0
- uses: bufbuild/buf-setup-action@v1.5.0
- uses: bufbuild/buf-lint-action@v1
with:
input: 'prompb'

View file

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: bufbuild/buf-setup-action@v1.4.0
- uses: bufbuild/buf-setup-action@v1.5.0
- uses: bufbuild/buf-lint-action@v1
with:
input: 'prompb'

View file

@ -21,7 +21,10 @@ jobs:
uses: actions/setup-go@v2
with:
go-version: 1.18.x
- name: Install snmp_exporter/generator dependencies
run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
if: github.repository == 'prometheus/snmp_exporter'
- name: Lint
uses: golangci/golangci-lint-action@v3.1.0
uses: golangci/golangci-lint-action@v3.2.0
with:
version: v1.45.2

View file

@ -14,6 +14,7 @@ rules:
document-start: disable
indentation:
spaces: consistent
indent-sequences: consistent
key-duplicates:
ignore: |
config/testdata/section_key_dup.bad.yml

View file

@ -1,5 +1,25 @@
# Changelog
## 2.36.1 / 2022-06-09
* [BUGFIX] promtool: Add --lint-fatal option #10840
## 2.36.0 / 2022-05-30
* [FEATURE] Add lowercase and uppercase relabel action. #10641
* [FEATURE] SD: Add IONOS Cloud integration. #10514
* [FEATURE] SD: Add Vultr integration. #10714
* [FEATURE] SD: Add Linode SD failure count metric. #10673
* [FEATURE] Add prometheus_ready metric. #10682
* [ENHANCEMENT] Add stripDomain to template function. #10475
* [ENHANCEMENT] UI: Enable active search through dropped targets. #10668
* [ENHANCEMENT] promtool: support matchers when querying label values. #10727
* [ENHANCEMENT] Add agent mode identifier. #9638
* [BUGFIX] Changing TotalQueryableSamples from int to int64. #10549
* [BUGFIX] tsdb/agent: Ignore duplicate exemplars. #10595
* [BUGFIX] TSDB: Fix chunk overflow appending samples at a variable rate. #10607
* [BUGFIX] Stop rule manager before TSDB is stopped. #10680
## 2.35.0 / 2022-04-21
This Prometheus release is built with go1.18, which contains two noticeable changes related to TLS:

View file

@ -3,7 +3,7 @@
Julien Pivotto (<roidelapluie@prometheus.io> / @roidelapluie) and Levi Harrison (<levi@leviharrison.dev> / @LeviHarrison) are the main/default maintainers, some parts of the codebase have other maintainers:
* `cmd`
* `promtool`: David Leadbeater (<dgl@dgl.cx> / @dgl), Jessica Grebenschikov (<jessicagreben@prometheus.io> / @jessicagreben)
* `promtool`: David Leadbeater (<dgl@dgl.cx> / @dgl)
* `discovery`
* `k8s`: Frederic Branczyk (<fbranczyk@gmail.com> / @brancz)
* `documentation`

View file

@ -59,7 +59,7 @@ Prometheus will now be reachable at <http://localhost:9090/>.
To build Prometheus from source code, You need:
* Go [version 1.16 or greater](https://golang.org/doc/install).
* Go [version 1.17 or greater](https://golang.org/doc/install).
* NodeJS [version 16 or greater](https://nodejs.org/).
* npm [version 7 or greater](https://www.npmjs.com/).

View file

@ -80,7 +80,10 @@ Maintaining the release branches for older minor releases happens on a best effo
A few days before a major or minor release, consider updating the dependencies.
Then create a pull request against the main branch.
Note that we use [Dependabot](.github/dependabot.yml) to continuously update most things automatically. Therefore, most dependencies should be up to date.
Check the [dependencies GitHub label](https://github.com/prometheus/prometheus/labels/dependencies) to see if there are any pending updates.
This bot currently does not manage `+incompatible` and `v0.0.0` in the version specifier for Go modules.
Note that after a dependency update, you should look out for any weirdness that
might have happened. Such weirdnesses include but are not limited to: flaky
@ -95,7 +98,9 @@ This is also a good time to consider any experimental features and feature
flags for promotion to stable or for deprecation or ultimately removal. Do any
of these in pull requests, one per feature.
#### Updating Go dependencies
#### Manually updating Go dependencies
This is usually only needed for `+incompatible` and `v0.0.0` non-semver updates.
```bash
make update-go-deps
@ -103,7 +108,7 @@ git add go.mod go.sum
git commit -m "Update dependencies"
```
#### Updating React dependencies
#### Manually updating React dependencies
The React application recently moved to a monorepo system with multiple internal npm packages. Dependency upgrades are
quite sensitive for the time being.

View file

@ -1 +1 @@
2.35.0
2.36.1

View file

@ -16,6 +16,7 @@ package main
import (
"context"
"errors"
"fmt"
"math"
"math/bits"
@ -38,7 +39,6 @@ import (
"github.com/grafana/regexp"
conntrack "github.com/mwitkow/go-conntrack"
"github.com/oklog/run"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model"
"github.com/prometheus/common/promlog"
@ -388,6 +388,9 @@ func main() {
serverOnlyFlag(a, "query.max-samples", "Maximum number of samples a single query can load into memory. Note that queries will fail if they try to load more samples than this into memory, so this also limits the number of samples a query can return.").
Default("50000000").IntVar(&cfg.queryMaxSamples)
a.Flag("scrape.discovery-reload-interval", "Interval used by scrape manager to throttle target groups updates.").
Hidden().Default("5s").SetValue(&cfg.scrape.DiscoveryReloadInterval)
a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: agent, exemplar-storage, expand-external-labels, memory-snapshot-on-shutdown, promql-at-modifier, promql-negative-offset, promql-per-step-stats, remote-write-receiver (DEPRECATED), extra-scrape-metrics, new-service-discovery-manager, auto-gomaxprocs. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details.").
Default("").StringsVar(&cfg.featureList)
@ -395,7 +398,7 @@ func main() {
_, err := a.Parse(os.Args[1:])
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Error parsing commandline arguments"))
fmt.Fprintln(os.Stderr, fmt.Errorf("Error parsing commandline arguments: %w", err))
a.Usage(os.Args[1:])
os.Exit(2)
}
@ -403,7 +406,7 @@ func main() {
logger := promlog.New(&cfg.promlogConfig)
if err := cfg.setFeatureListOptions(logger); err != nil {
fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Error parsing feature list"))
fmt.Fprintln(os.Stderr, fmt.Errorf("Error parsing feature list: %w", err))
os.Exit(1)
}
@ -424,13 +427,13 @@ func main() {
cfg.web.ExternalURL, err = computeExternalURL(cfg.prometheusURL, cfg.web.ListenAddress)
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrapf(err, "parse external URL %q", cfg.prometheusURL))
fmt.Fprintln(os.Stderr, fmt.Errorf("parse external URL %q: %w", cfg.prometheusURL, err))
os.Exit(2)
}
cfg.web.CORSOrigin, err = compileCORSRegexString(cfg.corsRegexString)
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrapf(err, "could not compile CORS regex string %q", cfg.corsRegexString))
fmt.Fprintln(os.Stderr, fmt.Errorf("could not compile CORS regex string %q: %w", cfg.corsRegexString, err))
os.Exit(2)
}
@ -523,7 +526,14 @@ func main() {
klogv2.ClampLevel(6)
klogv2.SetLogger(log.With(logger, "component", "k8s_client_runtime"))
level.Info(logger).Log("msg", "Starting Prometheus", "version", version.Info())
modeAppName := "Prometheus Server"
mode := "server"
if agentMode {
modeAppName = "Prometheus Agent"
mode = "agent"
}
level.Info(logger).Log("msg", "Starting "+modeAppName, "mode", mode, "version", version.Info())
if bits.UintSize < 64 {
level.Warn(logger).Log("msg", "This Prometheus binary has not been compiled for a 64-bit architecture. Due to virtual memory constraints of 32-bit systems, it is highly recommended to switch to a 64-bit binary of Prometheus.", "GOARCH", runtime.GOARCH)
}
@ -627,6 +637,7 @@ func main() {
cfg.web.Notifier = notifierManager
cfg.web.LookbackDelta = time.Duration(cfg.lookbackDelta)
cfg.web.IsAgent = agentMode
cfg.web.AppName = modeAppName
cfg.web.Version = &web.PrometheusVersion{
Version: version.Version,
@ -730,7 +741,7 @@ func main() {
fs, err := filepath.Glob(pat)
if err != nil {
// The only error can be a bad pattern.
return errors.Wrapf(err, "error retrieving rule files for %s", pat)
return fmt.Errorf("error retrieving rule files for %s: %w", pat, err)
}
files = append(files, fs...)
}
@ -805,6 +816,7 @@ func main() {
},
func(err error) {
close(cancel)
webHandler.SetReady(false)
},
)
}
@ -836,6 +848,19 @@ func main() {
},
)
}
if !agentMode {
// Rule manager.
g.Add(
func() error {
<-reloadReady.C
ruleManager.Run()
return nil
},
func(err error) {
ruleManager.Stop()
},
)
}
{
// Scrape manager.
g.Add(
@ -853,6 +878,8 @@ func main() {
func(err error) {
// Scrape manager needs to be stopped before closing the local TSDB
// so that it doesn't try to write samples to a closed storage.
// We should also wait for rule manager to be fully stopped to ensure
// we don't trigger any false positive alerts for rules using absent().
level.Info(logger).Log("msg", "Stopping scrape manager...")
scrapeManager.Stop()
},
@ -922,12 +949,12 @@ func main() {
}
if err := reloadConfig(cfg.configFile, cfg.enableExpandExternalLabels, cfg.tsdb.EnableExemplarStorage, logger, noStepSubqueryInterval, reloaders...); err != nil {
return errors.Wrapf(err, "error loading config from %q", cfg.configFile)
return fmt.Errorf("error loading config from %q: %w", cfg.configFile, err)
}
reloadReady.Close()
webHandler.Ready()
webHandler.SetReady(true)
level.Info(logger).Log("msg", "Server is ready to receive web requests.")
<-cancel
return nil
@ -938,18 +965,6 @@ func main() {
)
}
if !agentMode {
// Rule manager.
g.Add(
func() error {
<-reloadReady.C
ruleManager.Run()
return nil
},
func(err error) {
ruleManager.Stop()
},
)
// TSDB.
opts := cfg.tsdb.ToTSDBOptions()
cancel := make(chan struct{})
@ -969,7 +984,7 @@ func main() {
db, err := openDBWithMetrics(localStoragePath, logger, prometheus.DefaultRegisterer, &opts, localStorage.getStats())
if err != nil {
return errors.Wrapf(err, "opening storage failed")
return fmt.Errorf("opening storage failed: %w", err)
}
switch fsType := prom_runtime.Statfs(localStoragePath); fsType {
@ -1025,7 +1040,7 @@ func main() {
&opts,
)
if err != nil {
return errors.Wrap(err, "opening storage failed")
return fmt.Errorf("opening storage failed: %w", err)
}
switch fsType := prom_runtime.Statfs(localStoragePath); fsType {
@ -1063,7 +1078,7 @@ func main() {
g.Add(
func() error {
if err := webHandler.Run(ctxWeb, listener, *webConfig); err != nil {
return errors.Wrapf(err, "error starting web server")
return fmt.Errorf("error starting web server: %w", err)
}
return nil
},
@ -1173,7 +1188,7 @@ func reloadConfig(filename string, expandExternalLabels, enableExemplarStorage b
conf, err := config.LoadFile(filename, agentMode, expandExternalLabels, logger)
if err != nil {
return errors.Wrapf(err, "couldn't load configuration (--config.file=%q)", filename)
return fmt.Errorf("couldn't load configuration (--config.file=%q): %w", filename, err)
}
if enableExemplarStorage {
@ -1192,7 +1207,7 @@ func reloadConfig(filename string, expandExternalLabels, enableExemplarStorage b
timings = append(timings, rl.name, time.Since(rstart))
}
if failed {
return errors.Errorf("one or more errors occurred while applying the new configuration (--config.file=%q)", filename)
return fmt.Errorf("one or more errors occurred while applying the new configuration (--config.file=%q)", filename)
}
noStepSuqueryInterval.Set(conf.GlobalConfig.EvaluationInterval)

View file

@ -16,6 +16,7 @@ package main
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"math"
@ -121,7 +122,8 @@ func TestFailedStartupExitCode(t *testing.T) {
err := prom.Run()
require.Error(t, err)
if exitError, ok := err.(*exec.ExitError); ok {
var exitError *exec.ExitError
if errors.As(err, &exitError) {
status := exitError.Sys().(syscall.WaitStatus)
require.Equal(t, expectedExitStatus, status.ExitStatus())
} else {
@ -233,7 +235,8 @@ func TestWALSegmentSizeBounds(t *testing.T) {
err = prom.Wait()
require.Error(t, err)
if exitError, ok := err.(*exec.ExitError); ok {
var exitError *exec.ExitError
if errors.As(err, &exitError) {
status := exitError.Sys().(syscall.WaitStatus)
require.Equal(t, expectedExitStatus, status.ExitStatus())
} else {
@ -278,7 +281,8 @@ func TestMaxBlockChunkSegmentSizeBounds(t *testing.T) {
err = prom.Wait()
require.Error(t, err)
if exitError, ok := err.(*exec.ExitError); ok {
var exitError *exec.ExitError
if errors.As(err, &exitError) {
status := exitError.Sys().(syscall.WaitStatus)
require.Equal(t, expectedExitStatus, status.ExitStatus())
} else {
@ -467,7 +471,8 @@ func TestModeSpecificFlags(t *testing.T) {
err = prom.Wait()
require.Error(t, err)
if exitError, ok := err.(*exec.ExitError); ok {
var exitError *exec.ExitError
if errors.As(err, &exitError) {
status := exitError.Sys().(syscall.WaitStatus)
require.Equal(t, tc.exitStatus, status.ExitStatus())
} else {

View file

@ -16,9 +16,8 @@ package main
import (
"archive/tar"
"compress/gzip"
"fmt"
"os"
"github.com/pkg/errors"
)
const filePerm = 0o666
@ -32,7 +31,7 @@ type tarGzFileWriter struct {
func newTarGzFileWriter(archiveName string) (*tarGzFileWriter, error) {
file, err := os.Create(archiveName)
if err != nil {
return nil, errors.Wrapf(err, "error creating archive %q", archiveName)
return nil, fmt.Errorf("error creating archive %q: %w", archiveName, err)
}
gzw := gzip.NewWriter(file)
tw := tar.NewWriter(gzw)

View file

@ -15,12 +15,13 @@ package main
import (
"context"
"errors"
"fmt"
"io"
"math"
"time"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/textparse"
@ -33,11 +34,11 @@ func getMinAndMaxTimestamps(p textparse.Parser) (int64, int64, error) {
for {
entry, err := p.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return 0, 0, errors.Wrap(err, "next")
return 0, 0, fmt.Errorf("next: %w", err)
}
if entry != textparse.EntrySeries {
@ -46,7 +47,7 @@ func getMinAndMaxTimestamps(p textparse.Parser) (int64, int64, error) {
_, ts, _ := p.Series()
if ts == nil {
return 0, 0, errors.Errorf("expected timestamp for series got none")
return 0, 0, fmt.Errorf("expected timestamp for series got none")
}
if *ts > maxt {
@ -118,7 +119,7 @@ func createBlocks(input []byte, mint, maxt, maxBlockDuration int64, maxSamplesIn
// original interval later.
w, err := tsdb.NewBlockWriter(log.NewNopLogger(), outputDir, 2*blockDuration)
if err != nil {
return errors.Wrap(err, "block writer")
return fmt.Errorf("block writer: %w", err)
}
defer func() {
err = tsdb_errors.NewMulti(err, w.Close()).Err()
@ -130,11 +131,11 @@ func createBlocks(input []byte, mint, maxt, maxBlockDuration int64, maxSamplesIn
samplesCount := 0
for {
e, err := p.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return errors.Wrap(err, "parse")
return fmt.Errorf("parse: %w", err)
}
if e != textparse.EntrySeries {
continue
@ -144,7 +145,7 @@ func createBlocks(input []byte, mint, maxt, maxBlockDuration int64, maxSamplesIn
if ts == nil {
l := labels.Labels{}
p.Metric(&l)
return errors.Errorf("expected timestamp for series %v, got none", l)
return fmt.Errorf("expected timestamp for series %v, got none", l)
}
if *ts < t {
continue
@ -160,7 +161,7 @@ func createBlocks(input []byte, mint, maxt, maxBlockDuration int64, maxSamplesIn
p.Metric(&l)
if _, err := app.Append(0, l, *ts, v); err != nil {
return errors.Wrap(err, "add sample")
return fmt.Errorf("add sample: %w", err)
}
samplesCount++
@ -172,7 +173,7 @@ func createBlocks(input []byte, mint, maxt, maxBlockDuration int64, maxSamplesIn
// Therefore the old appender is committed and a new one is created.
// This prevents keeping too many samples lined up in an appender and thus in RAM.
if err := app.Commit(); err != nil {
return errors.Wrap(err, "commit")
return fmt.Errorf("commit: %w", err)
}
app = w.Appender(ctx)
@ -180,18 +181,18 @@ func createBlocks(input []byte, mint, maxt, maxBlockDuration int64, maxSamplesIn
}
if err := app.Commit(); err != nil {
return errors.Wrap(err, "commit")
return fmt.Errorf("commit: %w", err)
}
block, err := w.Flush(ctx)
switch err {
case nil:
switch {
case err == nil:
if quiet {
break
}
blocks, err := db.Blocks()
if err != nil {
return errors.Wrap(err, "get blocks")
return fmt.Errorf("get blocks: %w", err)
}
for _, b := range blocks {
if b.Meta().ULID == block {
@ -200,15 +201,15 @@ func createBlocks(input []byte, mint, maxt, maxBlockDuration int64, maxSamplesIn
break
}
}
case tsdb.ErrNoSeriesAppended:
case errors.Is(err, tsdb.ErrNoSeriesAppended):
default:
return errors.Wrap(err, "flush")
return fmt.Errorf("flush: %w", err)
}
return nil
}()
if err != nil {
return errors.Wrap(err, "process blocks")
return fmt.Errorf("process blocks: %w", err)
}
}
return nil
@ -218,7 +219,10 @@ func backfill(maxSamplesInAppender int, input []byte, outputDir string, humanRea
p := textparse.NewOpenMetricsParser(input)
maxt, mint, err := getMinAndMaxTimestamps(p)
if err != nil {
return errors.Wrap(err, "getting min and max timestamp")
return fmt.Errorf("getting min and max timestamp: %w", err)
}
return errors.Wrap(createBlocks(input, mint, maxt, int64(maxBlockDuration/time.Millisecond), maxSamplesInAppender, outputDir, humanReadable, quiet), "block creation")
if err = createBlocks(input, mint, maxt, int64(maxBlockDuration/time.Millisecond), maxSamplesInAppender, outputDir, humanReadable, quiet); err != nil {
return fmt.Errorf("block creation: %w", err)
}
return nil
}

View file

@ -17,8 +17,6 @@ import (
"fmt"
"io"
"net/http"
"github.com/pkg/errors"
)
type debugWriterConfig struct {
@ -30,7 +28,7 @@ type debugWriterConfig struct {
func debugWrite(cfg debugWriterConfig) error {
archiver, err := newTarGzFileWriter(cfg.tarballName)
if err != nil {
return errors.Wrap(err, "error creating a new archiver")
return fmt.Errorf("error creating a new archiver: %w", err)
}
for _, endPointGroup := range cfg.endPointGroups {
@ -39,28 +37,28 @@ func debugWrite(cfg debugWriterConfig) error {
fmt.Println("collecting:", url)
res, err := http.Get(url)
if err != nil {
return errors.Wrap(err, "error executing HTTP request")
return fmt.Errorf("error executing HTTP request: %w", err)
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
return errors.Wrap(err, "error reading the response body")
return fmt.Errorf("error reading the response body: %w", err)
}
if endPointGroup.postProcess != nil {
body, err = endPointGroup.postProcess(body)
if err != nil {
return errors.Wrap(err, "error post-processing HTTP response body")
return fmt.Errorf("error post-processing HTTP response body: %w", err)
}
}
if err := archiver.write(filename, body); err != nil {
return errors.Wrap(err, "error writing into the archive")
return fmt.Errorf("error writing into the archive: %w", err)
}
}
}
if err := archiver.close(); err != nil {
return errors.Wrap(err, "error closing archive writer")
return fmt.Errorf("error closing archive writer: %w", err)
}
fmt.Printf("Compiling debug information complete, all files written in %q.\n", cfg.tarballName)

View file

@ -17,6 +17,7 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"math"
@ -32,7 +33,6 @@ import (
"github.com/go-kit/log"
"github.com/google/pprof/profile"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/api"
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/prometheus/client_golang/prometheus/promhttp"
@ -95,6 +95,9 @@ func main() {
"lint",
"Linting checks to apply to the rules specified in the config. Available options are: "+strings.Join(lintOptions, ", ")+". Use --lint=none to disable linting",
).Default(lintOptionDuplicateRules).String()
checkConfigLintFatal := checkConfigCmd.Flag(
"lint-fatal",
"Make lint errors exit with exit code 3.").Default("false").Bool()
checkWebConfigCmd := checkCmd.Command("web-config", "Check if the web config files are valid or not.")
webConfigFiles := checkWebConfigCmd.Arg(
@ -111,6 +114,9 @@ func main() {
"lint",
"Linting checks to apply. Available options are: "+strings.Join(lintOptions, ", ")+". Use --lint=none to disable linting",
).Default(lintOptionDuplicateRules).String()
checkRulesLintFatal := checkRulesCmd.Flag(
"lint-fatal",
"Make lint errors exit with exit code 3.").Default("false").Bool()
checkMetricsCmd := checkCmd.Command("metrics", checkMetricsUsage)
checkMetricsExtended := checkCmd.Flag("extended", "Print extended information related to the cardinality of the metrics.").Bool()
@ -151,6 +157,7 @@ func main() {
queryLabelsName := queryLabelsCmd.Arg("name", "Label name to provide label values for.").Required().String()
queryLabelsBegin := queryLabelsCmd.Flag("start", "Start time (RFC3339 or Unix timestamp).").String()
queryLabelsEnd := queryLabelsCmd.Flag("end", "End time (RFC3339 or Unix timestamp).").String()
queryLabelsMatch := queryLabelsCmd.Flag("match", "Series selector. Can be specified multiple times.").Strings()
testCmd := app.Command("test", "Unit testing.")
testRulesCmd := testCmd.Command("rules", "Unit tests for rules.")
@ -235,13 +242,13 @@ func main() {
os.Exit(CheckSD(*sdConfigFile, *sdJobName, *sdTimeout))
case checkConfigCmd.FullCommand():
os.Exit(CheckConfig(*agentMode, *checkConfigSyntaxOnly, newLintConfig(*checkConfigLint), *configFiles...))
os.Exit(CheckConfig(*agentMode, *checkConfigSyntaxOnly, newLintConfig(*checkConfigLint, *checkConfigLintFatal), *configFiles...))
case checkWebConfigCmd.FullCommand():
os.Exit(CheckWebConfig(*webConfigFiles...))
case checkRulesCmd.FullCommand():
os.Exit(CheckRules(newLintConfig(*checkRulesLint), *ruleFiles...))
os.Exit(CheckRules(newLintConfig(*checkRulesLint, *checkRulesLintFatal), *ruleFiles...))
case checkMetricsCmd.FullCommand():
os.Exit(CheckMetrics(*checkMetricsExtended))
@ -265,7 +272,7 @@ func main() {
os.Exit(debugAll(*debugAllServer))
case queryLabelsCmd.FullCommand():
os.Exit(QueryLabels(*queryLabelsServer, *queryLabelsName, *queryLabelsBegin, *queryLabelsEnd, p))
os.Exit(QueryLabels(*queryLabelsServer, *queryLabelsMatch, *queryLabelsName, *queryLabelsBegin, *queryLabelsEnd, p))
case testRulesCmd.FullCommand():
os.Exit(RulesUnitTest(
@ -302,11 +309,14 @@ var lintError = fmt.Errorf("lint error")
type lintConfig struct {
all bool
duplicateRules bool
fatal bool
}
func newLintConfig(stringVal string) lintConfig {
func newLintConfig(stringVal string, fatal bool) lintConfig {
items := strings.Split(stringVal, ",")
ls := lintConfig{}
ls := lintConfig{
fatal: fatal,
}
for _, setting := range items {
switch setting {
case lintOptionAll:
@ -328,7 +338,7 @@ func (ls lintConfig) lintDuplicateRules() bool {
// CheckConfig validates configuration files.
func CheckConfig(agentMode, checkSyntaxOnly bool, lintSettings lintConfig, files ...string) int {
failed := false
hasLintErrors := false
hasErrors := false
for _, f := range files {
ruleFiles, err := checkConfig(agentMode, f, checkSyntaxOnly)
@ -351,7 +361,7 @@ func CheckConfig(agentMode, checkSyntaxOnly bool, lintSettings lintConfig, files
}
failed = true
for _, err := range errs {
hasLintErrors = hasLintErrors || errors.Is(err, lintError)
hasErrors = hasErrors || !errors.Is(err, lintError)
}
} else {
fmt.Printf(" SUCCESS: %d rules found\n", n)
@ -359,12 +369,12 @@ func CheckConfig(agentMode, checkSyntaxOnly bool, lintSettings lintConfig, files
fmt.Println()
}
}
if failed && hasLintErrors {
return lintErrExitCode
}
if failed {
if failed && hasErrors {
return failureExitCode
}
if failed && lintSettings.fatal {
return lintErrExitCode
}
return successExitCode
}
@ -413,10 +423,10 @@ func checkConfig(agentMode bool, filename string, checkSyntaxOnly bool) ([]strin
// If an explicit file was given, error if it is not accessible.
if !strings.Contains(rf, "*") {
if len(rfs) == 0 {
return nil, errors.Errorf("%q does not point to an existing file", rf)
return nil, fmt.Errorf("%q does not point to an existing file", rf)
}
if err := checkFileExists(rfs[0]); err != nil {
return nil, errors.Wrapf(err, "error checking rule file %q", rfs[0])
return nil, fmt.Errorf("error checking rule file %q: %w", rfs[0], err)
}
}
ruleFiles = append(ruleFiles, rfs...)
@ -426,7 +436,7 @@ func checkConfig(agentMode bool, filename string, checkSyntaxOnly bool) ([]strin
for _, scfg := range cfg.ScrapeConfigs {
if !checkSyntaxOnly && scfg.HTTPClientConfig.Authorization != nil {
if err := checkFileExists(scfg.HTTPClientConfig.Authorization.CredentialsFile); err != nil {
return nil, errors.Wrapf(err, "error checking authorization credentials or bearer token file %q", scfg.HTTPClientConfig.Authorization.CredentialsFile)
return nil, fmt.Errorf("error checking authorization credentials or bearer token file %q: %w", scfg.HTTPClientConfig.Authorization.CredentialsFile, err)
}
}
@ -454,7 +464,7 @@ func checkConfig(agentMode bool, filename string, checkSyntaxOnly bool) ([]strin
var targetGroups []*targetgroup.Group
targetGroups, err = checkSDFile(f)
if err != nil {
return nil, errors.Errorf("checking SD file %q: %v", file, err)
return nil, fmt.Errorf("checking SD file %q: %w", file, err)
}
if err := checkTargetGroupsForScrapeConfig(targetGroups, scfg); err != nil {
return nil, err
@ -490,7 +500,7 @@ func checkConfig(agentMode bool, filename string, checkSyntaxOnly bool) ([]strin
var targetGroups []*targetgroup.Group
targetGroups, err = checkSDFile(f)
if err != nil {
return nil, errors.Errorf("checking SD file %q: %v", file, err)
return nil, fmt.Errorf("checking SD file %q: %w", file, err)
}
if err := checkTargetGroupsForAlertmanager(targetGroups, amcfg); err != nil {
@ -513,10 +523,10 @@ func checkConfig(agentMode bool, filename string, checkSyntaxOnly bool) ([]strin
func checkTLSConfig(tlsConfig config_util.TLSConfig, checkSyntaxOnly bool) error {
if len(tlsConfig.CertFile) > 0 && len(tlsConfig.KeyFile) == 0 {
return errors.Errorf("client cert file %q specified without client key file", tlsConfig.CertFile)
return fmt.Errorf("client cert file %q specified without client key file", tlsConfig.CertFile)
}
if len(tlsConfig.KeyFile) > 0 && len(tlsConfig.CertFile) == 0 {
return errors.Errorf("client key file %q specified without client cert file", tlsConfig.KeyFile)
return fmt.Errorf("client key file %q specified without client cert file", tlsConfig.KeyFile)
}
if checkSyntaxOnly {
@ -524,10 +534,10 @@ func checkTLSConfig(tlsConfig config_util.TLSConfig, checkSyntaxOnly bool) error
}
if err := checkFileExists(tlsConfig.CertFile); err != nil {
return errors.Wrapf(err, "error checking client cert file %q", tlsConfig.CertFile)
return fmt.Errorf("error checking client cert file %q: %w", tlsConfig.CertFile, err)
}
if err := checkFileExists(tlsConfig.KeyFile); err != nil {
return errors.Wrapf(err, "error checking client key file %q", tlsConfig.KeyFile)
return fmt.Errorf("error checking client key file %q: %w", tlsConfig.KeyFile, err)
}
return nil
@ -557,12 +567,12 @@ func checkSDFile(filename string) ([]*targetgroup.Group, error) {
return nil, err
}
default:
return nil, errors.Errorf("invalid file extension: %q", ext)
return nil, fmt.Errorf("invalid file extension: %q", ext)
}
for i, tg := range targetGroups {
if tg == nil {
return nil, errors.Errorf("nil target group item found (index %d)", i)
return nil, fmt.Errorf("nil target group item found (index %d)", i)
}
}
@ -572,7 +582,7 @@ func checkSDFile(filename string) ([]*targetgroup.Group, error) {
// CheckRules validates rule files.
func CheckRules(ls lintConfig, files ...string) int {
failed := false
hasLintErrors := false
hasErrors := false
for _, f := range files {
if n, errs := checkRules(f, ls); errs != nil {
@ -582,19 +592,19 @@ func CheckRules(ls lintConfig, files ...string) int {
}
failed = true
for _, err := range errs {
hasLintErrors = hasLintErrors || errors.Is(err, lintError)
hasErrors = hasErrors || !errors.Is(err, lintError)
}
} else {
fmt.Printf(" SUCCESS: %d rules found\n", n)
}
fmt.Println()
}
if failed && hasLintErrors {
return lintErrExitCode
}
if failed {
if failed && hasErrors {
return failureExitCode
}
if failed && ls.fatal {
return lintErrExitCode
}
return successExitCode
}
@ -929,7 +939,7 @@ func QuerySeries(url *url.URL, matchers []string, start, end string, p printer)
}
// QueryLabels queries for label values against a Prometheus server.
func QueryLabels(url *url.URL, name, start, end string, p printer) int {
func QueryLabels(url *url.URL, matchers []string, name, start, end string, p printer) int {
if url.Scheme == "" {
url.Scheme = "http"
}
@ -953,7 +963,7 @@ func QueryLabels(url *url.URL, name, start, end string, p printer) int {
// Run query against client.
api := v1.NewAPI(c)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
val, warn, err := api.LabelValues(ctx, name, []string{}, stime, etime)
val, warn, err := api.LabelValues(ctx, name, matchers, stime, etime)
cancel()
for _, v := range warn {
@ -991,14 +1001,14 @@ func parseStartTimeAndEndTime(start, end string) (time.Time, time.Time, error) {
if start != "" {
stime, err = parseTime(start)
if err != nil {
return stime, etime, errors.Wrap(err, "error parsing start time")
return stime, etime, fmt.Errorf("error parsing start time: %w", err)
}
}
if end != "" {
etime, err = parseTime(end)
if err != nil {
return stime, etime, errors.Wrap(err, "error parsing end time")
return stime, etime, fmt.Errorf("error parsing end time: %w", err)
}
}
return stime, etime, nil
@ -1012,7 +1022,7 @@ func parseTime(s string) (time.Time, error) {
if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
return t, nil
}
return time.Time{}, errors.Errorf("cannot parse %q to a valid timestamp", s)
return time.Time{}, fmt.Errorf("cannot parse %q to a valid timestamp", s)
}
type endpointsGroup struct {
@ -1038,7 +1048,7 @@ var (
}
var buf bytes.Buffer
if err := p.WriteUncompressed(&buf); err != nil {
return nil, errors.Wrap(err, "writing the profile to the buffer")
return nil, fmt.Errorf("writing the profile to the buffer: %w", err)
}
return buf.Bytes(), nil
@ -1148,13 +1158,13 @@ func importRules(url *url.URL, start, end, outputDir string, evalInterval, maxBl
} else {
etime, err = parseTime(end)
if err != nil {
return fmt.Errorf("error parsing end time: %v", err)
return fmt.Errorf("error parsing end time: %w", err)
}
}
stime, err = parseTime(start)
if err != nil {
return fmt.Errorf("error parsing start time: %v", err)
return fmt.Errorf("error parsing start time: %w", err)
}
if !stime.Before(etime) {
@ -1172,14 +1182,14 @@ func importRules(url *url.URL, start, end, outputDir string, evalInterval, maxBl
Address: url.String(),
})
if err != nil {
return fmt.Errorf("new api client error: %v", err)
return fmt.Errorf("new api client error: %w", err)
}
ruleImporter := newRuleImporter(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), cfg, v1.NewAPI(client))
errs := ruleImporter.loadGroups(ctx, files)
for _, err := range errs {
if err != nil {
return fmt.Errorf("rule importer parse error: %v", err)
return fmt.Errorf("rule importer parse error: %w", err)
}
}

View file

@ -20,7 +20,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/prometheus/common/model"
@ -122,7 +121,7 @@ func (importer *ruleImporter) importRule(ctx context.Context, ruleExpr, ruleName
},
)
if err != nil {
return errors.Wrap(err, "query range")
return fmt.Errorf("query range: %w", err)
}
if warnings != nil {
level.Warn(importer.logger).Log("msg", "Range query returned warnings.", "warnings", warnings)
@ -136,7 +135,7 @@ func (importer *ruleImporter) importRule(ctx context.Context, ruleExpr, ruleName
// original interval later.
w, err := tsdb.NewBlockWriter(log.NewNopLogger(), importer.config.outputDir, 2*blockDuration)
if err != nil {
return errors.Wrap(err, "new block writer")
return fmt.Errorf("new block writer: %w", err)
}
var closed bool
defer func() {
@ -167,7 +166,7 @@ func (importer *ruleImporter) importRule(ctx context.Context, ruleExpr, ruleName
for _, value := range sample.Values {
if err := app.add(ctx, lb.Labels(), timestamp.FromTime(value.Timestamp.Time()), float64(value.Value)); err != nil {
return errors.Wrap(err, "add")
return fmt.Errorf("add: %w", err)
}
}
}
@ -176,7 +175,7 @@ func (importer *ruleImporter) importRule(ctx context.Context, ruleExpr, ruleName
}
if err := app.flushAndCommit(ctx); err != nil {
return errors.Wrap(err, "flush and commit")
return fmt.Errorf("flush and commit: %w", err)
}
err = tsdb_errors.NewMulti(err, w.Close()).Err()
closed = true
@ -204,7 +203,7 @@ type multipleAppender struct {
func (m *multipleAppender) add(ctx context.Context, l labels.Labels, t int64, v float64) error {
if _, err := m.appender.Append(0, l, t, v); err != nil {
return errors.Wrap(err, "multiappender append")
return fmt.Errorf("multiappender append: %w", err)
}
m.currentSampleCount++
if m.currentSampleCount >= m.maxSamplesInMemory {
@ -218,7 +217,7 @@ func (m *multipleAppender) commit(ctx context.Context) error {
return nil
}
if err := m.appender.Commit(); err != nil {
return errors.Wrap(err, "multiappender commit")
return fmt.Errorf("multiappender commit: %w", err)
}
m.appender = m.writer.Appender(ctx)
m.currentSampleCount = 0
@ -230,7 +229,7 @@ func (m *multipleAppender) flushAndCommit(ctx context.Context) error {
return err
}
if _, err := m.writer.Flush(ctx); err != nil {
return errors.Wrap(err, "multiappender flush")
return fmt.Errorf("multiappender flush: %w", err)
}
return nil
}

View file

@ -36,7 +36,6 @@ import (
"github.com/alecthomas/units"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/tsdb"
@ -237,29 +236,29 @@ func (b *writeBenchmark) startProfiling() error {
// Start CPU profiling.
b.cpuprof, err = os.Create(filepath.Join(b.outPath, "cpu.prof"))
if err != nil {
return fmt.Errorf("bench: could not create cpu profile: %v", err)
return fmt.Errorf("bench: could not create cpu profile: %w", err)
}
if err := pprof.StartCPUProfile(b.cpuprof); err != nil {
return fmt.Errorf("bench: could not start CPU profile: %v", err)
return fmt.Errorf("bench: could not start CPU profile: %w", err)
}
// Start memory profiling.
b.memprof, err = os.Create(filepath.Join(b.outPath, "mem.prof"))
if err != nil {
return fmt.Errorf("bench: could not create memory profile: %v", err)
return fmt.Errorf("bench: could not create memory profile: %w", err)
}
runtime.MemProfileRate = 64 * 1024
// Start fatal profiling.
b.blockprof, err = os.Create(filepath.Join(b.outPath, "block.prof"))
if err != nil {
return fmt.Errorf("bench: could not create block profile: %v", err)
return fmt.Errorf("bench: could not create block profile: %w", err)
}
runtime.SetBlockProfileRate(20)
b.mtxprof, err = os.Create(filepath.Join(b.outPath, "mutex.prof"))
if err != nil {
return fmt.Errorf("bench: could not create mutex profile: %v", err)
return fmt.Errorf("bench: could not create mutex profile: %w", err)
}
runtime.SetMutexProfileFraction(20)
return nil
@ -273,14 +272,14 @@ func (b *writeBenchmark) stopProfiling() error {
}
if b.memprof != nil {
if err := pprof.Lookup("heap").WriteTo(b.memprof, 0); err != nil {
return fmt.Errorf("error writing mem profile: %v", err)
return fmt.Errorf("error writing mem profile: %w", err)
}
b.memprof.Close()
b.memprof = nil
}
if b.blockprof != nil {
if err := pprof.Lookup("block").WriteTo(b.blockprof, 0); err != nil {
return fmt.Errorf("error writing block profile: %v", err)
return fmt.Errorf("error writing block profile: %w", err)
}
b.blockprof.Close()
b.blockprof = nil
@ -288,7 +287,7 @@ func (b *writeBenchmark) stopProfiling() error {
}
if b.mtxprof != nil {
if err := pprof.Lookup("mutex").WriteTo(b.mtxprof, 0); err != nil {
return fmt.Errorf("error writing mutex profile: %v", err)
return fmt.Errorf("error writing mutex profile: %w", err)
}
b.mtxprof.Close()
b.mtxprof = nil
@ -681,7 +680,7 @@ func backfillOpenMetrics(path, outputDir string, humanReadable, quiet bool, maxB
defer inputFile.Close()
if err := os.MkdirAll(outputDir, 0o777); err != nil {
return checkErr(errors.Wrap(err, "create output dir"))
return checkErr(fmt.Errorf("create output dir: %w", err))
}
return checkErr(backfill(5000, inputFile.Bytes(), outputDir, humanReadable, quiet, maxBlockDuration))

View file

@ -15,6 +15,7 @@ package main
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
@ -25,7 +26,6 @@ import (
"time"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
yaml "gopkg.in/yaml.v2"
@ -87,7 +87,7 @@ func ruleUnitTest(filename string, queryOpts promql.LazyLoaderOpts) []error {
groupOrderMap := make(map[string]int)
for i, gn := range unitTestInp.GroupEvalOrder {
if _, ok := groupOrderMap[gn]; ok {
return []error{errors.Errorf("group name repeated in evaluation order: %s", gn)}
return []error{fmt.Errorf("group name repeated in evaluation order: %s", gn)}
}
groupOrderMap[gn] = i
}
@ -195,7 +195,7 @@ func (tg *testGroup) test(evalInterval time.Duration, groupOrderMap map[string]i
if tg.TestGroupName != "" {
testGroupLog = fmt.Sprintf(" (in TestGroup %s)", tg.TestGroupName)
}
return []error{errors.Errorf("an item under alert_rule_test misses required attribute alertname at eval_time %v%s", alert.EvalTime, testGroupLog)}
return []error{fmt.Errorf("an item under alert_rule_test misses required attribute alertname at eval_time %v%s", alert.EvalTime, testGroupLog)}
}
alertEvalTimesMap[alert.EvalTime] = struct{}{}
@ -240,7 +240,7 @@ func (tg *testGroup) test(evalInterval time.Duration, groupOrderMap map[string]i
g.Eval(suite.Context(), ts)
for _, r := range g.Rules() {
if r.LastError() != nil {
evalErrs = append(evalErrs, errors.Errorf(" rule: %s, time: %s, err: %v",
evalErrs = append(evalErrs, fmt.Errorf(" rule: %s, time: %s, err: %v",
r.Name(), ts.Sub(time.Unix(0, 0).UTC()), r.LastError()))
}
}
@ -323,7 +323,7 @@ func (tg *testGroup) test(evalInterval time.Duration, groupOrderMap map[string]i
}
expString := indentLines(expAlerts.String(), " ")
gotString := indentLines(gotAlerts.String(), " ")
errs = append(errs, errors.Errorf("%s alertname: %s, time: %s, \n exp:%v, \n got:%v",
errs = append(errs, fmt.Errorf("%s alertname: %s, time: %s, \n exp:%v, \n got:%v",
testName, testcase.Alertname, testcase.EvalTime.String(), expString, gotString))
}
}
@ -338,7 +338,7 @@ Outer:
got, err := query(suite.Context(), testCase.Expr, mint.Add(time.Duration(testCase.EvalTime)),
suite.QueryEngine(), suite.Queryable())
if err != nil {
errs = append(errs, errors.Errorf(" expr: %q, time: %s, err: %s", testCase.Expr,
errs = append(errs, fmt.Errorf(" expr: %q, time: %s, err: %s", testCase.Expr,
testCase.EvalTime.String(), err.Error()))
continue
}
@ -355,9 +355,9 @@ Outer:
for _, s := range testCase.ExpSamples {
lb, err := parser.ParseMetric(s.Labels)
if err != nil {
err = errors.Wrapf(err, "labels %q", s.Labels)
errs = append(errs, errors.Errorf(" expr: %q, time: %s, err: %s", testCase.Expr,
testCase.EvalTime.String(), err.Error()))
err = fmt.Errorf("labels %q: %w", s.Labels, err)
errs = append(errs, fmt.Errorf(" expr: %q, time: %s, err: %w", testCase.Expr,
testCase.EvalTime.String(), err))
continue Outer
}
expSamples = append(expSamples, parsedSample{
@ -373,7 +373,7 @@ Outer:
return labels.Compare(gotSamples[i].Labels, gotSamples[j].Labels) <= 0
})
if !reflect.DeepEqual(expSamples, gotSamples) {
errs = append(errs, errors.Errorf(" expr: %q, time: %s,\n exp: %v\n got: %v", testCase.Expr,
errs = append(errs, fmt.Errorf(" expr: %q, time: %s,\n exp: %v\n got: %v", testCase.Expr,
testCase.EvalTime.String(), parsedSamplesString(expSamples), parsedSamplesString(gotSamples)))
}
}

View file

@ -14,6 +14,7 @@
package config
import (
"errors"
"fmt"
"net/url"
"os"
@ -25,7 +26,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/grafana/regexp"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/common/sigv4"
@ -108,7 +108,7 @@ func LoadFile(filename string, agentMode, expandExternalLabels bool, logger log.
}
cfg, err := Load(string(content), expandExternalLabels, logger)
if err != nil {
return nil, errors.Wrapf(err, "parsing YAML file %s", filename)
return nil, fmt.Errorf("parsing YAML file %s: %w", filename, err)
}
if agentMode {
@ -276,7 +276,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
for _, rf := range c.RuleFiles {
if !patRulePath.MatchString(rf) {
return errors.Errorf("invalid rule file path %q", rf)
return fmt.Errorf("invalid rule file path %q", rf)
}
}
// Do global overrides and validate unique names.
@ -291,7 +291,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
scfg.ScrapeInterval = c.GlobalConfig.ScrapeInterval
}
if scfg.ScrapeTimeout > scfg.ScrapeInterval {
return errors.Errorf("scrape timeout greater than scrape interval for scrape config with job name %q", scfg.JobName)
return fmt.Errorf("scrape timeout greater than scrape interval for scrape config with job name %q", scfg.JobName)
}
if scfg.ScrapeTimeout == 0 {
if c.GlobalConfig.ScrapeTimeout > scfg.ScrapeInterval {
@ -302,7 +302,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
if _, ok := jobNames[scfg.JobName]; ok {
return errors.Errorf("found multiple scrape configs with job name %q", scfg.JobName)
return fmt.Errorf("found multiple scrape configs with job name %q", scfg.JobName)
}
jobNames[scfg.JobName] = struct{}{}
}
@ -313,7 +313,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
// Skip empty names, we fill their name with their config hash in remote write code.
if _, ok := rwNames[rwcfg.Name]; ok && rwcfg.Name != "" {
return errors.Errorf("found multiple remote write configs with job name %q", rwcfg.Name)
return fmt.Errorf("found multiple remote write configs with job name %q", rwcfg.Name)
}
rwNames[rwcfg.Name] = struct{}{}
}
@ -324,7 +324,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
// Skip empty names, we fill their name with their config hash in remote read code.
if _, ok := rrNames[rrcfg.Name]; ok && rrcfg.Name != "" {
return errors.Errorf("found multiple remote read configs with job name %q", rrcfg.Name)
return fmt.Errorf("found multiple remote read configs with job name %q", rrcfg.Name)
}
rrNames[rrcfg.Name] = struct{}{}
}
@ -363,10 +363,10 @@ func (c *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
for _, l := range gc.ExternalLabels {
if !model.LabelName(l.Name).IsValid() {
return errors.Errorf("%q is not a valid label name", l.Name)
return fmt.Errorf("%q is not a valid label name", l.Name)
}
if !model.LabelValue(l.Value).IsValid() {
return errors.Errorf("%q is not a valid label value", l.Value)
return fmt.Errorf("%q is not a valid label value", l.Value)
}
}
@ -741,7 +741,7 @@ func checkStaticTargets(configs discovery.Configs) error {
func CheckTargetAddress(address model.LabelValue) error {
// For now check for a URL, we may want to expand this later.
if strings.Contains(string(address), "/") {
return errors.Errorf("%q is not a valid hostname", address)
return fmt.Errorf("%q is not a valid hostname", address)
}
return nil
}
@ -810,7 +810,7 @@ func validateHeadersForTracing(headers map[string]string) error {
return errors.New("custom authorization header configuration is not yet supported")
}
if _, ok := reservedHeaders[strings.ToLower(header)]; ok {
return errors.Errorf("%s is a reserved header. It must not be changed", header)
return fmt.Errorf("%s is a reserved header. It must not be changed", header)
}
}
return nil
@ -822,7 +822,7 @@ func validateHeaders(headers map[string]string) error {
return errors.New("authorization header must be changed via the basic_auth, authorization, oauth2, or sigv4 parameter")
}
if _, ok := reservedHeaders[strings.ToLower(header)]; ok {
return errors.Errorf("%s is a reserved header. It must not be changed", header)
return fmt.Errorf("%s is a reserved header. It must not be changed", header)
}
}
return nil

View file

@ -40,6 +40,7 @@ import (
"github.com/prometheus/prometheus/discovery/file"
"github.com/prometheus/prometheus/discovery/hetzner"
"github.com/prometheus/prometheus/discovery/http"
"github.com/prometheus/prometheus/discovery/ionos"
"github.com/prometheus/prometheus/discovery/kubernetes"
"github.com/prometheus/prometheus/discovery/linode"
"github.com/prometheus/prometheus/discovery/marathon"
@ -50,6 +51,7 @@ import (
"github.com/prometheus/prometheus/discovery/targetgroup"
"github.com/prometheus/prometheus/discovery/triton"
"github.com/prometheus/prometheus/discovery/uyuni"
"github.com/prometheus/prometheus/discovery/vultr"
"github.com/prometheus/prometheus/discovery/xds"
"github.com/prometheus/prometheus/discovery/zookeeper"
"github.com/prometheus/prometheus/model/labels"
@ -987,11 +989,61 @@ var expectedConf = &Config{
Scheme: DefaultScrapeConfig.Scheme,
ServiceDiscoveryConfigs: discovery.Configs{
&uyuni.SDConfig{
Server: "https://localhost:1234",
Username: "gopher",
Password: "hole",
Entitlement: "monitoring_entitled",
Separator: ",",
Server: "https://localhost:1234",
Username: "gopher",
Password: "hole",
Entitlement: "monitoring_entitled",
Separator: ",",
RefreshInterval: model.Duration(60 * time.Second),
HTTPClientConfig: config.DefaultHTTPClientConfig,
},
},
},
{
JobName: "ionos",
HonorTimestamps: true,
ScrapeInterval: model.Duration(15 * time.Second),
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
MetricsPath: DefaultScrapeConfig.MetricsPath,
Scheme: DefaultScrapeConfig.Scheme,
HTTPClientConfig: config.DefaultHTTPClientConfig,
ServiceDiscoveryConfigs: discovery.Configs{
&ionos.SDConfig{
DatacenterID: "8feda53f-15f0-447f-badf-ebe32dad2fc0",
HTTPClientConfig: config.HTTPClientConfig{
Authorization: &config.Authorization{Type: "Bearer", Credentials: "abcdef"},
FollowRedirects: true,
EnableHTTP2: true,
},
Port: 80,
RefreshInterval: model.Duration(60 * time.Second),
},
},
},
{
JobName: "vultr",
HonorTimestamps: true,
ScrapeInterval: model.Duration(15 * time.Second),
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
MetricsPath: DefaultScrapeConfig.MetricsPath,
Scheme: DefaultScrapeConfig.Scheme,
HTTPClientConfig: config.DefaultHTTPClientConfig,
ServiceDiscoveryConfigs: discovery.Configs{
&vultr.SDConfig{
HTTPClientConfig: config.HTTPClientConfig{
Authorization: &config.Authorization{
Type: "Bearer",
Credentials: "abcdef",
},
FollowRedirects: true,
EnableHTTP2: true,
},
Port: 80,
RefreshInterval: model.Duration(60 * time.Second),
},
},
@ -1093,7 +1145,7 @@ func TestElideSecrets(t *testing.T) {
yamlConfig := string(config)
matches := secretRe.FindAllStringIndex(yamlConfig, -1)
require.Equal(t, 16, len(matches), "wrong number of secret matches found")
require.Equal(t, 18, len(matches), "wrong number of secret matches found")
require.NotContains(t, yamlConfig, "mysecret",
"yaml marshal reveals authentication credentials.")
}
@ -1532,6 +1584,10 @@ var expectedErrors = []struct {
filename: "uyuni_no_server.bad.yml",
errMsg: "Uyuni SD configuration requires server host",
},
{
filename: "ionos_datacenter.bad.yml",
errMsg: "datacenter id can't be empty",
},
}
func TestBadConfigs(t *testing.T) {

View file

@ -367,6 +367,17 @@ scrape_configs:
username: gopher
password: hole
- job_name: ionos
ionos_sd_configs:
- datacenter_id: 8feda53f-15f0-447f-badf-ebe32dad2fc0
authorization:
credentials: abcdef
- job_name: vultr
vultr_sd_configs:
- authorization:
credentials: abcdef
alerting:
alertmanagers:
- scheme: https

View file

@ -0,0 +1,3 @@
scrape_configs:
- ionos_sd_configs:
- datacenter_id: ""

View file

@ -131,7 +131,7 @@ the Prometheus server will be able to see them.
### The SD interface
A Service Discovery (SD) mechanism has to discover targets and provide them to Prometheus. We expect similar targets to be grouped together, in the form of a [target group](https://pkg.go.dev/github.com/prometheus/prometheus@v1.8.2-0.20211105201321-411021ada9ab/discovery/targetgroup#Group). The SD mechanism sends the targets down to prometheus as list of target groups.
A Service Discovery (SD) mechanism has to discover targets and provide them to Prometheus. We expect similar targets to be grouped together, in the form of a [target group](https://pkg.go.dev/github.com/prometheus/prometheus/discovery/targetgroup#Group). The SD mechanism sends the targets down to prometheus as list of target groups.
An SD mechanism has to implement the `Discoverer` Interface:
```go

View file

@ -15,6 +15,7 @@ package aws
import (
"context"
"errors"
"fmt"
"net"
"strings"
@ -29,7 +30,6 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -179,7 +179,7 @@ func (d *EC2Discovery) ec2Client(ctx context.Context) (*ec2.EC2, error) {
Profile: d.cfg.Profile,
})
if err != nil {
return nil, errors.Wrap(err, "could not create aws session")
return nil, fmt.Errorf("could not create aws session: %w", err)
}
if d.cfg.RoleARN != "" {
@ -328,10 +328,11 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
}
return true
}); err != nil {
if awsErr, ok := err.(awserr.Error); ok && (awsErr.Code() == "AuthFailure" || awsErr.Code() == "UnauthorizedOperation") {
var awsErr awserr.Error
if errors.As(err, &awsErr) && (awsErr.Code() == "AuthFailure" || awsErr.Code() == "UnauthorizedOperation") {
d.ec2 = nil
}
return nil, errors.Wrap(err, "could not describe instances")
return nil, fmt.Errorf("could not describe instances: %w", err)
}
return []*targetgroup.Group{tg}, nil
}

View file

@ -15,6 +15,7 @@ package aws
import (
"context"
"errors"
"fmt"
"net"
"strings"
@ -28,7 +29,6 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -152,7 +152,7 @@ func (d *LightsailDiscovery) lightsailClient() (*lightsail.Lightsail, error) {
Profile: d.cfg.Profile,
})
if err != nil {
return nil, errors.Wrap(err, "could not create aws session")
return nil, fmt.Errorf("could not create aws session: %w", err)
}
if d.cfg.RoleARN != "" {
@ -179,10 +179,11 @@ func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group,
output, err := lightsailClient.GetInstancesWithContext(ctx, input)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && (awsErr.Code() == "AuthFailure" || awsErr.Code() == "UnauthorizedOperation") {
var awsErr awserr.Error
if errors.As(err, &awsErr) && (awsErr.Code() == "AuthFailure" || awsErr.Code() == "UnauthorizedOperation") {
d.lightsail = nil
}
return nil, errors.Wrap(err, "could not get instances")
return nil, fmt.Errorf("could not get instances: %w", err)
}
for _, inst := range output.Instances {

View file

@ -15,6 +15,7 @@ package azure
import (
"context"
"errors"
"fmt"
"net"
"net/http"
@ -29,7 +30,6 @@ import (
"github.com/Azure/go-autorest/autorest/azure"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
config_util "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -109,7 +109,7 @@ func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Di
func validateAuthParam(param, name string) error {
if len(param) == 0 {
return errors.Errorf("azure SD configuration requires a %s", name)
return fmt.Errorf("azure SD configuration requires a %s", name)
}
return nil
}
@ -140,7 +140,7 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
if c.AuthenticationMethod != authMethodOAuth && c.AuthenticationMethod != authMethodManagedIdentity {
return errors.Errorf("unknown authentication_type %q. Supported types are %q or %q", c.AuthenticationMethod, authMethodOAuth, authMethodManagedIdentity)
return fmt.Errorf("unknown authentication_type %q. Supported types are %q or %q", c.AuthenticationMethod, authMethodOAuth, authMethodManagedIdentity)
}
return nil
@ -271,7 +271,7 @@ func newAzureResourceFromID(id string, logger log.Logger) (azureResource, error)
// /subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP/providers/PROVIDER/TYPE/NAME/TYPE/NAME
s := strings.Split(id, "/")
if len(s) != 9 && len(s) != 11 {
err := errors.Errorf("invalid ID '%s'. Refusing to create azureResource", id)
err := fmt.Errorf("invalid ID '%s'. Refusing to create azureResource", id)
level.Error(logger).Log("err", err)
return azureResource{}, err
}
@ -288,13 +288,13 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
client, err := createAzureClient(*d.cfg)
if err != nil {
failuresCount.Inc()
return nil, errors.Wrap(err, "could not create Azure client")
return nil, fmt.Errorf("could not create Azure client: %w", err)
}
machines, err := client.getVMs(ctx, d.cfg.ResourceGroup)
if err != nil {
failuresCount.Inc()
return nil, errors.Wrap(err, "could not get virtual machines")
return nil, fmt.Errorf("could not get virtual machines: %w", err)
}
level.Debug(d.logger).Log("msg", "Found virtual machines during Azure discovery.", "count", len(machines))
@ -303,14 +303,14 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
scaleSets, err := client.getScaleSets(ctx, d.cfg.ResourceGroup)
if err != nil {
failuresCount.Inc()
return nil, errors.Wrap(err, "could not get virtual machine scale sets")
return nil, fmt.Errorf("could not get virtual machine scale sets: %w", err)
}
for _, scaleSet := range scaleSets {
scaleSetVms, err := client.getScaleSetVMs(ctx, scaleSet)
if err != nil {
failuresCount.Inc()
return nil, errors.Wrap(err, "could not get virtual machine scale set vms")
return nil, fmt.Errorf("could not get virtual machine scale set vms: %w", err)
}
machines = append(machines, scaleSetVms...)
}
@ -396,7 +396,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
}
// If we made it here, we don't have a private IP which should be impossible.
// Return an empty target and error to ensure an all or nothing situation.
err = errors.Errorf("unable to find a private IP for VM %s", vm.Name)
err = fmt.Errorf("unable to find a private IP for VM %s", vm.Name)
ch <- target{labelSet: nil, err: err}
return
}
@ -412,7 +412,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
for tgt := range ch {
if tgt.err != nil {
failuresCount.Inc()
return nil, errors.Wrap(tgt.err, "unable to complete Azure service discovery")
return nil, fmt.Errorf("unable to complete Azure service discovery: %w", tgt.err)
}
if tgt.labelSet != nil {
tg.Targets = append(tg.Targets, tgt.labelSet)
@ -432,7 +432,7 @@ func (client *azureClient) getVMs(ctx context.Context, resourceGroup string) ([]
result, err = client.vm.List(ctx, resourceGroup)
}
if err != nil {
return nil, errors.Wrap(err, "could not list virtual machines")
return nil, fmt.Errorf("could not list virtual machines: %w", err)
}
for result.NotDone() {
for _, vm := range result.Values() {
@ -440,7 +440,7 @@ func (client *azureClient) getVMs(ctx context.Context, resourceGroup string) ([]
}
err = result.NextWithContext(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not list virtual machines")
return nil, fmt.Errorf("could not list virtual machines: %w", err)
}
}
@ -461,14 +461,14 @@ func (client *azureClient) getScaleSets(ctx context.Context, resourceGroup strin
var rtn compute.VirtualMachineScaleSetListWithLinkResultPage
rtn, err = client.vmss.ListAll(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not list virtual machine scale sets")
return nil, fmt.Errorf("could not list virtual machine scale sets: %w", err)
}
result = &rtn
} else {
var rtn compute.VirtualMachineScaleSetListResultPage
rtn, err = client.vmss.List(ctx, resourceGroup)
if err != nil {
return nil, errors.Wrap(err, "could not list virtual machine scale sets")
return nil, fmt.Errorf("could not list virtual machine scale sets: %w", err)
}
result = &rtn
}
@ -477,7 +477,7 @@ func (client *azureClient) getScaleSets(ctx context.Context, resourceGroup strin
scaleSets = append(scaleSets, result.Values()...)
err = result.NextWithContext(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not list virtual machine scale sets")
return nil, fmt.Errorf("could not list virtual machine scale sets: %w", err)
}
}
@ -489,12 +489,12 @@ func (client *azureClient) getScaleSetVMs(ctx context.Context, scaleSet compute.
// TODO do we really need to fetch the resourcegroup this way?
r, err := newAzureResourceFromID(*scaleSet.ID, nil)
if err != nil {
return nil, errors.Wrap(err, "could not parse scale set ID")
return nil, fmt.Errorf("could not parse scale set ID: %w", err)
}
result, err := client.vmssvm.List(ctx, r.ResourceGroup, *(scaleSet.Name), "", "", "")
if err != nil {
return nil, errors.Wrap(err, "could not list virtual machine scale set vms")
return nil, fmt.Errorf("could not list virtual machine scale set vms: %w", err)
}
for result.NotDone() {
for _, vm := range result.Values() {
@ -502,7 +502,7 @@ func (client *azureClient) getScaleSetVMs(ctx context.Context, scaleSet compute.
}
err = result.NextWithContext(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not list virtual machine scale set vms")
return nil, fmt.Errorf("could not list virtual machine scale set vms: %w", err)
}
}

View file

@ -15,6 +15,7 @@ package consul
import (
"context"
"errors"
"fmt"
"net"
"strconv"
@ -24,7 +25,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
consul "github.com/hashicorp/consul/api"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -291,7 +291,7 @@ func (d *Discovery) getDatacenter() error {
dc, ok := info["Config"]["Datacenter"].(string)
if !ok {
err := errors.Errorf("invalid value '%v' for Config.Datacenter", info["Config"]["Datacenter"])
err := fmt.Errorf("invalid value '%v' for Config.Datacenter", info["Config"]["Datacenter"])
level.Error(d.logger).Log("msg", "Error retrieving datacenter name", "err", err)
return err
}

View file

@ -15,6 +15,7 @@ package dns
import (
"context"
"errors"
"fmt"
"net"
"strings"
@ -24,7 +25,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/miekg/dns"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model"
@ -105,7 +105,7 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
return errors.New("a port is required in DNS-SD configs for all record types except SRV")
}
default:
return errors.Errorf("invalid DNS-SD records type %s", c.Type)
return fmt.Errorf("invalid DNS-SD records type %s", c.Type)
}
return nil
}
@ -163,7 +163,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
wg.Add(len(d.names))
for _, name := range d.names {
go func(n string) {
if err := d.refreshOne(ctx, n, ch); err != nil && err != context.Canceled {
if err := d.refreshOne(ctx, n, ch); err != nil && !errors.Is(err, context.Canceled) {
level.Error(d.logger).Log("msg", "Error refreshing DNS targets", "err", err)
}
wg.Done()
@ -265,7 +265,7 @@ func (d *Discovery) refreshOne(ctx context.Context, name string, ch chan<- *targ
func lookupWithSearchPath(name string, qtype uint16, logger log.Logger) (*dns.Msg, error) {
conf, err := dns.ClientConfigFromFile(resolvConf)
if err != nil {
return nil, errors.Wrap(err, "could not load resolv.conf")
return nil, fmt.Errorf("could not load resolv.conf: %w", err)
}
allResponsesValid := true
@ -291,7 +291,7 @@ func lookupWithSearchPath(name string, qtype uint16, logger log.Logger) (*dns.Ms
return &dns.Msg{}, nil
}
// Outcome 3: boned.
return nil, errors.Errorf("could not resolve %q: all servers responded with errors to at least one search domain", name)
return nil, fmt.Errorf("could not resolve %q: all servers responded with errors to at least one search domain", name)
}
// lookupFromAnyServer uses all configured servers to try and resolve a specific
@ -327,7 +327,7 @@ func lookupFromAnyServer(name string, qtype uint16, conf *dns.ClientConfig, logg
}
}
return nil, errors.Errorf("could not resolve %s: no servers returned a viable answer", name)
return nil, fmt.Errorf("could not resolve %s: no servers returned a viable answer", name)
}
// askServerForName makes a request to a specific DNS server for a specific

View file

@ -20,7 +20,6 @@ import (
"io"
"net/http"
"github.com/pkg/errors"
"github.com/prometheus/common/version"
)
@ -99,13 +98,13 @@ func fetchApps(ctx context.Context, server string, client *http.Client) (*Applic
}()
if resp.StatusCode/100 != 2 {
return nil, errors.Errorf("non 2xx status '%d' response during eureka service discovery", resp.StatusCode)
return nil, fmt.Errorf("non 2xx status '%d' response during eureka service discovery", resp.StatusCode)
}
var apps Applications
err = xml.NewDecoder(resp.Body).Decode(&apps)
if err != nil {
return nil, errors.Wrapf(err, "%q", url)
return nil, fmt.Errorf("%q: %w", url, err)
}
return &apps, nil
}

View file

@ -15,6 +15,7 @@ package eureka
import (
"context"
"errors"
"net"
"net/http"
"net/url"
@ -22,7 +23,6 @@ import (
"time"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"

View file

@ -16,6 +16,7 @@ package file
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
@ -28,7 +29,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/grafana/regexp"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -99,7 +99,7 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
for _, name := range c.Files {
if !patFileSDName.MatchString(name) {
return errors.Errorf("path name %q is not valid for file discovery", name)
return fmt.Errorf("path name %q is not valid for file discovery", name)
}
}
return nil
@ -398,7 +398,7 @@ func (d *Discovery) readFile(filename string) ([]*targetgroup.Group, error) {
return nil, err
}
default:
panic(errors.Errorf("discovery.File.readFile: unhandled file extension %q", ext))
panic(fmt.Errorf("discovery.File.readFile: unhandled file extension %q", ext))
}
for i, tg := range targetGroups {

View file

@ -15,6 +15,7 @@ package gce
import (
"context"
"errors"
"fmt"
"net/http"
"strconv"
@ -22,7 +23,6 @@ import (
"time"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"golang.org/x/oauth2/google"
compute "google.golang.org/api/compute/v1"
@ -132,11 +132,11 @@ func NewDiscovery(conf SDConfig, logger log.Logger) (*Discovery, error) {
var err error
d.client, err = google.DefaultClient(context.Background(), compute.ComputeReadonlyScope)
if err != nil {
return nil, errors.Wrap(err, "error setting up communication with GCE service")
return nil, fmt.Errorf("error setting up communication with GCE service: %w", err)
}
d.svc, err = compute.NewService(context.Background(), option.WithHTTPClient(d.client))
if err != nil {
return nil, errors.Wrap(err, "error setting up communication with GCE service")
return nil, fmt.Errorf("error setting up communication with GCE service: %w", err)
}
d.isvc = compute.NewInstancesService(d.svc)
@ -221,7 +221,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
return nil
})
if err != nil {
return nil, errors.Wrap(err, "error retrieving refresh targets from gce")
return nil, fmt.Errorf("error retrieving refresh targets from gce: %w", err)
}
return []*targetgroup.Group{tg}, nil
}

View file

@ -15,11 +15,12 @@ package hetzner
import (
"context"
"errors"
"fmt"
"time"
"github.com/go-kit/log"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -95,7 +96,7 @@ func (c *role) UnmarshalYAML(unmarshal func(interface{}) error) error {
case hetznerRoleRobot, hetznerRoleHcloud:
return nil
default:
return errors.Errorf("unknown role %q", *c)
return fmt.Errorf("unknown role %q", *c)
}
}
@ -114,6 +115,11 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
return c.HTTPClientConfig.Validate()
}
// SetDirectory joins any relative file paths with dir.
func (c *SDConfig) SetDirectory(dir string) {
c.HTTPClientConfig.SetDirectory(dir)
}
// Discovery periodically performs Hetzner requests. It implements
// the Discoverer interface.
type Discovery struct {

View file

@ -25,7 +25,6 @@ import (
"time"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/common/version"
@ -89,7 +88,7 @@ func (d *robotDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, err
}()
if resp.StatusCode/100 != 2 {
return nil, errors.Errorf("non 2xx status '%d' response during hetzner service discovery with role robot", resp.StatusCode)
return nil, fmt.Errorf("non 2xx status '%d' response during hetzner service discovery with role robot", resp.StatusCode)
}
b, err := io.ReadAll(resp.Body)

View file

@ -16,6 +16,7 @@ package http
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
@ -26,7 +27,6 @@ import (
"github.com/go-kit/log"
"github.com/grafana/regexp"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -162,12 +162,12 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
if resp.StatusCode != http.StatusOK {
failuresCount.Inc()
return nil, errors.Errorf("server returned HTTP status %s", resp.Status)
return nil, fmt.Errorf("server returned HTTP status %s", resp.Status)
}
if !matchContentType.MatchString(strings.TrimSpace(resp.Header.Get("Content-Type"))) {
failuresCount.Inc()
return nil, errors.Errorf("unsupported content type %q", resp.Header.Get("Content-Type"))
return nil, fmt.Errorf("unsupported content type %q", resp.Header.Get("Content-Type"))
}
b, err := io.ReadAll(resp.Body)

View file

@ -26,6 +26,7 @@ import (
_ "github.com/prometheus/prometheus/discovery/gce" // register gce
_ "github.com/prometheus/prometheus/discovery/hetzner" // register hetzner
_ "github.com/prometheus/prometheus/discovery/http" // register http
_ "github.com/prometheus/prometheus/discovery/ionos" // register ionos
_ "github.com/prometheus/prometheus/discovery/kubernetes" // register kubernetes
_ "github.com/prometheus/prometheus/discovery/linode" // register linode
_ "github.com/prometheus/prometheus/discovery/marathon" // register marathon
@ -35,6 +36,7 @@ import (
_ "github.com/prometheus/prometheus/discovery/scaleway" // register scaleway
_ "github.com/prometheus/prometheus/discovery/triton" // register triton
_ "github.com/prometheus/prometheus/discovery/uyuni" // register uyuni
_ "github.com/prometheus/prometheus/discovery/vultr" // register vultr
_ "github.com/prometheus/prometheus/discovery/xds" // register xds
_ "github.com/prometheus/prometheus/discovery/zookeeper" // register zookeeper
)

111
discovery/ionos/ionos.go Normal file
View file

@ -0,0 +1,111 @@
// Copyright 2022 The Prometheus Authors
// 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 ionos
import (
"time"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/discovery"
"github.com/prometheus/prometheus/discovery/refresh"
)
const (
// metaLabelPrefix is the meta prefix used for all meta labels in this
// discovery.
metaLabelPrefix = model.MetaLabelPrefix + "ionos_"
metaLabelSeparator = ","
)
func init() {
discovery.RegisterConfig(&SDConfig{})
}
// Discovery periodically performs IONOS Cloud target discovery. It implements
// the Discoverer interface.
type Discovery struct{}
// NewDiscovery returns a new refresh.Discovery for IONOS Cloud.
func NewDiscovery(conf *SDConfig, logger log.Logger) (*refresh.Discovery, error) {
if conf.ionosEndpoint == "" {
conf.ionosEndpoint = "https://api.ionos.com"
}
d, err := newServerDiscovery(conf, logger)
if err != nil {
return nil, err
}
return refresh.NewDiscovery(
logger,
"ionos",
time.Duration(conf.RefreshInterval),
d.refresh,
), nil
}
// DefaultSDConfig is the default IONOS Cloud service discovery configuration.
var DefaultSDConfig = SDConfig{
HTTPClientConfig: config.DefaultHTTPClientConfig,
RefreshInterval: model.Duration(60 * time.Second),
Port: 80,
}
// SDConfig configuration to use for IONOS Cloud Discovery.
type SDConfig struct {
// DatacenterID: IONOS Cloud data center ID used to discover targets.
DatacenterID string `yaml:"datacenter_id"`
HTTPClientConfig config.HTTPClientConfig `yaml:",inline"`
RefreshInterval model.Duration `yaml:"refresh_interval"`
Port int `yaml:"port"`
ionosEndpoint string // For tests only.
}
// Name returns the name of the IONOS Cloud service discovery.
func (c SDConfig) Name() string {
return "ionos"
}
// NewDiscoverer returns a new discovery.Discoverer for IONOS Cloud.
func (c SDConfig) NewDiscoverer(options discovery.DiscovererOptions) (discovery.Discoverer, error) {
return NewDiscovery(&c, options.Logger)
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultSDConfig
type plain SDConfig
err := unmarshal((*plain)(c))
if err != nil {
return err
}
if c.DatacenterID == "" {
return errors.New("datacenter id can't be empty")
}
return c.HTTPClientConfig.Validate()
}
// SetDirectory joins any relative file paths with dir.
func (c *SDConfig) SetDirectory(dir string) {
c.HTTPClientConfig.SetDirectory(dir)
}

166
discovery/ionos/server.go Normal file
View file

@ -0,0 +1,166 @@
// Copyright 2022 The Prometheus Authors
// 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 ionos
import (
"context"
"fmt"
"net"
"net/http"
"strconv"
"strings"
"time"
"github.com/go-kit/log"
ionoscloud "github.com/ionos-cloud/sdk-go/v6"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/common/version"
"github.com/prometheus/prometheus/discovery/refresh"
"github.com/prometheus/prometheus/discovery/targetgroup"
"github.com/prometheus/prometheus/util/strutil"
)
const (
serverLabelPrefix = metaLabelPrefix + "server_"
serverAvailabilityZoneLabel = serverLabelPrefix + "availability_zone"
serverBootCDROMIDLabel = serverLabelPrefix + "boot_cdrom_id"
serverBootImageIDLabel = serverLabelPrefix + "boot_image_id"
serverBootVolumeIDLabel = serverLabelPrefix + "boot_volume_id"
serverCPUFamilyLabel = serverLabelPrefix + "cpu_family"
serverIDLabel = serverLabelPrefix + "id"
serverIPLabel = serverLabelPrefix + "ip"
serverLifecycleLabel = serverLabelPrefix + "lifecycle"
serverNameLabel = serverLabelPrefix + "name"
serverNICIPLabelPrefix = serverLabelPrefix + "nic_ip_"
serverServersIDLabel = serverLabelPrefix + "servers_id"
serverStateLabel = serverLabelPrefix + "state"
serverTypeLabel = serverLabelPrefix + "type"
nicDefaultName = "unnamed"
)
type serverDiscovery struct {
*refresh.Discovery
client *ionoscloud.APIClient
port int
datacenterID string
}
func newServerDiscovery(conf *SDConfig, logger log.Logger) (*serverDiscovery, error) {
d := &serverDiscovery{
port: conf.Port,
datacenterID: conf.DatacenterID,
}
rt, err := config.NewRoundTripperFromConfig(conf.HTTPClientConfig, "ionos_sd")
if err != nil {
return nil, err
}
// Username, password and token are set via http client config.
cfg := ionoscloud.NewConfiguration("", "", "", conf.ionosEndpoint)
cfg.HTTPClient = &http.Client{
Transport: rt,
Timeout: time.Duration(conf.RefreshInterval),
}
cfg.UserAgent = fmt.Sprintf("Prometheus/%s", version.Version)
d.client = ionoscloud.NewAPIClient(cfg)
return d, nil
}
func (d *serverDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
api := d.client.ServersApi
servers, _, err := api.DatacentersServersGet(ctx, d.datacenterID).
Depth(3).
Execute()
if err != nil {
return nil, err
}
var targets []model.LabelSet
for _, server := range *servers.Items {
var ips []string
ipsByNICName := make(map[string][]string)
if server.Entities != nil && server.Entities.Nics != nil {
for _, nic := range *server.Entities.Nics.Items {
nicName := nicDefaultName
if name := nic.Properties.Name; name != nil {
nicName = *name
}
nicIPs := *nic.Properties.Ips
ips = append(nicIPs, ips...)
ipsByNICName[nicName] = append(nicIPs, ipsByNICName[nicName]...)
}
}
// If a server has no IP addresses, it's being dropped from the targets.
if len(ips) == 0 {
continue
}
addr := net.JoinHostPort(ips[0], strconv.FormatUint(uint64(d.port), 10))
labels := model.LabelSet{
model.AddressLabel: model.LabelValue(addr),
serverAvailabilityZoneLabel: model.LabelValue(*server.Properties.AvailabilityZone),
serverCPUFamilyLabel: model.LabelValue(*server.Properties.CpuFamily),
serverServersIDLabel: model.LabelValue(*servers.Id),
serverIDLabel: model.LabelValue(*server.Id),
serverIPLabel: model.LabelValue(join(ips, metaLabelSeparator)),
serverLifecycleLabel: model.LabelValue(*server.Metadata.State),
serverNameLabel: model.LabelValue(*server.Properties.Name),
serverStateLabel: model.LabelValue(*server.Properties.VmState),
serverTypeLabel: model.LabelValue(*server.Properties.Type),
}
for nicName, nicIPs := range ipsByNICName {
name := serverNICIPLabelPrefix + strutil.SanitizeLabelName(nicName)
labels[model.LabelName(name)] = model.LabelValue(join(nicIPs, metaLabelSeparator))
}
if server.Properties.BootCdrom != nil {
labels[serverBootCDROMIDLabel] = model.LabelValue(*server.Properties.BootCdrom.Id)
}
if server.Properties.BootVolume != nil {
labels[serverBootVolumeIDLabel] = model.LabelValue(*server.Properties.BootVolume.Id)
}
if server.Entities != nil && server.Entities.Volumes != nil {
volumes := *server.Entities.Volumes.Items
if len(volumes) > 0 {
image := volumes[0].Properties.Image
if image != nil {
labels[serverBootImageIDLabel] = model.LabelValue(*image)
}
}
}
targets = append(targets, labels)
}
return []*targetgroup.Group{{Source: "ionos", Targets: targets}}, nil
}
// join returns strings.Join with additional separators at beginning and end.
func join(elems []string, sep string) string {
return sep + strings.Join(elems, sep) + sep
}

View file

@ -0,0 +1,115 @@
// Copyright 2022 The Prometheus Authors
// 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 ionos
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
ionoscloud "github.com/ionos-cloud/sdk-go/v6"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
)
var (
ionosTestBearerToken = config.Secret("jwt")
ionosTestDatacenterID = "8feda53f-15f0-447f-badf-ebe32dad2fc0"
)
func TestIONOSServerRefresh(t *testing.T) {
mock := httptest.NewServer(http.HandlerFunc(mockIONOSServers))
defer mock.Close()
cfg := DefaultSDConfig
cfg.DatacenterID = ionosTestDatacenterID
cfg.HTTPClientConfig.BearerToken = ionosTestBearerToken
cfg.ionosEndpoint = mock.URL
d, err := newServerDiscovery(&cfg, nil)
require.NoError(t, err)
ctx := context.Background()
tgs, err := d.refresh(ctx)
require.NoError(t, err)
require.Equal(t, 1, len(tgs))
tg := tgs[0]
require.NotNil(t, tg)
require.NotNil(t, tg.Targets)
require.Equal(t, 2, len(tg.Targets))
for i, lbls := range []model.LabelSet{
{
"__address__": "85.215.243.177:80",
"__meta_ionos_server_availability_zone": "ZONE_2",
"__meta_ionos_server_boot_cdrom_id": "0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"__meta_ionos_server_cpu_family": "INTEL_SKYLAKE",
"__meta_ionos_server_id": "b501942c-4e08-43e6-8ec1-00e59c64e0e4",
"__meta_ionos_server_ip": ",85.215.243.177,185.56.150.9,85.215.238.118,",
"__meta_ionos_server_nic_ip_metrics": ",85.215.243.177,",
"__meta_ionos_server_nic_ip_unnamed": ",185.56.150.9,85.215.238.118,",
"__meta_ionos_server_lifecycle": "AVAILABLE",
"__meta_ionos_server_name": "prometheus-2",
"__meta_ionos_server_servers_id": "8feda53f-15f0-447f-badf-ebe32dad2fc0/servers",
"__meta_ionos_server_state": "RUNNING",
"__meta_ionos_server_type": "ENTERPRISE",
},
{
"__address__": "85.215.248.84:80",
"__meta_ionos_server_availability_zone": "ZONE_1",
"__meta_ionos_server_boot_cdrom_id": "0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"__meta_ionos_server_cpu_family": "INTEL_SKYLAKE",
"__meta_ionos_server_id": "523415e6-ff8c-4dc0-86d3-09c256039b30",
"__meta_ionos_server_ip": ",85.215.248.84,",
"__meta_ionos_server_nic_ip_unnamed": ",85.215.248.84,",
"__meta_ionos_server_lifecycle": "AVAILABLE",
"__meta_ionos_server_name": "prometheus-1",
"__meta_ionos_server_servers_id": "8feda53f-15f0-447f-badf-ebe32dad2fc0/servers",
"__meta_ionos_server_state": "RUNNING",
"__meta_ionos_server_type": "ENTERPRISE",
},
} {
t.Run(fmt.Sprintf("item %d", i), func(t *testing.T) {
require.Equal(t, lbls, tg.Targets[i])
})
}
}
func mockIONOSServers(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", ionosTestBearerToken) {
http.Error(w, "bad token", http.StatusUnauthorized)
return
}
if r.URL.Path != fmt.Sprintf("%s/datacenters/%s/servers", ionoscloud.DefaultIonosBasePath, ionosTestDatacenterID) {
http.Error(w, fmt.Sprintf("bad url: %s", r.URL.Path), http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
server, err := os.ReadFile("testdata/servers.json")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
_, err = w.Write(server)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}

615
discovery/ionos/testdata/servers.json vendored Normal file
View file

@ -0,0 +1,615 @@
{
"id": "8feda53f-15f0-447f-badf-ebe32dad2fc0/servers",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers",
"items": [
{
"id": "d6bf44ee-f7e8-4e19-8716-96fdd18cc697",
"type": "server",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/d6bf44ee-f7e8-4e19-8716-96fdd18cc697",
"metadata": {
"etag": "3bd2cf1f190fdba8502ba8c0cc79433e",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "prometheus-3",
"cores": 2,
"ram": 4096,
"availabilityZone": "AUTO",
"vmState": "RUNNING",
"bootCdrom": {
"id": "0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"type": "image",
"href": "https://api.ionos.com/cloudapi/v6/images/0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"metadata": {
"etag": "ca643281fd2a5b68002bc00ac0ecd920",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "debian-10.0.0-amd64-netinst.iso",
"description": null,
"location": "de/txl",
"size": 0.33,
"cpuHotPlug": true,
"cpuHotUnplug": false,
"ramHotPlug": true,
"ramHotUnplug": false,
"nicHotPlug": true,
"nicHotUnplug": true,
"discVirtioHotPlug": true,
"discVirtioHotUnplug": true,
"discScsiHotPlug": false,
"discScsiHotUnplug": false,
"licenceType": "LINUX",
"imageType": "CDROM",
"imageAliases": [
"debian:latest_iso",
"debian:10_iso"
],
"cloudInit": "NONE",
"public": true
}
},
"bootVolume": null,
"cpuFamily": "INTEL_SKYLAKE",
"type": "ENTERPRISE"
},
"entities": {
"cdroms": {
"id": "d6bf44ee-f7e8-4e19-8716-96fdd18cc697/cdroms",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/d6bf44ee-f7e8-4e19-8716-96fdd18cc697/cdroms",
"items": [
{
"id": "0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"type": "image",
"href": "https://api.ionos.com/cloudapi/v6/images/0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"metadata": {
"etag": "ca643281fd2a5b68002bc00ac0ecd920",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "debian-10.0.0-amd64-netinst.iso",
"description": null,
"location": "de/txl",
"size": 0.33,
"cpuHotPlug": true,
"cpuHotUnplug": false,
"ramHotPlug": true,
"ramHotUnplug": false,
"nicHotPlug": true,
"nicHotUnplug": true,
"discVirtioHotPlug": true,
"discVirtioHotUnplug": true,
"discScsiHotPlug": false,
"discScsiHotUnplug": false,
"licenceType": "LINUX",
"imageType": "CDROM",
"imageAliases": [
"debian:10_iso",
"debian:latest_iso"
],
"cloudInit": "NONE",
"public": true
}
}
]
},
"volumes": {
"id": "d6bf44ee-f7e8-4e19-8716-96fdd18cc697/volumes",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/d6bf44ee-f7e8-4e19-8716-96fdd18cc697/volumes",
"items": []
},
"nics": {
"id": "d6bf44ee-f7e8-4e19-8716-96fdd18cc697/nics",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/d6bf44ee-f7e8-4e19-8716-96fdd18cc697/nics",
"items": []
}
}
},
{
"id": "b501942c-4e08-43e6-8ec1-00e59c64e0e4",
"type": "server",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4",
"metadata": {
"etag": "fe405a5f2e041d506086ee0c2159b678",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "prometheus-2",
"cores": 1,
"ram": 1024,
"availabilityZone": "ZONE_2",
"vmState": "RUNNING",
"bootCdrom": {
"id": "0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"type": "image",
"href": "https://api.ionos.com/cloudapi/v6/images/0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"metadata": {
"etag": "ca643281fd2a5b68002bc00ac0ecd920",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "debian-10.0.0-amd64-netinst.iso",
"description": null,
"location": "de/txl",
"size": 0.33,
"cpuHotPlug": true,
"cpuHotUnplug": false,
"ramHotPlug": true,
"ramHotUnplug": false,
"nicHotPlug": true,
"nicHotUnplug": true,
"discVirtioHotPlug": true,
"discVirtioHotUnplug": true,
"discScsiHotPlug": false,
"discScsiHotUnplug": false,
"licenceType": "LINUX",
"imageType": "CDROM",
"imageAliases": [
"debian:latest_iso",
"debian:10_iso"
],
"cloudInit": "NONE",
"public": true
}
},
"bootVolume": null,
"cpuFamily": "INTEL_SKYLAKE",
"type": "ENTERPRISE"
},
"entities": {
"cdroms": {
"id": "b501942c-4e08-43e6-8ec1-00e59c64e0e4/cdroms",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/cdroms",
"items": [
{
"id": "0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"type": "image",
"href": "https://api.ionos.com/cloudapi/v6/images/0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"metadata": {
"etag": "ca643281fd2a5b68002bc00ac0ecd920",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "debian-10.0.0-amd64-netinst.iso",
"description": null,
"location": "de/txl",
"size": 0.33,
"cpuHotPlug": true,
"cpuHotUnplug": false,
"ramHotPlug": true,
"ramHotUnplug": false,
"nicHotPlug": true,
"nicHotUnplug": true,
"discVirtioHotPlug": true,
"discVirtioHotUnplug": true,
"discScsiHotPlug": false,
"discScsiHotUnplug": false,
"licenceType": "LINUX",
"imageType": "CDROM",
"imageAliases": [
"debian:10_iso",
"debian:latest_iso"
],
"cloudInit": "NONE",
"public": true
}
}
]
},
"volumes": {
"id": "b501942c-4e08-43e6-8ec1-00e59c64e0e4/volumes",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/volumes",
"items": [
{
"id": "97a58f00-e2a5-4ebb-8b9a-f694837b0548",
"type": "volume",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/volumes/97a58f00-e2a5-4ebb-8b9a-f694837b0548",
"metadata": {
"etag": "27491713a77c5453c6ae7d0f98a1beec",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "prometheus-data-1",
"type": "HDD",
"size": 10,
"availabilityZone": "AUTO",
"image": null,
"imagePassword": null,
"sshKeys": null,
"bus": "VIRTIO",
"licenceType": "UNKNOWN",
"cpuHotPlug": false,
"ramHotPlug": false,
"nicHotPlug": false,
"nicHotUnplug": false,
"discVirtioHotPlug": false,
"discVirtioHotUnplug": false,
"deviceNumber": 1,
"userData": null,
"pciSlot": 6,
"bootServer": "b501942c-4e08-43e6-8ec1-00e59c64e0e4"
}
}
]
},
"nics": {
"id": "b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics",
"items": [
{
"id": "206e9a88-097c-4c87-8544-1f5fe0b6b0f1",
"type": "nic",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics/206e9a88-097c-4c87-8544-1f5fe0b6b0f1",
"metadata": {
"etag": "be2b6fff29e7e09c1a4a5ca150876eaa",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": null,
"mac": "02:01:4b:1c:cf:57",
"ips": [
"85.215.238.118"
],
"dhcp": true,
"lan": 1,
"firewallActive": false,
"firewallType": "INGRESS",
"deviceNumber": 1,
"pciSlot": 5
},
"entities": {
"firewallrules": {
"id": "206e9a88-097c-4c87-8544-1f5fe0b6b0f1/firewallrules",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics/206e9a88-097c-4c87-8544-1f5fe0b6b0f1/firewallrules"
},
"flowlogs": {
"id": "206e9a88-097c-4c87-8544-1f5fe0b6b0f1/flowlogs",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics/206e9a88-097c-4c87-8544-1f5fe0b6b0f1/flowlogs"
}
}
},
{
"id": "8a568a32-da62-437d-8b73-f580d1a1588f",
"type": "nic",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics/8a568a32-da62-437d-8b73-f580d1a1588f",
"metadata": {
"etag": "2dca0340a0610b1dfdb2e66f6aae3ed3",
"createdDate": "2022-05-05T13:12:31Z",
"createdBy": "felix.ehrenpfort@codecentric.cloud",
"createdByUserId": "cfcb4526-fbe0-461e-9149-ffb00a83fbec",
"lastModifiedDate": "2022-05-05T13:12:31Z",
"lastModifiedBy": "felix.ehrenpfort@codecentric.cloud",
"lastModifiedByUserId": "cfcb4526-fbe0-461e-9149-ffb00a83fbec",
"state": "AVAILABLE"
},
"properties": {
"name": null,
"mac": "02:01:56:f6:47:a8",
"ips": [
"185.56.150.9"
],
"dhcp": true,
"lan": 1,
"firewallActive": false,
"firewallType": "INGRESS",
"deviceNumber": 3,
"pciSlot": 8
},
"entities": {
"firewallrules": {
"id": "8a568a32-da62-437d-8b73-f580d1a1588f/firewallrules",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics/8a568a32-da62-437d-8b73-f580d1a1588f/firewallrules"
},
"flowlogs": {
"id": "8a568a32-da62-437d-8b73-f580d1a1588f/flowlogs",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics/8a568a32-da62-437d-8b73-f580d1a1588f/flowlogs"
}
}
},
{
"id": "17ecc866-4a08-4ada-858f-7b726a5f5070",
"type": "nic",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics/17ecc866-4a08-4ada-858f-7b726a5f5070",
"metadata": {
"etag": "f6a82276c6fdb460811d3789e54e0054",
"createdDate": "2022-05-05T11:45:36Z",
"createdBy": "felix.ehrenpfort@codecentric.cloud",
"createdByUserId": "cfcb4526-fbe0-461e-9149-ffb00a83fbec",
"lastModifiedDate": "2022-05-05T11:45:36Z",
"lastModifiedBy": "felix.ehrenpfort@codecentric.cloud",
"lastModifiedByUserId": "cfcb4526-fbe0-461e-9149-ffb00a83fbec",
"state": "AVAILABLE"
},
"properties": {
"name": "metrics",
"mac": "02:01:93:80:fa:a4",
"ips": [
"85.215.243.177"
],
"dhcp": true,
"lan": 1,
"firewallActive": false,
"firewallType": "INGRESS",
"deviceNumber": 2,
"pciSlot": 7
},
"entities": {
"firewallrules": {
"id": "17ecc866-4a08-4ada-858f-7b726a5f5070/firewallrules",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics/17ecc866-4a08-4ada-858f-7b726a5f5070/firewallrules"
},
"flowlogs": {
"id": "17ecc866-4a08-4ada-858f-7b726a5f5070/flowlogs",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/b501942c-4e08-43e6-8ec1-00e59c64e0e4/nics/17ecc866-4a08-4ada-858f-7b726a5f5070/flowlogs"
}
}
}
]
}
}
},
{
"id": "523415e6-ff8c-4dc0-86d3-09c256039b30",
"type": "server",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/523415e6-ff8c-4dc0-86d3-09c256039b30",
"metadata": {
"etag": "fe405a5f2e041d506086ee0c2159b678",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "prometheus-1",
"cores": 1,
"ram": 1024,
"availabilityZone": "ZONE_1",
"vmState": "RUNNING",
"bootCdrom": {
"id": "0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"type": "image",
"href": "https://api.ionos.com/cloudapi/v6/images/0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"metadata": {
"etag": "ca643281fd2a5b68002bc00ac0ecd920",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "debian-10.0.0-amd64-netinst.iso",
"description": null,
"location": "de/txl",
"size": 0.33,
"cpuHotPlug": true,
"cpuHotUnplug": false,
"ramHotPlug": true,
"ramHotUnplug": false,
"nicHotPlug": true,
"nicHotUnplug": true,
"discVirtioHotPlug": true,
"discVirtioHotUnplug": true,
"discScsiHotPlug": false,
"discScsiHotUnplug": false,
"licenceType": "LINUX",
"imageType": "CDROM",
"imageAliases": [
"debian:latest_iso",
"debian:10_iso"
],
"cloudInit": "NONE",
"public": true
}
},
"bootVolume": null,
"cpuFamily": "INTEL_SKYLAKE",
"type": "ENTERPRISE"
},
"entities": {
"cdroms": {
"id": "523415e6-ff8c-4dc0-86d3-09c256039b30/cdroms",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/523415e6-ff8c-4dc0-86d3-09c256039b30/cdroms",
"items": [
{
"id": "0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"type": "image",
"href": "https://api.ionos.com/cloudapi/v6/images/0e4d57f9-cd78-11e9-b88c-525400f64d8d",
"metadata": {
"etag": "ca643281fd2a5b68002bc00ac0ecd920",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "debian-10.0.0-amd64-netinst.iso",
"description": null,
"location": "de/txl",
"size": 0.33,
"cpuHotPlug": true,
"cpuHotUnplug": false,
"ramHotPlug": true,
"ramHotUnplug": false,
"nicHotPlug": true,
"nicHotUnplug": true,
"discVirtioHotPlug": true,
"discVirtioHotUnplug": true,
"discScsiHotPlug": false,
"discScsiHotUnplug": false,
"licenceType": "LINUX",
"imageType": "CDROM",
"imageAliases": [
"debian:10_iso",
"debian:latest_iso"
],
"cloudInit": "NONE",
"public": true
}
}
]
},
"volumes": {
"id": "523415e6-ff8c-4dc0-86d3-09c256039b30/volumes",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/523415e6-ff8c-4dc0-86d3-09c256039b30/volumes",
"items": [
{
"id": "5dc21326-7e33-4dc7-84ac-63b02aad4624",
"type": "volume",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/volumes/5dc21326-7e33-4dc7-84ac-63b02aad4624",
"metadata": {
"etag": "27491713a77c5453c6ae7d0f98a1beec",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": "prometheus-data-2",
"type": "HDD",
"size": 10,
"availabilityZone": "AUTO",
"image": null,
"imagePassword": null,
"sshKeys": null,
"bus": "VIRTIO",
"licenceType": "UNKNOWN",
"cpuHotPlug": false,
"ramHotPlug": false,
"nicHotPlug": false,
"nicHotUnplug": false,
"discVirtioHotPlug": false,
"discVirtioHotUnplug": false,
"deviceNumber": 1,
"userData": null,
"pciSlot": 6,
"bootServer": "523415e6-ff8c-4dc0-86d3-09c256039b30"
}
}
]
},
"nics": {
"id": "523415e6-ff8c-4dc0-86d3-09c256039b30/nics",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/523415e6-ff8c-4dc0-86d3-09c256039b30/nics",
"items": [
{
"id": "684033a2-317f-4977-8a01-68263d59336c",
"type": "nic",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/523415e6-ff8c-4dc0-86d3-09c256039b30/nics/684033a2-317f-4977-8a01-68263d59336c",
"metadata": {
"etag": "fe405a5f2e041d506086ee0c2159b678",
"createdDate": "2019-09-02T11:51:53Z",
"createdBy": "System",
"createdByUserId": "[UNKNOWN]",
"lastModifiedDate": "2019-09-02T11:51:53Z",
"lastModifiedBy": "System",
"lastModifiedByUserId": "[UNKNOWN]",
"state": "AVAILABLE"
},
"properties": {
"name": null,
"mac": "02:01:17:12:27:1b",
"ips": [
"85.215.248.84"
],
"dhcp": true,
"lan": 1,
"firewallActive": false,
"firewallType": "INGRESS",
"deviceNumber": 1,
"pciSlot": 5
},
"entities": {
"firewallrules": {
"id": "684033a2-317f-4977-8a01-68263d59336c/firewallrules",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/523415e6-ff8c-4dc0-86d3-09c256039b30/nics/684033a2-317f-4977-8a01-68263d59336c/firewallrules"
},
"flowlogs": {
"id": "684033a2-317f-4977-8a01-68263d59336c/flowlogs",
"type": "collection",
"href": "https://api.ionos.com/cloudapi/v6/datacenters/8feda53f-15f0-447f-badf-ebe32dad2fc0/servers/523415e6-ff8c-4dc0-86d3-09c256039b30/nics/684033a2-317f-4977-8a01-68263d59336c/flowlogs"
}
}
}
]
}
}
}
]
}

View file

@ -15,12 +15,13 @@ package kubernetes
import (
"context"
"errors"
"fmt"
"net"
"strconv"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
@ -135,7 +136,7 @@ func (e *Endpoints) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
defer e.queue.ShutDown()
if !cache.WaitForCacheSync(ctx.Done(), e.endpointsInf.HasSynced, e.serviceInf.HasSynced, e.podInf.HasSynced) {
if ctx.Err() != context.Canceled {
if !errors.Is(ctx.Err(), context.Canceled) {
level.Error(e.logger).Log("msg", "endpoints informer unable to sync cache")
}
return
@ -188,7 +189,7 @@ func convertToEndpoints(o interface{}) (*apiv1.Endpoints, error) {
return endpoints, nil
}
return nil, errors.Errorf("received unexpected object: %v", o)
return nil, fmt.Errorf("received unexpected object: %v", o)
}
func endpointsSource(ep *apiv1.Endpoints) string {

View file

@ -15,12 +15,12 @@ package kubernetes
import (
"context"
"fmt"
"net"
"strconv"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
apiv1 "k8s.io/api/core/v1"
v1 "k8s.io/api/discovery/v1"
@ -193,7 +193,7 @@ func (e *EndpointSlice) getEndpointSliceAdaptor(o interface{}) (endpointSliceAda
case *v1beta1.EndpointSlice:
return newEndpointSliceAdaptorFromV1beta1(endpointSlice), nil
default:
return nil, errors.Errorf("received unexpected object: %v", o)
return nil, fmt.Errorf("received unexpected object: %v", o)
}
}

View file

@ -644,10 +644,16 @@ func TestEndpointSliceDiscoveryWithServiceUpdate(t *testing.T) {
Targets: []model.LabelSet{
{
"__address__": "1.2.3.4:9000",
"__meta_kubernetes_endpointslice_endpoint_hostname": "testendpoint1",
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
"__meta_kubernetes_endpointslice_address_target_kind": "",
"__meta_kubernetes_endpointslice_address_target_name": "",
"__meta_kubernetes_endpointslice_endpoint_conditions_ready": "true",
"__meta_kubernetes_endpointslice_endpoint_hostname": "testendpoint1",
"__meta_kubernetes_endpointslice_endpoint_topology_present_topology": "true",
"__meta_kubernetes_endpointslice_endpoint_topology_topology": "value",
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_app_protocol": "http",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
},
{
"__address__": "2.3.4.5:9000",
@ -655,6 +661,7 @@ func TestEndpointSliceDiscoveryWithServiceUpdate(t *testing.T) {
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
"__meta_kubernetes_endpointslice_port_app_protocol": "http",
},
{
"__address__": "3.4.5.6:9000",
@ -662,6 +669,7 @@ func TestEndpointSliceDiscoveryWithServiceUpdate(t *testing.T) {
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
"__meta_kubernetes_endpointslice_port_app_protocol": "http",
},
},
Labels: model.LabelSet{
@ -755,10 +763,16 @@ func TestEndpointSliceDiscoveryNamespaces(t *testing.T) {
Targets: []model.LabelSet{
{
"__address__": "1.2.3.4:9000",
"__meta_kubernetes_endpointslice_endpoint_hostname": "testendpoint1",
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
"__meta_kubernetes_endpointslice_address_target_kind": "",
"__meta_kubernetes_endpointslice_address_target_name": "",
"__meta_kubernetes_endpointslice_endpoint_conditions_ready": "true",
"__meta_kubernetes_endpointslice_endpoint_hostname": "testendpoint1",
"__meta_kubernetes_endpointslice_endpoint_topology_present_topology": "true",
"__meta_kubernetes_endpointslice_endpoint_topology_topology": "value",
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_app_protocol": "http",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
},
{
"__address__": "2.3.4.5:9000",
@ -766,6 +780,7 @@ func TestEndpointSliceDiscoveryNamespaces(t *testing.T) {
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
"__meta_kubernetes_endpointslice_port_app_protocol": "http",
},
{
"__address__": "3.4.5.6:9000",
@ -773,6 +788,7 @@ func TestEndpointSliceDiscoveryNamespaces(t *testing.T) {
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
"__meta_kubernetes_endpointslice_port_app_protocol": "http",
},
},
Labels: model.LabelSet{
@ -871,10 +887,16 @@ func TestEndpointSliceDiscoveryOwnNamespace(t *testing.T) {
Targets: []model.LabelSet{
{
"__address__": "1.2.3.4:9000",
"__meta_kubernetes_endpointslice_endpoint_hostname": "testendpoint1",
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
"__meta_kubernetes_endpointslice_address_target_kind": "",
"__meta_kubernetes_endpointslice_address_target_name": "",
"__meta_kubernetes_endpointslice_endpoint_conditions_ready": "true",
"__meta_kubernetes_endpointslice_endpoint_hostname": "testendpoint1",
"__meta_kubernetes_endpointslice_endpoint_topology_present_topology": "true",
"__meta_kubernetes_endpointslice_endpoint_topology_topology": "value",
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_app_protocol": "http",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
},
{
"__address__": "2.3.4.5:9000",
@ -882,6 +904,7 @@ func TestEndpointSliceDiscoveryOwnNamespace(t *testing.T) {
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
"__meta_kubernetes_endpointslice_port_app_protocol": "http",
},
{
"__address__": "3.4.5.6:9000",
@ -889,6 +912,7 @@ func TestEndpointSliceDiscoveryOwnNamespace(t *testing.T) {
"__meta_kubernetes_endpointslice_port": "9000",
"__meta_kubernetes_endpointslice_port_name": "testport",
"__meta_kubernetes_endpointslice_port_protocol": "TCP",
"__meta_kubernetes_endpointslice_port_app_protocol": "http",
},
},
Labels: model.LabelSet{

View file

@ -15,11 +15,12 @@ package kubernetes
import (
"context"
"errors"
"fmt"
"strings"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
v1 "k8s.io/api/networking/v1"
"k8s.io/api/networking/v1beta1"
@ -78,7 +79,7 @@ func (i *Ingress) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
defer i.queue.ShutDown()
if !cache.WaitForCacheSync(ctx.Done(), i.informer.HasSynced) {
if ctx.Err() != context.Canceled {
if !errors.Is(ctx.Err(), context.Canceled) {
level.Error(i.logger).Log("msg", "ingress informer unable to sync cache")
}
return
@ -123,7 +124,7 @@ func (i *Ingress) process(ctx context.Context, ch chan<- []*targetgroup.Group) b
ia = newIngressAdaptorFromV1beta1(ingress)
default:
level.Error(i.logger).Log("msg", "converting to Ingress object failed", "err",
errors.Errorf("received unexpected object: %v", o))
fmt.Errorf("received unexpected object: %v", o))
return true
}
send(ctx, ch, i.buildIngress(ia))

View file

@ -15,6 +15,7 @@ package kubernetes
import (
"context"
"errors"
"fmt"
"os"
"reflect"
@ -24,7 +25,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -114,7 +114,7 @@ func (c *Role) UnmarshalYAML(unmarshal func(interface{}) error) error {
case RoleNode, RolePod, RoleService, RoleEndpoint, RoleEndpointSlice, RoleIngress:
return nil
default:
return errors.Errorf("unknown Kubernetes SD role %q", *c)
return fmt.Errorf("unknown Kubernetes SD role %q", *c)
}
}
@ -178,7 +178,7 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
return err
}
if c.Role == "" {
return errors.Errorf("role missing (one of: pod, service, endpoints, endpointslice, node, ingress)")
return fmt.Errorf("role missing (one of: pod, service, endpoints, endpointslice, node, ingress)")
}
err = c.HTTPClientConfig.Validate()
if err != nil {
@ -186,20 +186,20 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
if c.APIServer.URL != nil && c.KubeConfig != "" {
// Api-server and kubeconfig_file are mutually exclusive
return errors.Errorf("cannot use 'kubeconfig_file' and 'api_server' simultaneously")
return fmt.Errorf("cannot use 'kubeconfig_file' and 'api_server' simultaneously")
}
if c.KubeConfig != "" && !reflect.DeepEqual(c.HTTPClientConfig, config.DefaultHTTPClientConfig) {
// Kubeconfig_file and custom http config are mutually exclusive
return errors.Errorf("cannot use a custom HTTP client configuration together with 'kubeconfig_file'")
return fmt.Errorf("cannot use a custom HTTP client configuration together with 'kubeconfig_file'")
}
if c.APIServer.URL == nil && !reflect.DeepEqual(c.HTTPClientConfig, config.DefaultHTTPClientConfig) {
return errors.Errorf("to use custom HTTP client configuration please provide the 'api_server' URL explicitly")
return fmt.Errorf("to use custom HTTP client configuration please provide the 'api_server' URL explicitly")
}
if c.APIServer.URL != nil && c.NamespaceDiscovery.IncludeOwnNamespace {
return errors.Errorf("cannot use 'api_server' and 'namespaces.own_namespace' simultaneously")
return fmt.Errorf("cannot use 'api_server' and 'namespaces.own_namespace' simultaneously")
}
if c.KubeConfig != "" && c.NamespaceDiscovery.IncludeOwnNamespace {
return errors.Errorf("cannot use 'kubeconfig_file' and 'namespaces.own_namespace' simultaneously")
return fmt.Errorf("cannot use 'kubeconfig_file' and 'namespaces.own_namespace' simultaneously")
}
foundSelectorRoles := make(map[Role]struct{})
@ -214,12 +214,12 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
for _, selector := range c.Selectors {
if _, ok := foundSelectorRoles[selector.Role]; ok {
return errors.Errorf("duplicated selector role: %s", selector.Role)
return fmt.Errorf("duplicated selector role: %s", selector.Role)
}
foundSelectorRoles[selector.Role] = struct{}{}
if _, ok := allowedSelectors[c.Role]; !ok {
return errors.Errorf("invalid role: %q, expecting one of: pod, service, endpoints, endpointslice, node or ingress", c.Role)
return fmt.Errorf("invalid role: %q, expecting one of: pod, service, endpoints, endpointslice, node or ingress", c.Role)
}
var allowed bool
for _, role := range allowedSelectors[c.Role] {
@ -230,7 +230,7 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
if !allowed {
return errors.Errorf("%s role supports only %s selectors", c.Role, strings.Join(allowedSelectors[c.Role], ", "))
return fmt.Errorf("%s role supports only %s selectors", c.Role, strings.Join(allowedSelectors[c.Role], ", "))
}
_, err := fields.ParseSelector(selector.Field)

View file

@ -16,11 +16,11 @@ package kubernetes
import (
"context"
"encoding/json"
"errors"
"testing"
"time"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/version"
@ -163,11 +163,11 @@ Loop:
func requireTargetGroups(t *testing.T, expected, res map[string]*targetgroup.Group) {
t.Helper()
b1, err := json.Marshal(expected)
b1, err := marshalTargetGroups(expected)
if err != nil {
panic(err)
}
b2, err := json.Marshal(res)
b2, err := marshalTargetGroups(res)
if err != nil {
panic(err)
}
@ -175,6 +175,22 @@ func requireTargetGroups(t *testing.T, expected, res map[string]*targetgroup.Gro
require.Equal(t, string(b1), string(b2))
}
// marshalTargetGroups serializes a set of target groups to JSON, ignoring the
// custom MarshalJSON function defined on the targetgroup.Group struct.
// marshalTargetGroups can be used for making exact comparisons between target groups
// as it will serialize all target labels.
func marshalTargetGroups(tgs map[string]*targetgroup.Group) ([]byte, error) {
type targetGroupAlias targetgroup.Group
aliases := make(map[string]*targetGroupAlias, len(tgs))
for k, v := range tgs {
tg := targetGroupAlias(*v)
aliases[k] = &tg
}
return json.Marshal(aliases)
}
type hasSynced interface {
// hasSynced returns true if all informers synced.
// This is only used in testing to determine when discoverer synced to

View file

@ -15,12 +15,13 @@ package kubernetes
import (
"context"
"errors"
"fmt"
"net"
"strconv"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
@ -85,7 +86,7 @@ func (n *Node) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
defer n.queue.ShutDown()
if !cache.WaitForCacheSync(ctx.Done(), n.informer.HasSynced) {
if ctx.Err() != context.Canceled {
if !errors.Is(ctx.Err(), context.Canceled) {
level.Error(n.logger).Log("msg", "node informer unable to sync cache")
}
return
@ -136,7 +137,7 @@ func convertToNode(o interface{}) (*apiv1.Node, error) {
return node, nil
}
return nil, errors.Errorf("received unexpected object: %v", o)
return nil, fmt.Errorf("received unexpected object: %v", o)
}
func nodeSource(n *apiv1.Node) string {

View file

@ -15,13 +15,14 @@ package kubernetes
import (
"context"
"errors"
"fmt"
"net"
"strconv"
"strings"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -118,7 +119,7 @@ func (p *Pod) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
}
if !cache.WaitForCacheSync(ctx.Done(), cacheSyncs...) {
if ctx.Err() != context.Canceled {
if !errors.Is(ctx.Err(), context.Canceled) {
level.Error(p.logger).Log("msg", "pod informer unable to sync cache")
}
return
@ -169,7 +170,7 @@ func convertToPod(o interface{}) (*apiv1.Pod, error) {
return pod, nil
}
return nil, errors.Errorf("received unexpected object: %v", o)
return nil, fmt.Errorf("received unexpected object: %v", o)
}
const (

View file

@ -15,12 +15,13 @@ package kubernetes
import (
"context"
"errors"
"fmt"
"net"
"strconv"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
@ -81,7 +82,7 @@ func (s *Service) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
defer s.queue.ShutDown()
if !cache.WaitForCacheSync(ctx.Done(), s.informer.HasSynced) {
if ctx.Err() != context.Canceled {
if !errors.Is(ctx.Err(), context.Canceled) {
level.Error(s.logger).Log("msg", "service informer unable to sync cache")
}
return
@ -131,7 +132,7 @@ func convertToService(o interface{}) (*apiv1.Service, error) {
if ok {
return service, nil
}
return nil, errors.Errorf("received unexpected object: %v", o)
return nil, fmt.Errorf("received unexpected object: %v", o)
}
func serviceSource(s *apiv1.Service) string {

View file

@ -14,6 +14,7 @@
package legacymanager
import (
"errors"
"fmt"
"reflect"
"sort"
@ -248,7 +249,8 @@ func writeConfigs(structVal reflect.Value, configs discovery.Configs) error {
}
func replaceYAMLTypeError(err error, oldTyp, newTyp reflect.Type) error {
if e, ok := err.(*yaml.TypeError); ok {
var e *yaml.TypeError
if errors.As(err, &e) {
oldStr := oldTyp.String()
newStr := newTyp.String()
for i, s := range e.Errors {

View file

@ -25,6 +25,7 @@ import (
"github.com/go-kit/log"
"github.com/linode/linodego"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/common/version"
@ -65,15 +66,24 @@ const (
)
// DefaultSDConfig is the default Linode SD configuration.
var DefaultSDConfig = SDConfig{
TagSeparator: ",",
Port: 80,
RefreshInterval: model.Duration(60 * time.Second),
HTTPClientConfig: config.DefaultHTTPClientConfig,
}
var (
DefaultSDConfig = SDConfig{
TagSeparator: ",",
Port: 80,
RefreshInterval: model.Duration(60 * time.Second),
HTTPClientConfig: config.DefaultHTTPClientConfig,
}
failuresCount = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "prometheus_sd_linode_failures_total",
Help: "Number of Linode service discovery refresh failures.",
})
)
func init() {
discovery.RegisterConfig(&SDConfig{})
prometheus.MustRegister(failuresCount)
}
// SDConfig is the configuration for Linode based service discovery.
@ -211,12 +221,14 @@ func (d *Discovery) refreshData(ctx context.Context) ([]*targetgroup.Group, erro
// Gather all linode instances.
instances, err := d.client.ListInstances(ctx, &linodego.ListOptions{PageSize: 500})
if err != nil {
failuresCount.Inc()
return nil, err
}
// Gather detailed IP address info for all IPs on all linode instances.
detailedIPs, err := d.client.ListIPAddresses(ctx, &linodego.ListOptions{PageSize: 500})
if err != nil {
failuresCount.Inc()
return nil, err
}

View file

@ -16,6 +16,7 @@ package marathon
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"math/rand"
@ -27,7 +28,6 @@ import (
"time"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -188,7 +188,7 @@ func newAuthTokenFileRoundTripper(tokenFile string, rt http.RoundTripper) (http.
// fail-fast if we can't read the file.
_, err := os.ReadFile(tokenFile)
if err != nil {
return nil, errors.Wrapf(err, "unable to read auth token file %s", tokenFile)
return nil, fmt.Errorf("unable to read auth token file %s: %w", tokenFile, err)
}
return &authTokenFileRoundTripper{tokenFile, rt}, nil
}
@ -196,7 +196,7 @@ func newAuthTokenFileRoundTripper(tokenFile string, rt http.RoundTripper) (http.
func (rt *authTokenFileRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
b, err := os.ReadFile(rt.authTokenFile)
if err != nil {
return nil, errors.Wrapf(err, "unable to read auth token file %s", rt.authTokenFile)
return nil, fmt.Errorf("unable to read auth token file %s: %w", rt.authTokenFile, err)
}
authToken := strings.TrimSpace(string(b))
@ -336,7 +336,7 @@ func fetchApps(ctx context.Context, client *http.Client, url string) (*appList,
}()
if (resp.StatusCode < 200) || (resp.StatusCode >= 300) {
return nil, errors.Errorf("non 2xx status '%v' response during marathon service discovery", resp.StatusCode)
return nil, fmt.Errorf("non 2xx status '%v' response during marathon service discovery", resp.StatusCode)
}
b, err := io.ReadAll(resp.Body)
@ -347,7 +347,7 @@ func fetchApps(ctx context.Context, client *http.Client, url string) (*appList,
var apps appList
err = json.Unmarshal(b, &apps)
if err != nil {
return nil, errors.Wrapf(err, "%q", url)
return nil, fmt.Errorf("%q: %w", url, err)
}
return &apps, nil
}

View file

@ -54,7 +54,7 @@ func TestMarathonSDHandleError(t *testing.T) {
}
)
tgs, err := testUpdateServices(client)
if err != errTesting {
if !errors.Is(err, errTesting) {
t.Fatalf("Expected error: %s", err)
}
if len(tgs) != 0 {

View file

@ -23,7 +23,6 @@ import (
"github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors"
"github.com/gophercloud/gophercloud/pagination"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/discovery/targetgroup"
@ -62,14 +61,14 @@ func (h *HypervisorDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group
h.provider.Context = ctx
err := openstack.Authenticate(h.provider, *h.authOpts)
if err != nil {
return nil, errors.Wrap(err, "could not authenticate to OpenStack")
return nil, fmt.Errorf("could not authenticate to OpenStack: %w", err)
}
client, err := openstack.NewComputeV2(h.provider, gophercloud.EndpointOpts{
Region: h.region, Availability: h.availability,
})
if err != nil {
return nil, errors.Wrap(err, "could not create OpenStack compute session")
return nil, fmt.Errorf("could not create OpenStack compute session: %w", err)
}
tg := &targetgroup.Group{
@ -81,7 +80,7 @@ func (h *HypervisorDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group
err = pagerHypervisors.EachPage(func(page pagination.Page) (bool, error) {
hypervisorList, err := hypervisors.ExtractHypervisors(page)
if err != nil {
return false, errors.Wrap(err, "could not extract hypervisors")
return false, fmt.Errorf("could not extract hypervisors: %w", err)
}
for _, hypervisor := range hypervisorList {
labels := model.LabelSet{}

View file

@ -25,7 +25,6 @@ import (
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/pagination"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/discovery/targetgroup"
@ -79,14 +78,14 @@ func (i *InstanceDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group,
i.provider.Context = ctx
err := openstack.Authenticate(i.provider, *i.authOpts)
if err != nil {
return nil, errors.Wrap(err, "could not authenticate to OpenStack")
return nil, fmt.Errorf("could not authenticate to OpenStack: %w", err)
}
client, err := openstack.NewComputeV2(i.provider, gophercloud.EndpointOpts{
Region: i.region, Availability: i.availability,
})
if err != nil {
return nil, errors.Wrap(err, "could not create OpenStack compute session")
return nil, fmt.Errorf("could not create OpenStack compute session: %w", err)
}
// OpenStack API reference
@ -97,7 +96,7 @@ func (i *InstanceDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group,
err = pagerFIP.EachPage(func(page pagination.Page) (bool, error) {
result, err := floatingips.ExtractFloatingIPs(page)
if err != nil {
return false, errors.Wrap(err, "could not extract floatingips")
return false, fmt.Errorf("could not extract floatingips: %w", err)
}
for _, ip := range result {
// Skip not associated ips
@ -124,11 +123,11 @@ func (i *InstanceDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group,
}
err = pager.EachPage(func(page pagination.Page) (bool, error) {
if ctx.Err() != nil {
return false, errors.Wrap(ctx.Err(), "could not extract instances")
return false, fmt.Errorf("could not extract instances: %w", ctx.Err())
}
instanceList, err := servers.ExtractServers(page)
if err != nil {
return false, errors.Wrap(err, "could not extract instances")
return false, fmt.Errorf("could not extract instances: %w", err)
}
for _, s := range instanceList {

View file

@ -15,6 +15,7 @@ package openstack
import (
"context"
"errors"
"fmt"
"net/http"
"time"
@ -23,7 +24,6 @@ import (
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack"
conntrack "github.com/mwitkow/go-conntrack"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -100,7 +100,7 @@ func (c *Role) UnmarshalYAML(unmarshal func(interface{}) error) error {
case OpenStackRoleHypervisor, OpenStackRoleInstance:
return nil
default:
return errors.Errorf("unknown OpenStack SD role %q", *c)
return fmt.Errorf("unknown OpenStack SD role %q", *c)
}
}

View file

@ -29,7 +29,6 @@ import (
"github.com/go-kit/log"
"github.com/grafana/regexp"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/common/version"
@ -191,11 +190,11 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
}()
if resp.StatusCode != http.StatusOK {
return nil, errors.Errorf("server returned HTTP status %s", resp.Status)
return nil, fmt.Errorf("server returned HTTP status %s", resp.Status)
}
if ct := resp.Header.Get("Content-Type"); !matchContentType.MatchString(ct) {
return nil, errors.Errorf("unsupported content type %s", resp.Header.Get("Content-Type"))
return nil, fmt.Errorf("unsupported content type %s", resp.Header.Get("Content-Type"))
}
b, err := io.ReadAll(resp.Body)

View file

@ -15,6 +15,7 @@ package refresh
import (
"context"
"errors"
"time"
"github.com/go-kit/log"
@ -75,7 +76,7 @@ func (d *Discovery) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
// Get an initial set right away.
tgs, err := d.refresh(ctx)
if err != nil {
if ctx.Err() != context.Canceled {
if !errors.Is(ctx.Err(), context.Canceled) {
level.Error(d.logger).Log("msg", "Unable to refresh target groups", "err", err.Error())
}
} else {
@ -94,7 +95,7 @@ func (d *Discovery) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
case <-ticker.C:
tgs, err := d.refresh(ctx)
if err != nil {
if ctx.Err() != context.Canceled {
if !errors.Is(ctx.Err(), context.Canceled) {
level.Error(d.logger).Log("msg", "Unable to refresh target groups", "err", err.Error())
}
continue

View file

@ -14,6 +14,7 @@
package discovery
import (
"errors"
"fmt"
"reflect"
"sort"
@ -247,7 +248,8 @@ func writeConfigs(structVal reflect.Value, configs Configs) error {
}
func replaceYAMLTypeError(err error, oldTyp, newTyp reflect.Type) error {
if e, ok := err.(*yaml.TypeError); ok {
var e *yaml.TypeError
if errors.As(err, &e) {
oldStr := oldTyp.String()
newStr := newTyp.String()
for i, s := range e.Errors {

View file

@ -15,13 +15,14 @@ package scaleway
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/scaleway/scaleway-sdk-go/scw"
@ -61,7 +62,7 @@ func (c *role) UnmarshalYAML(unmarshal func(interface{}) error) error {
case roleInstance, roleBaremetal:
return nil
default:
return errors.Errorf("unknown role %q", *c)
return fmt.Errorf("unknown role %q", *c)
}
}
@ -228,7 +229,7 @@ func newAuthTokenFileRoundTripper(tokenFile string, rt http.RoundTripper) (http.
// fail-fast if we can't read the file.
_, err := os.ReadFile(tokenFile)
if err != nil {
return nil, errors.Wrapf(err, "unable to read auth token file %s", tokenFile)
return nil, fmt.Errorf("unable to read auth token file %s: %w", tokenFile, err)
}
return &authTokenFileRoundTripper{tokenFile, rt}, nil
}
@ -236,7 +237,7 @@ func newAuthTokenFileRoundTripper(tokenFile string, rt http.RoundTripper) (http.
func (rt *authTokenFileRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
b, err := os.ReadFile(rt.authTokenFile)
if err != nil {
return nil, errors.Wrapf(err, "unable to read auth token file %s", rt.authTokenFile)
return nil, fmt.Errorf("unable to read auth token file %s: %w", rt.authTokenFile, err)
}
authToken := strings.TrimSpace(string(b))

View file

@ -16,6 +16,7 @@ package triton
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
@ -25,7 +26,6 @@ import (
"github.com/go-kit/log"
conntrack "github.com/mwitkow/go-conntrack"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -202,7 +202,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
req = req.WithContext(ctx)
resp, err := d.client.Do(req)
if err != nil {
return nil, errors.Wrap(err, "an error occurred when requesting targets from the discovery endpoint")
return nil, fmt.Errorf("an error occurred when requesting targets from the discovery endpoint: %w", err)
}
defer func() {
@ -212,7 +212,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "an error occurred when reading the response body")
return nil, fmt.Errorf("an error occurred when reading the response body: %w", err)
}
// The JSON response body is different so it needs to be processed/mapped separately.
@ -234,7 +234,7 @@ func (d *Discovery) processContainerResponse(data []byte, endpoint string) ([]*t
dr := DiscoveryResponse{}
err := json.Unmarshal(data, &dr)
if err != nil {
return nil, errors.Wrap(err, "an error occurred unmarshaling the discovery response json")
return nil, fmt.Errorf("an error occurred unmarshaling the discovery response json: %w", err)
}
for _, container := range dr.Containers {
@ -267,7 +267,7 @@ func (d *Discovery) processComputeNodeResponse(data []byte, endpoint string) ([]
dr := ComputeNodeDiscoveryResponse{}
err := json.Unmarshal(data, &dr)
if err != nil {
return nil, errors.Wrap(err, "an error occurred unmarshaling the compute node discovery response json")
return nil, fmt.Errorf("an error occurred unmarshaling the compute node discovery response json: %w", err)
}
for _, cn := range dr.ComputeNodes {

View file

@ -15,6 +15,7 @@ package uyuni
import (
"context"
"errors"
"fmt"
"net/http"
"net/url"
@ -24,7 +25,6 @@ import (
"github.com/go-kit/log"
"github.com/kolo/xmlrpc"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -52,9 +52,10 @@ const (
// DefaultSDConfig is the default Uyuni SD configuration.
var DefaultSDConfig = SDConfig{
Entitlement: "monitoring_entitled",
Separator: ",",
RefreshInterval: model.Duration(1 * time.Minute),
Entitlement: "monitoring_entitled",
Separator: ",",
RefreshInterval: model.Duration(1 * time.Minute),
HTTPClientConfig: config.DefaultHTTPClientConfig,
}
func init() {
@ -117,6 +118,11 @@ func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Di
return NewDiscovery(c, opts.Logger)
}
// SetDirectory joins any relative file paths with dir.
func (c *SDConfig) SetDirectory(dir string) {
c.HTTPClientConfig.SetDirectory(dir)
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultSDConfig
@ -131,7 +137,7 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
_, err = url.Parse(c.Server)
if err != nil {
return errors.Wrap(err, "Uyuni Server URL is not valid")
return fmt.Errorf("Uyuni Server URL is not valid: %w", err)
}
if c.Username == "" {
@ -276,7 +282,7 @@ func (d *Discovery) getTargetsForSystems(
systemGroupIDsBySystemID, err := getSystemGroupsInfoOfMonitoredClients(rpcClient, d.token, entitlement)
if err != nil {
return nil, errors.Wrap(err, "unable to get the managed system groups information of monitored clients")
return nil, fmt.Errorf("unable to get the managed system groups information of monitored clients: %w", err)
}
systemIDs := make([]int, 0, len(systemGroupIDsBySystemID))
@ -286,12 +292,12 @@ func (d *Discovery) getTargetsForSystems(
endpointInfos, err := getEndpointInfoForSystems(rpcClient, d.token, systemIDs)
if err != nil {
return nil, errors.Wrap(err, "unable to get endpoints information")
return nil, fmt.Errorf("unable to get endpoints information: %w", err)
}
networkInfoBySystemID, err := getNetworkInformationForSystems(rpcClient, d.token, systemIDs)
if err != nil {
return nil, errors.Wrap(err, "unable to get the systems network information")
return nil, fmt.Errorf("unable to get the systems network information: %w", err)
}
for _, endpoint := range endpointInfos {
@ -317,7 +323,7 @@ func (d *Discovery) refresh(_ context.Context) ([]*targetgroup.Group, error) {
// Uyuni API takes duration in seconds.
d.token, err = login(rpcClient, d.username, d.password, int(tokenDuration.Seconds()))
if err != nil {
return nil, errors.Wrap(err, "unable to login to Uyuni API")
return nil, fmt.Errorf("unable to login to Uyuni API: %w", err)
}
// Login again at half the token lifetime.
d.tokenExpiration = time.Now().Add(tokenDuration / 2)

View file

@ -0,0 +1,170 @@
// Copyright 2022 The Prometheus Authors
// 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 vultr
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
)
// SDMock is the interface for the Vultr mock
type SDMock struct {
t *testing.T
Server *httptest.Server
Mux *http.ServeMux
}
// NewSDMock returns a new Vultr mock server.
func NewSDMock(t *testing.T) *SDMock {
return &SDMock{
t: t,
}
}
// Endpoint returns the URI to the mock server.
func (m *SDMock) Endpoint() string {
return m.Server.URL + "/"
}
// Setup creates the mock server.
func (m *SDMock) Setup() {
m.Mux = http.NewServeMux()
m.Server = httptest.NewServer(m.Mux)
}
// ShutdownServer shuts down the mock server.
func (m *SDMock) ShutdownServer() {
m.Server.Close()
}
const APIKey = "ABCBTDG35OTGH2UKCC3S6CNMDUPCN3ZWSGFQ"
// HandleInstanceList mocks vultr instances list.
func (m *SDMock) HandleInstanceList() {
m.Mux.HandleFunc("/v2/instances", func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", APIKey) {
w.WriteHeader(http.StatusForbidden)
return
}
w.Header().Add("content-type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `
{
"instances": [
{
"id": "dbdbd38c-9884-4c92-95fe-899e50dee717",
"os": "Marketplace",
"ram": 4096,
"disk": 128,
"main_ip": "149.28.234.27",
"vcpu_count": 2,
"region": "ewr",
"plan": "vhf-2c-4gb",
"date_created": "2022-03-10T02:25:30+00:00",
"status": "active",
"allowed_bandwidth": 3000,
"netmask_v4": "255.255.254.0",
"gateway_v4": "149.28.234.1",
"power_status": "running",
"server_status": "ok",
"v6_network": "",
"v6_main_ip": "",
"v6_network_size": 0,
"label": "np-2-eae38a19b0f3",
"internal_ip": "10.1.96.5",
"kvm": "https://my.vultr.com/subs/vps/novnc/api.php?data=djJ8Q2k2NlVpNUlZNE5kM3NZMWdBNzFKUkVadDBDU3ItTEJ8-yNWBdMn1AWPqX-EnpTVbA1qBeP0txBZbahSOh3UtHIL2p0fZKEdFJwFNjWtvBKC9BGvJkoJdWBoo7hF-6RzavdUKPD3KIU5xD_T6keV8X-PPukPgXMuSUaAqurAuJvwAWXcTcodBjaIH961xgbvfkFyqVl0ZgHf3ErZQE-EmpZ0Jc3uF1DO2McJ4UL8KUvHLwa-ZDUZ_AtWJjQWsjNkxjvOiA",
"hostname": "np-2-eae38a19b0f3",
"os_id": 426,
"app_id": 0,
"image_id": "vke-worker",
"firewall_group_id": "",
"features": ["backups"],
"tags": ["tag1", "tag2", "tag3"]
},
{
"id": "fccb117c-62f7-4b17-995d-a8e56dd30b33",
"os": "Marketplace",
"ram": 4096,
"disk": 128,
"main_ip": "45.63.1.222",
"vcpu_count": 2,
"region": "ewr",
"plan": "vhf-2c-4gb",
"date_created": "2022-03-10T02:25:32+00:00",
"status": "active",
"allowed_bandwidth": 3000,
"netmask_v4": "255.255.254.0",
"gateway_v4": "45.63.0.1",
"power_status": "running",
"server_status": "ok",
"v6_network": "",
"v6_main_ip": "",
"v6_network_size": 0,
"label": "np-2-fd0714b5fe42",
"internal_ip": "10.1.96.6",
"kvm": "https://my.vultr.com/subs/vps/novnc/api.php?data=djJ8R0tzbmI0VWVfSDR6VE51Q0FGUTBCamwyTFNzZDNkVTF8Zwm93t6L2BLTYSNmM0g0Ppf43r71aIbqLiJCx6_1NuCugUqIKz8POshH91vi6XXDorpb3hYTk8RrJWQbaFMak7XbbfMObzsrFxKAQUVGfbjTBIHCKftxNIXeQOp1fEIDa3p-vkfSxEpx6ZX8nChGYYSVuOjS1twV6oI_IKtHXKdFJiVFz0Unay_K3HuXW_H2wrYSjmAChloR-eA1jSmBXLccJQ",
"hostname": "np-2-fd0714b5fe42",
"os_id": 426,
"app_id": 0,
"image_id": "vke-worker",
"firewall_group_id": "",
"features": [],
"tags": []
},
{
"id": "c4e58767-f61b-4c5e-9bce-43761cc04868",
"os": "Marketplace",
"ram": 4096,
"disk": 128,
"main_ip": "149.28.237.151",
"vcpu_count": 2,
"region": "ewr",
"plan": "vhf-2c-4gb",
"date_created": "2022-03-10T02:25:33+00:00",
"status": "active",
"allowed_bandwidth": 3000,
"netmask_v4": "255.255.254.0",
"gateway_v4": "149.28.236.1",
"power_status": "running",
"server_status": "ok",
"v6_network": "",
"v6_main_ip": "",
"v6_network_size": 0,
"label": "np-2-d04e7829fa43",
"internal_ip": "10.1.96.7",
"kvm": "https://my.vultr.com/subs/vps/novnc/api.php?data=djJ8NFEzbFZmV2hOUWNrc2R4bndnckhFQTJ3ME1yWmxVeW98P-p-KeyNtP73wzVvuWZSOrV5PvQDiBmYyB3cp99btIy5nczmvX8DOGSChbEldk3idKNdFLQMIieWnpR5xU5K-dMYlL6n6oYv2u-rh8zxS6-8hQsSbL2OsoywNPoLpQ3xPkT9Rb-yPrZX4fDLbt8yPVKDHqHO0GB-TxVPemueovfkkUZMXSraAXgHtl1YgK2uLMQf1WBFVzgwnQ7MYcfMGGYFQw",
"hostname": "np-2-d04e7829fa43",
"os_id": 426,
"app_id": 0,
"image_id": "vke-worker",
"firewall_group_id": "",
"features": [],
"tags": []
}
],
"meta": {
"total": 3,
"links": {
"next": "",
"prev": ""
}
}
}
`)
})
}

212
discovery/vultr/vultr.go Normal file
View file

@ -0,0 +1,212 @@
// Copyright 2022 The Prometheus Authors
// 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 vultr
import (
"context"
"fmt"
"net"
"net/http"
"strconv"
"strings"
"time"
"github.com/go-kit/log"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/common/version"
"github.com/vultr/govultr/v2"
"github.com/prometheus/prometheus/discovery"
"github.com/prometheus/prometheus/discovery/refresh"
"github.com/prometheus/prometheus/discovery/targetgroup"
)
const (
vultrInstanceLabel = model.MetaLabelPrefix + "vultr_instance_"
vultrInstanceLabelID = vultrInstanceLabel + "id"
vultrInstanceLabelLabel = vultrInstanceLabel + "label"
vultrInstanceLabelOS = vultrInstanceLabel + "os"
vultrInstanceLabelOSID = vultrInstanceLabel + "os_id"
vultrInstanceLabelRegion = vultrInstanceLabel + "region"
vultrInstanceLabelPlan = vultrInstanceLabel + "plan"
vultrInstanceLabelMainIP = vultrInstanceLabel + "main_ip"
vultrInstanceLabelMainIPV6 = vultrInstanceLabel + "main_ipv6"
vultrInstanceLabelInternalIP = vultrInstanceLabel + "internal_ip"
vultrInstanceLabelFeatures = vultrInstanceLabel + "features"
vultrInstanceLabelTags = vultrInstanceLabel + "tags"
vultrInstanceLabelHostname = vultrInstanceLabel + "hostname"
vultrInstanceLabelServerStatus = vultrInstanceLabel + "server_status"
vultrInstanceLabelVCPU = vultrInstanceLabel + "vcpu_count"
vultrInstanceLabelMemory = vultrInstanceLabel + "ram_mb"
vultrInstanceLabelBandwidth = vultrInstanceLabel + "allowed_bandwidth_gb"
vultrInstanceLabelDisk = vultrInstanceLabel + "disk_gb"
separator = ","
)
// DefaultSDConfig is the default Vultr SD configuration.
var DefaultSDConfig = SDConfig{
Port: 80,
RefreshInterval: model.Duration(60 * time.Second),
HTTPClientConfig: config.DefaultHTTPClientConfig,
}
func init() {
discovery.RegisterConfig(&SDConfig{})
}
type SDConfig struct {
HTTPClientConfig config.HTTPClientConfig `yaml:",inline"`
RefreshInterval model.Duration `yaml:"refresh_interval"`
Port int `yaml:"port"`
}
// Name returns the name of the Config.
func (*SDConfig) Name() string { return "vultr" }
// NewDiscoverer returns a Discoverer for the Config.
func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) {
return NewDiscovery(c, opts.Logger)
}
// SetDirectory joins any relative file paths with dir.
func (c *SDConfig) SetDirectory(dir string) {
c.HTTPClientConfig.SetDirectory(dir)
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultSDConfig
type plain SDConfig
if err := unmarshal((*plain)(c)); err != nil {
return err
}
return c.HTTPClientConfig.Validate()
}
// Discovery periodically performs Vultr requests. It implements
// the Discoverer interface.
type Discovery struct {
*refresh.Discovery
client *govultr.Client
port int
}
// NewDiscovery returns a new Discovery which periodically refreshes its targets.
func NewDiscovery(conf *SDConfig, logger log.Logger) (*Discovery, error) {
d := &Discovery{
port: conf.Port,
}
rt, err := config.NewRoundTripperFromConfig(conf.HTTPClientConfig, "vultr_sd")
if err != nil {
return nil, err
}
d.client = govultr.NewClient(&http.Client{
Transport: rt,
Timeout: time.Duration(conf.RefreshInterval),
})
d.client.SetUserAgent(fmt.Sprintf("Prometheus/%s", version.Version))
if err != nil {
return nil, fmt.Errorf("error setting up vultr agent: %w", err)
}
d.Discovery = refresh.NewDiscovery(
logger,
"vultr",
time.Duration(conf.RefreshInterval),
d.refresh,
)
return d, nil
}
func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
tg := &targetgroup.Group{
Source: "Vultr",
}
instances, err := d.listInstances(ctx)
if err != nil {
return nil, err
}
for _, instance := range instances {
labels := model.LabelSet{
vultrInstanceLabelID: model.LabelValue(instance.ID),
vultrInstanceLabelLabel: model.LabelValue(instance.Label),
vultrInstanceLabelOS: model.LabelValue(instance.Os),
vultrInstanceLabelOSID: model.LabelValue(strconv.Itoa(instance.OsID)),
vultrInstanceLabelRegion: model.LabelValue(instance.Region),
vultrInstanceLabelPlan: model.LabelValue(instance.Plan),
vultrInstanceLabelVCPU: model.LabelValue(strconv.Itoa(instance.VCPUCount)),
vultrInstanceLabelMemory: model.LabelValue(strconv.Itoa(instance.RAM)),
vultrInstanceLabelBandwidth: model.LabelValue(strconv.Itoa(instance.AllowedBandwidth)),
vultrInstanceLabelDisk: model.LabelValue(strconv.Itoa(instance.Disk)),
vultrInstanceLabelMainIP: model.LabelValue(instance.MainIP),
vultrInstanceLabelMainIPV6: model.LabelValue(instance.V6MainIP),
vultrInstanceLabelInternalIP: model.LabelValue(instance.InternalIP),
vultrInstanceLabelHostname: model.LabelValue(instance.Hostname),
vultrInstanceLabelServerStatus: model.LabelValue(instance.ServerStatus),
}
addr := net.JoinHostPort(instance.MainIP, strconv.FormatUint(uint64(d.port), 10))
labels[model.AddressLabel] = model.LabelValue(addr)
// We surround the separated list with the separator as well. This way regular expressions
// in relabeling rules don't have to consider feature positions.
if len(instance.Features) > 0 {
features := separator + strings.Join(instance.Features, separator) + separator
labels[vultrInstanceLabelFeatures] = model.LabelValue(features)
}
if len(instance.Tags) > 0 {
tags := separator + strings.Join(instance.Tags, separator) + separator
labels[vultrInstanceLabelTags] = model.LabelValue(tags)
}
tg.Targets = append(tg.Targets, labels)
}
return []*targetgroup.Group{tg}, nil
}
func (d *Discovery) listInstances(ctx context.Context) ([]govultr.Instance, error) {
var instances []govultr.Instance
listOptions := &govultr.ListOptions{
PerPage: 100,
}
for {
pagedInstances, meta, err := d.client.Instance.List(ctx, listOptions)
if err != nil {
return nil, err
}
instances = append(instances, pagedInstances...)
if meta.Links.Next == "" {
break
} else {
listOptions.Cursor = meta.Links.Next
continue
}
}
return instances, nil
}

View file

@ -0,0 +1,128 @@
// Copyright 2022 The Prometheus Authors
// 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 vultr
import (
"context"
"fmt"
"net/url"
"testing"
"github.com/go-kit/log"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
)
type VultrSDTestSuite struct {
Mock *SDMock
}
func (s *VultrSDTestSuite) TearDownSuite() {
s.Mock.ShutdownServer()
}
func (s *VultrSDTestSuite) SetupTest(t *testing.T) {
s.Mock = NewSDMock(t)
s.Mock.Setup()
s.Mock.HandleInstanceList()
}
func TestVultrSDRefresh(t *testing.T) {
sdMock := &VultrSDTestSuite{}
sdMock.SetupTest(t)
t.Cleanup(sdMock.TearDownSuite)
cfg := DefaultSDConfig
cfg.HTTPClientConfig.BearerToken = APIKey
d, err := NewDiscovery(&cfg, log.NewNopLogger())
require.NoError(t, err)
endpoint, err := url.Parse(sdMock.Mock.Endpoint())
require.NoError(t, err)
d.client.BaseURL = endpoint
ctx := context.Background()
tgs, err := d.refresh(ctx)
require.NoError(t, err)
require.Equal(t, 1, len(tgs))
tg := tgs[0]
require.NotNil(t, tg)
require.NotNil(t, tg.Targets)
require.Equal(t, 3, len(tg.Targets))
for i, k := range []model.LabelSet{
{
"__address__": model.LabelValue("149.28.234.27:80"),
"__meta_vultr_instance_id": model.LabelValue("dbdbd38c-9884-4c92-95fe-899e50dee717"),
"__meta_vultr_instance_label": model.LabelValue("np-2-eae38a19b0f3"),
"__meta_vultr_instance_os": model.LabelValue("Marketplace"),
"__meta_vultr_instance_os_id": model.LabelValue("426"),
"__meta_vultr_instance_region": model.LabelValue("ewr"),
"__meta_vultr_instance_plan": model.LabelValue("vhf-2c-4gb"),
"__meta_vultr_instance_main_ip": model.LabelValue("149.28.234.27"),
"__meta_vultr_instance_internal_ip": model.LabelValue("10.1.96.5"),
"__meta_vultr_instance_main_ipv6": model.LabelValue(""),
"__meta_vultr_instance_features": model.LabelValue(",backups,"),
"__meta_vultr_instance_tags": model.LabelValue(",tag1,tag2,tag3,"),
"__meta_vultr_instance_hostname": model.LabelValue("np-2-eae38a19b0f3"),
"__meta_vultr_instance_server_status": model.LabelValue("ok"),
"__meta_vultr_instance_vcpu_count": model.LabelValue("2"),
"__meta_vultr_instance_ram_mb": model.LabelValue("4096"),
"__meta_vultr_instance_disk_gb": model.LabelValue("128"),
"__meta_vultr_instance_allowed_bandwidth_gb": model.LabelValue("3000"),
},
{
"__address__": model.LabelValue("45.63.1.222:80"),
"__meta_vultr_instance_id": model.LabelValue("fccb117c-62f7-4b17-995d-a8e56dd30b33"),
"__meta_vultr_instance_label": model.LabelValue("np-2-fd0714b5fe42"),
"__meta_vultr_instance_os": model.LabelValue("Marketplace"),
"__meta_vultr_instance_os_id": model.LabelValue("426"),
"__meta_vultr_instance_region": model.LabelValue("ewr"),
"__meta_vultr_instance_plan": model.LabelValue("vhf-2c-4gb"),
"__meta_vultr_instance_main_ip": model.LabelValue("45.63.1.222"),
"__meta_vultr_instance_internal_ip": model.LabelValue("10.1.96.6"),
"__meta_vultr_instance_main_ipv6": model.LabelValue(""),
"__meta_vultr_instance_hostname": model.LabelValue("np-2-fd0714b5fe42"),
"__meta_vultr_instance_server_status": model.LabelValue("ok"),
"__meta_vultr_instance_vcpu_count": model.LabelValue("2"),
"__meta_vultr_instance_ram_mb": model.LabelValue("4096"),
"__meta_vultr_instance_disk_gb": model.LabelValue("128"),
"__meta_vultr_instance_allowed_bandwidth_gb": model.LabelValue("3000"),
},
{
"__address__": model.LabelValue("149.28.237.151:80"),
"__meta_vultr_instance_id": model.LabelValue("c4e58767-f61b-4c5e-9bce-43761cc04868"),
"__meta_vultr_instance_label": model.LabelValue("np-2-d04e7829fa43"),
"__meta_vultr_instance_os": model.LabelValue("Marketplace"),
"__meta_vultr_instance_os_id": model.LabelValue("426"),
"__meta_vultr_instance_region": model.LabelValue("ewr"),
"__meta_vultr_instance_plan": model.LabelValue("vhf-2c-4gb"),
"__meta_vultr_instance_main_ip": model.LabelValue("149.28.237.151"),
"__meta_vultr_instance_internal_ip": model.LabelValue("10.1.96.7"),
"__meta_vultr_instance_main_ipv6": model.LabelValue(""),
"__meta_vultr_instance_hostname": model.LabelValue("np-2-d04e7829fa43"),
"__meta_vultr_instance_server_status": model.LabelValue("ok"),
"__meta_vultr_instance_vcpu_count": model.LabelValue("2"),
"__meta_vultr_instance_ram_mb": model.LabelValue("4096"),
"__meta_vultr_instance_disk_gb": model.LabelValue("128"),
"__meta_vultr_instance_allowed_bandwidth_gb": model.LabelValue("3000"),
},
} {
t.Run(fmt.Sprintf("item %d", i), func(t *testing.T) {
require.Equal(t, k, tg.Targets[i])
})
}
}

View file

@ -20,7 +20,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -92,7 +91,7 @@ func (c *KumaSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
if len(c.Server) == 0 {
return errors.Errorf("kuma SD server must not be empty: %s", c.Server)
return fmt.Errorf("kuma SD server must not be empty: %s", c.Server)
}
parsedURL, err := url.Parse(c.Server)
if err != nil {
@ -100,7 +99,7 @@ func (c *KumaSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
if len(parsedURL.Scheme) == 0 || len(parsedURL.Host) == 0 {
return errors.Errorf("kuma SD server must not be empty and have a scheme: %s", c.Server)
return fmt.Errorf("kuma SD server must not be empty and have a scheme: %s", c.Server)
}
return c.HTTPClientConfig.Validate()
@ -159,7 +158,7 @@ func convertKumaUserLabels(labels map[string]string) model.LabelSet {
// kumaMadsV1ResourceParser is an xds.resourceParser.
func kumaMadsV1ResourceParser(resources []*anypb.Any, typeURL string) ([]model.LabelSet, error) {
if typeURL != KumaMadsV1ResourceTypeURL {
return nil, errors.Errorf("received invalid typeURL for Kuma MADS v1 Resource: %s", typeURL)
return nil, fmt.Errorf("received invalid typeURL for Kuma MADS v1 Resource: %s", typeURL)
}
var targets []model.LabelSet

View file

@ -15,12 +15,12 @@ package xds
import (
"context"
"errors"
"fmt"
"testing"
"time"
v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"

View file

@ -16,6 +16,7 @@ package zookeeper
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
"strconv"
@ -24,7 +25,6 @@ import (
"github.com/go-kit/log"
"github.com/go-zookeeper/zk"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/discovery"
@ -80,7 +80,7 @@ func (c *ServersetSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) err
}
for _, path := range c.Paths {
if !strings.HasPrefix(path, "/") {
return errors.Errorf("serverset SD config paths must begin with '/': %s", path)
return fmt.Errorf("serverset SD config paths must begin with '/': %s", path)
}
}
return nil
@ -117,7 +117,7 @@ func (c *NerveSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
for _, path := range c.Paths {
if !strings.HasPrefix(path, "/") {
return errors.Errorf("nerve SD config paths must begin with '/': %s", path)
return fmt.Errorf("nerve SD config paths must begin with '/': %s", path)
}
}
return nil
@ -263,7 +263,7 @@ func parseServersetMember(data []byte, path string) (model.LabelSet, error) {
member := serversetMember{}
if err := json.Unmarshal(data, &member); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling serverset member %q", path)
return nil, fmt.Errorf("error unmarshaling serverset member %q: %w", path, err)
}
labels := model.LabelSet{}
@ -305,7 +305,7 @@ func parseNerveMember(data []byte, path string) (model.LabelSet, error) {
member := nerveMember{}
err := json.Unmarshal(data, &member)
if err != nil {
return nil, errors.Wrapf(err, "error unmarshaling nerve member %q", path)
return nil, fmt.Errorf("error unmarshaling nerve member %q: %w", path, err)
}
labels := model.LabelSet{}

View file

@ -256,6 +256,11 @@ hetzner_sd_configs:
http_sd_configs:
[ - <http_sd_config> ... ]
# List of IONOS service discovery configurations.
ionos_sd_configs:
[ - <ionos_sd_config> ... ]
# List of Kubernetes service discovery configurations.
kubernetes_sd_configs:
[ - <kubernetes_sd_config> ... ]
@ -1539,6 +1544,86 @@ tls_config:
[ <tls_config> ]
```
### `<ionos_sd_config>`
IONOS SD configurations allows retrieving scrape targets from
[IONOS Cloud](https://cloud.ionos.com/) API. This service discovery uses the
first NICs IP address by default, but that can be changed with relabeling. The
following meta labels are available on all targets during
[relabeling](#relabel_config):
* `__meta_ionos_server_availability_zone`: the availability zone of the server
* `__meta_ionos_server_boot_cdrom_id`: the ID of the CD-ROM the server is booted
from
* `__meta_ionos_server_boot_image_id`: the ID of the boot image or snapshot the
server is booted from
* `__meta_ionos_server_boot_volume_id`: the ID of the boot volume
* `__meta_ionos_server_cpu_family`: the CPU family of the server
to
* `__meta_ionos_server_id`: the ID of the server
* `__meta_ionos_server_ip`: comma separated list of all IPs assigned to the
server
* `__meta_ionos_server_lifecycle`: the lifecycle state of the server resource
* `__meta_ionos_server_name`: the name of the server
* `__meta_ionos_server_nic_ip_<nic_name>`: comma separated list of IPs, grouped
by the name of each NIC attached to the server
* `__meta_ionos_server_servers_id`: the ID of the servers the server belongs to
* `__meta_ionos_server_state`: the execution state of the server
* `__meta_ionos_server_type`: the type of the server
```yaml
# The unique ID of the data center.
datacenter_id: <string>
# Authentication information used to authenticate to the API server.
# Note that `basic_auth` and `authorization` options are
# mutually exclusive.
# password and password_file are mutually exclusive.
# Optional HTTP basic authentication information, required when using IONOS
# Cloud username and password as authentication method.
basic_auth:
[ username: <string> ]
[ password: <secret> ]
[ password_file: <string> ]
# Optional `Authorization` header configuration, required when using IONOS
# Cloud token as authentication method.
authorization:
# Sets the authentication type.
[ type: <string> | default: Bearer ]
# Sets the credentials. It is mutually exclusive with
# `credentials_file`.
[ credentials: <secret> ]
# Sets the credentials to the credentials read from the configured file.
# It is mutually exclusive with `credentials`.
[ credentials_file: <filename> ]
# Optional OAuth 2.0 configuration.
# Cannot be used at the same time as basic_auth or authorization.
oauth2:
[ <oauth2> ]
# Optional proxy URL.
[ proxy_url: <string> ]
# Configure whether HTTP requests follow HTTP 3xx redirects.
[ follow_redirects: <boolean> | default = true ]
# Whether to enable HTTP2.
[ enable_http2: <bool> | default: true ]
# TLS configuration.
tls_config:
[ <tls_config> ]
# The port to scrape metrics from.
[ port: <int> | default = 80 ]
# The time after which the servers are refreshed.
[ refresh_interval: <duration> | default = 60s ]
```
### `<kubernetes_sd_config>`
Kubernetes SD configurations allow retrieving scrape targets from
@ -2453,6 +2538,83 @@ tls_config:
See [the Prometheus uyuni-sd configuration file](/documentation/examples/prometheus-uyuni.yml)
for a practical example on how to set up Uyuni Prometheus configuration.
### `<vultr_sd_config>`
Vultr SD configurations allow retrieving scrape targets from [Vultr](https://www.vultr.com/).
This service discovery uses the main IPv4 address by default, which that be
changed with relabelling, as demonstrated in [the Prometheus vultr-sd
configuration file](/documentation/examples/prometheus-vultr.yml).
The following meta labels are available on targets during [relabeling](#relabel_config):
* `__meta_vultr_instance_id` : A unique ID for the vultr Instance.
* `__meta_vultr_instance_label` : The user-supplied label for this instance.
* `__meta_vultr_instance_os` : The Operating System name.
* `__meta_vultr_instance_os_id` : The Operating System id used by this instance.
* `__meta_vultr_instance_region` : The Region id where the Instance is located.
* `__meta_vultr_instance_plan` : A unique ID for the Plan.
* `__meta_vultr_instance_main_ip` : The main IPv4 address.
* `__meta_vultr_instance_internal_ip` : The private IP address.
* `__meta_vultr_instance_main_ipv6` : The main IPv6 address.
* `__meta_vultr_instance_features` : List of features that are available to the instance.
* `__meta_vultr_instance_tags` : List of tags associated with the instance.
* `__meta_vultr_instance_hostname` : The hostname for this instance.
* `__meta_vultr_instance_server_status` : The server health status.
* `__meta_vultr_instance_vcpu_count` : Number of vCPUs.
* `__meta_vultr_instance_ram_mb` : The amount of RAM in MB.
* `__meta_vultr_instance_disk_gb` : The size of the disk in GB.
* `__meta_vultr_instance_allowed_bandwidth_gb` : Monthly bandwidth quota in GB.
```yaml
# Authentication information used to authenticate to the API server.
# Note that `basic_auth` and `authorization` options are
# mutually exclusive.
# password and password_file are mutually exclusive.
# Optional HTTP basic authentication information, not currently supported by Vultr.
basic_auth:
[ username: <string> ]
[ password: <secret> ]
[ password_file: <string> ]
# Optional `Authorization` header configuration.
authorization:
# Sets the authentication type.
[ type: <string> | default: Bearer ]
# Sets the credentials. It is mutually exclusive with
# `credentials_file`.
[ credentials: <secret> ]
# Sets the credentials to the credentials read from the configured file.
# It is mutually exclusive with `credentials`.
[ credentials_file: <filename> ]
# Optional OAuth 2.0 configuration.
# Cannot be used at the same time as basic_auth or authorization.
oauth2:
[ <oauth2> ]
# Optional proxy URL.
[ proxy_url: <string> ]
# Configure whether HTTP requests follow HTTP 3xx redirects.
[ follow_redirects: <boolean> | default = true ]
# Whether to enable HTTP2.
[ enable_http2: <bool> | default: true ]
# TLS configuration.
tls_config:
[ <tls_config> ]
# The port to scrape metrics from.
[ port: <int> | default = 80 ]
# The time after which the instances are refreshed.
[ refresh_interval: <duration> | default = 60s ]
```
### `<static_config>`
A `static_config` allows specifying a list of targets and a common label set
@ -2680,6 +2842,10 @@ hetzner_sd_configs:
http_sd_configs:
[ - <http_sd_config> ... ]
# List of IONOS service discovery configurations.
ionos_sd_configs:
[ - <ionos_sd_config> ... ]
# List of Kubernetes service discovery configurations.
kubernetes_sd_configs:
[ - <kubernetes_sd_config> ... ]
@ -2724,6 +2890,10 @@ triton_sd_configs:
uyuni_sd_configs:
[ - <uyuni_sd_config> ... ]
# List of Vultr service discovery configurations.
vultr_sd_configs:
[ - <vultr_sd_config> ... ]
# List of labeled statically configured Alertmanagers.
static_configs:
[ - <static_config> ... ]

View file

@ -103,6 +103,12 @@ for each of the given times in UTC. Returned values are from 1 to 31.
each of the given times in UTC. Returned values are from 0 to 6, where 0 means
Sunday etc.
## `day_of_year()`
`day_of_year(v=vector(time()) instant-vector)` returns the day of the year for
each of the given times in UTC. Returned values are from 1 to 365 for non-leap years,
and 1 to 366 in leap years.
## `days_in_month()`
`days_in_month(v=vector(time()) instant-vector)` returns number of days in the
@ -405,8 +411,6 @@ expression is to be evaluated.
`timestamp(v instant-vector)` returns the timestamp of each of the samples of
the given vector as the number of seconds since January 1, 1970 UTC.
*This function was added in Prometheus 2.0*
## `vector()`
`vector(s scalar)` returns the scalar `s` as a vector with no labels.

View file

@ -0,0 +1,20 @@
# Copyright 2022 The Prometheus Authors
# 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.
.PHONY: all
all: check-examples-syntax
.PHONY: check-examples-syntax
check-examples-syntax: prometheus-*.yml
@echo ">> check example configurations syntax"
@set -e; for file in $^; do ../../promtool check config --syntax-only $$file; done

View file

@ -155,6 +155,7 @@ func (a *Adapter) runCustomSD(ctx context.Context) {
// Run starts a Discovery Manager and the custom service discovery implementation.
func (a *Adapter) Run() {
//nolint:errcheck
go a.manager.Run()
a.manager.StartCustomProvider(a.ctx, a.name, a.disc)
go a.runCustomSD(a.ctx)

View file

@ -13,7 +13,7 @@ scrape_configs:
hetzner_sd_configs:
- authorization:
credentials: "<replace with a Hetzner Cloud API Token>"
platform: "hcloud"
role: "hcloud"
relabel_configs:
# Use the public IPv4 and port 9100 to scrape the target.
- source_labels: [__meta_hetzner_public_ipv4]
@ -26,7 +26,7 @@ scrape_configs:
hetzner_sd_configs:
- authorization:
credentials: "<replace with a Hetzner Cloud API Token>"
platform: "hcloud"
role: "hcloud"
relabel_configs:
# Use the private IPv4 within the Hetzner Cloud Network and port 9100 to scrape the target.
- source_labels: [__meta_hetzner_hcloud_private_ipv4_mynet]
@ -40,7 +40,7 @@ scrape_configs:
- basic_auth:
username: "<replace with a Hetzner Robot API username>"
password: "<replace with a Hetzner Robot API password>"
platform: "robot"
role: "robot"
relabel_configs:
# Use the public IPv4 and port 9100 to scrape the target.
- source_labels: [__meta_hetzner_public_ipv4]

View file

@ -0,0 +1,30 @@
# A example scrape configuration for running Prometheus with IONOS Cloud.
scrape_configs:
- job_name: "node"
ionos_sd_configs:
- basic_auth:
# Replace with your username.
username: username
# Replace with your password.
password: password
# You can find your data center unique ID here: https://dcd.ionos.com/latest/
datacenter_id: 11111111-1111-1111-1111-111111111111
port: 9100
relabel_configs:
# Only scrape servers that are available via API.
- source_labels: [__meta_ionos_server_lifecycle]
action: keep
regex: "AVAILABLE"
# Only scrape servers that are in a running state.
- source_labels: [__meta_ionos_server_state]
action: keep
regex: "RUNNING"
# Replace instance label with server name.
- source_labels: [__meta_ionos_server_name]
target_label: instance
# Add server availability zone as label.
- source_labels: [__meta_ionos_availability_zone]
target_label: ionos_zone

View file

@ -11,7 +11,7 @@ scrape_configs:
- job_name: "node"
linode_sd_configs:
- authorization:
credentials: "<replace with a Personal Access Token with linodes:read_only, ips:read_only, and events:read_only access>"
credentials: "<replace with a Personal Access Token with linodes:read_only, ips:read_only, and events:read_only access>"
relabel_configs:
# Only scrape targets that have a tag 'monitoring'.
- source_labels: [__meta_linode_tags]

View file

@ -0,0 +1,24 @@
# An example scrape configuration for running Prometheus with
# Vultr.
scrape_configs:
# Make Prometheus scrape itself for metrics.
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
# Discover Node Exporter instances to scrape.
- job_name: "node"
vultr_sd_configs:
- authorization:
credentials: "<replace with a Personal Access Token>"
relabel_configs:
# Only scrape targets that have a tag 'monitoring'.
- source_labels: [__meta_vultr_instance_tags]
regex: ".*,monitoring,.*"
action: keep
# Use the public IPv6 address and port 9100 to scrape the target.
- source_labels: [__meta_vultr_instance_main_ipv6]
target_label: __address__
replacement: "[$1]:9100"

View file

@ -3,15 +3,14 @@ module github.com/prometheus/prometheus/documentation/examples/remote_storage
go 1.17
require (
github.com/go-kit/log v0.2.0
github.com/go-kit/log v0.2.1
github.com/gogo/protobuf v1.3.2
github.com/golang/snappy v0.0.4
github.com/influxdata/influxdb v1.9.5
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.12.1
github.com/prometheus/common v0.32.1
github.com/prometheus/client_golang v1.12.2
github.com/prometheus/common v0.34.0
github.com/prometheus/prometheus v1.8.2-0.20220202104425-d819219dd438
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.7.2
gopkg.in/alecthomas/kingpin.v2 v2.2.6
)
@ -30,6 +29,7 @@ require (
github.com/jpillora/backoff v1.0.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common/sigv4 v0.1.0 // indirect
@ -41,13 +41,13 @@ require (
go.opentelemetry.io/otel/trace v1.2.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/goleak v1.1.12 // indirect
golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View file

@ -458,8 +458,9 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
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.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@ -1104,8 +1105,9 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -1126,8 +1128,9 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE=
github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE=
github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4=
github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI=
github.com/prometheus/exporter-toolkit v0.6.1/go.mod h1:ZUBIj498ePooX9t/2xtDjeQYwvRpiPP2lh5u4iblj2g=
@ -1228,8 +1231,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@ -1502,8 +1506,10 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY=
golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1519,8 +1525,9 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1653,8 +1660,9 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1975,8 +1983,9 @@ 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.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=

View file

@ -15,6 +15,7 @@ package influxdb
import (
"encoding/json"
"errors"
"fmt"
"math"
"os"
@ -23,7 +24,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
influx "github.com/influxdata/influxdb/client/v2"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model"
@ -174,7 +174,7 @@ func (c *Client) buildCommand(q *prompb.Query) (string, error) {
case prompb.LabelMatcher_NRE:
matchers = append(matchers, fmt.Sprintf("%q !~ /^%s$/", m.Name, escapeSlashes(m.Value)))
default:
return "", errors.Errorf("unknown match type %v", m.Type)
return "", fmt.Errorf("unknown match type %v", m.Type)
}
}
matchers = append(matchers, fmt.Sprintf("time >= %vms", q.StartTimestampMs))
@ -253,27 +253,27 @@ func valuesToSamples(values [][]interface{}) ([]prompb.Sample, error) {
samples := make([]prompb.Sample, 0, len(values))
for _, v := range values {
if len(v) != 2 {
return nil, errors.Errorf("bad sample tuple length, expected [<timestamp>, <value>], got %v", v)
return nil, fmt.Errorf("bad sample tuple length, expected [<timestamp>, <value>], got %v", v)
}
jsonTimestamp, ok := v[0].(json.Number)
if !ok {
return nil, errors.Errorf("bad timestamp: %v", v[0])
return nil, fmt.Errorf("bad timestamp: %v", v[0])
}
jsonValue, ok := v[1].(json.Number)
if !ok {
return nil, errors.Errorf("bad sample value: %v", v[1])
return nil, fmt.Errorf("bad sample value: %v", v[1])
}
timestamp, err := jsonTimestamp.Int64()
if err != nil {
return nil, errors.Wrap(err, "unable to convert sample timestamp to int64")
return nil, fmt.Errorf("unable to convert sample timestamp to int64: %w", err)
}
value, err := jsonValue.Float64()
if err != nil {
return nil, errors.Wrap(err, "unable to convert sample value to float64")
return nil, fmt.Errorf("unable to convert sample value to float64: %w", err)
}
samples = append(samples, prompb.Sample{

View file

@ -30,7 +30,6 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/golang/snappy"
influx "github.com/influxdata/influxdb/client/v2"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/model"
@ -148,7 +147,7 @@ func parseFlags() *config {
_, err := a.Parse(os.Args[1:])
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Error parsing commandline arguments"))
fmt.Fprintln(os.Stderr, fmt.Errorf("Error parsing commandline arguments: %w", err))
a.Usage(os.Args[1:])
os.Exit(2)
}

View file

@ -17,6 +17,7 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"math"
"net/http"
@ -25,7 +26,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
)
@ -136,7 +136,7 @@ func (c *Client) Write(samples model.Samples) error {
if err := json.Unmarshal(buf, &r); err != nil {
return err
}
return errors.Errorf("failed to write %d samples to OpenTSDB, %d succeeded", r["failed"], r["success"])
return fmt.Errorf("failed to write %d samples to OpenTSDB, %d succeeded", r["failed"], r["success"])
}
// Name identifies the client as an OpenTSDB client.

View file

@ -17,7 +17,6 @@ import (
"bytes"
"fmt"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
)
@ -98,13 +97,13 @@ func (tv *TagValue) UnmarshalJSON(json []byte) error {
for i, b := range json {
if i == 0 {
if b != '"' {
return errors.Errorf("expected '\"', got %q", b)
return fmt.Errorf("expected '\"', got %q", b)
}
continue
}
if i == len(json)-1 {
if b != '"' {
return errors.Errorf("expected '\"', got %q", b)
return fmt.Errorf("expected '\"', got %q", b)
}
break
}
@ -130,7 +129,7 @@ func (tv *TagValue) UnmarshalJSON(json []byte) error {
parsedByte = (b - 55) << 4
escapeLevel = 2
default:
return errors.Errorf(
return fmt.Errorf(
"illegal escape sequence at byte %d (%c)",
i, b,
)
@ -142,7 +141,7 @@ func (tv *TagValue) UnmarshalJSON(json []byte) error {
case b >= 'A' && b <= 'F': // A-F
parsedByte += b - 55
default:
return errors.Errorf(
return fmt.Errorf(
"illegal escape sequence at byte %d (%c)",
i, b,
)

272
go.mod
View file

@ -1,52 +1,45 @@
module github.com/prometheus/prometheus
go 1.16
go 1.17
require (
github.com/Azure/azure-sdk-for-go v63.0.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.25
github.com/Azure/go-autorest/autorest/adal v0.9.18
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.27
github.com/Azure/go-autorest/autorest/adal v0.9.20
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137
github.com/armon/go-metrics v0.3.3 // indirect
github.com/aws/aws-sdk-go v1.43.31
github.com/aws/aws-sdk-go v1.44.29
github.com/cespare/xxhash/v2 v2.1.2
github.com/containerd/containerd v1.6.1 // indirect
github.com/dennwc/varint v1.0.0
github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245
github.com/digitalocean/godo v1.78.0
github.com/docker/docker v20.10.14+incompatible
github.com/digitalocean/godo v1.80.0
github.com/docker/docker v20.10.16+incompatible
github.com/edsrzf/mmap-go v1.1.0
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1
github.com/envoyproxy/protoc-gen-validate v0.6.7
github.com/fsnotify/fsnotify v1.5.1
github.com/go-kit/log v0.2.0
github.com/fsnotify/fsnotify v1.5.4
github.com/go-kit/log v0.2.1
github.com/go-logfmt/logfmt v0.5.1
github.com/go-openapi/strfmt v0.21.2
github.com/go-zookeeper/zk v1.0.2
github.com/gogo/protobuf v1.3.2
github.com/golang/snappy v0.0.4
github.com/google/pprof v0.0.0-20220318212150-b2ab0324ddda
github.com/gophercloud/gophercloud v0.24.0
github.com/google/pprof v0.0.0-20220520215854-d04f2422c8a1
github.com/gophercloud/gophercloud v0.25.0
github.com/grafana/regexp v0.0.0-20220304095617-2e8d9baf4ac2
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/hashicorp/consul/api v1.12.0
github.com/hashicorp/go-hclog v0.12.2 // indirect
github.com/hashicorp/go-immutable-radix v1.2.0 // indirect
github.com/hetznercloud/hcloud-go v1.33.1
github.com/hashicorp/consul/api v1.13.0
github.com/hetznercloud/hcloud-go v1.33.2
github.com/ionos-cloud/sdk-go/v6 v6.0.5851
github.com/json-iterator/go v1.1.12
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b
github.com/linode/linodego v1.4.1
github.com/miekg/dns v1.1.48
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/linode/linodego v1.6.0
github.com/miekg/dns v1.1.49
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
github.com/oklog/run v1.1.0
github.com/oklog/ulid v1.3.1
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/alertmanager v0.24.0
github.com/prometheus/client_golang v1.12.1
github.com/prometheus/client_golang v1.12.2
github.com/prometheus/client_model v0.2.0
github.com/prometheus/common v0.34.0
github.com/prometheus/common/assets v0.1.0
@ -55,34 +48,139 @@ require (
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
github.com/stretchr/testify v1.7.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0
github.com/vultr/govultr/v2 v2.17.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.32.0
go.opentelemetry.io/otel v1.7.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.1
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.1
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.6.1
go.opentelemetry.io/otel/sdk v1.6.1
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0
go.opentelemetry.io/otel/sdk v1.7.0
go.opentelemetry.io/otel/trace v1.7.0
go.uber.org/atomic v1.9.0
go.uber.org/automaxprocs v1.5.1
go.uber.org/goleak v1.1.12
golang.org/x/net v0.0.0-20220412020605-290c469a71a5
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
golang.org/x/tools v0.1.10
google.golang.org/api v0.77.0
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4
google.golang.org/grpc v1.46.0
google.golang.org/api v0.83.0
google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8
google.golang.org/grpc v1.47.0
google.golang.org/protobuf v1.28.0
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
k8s.io/api v0.23.6
k8s.io/apimachinery v0.23.6
k8s.io/client-go v0.23.5
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.24.1
k8s.io/apimachinery v0.24.1
k8s.io/client-go v0.24.0
k8s.io/klog v1.0.0
k8s.io/klog/v2 v2.40.1
k8s.io/klog/v2 v2.60.1
)
require (
cloud.google.com/go/compute v1.6.1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/armon/go-metrics v0.3.3 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.21.2 // indirect
github.com/go-openapi/errors v0.20.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/loads v0.21.1 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.21.1 // indirect
github.com/go-openapi/validate v0.21.0 // indirect
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.2 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v0.12.2 // indirect
github.com/hashicorp/go-immutable-radix v1.2.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/serf v0.9.6 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/onsi/gomega v1.15.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.2.0 // indirect
go.mongodb.org/mongo-driver v1.8.3 // indirect
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 // indirect
go.opentelemetry.io/otel/metric v0.30.0 // indirect
go.opentelemetry.io/proto/otlp v0.16.0 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gotest.tools/v3 v3.0.3 // indirect
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
replace (
@ -98,95 +196,3 @@ exclude (
github.com/grpc-ecosystem/grpc-gateway v1.14.7
google.golang.org/api v0.30.0
)
// Exclude pre-go-mod kubernetes tags, as they are older
// than v0.x releases but are picked when we update the dependencies.
exclude (
k8s.io/client-go v1.4.0
k8s.io/client-go v1.4.0+incompatible
k8s.io/client-go v1.5.0
k8s.io/client-go v1.5.0+incompatible
k8s.io/client-go v1.5.1
k8s.io/client-go v1.5.1+incompatible
k8s.io/client-go v10.0.0+incompatible
k8s.io/client-go v11.0.0+incompatible
k8s.io/client-go v2.0.0+incompatible
k8s.io/client-go v2.0.0-alpha.1+incompatible
k8s.io/client-go v3.0.0+incompatible
k8s.io/client-go v3.0.0-beta.0+incompatible
k8s.io/client-go v4.0.0+incompatible
k8s.io/client-go v4.0.0-beta.0+incompatible
k8s.io/client-go v5.0.0+incompatible
k8s.io/client-go v5.0.1+incompatible
k8s.io/client-go v6.0.0+incompatible
k8s.io/client-go v7.0.0+incompatible
k8s.io/client-go v8.0.0+incompatible
k8s.io/client-go v9.0.0+incompatible
k8s.io/client-go v9.0.0-invalid+incompatible
)
retract (
v2.5.0+incompatible
v2.5.0-rc.2+incompatible
v2.5.0-rc.1+incompatible
v2.5.0-rc.0+incompatible
v2.4.3+incompatible
v2.4.2+incompatible
v2.4.1+incompatible
v2.4.0+incompatible
v2.4.0-rc.0+incompatible
v2.3.2+incompatible
v2.3.1+incompatible
v2.3.0+incompatible
v2.2.1+incompatible
v2.2.0+incompatible
v2.2.0-rc.1+incompatible
v2.2.0-rc.0+incompatible
v2.1.0+incompatible
v2.0.0+incompatible
v2.0.0-rc.3+incompatible
v2.0.0-rc.2+incompatible
v2.0.0-rc.1+incompatible
v2.0.0-rc.0+incompatible
v2.0.0-beta.5+incompatible
v2.0.0-beta.4+incompatible
v2.0.0-beta.3+incompatible
v2.0.0-beta.2+incompatible
v2.0.0-beta.1+incompatible
v2.0.0-beta.0+incompatible
v2.0.0-alpha.3+incompatible
v2.0.0-alpha.2+incompatible
v2.0.0-alpha.1+incompatible
v2.0.0-alpha.0+incompatible
v1.8.2
v1.8.1
v1.8.0
v1.7.2
v1.7.1
v1.7.0
v1.6.3
v1.6.2
v1.6.1
v1.6.0
v1.5.3
v1.5.2
v1.5.1
v1.5.0
v1.4.1
v1.4.0
v1.3.1
v1.3.0
v1.3.0-beta.0
v1.2.3
v1.2.2
v1.2.1
v1.2.0
v1.1.3
v1.1.2
v1.1.1
v1.1.0
v1.0.2
v1.0.1
v1.0.0
v1.0.0-rc.0
)

787
go.sum

File diff suppressed because it is too large Load diff

View file

@ -119,7 +119,7 @@ func (ls *Labels) UnmarshalYAML(unmarshal func(interface{}) error) error {
func (ls Labels) MatchLabels(on bool, names ...string) Labels {
matchedLabels := Labels{}
nameSet := map[string]struct{}{}
nameSet := make(map[string]struct{}, len(names))
for _, n := range names {
nameSet[n] = struct{}{}
}
@ -202,11 +202,11 @@ func (ls Labels) HashWithoutLabels(b []byte, names ...string) (uint64, []byte) {
return xxhash.Sum64(b), b
}
// WithLabels returns a new labels.Labels from ls that only contains labels matching names.
// BytesWithLabels is just as Bytes(), but only for labels matching names.
// 'names' have to be sorted in ascending order.
func (ls Labels) WithLabels(names ...string) Labels {
ret := make([]Label, 0, len(ls))
func (ls Labels) BytesWithLabels(buf []byte, names ...string) []byte {
b := bytes.NewBuffer(buf[:0])
b.WriteByte(labelSep)
i, j := 0, 0
for i < len(ls) && j < len(names) {
if names[j] < ls[i].Name {
@ -214,30 +214,40 @@ func (ls Labels) WithLabels(names ...string) Labels {
} else if ls[i].Name < names[j] {
i++
} else {
ret = append(ret, ls[i])
if b.Len() > 1 {
b.WriteByte(seps[0])
}
b.WriteString(ls[i].Name)
b.WriteByte(seps[0])
b.WriteString(ls[i].Value)
i++
j++
}
}
return ret
return b.Bytes()
}
// WithoutLabels returns a new labels.Labels from ls that contains labels not matching names.
// BytesWithoutLabels is just as Bytes(), but only for labels not matching names.
// 'names' have to be sorted in ascending order.
func (ls Labels) WithoutLabels(names ...string) Labels {
ret := make([]Label, 0, len(ls))
func (ls Labels) BytesWithoutLabels(buf []byte, names ...string) []byte {
b := bytes.NewBuffer(buf[:0])
b.WriteByte(labelSep)
j := 0
for i := range ls {
for j < len(names) && names[j] < ls[i].Name {
j++
}
if ls[i].Name == MetricName || (j < len(names) && ls[i].Name == names[j]) {
if j < len(names) && ls[i].Name == names[j] {
continue
}
ret = append(ret, ls[i])
if b.Len() > 1 {
b.WriteByte(seps[0])
}
b.WriteString(ls[i].Name)
b.WriteByte(seps[0])
b.WriteString(ls[i].Value)
}
return ret
return b.Bytes()
}
// Copy returns a copy of the labels.
@ -349,7 +359,7 @@ func FromStrings(ss ...string) Labels {
if len(ss)%2 != 0 {
panic("invalid number of strings")
}
var res Labels
res := make(Labels, 0, len(ss)/2)
for i := 0; i < len(ss); i += 2 {
res = append(res, Label{Name: ss[i], Value: ss[i+1]})
}
@ -426,6 +436,20 @@ func (b *Builder) Del(ns ...string) *Builder {
return b
}
// Keep removes all labels from the base except those with the given names.
func (b *Builder) Keep(ns ...string) *Builder {
Outer:
for _, l := range b.base {
for _, n := range ns {
if l.Name == n {
continue Outer
}
}
b.del = append(b.del, l.Name)
}
return b
}
// Set the name/value pair as a label.
func (b *Builder) Set(n, v string) *Builder {
if v == "" {
@ -467,8 +491,9 @@ Outer:
}
res = append(res, l)
}
res = append(res, b.add...)
sort.Sort(res)
if len(b.add) > 0 { // Base is already in order, so we only need to sort if we add to it.
res = append(res, b.add...)
sort.Sort(res)
}
return res
}

View file

@ -624,81 +624,94 @@ func TestLabels_Map(t *testing.T) {
require.Equal(t, map[string]string{"aaa": "111", "bbb": "222"}, Labels{{"aaa", "111"}, {"bbb", "222"}}.Map())
}
func TestLabels_WithLabels(t *testing.T) {
require.Equal(t, Labels{{"aaa", "111"}, {"bbb", "222"}}, Labels{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}}.WithLabels("aaa", "bbb"))
func TestLabels_BytesWithLabels(t *testing.T) {
require.Equal(t, Labels{{"aaa", "111"}, {"bbb", "222"}}.Bytes(nil), Labels{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}}.BytesWithLabels(nil, "aaa", "bbb"))
require.Equal(t, Labels{}.Bytes(nil), Labels{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}}.BytesWithLabels(nil))
}
func TestLabels_WithoutLabels(t *testing.T) {
require.Equal(t, Labels{{"aaa", "111"}}, Labels{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}}.WithoutLabels("bbb", "ccc"))
require.Equal(t, Labels{{"aaa", "111"}}, Labels{{"aaa", "111"}, {"bbb", "222"}, {MetricName, "333"}}.WithoutLabels("bbb"))
func TestLabels_BytesWithoutLabels(t *testing.T) {
require.Equal(t, Labels{{"aaa", "111"}}.Bytes(nil), Labels{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}}.BytesWithoutLabels(nil, "bbb", "ccc"))
require.Equal(t, Labels{{"aaa", "111"}}.Bytes(nil), Labels{{MetricName, "333"}, {"aaa", "111"}, {"bbb", "222"}}.BytesWithoutLabels(nil, MetricName, "bbb"))
}
func TestBulider_NewBulider(t *testing.T) {
require.Equal(
t,
&Builder{
base: Labels{{"aaa", "111"}},
del: []string{},
add: []Label{},
func TestBuilder(t *testing.T) {
for i, tcase := range []struct {
base Labels
del []string
keep []string
set []Label
want Labels
}{
{
base: FromStrings("aaa", "111"),
want: FromStrings("aaa", "111"),
},
NewBuilder(Labels{{"aaa", "111"}}),
)
}
func TestBuilder_Del(t *testing.T) {
require.Equal(
t,
&Builder{
del: []string{"bbb"},
add: []Label{{"aaa", "111"}, {"ccc", "333"}},
},
(&Builder{
del: []string{},
add: []Label{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}},
}).Del("bbb"),
)
}
func TestBuilder_Set(t *testing.T) {
require.Equal(
t,
&Builder{
base: Labels{{"aaa", "111"}},
del: []string{},
add: []Label{{"bbb", "222"}},
},
(&Builder{
base: Labels{{"aaa", "111"}},
del: []string{},
add: []Label{},
}).Set("bbb", "222"),
)
require.Equal(
t,
&Builder{
base: Labels{{"aaa", "111"}},
del: []string{},
add: []Label{{"bbb", "333"}},
},
(&Builder{
base: Labels{{"aaa", "111"}},
del: []string{},
add: []Label{{"bbb", "222"}},
}).Set("bbb", "333"),
)
}
func TestBuilder_Labels(t *testing.T) {
require.Equal(
t,
Labels{{"aaa", "111"}, {"ccc", "333"}, {"ddd", "444"}},
(&Builder{
base: Labels{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}},
{
base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"),
del: []string{"bbb"},
add: []Label{{"ddd", "444"}},
}).Labels(),
)
want: FromStrings("aaa", "111", "ccc", "333"),
},
{
base: nil,
set: []Label{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}},
del: []string{"bbb"},
want: FromStrings("aaa", "111", "ccc", "333"),
},
{
base: FromStrings("aaa", "111"),
set: []Label{{"bbb", "222"}},
want: FromStrings("aaa", "111", "bbb", "222"),
},
{
base: FromStrings("aaa", "111"),
set: []Label{{"bbb", "222"}, {"bbb", "333"}},
want: FromStrings("aaa", "111", "bbb", "333"),
},
{
base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"),
del: []string{"bbb"},
set: []Label{{"ddd", "444"}},
want: FromStrings("aaa", "111", "ccc", "333", "ddd", "444"),
},
{ // Blank value is interpreted as delete.
base: FromStrings("aaa", "111", "bbb", "", "ccc", "333"),
want: FromStrings("aaa", "111", "ccc", "333"),
},
{
base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"),
set: []Label{{"bbb", ""}},
want: FromStrings("aaa", "111", "ccc", "333"),
},
{
base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"),
keep: []string{"bbb"},
want: FromStrings("bbb", "222"),
},
{
base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"),
keep: []string{"aaa", "ccc"},
want: FromStrings("aaa", "111", "ccc", "333"),
},
{
base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"),
del: []string{"bbb"},
set: []Label{{"ddd", "444"}},
keep: []string{"aaa", "ddd"},
want: FromStrings("aaa", "111", "ddd", "444"),
},
} {
t.Run(fmt.Sprint(i), func(t *testing.T) {
b := NewBuilder(tcase.base)
for _, lbl := range tcase.set {
b.Set(lbl.Name, lbl.Value)
}
if len(tcase.keep) > 0 {
b.Keep(tcase.keep...)
}
b.Del(tcase.del...)
require.Equal(t, tcase.want, b.Labels())
})
}
}
func TestLabels_Hash(t *testing.T) {

View file

@ -6,6 +6,7 @@
- github.com/prometheus/prometheus/discovery/eureka
- github.com/prometheus/prometheus/discovery/gce
- github.com/prometheus/prometheus/discovery/hetzner
- github.com/prometheus/prometheus/discovery/ionos
- github.com/prometheus/prometheus/discovery/kubernetes
- github.com/prometheus/prometheus/discovery/linode
- github.com/prometheus/prometheus/discovery/marathon

View file

@ -40,6 +40,9 @@ import (
// Register hetzner plugin.
_ "github.com/prometheus/prometheus/discovery/hetzner"
// Register ionos plugin.
_ "github.com/prometheus/prometheus/discovery/ionos"
// Register kubernetes plugin.
_ "github.com/prometheus/prometheus/discovery/kubernetes"
@ -67,6 +70,9 @@ import (
// Register uyuni plugin.
_ "github.com/prometheus/prometheus/discovery/uyuni"
// Register vultr plugin.
_ "github.com/prometheus/prometheus/discovery/vultr"
// Register xds plugin.
_ "github.com/prometheus/prometheus/discovery/xds"

View file

@ -17,6 +17,7 @@ import (
"bytes"
"container/heap"
"context"
"errors"
"fmt"
"math"
"reflect"
@ -29,7 +30,6 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/grafana/regexp"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model"
"go.opentelemetry.io/otel"
@ -209,10 +209,10 @@ func contextDone(ctx context.Context, env string) error {
}
func contextErr(err error, env string) error {
switch err {
case context.Canceled:
switch {
case errors.Is(err, context.Canceled):
return ErrQueryCanceled(env)
case context.DeadlineExceeded:
case errors.Is(err, context.DeadlineExceeded):
return ErrQueryTimeout(env)
default:
return err
@ -417,7 +417,7 @@ func (ng *Engine) NewRangeQuery(q storage.Queryable, opts *QueryOpts, qs string,
return nil, err
}
if expr.Type() != parser.ValueTypeVector && expr.Type() != parser.ValueTypeScalar {
return nil, errors.Errorf("invalid expression type %q for range query, must be Scalar or instant Vector", parser.DocumentedType(expr.Type()))
return nil, fmt.Errorf("invalid expression type %q for range query, must be Scalar or instant Vector", parser.DocumentedType(expr.Type()))
}
qry, err := ng.newQuery(q, opts, expr, start, end, interval)
if err != nil {
@ -598,7 +598,7 @@ func (ng *Engine) exec(ctx context.Context, q *query) (v parser.Value, ws storag
return nil, nil, s(ctx)
}
panic(errors.Errorf("promql.Engine.exec: unhandled statement of type %T", q.Statement()))
panic(fmt.Errorf("promql.Engine.exec: unhandled statement of type %T", q.Statement()))
}
func timeMilliseconds(t time.Time) int64 {
@ -658,7 +658,7 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval
case String:
return result, warnings, nil
default:
panic(errors.Errorf("promql.Engine.exec: invalid expression type %q", val.Type()))
panic(fmt.Errorf("promql.Engine.exec: invalid expression type %q", val.Type()))
}
query.matrix = mat
@ -677,7 +677,7 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval
case parser.ValueTypeMatrix:
return mat, warnings, nil
default:
panic(errors.Errorf("promql.Engine.exec: unexpected expression type %q", s.Expr.Type()))
panic(fmt.Errorf("promql.Engine.exec: unexpected expression type %q", s.Expr.Type()))
}
}
@ -702,7 +702,7 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval
mat, ok := val.(Matrix)
if !ok {
panic(errors.Errorf("promql.Engine.exec: invalid expression type %q", val.Type()))
panic(fmt.Errorf("promql.Engine.exec: invalid expression type %q", val.Type()))
}
query.matrix = mat
@ -929,7 +929,7 @@ type evaluator struct {
// errorf causes a panic with the input formatted into an error.
func (ev *evaluator) errorf(format string, args ...interface{}) {
ev.error(errors.Errorf(format, args...))
ev.error(fmt.Errorf(format, args...))
}
// error causes a panic with the given error.
@ -951,7 +951,7 @@ func (ev *evaluator) recover(ws *storage.Warnings, errp *error) {
buf = buf[:runtime.Stack(buf, false)]
level.Error(ev.logger).Log("msg", "runtime panic in parser", "err", e, "stacktrace", string(buf))
*errp = errors.Wrap(err, "unexpected error")
*errp = fmt.Errorf("unexpected error: %w", err)
case errWithWarnings:
*errp = err.err
*ws = append(*ws, err.warnings...)
@ -1347,7 +1347,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) {
ws, err := checkAndExpandSeriesSet(ev.ctx, sel)
warnings = append(warnings, ws...)
if err != nil {
ev.error(errWithWarnings{errors.Wrap(err, "expanding series"), warnings})
ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), warnings})
}
mat := make(Matrix, 0, len(selVS.Series)) // Output matrix.
offset := durationMilliseconds(selVS.Offset)
@ -1544,7 +1544,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) {
case *parser.VectorSelector:
ws, err := checkAndExpandSeriesSet(ev.ctx, e)
if err != nil {
ev.error(errWithWarnings{errors.Wrap(err, "expanding series"), ws})
ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), ws})
}
mat := make(Matrix, 0, len(e.Series))
it := storage.NewMemoizedEmptyIterator(durationMilliseconds(ev.lookbackDelta))
@ -1660,11 +1660,11 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) {
// with changed timestamps.
mat, ok := res.(Matrix)
if !ok {
panic(errors.Errorf("unexpected result in StepInvariantExpr evaluation: %T", expr))
panic(fmt.Errorf("unexpected result in StepInvariantExpr evaluation: %T", expr))
}
for i := range mat {
if len(mat[i].Points) != 1 {
panic(errors.Errorf("unexpected number of samples"))
panic(fmt.Errorf("unexpected number of samples"))
}
for ts := ev.startTimestamp + ev.interval; ts <= ev.endTimestamp; ts = ts + ev.interval {
mat[i].Points = append(mat[i].Points, Point{
@ -1682,14 +1682,14 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) {
return res, ws
}
panic(errors.Errorf("unhandled expression of type: %T", expr))
panic(fmt.Errorf("unhandled expression of type: %T", expr))
}
// vectorSelector evaluates a *parser.VectorSelector expression.
func (ev *evaluator) vectorSelector(node *parser.VectorSelector, ts int64) (Vector, storage.Warnings) {
ws, err := checkAndExpandSeriesSet(ev.ctx, node)
if err != nil {
ev.error(errWithWarnings{errors.Wrap(err, "expanding series"), ws})
ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), ws})
}
vec := make(Vector, 0, len(node.Series))
it := storage.NewMemoizedEmptyIterator(durationMilliseconds(ev.lookbackDelta))
@ -1779,7 +1779,7 @@ func (ev *evaluator) matrixSelector(node *parser.MatrixSelector) (Matrix, storag
)
ws, err := checkAndExpandSeriesSet(ev.ctx, node)
if err != nil {
ev.error(errWithWarnings{errors.Wrap(err, "expanding series"), ws})
ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), ws})
}
series := vs.Series
@ -2094,14 +2094,16 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
}
func signatureFunc(on bool, b []byte, names ...string) func(labels.Labels) string {
sort.Strings(names)
if on {
sort.Strings(names)
return func(lset labels.Labels) string {
return string(lset.WithLabels(names...).Bytes(b))
return string(lset.BytesWithLabels(b, names...))
}
}
names = append([]string{labels.MetricName}, names...)
sort.Strings(names)
return func(lset labels.Labels) string {
return string(lset.WithoutLabels(names...).Bytes(b))
return string(lset.BytesWithoutLabels(b, names...))
}
}
@ -2136,15 +2138,7 @@ func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.V
if matching.Card == parser.CardOneToOne {
if matching.On {
Outer:
for _, l := range lhs {
for _, n := range matching.MatchingLabels {
if l.Name == n {
continue Outer
}
}
enh.lb.Del(l.Name)
}
enh.lb.Keep(matching.MatchingLabels...)
} else {
enh.lb.Del(matching.MatchingLabels...)
}
@ -2231,7 +2225,7 @@ func scalarBinop(op parser.ItemType, lhs, rhs float64) float64 {
case parser.ATAN2:
return math.Atan2(lhs, rhs)
}
panic(errors.Errorf("operator %q not allowed for Scalar operations", op))
panic(fmt.Errorf("operator %q not allowed for Scalar operations", op))
}
// vectorElemBinop evaluates a binary operation between two Vector elements.
@ -2272,7 +2266,7 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
case parser.ATAN2:
return math.Atan2(lhs, rhs), nil, true
}
panic(errors.Errorf("operator %q not allowed for operations between Vectors", op))
panic(fmt.Errorf("operator %q not allowed for operations between Vectors", op))
}
type groupedAggregation struct {
@ -2350,16 +2344,14 @@ func (ev *evaluator) aggregation(op parser.ItemType, grouping []string, without
group, ok := result[groupingKey]
// Add a new group if it doesn't exist.
if !ok {
var m labels.Labels
lb.Reset(metric)
if without {
lb.Reset(metric)
lb.Del(grouping...)
lb.Del(labels.MetricName)
m = lb.Labels()
} else {
m = metric.WithLabels(grouping...)
lb.Keep(grouping...)
}
m := lb.Labels()
newAgg := &groupedAggregation{
labels: m,
value: s.V,
@ -2511,7 +2503,7 @@ func (ev *evaluator) aggregation(op parser.ItemType, grouping []string, without
group.heap = append(group.heap, s)
default:
panic(errors.Errorf("expected aggregation operator but got %q", op))
panic(fmt.Errorf("expected aggregation operator but got %q", op))
}
}

View file

@ -684,7 +684,6 @@ load 10s
Result: Matrix{
Series{
Points: []Point{{V: 1, T: 0}, {V: 1, T: 1000}, {V: 1, T: 2000}},
Metric: labels.FromStrings(),
},
},
Start: time.Unix(0, 0),
@ -721,24 +720,26 @@ load 10s
},
}
for _, c := range cases {
var err error
var qry Query
if c.Interval == 0 {
qry, err = test.QueryEngine().NewInstantQuery(test.Queryable(), nil, c.Query, c.Start)
} else {
qry, err = test.QueryEngine().NewRangeQuery(test.Queryable(), nil, c.Query, c.Start, c.End, c.Interval)
}
require.NoError(t, err)
for i, c := range cases {
t.Run(fmt.Sprintf("%d query=%s", i, c.Query), func(t *testing.T) {
var err error
var qry Query
if c.Interval == 0 {
qry, err = test.QueryEngine().NewInstantQuery(test.Queryable(), nil, c.Query, c.Start)
} else {
qry, err = test.QueryEngine().NewRangeQuery(test.Queryable(), nil, c.Query, c.Start, c.End, c.Interval)
}
require.NoError(t, err)
res := qry.Exec(test.Context())
if c.ShouldError {
require.Error(t, res.Err, "expected error for the query %q", c.Query)
continue
}
res := qry.Exec(test.Context())
if c.ShouldError {
require.Error(t, res.Err, "expected error for the query %q", c.Query)
return
}
require.NoError(t, res.Err)
require.Equal(t, c.Result, res.Value, "query %q failed", c.Query)
require.NoError(t, res.Err)
require.Equal(t, c.Result, res.Value, "query %q failed", c.Query)
})
}
}

View file

@ -14,6 +14,7 @@
package promql
import (
"fmt"
"math"
"sort"
"strconv"
@ -21,7 +22,6 @@ import (
"time"
"github.com/grafana/regexp"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/histogram"
@ -300,10 +300,10 @@ func funcHoltWinters(vals []parser.Value, args parser.Expressions, enh *EvalNode
// Sanity check the input.
if sf <= 0 || sf >= 1 {
panic(errors.Errorf("invalid smoothing factor. Expected: 0 < sf < 1, got: %f", sf))
panic(fmt.Errorf("invalid smoothing factor. Expected: 0 < sf < 1, got: %f", sf))
}
if tf <= 0 || tf >= 1 {
panic(errors.Errorf("invalid trend factor. Expected: 0 < tf < 1, got: %f", tf))
panic(fmt.Errorf("invalid trend factor. Expected: 0 < tf < 1, got: %f", tf))
}
l := len(samples.Points)
@ -868,7 +868,6 @@ func funcPredictLinear(vals []parser.Value, args parser.Expressions, enh *EvalNo
func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
q := vals[0].(Vector)[0].V
inVec := vals[1].(Vector)
sigf := signatureFunc(false, enh.lblBuf, labels.BucketLabel)
if enh.signatureToMetricWithBuckets == nil {
enh.signatureToMetricWithBuckets = map[string]*metricWithBuckets{}
@ -896,21 +895,15 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev
// TODO(beorn7): Issue a warning somehow.
continue
}
l := sigf(sample.Metric)
// Add the metric name (which is always removed) to the signature to prevent combining multiple histograms
// with the same label set. See https://github.com/prometheus/prometheus/issues/9910
// TODO(beorn7): What's the performance impact? We might need to implement this more efficiently.
l = l + sample.Metric.Get(model.MetricNameLabel)
mb, ok := enh.signatureToMetricWithBuckets[l]
enh.lblBuf = sample.Metric.BytesWithoutLabels(enh.lblBuf, labels.BucketLabel)
mb, ok := enh.signatureToMetricWithBuckets[string(enh.lblBuf)]
if !ok {
sample.Metric = labels.NewBuilder(sample.Metric).
Del(excludedLabels...).
Labels()
mb = &metricWithBuckets{sample.Metric, nil}
enh.signatureToMetricWithBuckets[l] = mb
enh.signatureToMetricWithBuckets[string(enh.lblBuf)] = mb
}
mb.buckets = append(mb.buckets, bucket{upperBound, sample.V})
@ -920,17 +913,13 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev
for _, sample := range histogramSamples {
// We have to reconstruct the exact same signature as above for
// a conventional histogram, just ignoring any le label.
// TODO(beorn7): This replicates the inefficient implementation
// from above and has to be improved under the same
// circumstances.
l := string(sample.Metric.WithoutLabels().Bytes(enh.lblBuf))
l = l + sample.Metric.Get(model.MetricNameLabel)
if mb, ok := enh.signatureToMetricWithBuckets[l]; ok && len(mb.buckets) > 0 {
enh.lblBuf = sample.Metric.Bytes(enh.lblBuf)
if mb, ok := enh.signatureToMetricWithBuckets[string(enh.lblBuf)]; ok && len(mb.buckets) > 0 {
// At this data point, we have conventional histogram
// buckets and a native histogram with the same name and
// labels. Do not evaluate anything.
// TODO(beorn7): Issue a warning somehow.
delete(enh.signatureToMetricWithBuckets, l)
delete(enh.signatureToMetricWithBuckets, string(enh.lblBuf))
continue
}
@ -1004,10 +993,10 @@ func funcLabelReplace(vals []parser.Value, args parser.Expressions, enh *EvalNod
var err error
enh.regex, err = regexp.Compile("^(?:" + regexStr + ")$")
if err != nil {
panic(errors.Errorf("invalid regular expression in label_replace(): %s", regexStr))
panic(fmt.Errorf("invalid regular expression in label_replace(): %s", regexStr))
}
if !model.LabelNameRE.MatchString(dst) {
panic(errors.Errorf("invalid destination label name in label_replace(): %s", dst))
panic(fmt.Errorf("invalid destination label name in label_replace(): %s", dst))
}
enh.Dmn = make(map[uint64]labels.Labels, len(enh.Out))
}
@ -1069,13 +1058,13 @@ func funcLabelJoin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHe
for i := 3; i < len(args); i++ {
src := stringFromArg(args[i])
if !model.LabelName(src).IsValid() {
panic(errors.Errorf("invalid source label name in label_join(): %s", src))
panic(fmt.Errorf("invalid source label name in label_join(): %s", src))
}
srcLabels[i-3] = src
}
if !model.LabelName(dst).IsValid() {
panic(errors.Errorf("invalid destination label name in label_join(): %s", dst))
panic(fmt.Errorf("invalid destination label name in label_join(): %s", dst))
}
srcVals := make([]string, len(srcLabels))
@ -1152,6 +1141,13 @@ func funcDayOfWeek(vals []parser.Value, args parser.Expressions, enh *EvalNodeHe
})
}
// === day_of_year(v Vector) Scalar ===
func funcDayOfYear(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
return dateWrapper(vals, enh, func(t time.Time) float64 {
return float64(t.YearDay())
})
}
// === hour(v Vector) Scalar ===
func funcHour(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
return dateWrapper(vals, enh, func(t time.Time) float64 {
@ -1203,6 +1199,7 @@ var FunctionCalls = map[string]FunctionCall{
"days_in_month": funcDaysInMonth,
"day_of_month": funcDayOfMonth,
"day_of_week": funcDayOfWeek,
"day_of_year": funcDayOfYear,
"deg": funcDeg,
"delta": funcDelta,
"deriv": funcDeriv,
@ -1257,7 +1254,7 @@ var FunctionCalls = map[string]FunctionCall{
// that can also change with change in eval time.
var AtModifierUnsafeFunctions = map[string]struct{}{
// Step invariant functions.
"days_in_month": {}, "day_of_month": {}, "day_of_week": {},
"days_in_month": {}, "day_of_month": {}, "day_of_week": {}, "day_of_year": {},
"hour": {}, "minute": {}, "month": {}, "year": {},
"predict_linear": {}, "time": {},
// Uses timestamp of the argument for the result,

View file

@ -18,6 +18,7 @@
package promql
import (
"errors"
"io"
"github.com/prometheus/prometheus/model/textparse"
@ -71,7 +72,7 @@ func fuzzParseMetricWithContentType(in []byte, contentType string) int {
break
}
}
if err == io.EOF {
if errors.Is(err, io.EOF) {
err = nil
}

View file

@ -15,10 +15,9 @@ package parser
import (
"context"
"fmt"
"time"
"github.com/pkg/errors"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/storage"
)
@ -394,7 +393,7 @@ func Children(node Node) []Node {
// nothing to do
return []Node{}
default:
panic(errors.Errorf("promql.Children: unhandled node type %T", node))
panic(fmt.Errorf("promql.Children: unhandled node type %T", node))
}
}

View file

@ -132,6 +132,12 @@ var Functions = map[string]*Function{
Variadic: 1,
ReturnType: ValueTypeVector,
},
"day_of_year": {
Name: "day_of_year",
ArgTypes: []ValueType{ValueTypeVector},
Variadic: 1,
ReturnType: ValueTypeVector,
},
"deg": {
Name: "deg",
ArgTypes: []ValueType{ValueTypeVector},

View file

@ -14,6 +14,7 @@
package parser
import (
"errors"
"fmt"
"math"
"os"
@ -23,7 +24,6 @@ import (
"sync"
"time"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/labels"
@ -224,7 +224,7 @@ func ParseSeriesDesc(input string) (labels labels.Labels, values []SequenceValue
// addParseErrf formats the error and appends it to the list of parsing errors.
func (p *parser) addParseErrf(positionRange PositionRange, format string, args ...interface{}) {
p.addParseErr(positionRange, errors.Errorf(format, args...))
p.addParseErr(positionRange, fmt.Errorf(format, args...))
}
// addParseErr appends the provided error to the list of parsing errors.
@ -801,7 +801,7 @@ func MustLabelMatcher(mt labels.MatchType, name, val string) *labels.Matcher {
func MustGetFunction(name string) *Function {
f, ok := getFunction(name)
if !ok {
panic(errors.Errorf("function %q does not exist", name))
panic(fmt.Errorf("function %q does not exist", name))
}
return f
}

View file

@ -14,13 +14,13 @@
package parser
import (
"errors"
"fmt"
"math"
"strings"
"testing"
"time"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
@ -3570,7 +3570,8 @@ func TestParseExpressions(t *testing.T) {
require.Error(t, err)
require.Contains(t, err.Error(), test.errMsg, "unexpected error on input '%s', expected '%s', got '%s'", test.input, test.errMsg, err.Error())
errorList, ok := err.(ParseErrors)
var errorList ParseErrors
ok := errors.As(err, &errorList)
require.True(t, ok, "unexpected error type")

View file

@ -15,6 +15,7 @@ package promql
import (
"context"
"errors"
"fmt"
"math"
"os"
@ -23,7 +24,6 @@ import (
"time"
"github.com/grafana/regexp"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
@ -122,7 +122,7 @@ func (t *Test) ExemplarQueryable() storage.ExemplarQueryable {
func raise(line int, format string, v ...interface{}) error {
return &parser.ParseErr{
LineOffset: line,
Err: errors.Errorf(format, v...),
Err: fmt.Errorf(format, v...),
}
}
@ -146,7 +146,8 @@ func parseLoad(lines []string, i int) (int, *loadCmd, error) {
}
metric, vals, err := parser.ParseSeriesDesc(defLine)
if err != nil {
if perr, ok := err.(*parser.ParseErr); ok {
var perr *parser.ParseErr
if errors.As(err, &perr) {
perr.LineOffset = i
}
return i, nil, err
@ -168,7 +169,8 @@ func (t *Test) parseEval(lines []string, i int) (int, *evalCmd, error) {
)
_, err := parser.ParseExpr(expr)
if err != nil {
if perr, ok := err.(*parser.ParseErr); ok {
var perr *parser.ParseErr
if errors.As(err, &perr) {
perr.LineOffset = i
posOffset := parser.Pos(strings.Index(lines[i], expr))
perr.PositionRange.Start += posOffset
@ -205,7 +207,8 @@ func (t *Test) parseEval(lines []string, i int) (int, *evalCmd, error) {
}
metric, vals, err := parser.ParseSeriesDesc(defLine)
if err != nil {
if perr, ok := err.(*parser.ParseErr); ok {
var perr *parser.ParseErr
if errors.As(err, &perr) {
perr.LineOffset = i
}
return i, nil, err
@ -388,14 +391,14 @@ func (ev *evalCmd) compareResult(result parser.Value) error {
for pos, v := range val {
fp := v.Metric.Hash()
if _, ok := ev.metrics[fp]; !ok {
return errors.Errorf("unexpected metric %s in result", v.Metric)
return fmt.Errorf("unexpected metric %s in result", v.Metric)
}
exp := ev.expected[fp]
if ev.ordered && exp.pos != pos+1 {
return errors.Errorf("expected metric %s with %v at position %d but was at %d", v.Metric, exp.vals, exp.pos, pos+1)
return fmt.Errorf("expected metric %s with %v at position %d but was at %d", v.Metric, exp.vals, exp.pos, pos+1)
}
if !almostEqual(exp.vals[0].Value, v.V) {
return errors.Errorf("expected %v for %s but got %v", exp.vals[0].Value, v.Metric, v.V)
return fmt.Errorf("expected %v for %s but got %v", exp.vals[0].Value, v.Metric, v.V)
}
seen[fp] = true
@ -406,17 +409,17 @@ func (ev *evalCmd) compareResult(result parser.Value) error {
for _, ss := range val {
fmt.Println(" ", ss.Metric, ss.Point)
}
return errors.Errorf("expected metric %s with %v not found", ev.metrics[fp], expVals)
return fmt.Errorf("expected metric %s with %v not found", ev.metrics[fp], expVals)
}
}
case Scalar:
if !almostEqual(ev.expected[0].vals[0].Value, val.V) {
return errors.Errorf("expected Scalar %v but got %v", val.V, ev.expected[0].vals[0].Value)
return fmt.Errorf("expected Scalar %v but got %v", val.V, ev.expected[0].vals[0].Value)
}
default:
panic(errors.Errorf("promql.Test.compareResult: unexpected result type %T", result))
panic(fmt.Errorf("promql.Test.compareResult: unexpected result type %T", result))
}
return nil
}
@ -543,14 +546,14 @@ func (t *Test) exec(tc testCommand) error {
if cmd.fail {
continue
}
return errors.Wrapf(res.Err, "error evaluating query %q (line %d)", iq.expr, cmd.line)
return fmt.Errorf("error evaluating query %q (line %d): %w", iq.expr, cmd.line, res.Err)
}
if res.Err == nil && cmd.fail {
return errors.Errorf("expected error evaluating query %q (line %d) but got none", iq.expr, cmd.line)
return fmt.Errorf("expected error evaluating query %q (line %d) but got none", iq.expr, cmd.line)
}
err = cmd.compareResult(res.Value)
if err != nil {
return errors.Wrapf(err, "error in %s %s", cmd, iq.expr)
return fmt.Errorf("error in %s %s: %w", cmd, iq.expr, err)
}
// Check query returns same result in range mode,
@ -561,7 +564,7 @@ func (t *Test) exec(tc testCommand) error {
}
rangeRes := q.Exec(t.context)
if rangeRes.Err != nil {
return errors.Wrapf(rangeRes.Err, "error evaluating query %q (line %d) in range mode", iq.expr, cmd.line)
return fmt.Errorf("error evaluating query %q (line %d) in range mode: %w", iq.expr, cmd.line, rangeRes.Err)
}
defer q.Close()
if cmd.ordered {
@ -584,7 +587,7 @@ func (t *Test) exec(tc testCommand) error {
err = cmd.compareResult(vec)
}
if err != nil {
return errors.Wrapf(err, "error in %s %s (line %d) range mode", cmd, iq.expr, cmd.line)
return fmt.Errorf("error in %s %s (line %d) range mode: %w", cmd, iq.expr, cmd.line, err)
}
}
@ -658,7 +661,7 @@ func parseNumber(s string) (float64, error) {
f, err = strconv.ParseFloat(s, 64)
}
if err != nil {
return 0, errors.Wrap(err, "error parsing number")
return 0, fmt.Errorf("error parsing number: %w", err)
}
return f, nil
}

Some files were not shown because too many files have changed in this diff Show more