mirror of
https://github.com/prometheus/prometheus
synced 2025-02-19 05:57:01 +00:00
Merge branch 'master' into go-modules
Signed-off-by: Simon Pasquier <spasquie@redhat.com>
This commit is contained in:
commit
e3cd3ab79a
@ -16,7 +16,7 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- run: make promu
|
||||
- run: make check_license style unused staticcheck build
|
||||
- run: make check_license style unused staticcheck build check_assets
|
||||
- run: git diff --exit-code
|
||||
- store_artifacts:
|
||||
path: prometheus
|
||||
|
@ -10,5 +10,5 @@ go:
|
||||
go_import_path: github.com/prometheus/prometheus
|
||||
|
||||
script:
|
||||
- make check_license style unused test staticcheck
|
||||
- make check_license style unused test staticcheck check_assets
|
||||
- git diff --exit-code
|
||||
|
@ -1,5 +1,6 @@
|
||||
## 2.5.0-rc.0 / 2018-10-24
|
||||
## 2.5.0 / 2018-11-06
|
||||
|
||||
* [CHANGE] Group targets by scrape config instead of job name. #4806 #4526
|
||||
* [CHANGE] Marathon SD: Various changes to adapt to Marathon 1.5+. #4499
|
||||
* [CHANGE] Discovery: Split `prometheus_sd_discovered_targets` metric by scrape and notify (Alertmanager SD) as well as by section in the respective configuration. #4753
|
||||
* [FEATURE] Add OpenMetrics support for scraping (EXPERIMENTAL). #4700
|
||||
|
9
Makefile
9
Makefile
@ -29,3 +29,12 @@ DOCKER_IMAGE_NAME ?= prometheus
|
||||
assets:
|
||||
@echo ">> writing assets"
|
||||
cd $(PREFIX)/web/ui && $(GO) generate $(GOOPTS)
|
||||
@$(GOFMT) -w ./web/ui
|
||||
|
||||
.PHONY: check_assets
|
||||
check_assets: assets
|
||||
@echo ">> checking that assets are up-to-date"
|
||||
@if ! (cd $(PREFIX)/web/ui && git diff --exit-code); then \
|
||||
echo "Run 'make assets' and commit the changes to fix the error."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
@ -164,6 +164,11 @@ common-docker-tag-latest:
|
||||
promu:
|
||||
GOOS= GOARCH= GO111MODULE=off $(GO) get -u github.com/prometheus/promu
|
||||
|
||||
.PHONY: proto
|
||||
proto:
|
||||
@echo ">> generating code from proto files"
|
||||
@./scripts/genproto.sh
|
||||
|
||||
.PHONY: $(STATICCHECK)
|
||||
$(STATICCHECK):
|
||||
ifdef GO111MODULE
|
||||
|
100
RELEASE.md
Normal file
100
RELEASE.md
Normal file
@ -0,0 +1,100 @@
|
||||
# Releases
|
||||
|
||||
This page describes the release process and the currently planned schedule for upcoming releases as well as the respective release schepherds. Release shepards are chosen on a voluntary basis.
|
||||
|
||||
## Release schedule
|
||||
|
||||
Release cadence of first pre-releases being cut is 6 weeks.
|
||||
|
||||
| release series | date of first pre-release (year-month-day) | release shepard |
|
||||
|----------------|--------------------------------------------|---------------------------------------------|
|
||||
| v2.4 | 2018-09-06 | Goutham Veeramachaneni (GitHub: @gouthamve) |
|
||||
| v2.5 | 2018-10-24 | Frederic Branczyk (GitHub: @brancz) |
|
||||
| v2.6 | 2018-12-05 | Simon Pasquier (GitHub: @simonpasquier) |
|
||||
| v2.7 | 2019-01-16 | **searching for volunteer** |
|
||||
|
||||
If you are interested in volunteering please create a pull request against the [prometheus/prometheus](https://github.com/prometheus/prometheus) repository and propose yourself for the release series of your choice.
|
||||
|
||||
## Release shepard responsibilities
|
||||
|
||||
The release shepard is responsible for the entire release series of a minor release, meaning all pre- and patch releases of a minor release. The process starts with the initial pre-release.
|
||||
|
||||
* The first pre-release is scheduled according to the above schedule.
|
||||
* With the pre-release the release shepard is responsible for running and monitoring a benchmark run of the pre-release for 3 days, after which, if successful, the pre-release is promoted to a stable release.
|
||||
* Once a pre-release has been released, the `master` branch of the repository is frozen for any feature work, only critical bug fix work concerning the minor release is merged.
|
||||
* Pre-releases are done from `master`, after pre-releases are promoted to the stable release a `release-major.minor` branch is created.
|
||||
|
||||
See the next section for details on cutting an individual release.
|
||||
|
||||
## How to cut an individual release
|
||||
|
||||
These instructions are currently valid for the Prometheus server, i.e. the [prometheus/prometheus repository](https://github.com/prometheus/prometheus). Applicability to other Prometheus repositories depends on the current state of each repository. We aspire to unify the release procedures as much as possible.
|
||||
|
||||
### Branch management and versioning strategy
|
||||
|
||||
We use [Semantic Versioning](http://semver.org/).
|
||||
|
||||
We maintain a separate branch for each minor release, named `release-<major>.<minor>`, e.g. `release-1.1`, `release-2.0`.
|
||||
|
||||
The usual flow is to merge new features and changes into the master branch and to merge bug fixes into the latest release branch. Bug fixes are then merged into master from the latest release branch. The master branch should always contain all commits from the latest release branch. Whether merging master back into a release branch makes more sense is left up to the shepard's judgement.
|
||||
|
||||
If a bug fix got accidentally merged into master, cherry-pick commits have to be created in the latest release branch, which then have to be merged back into master. Try to avoid that situation.
|
||||
|
||||
Maintaining the release branches for older minor releases happens on a best effort basis.
|
||||
|
||||
### Prepare your release
|
||||
|
||||
For a patch release, work in the branch of the minor release you want to patch.
|
||||
|
||||
For a new major or minor release, create the corresponding release branch based on the master branch.
|
||||
|
||||
Bump the version in the `VERSION` file and update `CHANGELOG.md`. Do this in a proper PR as this gives others the opportunity to chime in on the release in general and on the addition to the changelog in particular.
|
||||
|
||||
Note that `CHANGELOG.md` should only document changes relevant to users of Prometheus, including external API changes, performance improvements, and new features. Do not document changes of internal interfaces, code refactorings and clean-ups, changes to the build process, etc. People interested in these are asked to refer to the git history.
|
||||
|
||||
Entries in the `CHANGELOG.md` are meant to be in this order:
|
||||
|
||||
* `[CHANGE]`
|
||||
* `[FEATURE]`
|
||||
* `[ENHANCEMENT]`
|
||||
* `[BUGFIX]`
|
||||
|
||||
### Draft the new release
|
||||
|
||||
Tag the new release with a tag named `v<major>.<minor>.<patch>`, e.g. `v2.1.3`. Note the `v` prefix.
|
||||
|
||||
You can do the tagging on the commandline:
|
||||
|
||||
```bash
|
||||
$ tag=$(< VERSION)
|
||||
$ git tag -s "v${tag}" -m "v${tag}"
|
||||
$ git push --tags
|
||||
```
|
||||
|
||||
Signing a tag with a GPG key is appreciated, but in case you can't add a GPG key to your Github account using the following [procedure](https://help.github.com/articles/generating-a-gpg-key/), you can replace the `-s` flag by `-a` flag of the `git tag` command to only annotate the tag without signing.
|
||||
|
||||
Once a tag is created, the release process through CircleCI will be triggered for this tag.
|
||||
You must create a Github Release using the UI for this tag, as otherwise CircleCI will not be able to upload tarballs for this tag. __Also, you must create the Github Release using a Github user that has granted access rights to CircleCI.__ If you did not or cannot grant those rights to your personal account, you can log in as `prombot` in an anonymous browser tab. (This will, however, prevent verified releases signed with your GPG key. For verified releases, the signing identity must be the same as the one creating the release.)
|
||||
|
||||
Go to the releases page of the project, click on the _Draft a new release_ button and select the tag you just pushed. The title of the release is formatted `x.y.z / YYYY-MM-DD`. Add the relevant part of `CHANGELOG.md` as description. Click _Save draft_ rather than _Publish release_ at this time. (This will prevent the release being visible before it has got the binaries attached to it.)
|
||||
|
||||
You can also create the tag and the Github release in one go through the Github UI by going to the releases page and then click on the `Draft a new release` button and enter your tag version.
|
||||
|
||||
Now all you can do is to wait for tarballs to be uploaded to the Github release and Docker images to be pushed to the Docker Hub and Quay.io. Once that has happened, click _Publish release_, which will make the release publicly visible and create a GitHub notification.
|
||||
|
||||
### Wrapping up
|
||||
|
||||
If the release has happened in the latest release branch, merge the changes into master.
|
||||
|
||||
To update the docs, a PR needs to be created to `prometheus/docs`. See [this PR](https://github.com/prometheus/docs/pull/952/files) for inspiration.
|
||||
|
||||
Once the binaries have been uploaded, announce the release on `prometheus-users@googlegroups.com`. Start the subject with `[ANN]`. Check out previous announcement mails for inspiration.
|
||||
|
||||
### Pre-releases
|
||||
|
||||
The following changes to the above procedures apply:
|
||||
|
||||
* In line with [Semantic Versioning](http://semver.org/), append something like `-rc.0` to the version (with the corresponding changes to the tag name, the release name etc.).
|
||||
* Tick the _This is a pre-release_ box when drafting the release in the Github UI.
|
||||
* Still update `CHANGELOG.md`, but when you cut the final release later, merge all the changes from the pre-releases into the one final update.
|
||||
|
@ -16,7 +16,6 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net/url"
|
||||
"os"
|
||||
@ -26,16 +25,13 @@ import (
|
||||
"time"
|
||||
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/prometheus/client_golang/api"
|
||||
"github.com/prometheus/client_golang/api/prometheus/v1"
|
||||
config_util "github.com/prometheus/common/config"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/common/version"
|
||||
"github.com/prometheus/prometheus/config"
|
||||
"github.com/prometheus/prometheus/pkg/rulefmt"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/util/promlint"
|
||||
)
|
||||
|
||||
@ -60,10 +56,6 @@ func main() {
|
||||
|
||||
checkMetricsCmd := checkCmd.Command("metrics", checkMetricsUsage)
|
||||
|
||||
updateCmd := app.Command("update", "Update the resources to newer formats.")
|
||||
updateRulesCmd := updateCmd.Command("rules", "Update rules from the 1.x to 2.x format.")
|
||||
ruleFilesUp := updateRulesCmd.Arg("rule-files", "The rule files to update.").Required().ExistingFiles()
|
||||
|
||||
queryCmd := app.Command("query", "Run query against a Prometheus server.")
|
||||
queryInstantCmd := queryCmd.Command("instant", "Run instant query.")
|
||||
queryServer := queryInstantCmd.Arg("server", "Prometheus server to query.").Required().String()
|
||||
@ -111,9 +103,6 @@ func main() {
|
||||
case checkMetricsCmd.FullCommand():
|
||||
os.Exit(CheckMetrics())
|
||||
|
||||
case updateRulesCmd.FullCommand():
|
||||
os.Exit(UpdateRules(*ruleFilesUp...))
|
||||
|
||||
case queryInstantCmd.FullCommand():
|
||||
os.Exit(QueryInstant(*queryServer, *queryExpr))
|
||||
|
||||
@ -296,74 +285,6 @@ func checkRules(filename string) (int, []error) {
|
||||
return numRules, nil
|
||||
}
|
||||
|
||||
// UpdateRules updates the rule files.
|
||||
func UpdateRules(files ...string) int {
|
||||
failed := false
|
||||
|
||||
for _, f := range files {
|
||||
if err := updateRules(f); err != nil {
|
||||
fmt.Fprintln(os.Stderr, " FAILED:", err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
|
||||
if failed {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func updateRules(filename string) error {
|
||||
fmt.Println("Updating", filename)
|
||||
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rules, err := promql.ParseStmts(string(content))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
yamlRG := &rulefmt.RuleGroups{
|
||||
Groups: []rulefmt.RuleGroup{{
|
||||
Name: filename,
|
||||
}},
|
||||
}
|
||||
|
||||
yamlRules := make([]rulefmt.Rule, 0, len(rules))
|
||||
|
||||
for _, rule := range rules {
|
||||
switch r := rule.(type) {
|
||||
case *promql.AlertStmt:
|
||||
yamlRules = append(yamlRules, rulefmt.Rule{
|
||||
Alert: r.Name,
|
||||
Expr: r.Expr.String(),
|
||||
For: model.Duration(r.Duration),
|
||||
Labels: r.Labels.Map(),
|
||||
Annotations: r.Annotations.Map(),
|
||||
})
|
||||
case *promql.RecordStmt:
|
||||
yamlRules = append(yamlRules, rulefmt.Rule{
|
||||
Record: r.Name,
|
||||
Expr: r.Expr.String(),
|
||||
Labels: r.Labels.Map(),
|
||||
})
|
||||
default:
|
||||
panic("unknown statement type")
|
||||
}
|
||||
}
|
||||
|
||||
yamlRG.Groups[0].Rules = yamlRules
|
||||
y, err := yaml.Marshal(yamlRG)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename+".yml", y, 0666)
|
||||
}
|
||||
|
||||
var checkMetricsUsage = strings.TrimSpace(`
|
||||
Pass Prometheus metrics over stdin to lint them for consistency and correctness.
|
||||
|
||||
|
@ -43,6 +43,7 @@ const (
|
||||
ec2LabelInstanceState = ec2Label + "instance_state"
|
||||
ec2LabelInstanceType = ec2Label + "instance_type"
|
||||
ec2LabelOwnerID = ec2Label + "owner_id"
|
||||
ec2LabelPlatform = ec2Label + "platform"
|
||||
ec2LabelPublicDNS = ec2Label + "public_dns_name"
|
||||
ec2LabelPublicIP = ec2Label + "public_ip"
|
||||
ec2LabelPrivateIP = ec2Label + "private_ip"
|
||||
@ -252,6 +253,10 @@ func (d *Discovery) refresh() (tg *targetgroup.Group, err error) {
|
||||
addr := net.JoinHostPort(*inst.PrivateIpAddress, fmt.Sprintf("%d", d.port))
|
||||
labels[model.AddressLabel] = model.LabelValue(addr)
|
||||
|
||||
if inst.Platform != nil {
|
||||
labels[ec2LabelPlatform] = model.LabelValue(*inst.Platform)
|
||||
}
|
||||
|
||||
if inst.PublicIpAddress != nil {
|
||||
labels[ec2LabelPublicIP] = model.LabelValue(*inst.PublicIpAddress)
|
||||
labels[ec2LabelPublicDNS] = model.LabelValue(*inst.PublicDnsName)
|
||||
|
@ -197,6 +197,7 @@ func TestEndpointsDiscoveryAdd(t *testing.T) {
|
||||
"__meta_kubernetes_pod_name": "testpod",
|
||||
"__meta_kubernetes_pod_ip": "1.2.3.4",
|
||||
"__meta_kubernetes_pod_ready": "unknown",
|
||||
"__meta_kubernetes_pod_phase": "",
|
||||
"__meta_kubernetes_pod_node_name": "testnode",
|
||||
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
|
||||
"__meta_kubernetes_pod_container_name": "c1",
|
||||
@ -210,6 +211,7 @@ func TestEndpointsDiscoveryAdd(t *testing.T) {
|
||||
"__meta_kubernetes_pod_name": "testpod",
|
||||
"__meta_kubernetes_pod_ip": "1.2.3.4",
|
||||
"__meta_kubernetes_pod_ready": "unknown",
|
||||
"__meta_kubernetes_pod_phase": "",
|
||||
"__meta_kubernetes_pod_node_name": "testnode",
|
||||
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
|
||||
"__meta_kubernetes_pod_container_name": "c2",
|
||||
@ -585,6 +587,7 @@ func TestEndpointsDiscoveryNamespaces(t *testing.T) {
|
||||
"__meta_kubernetes_pod_name": "testpod",
|
||||
"__meta_kubernetes_pod_ip": "4.3.2.1",
|
||||
"__meta_kubernetes_pod_ready": "unknown",
|
||||
"__meta_kubernetes_pod_phase": "",
|
||||
"__meta_kubernetes_pod_node_name": "testnode",
|
||||
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
|
||||
"__meta_kubernetes_pod_container_name": "c1",
|
||||
|
@ -142,6 +142,7 @@ const (
|
||||
podContainerPortNumberLabel = metaLabelPrefix + "pod_container_port_number"
|
||||
podContainerPortProtocolLabel = metaLabelPrefix + "pod_container_port_protocol"
|
||||
podReadyLabel = metaLabelPrefix + "pod_ready"
|
||||
podPhaseLabel = metaLabelPrefix + "pod_phase"
|
||||
podLabelPrefix = metaLabelPrefix + "pod_label_"
|
||||
podAnnotationPrefix = metaLabelPrefix + "pod_annotation_"
|
||||
podNodeNameLabel = metaLabelPrefix + "pod_node_name"
|
||||
@ -167,6 +168,7 @@ func podLabels(pod *apiv1.Pod) model.LabelSet {
|
||||
podNameLabel: lv(pod.ObjectMeta.Name),
|
||||
podIPLabel: lv(pod.Status.PodIP),
|
||||
podReadyLabel: podReady(pod),
|
||||
podPhaseLabel: lv(string(pod.Status.Phase)),
|
||||
podNodeNameLabel: lv(pod.Spec.NodeName),
|
||||
podHostIPLabel: lv(pod.Status.HostIP),
|
||||
podUID: lv(string(pod.ObjectMeta.UID)),
|
||||
|
@ -70,6 +70,7 @@ func makeMultiPortPods() *v1.Pod {
|
||||
Status: v1.PodStatus{
|
||||
PodIP: "1.2.3.4",
|
||||
HostIP: "2.3.4.5",
|
||||
Phase: "Running",
|
||||
Conditions: []v1.PodCondition{
|
||||
{
|
||||
Type: v1.PodReady,
|
||||
@ -105,6 +106,7 @@ func makePods() *v1.Pod {
|
||||
Status: v1.PodStatus{
|
||||
PodIP: "1.2.3.4",
|
||||
HostIP: "2.3.4.5",
|
||||
Phase: "Running",
|
||||
Conditions: []v1.PodCondition{
|
||||
{
|
||||
Type: v1.PodReady,
|
||||
@ -135,6 +137,7 @@ func expectedPodTargetGroups(ns string) map[string]*targetgroup.Group {
|
||||
"__meta_kubernetes_pod_ip": "1.2.3.4",
|
||||
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
|
||||
"__meta_kubernetes_pod_ready": "true",
|
||||
"__meta_kubernetes_pod_phase": "Running",
|
||||
"__meta_kubernetes_pod_uid": "abc123",
|
||||
},
|
||||
Source: key,
|
||||
@ -184,6 +187,7 @@ func TestPodDiscoveryBeforeRun(t *testing.T) {
|
||||
"__meta_kubernetes_pod_ip": "1.2.3.4",
|
||||
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
|
||||
"__meta_kubernetes_pod_ready": "true",
|
||||
"__meta_kubernetes_pod_phase": "Running",
|
||||
"__meta_kubernetes_pod_uid": "abc123",
|
||||
"__meta_kubernetes_pod_controller_kind": "testcontrollerkind",
|
||||
"__meta_kubernetes_pod_controller_name": "testcontrollername",
|
||||
|
@ -405,6 +405,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
|
||||
* `__meta_ec2_instance_state`: the state of the EC2 instance
|
||||
* `__meta_ec2_instance_type`: the type of the EC2 instance
|
||||
* `__meta_ec2_owner_id`: the ID of the AWS account that owns the EC2 instance
|
||||
* `__meta_ec2_platform`: the Operating System platform, set to 'windows' on Windows servers, absent otherwise
|
||||
* `__meta_ec2_primary_subnet_id`: the subnet ID of the primary network interface, if available
|
||||
* `__meta_ec2_private_ip`: the private IP address of the instance, if present
|
||||
* `__meta_ec2_public_dns_name`: the public DNS name of the instance, if available
|
||||
@ -716,6 +717,8 @@ Available meta labels:
|
||||
* `__meta_kubernetes_pod_container_port_number`: Number of the container port.
|
||||
* `__meta_kubernetes_pod_container_port_protocol`: Protocol of the container port.
|
||||
* `__meta_kubernetes_pod_ready`: Set to `true` or `false` for the pod's ready state.
|
||||
* `__meta_kubernetes_pod_phase`: Set to `Pending`, `Running`, `Succeeded`, `Failed` or `Unknown`
|
||||
in the [lifecycle](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase).
|
||||
* `__meta_kubernetes_pod_node_name`: The name of the node the pod is scheduled onto.
|
||||
* `__meta_kubernetes_pod_host_ip`: The current host IP of the pod object.
|
||||
* `__meta_kubernetes_pod_uid`: The UID of the pod object.
|
||||
|
67
pkg/modtimevfs/modtimevfs.go
Normal file
67
pkg/modtimevfs/modtimevfs.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2018 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 modtimevfs implements a virtual file system that returns a fixed
|
||||
// modification time for all files and directories.
|
||||
package modtimevfs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type timefs struct {
|
||||
fs http.FileSystem
|
||||
t time.Time
|
||||
}
|
||||
|
||||
// New returns a file system that returns constant modification time for all files.
|
||||
func New(fs http.FileSystem, t time.Time) http.FileSystem {
|
||||
return &timefs{fs: fs, t: t}
|
||||
}
|
||||
|
||||
type file struct {
|
||||
http.File
|
||||
os.FileInfo
|
||||
t time.Time
|
||||
}
|
||||
|
||||
func (t *timefs) Open(name string) (http.File, error) {
|
||||
f, err := t.fs.Open(name)
|
||||
if err != nil {
|
||||
return f, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
fstat, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &file{f, fstat, t.t}, nil
|
||||
}
|
||||
|
||||
// Stat implements the http.File interface.
|
||||
func (f *file) Stat() (os.FileInfo, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// ModTime implements the os.FileInfo interface.
|
||||
func (f *file) ModTime() time.Time {
|
||||
return f.t
|
||||
}
|
@ -2,13 +2,8 @@ The compiled protobufs are version controlled and you won't normally need to
|
||||
re-compile them when building Prometheus.
|
||||
|
||||
If however you have modified the defs and do need to re-compile, run
|
||||
`./scripts/genproto.sh` from the parent dir.
|
||||
`make proto` from the parent dir.
|
||||
|
||||
In order for the script to run, you'll need `protoc` (version 3.5) in your
|
||||
PATH, and the following Go packages installed:
|
||||
In order for the script to run, you'll need `protoc` (version 3.5.1) in your
|
||||
PATH.
|
||||
|
||||
- github.com/gogo/protobuf
|
||||
- github.com/gogo/protobuf/protoc-gen-gogofast
|
||||
- github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/
|
||||
- github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
|
||||
- golang.org/x/tools/cmd/goimports
|
||||
|
@ -330,24 +330,6 @@ func (m *QueryResult) MarshalTo(dAtA []byte) (int, error) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeFixed64Remote(dAtA []byte, offset int, v uint64) int {
|
||||
dAtA[offset] = uint8(v)
|
||||
dAtA[offset+1] = uint8(v >> 8)
|
||||
dAtA[offset+2] = uint8(v >> 16)
|
||||
dAtA[offset+3] = uint8(v >> 24)
|
||||
dAtA[offset+4] = uint8(v >> 32)
|
||||
dAtA[offset+5] = uint8(v >> 40)
|
||||
dAtA[offset+6] = uint8(v >> 48)
|
||||
dAtA[offset+7] = uint8(v >> 56)
|
||||
return offset + 8
|
||||
}
|
||||
func encodeFixed32Remote(dAtA []byte, offset int, v uint32) int {
|
||||
dAtA[offset] = uint8(v)
|
||||
dAtA[offset+1] = uint8(v >> 8)
|
||||
dAtA[offset+2] = uint8(v >> 16)
|
||||
dAtA[offset+3] = uint8(v >> 24)
|
||||
return offset + 4
|
||||
}
|
||||
func encodeVarintRemote(dAtA []byte, offset int, v uint64) int {
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
|
@ -11,12 +11,10 @@ import _ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
|
||||
import time "time"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
import context "golang.org/x/net/context"
|
||||
import grpc "google.golang.org/grpc"
|
||||
|
||||
import github_com_gogo_protobuf_types "github.com/gogo/protobuf/types"
|
||||
import types "github.com/gogo/protobuf/types"
|
||||
|
||||
import io "io"
|
||||
|
||||
@ -338,8 +336,8 @@ func (m *SeriesDeleteRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||
if m.MinTime != nil {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintRpc(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(*m.MinTime)))
|
||||
n1, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.MinTime, dAtA[i:])
|
||||
i = encodeVarintRpc(dAtA, i, uint64(types.SizeOfStdTime(*m.MinTime)))
|
||||
n1, err := types.StdTimeMarshalTo(*m.MinTime, dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -348,8 +346,8 @@ func (m *SeriesDeleteRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||
if m.MaxTime != nil {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintRpc(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(*m.MaxTime)))
|
||||
n2, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.MaxTime, dAtA[i:])
|
||||
i = encodeVarintRpc(dAtA, i, uint64(types.SizeOfStdTime(*m.MaxTime)))
|
||||
n2, err := types.StdTimeMarshalTo(*m.MaxTime, dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -388,24 +386,6 @@ func (m *SeriesDeleteResponse) MarshalTo(dAtA []byte) (int, error) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeFixed64Rpc(dAtA []byte, offset int, v uint64) int {
|
||||
dAtA[offset] = uint8(v)
|
||||
dAtA[offset+1] = uint8(v >> 8)
|
||||
dAtA[offset+2] = uint8(v >> 16)
|
||||
dAtA[offset+3] = uint8(v >> 24)
|
||||
dAtA[offset+4] = uint8(v >> 32)
|
||||
dAtA[offset+5] = uint8(v >> 40)
|
||||
dAtA[offset+6] = uint8(v >> 48)
|
||||
dAtA[offset+7] = uint8(v >> 56)
|
||||
return offset + 8
|
||||
}
|
||||
func encodeFixed32Rpc(dAtA []byte, offset int, v uint32) int {
|
||||
dAtA[offset] = uint8(v)
|
||||
dAtA[offset+1] = uint8(v >> 8)
|
||||
dAtA[offset+2] = uint8(v >> 16)
|
||||
dAtA[offset+3] = uint8(v >> 24)
|
||||
return offset + 4
|
||||
}
|
||||
func encodeVarintRpc(dAtA []byte, offset int, v uint64) int {
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
@ -450,11 +430,11 @@ func (m *SeriesDeleteRequest) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if m.MinTime != nil {
|
||||
l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.MinTime)
|
||||
l = types.SizeOfStdTime(*m.MinTime)
|
||||
n += 1 + l + sovRpc(uint64(l))
|
||||
}
|
||||
if m.MaxTime != nil {
|
||||
l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.MaxTime)
|
||||
l = types.SizeOfStdTime(*m.MaxTime)
|
||||
n += 1 + l + sovRpc(uint64(l))
|
||||
}
|
||||
if len(m.Matchers) > 0 {
|
||||
@ -792,7 +772,7 @@ func (m *SeriesDeleteRequest) Unmarshal(dAtA []byte) error {
|
||||
if m.MinTime == nil {
|
||||
m.MinTime = new(time.Time)
|
||||
}
|
||||
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.MinTime, dAtA[iNdEx:postIndex]); err != nil {
|
||||
if err := types.StdTimeUnmarshal(m.MinTime, dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
@ -825,7 +805,7 @@ func (m *SeriesDeleteRequest) Unmarshal(dAtA []byte) error {
|
||||
if m.MaxTime == nil {
|
||||
m.MaxTime = new(time.Time)
|
||||
}
|
||||
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.MaxTime, dAtA[iNdEx:postIndex]); err != nil {
|
||||
if err := types.StdTimeUnmarshal(m.MaxTime, dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
|
@ -58,7 +58,7 @@ func request_Admin_DeleteSeries_0(ctx context.Context, marshaler runtime.Marshal
|
||||
var protoReq SeriesDeleteRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import binary "encoding/binary"
|
||||
|
||||
import io "io"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@ -229,7 +231,8 @@ func (m *Sample) MarshalTo(dAtA []byte) (int, error) {
|
||||
if m.Value != 0 {
|
||||
dAtA[i] = 0x9
|
||||
i++
|
||||
i = encodeFixed64Types(dAtA, i, uint64(math.Float64bits(float64(m.Value))))
|
||||
binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Value))))
|
||||
i += 8
|
||||
}
|
||||
if m.Timestamp != 0 {
|
||||
dAtA[i] = 0x10
|
||||
@ -415,24 +418,6 @@ func (m *ReadHints) MarshalTo(dAtA []byte) (int, error) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeFixed64Types(dAtA []byte, offset int, v uint64) int {
|
||||
dAtA[offset] = uint8(v)
|
||||
dAtA[offset+1] = uint8(v >> 8)
|
||||
dAtA[offset+2] = uint8(v >> 16)
|
||||
dAtA[offset+3] = uint8(v >> 24)
|
||||
dAtA[offset+4] = uint8(v >> 32)
|
||||
dAtA[offset+5] = uint8(v >> 40)
|
||||
dAtA[offset+6] = uint8(v >> 48)
|
||||
dAtA[offset+7] = uint8(v >> 56)
|
||||
return offset + 8
|
||||
}
|
||||
func encodeFixed32Types(dAtA []byte, offset int, v uint32) int {
|
||||
dAtA[offset] = uint8(v)
|
||||
dAtA[offset+1] = uint8(v >> 8)
|
||||
dAtA[offset+2] = uint8(v >> 16)
|
||||
dAtA[offset+3] = uint8(v >> 24)
|
||||
return offset + 4
|
||||
}
|
||||
func encodeVarintTypes(dAtA []byte, offset int, v uint64) int {
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
@ -584,15 +569,8 @@ func (m *Sample) Unmarshal(dAtA []byte) error {
|
||||
if (iNdEx + 8) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
|
||||
iNdEx += 8
|
||||
v = uint64(dAtA[iNdEx-8])
|
||||
v |= uint64(dAtA[iNdEx-7]) << 8
|
||||
v |= uint64(dAtA[iNdEx-6]) << 16
|
||||
v |= uint64(dAtA[iNdEx-5]) << 24
|
||||
v |= uint64(dAtA[iNdEx-4]) << 32
|
||||
v |= uint64(dAtA[iNdEx-3]) << 40
|
||||
v |= uint64(dAtA[iNdEx-2]) << 48
|
||||
v |= uint64(dAtA[iNdEx-1]) << 56
|
||||
m.Value = float64(math.Float64frombits(v))
|
||||
case 2:
|
||||
if wireType != 0 {
|
||||
|
@ -48,18 +48,6 @@ type Statement interface {
|
||||
stmt()
|
||||
}
|
||||
|
||||
// Statements is a list of statement nodes that implements Node.
|
||||
type Statements []Statement
|
||||
|
||||
// AlertStmt represents an added alert rule.
|
||||
type AlertStmt struct {
|
||||
Name string
|
||||
Expr Expr
|
||||
Duration time.Duration
|
||||
Labels labels.Labels
|
||||
Annotations labels.Labels
|
||||
}
|
||||
|
||||
// EvalStmt holds an expression and information on the range it should
|
||||
// be evaluated on.
|
||||
type EvalStmt struct {
|
||||
@ -72,16 +60,7 @@ type EvalStmt struct {
|
||||
Interval time.Duration
|
||||
}
|
||||
|
||||
// RecordStmt represents an added recording rule.
|
||||
type RecordStmt struct {
|
||||
Name string
|
||||
Expr Expr
|
||||
Labels labels.Labels
|
||||
}
|
||||
|
||||
func (*AlertStmt) stmt() {}
|
||||
func (*EvalStmt) stmt() {}
|
||||
func (*RecordStmt) stmt() {}
|
||||
func (*EvalStmt) stmt() {}
|
||||
|
||||
// Expr is a generic interface for all expression types.
|
||||
type Expr interface {
|
||||
@ -257,27 +236,11 @@ func Walk(v Visitor, node Node, path []Node) error {
|
||||
path = append(path, node)
|
||||
|
||||
switch n := node.(type) {
|
||||
case Statements:
|
||||
for _, s := range n {
|
||||
if err := Walk(v, s, path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *AlertStmt:
|
||||
if err := Walk(v, n.Expr, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *EvalStmt:
|
||||
if err := Walk(v, n.Expr, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *RecordStmt:
|
||||
if err := Walk(v, n.Expr, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case Expressions:
|
||||
for _, e := range n {
|
||||
if err := Walk(v, e, path); err != nil {
|
||||
|
@ -183,11 +183,6 @@ const (
|
||||
|
||||
keywordsStart
|
||||
// Keywords.
|
||||
itemAlert
|
||||
itemIf
|
||||
itemFor
|
||||
itemLabels
|
||||
itemAnnotations
|
||||
itemOffset
|
||||
itemBy
|
||||
itemWithout
|
||||
@ -219,11 +214,6 @@ var key = map[string]ItemType{
|
||||
"quantile": itemQuantile,
|
||||
|
||||
// Keywords.
|
||||
"alert": itemAlert,
|
||||
"if": itemIf,
|
||||
"for": itemFor,
|
||||
"labels": itemLabels,
|
||||
"annotations": itemAnnotations,
|
||||
"offset": itemOffset,
|
||||
"by": itemBy,
|
||||
"without": itemWithout,
|
||||
|
@ -249,21 +249,6 @@ var tests = []struct {
|
||||
},
|
||||
// Test keywords.
|
||||
{
|
||||
input: "alert",
|
||||
expected: []item{{itemAlert, 0, "alert"}},
|
||||
}, {
|
||||
input: "if",
|
||||
expected: []item{{itemIf, 0, "if"}},
|
||||
}, {
|
||||
input: "for",
|
||||
expected: []item{{itemFor, 0, "for"}},
|
||||
}, {
|
||||
input: "labels",
|
||||
expected: []item{{itemLabels, 0, "labels"}},
|
||||
}, {
|
||||
input: "annotations",
|
||||
expected: []item{{itemAnnotations, 0, "annotations"}},
|
||||
}, {
|
||||
input: "offset",
|
||||
expected: []item{{itemOffset, 0, "offset"}},
|
||||
}, {
|
||||
|
130
promql/parse.go
130
promql/parse.go
@ -51,18 +51,6 @@ func (e *ParseErr) Error() string {
|
||||
return fmt.Sprintf("parse error at line %d, char %d: %s", e.Line, e.Pos, e.Err)
|
||||
}
|
||||
|
||||
// ParseStmts parses the input and returns the resulting statements or any occurring error.
|
||||
func ParseStmts(input string) (Statements, error) {
|
||||
p := newParser(input)
|
||||
|
||||
stmts, err := p.parseStmts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = p.typecheck(stmts)
|
||||
return stmts, err
|
||||
}
|
||||
|
||||
// ParseExpr returns the expression parsed from the input.
|
||||
func ParseExpr(input string) (Expr, error) {
|
||||
p := newParser(input)
|
||||
@ -112,20 +100,6 @@ func newParser(input string) *parser {
|
||||
return p
|
||||
}
|
||||
|
||||
// parseStmts parses a sequence of statements from the input.
|
||||
func (p *parser) parseStmts() (stmts Statements, err error) {
|
||||
defer p.recover(&err)
|
||||
stmts = Statements{}
|
||||
|
||||
for p.peek().typ != itemEOF {
|
||||
if p.peek().typ == itemComment {
|
||||
continue
|
||||
}
|
||||
stmts = append(stmts, p.stmt())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// parseExpr parses a single expression from the input.
|
||||
func (p *parser) parseExpr() (expr Expr, err error) {
|
||||
defer p.recover(&err)
|
||||
@ -365,93 +339,6 @@ func (p *parser) recover(errp *error) {
|
||||
}
|
||||
}
|
||||
|
||||
// stmt parses any statement.
|
||||
//
|
||||
// alertStatement | recordStatement
|
||||
//
|
||||
func (p *parser) stmt() Statement {
|
||||
switch tok := p.peek(); tok.typ {
|
||||
case itemAlert:
|
||||
return p.alertStmt()
|
||||
case itemIdentifier, itemMetricIdentifier:
|
||||
return p.recordStmt()
|
||||
}
|
||||
p.errorf("no valid statement detected")
|
||||
return nil
|
||||
}
|
||||
|
||||
// alertStmt parses an alert rule.
|
||||
//
|
||||
// ALERT name IF expr [FOR duration]
|
||||
// [LABELS label_set]
|
||||
// [ANNOTATIONS label_set]
|
||||
//
|
||||
func (p *parser) alertStmt() *AlertStmt {
|
||||
const ctx = "alert statement"
|
||||
|
||||
p.expect(itemAlert, ctx)
|
||||
name := p.expect(itemIdentifier, ctx)
|
||||
// Alerts require a Vector typed expression.
|
||||
p.expect(itemIf, ctx)
|
||||
expr := p.expr()
|
||||
|
||||
// Optional for clause.
|
||||
var (
|
||||
duration time.Duration
|
||||
err error
|
||||
)
|
||||
if p.peek().typ == itemFor {
|
||||
p.next()
|
||||
dur := p.expect(itemDuration, ctx)
|
||||
duration, err = parseDuration(dur.val)
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
lset labels.Labels
|
||||
annotations labels.Labels
|
||||
)
|
||||
if p.peek().typ == itemLabels {
|
||||
p.expect(itemLabels, ctx)
|
||||
lset = p.labelSet()
|
||||
}
|
||||
if p.peek().typ == itemAnnotations {
|
||||
p.expect(itemAnnotations, ctx)
|
||||
annotations = p.labelSet()
|
||||
}
|
||||
|
||||
return &AlertStmt{
|
||||
Name: name.val,
|
||||
Expr: expr,
|
||||
Duration: duration,
|
||||
Labels: lset,
|
||||
Annotations: annotations,
|
||||
}
|
||||
}
|
||||
|
||||
// recordStmt parses a recording rule.
|
||||
func (p *parser) recordStmt() *RecordStmt {
|
||||
const ctx = "record statement"
|
||||
|
||||
name := p.expectOneOf(itemIdentifier, itemMetricIdentifier, ctx).val
|
||||
|
||||
var lset labels.Labels
|
||||
if p.peek().typ == itemLeftBrace {
|
||||
lset = p.labelSet()
|
||||
}
|
||||
|
||||
p.expect(itemAssign, ctx)
|
||||
expr := p.expr()
|
||||
|
||||
return &RecordStmt{
|
||||
Name: name,
|
||||
Labels: lset,
|
||||
Expr: expr,
|
||||
}
|
||||
}
|
||||
|
||||
// expr parses any expression.
|
||||
func (p *parser) expr() Expr {
|
||||
// Parse the starting expression.
|
||||
@ -1009,9 +896,9 @@ func (p *parser) expectType(node Node, want ValueType, context string) {
|
||||
// them, but the costs are small and might reveal errors when making changes.
|
||||
func (p *parser) checkType(node Node) (typ ValueType) {
|
||||
// For expressions the type is determined by their Type function.
|
||||
// Statements and lists do not have a type but are not invalid either.
|
||||
// Lists do not have a type but are not invalid either.
|
||||
switch n := node.(type) {
|
||||
case Statements, Expressions, Statement:
|
||||
case Expressions:
|
||||
typ = ValueTypeNone
|
||||
case Expr:
|
||||
typ = n.Type()
|
||||
@ -1022,25 +909,12 @@ func (p *parser) checkType(node Node) (typ ValueType) {
|
||||
// Recursively check correct typing for child nodes and raise
|
||||
// errors in case of bad typing.
|
||||
switch n := node.(type) {
|
||||
case Statements:
|
||||
for _, s := range n {
|
||||
p.expectType(s, ValueTypeNone, "statement list")
|
||||
}
|
||||
case *AlertStmt:
|
||||
p.expectType(n.Expr, ValueTypeVector, "alert statement")
|
||||
|
||||
case *EvalStmt:
|
||||
ty := p.checkType(n.Expr)
|
||||
if ty == ValueTypeNone {
|
||||
p.errorf("evaluation statement must have a valid expression type but got %s", documentedType(ty))
|
||||
}
|
||||
|
||||
case *RecordStmt:
|
||||
ty := p.checkType(n.Expr)
|
||||
if ty != ValueTypeVector && ty != ValueTypeScalar {
|
||||
p.errorf("record statement must have a valid expression of type instant vector or scalar but got %s", documentedType(ty))
|
||||
}
|
||||
|
||||
case Expressions:
|
||||
for _, e := range n {
|
||||
ty := p.checkType(e)
|
||||
|
@ -1341,10 +1341,6 @@ var testExpr = []struct {
|
||||
input: "e-+=/(0)",
|
||||
fail: true,
|
||||
errMsg: `no valid expression found`,
|
||||
}, {
|
||||
input: "-If",
|
||||
fail: true,
|
||||
errMsg: `no valid expression found`,
|
||||
},
|
||||
// String quoting and escape sequence interpretation tests.
|
||||
{
|
||||
@ -1445,241 +1441,6 @@ func TestNaNExpression(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var testStatement = []struct {
|
||||
input string
|
||||
expected Statements
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
// Test a file-like input.
|
||||
input: `
|
||||
# A simple test recording rule.
|
||||
dc:http_request:rate5m = sum(rate(http_request_count[5m])) by (dc)
|
||||
|
||||
# A simple test alerting rule.
|
||||
ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5m
|
||||
LABELS {
|
||||
service = "testservice"
|
||||
# ... more fields here ...
|
||||
}
|
||||
ANNOTATIONS {
|
||||
summary = "Global request rate low",
|
||||
description = "The global request rate is low"
|
||||
}
|
||||
|
||||
foo = bar{label1="value1"}
|
||||
|
||||
ALERT BazAlert IF foo > 10
|
||||
ANNOTATIONS {
|
||||
description = "BazAlert",
|
||||
runbook = "http://my.url",
|
||||
summary = "Baz",
|
||||
}
|
||||
`,
|
||||
expected: Statements{
|
||||
&RecordStmt{
|
||||
Name: "dc:http_request:rate5m",
|
||||
Expr: &AggregateExpr{
|
||||
Op: itemSum,
|
||||
Grouping: []string{"dc"},
|
||||
Expr: &Call{
|
||||
Func: mustGetFunction("rate"),
|
||||
Args: Expressions{
|
||||
&MatrixSelector{
|
||||
Name: "http_request_count",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "http_request_count"),
|
||||
},
|
||||
Range: 5 * time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Labels: nil,
|
||||
},
|
||||
&AlertStmt{
|
||||
Name: "GlobalRequestRateLow",
|
||||
Expr: &ParenExpr{&BinaryExpr{
|
||||
Op: itemLSS,
|
||||
LHS: &VectorSelector{
|
||||
Name: "dc:http_request:rate5m",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "dc:http_request:rate5m"),
|
||||
},
|
||||
},
|
||||
RHS: &NumberLiteral{10000},
|
||||
}},
|
||||
Labels: labels.FromStrings("service", "testservice"),
|
||||
Duration: 5 * time.Minute,
|
||||
Annotations: labels.FromStrings(
|
||||
"summary", "Global request rate low",
|
||||
"description", "The global request rate is low",
|
||||
),
|
||||
},
|
||||
&RecordStmt{
|
||||
Name: "foo",
|
||||
Expr: &VectorSelector{
|
||||
Name: "bar",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, "label1", "value1"),
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
|
||||
},
|
||||
},
|
||||
},
|
||||
&AlertStmt{
|
||||
Name: "BazAlert",
|
||||
Expr: &BinaryExpr{
|
||||
Op: itemGTR,
|
||||
LHS: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
|
||||
},
|
||||
},
|
||||
RHS: &NumberLiteral{10},
|
||||
},
|
||||
Annotations: labels.FromStrings(
|
||||
"summary", "Baz",
|
||||
"description", "BazAlert",
|
||||
"runbook", "http://my.url",
|
||||
),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
input: `foo{x="", a="z"} = bar{a="b", x=~"y"}`,
|
||||
expected: Statements{
|
||||
&RecordStmt{
|
||||
Name: "foo",
|
||||
Expr: &VectorSelector{
|
||||
Name: "bar",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, "a", "b"),
|
||||
mustLabelMatcher(labels.MatchRegexp, "x", "y"),
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
|
||||
},
|
||||
},
|
||||
Labels: labels.FromStrings("x", "", "a", "z"),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
input: `ALERT SomeName IF some_metric > 1
|
||||
LABELS {}
|
||||
ANNOTATIONS {
|
||||
summary = "Global request rate low",
|
||||
description = "The global request rate is low",
|
||||
}
|
||||
`,
|
||||
expected: Statements{
|
||||
&AlertStmt{
|
||||
Name: "SomeName",
|
||||
Expr: &BinaryExpr{
|
||||
Op: itemGTR,
|
||||
LHS: &VectorSelector{
|
||||
Name: "some_metric",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
|
||||
},
|
||||
},
|
||||
RHS: &NumberLiteral{1},
|
||||
},
|
||||
Labels: labels.Labels{},
|
||||
Annotations: labels.FromStrings(
|
||||
"summary", "Global request rate low",
|
||||
"description", "The global request rate is low",
|
||||
),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
input: `
|
||||
# A simple test alerting rule.
|
||||
ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5
|
||||
LABELS {
|
||||
service = "testservice"
|
||||
# ... more fields here ...
|
||||
}
|
||||
ANNOTATIONS {
|
||||
summary = "Global request rate low"
|
||||
description = "The global request rate is low"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
}, {
|
||||
input: "",
|
||||
expected: Statements{},
|
||||
}, {
|
||||
input: "foo = time()",
|
||||
expected: Statements{
|
||||
&RecordStmt{
|
||||
Name: "foo",
|
||||
Expr: &Call{Func: mustGetFunction("time")},
|
||||
Labels: nil,
|
||||
}},
|
||||
}, {
|
||||
input: "foo = 1",
|
||||
expected: Statements{
|
||||
&RecordStmt{
|
||||
Name: "foo",
|
||||
Expr: &NumberLiteral{1},
|
||||
Labels: nil,
|
||||
}},
|
||||
}, {
|
||||
input: "foo = bar[5m]",
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo = "test"`,
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo = `,
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo{a!="b"} = bar`,
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo{a=~"b"} = bar`,
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo{a!~"b"} = bar`,
|
||||
fail: true,
|
||||
},
|
||||
// Fuzzing regression tests.
|
||||
{
|
||||
input: `I=-/`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
input: `I=3E8/-=`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
input: `M=-=-0-0`,
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestParseStatements(t *testing.T) {
|
||||
for _, test := range testStatement {
|
||||
stmts, err := ParseStmts(test.input)
|
||||
|
||||
// Unexpected errors are always caused by a bug.
|
||||
if err == errUnexpected {
|
||||
t.Fatalf("unexpected error occurred")
|
||||
}
|
||||
|
||||
if !test.fail && err != nil {
|
||||
t.Errorf("error in input: \n\n%s\n", test.input)
|
||||
t.Fatalf("could not parse: %s", err)
|
||||
}
|
||||
if test.fail && err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(stmts, test.expected) {
|
||||
t.Errorf("error in input: \n\n%s\n", test.input)
|
||||
t.Fatalf("no match\n\nexpected:\n%s\ngot: \n%s\n", Tree(test.expected), Tree(stmts))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mustLabelMatcher(mt labels.MatchType, name, val string) *labels.Matcher {
|
||||
m, err := labels.NewMatcher(mt, name, val)
|
||||
if err != nil {
|
||||
|
@ -34,30 +34,14 @@ func tree(node Node, level string) string {
|
||||
}
|
||||
typs := strings.Split(fmt.Sprintf("%T", node), ".")[1]
|
||||
|
||||
var t string
|
||||
// Only print the number of statements for readability.
|
||||
if stmts, ok := node.(Statements); ok {
|
||||
t = fmt.Sprintf("%s |---- %s :: %d\n", level, typs, len(stmts))
|
||||
} else {
|
||||
t = fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node)
|
||||
}
|
||||
t := fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node)
|
||||
|
||||
level += " · · ·"
|
||||
|
||||
switch n := node.(type) {
|
||||
case Statements:
|
||||
for _, s := range n {
|
||||
t += tree(s, level)
|
||||
}
|
||||
case *AlertStmt:
|
||||
t += tree(n.Expr, level)
|
||||
|
||||
case *EvalStmt:
|
||||
t += tree(n.Expr, level)
|
||||
|
||||
case *RecordStmt:
|
||||
t += tree(n.Expr, level)
|
||||
|
||||
case Expressions:
|
||||
for _, e := range n {
|
||||
t += tree(e, level)
|
||||
@ -87,41 +71,10 @@ func tree(node Node, level string) string {
|
||||
return t
|
||||
}
|
||||
|
||||
func (stmts Statements) String() (s string) {
|
||||
if len(stmts) == 0 {
|
||||
return ""
|
||||
}
|
||||
for _, stmt := range stmts {
|
||||
s += stmt.String()
|
||||
s += "\n\n"
|
||||
}
|
||||
return s[:len(s)-2]
|
||||
}
|
||||
|
||||
func (node *AlertStmt) String() string {
|
||||
s := fmt.Sprintf("ALERT %s", node.Name)
|
||||
s += fmt.Sprintf("\n\tIF %s", node.Expr)
|
||||
if node.Duration > 0 {
|
||||
s += fmt.Sprintf("\n\tFOR %s", model.Duration(node.Duration))
|
||||
}
|
||||
if len(node.Labels) > 0 {
|
||||
s += fmt.Sprintf("\n\tLABELS %s", node.Labels)
|
||||
}
|
||||
if len(node.Annotations) > 0 {
|
||||
s += fmt.Sprintf("\n\tANNOTATIONS %s", node.Annotations)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (node *EvalStmt) String() string {
|
||||
return "EVAL " + node.Expr.String()
|
||||
}
|
||||
|
||||
func (node *RecordStmt) String() string {
|
||||
s := fmt.Sprintf("%s%s = %s", node.Name, node.Labels, node.Expr)
|
||||
return s
|
||||
}
|
||||
|
||||
func (es Expressions) String() (s string) {
|
||||
if len(es) == 0 {
|
||||
return ""
|
||||
|
@ -15,40 +15,8 @@ package promql
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
)
|
||||
|
||||
func TestStatementString(t *testing.T) {
|
||||
in := &AlertStmt{
|
||||
Name: "FooAlert",
|
||||
Expr: &BinaryExpr{
|
||||
Op: itemGTR,
|
||||
LHS: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
{Type: labels.MatchEqual, Name: labels.MetricName, Value: "bar"},
|
||||
},
|
||||
},
|
||||
RHS: &NumberLiteral{10},
|
||||
},
|
||||
Duration: 5 * time.Minute,
|
||||
Labels: labels.FromStrings("foo", "bar"),
|
||||
Annotations: labels.FromStrings("notify", "team-a"),
|
||||
}
|
||||
|
||||
expected := `ALERT FooAlert
|
||||
IF foo > 10
|
||||
FOR 5m
|
||||
LABELS {foo="bar"}
|
||||
ANNOTATIONS {notify="team-a"}`
|
||||
|
||||
if in.String() != expected {
|
||||
t.Fatalf("expected:\n%s\ngot:\n%s\n", expected, in.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestExprString(t *testing.T) {
|
||||
// A list of valid expressions that are expected to be
|
||||
// returned as out when calling String(). If out is empty the output
|
||||
@ -129,35 +97,3 @@ func TestExprString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStmtsString(t *testing.T) {
|
||||
// A list of valid statements that are expected to be returned as out when
|
||||
// calling String(). If out is empty the output is expected to equal the
|
||||
// input.
|
||||
inputs := []struct {
|
||||
in, out string
|
||||
}{
|
||||
{
|
||||
in: `ALERT foo IF up == 0 FOR 1m`,
|
||||
out: "ALERT foo\n\tIF up == 0\n\tFOR 1m",
|
||||
},
|
||||
{
|
||||
in: `ALERT foo IF up == 0 FOR 1m ANNOTATIONS {summary="foo"}`,
|
||||
out: "ALERT foo\n\tIF up == 0\n\tFOR 1m\n\tANNOTATIONS {summary=\"foo\"}",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range inputs {
|
||||
expr, err := ParseStmts(test.in)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing error for %q: %s", test.in, err)
|
||||
}
|
||||
exp := test.in
|
||||
if test.out != "" {
|
||||
exp = test.out
|
||||
}
|
||||
if expr.String() != exp {
|
||||
t.Fatalf("expected %q to be returned as:\n%s\ngot:\n%s\n", test.in, exp, expr.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,11 +10,31 @@ if ! [[ "$0" =~ "scripts/genproto.sh" ]]; then
|
||||
exit 255
|
||||
fi
|
||||
|
||||
if ! [[ $(protoc --version) =~ "3.5" ]]; then
|
||||
echo "could not find protoc 3.5.x, is it installed + in PATH?"
|
||||
if ! [[ $(protoc --version) =~ "3.5.1" ]]; then
|
||||
echo "could not find protoc 3.5.1, is it installed + in PATH?"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
# Exact version of plugins to build.
|
||||
PROTOC_GEN_GOGOFAST_SHA="971cbfd2e72b513a28c74af7462aee0800248d69"
|
||||
PROTOC_GEN_GRPC_ECOSYSTEM_SHA="e4b8a938efae14de11fd97311e873e989896348c"
|
||||
|
||||
echo "installing plugins"
|
||||
go install "golang.org/x/tools/cmd/goimports"
|
||||
go get -d -u "github.com/gogo/protobuf/protoc-gen-gogo"
|
||||
pushd ${GOPATH}/src/github.com/gogo/protobuf
|
||||
git reset --hard "${PROTOC_GEN_GOGOFAST_SHA}"
|
||||
go install "github.com/gogo/protobuf/protoc-gen-gogofast"
|
||||
popd
|
||||
|
||||
go get -d -u "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
|
||||
go get -d -u "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
|
||||
pushd ${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway
|
||||
git reset --hard "${PROTOC_GEN_GRPC_ECOSYSTEM_SHA}"
|
||||
go install "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
|
||||
go install "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
|
||||
popd
|
||||
|
||||
PROM_ROOT="${GOPATH}/src/github.com/prometheus/prometheus"
|
||||
PROM_PATH="${PROM_ROOT}/prompb"
|
||||
GOGOPROTO_ROOT="${GOPATH}/src/github.com/gogo/protobuf"
|
||||
@ -40,7 +60,7 @@ for dir in ${DIRS}; do
|
||||
rpc.proto
|
||||
mv ../documentation/dev/api/rpc.swagger.json ../documentation/dev/api/swagger.json
|
||||
|
||||
sed -i.bak -E 's/import _ \"gogoproto\"//g' *.pb.go
|
||||
sed -i.bak -E 's/import _ \"github.com\/gogo\/protobuf\/gogoproto\"//g' *.pb.go
|
||||
sed -i.bak -E 's/import _ \"google\/protobuf\"//g' *.pb.go
|
||||
sed -i.bak -E 's/golang\/protobuf/gogo\/protobuf/g' *.go
|
||||
rm -f *.bak
|
||||
|
@ -30,10 +30,27 @@ import (
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/util/strutil"
|
||||
)
|
||||
|
||||
var (
|
||||
templateTextExpansionFailures = prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "prometheus_template_text_expansion_failures_total",
|
||||
Help: "The total number of template text expansion failures.",
|
||||
})
|
||||
templateTextExpansionTotal = prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "prometheus_template_text_expansions_total",
|
||||
Help: "The total number of template text expansions.",
|
||||
})
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(templateTextExpansionFailures)
|
||||
prometheus.MustRegister(templateTextExpansionTotal)
|
||||
}
|
||||
|
||||
// A version of vector that's easier to use from templates.
|
||||
type sample struct {
|
||||
Labels map[string]string
|
||||
@ -274,8 +291,13 @@ func (te Expander) Expand() (result string, resultErr error) {
|
||||
resultErr = fmt.Errorf("panic expanding template %v: %v", te.name, r)
|
||||
}
|
||||
}
|
||||
if resultErr != nil {
|
||||
templateTextExpansionFailures.Inc()
|
||||
}
|
||||
}()
|
||||
|
||||
templateTextExpansionTotal.Inc()
|
||||
|
||||
tmpl, err := text_template.New(te.name).Funcs(te.funcMap).Option("missingkey=zero").Parse(te.text)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing template %v: %v", te.name, err)
|
||||
|
@ -17,13 +17,17 @@ package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/prometheus/web/ui"
|
||||
"github.com/shurcooL/vfsgen"
|
||||
|
||||
"github.com/prometheus/prometheus/pkg/modtimevfs"
|
||||
"github.com/prometheus/prometheus/web/ui"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := vfsgen.Generate(ui.Assets, vfsgen.Options{
|
||||
fs := modtimevfs.New(ui.Assets, time.Unix(1, 0))
|
||||
err := vfsgen.Generate(fs, vfsgen.Options{
|
||||
PackageName: "ui",
|
||||
BuildTags: "!dev",
|
||||
VariableName: "Assets",
|
||||
|
File diff suppressed because one or more lines are too long
@ -54,8 +54,7 @@
|
||||
</td>
|
||||
<td class="labels">
|
||||
<span class="cursor-pointer" data-toggle="tooltip" title="" data-html=true data-original-title="<b>Before relabeling:</b>{{range $k, $v := .DiscoveredLabels.Map}}<br>{{$ev := $v | html}}{{$k}}="{{$ev}}"{{end}}">
|
||||
{{$labels := stripLabels .Labels.Map "job"}}
|
||||
{{range $label, $value := $labels}}
|
||||
{{range $label, $value := .Labels.Map }}
|
||||
<span class="label label-primary">{{$label}}="{{$value}}"</span>
|
||||
{{else}}
|
||||
<span class="label label-default">none</span>
|
||||
|
Loading…
Reference in New Issue
Block a user