Merge branch 'master' into go-modules

Signed-off-by: Simon Pasquier <spasquie@redhat.com>
This commit is contained in:
Simon Pasquier 2018-11-09 11:42:12 +01:00
commit e3cd3ab79a
31 changed files with 347 additions and 785 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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.

View File

@ -1 +1 @@
2.5.0-rc.0
2.5.0

View File

@ -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.

View File

@ -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)

View File

@ -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",

View File

@ -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)),

View File

@ -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",

View File

@ -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.

View 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
}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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,

View File

@ -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"}},
}, {

View File

@ -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)

View File

@ -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 {

View File

@ -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 ""

View File

@ -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())
}
}
}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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}}=&quot;{{$ev}}&quot;{{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>