Unverified Commit a6ee1f85 by Julien Pivotto Committed by GitHub

Merge pull request #7913 from prometheus/release-2.21

Merge release 2.21 into master
parents 8219b442 e55b0be9
## 2.21.0-rc.1 / 2020-09-08
This release is built with Go 1.15, which deprecates [X.509
CommonName](https://golang.org/doc/go1.15#commonname) in TLS certificates
validation.
In the unlikely case that you use the gRPC API v2 (which is limited to TSDB
admin commands), please note that we will remove this experimental API in the
next minor release 2.22.
* [CHANGE] Disable HTTP2 because of concerns with the Go HTTP/2 client. #7588 #7701
* [CHANGE] PromQL: `query_log_file` path is now relative to the config file. #7701
* [CHANGE] Promtool: Replace the tsdb command line tool by a promtool tsdb subcommand. #6088
* [CHANGE] Rules: Label `rule_group_iterations` metric with group name. #7823
* [FEATURE] Eureka SD: New service discovery. #3369
* [FEATURE] Hetzner SD: New service discovery. #7822
* [FEATURE] Kubernetes SD: Support Kubernetes EndpointSlices. #6838
* [FEATURE] Scrape: Add per scrape-config targets limit. #7554
* [ENHANCEMENT] Support composite durations in PromQL, config and UI, e.g. 1h30m. #7713 #7833
* [ENHANCEMENT] DNS SD: Add SRV record target and port meta labels. #7678
* [ENHANCEMENT] Docker Swarm SD: Support tasks and service without published ports. #7686
* [ENHANCEMENT] PromQL: Reduce the amount of data queried by remote read when a subquery has an offset. #7667
* [ENHANCEMENT] Promtool: Add `--time` option to query instant command. #7829
* [ENHANCEMENT] UI: Respect the `--web.page-title` parameter in the React UI. #7607
* [ENHANCEMENT] UI: Add duration, labels, annotations to alerts page in the React UI. #7605
* [ENHANCEMENT] UI: Add duration on the React UI rules page, hide annotation and labels if empty. #7606
* [BUGFIX] API: Deduplicate series in /api/v1/series. #7862
* [BUGFIX] PromQL: Drop metric name in bool comparison between two instant vectors. #7819
* [BUGFIX] PromQL: Exit with an error when time parameters can't be parsed. #7505
* [BUGFIX] Rules: Detect extra fields in rule files. #7767
* [BUGFIX] Rules: Disallow overwriting the metric name in the `labels` section of recording rules. #7787
* [BUGFIX] Rules: Keep evaluation timestamp across reloads. #7775
* [BUGFIX] Scrape: Do not stop scrapes in progress during reload. #7752
* [BUGFIX] TSDB: Fix `chunks.HeadReadWriter: maxt of the files are not set` error. #7856
* [BUGFIX] TSDB: Delete blocks atomically to prevent corruption when there is a panic/crash during deletion. #7772
* [BUGFIX] Triton SD: Fix a panic when triton_sd_config is nil. #7671
* [BUGFIX] UI: Fix react UI bug with series going on and off. #7804
* [BUGFIX] Web: Stop CMUX and GRPC servers even with stale connections, preventing the server to stop on SIGTERM. #7810
## 2.20.1 / 2020-08-05
* [BUGFIX] SD: Reduce the Consul watch timeout to 2m and adjust the request timeout accordingly. #7724
......
......@@ -26,7 +26,8 @@ import (
"time"
"github.com/go-kit/kit/log"
config_util "github.com/prometheus/common/config"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/discovery/refresh"
"github.com/prometheus/prometheus/discovery/targetgroup"
......@@ -54,7 +55,7 @@ func newRobotDiscovery(conf *SDConfig, logger log.Logger) (*robotDiscovery, erro
endpoint: conf.robotEndpoint,
}
rt, err := config_util.NewRoundTripperFromConfig(conf.HTTPClientConfig, "hetzner_sd", false, false)
rt, err := config.NewRoundTripperFromConfig(conf.HTTPClientConfig, "hetzner_sd", false, false)
if err != nil {
return nil, err
}
......@@ -70,10 +71,16 @@ func (d *robotDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, err
if err != nil {
return nil, err
}
defer func() {
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
}()
if resp.StatusCode/100 != 2 {
return nil, errors.Errorf("non 2xx status '%d' response during hetzner service discovery with role robot", resp.StatusCode)
}
var servers serversList
err = json.NewDecoder(resp.Body).Decode(&servers)
if err != nil {
......
......@@ -37,7 +37,6 @@ func (s *robotSDTestSuite) SetupTest(t *testing.T) {
func TestRobotSDRefresh(t *testing.T) {
suite := &robotSDTestSuite{}
suite.SetupTest(t)
cfg := DefaultSDConfig
cfg.HTTPClientConfig.BasicAuth = &config.BasicAuth{Username: robotTestUsername, Password: robotTestPassword}
cfg.robotEndpoint = suite.Mock.Endpoint()
......@@ -84,3 +83,19 @@ func TestRobotSDRefresh(t *testing.T) {
})
}
}
func TestRobotSDRefreshHandleError(t *testing.T) {
suite := &robotSDTestSuite{}
suite.SetupTest(t)
cfg := DefaultSDConfig
cfg.robotEndpoint = suite.Mock.Endpoint()
d, err := newRobotDiscovery(&cfg, log.NewNopLogger())
testutil.Ok(t, err)
targetGroups, err := d.refresh(context.Background())
testutil.NotOk(t, err)
testutil.Equals(t, "non 2xx status '401' response during hetzner service discovery with role robot", err.Error())
testutil.Equals(t, 0, len(targetGroups))
}
......@@ -936,6 +936,7 @@ changed with relabeling, as demonstrated in [the Prometheus hetzner-sd
configuration file](/documentation/examples/prometheus-hetzner.yml).
The following meta labels are available on all targets during [relabeling](#relabel_config):
* `__meta_hetzner_server_id`: the ID of the server
* `__meta_hetzner_server_name`: the name of the server
* `__meta_hetzner_server_status`: the status of the server
......@@ -944,6 +945,7 @@ The following meta labels are available on all targets during [relabeling](#rela
* `__meta_hetzner_datacenter`: the datacenter of the server
The labels below are only available for targets with `role` set to `hcloud`:
* `__meta_hetzner_hcloud_image_name`: the image name of the server
* `__meta_hetzner_hcloud_image_description`: the description of the server image
* `__meta_hetzner_hcloud_image_os_flavor`: the OS flavor of the server image
......@@ -960,6 +962,7 @@ The labels below are only available for targets with `role` set to `hcloud`:
* `__meta_hetzner_hcloud_label_<labelname>`: each label of the server
The labels below are only available for targets with `role` set to `robot`:
* `__meta_hetzner_robot_product`: the product of the server
* `__meta_hetzner_robot_cancelled`: the server cancellation status
......
......@@ -16,6 +16,7 @@ package storage
import (
"bytes"
"container/heap"
"math"
"sort"
"strings"
"sync"
......@@ -331,7 +332,7 @@ func (c *genericMergeSeriesSet) Next() bool {
// If, for the current label set, all the next series sets come from
// failed remote storage sources, we want to keep trying with the next label set.
for {
// Firstly advance all the current series sets. If any of them have run out
// Firstly advance all the current series sets. If any of them have run out,
// we can drop them, otherwise they should be inserted back into the heap.
for _, set := range c.currentSets {
if set.Next() {
......@@ -418,8 +419,7 @@ func (h *genericSeriesSetHeap) Pop() interface{} {
// with "almost" the same data, e.g. from 2 Prometheus HA replicas. This is fine, since from the Prometheus perspective
// this never happens.
//
// NOTE: Use this merge function only when you see potentially overlapping series, as this introduces a small overhead
// to handle overlaps between series.
// It's optimized for non-overlap cases as well.
func ChainedSeriesMerge(series ...Series) Series {
if len(series) == 0 {
return nil
......@@ -438,16 +438,20 @@ func ChainedSeriesMerge(series ...Series) Series {
// chainSampleIterator is responsible to iterate over samples from different iterators of the same time series in timestamps
// order. If one or more samples overlap, one sample from random overlapped ones is kept and all others with the same
// timestamp are dropped.
// timestamp are dropped. It's optimized for non-overlap cases as well.
type chainSampleIterator struct {
iterators []chunkenc.Iterator
h samplesIteratorHeap
curr chunkenc.Iterator
lastt int64
}
func newChainSampleIterator(iterators []chunkenc.Iterator) chunkenc.Iterator {
return &chainSampleIterator{
iterators: iterators,
h: nil,
lastt: math.MinInt64,
}
}
......@@ -458,47 +462,74 @@ func (c *chainSampleIterator) Seek(t int64) bool {
heap.Push(&c.h, iter)
}
}
return len(c.h) > 0
if len(c.h) > 0 {
c.curr = heap.Pop(&c.h).(chunkenc.Iterator)
return true
}
c.curr = nil
return false
}
func (c *chainSampleIterator) At() (t int64, v float64) {
if len(c.h) == 0 {
panic("chainSampleIterator.At() called after .Next() returned false.")
if c.curr == nil {
panic("chainSampleIterator.At() called before first .Next() or after .Next() returned false.")
}
return c.h[0].At()
return c.curr.At()
}
func (c *chainSampleIterator) Next() bool {
if c.h == nil {
for _, iter := range c.iterators {
c.h = samplesIteratorHeap{}
// We call c.curr.Next() as the first thing below.
// So, we don't call Next() on it here.
c.curr = c.iterators[0]
for _, iter := range c.iterators[1:] {
if iter.Next() {
heap.Push(&c.h, iter)
}
}
return len(c.h) > 0
}
if len(c.h) == 0 {
if c.curr == nil {
return false
}
currt, _ := c.At()
for len(c.h) > 0 {
nextt, _ := c.h[0].At()
// All but one of the overlapping samples will be dropped.
if nextt != currt {
break
var currt int64
for {
if c.curr.Next() {
currt, _ = c.curr.At()
if currt == c.lastt {
// Ignoring sample for the same timestamp.
continue
}
if len(c.h) == 0 {
// curr is the only iterator remaining,
// no need to check with the heap.
break
}
// Check current iterator with the top of the heap.
if nextt, _ := c.h[0].At(); currt < nextt {
// Current iterator has smaller timestamp than the heap.
break
}
// Current iterator does not hold the smallest timestamp.
heap.Push(&c.h, c.curr)
} else if len(c.h) == 0 {
// No iterator left to iterate.
c.curr = nil
return false
}
iter := heap.Pop(&c.h).(chunkenc.Iterator)
if iter.Next() {
heap.Push(&c.h, iter)
c.curr = heap.Pop(&c.h).(chunkenc.Iterator)
currt, _ = c.curr.At()
if currt != c.lastt {
break
}
}
return len(c.h) > 0
c.lastt = currt
return true
}
func (c *chainSampleIterator) Err() error {
......
......@@ -448,10 +448,10 @@ func TestCompactingChunkSeriesMerger(t *testing.T) {
name: "three in chained overlap",
input: []ChunkSeries{
NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []tsdbutil.Sample{sample{1, 1}, sample{2, 2}, sample{3, 3}, sample{5, 5}}),
NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []tsdbutil.Sample{sample{4, 4}, sample{6, 6}}),
NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []tsdbutil.Sample{sample{4, 4}, sample{6, 66}}),
NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []tsdbutil.Sample{sample{6, 6}, sample{10, 10}}),
},
expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []tsdbutil.Sample{sample{1, 1}, sample{2, 2}, sample{3, 3}, sample{4, 4}, sample{5, 5}, sample{6, 6}, sample{10, 10}}),
expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []tsdbutil.Sample{sample{1, 1}, sample{2, 2}, sample{3, 3}, sample{4, 4}, sample{5, 5}, sample{6, 66}, sample{10, 10}}),
},
{
name: "three in chained overlap complex",
......
......@@ -603,7 +603,8 @@ func (api *API) series(r *http.Request) (result apiFuncResult) {
var sets []storage.SeriesSet
for _, mset := range matcherSets {
s := q.Select(false, nil, mset...)
// We need to sort this select results to merge (deduplicate) the series sets later.
s := q.Select(true, nil, mset...)
sets = append(sets, s)
}
......
......@@ -303,6 +303,11 @@ func TestEndpoints(t *testing.T) {
test_metric1{foo="bar"} 0+100x100
test_metric1{foo="boo"} 1+0x100
test_metric2{foo="boo"} 1+0x100
test_metric3{foo="bar", dup="1"} 1+0x100
test_metric3{foo="boo", dup="1"} 1+0x100
test_metric4{foo="bar", dup="1"} 1+0x100
test_metric4{foo="boo", dup="1"} 1+0x100
test_metric4{foo="boo"} 1+0x100
`)
testutil.Ok(t, err)
defer suite.Close()
......@@ -737,6 +742,18 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, testLabelAPI
labels.FromStrings("__name__", "test_metric1", "foo", "boo"),
},
},
// Try to overlap the selected series set as much as possible to test the result de-duplication works well.
{
endpoint: api.series,
query: url.Values{
"match[]": []string{`test_metric4{foo=~".+o$"}`, `test_metric4{dup=~"^1"}`},
},
response: []labels.Labels{
labels.FromStrings("__name__", "test_metric4", "dup", "1", "foo", "bar"),
labels.FromStrings("__name__", "test_metric4", "dup", "1", "foo", "boo"),
labels.FromStrings("__name__", "test_metric4", "foo", "boo"),
},
},
{
endpoint: api.series,
query: url.Values{
......@@ -1449,6 +1466,8 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, testLabelAPI
response: []string{
"test_metric1",
"test_metric2",
"test_metric3",
"test_metric4",
},
},
{
......@@ -1597,7 +1616,7 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, testLabelAPI
// Label names.
{
endpoint: api.labelNames,
response: []string{"__name__", "foo"},
response: []string{"__name__", "dup", "foo"},
},
// Start and end before Label names starts.
{
......@@ -1615,7 +1634,7 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, testLabelAPI
"start": []string{"1"},
"end": []string{"100"},
},
response: []string{"__name__", "foo"},
response: []string{"__name__", "dup", "foo"},
},
// Start before Label names, end within Label names.
{
......@@ -1624,7 +1643,7 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, testLabelAPI
"start": []string{"-1"},
"end": []string{"10"},
},
response: []string{"__name__", "foo"},
response: []string{"__name__", "dup", "foo"},
},
// Start before Label names starts, end after Label names ends.
......@@ -1634,7 +1653,7 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, testLabelAPI
"start": []string{"-1"},
"end": []string{"100000"},
},
response: []string{"__name__", "foo"},
response: []string{"__name__", "dup", "foo"},
},
// Start with bad data for Label names, end within Label names.
{
......@@ -1652,7 +1671,7 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, testLabelAPI
"start": []string{"1"},
"end": []string{"1000000006"},
},
response: []string{"__name__", "foo"},
response: []string{"__name__", "dup", "foo"},
},
// Start and end after Label names ends.
{
......@@ -1669,7 +1688,7 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, testLabelAPI
query: url.Values{
"start": []string{"4"},
},
response: []string{"__name__", "foo"},
response: []string{"__name__", "dup", "foo"},
},
// Only provide End within Label names, don't provide a start time.
{
......@@ -1677,7 +1696,7 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, testLabelAPI
query: url.Values{
"end": []string{"20"},
},
response: []string{"__name__", "foo"},
response: []string{"__name__", "dup", "foo"},
},
}...)
}
......
......@@ -31,8 +31,10 @@ describe('targetLabels', () => {
it('wraps each label in a label badge', () => {
const l: { [key: string]: string } = defaultProps.labels;
Object.keys(l).forEach((labelName: string): void => {
const badge = targetLabels.find(Badge).filterWhere(badge => badge.hasClass(labelName));
expect(badge.children().text()).toEqual(`${labelName}="${l[labelName]}"`);
const badge = targetLabels
.find(Badge)
.filterWhere(badge => badge.children().text() === `${labelName}="${l[labelName]}"`);
expect(badge).toHaveLength(1);
});
expect(targetLabels.find(Badge)).toHaveLength(3);
});
......
......@@ -27,7 +27,7 @@ const TargetLabels: FC<TargetLabelsProps> = ({ discoveredLabels, labels, idx, sc
<div id={id} className="series-labels-container">
{Object.keys(labels).map(labelName => {
return (
<Badge color="primary" className={`mr-1 ${labelName}`} key={labelName}>
<Badge color="primary" className="mr-1" key={labelName}>
{`${labelName}="${labels[labelName]}"`}
</Badge>
);
......
......@@ -7,7 +7,7 @@ exports[`targetLabels renders discovered labels 1`] = `
id="series-labels-cortex/node-exporter_group/0-1"
>
<Badge
className="mr-1 instance"
className="mr-1"
color="primary"
key="instance"
pill={false}
......@@ -16,7 +16,7 @@ exports[`targetLabels renders discovered labels 1`] = `
instance="localhost:9100"
</Badge>
<Badge
className="mr-1 job"
className="mr-1"
color="primary"
key="job"
pill={false}
......@@ -25,7 +25,7 @@ exports[`targetLabels renders discovered labels 1`] = `
job="node_exporter"
</Badge>
<Badge
className="mr-1 foo"
className="mr-1"
color="primary"
key="foo"
pill={false}
......
......@@ -10,8 +10,8 @@
<thead>
<tr>
<td colspan="3"><h2><a href="#{{reReplaceAll "([^a-zA-Z0-9])" "$1" .Name}}" id="{{reReplaceAll "([^a-zA-Z0-9])" "$1" .Name}}">{{.Name}}</a></h2></td>
<td><h2>{{if .GetEvaluationTimestamp.IsZero}}Never{{else}}{{since .GetEvaluationTimestamp}} ago{{end}}</h2></td>
<td><h2>{{humanizeDuration .GetEvaluationDuration.Seconds}}</h2></td>
<td><h2>{{if .GetLastEvaluation.IsZero}}Never{{else}}{{since .GetLastEvaluation}} ago{{end}}</h2></td>
<td><h2>{{humanizeDuration .GetEvaluationTime.Seconds}}</h2></td>
</tr>
</thead>
<tbody>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment