mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-06-09 03:43:58 +03:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69962a7001 | ||
|
|
9f03548e55 | ||
|
|
022310f35b | ||
|
|
895cadfae7 | ||
|
|
57704aa584 | ||
|
|
f9b24d4899 | ||
|
|
fa0554b771 | ||
|
|
35b133bff4 | ||
|
|
a884803377 | ||
|
|
b38d048dd9 | ||
|
|
de2cd4231b | ||
|
|
298eb0a0f8 | ||
|
|
12fe915b48 | ||
|
|
cdf0a4cf8f | ||
|
|
1c9c57db1c | ||
|
|
8edc72201d | ||
|
|
b024ecd10c | ||
|
|
e0d0348f36 | ||
|
|
3e55c7e069 | ||
|
|
c4acd20d2a | ||
|
|
8661dc4624 | ||
|
|
16572c8722 | ||
|
|
b699c46046 | ||
|
|
e71519b8b2 | ||
|
|
972713bd79 | ||
|
|
5d99ca6cfc | ||
|
|
318326c309 | ||
|
|
a1e4c6a2be | ||
|
|
ac3ee44fa7 | ||
|
|
b98ca56d94 | ||
|
|
b41ee5f27d | ||
|
|
8d35af6fdb | ||
|
|
0f2dd77a76 | ||
|
|
0c485f14d1 | ||
|
|
2ebf7d86ff | ||
|
|
bf6c24d0f4 | ||
|
|
1f7292675a | ||
|
|
bd156cd088 | ||
|
|
b695087119 | ||
|
|
80f53e5396 | ||
|
|
7acb797595 | ||
|
|
3a8bbfd6b9 | ||
|
|
27373807c1 | ||
|
|
8d7f0aa632 | ||
|
|
149f365f74 | ||
|
|
b22da547a2 | ||
|
|
047849e855 | ||
|
|
f3ec424e7d | ||
|
|
ef8aee8a2d | ||
|
|
dde4a97534 | ||
|
|
f3e0c55ea1 | ||
|
|
97fb0edd07 | ||
|
|
25f585ecf2 | ||
|
|
df91d2d91f | ||
|
|
3c7c71a49c | ||
|
|
69f1470692 | ||
|
|
4fc4912f0c | ||
|
|
a746cb62b6 | ||
|
|
499594f421 | ||
|
|
fdc2a9d1d7 | ||
|
|
92d67e2592 | ||
|
|
8a853778d7 | ||
|
|
8d75a5dbd0 | ||
|
|
cdd6171af1 | ||
|
|
cc183bc899 | ||
|
|
3935038e20 | ||
|
|
c8dc1cd218 | ||
|
|
c1551a3269 | ||
|
|
8023ad7dbd | ||
|
|
d4beb17ebe | ||
|
|
fcd91795d5 | ||
|
|
650830db79 |
8
.github/workflows/github-pages.yml
vendored
8
.github/workflows/github-pages.yml
vendored
@@ -2,7 +2,7 @@ name: github-pages
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/*.md'
|
||||
- 'docs/*'
|
||||
- 'README.md'
|
||||
branches:
|
||||
- master
|
||||
@@ -17,14 +17,14 @@ jobs:
|
||||
TOKEN: ${{secrets.CI_TOKEN}}
|
||||
run: |
|
||||
git clone https://vika:${TOKEN}@github.com/VictoriaMetrics/VictoriaMetrics.github.io.git gpages
|
||||
cp docs/*.md gpages
|
||||
cp docs/* gpages
|
||||
cp README.md gpages
|
||||
cd gpages
|
||||
git config --local user.email "info@victoriametrics.com"
|
||||
git config --local user.name "Vika"
|
||||
git add "*.md"
|
||||
git add .
|
||||
git commit -m "update github pages"
|
||||
remote_repo="https://vika:${TOKEN}@github.com/VictoriaMetrics/VictoriaMetrics.github.io.git"
|
||||
git push "${remote_repo}"
|
||||
cd ..
|
||||
rm -rf gpages
|
||||
rm -rf gpages
|
||||
|
||||
9
.github/workflows/wiki.yml
vendored
9
.github/workflows/wiki.yml
vendored
@@ -2,7 +2,7 @@ name: wiki
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/*.md'
|
||||
- 'docs/*'
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
@@ -15,15 +15,14 @@ jobs:
|
||||
env:
|
||||
TOKEN: ${{secrets.CI_TOKEN}}
|
||||
run: |
|
||||
cd docs
|
||||
git clone https://vika:${TOKEN}@github.com/VictoriaMetrics/VictoriaMetrics.wiki.git wiki
|
||||
find ./ -name '*.md' -exec cp -prv '{}' 'wiki' ';'
|
||||
cp docs/* wiki
|
||||
cd wiki
|
||||
git config --local user.email "info@victoriametrics.com"
|
||||
git config --local user.name "Vika"
|
||||
git add "*.md"
|
||||
git add .
|
||||
git commit -m "update wiki pages"
|
||||
remote_repo="https://vika:${TOKEN}@github.com/VictoriaMetrics/VictoriaMetrics.wiki.git"
|
||||
git push "${remote_repo}"
|
||||
cd ..
|
||||
rm -rf wiki
|
||||
rm -rf wiki
|
||||
|
||||
62
README.md
62
README.md
@@ -24,7 +24,9 @@ Cluster version is available [here](https://github.com/VictoriaMetrics/VictoriaM
|
||||
* [Wix.com](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#wixcom)
|
||||
* [Wedos.com](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#wedoscom)
|
||||
* [Synthesio](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#synthesio)
|
||||
* [MHI Vestas Offshore Wind](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#mhi-vestas-offshore-wind)
|
||||
* [Dreamteam](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#dreamteam)
|
||||
* [Brandwatch](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#brandwatch)
|
||||
|
||||
|
||||
## Prominent features
|
||||
@@ -113,11 +115,11 @@ Cluster version is available [here](https://github.com/VictoriaMetrics/VictoriaM
|
||||
* [Backfilling](#backfilling)
|
||||
* [Profiling](#profiling)
|
||||
* [Integrations](#integrations)
|
||||
* [Roadmap](#roadmap)
|
||||
* [Third-party contributions](#third-party-contributions)
|
||||
* [Contacts](#contacts)
|
||||
* [Community and contributions](#community-and-contributions)
|
||||
* [Third-party contributions](#third-party-contributions)
|
||||
* [Reporting bugs](#reporting-bugs)
|
||||
* [Roadmap](#roadmap)
|
||||
* [Victoria Metrics Logo](#victoria-metrics-logo)
|
||||
* [Logo Usage Guidelines](#logo-usage-guidelines)
|
||||
* [Font used](#font-used)
|
||||
@@ -146,6 +148,7 @@ Each flag values can be set thru environment variables by following these rules:
|
||||
* The `-envflag.enable` flag must be set
|
||||
* Each `.` in flag names must be substituted by `_` (for example `-insert.maxQueueDuration <duration>` will translate to `insert_maxQueueDuration=<duration>`)
|
||||
* For repeating flags, an alternative syntax can be used by joining the different values into one using `,` as separator (for example `-storageNode <nodeA> -storageNode <nodeB>` will translate to `storageNode=<nodeA>,<nodeB>`)
|
||||
* It is possible setting prefix for environment vars with `-envflag.prefix`. For instance, if `-envflag.prefix=VM_`, then env vars must be prepended with `VM_`
|
||||
|
||||
### Prometheus setup
|
||||
|
||||
@@ -474,6 +477,8 @@ The following response should be returned:
|
||||
{"metric":{"__name__":"ask","market":"NYSE","ticker":"GOOG"},"values":[1.23],"timestamps":[1583865146495]}
|
||||
```
|
||||
|
||||
Note that it could be required to flush response cache after importing historical data. See [these docs](#backfilling) for detail.
|
||||
|
||||
|
||||
### Prometheus querying API usage
|
||||
|
||||
@@ -504,11 +509,11 @@ Additionally VictoriaMetrics provides the following handlers:
|
||||
We recommend using either [binary releases](https://github.com/VictoriaMetrics/VictoriaMetrics/releases) or
|
||||
[docker images](https://hub.docker.com/r/victoriametrics/victoria-metrics/) instead of building VictoriaMetrics
|
||||
from sources. Building from sources is reasonable when developing additional features specific
|
||||
to your needs.
|
||||
to your needs or when testing bugfixes.
|
||||
|
||||
#### Development build
|
||||
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.12.
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.13.
|
||||
2. Run `make victoria-metrics` from the root folder of the repository.
|
||||
It builds `victoria-metrics` binary and puts it into the `bin` folder.
|
||||
|
||||
@@ -524,7 +529,7 @@ ARM build may run on Raspberry Pi or on [energy-efficient ARM servers](https://b
|
||||
|
||||
#### Development ARM build
|
||||
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.12.
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.13.
|
||||
2. Run `make victoria-metrics-arm` or `make victoria-metrics-arm64` from the root folder of the repository.
|
||||
It builds `victoria-metrics-arm` or `victoria-metrics-arm64` binary respectively and puts it into the `bin` folder.
|
||||
|
||||
@@ -540,7 +545,7 @@ ARM build may run on Raspberry Pi or on [energy-efficient ARM servers](https://b
|
||||
This is an experimental mode, which may result in a lower compression ratio and slower decompression performance.
|
||||
Use it with caution!
|
||||
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.12.
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.13.
|
||||
2. Run `make victoria-metrics-pure` from the root folder of the repository.
|
||||
It builds `victoria-metrics-pure` binary and puts it into the `bin` folder.
|
||||
|
||||
@@ -675,6 +680,8 @@ curl -H 'Accept-Encoding: gzip' http://source-victoriametrics:8428/api/v1/export
|
||||
curl -X POST -H 'Content-Encoding: gzip' http://destination-victoriametrics:8428/api/v1/import -T exported_data.jsonl.gz
|
||||
```
|
||||
|
||||
Note that it could be required to flush response cache after importing historical data. See [these docs](#backfilling) for detail.
|
||||
|
||||
Each request to `/api/v1/import` can load up to a single vCPU core on VictoriaMetrics. Import speed can be improved by splitting the original file into smaller parts
|
||||
and importing them concurrently. Note that the original file must be split on newlines.
|
||||
|
||||
@@ -857,6 +864,7 @@ Alternatively they can be self-scraped by setting `-selfScrapeInterval` command-
|
||||
For example, `-selfScrapeInterval=10s` would enable self-scraping of `/metrics` page with 10 seconds interval.
|
||||
|
||||
There are officials Grafana dashboards for [single-node VictoriaMetrics](https://grafana.com/dashboards/10229) and [clustered VictoriaMetrics](https://grafana.com/grafana/dashboards/11176).
|
||||
There is also an [alternative dashboard for clustered VictoriaMetrics](https://grafana.com/grafana/dashboards/11831).
|
||||
|
||||
The most interesting metrics are:
|
||||
|
||||
@@ -866,10 +874,9 @@ The most interesting metrics are:
|
||||
* `vm_rows{type="indexdb"}` - the number of rows in inverted index. High value for this number usually mean high churn rate for time series.
|
||||
* Sum of `vm_rows{type="storage/big"}` and `vm_rows{type="storage/small"}` - total number of `(timestamp, value)` data points
|
||||
in the database.
|
||||
* Sum of all the `vm_cache_size_bytes` metrics - the total size of all the caches in the database.
|
||||
* `vm_allowed_memory_bytes` - the maximum allowed size for caches in the database. It is calculated as `system_memory * <-memory.allowedPercent> / 100`,
|
||||
where `system_memory` is the amount of system memory and `-memory.allowedPercent` is the corresponding flag value.
|
||||
* `vm_rows_inserted_total` - the total number of inserted rows since VictoriaMetrics start.
|
||||
* `vm_free_disk_space_bytes` - free space left at `-storageDataPath`.
|
||||
* `sum(vm_data_size_bytes)` - the total data size on disk.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
@@ -885,7 +892,9 @@ The most interesting metrics are:
|
||||
|
||||
* VictoriaMetrics requires free disk space for [merging data files to bigger ones](https://medium.com/@valyala/how-victoriametrics-makes-instant-snapshots-for-multi-terabyte-time-series-data-e1f3fb0e0282).
|
||||
It may slow down when there is no enough free space left. So make sure `-storageDataPath` directory
|
||||
has at least 20% of free space comparing to disk size.
|
||||
has at least 20% of free space comparing to disk size. The remaining amount of free space
|
||||
can be [monitored](#monitoring) via `vm_free_disk_space_bytes` metric. The total size of data
|
||||
stored on the disk can be monitored via sum of `vm_data_size_bytes` metrics.
|
||||
|
||||
* If VictoriaMetrics doesn't work because of certain parts are corrupted due to disk errors,
|
||||
then just remove directories with broken parts. This will recover VictoriaMetrics at the cost
|
||||
@@ -936,18 +945,15 @@ The collected profiles may be analyzed with [go tool pprof](https://github.com/g
|
||||
* [netdata](https://github.com/netdata/netdata) can push data into VictoriaMetrics via `Prometheus remote_write API`.
|
||||
See [these docs](https://github.com/netdata/netdata#integrations).
|
||||
* [go-graphite/carbonapi](https://github.com/go-graphite/carbonapi) can use VictoriaMetrics as time series backend.
|
||||
See [this example](/blob/master/cmd/carbonapi/carbonapi.example.prometheus.yaml).
|
||||
See [this example](https://github.com/go-graphite/carbonapi/blob/master/cmd/carbonapi/carbonapi.example.prometheus.yaml).
|
||||
* [Ansible role for installing VictoriaMetrics](https://github.com/dreamteam-gg/ansible-victoriametrics-role).
|
||||
|
||||
## Roadmap
|
||||
## Third-party contributions
|
||||
|
||||
* [ ] Replication [#118](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/118)
|
||||
* [ ] Support of Object Storages (GCS, S3, Azure Storage) [#38](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/38)
|
||||
* [ ] Data downsampling [#36](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/36)
|
||||
* [ ] Alert Manager Integration [#119](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/119)
|
||||
* [ ] CLI tool for data migration, re-balancing and adding/removing nodes [#103](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/103)
|
||||
|
||||
The discussion happens [here](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/129). Feel free to comment on any item or add you own one.
|
||||
* [Unofficial yum repository](https://copr.fedorainfracloud.org/coprs/antonpatsev/VictoriaMetrics/) ([source code](https://github.com/patsevanton/victoriametrics-rpm))
|
||||
* [Prometheus -> VictoriaMetrics exporter #1](https://github.com/ryotarai/prometheus-tsdb-dump)
|
||||
* [Prometheus -> VictoriaMetrics exporter #2](https://github.com/AnchorFree/tsdb-remote-write)
|
||||
* [Prometheus Oauth proxy](https://gitlab.com/optima_public/prometheus_oauth_proxy) - see [this article](https://medium.com/@richard.holly/powerful-saas-solution-for-detection-metrics-c67b9208d362) for details.
|
||||
|
||||
## Contacts
|
||||
|
||||
@@ -980,17 +986,21 @@ We are open to third-party pull requests provided they follow [KISS design princ
|
||||
|
||||
Adhering `KISS` principle simplifies the resulting code and architecture, so it can be reviewed, understood and verified by many people.
|
||||
|
||||
## Third-party contributions
|
||||
|
||||
* [Unofficial yum repository](https://copr.fedorainfracloud.org/coprs/antonpatsev/VictoriaMetrics/) ([source code](https://github.com/patsevanton/victoriametrics-rpm))
|
||||
* [Prometheus -> VictoriaMetrics exporter #1](https://github.com/ryotarai/prometheus-tsdb-dump)
|
||||
* [Prometheus -> VictoriaMetrics exporter #2](https://github.com/AnchorFree/tsdb-remote-write)
|
||||
* [Prometheus Oauth proxy](https://gitlab.com/optima_public/prometheus_oauth_proxy) - see [this article](https://medium.com/@richard.holly/powerful-saas-solution-for-detection-metrics-c67b9208d362) for details.
|
||||
|
||||
## Reporting bugs
|
||||
|
||||
Report bugs and propose new features [here](https://github.com/VictoriaMetrics/VictoriaMetrics/issues).
|
||||
|
||||
## Roadmap
|
||||
|
||||
* [ ] Replication [#118](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/118)
|
||||
* [ ] Support of Object Storages (GCS, S3, Azure Storage) [#38](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/38)
|
||||
* [ ] Data downsampling [#36](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/36)
|
||||
* [ ] Alert Manager Integration [#119](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/119)
|
||||
* [ ] CLI tool for data migration, re-balancing and adding/removing nodes [#103](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/103)
|
||||
|
||||
The discussion happens [here](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/129). Feel free to comment on any item or add you own one.
|
||||
|
||||
|
||||
## Victoria Metrics Logo
|
||||
|
||||
[Zip](VM_logo.zip) contains three folders with different image orientations (main color and inverted version).
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
ARG certs_image
|
||||
FROM $certs_image AS certs
|
||||
FROM scratch
|
||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
ARG base_image
|
||||
FROM $base_image
|
||||
|
||||
EXPOSE 8428
|
||||
|
||||
ENTRYPOINT ["/victoria-metrics-prod"]
|
||||
ARG src_binary
|
||||
COPY $src_binary ./victoria-metrics-prod
|
||||
EXPOSE 8428
|
||||
ENTRYPOINT ["/victoria-metrics-prod"]
|
||||
|
||||
@@ -14,7 +14,11 @@ import (
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||
)
|
||||
|
||||
var selfScrapeInterval = flag.Duration("selfScrapeInterval", 0, "Interval for self-scraping own metrics at /metrics page")
|
||||
var (
|
||||
selfScrapeInterval = flag.Duration("selfScrapeInterval", 0, "Interval for self-scraping own metrics at /metrics page")
|
||||
selfScrapeInstance = flag.String("selfScrapeInstance", "self", "Value for 'instance' label, which is added to self-scraped metrics")
|
||||
selfScrapeJob = flag.String("selfScrapeJob", "victoria-metrics", "Value for 'job' label, which is added to self-scraped metrics")
|
||||
)
|
||||
|
||||
var selfScraperStopCh chan struct{}
|
||||
var selfScraperWG sync.WaitGroup
|
||||
@@ -65,8 +69,8 @@ func selfScraper(scrapeInterval time.Duration) {
|
||||
r := &rows.Rows[i]
|
||||
labels = labels[:0]
|
||||
labels = addLabel(labels, "", r.Metric)
|
||||
labels = addLabel(labels, "job", "victoria-metrics")
|
||||
labels = addLabel(labels, "instance", "self")
|
||||
labels = addLabel(labels, "job", *selfScrapeJob)
|
||||
labels = addLabel(labels, "instance", *selfScrapeInstance)
|
||||
for j := range r.Tags {
|
||||
t := &r.Tags[j]
|
||||
labels = addLabel(labels, t.Key, t.Value)
|
||||
|
||||
16
app/victoria-metrics/testdata/graphite/empty-label-match.json
vendored
Normal file
16
app/victoria-metrics/testdata/graphite/empty-label-match.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "empty-label-match",
|
||||
"issue": "https://github.com/VictoriaMetrics/VictoriaMetrics/issues/395",
|
||||
"data": [
|
||||
"empty_label_match 1 {TIME_S-1m}",
|
||||
"empty_label_match;foo=bar 2 {TIME_S-1m}",
|
||||
"empty_label_match;foo=baz 3 {TIME_S-1m}"],
|
||||
"query": ["/api/v1/query_range?query=empty_label_match{foo=~'bar|'}&start={TIME_S}&end={TIME_S}&step=60"],
|
||||
"result_query_range": {
|
||||
"status":"success",
|
||||
"data":{"resultType":"matrix",
|
||||
"result":[
|
||||
{"metric":{"__name__":"empty_label_match"},"values":[["{TIME_S}","1"]]},
|
||||
{"metric":{"__name__":"empty_label_match","foo":"bar"},"values":[["{TIME_S}","2"]]}
|
||||
]}}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
## vmagent
|
||||
|
||||
`vmagent` is a tiny but brave agent, which helps you collecting metrics from various sources
|
||||
and storing them to [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics).
|
||||
and storing them to [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics)
|
||||
or any other Prometheus-compatible storage system that supports `remote_write` protocol.
|
||||
|
||||
<img alt="vmagent" src="vmagent.png">
|
||||
|
||||
@@ -18,7 +19,7 @@ to `vmagent` (like the ability to push metrics instead of pulling them). We did
|
||||
|
||||
* Can be used as drop-in replacement for Prometheus for scraping targets such as [node_exporter](https://github.com/prometheus/node_exporter).
|
||||
See [Quick Start](#quick-start) for details.
|
||||
* Can add, remove and modify labels via Prometheus relabeling. See [these docs](#relabeling) for details.
|
||||
* Can add, remove and modify labels (aka tags) via Prometheus relabeling. Can filter data before sending it to remote storage. See [these docs](#relabeling) for details.
|
||||
* Accepts data via all the ingestion protocols supported by VictoriaMetrics:
|
||||
* Influx line protocol via `http://<vmagent>:8429/write`. See [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/README.md#how-to-send-data-from-influxdb-compatible-agents-such-as-telegraf).
|
||||
* Graphite plaintext protocol if `-graphiteListenAddr` command-line flag is set. See [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/README.md#how-to-send-data-from-graphite-compatible-agents-such-as-statsd).
|
||||
@@ -105,6 +106,14 @@ data among long-term remote storage, short-term remote storage and real-time ana
|
||||
Note that each destination can receive its own subset of the collected data thanks to per-destination relabeling via `-remoteWrite.urlRelabelConfig`.
|
||||
|
||||
|
||||
#### Prometheus remote_write proxy
|
||||
|
||||
`vmagent` may be used as a proxy for Prometheus data sent via Prometheus `remote_write` protocol. It can accept data via `remote_write` API
|
||||
at `/api/v1/write` endpoint, apply relabeling and filtering and then proxy it to another `remote_write` systems.
|
||||
The `vmagent` can be configured to encrypt the incoming `remote_write` requests with `-tls*` command-line flags.
|
||||
Additionally, Basic Auth can be enabled for the incoming `remote_write` requests with `-httpAuth.*` command-line flags.
|
||||
|
||||
|
||||
|
||||
### How to collect metrics in Prometheus format
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
ARG certs_image
|
||||
FROM $certs_image AS certs
|
||||
FROM scratch
|
||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
ARG base_image
|
||||
FROM $base_image
|
||||
|
||||
EXPOSE 8429
|
||||
|
||||
ENTRYPOINT ["/vmagent-prod"]
|
||||
ARG src_binary
|
||||
COPY $src_binary ./vmagent-prod
|
||||
EXPOSE 8429
|
||||
ENTRYPOINT ["/vmagent-prod"]
|
||||
|
||||
@@ -214,6 +214,7 @@ func (c *client) sendBlock(block []byte) {
|
||||
req.Header.SetMethod("POST")
|
||||
req.Header.Add("Content-Type", "application/x-protobuf")
|
||||
req.Header.Add("Content-Encoding", "snappy")
|
||||
req.Header.Add("X-Prometheus-Remote-Write-Version", "0.1.0")
|
||||
if c.authHeader != "" {
|
||||
req.Header.Set("Authorization", c.authHeader)
|
||||
}
|
||||
|
||||
132
app/vmalert/common/alert.go
Normal file
132
app/vmalert/common/alert.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/datasource"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
)
|
||||
|
||||
// Alert the triggered alert
|
||||
type Alert struct {
|
||||
Group string
|
||||
Name string
|
||||
Labels []datasource.Label
|
||||
Annotations map[string]string
|
||||
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Value float64
|
||||
}
|
||||
|
||||
type alertTplData struct {
|
||||
Labels map[string]string
|
||||
ExternalLabels map[string]string
|
||||
Value float64
|
||||
}
|
||||
|
||||
const tplHeader = `{{ $value := .Value }}{{ $labels := .Labels }}{{ $externalLabels := .ExternalLabels }}`
|
||||
|
||||
// AlertsFromMetrics converts metrics to alerts by alert Rule
|
||||
func AlertsFromMetrics(metrics []datasource.Metric, group string, rule Rule, start, end time.Time) []Alert {
|
||||
alerts := make([]Alert, 0, len(metrics))
|
||||
var err error
|
||||
for i, m := range metrics {
|
||||
a := Alert{
|
||||
Group: group,
|
||||
Name: rule.Name,
|
||||
Start: start,
|
||||
End: end,
|
||||
Value: m.Value,
|
||||
}
|
||||
tplData := alertTplData{Value: m.Value, ExternalLabels: make(map[string]string)}
|
||||
tplData.Labels, a.Labels = mergeLabels(metrics[i].Labels, rule.Labels)
|
||||
a.Annotations, err = templateAnnotations(rule.Annotations, tplHeader, tplData)
|
||||
if err != nil {
|
||||
logger.Errorf("%s", err)
|
||||
}
|
||||
alerts = append(alerts, a)
|
||||
}
|
||||
return alerts
|
||||
}
|
||||
|
||||
func mergeLabels(ml []datasource.Label, rl map[string]string) (map[string]string, []datasource.Label) {
|
||||
set := make(map[string]string, len(ml)+len(rl))
|
||||
sl := append([]datasource.Label(nil), ml...)
|
||||
for _, i := range ml {
|
||||
set[i.Name] = i.Value
|
||||
}
|
||||
for name, value := range rl {
|
||||
if _, ok := set[name]; ok {
|
||||
continue
|
||||
}
|
||||
set[name] = value
|
||||
sl = append(sl, datasource.Label{
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
return set, sl
|
||||
}
|
||||
|
||||
func templateAnnotations(annotations map[string]string, header string, data alertTplData) (map[string]string, error) {
|
||||
var builder strings.Builder
|
||||
var buf bytes.Buffer
|
||||
eg := errGroup{}
|
||||
r := make(map[string]string, len(annotations))
|
||||
for key, text := range annotations {
|
||||
r[key] = text
|
||||
buf.Reset()
|
||||
builder.Reset()
|
||||
builder.Grow(len(header) + len(text))
|
||||
builder.WriteString(header)
|
||||
builder.WriteString(text)
|
||||
if err := templateAnnotation(&buf, builder.String(), data); err != nil {
|
||||
eg.errs = append(eg.errs, fmt.Sprintf("key %s, template %s:%s", key, text, err))
|
||||
continue
|
||||
}
|
||||
r[key] = buf.String()
|
||||
}
|
||||
return r, eg.err()
|
||||
}
|
||||
|
||||
// ValidateAnnotations validate annotations for possible template error, uses empty data for template population
|
||||
func ValidateAnnotations(annotations map[string]string) error {
|
||||
_, err := templateAnnotations(annotations, tplHeader, alertTplData{
|
||||
Labels: map[string]string{},
|
||||
ExternalLabels: map[string]string{},
|
||||
Value: 0,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func templateAnnotation(dst io.Writer, text string, data alertTplData) error {
|
||||
tpl, err := template.New("").Funcs(tmplFunc).Option("missingkey=zero").Parse(text)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing annotation:%w", err)
|
||||
}
|
||||
if err = tpl.Execute(dst, data); err != nil {
|
||||
return fmt.Errorf("error evaluating annotation template:%w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type errGroup struct {
|
||||
errs []string
|
||||
}
|
||||
|
||||
func (eg *errGroup) err() error {
|
||||
if eg == nil || len(eg.errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return eg
|
||||
}
|
||||
|
||||
func (eg *errGroup) Error() string {
|
||||
return fmt.Sprintf("errors:%s", strings.Join(eg.errs, "\n"))
|
||||
}
|
||||
99
app/vmalert/common/alert_test.go
Normal file
99
app/vmalert/common/alert_test.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/datasource"
|
||||
)
|
||||
|
||||
func TestAlertsFromMetrics(t *testing.T) {
|
||||
now := time.Now()
|
||||
metrics := []datasource.Metric{
|
||||
{
|
||||
Labels: []datasource.Label{
|
||||
{Name: "__name__", Value: "foo"},
|
||||
{Name: "label", Value: "value"},
|
||||
},
|
||||
Timestamp: 10,
|
||||
Value: 20,
|
||||
},
|
||||
{
|
||||
Labels: []datasource.Label{
|
||||
{Name: "__name__", Value: "bar"},
|
||||
{Name: "label", Value: "value"},
|
||||
},
|
||||
Timestamp: 10,
|
||||
Value: 30,
|
||||
},
|
||||
}
|
||||
rule := Rule{
|
||||
Name: "alertname",
|
||||
Expr: "up==0",
|
||||
Labels: map[string]string{
|
||||
"label2": "value",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"tpl": "{{$value}} {{ $labels.label}}",
|
||||
},
|
||||
}
|
||||
alerts := AlertsFromMetrics(metrics, "group", rule, now, now)
|
||||
if len(alerts) != 2 {
|
||||
t.Fatalf("expecting 2 alerts got %d", len(alerts))
|
||||
}
|
||||
|
||||
f := func(got, exp Alert) {
|
||||
t.Helper()
|
||||
if got.Group != exp.Group ||
|
||||
got.Value != exp.Value ||
|
||||
got.End != exp.End ||
|
||||
got.Name != exp.Name ||
|
||||
got.Start != exp.Start {
|
||||
t.Errorf("alerts are not equal: \nwant %#v \ngot %#v", exp, got)
|
||||
}
|
||||
sort.Slice(got.Labels, func(i, j int) bool {
|
||||
return got.Labels[i].Name < got.Labels[j].Name
|
||||
})
|
||||
sort.Slice(exp.Labels, func(i, j int) bool {
|
||||
return got.Labels[i].Name < got.Labels[j].Name
|
||||
})
|
||||
if !reflect.DeepEqual(got.Labels, exp.Labels) {
|
||||
t.Errorf("alerts labels are not equal: want %+v got %+v", exp.Labels, got.Labels)
|
||||
}
|
||||
if !reflect.DeepEqual(got.Annotations, exp.Annotations) {
|
||||
t.Errorf("alerts annotations are not equal: want %+v got %+v", exp.Annotations, got.Annotations)
|
||||
}
|
||||
}
|
||||
f(alerts[0], Alert{
|
||||
Group: "group",
|
||||
Name: "alertname",
|
||||
Labels: []datasource.Label{
|
||||
{Name: "__name__", Value: "foo"},
|
||||
{Name: "label", Value: "value"},
|
||||
{Name: "label2", Value: "value"},
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"tpl": "20 value",
|
||||
},
|
||||
Start: now,
|
||||
End: now,
|
||||
Value: 20,
|
||||
})
|
||||
f(alerts[1], Alert{
|
||||
Group: "group",
|
||||
Name: "alertname",
|
||||
Labels: []datasource.Label{
|
||||
{Name: "__name__", Value: "bar"},
|
||||
{Name: "label", Value: "value"},
|
||||
{Name: "label2", Value: "value"},
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"tpl": "30 value",
|
||||
},
|
||||
Start: now,
|
||||
End: now,
|
||||
Value: 30,
|
||||
})
|
||||
}
|
||||
38
app/vmalert/common/rule.go
Normal file
38
app/vmalert/common/rule.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
)
|
||||
|
||||
// Rule is basic alert entity
|
||||
type Rule struct {
|
||||
Name string `yaml:"alert"`
|
||||
Expr string `yaml:"expr"`
|
||||
For time.Duration `yaml:"for"`
|
||||
Labels map[string]string `yaml:"labels"`
|
||||
Annotations map[string]string `yaml:"annotations"`
|
||||
}
|
||||
|
||||
// Validate validates rule
|
||||
func (r Rule) Validate() error {
|
||||
if r.Name == "" {
|
||||
return errors.New("rule name can not be empty")
|
||||
}
|
||||
if r.Expr == "" {
|
||||
return fmt.Errorf("rule %s expression can not be empty", r.Name)
|
||||
}
|
||||
if _, err := metricsql.Parse(r.Expr); err != nil {
|
||||
return fmt.Errorf("rule %s invalid expression: %w", r.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Group grouping array of alert
|
||||
type Group struct {
|
||||
Name string
|
||||
Rules []Rule
|
||||
}
|
||||
18
app/vmalert/common/rule_test.go
Normal file
18
app/vmalert/common/rule_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package common
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRule_Validate(t *testing.T) {
|
||||
if err := (Rule{}).Validate(); err == nil {
|
||||
t.Errorf("exptected empty name error")
|
||||
}
|
||||
if err := (Rule{Name: "alert"}).Validate(); err == nil {
|
||||
t.Errorf("exptected empty expr error")
|
||||
}
|
||||
if err := (Rule{Name: "alert", Expr: "test{"}).Validate(); err == nil {
|
||||
t.Errorf("exptected invalid expr error")
|
||||
}
|
||||
if err := (Rule{Name: "alert", Expr: "test>0"}).Validate(); err != nil {
|
||||
t.Errorf("exptected valid rule got %s", err)
|
||||
}
|
||||
}
|
||||
171
app/vmalert/common/template_func.go
Normal file
171
app/vmalert/common/template_func.go
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright 2013 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 common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
html_template "html/template"
|
||||
"math"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
text_template "text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
var tmplFunc text_template.FuncMap
|
||||
|
||||
// InitTemplateFunc returns template helper functions
|
||||
func InitTemplateFunc(externalURL *url.URL) {
|
||||
tmplFunc = text_template.FuncMap{
|
||||
"args": func(args ...interface{}) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
for i, a := range args {
|
||||
result[fmt.Sprintf("arg%d", i)] = a
|
||||
}
|
||||
return result
|
||||
},
|
||||
"reReplaceAll": func(pattern, repl, text string) string {
|
||||
re := regexp.MustCompile(pattern)
|
||||
return re.ReplaceAllString(text, repl)
|
||||
},
|
||||
"safeHtml": func(text string) html_template.HTML {
|
||||
return html_template.HTML(text)
|
||||
},
|
||||
"match": regexp.MatchString,
|
||||
"title": strings.Title,
|
||||
"toUpper": strings.ToUpper,
|
||||
"toLower": strings.ToLower,
|
||||
"humanize": func(v float64) string {
|
||||
if v == 0 || math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
return fmt.Sprintf("%.4g", v)
|
||||
}
|
||||
if math.Abs(v) >= 1 {
|
||||
prefix := ""
|
||||
for _, p := range []string{"k", "M", "G", "T", "P", "E", "Z", "Y"} {
|
||||
if math.Abs(v) < 1000 {
|
||||
break
|
||||
}
|
||||
prefix = p
|
||||
v /= 1000
|
||||
}
|
||||
return fmt.Sprintf("%.4g%s", v, prefix)
|
||||
}
|
||||
prefix := ""
|
||||
for _, p := range []string{"m", "u", "n", "p", "f", "a", "z", "y"} {
|
||||
if math.Abs(v) >= 1 {
|
||||
break
|
||||
}
|
||||
prefix = p
|
||||
v *= 1000
|
||||
}
|
||||
return fmt.Sprintf("%.4g%s", v, prefix)
|
||||
},
|
||||
"humanize1024": func(v float64) string {
|
||||
if math.Abs(v) <= 1 || math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
return fmt.Sprintf("%.4g", v)
|
||||
}
|
||||
prefix := ""
|
||||
for _, p := range []string{"ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"} {
|
||||
if math.Abs(v) < 1024 {
|
||||
break
|
||||
}
|
||||
prefix = p
|
||||
v /= 1024
|
||||
}
|
||||
return fmt.Sprintf("%.4g%s", v, prefix)
|
||||
},
|
||||
"humanizeDuration": func(v float64) string {
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
return fmt.Sprintf("%.4g", v)
|
||||
}
|
||||
if v == 0 {
|
||||
return fmt.Sprintf("%.4gs", v)
|
||||
}
|
||||
if math.Abs(v) >= 1 {
|
||||
sign := ""
|
||||
if v < 0 {
|
||||
sign = "-"
|
||||
v = -v
|
||||
}
|
||||
seconds := int64(v) % 60
|
||||
minutes := (int64(v) / 60) % 60
|
||||
hours := (int64(v) / 60 / 60) % 24
|
||||
days := int64(v) / 60 / 60 / 24
|
||||
// For days to minutes, we display seconds as an integer.
|
||||
if days != 0 {
|
||||
return fmt.Sprintf("%s%dd %dh %dm %ds", sign, days, hours, minutes, seconds)
|
||||
}
|
||||
if hours != 0 {
|
||||
return fmt.Sprintf("%s%dh %dm %ds", sign, hours, minutes, seconds)
|
||||
}
|
||||
if minutes != 0 {
|
||||
return fmt.Sprintf("%s%dm %ds", sign, minutes, seconds)
|
||||
}
|
||||
// For seconds, we display 4 significant digits.
|
||||
return fmt.Sprintf("%s%.4gs", sign, v)
|
||||
}
|
||||
prefix := ""
|
||||
for _, p := range []string{"m", "u", "n", "p", "f", "a", "z", "y"} {
|
||||
if math.Abs(v) >= 1 {
|
||||
break
|
||||
}
|
||||
prefix = p
|
||||
v *= 1000
|
||||
}
|
||||
return fmt.Sprintf("%.4g%ss", v, prefix)
|
||||
},
|
||||
"humanizePercentage": func(v float64) string {
|
||||
return fmt.Sprintf("%.4g%%", v*100)
|
||||
},
|
||||
"humanizeTimestamp": func(v float64) string {
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
return fmt.Sprintf("%.4g", v)
|
||||
}
|
||||
t := TimeFromUnixNano(int64(v * 1e9)).Time().UTC()
|
||||
return fmt.Sprint(t)
|
||||
},
|
||||
"pathPrefix": func() string {
|
||||
return externalURL.Path
|
||||
},
|
||||
"externalURL": func() string {
|
||||
return externalURL.String()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Time is the number of milliseconds since the epoch
|
||||
// (1970-01-01 00:00 UTC) excluding leap seconds.
|
||||
type Time int64
|
||||
|
||||
// TimeFromUnixNano returns the Time equivalent to the Unix Time
|
||||
// t provided in nanoseconds.
|
||||
func TimeFromUnixNano(t int64) Time {
|
||||
return Time(t / nanosPerTick)
|
||||
}
|
||||
|
||||
// The number of nanoseconds per minimum tick.
|
||||
const nanosPerTick = int64(minimumTick / time.Nanosecond)
|
||||
|
||||
// MinimumTick is the minimum supported time resolution. This has to be
|
||||
// at least time.Second in order for the code below to work.
|
||||
const minimumTick = time.Millisecond
|
||||
|
||||
// second is the Time duration equivalent to one second.
|
||||
const second = int64(time.Second / minimumTick)
|
||||
|
||||
// Time returns the time.Time representation of t.
|
||||
func (t Time) Time() time.Time {
|
||||
return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick)
|
||||
}
|
||||
@@ -1,32 +1,64 @@
|
||||
package config
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
// Annotations basic annotation for alert rule
|
||||
type Annotations struct {
|
||||
Summary string
|
||||
Description string
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/common"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Parse parses rule configs from given file patterns
|
||||
func Parse(pathPatterns []string, validateAnnotations bool) ([]common.Group, error) {
|
||||
var fp []string
|
||||
for _, pattern := range pathPatterns {
|
||||
matches, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading file patther %s:%v", pattern, err)
|
||||
}
|
||||
fp = append(fp, matches...)
|
||||
}
|
||||
var groups []common.Group
|
||||
for _, file := range fp {
|
||||
groupsNames := map[string]struct{}{}
|
||||
gr, err := parseFile(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("file %s: %w", file, err)
|
||||
}
|
||||
for _, group := range gr {
|
||||
if _, ok := groupsNames[group.Name]; ok {
|
||||
return nil, fmt.Errorf("one file can not contain groups with the same name %s, filepath:%s", file, group.Name)
|
||||
}
|
||||
groupsNames[group.Name] = struct{}{}
|
||||
for _, rule := range group.Rules {
|
||||
if err = rule.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("invalid rule filepath:%s, group %s:%w", file, group.Name, err)
|
||||
}
|
||||
if validateAnnotations {
|
||||
if err = common.ValidateAnnotations(rule.Annotations); err != nil {
|
||||
return nil, fmt.Errorf("invalida annotations filepath:%s, group %s:%w", file, group.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
groups = append(groups, gr...)
|
||||
}
|
||||
if len(groups) < 1 {
|
||||
return nil, fmt.Errorf("no groups found in %s", strings.Join(pathPatterns, ";"))
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
// Alert basic alert entity rule
|
||||
type Alert struct {
|
||||
Name string
|
||||
Expr string
|
||||
For time.Duration
|
||||
Labels map[string]string
|
||||
Annotations Annotations
|
||||
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
// Group grouping array of alert
|
||||
type Group struct {
|
||||
Name string
|
||||
Rules []Alert
|
||||
}
|
||||
|
||||
// Parse parses config from given file
|
||||
func Parse(filepath string) ([]Group, error) {
|
||||
return []Group{}, nil
|
||||
func parseFile(path string) ([]common.Group, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading alert rule file: %w", err)
|
||||
}
|
||||
g := struct {
|
||||
Groups []common.Group `yaml:"groups"`
|
||||
}{}
|
||||
err = yaml.Unmarshal(data, &g)
|
||||
return g.Groups, err
|
||||
}
|
||||
|
||||
36
app/vmalert/config/parser_test.go
Normal file
36
app/vmalert/config/parser_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/common"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
u, _ := url.Parse("https://victoriametrics.com/path")
|
||||
common.InitTemplateFunc(u)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestParseGood(t *testing.T) {
|
||||
if _, err := Parse([]string{"testdata/*good.rules", "testdata/dir/*good.*"}, true); err != nil {
|
||||
t.Errorf("error parsing files %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBad(t *testing.T) {
|
||||
if _, err := Parse([]string{"testdata/rules0-bad.rules"}, true); err == nil {
|
||||
t.Errorf("expected syntaxt error")
|
||||
}
|
||||
if _, err := Parse([]string{"testdata/dir/rules0-bad.rules"}, true); err == nil {
|
||||
t.Errorf("expected template annotation error")
|
||||
}
|
||||
if _, err := Parse([]string{"testdata/dir/rules1-bad.rules"}, true); err == nil {
|
||||
t.Errorf("expected same group error")
|
||||
}
|
||||
if _, err := Parse([]string{"testdata/*.yaml"}, true); err == nil {
|
||||
t.Errorf("expected empty group")
|
||||
}
|
||||
}
|
||||
19
app/vmalert/config/testdata/dir/rules0-bad.rules
vendored
Normal file
19
app/vmalert/config/testdata/dir/rules0-bad.rules
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
groups:
|
||||
- name: group
|
||||
rules:
|
||||
- alert: InvalidAnnotations
|
||||
for: 5m
|
||||
expr: vm_rows > 0
|
||||
labels:
|
||||
label: bar
|
||||
annotations:
|
||||
summary: "{{ $value }"
|
||||
description: "{{$labels}}"
|
||||
- alert: UnkownAnnotationsFunction
|
||||
for: 5m
|
||||
expr: vm_rows > 0
|
||||
labels:
|
||||
label: bar
|
||||
annotations:
|
||||
summary: "{{ value|query }}"
|
||||
description: "{{$labels}}"
|
||||
13
app/vmalert/config/testdata/dir/rules0-good.rules
vendored
Normal file
13
app/vmalert/config/testdata/dir/rules0-good.rules
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
groups:
|
||||
- name: duplicatedGroupDiffFiles
|
||||
rules:
|
||||
- alert: VMRows
|
||||
for: 5m
|
||||
expr: vm_rows > 0
|
||||
labels:
|
||||
label: bar
|
||||
annotations:
|
||||
summary: "{{ $value|humanize }}"
|
||||
description: "{{$labels}}"
|
||||
|
||||
|
||||
22
app/vmalert/config/testdata/dir/rules1-bad.rules
vendored
Normal file
22
app/vmalert/config/testdata/dir/rules1-bad.rules
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
groups:
|
||||
- name: sameGroup
|
||||
rules:
|
||||
- alert: alert
|
||||
for: 5m
|
||||
expr: vm_rows > 0
|
||||
labels:
|
||||
label: bar
|
||||
annotations:
|
||||
summary: "{{ $value }}"
|
||||
description: "{{$labels}}"
|
||||
- name: sameGroup
|
||||
rules:
|
||||
- alert: alert
|
||||
for: 5m
|
||||
expr: vm_rows > 0
|
||||
labels:
|
||||
label: bar
|
||||
annotations:
|
||||
summary: "{{ $value }}"
|
||||
description: "{{$labels}}"
|
||||
|
||||
13
app/vmalert/config/testdata/dir/rules1-good.rules
vendored
Normal file
13
app/vmalert/config/testdata/dir/rules1-good.rules
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
groups:
|
||||
- name: duplicatedGroupDiffFiles
|
||||
rules:
|
||||
- alert: VMRows
|
||||
for: 5m
|
||||
expr: vm_rows > 0
|
||||
labels:
|
||||
label: bar
|
||||
annotations:
|
||||
summary: "{{ $value }}"
|
||||
description: "{{$labels}}"
|
||||
|
||||
|
||||
28
app/vmalert/config/testdata/rules0-bad.rules
vendored
Normal file
28
app/vmalert/config/testdata/rules0-bad.rules
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
groups:
|
||||
- name: group
|
||||
rules:
|
||||
- alert: InvalidExpr
|
||||
for: 5m
|
||||
expr: vm_rows{ > 0
|
||||
labels:
|
||||
label: bar
|
||||
annotations:
|
||||
summary: "{{ $value }}"
|
||||
description: "{{$labels}}"
|
||||
- alert: EmptyExpr
|
||||
for: 5m
|
||||
expr: ""
|
||||
labels:
|
||||
label: bar
|
||||
annotations:
|
||||
summary: "{{ $value }}"
|
||||
description: "{{$labels}}"
|
||||
- alert: ""
|
||||
for: 5m
|
||||
expr: vm_rows > 0
|
||||
labels:
|
||||
label: foo
|
||||
annotations:
|
||||
summary: "{{ $value }}"
|
||||
description: "{{$labels}}"
|
||||
|
||||
12
app/vmalert/config/testdata/rules0-good.rules
vendored
Normal file
12
app/vmalert/config/testdata/rules0-good.rules
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
groups:
|
||||
- name: groupGorSingleAlert
|
||||
rules:
|
||||
- alert: VMRows
|
||||
for: 5m
|
||||
expr: vm_rows > 0
|
||||
labels:
|
||||
label: bar
|
||||
annotations:
|
||||
summary: "{{ $value|humanize }}"
|
||||
description: "{{$labels}}"
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package datasource
|
||||
|
||||
import "context"
|
||||
|
||||
// Metrics the data returns from storage
|
||||
type Metrics struct{}
|
||||
|
||||
// VMStorage represents vmstorage entity with ability to read and write metrics
|
||||
type VMStorage struct{}
|
||||
|
||||
//Query basic query to the datasource
|
||||
func (s *VMStorage) Query(ctx context.Context, query string) ([]Metrics, error) {
|
||||
return nil, nil
|
||||
// Metric is the basic entity which should be return by datasource
|
||||
// It represents single data point with full list of labels
|
||||
type Metric struct {
|
||||
Labels []Label
|
||||
Timestamp int64
|
||||
Value float64
|
||||
}
|
||||
|
||||
// Label represents metric's label
|
||||
type Label struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
103
app/vmalert/datasource/vm.go
Normal file
103
app/vmalert/datasource/vm.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type response struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
ResultType string `json:"resultType"`
|
||||
Result []struct {
|
||||
Labels map[string]string `json:"metric"`
|
||||
TV [2]interface{} `json:"value"`
|
||||
} `json:"result"`
|
||||
} `json:"data"`
|
||||
ErrorType string `json:"errorType"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func (r response) metrics() ([]Metric, error) {
|
||||
var ms []Metric
|
||||
var m Metric
|
||||
var f float64
|
||||
var err error
|
||||
for i, res := range r.Data.Result {
|
||||
f, err = strconv.ParseFloat(res.TV[1].(string), 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("metric %v, unable to parse float64 from %s: %s", res, res.TV[1], err)
|
||||
}
|
||||
m.Labels = nil
|
||||
for k, v := range r.Data.Result[i].Labels {
|
||||
m.Labels = append(m.Labels, Label{Name: k, Value: v})
|
||||
}
|
||||
m.Timestamp = int64(res.TV[0].(float64))
|
||||
m.Value = f
|
||||
ms = append(ms, m)
|
||||
}
|
||||
return ms, nil
|
||||
}
|
||||
|
||||
const queryPath = "/api/v1/query?query="
|
||||
|
||||
// VMStorage represents vmstorage entity with ability to read and write metrics
|
||||
type VMStorage struct {
|
||||
c *http.Client
|
||||
queryURL string
|
||||
basicAuthUser, basicAuthPass string
|
||||
}
|
||||
|
||||
// NewVMStorage is a constructor for VMStorage
|
||||
func NewVMStorage(baseURL, basicAuthUser, basicAuthPass string, c *http.Client) *VMStorage {
|
||||
return &VMStorage{
|
||||
c: c,
|
||||
basicAuthUser: basicAuthUser,
|
||||
basicAuthPass: basicAuthPass,
|
||||
queryURL: strings.TrimSuffix(baseURL, "/") + queryPath,
|
||||
}
|
||||
}
|
||||
|
||||
// Query reads metrics from datasource by given query
|
||||
func (s *VMStorage) Query(ctx context.Context, query string) ([]Metric, error) {
|
||||
const (
|
||||
statusSuccess, statusError, rtVector = "success", "error", "vector"
|
||||
)
|
||||
req, err := http.NewRequest("POST", s.queryURL+url.QueryEscape(query), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if s.basicAuthPass != "" {
|
||||
req.SetBasicAuth(s.basicAuthUser, s.basicAuthPass)
|
||||
}
|
||||
resp, err := s.c.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting response from %s:%s", req.URL, err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("datasource returns unxeprected response code %d for %s with err %s. Reponse body %s", resp.StatusCode, req.URL, err, body)
|
||||
}
|
||||
r := &response{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(r); err != nil {
|
||||
return nil, fmt.Errorf("error parsing metrics for %s:%s", req.URL, err)
|
||||
}
|
||||
if r.Status == statusError {
|
||||
return nil, fmt.Errorf("response error, query: %s, errorType: %s, error: %s", req.URL, r.ErrorType, r.Error)
|
||||
}
|
||||
if r.Status != statusSuccess {
|
||||
return nil, fmt.Errorf("unkown status:%s, Expected success or error ", r.Status)
|
||||
}
|
||||
if r.Data.ResultType != rtVector {
|
||||
return nil, fmt.Errorf("unkown restul type:%s. Expected vector", r.Data.ResultType)
|
||||
}
|
||||
return r.metrics()
|
||||
}
|
||||
93
app/vmalert/datasource/vm_test.go
Normal file
93
app/vmalert/datasource/vm_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.Background()
|
||||
basicAuthName = "foo"
|
||||
basicAuthPass = "bar"
|
||||
query = "vm_rows"
|
||||
)
|
||||
|
||||
func TestVMSelectQuery(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(_ http.ResponseWriter, _ *http.Request) {
|
||||
t.Errorf("should not be called")
|
||||
})
|
||||
c := -1
|
||||
mux.HandleFunc("/api/v1/query", func(w http.ResponseWriter, r *http.Request) {
|
||||
c++
|
||||
if r.Method != http.MethodPost {
|
||||
t.Errorf("expected POST method got %s", r.Method)
|
||||
}
|
||||
if name, pass, _ := r.BasicAuth(); name != basicAuthName || pass != basicAuthPass {
|
||||
t.Errorf("expected %s:%s as basic auth got %s:%s", basicAuthName, basicAuthPass, name, pass)
|
||||
}
|
||||
if r.URL.Query().Get("query") != query {
|
||||
t.Errorf("exptected %s in query param, got %s", query, r.URL.Query().Get("query"))
|
||||
}
|
||||
switch c {
|
||||
case 0:
|
||||
conn, _, _ := w.(http.Hijacker).Hijack()
|
||||
_ = conn.Close()
|
||||
case 1:
|
||||
w.WriteHeader(500)
|
||||
case 2:
|
||||
w.Write([]byte("[]"))
|
||||
case 3:
|
||||
w.Write([]byte(`{"status":"error", "errorType":"type:", "error":"some error msg"}`))
|
||||
case 4:
|
||||
w.Write([]byte(`{"status":"unknown"}`))
|
||||
case 5:
|
||||
w.Write([]byte(`{"status":"success","data":{"resultType":"matrix"}}`))
|
||||
case 6:
|
||||
w.Write([]byte(`{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"vm_rows"},"value":[1583786142,"13763"]}]}}`))
|
||||
}
|
||||
})
|
||||
|
||||
srv := httptest.NewServer(mux)
|
||||
defer srv.Close()
|
||||
am := NewVMStorage(srv.URL, basicAuthName, basicAuthPass, srv.Client())
|
||||
if _, err := am.Query(ctx, query); err == nil {
|
||||
t.Fatalf("expected connection error got nil")
|
||||
}
|
||||
if _, err := am.Query(ctx, query); err == nil {
|
||||
t.Fatalf("expected invalid response status error got nil")
|
||||
}
|
||||
if _, err := am.Query(ctx, query); err == nil {
|
||||
t.Fatalf("expected response body error got nil")
|
||||
}
|
||||
if _, err := am.Query(ctx, query); err == nil {
|
||||
t.Fatalf("expected error status got nil")
|
||||
}
|
||||
if _, err := am.Query(ctx, query); err == nil {
|
||||
t.Fatalf("expected unkown status got nil")
|
||||
}
|
||||
if _, err := am.Query(ctx, query); err == nil {
|
||||
t.Fatalf("expected non-vector resultType error got nil")
|
||||
}
|
||||
m, err := am.Query(ctx, query)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected %s", err)
|
||||
}
|
||||
if len(m) != 1 {
|
||||
t.Fatalf("exptected 1 metric got %d in %+v", len(m), m)
|
||||
}
|
||||
expected := Metric{
|
||||
Labels: []Label{{Value: "vm_rows", Name: "__name__"}},
|
||||
Timestamp: 1583786142,
|
||||
Value: 13763,
|
||||
}
|
||||
if m[0].Timestamp != expected.Timestamp &&
|
||||
m[0].Value != expected.Value &&
|
||||
m[0].Labels[0].Value != expected.Labels[0].Value &&
|
||||
m[0].Labels[0].Name != expected.Labels[0].Name {
|
||||
t.Fatalf("unexpected metric %+v want %+v", m[0], expected)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/common"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/config"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/datasource"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/provider"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/buildinfo"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/envflag"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/procutil"
|
||||
)
|
||||
|
||||
var (
|
||||
configPath = flag.String("config", "config.yaml", "Path to alert configuration file")
|
||||
httpListenAddr = flag.String("httpListenAddr", ":8880", "Address to listen for http connections")
|
||||
rulePath = flagutil.NewArray("rule", `Path to file with alert rules, accepts patterns.
|
||||
Flag can be specified multiple time.
|
||||
Examples:
|
||||
-rule /path/to/file. Path to single file with alerting rules
|
||||
-rule dir/*.yaml -rule /*.yaml. Paths to all yaml files in relative dir folder and absolute yaml file in a root.`)
|
||||
validateAlertAnnotations = flag.Bool("rule.validateAnnotations", true, "Indicates to validate annotation templates")
|
||||
httpListenAddr = flag.String("httpListenAddr", ":8880", "Address to listen for http connections")
|
||||
datasourceURL = flag.String("datasource.url", "", "Victoria Metrics or VMSelect url. Required parameter. e.g. http://127.0.0.1:8428")
|
||||
basicAuthUsername = flag.String("datasource.basicAuth.username", "", "Optional basic auth username to use for -datasource.url")
|
||||
basicAuthPassword = flag.String("datasource.basicAuth.password", "", "Optional basic auth password to use for -datasource.url")
|
||||
evaluationInterval = flag.Duration("evaluationInterval", 1*time.Minute, "How often to evaluate the rules. Default 1m")
|
||||
providerURL = flag.String("provider.url", "", "Prometheus alertmanager url. Required parameter. e.g. http://127.0.0.1:9093")
|
||||
externalURL = flag.String("external.url", "", "Reachable external url. URL is used to generate sharable alert url and in annotation templates")
|
||||
)
|
||||
|
||||
func main() {
|
||||
envflag.Parse()
|
||||
buildinfo.Init()
|
||||
logger.Init()
|
||||
|
||||
logger.Infof("reading alert rules configuration file from %s", *configPath)
|
||||
alertGroups, err := config.Parse(*configPath)
|
||||
checkFlags()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
eu, err := getExternalURL(*externalURL, *httpListenAddr, httpserver.IsTLS())
|
||||
if err != nil {
|
||||
logger.Fatalf("Cannot parse configuration file %s", err)
|
||||
logger.Fatalf("can not get external url:%s ", err)
|
||||
}
|
||||
common.InitTemplateFunc(eu)
|
||||
|
||||
logger.Infof("reading alert rules configuration file from %s", strings.Join(*rulePath, ";"))
|
||||
alertGroups, err := config.Parse(*rulePath, *validateAlertAnnotations)
|
||||
if err != nil {
|
||||
logger.Fatalf("Cannot parse configuration file: %s", err)
|
||||
}
|
||||
|
||||
w := &watchdog{
|
||||
storage: datasource.NewVMStorage(*datasourceURL, *basicAuthUsername, *basicAuthPassword, &http.Client{}),
|
||||
alertProvider: provider.NewAlertManager(*providerURL, func(group, name string) string {
|
||||
return strings.Replace(fmt.Sprintf("%s/%s/%s/status", eu, group, name), "//", "/", -1)
|
||||
}, &http.Client{}),
|
||||
}
|
||||
w := &watchdog{storage: &datasource.VMStorage{}}
|
||||
for id := range alertGroups {
|
||||
go func(group config.Group) {
|
||||
w.run(group)
|
||||
go func(group common.Group) {
|
||||
w.run(ctx, group, *evaluationInterval)
|
||||
}(alertGroups[id])
|
||||
}
|
||||
go func() {
|
||||
@@ -44,17 +77,82 @@ func main() {
|
||||
if err := httpserver.Stop(*httpListenAddr); err != nil {
|
||||
logger.Fatalf("cannot stop the webservice: %s", err)
|
||||
}
|
||||
cancel()
|
||||
w.stop()
|
||||
}
|
||||
|
||||
type watchdog struct {
|
||||
storage *datasource.VMStorage
|
||||
storage *datasource.VMStorage
|
||||
alertProvider provider.AlertProvider
|
||||
}
|
||||
|
||||
func (w *watchdog) run(a config.Group) {
|
||||
func (w *watchdog) run(ctx context.Context, a common.Group, evaluationInterval time.Duration) {
|
||||
logger.Infof("watchdog for %s has been run", a.Name)
|
||||
t := time.NewTicker(evaluationInterval)
|
||||
var metrics []datasource.Metric
|
||||
var err error
|
||||
var alerts []common.Alert
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
start := time.Now()
|
||||
for _, r := range a.Rules {
|
||||
if metrics, err = w.storage.Query(ctx, r.Expr); err != nil {
|
||||
logger.Errorf("error reading metrics %s", err)
|
||||
continue
|
||||
}
|
||||
// todo check for and calculate alert states
|
||||
if len(metrics) < 1 {
|
||||
continue
|
||||
}
|
||||
// todo define alert end time
|
||||
alerts = common.AlertsFromMetrics(metrics, a.Name, r, start, time.Time{})
|
||||
// todo save to storage
|
||||
if err := w.alertProvider.Send(alerts); err != nil {
|
||||
logger.Errorf("error sending alerts %s", err)
|
||||
continue
|
||||
}
|
||||
// todo is alert still active/pending?
|
||||
}
|
||||
|
||||
case <-ctx.Done():
|
||||
logger.Infof("%s receive stop signal", a.Name)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getExternalURL(externalURL, httpListenAddr string, isSecure bool) (*url.URL, error) {
|
||||
if externalURL != "" {
|
||||
return url.Parse(externalURL)
|
||||
}
|
||||
hname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port := ""
|
||||
if ipport := strings.Split(httpListenAddr, ":"); len(ipport) > 1 {
|
||||
port = ":" + ipport[1]
|
||||
}
|
||||
schema := "http://"
|
||||
if isSecure {
|
||||
schema = "https://"
|
||||
}
|
||||
return url.Parse(fmt.Sprintf("%s%s%s", schema, hname, port))
|
||||
}
|
||||
|
||||
func (w *watchdog) stop() {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func checkFlags() {
|
||||
if *providerURL == "" {
|
||||
flag.PrintDefaults()
|
||||
logger.Fatalf("provider.url is empty")
|
||||
}
|
||||
if *datasourceURL == "" {
|
||||
flag.PrintDefaults()
|
||||
logger.Fatalf("datasource.url is empty")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,34 @@
|
||||
{% import (
|
||||
"time"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/config"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/common"
|
||||
) %}
|
||||
{% stripspace %}
|
||||
|
||||
{% func amRequest(alert *config.Alert, generatorURL string) %}
|
||||
{% func amRequest(alerts []common.Alert, generatorURL func(string, string) string) %}
|
||||
[
|
||||
{% for i, alert := range alerts %}
|
||||
{
|
||||
"startsAt":{%q= alert.Start.Format(time.RFC3339Nano) %},
|
||||
"generatorURL": {%q= generatorURL %},
|
||||
"generatorURL": {%q= generatorURL(alert.Group, alert.Name) %},
|
||||
{% if !alert.End.IsZero() %}
|
||||
"endsAt":{%q= alert.End.Format(time.RFC3339Nano) %},
|
||||
{% endif %}
|
||||
"labels": {
|
||||
"alertname":{%q= alert.Name %}
|
||||
{% for k,v := range alert.Labels %}
|
||||
,{%q= k %}:{%q= v %}
|
||||
{% for _,v := range alert.Labels %}
|
||||
,{%q= v.Name %}:{%q= v.Value %}
|
||||
{% endfor %}
|
||||
},
|
||||
"annotations": {
|
||||
"summary": {%q= alert.Annotations.Summary %},
|
||||
"description": {%q= alert.Annotations.Description %}
|
||||
{% code c := len(alert.Annotations) %}
|
||||
{% for k,v := range alert.Annotations %}
|
||||
{% code c = c-1 %}
|
||||
{%q= k %}:{%q= v %}{% if c > 0 %},{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
||||
{% if i != len(alerts)-1 %},{% endif %}
|
||||
{% endfor %}
|
||||
]
|
||||
{% endfunc %}
|
||||
{% endstripspace %}
|
||||
|
||||
@@ -6,7 +6,7 @@ package provider
|
||||
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:1
|
||||
import (
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/config"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -24,78 +24,108 @@ var (
|
||||
)
|
||||
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:7
|
||||
func streamamRequest(qw422016 *qt422016.Writer, alert *config.Alert, generatorURL string) {
|
||||
func streamamRequest(qw422016 *qt422016.Writer, alerts []common.Alert, generatorURL func(string, string) string) {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:7
|
||||
qw422016.N().S(`{"startsAt":`)
|
||||
qw422016.N().S(`[`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:9
|
||||
qw422016.N().Q(alert.Start.Format(time.RFC3339Nano))
|
||||
for i, alert := range alerts {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:9
|
||||
qw422016.N().S(`,"generatorURL":`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:10
|
||||
qw422016.N().Q(generatorURL)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:10
|
||||
qw422016.N().S(`,`)
|
||||
qw422016.N().S(`{"startsAt":`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:11
|
||||
if !alert.End.IsZero() {
|
||||
qw422016.N().Q(alert.Start.Format(time.RFC3339Nano))
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:11
|
||||
qw422016.N().S(`"endsAt":`)
|
||||
qw422016.N().S(`,"generatorURL":`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:12
|
||||
qw422016.N().Q(alert.End.Format(time.RFC3339Nano))
|
||||
qw422016.N().Q(generatorURL(alert.Group, alert.Name))
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:12
|
||||
qw422016.N().S(`,`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:13
|
||||
}
|
||||
if !alert.End.IsZero() {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:13
|
||||
qw422016.N().S(`"labels": {"alertname":`)
|
||||
qw422016.N().S(`"endsAt":`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:14
|
||||
qw422016.N().Q(alert.End.Format(time.RFC3339Nano))
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:14
|
||||
qw422016.N().S(`,`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:15
|
||||
qw422016.N().Q(alert.Name)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:16
|
||||
for k, v := range alert.Labels {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:16
|
||||
qw422016.N().S(`,`)
|
||||
}
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:15
|
||||
qw422016.N().S(`"labels": {"alertname":`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:17
|
||||
qw422016.N().Q(k)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:17
|
||||
qw422016.N().S(`:`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:17
|
||||
qw422016.N().Q(v)
|
||||
qw422016.N().Q(alert.Name)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:18
|
||||
for _, v := range alert.Labels {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:18
|
||||
qw422016.N().S(`,`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:19
|
||||
qw422016.N().Q(v.Name)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:19
|
||||
qw422016.N().S(`:`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:19
|
||||
qw422016.N().Q(v.Value)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:20
|
||||
}
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:20
|
||||
qw422016.N().S(`},"annotations": {`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:23
|
||||
c := len(alert.Annotations)
|
||||
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:24
|
||||
for k, v := range alert.Annotations {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
c = c - 1
|
||||
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:26
|
||||
qw422016.N().Q(k)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:26
|
||||
qw422016.N().S(`:`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:26
|
||||
qw422016.N().Q(v)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:26
|
||||
if c > 0 {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:26
|
||||
qw422016.N().S(`,`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:26
|
||||
}
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:27
|
||||
}
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:27
|
||||
qw422016.N().S(`}}`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:30
|
||||
if i != len(alerts)-1 {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:30
|
||||
qw422016.N().S(`,`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:30
|
||||
}
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:31
|
||||
}
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:18
|
||||
qw422016.N().S(`},"annotations": {"summary":`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:21
|
||||
qw422016.N().Q(alert.Annotations.Summary)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:21
|
||||
qw422016.N().S(`,"description":`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:22
|
||||
qw422016.N().Q(alert.Annotations.Description)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:22
|
||||
qw422016.N().S(`}}`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:31
|
||||
qw422016.N().S(`]`)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
}
|
||||
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
func writeamRequest(qq422016 qtio422016.Writer, alert *config.Alert, generatorURL string) {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
func writeamRequest(qq422016 qtio422016.Writer, alerts []common.Alert, generatorURL func(string, string) string) {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
streamamRequest(qw422016, alert, generatorURL)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
streamamRequest(qw422016, alerts, generatorURL)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
}
|
||||
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
func amRequest(alert *config.Alert, generatorURL string) string {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
func amRequest(alerts []common.Alert, generatorURL func(string, string) string) string {
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
writeamRequest(qb422016, alert, generatorURL)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
writeamRequest(qb422016, alerts, generatorURL)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
return qs422016
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:25
|
||||
//line app/vmalert/provider/alert_manager_request.qtpl:33
|
||||
}
|
||||
|
||||
@@ -8,12 +8,17 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/config"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/common"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
)
|
||||
|
||||
const alertsPath = "/api/v2/alerts"
|
||||
|
||||
// AlertProvider is common interface for alert manager provider
|
||||
type AlertProvider interface {
|
||||
Send(alerts []common.Alert) error
|
||||
}
|
||||
|
||||
var pool = sync.Pool{New: func() interface{} {
|
||||
return &bytes.Buffer{}
|
||||
}}
|
||||
@@ -26,7 +31,7 @@ type AlertManager struct {
|
||||
}
|
||||
|
||||
// AlertURLGenerator returns URL to single alert by given name
|
||||
type AlertURLGenerator func(name string) string
|
||||
type AlertURLGenerator func(group, name string) string
|
||||
|
||||
// NewAlertManager is a constructor for AlertManager
|
||||
func NewAlertManager(alertManagerURL string, fn AlertURLGenerator, c *http.Client) *AlertManager {
|
||||
@@ -37,19 +42,12 @@ func NewAlertManager(alertManagerURL string, fn AlertURLGenerator, c *http.Clien
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
jsonArrayOpen byte = 91 // [
|
||||
jsonArrayClose byte = 93 // ]
|
||||
)
|
||||
|
||||
// Send an alert or resolve message
|
||||
func (am *AlertManager) Send(alert *config.Alert) error {
|
||||
func (am *AlertManager) Send(alerts []common.Alert) error {
|
||||
b := pool.Get().(*bytes.Buffer)
|
||||
b.Reset()
|
||||
defer pool.Put(b)
|
||||
b.WriteByte(jsonArrayOpen)
|
||||
writeamRequest(b, alert, am.argFunc(alert.Name))
|
||||
b.WriteByte(jsonArrayClose)
|
||||
writeamRequest(b, alerts, am.argFunc)
|
||||
resp, err := am.client.Post(am.alertURL, "application/json", b)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/config"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/common"
|
||||
)
|
||||
|
||||
func TestAlertManager_Send(t *testing.T) {
|
||||
@@ -42,7 +42,7 @@ func TestAlertManager_Send(t *testing.T) {
|
||||
if len(a) != 1 {
|
||||
t.Errorf("expected 1 alert in array got %d", len(a))
|
||||
}
|
||||
if a[0].GeneratorURL != "alert0" {
|
||||
if a[0].GeneratorURL != "group0alert0" {
|
||||
t.Errorf("exptected alert0 as generatorURL got %s", a[0].GeneratorURL)
|
||||
}
|
||||
if a[0].Labels["alertname"] != "alert0" {
|
||||
@@ -58,20 +58,22 @@ func TestAlertManager_Send(t *testing.T) {
|
||||
})
|
||||
srv := httptest.NewServer(mux)
|
||||
defer srv.Close()
|
||||
am := NewAlertManager(srv.URL, func(name string) string {
|
||||
return name
|
||||
am := NewAlertManager(srv.URL, func(group, name string) string {
|
||||
return group + name
|
||||
}, srv.Client())
|
||||
if err := am.Send(&config.Alert{}); err == nil {
|
||||
if err := am.Send([]common.Alert{{}, {}}); err == nil {
|
||||
t.Error("expected connection error got nil")
|
||||
}
|
||||
if err := am.Send(&config.Alert{}); err == nil {
|
||||
if err := am.Send([]common.Alert{}); err == nil {
|
||||
t.Error("expected wrong http code error got nil")
|
||||
}
|
||||
if err := am.Send(&config.Alert{
|
||||
Name: "alert0",
|
||||
Start: time.Now().UTC(),
|
||||
End: time.Now().UTC(),
|
||||
}); err != nil {
|
||||
if err := am.Send([]common.Alert{{
|
||||
Group: "group0",
|
||||
Name: "alert0",
|
||||
Start: time.Now().UTC(),
|
||||
End: time.Now().UTC(),
|
||||
Annotations: map[string]string{"a": "b", "c": "d", "e": "f"},
|
||||
}}); err != nil {
|
||||
t.Errorf("unexpected error %s", err)
|
||||
}
|
||||
if c != 2 {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package provider
|
||||
|
||||
import "github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/config"
|
||||
|
||||
// AlertProvider is common interface for alert manager provider
|
||||
type AlertProvider interface {
|
||||
Send(rule config.Alert) error
|
||||
}
|
||||
1
app/vmalert/storage/memory.go
Normal file
1
app/vmalert/storage/memory.go
Normal file
@@ -0,0 +1 @@
|
||||
package storage
|
||||
@@ -1,7 +1,6 @@
|
||||
ARG certs_image
|
||||
FROM $certs_image AS certs
|
||||
FROM scratch
|
||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
ARG base_image
|
||||
FROM $base_image
|
||||
|
||||
ENTRYPOINT ["/vmbackup-prod"]
|
||||
ARG src_binary
|
||||
COPY $src_binary ./vmbackup-prod
|
||||
ENTRYPOINT ["/vmbackup-prod"]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
ARG certs_image
|
||||
FROM $certs_image AS certs
|
||||
FROM scratch
|
||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
ARG base_image
|
||||
FROM $base_image
|
||||
|
||||
ENTRYPOINT ["/vmrestore-prod"]
|
||||
ARG src_binary
|
||||
COPY $src_binary ./vmrestore-prod
|
||||
ENTRYPOINT ["/vmrestore-prod"]
|
||||
|
||||
@@ -576,6 +576,7 @@ func setupTfss(tagFilterss [][]storage.TagFilter) ([]*storage.TagFilters, error)
|
||||
}
|
||||
}
|
||||
tfss = append(tfss, tfs)
|
||||
tfss = append(tfss, tfs.Finalize()...)
|
||||
}
|
||||
return tfss, nil
|
||||
}
|
||||
|
||||
@@ -576,8 +576,7 @@ func QueryHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
queryOffset := getLatencyOffsetMilliseconds()
|
||||
step, err := getDuration(r, "step", queryOffset)
|
||||
step, err := getDuration(r, "step", defaultStep)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -590,6 +589,7 @@ func QueryHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) e
|
||||
if len(query) > *maxQueryLen {
|
||||
return fmt.Errorf("too long query; got %d bytes; mustn't exceed `-search.maxQueryLen=%d` bytes", len(query), *maxQueryLen)
|
||||
}
|
||||
queryOffset := getLatencyOffsetMilliseconds()
|
||||
if !getBool(r, "nocache") && ct-start < queryOffset {
|
||||
// Adjust start time only if `nocache` arg isn't set.
|
||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/241
|
||||
@@ -840,7 +840,15 @@ func getTime(r *http.Request, argKey string, defaultValue int64) (int64, error)
|
||||
case prometheusMaxTimeFormatted:
|
||||
return maxTimeMsecs, nil
|
||||
}
|
||||
return 0, fmt.Errorf("cannot parse %q=%q: %s", argKey, argValue, err)
|
||||
// Try parsing duration relative to the current time
|
||||
d, err1 := time.ParseDuration(argValue)
|
||||
if err1 != nil {
|
||||
return 0, fmt.Errorf("cannot parse %q=%q: %s", argKey, argValue, err)
|
||||
}
|
||||
if d > 0 {
|
||||
d = -d
|
||||
}
|
||||
t = time.Now().Add(d)
|
||||
}
|
||||
secs = float64(t.UnixNano()) / 1e9
|
||||
}
|
||||
@@ -891,17 +899,17 @@ func getDuration(r *http.Request, argKey string, defaultValue int64) (int64, err
|
||||
const maxDurationMsecs = 100 * 365 * 24 * 3600 * 1000
|
||||
|
||||
func getMaxLookback(r *http.Request) (int64, error) {
|
||||
d := int64(*maxLookback / time.Millisecond)
|
||||
d := maxLookback.Milliseconds()
|
||||
return getDuration(r, "max_lookback", d)
|
||||
}
|
||||
|
||||
func getDeadlineForQuery(r *http.Request) netstorage.Deadline {
|
||||
dMax := int64(maxQueryDuration.Seconds() * 1e3)
|
||||
dMax := maxQueryDuration.Milliseconds()
|
||||
return getDeadlineWithMaxDuration(r, dMax, "-search.maxQueryDuration")
|
||||
}
|
||||
|
||||
func getDeadlineForExport(r *http.Request) netstorage.Deadline {
|
||||
dMax := int64(maxExportDuration.Seconds() * 1e3)
|
||||
dMax := maxExportDuration.Milliseconds()
|
||||
return getDeadlineWithMaxDuration(r, dMax, "-search.maxExportDuration")
|
||||
}
|
||||
|
||||
@@ -944,7 +952,7 @@ func getTagFilterssFromMatches(matches []string) ([][]storage.TagFilter, error)
|
||||
}
|
||||
|
||||
func getLatencyOffsetMilliseconds() int64 {
|
||||
d := int64(*latencyOffset / time.Millisecond)
|
||||
d := latencyOffset.Milliseconds()
|
||||
if d <= 1000 {
|
||||
d = 1000
|
||||
}
|
||||
|
||||
@@ -974,6 +974,65 @@ func TestExecSuccess(t *testing.T) {
|
||||
resultExpected := []netstorage.Result{r}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`label_map(match)`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `sort(label_map((
|
||||
label_set(time(), "label", "v1"),
|
||||
label_set(time()+100, "label", "v2"),
|
||||
label_set(time()+200, "label", "v3"),
|
||||
label_set(time()+300, "x", "y"),
|
||||
label_set(time()+400, "label", "v4"),
|
||||
), "label", "v1", "foo", "v2", "bar", "", "qwe", "v4", ""))`
|
||||
r1 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{1000, 1200, 1400, 1600, 1800, 2000},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r1.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("label"),
|
||||
Value: []byte("foo"),
|
||||
}}
|
||||
r2 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{1100, 1300, 1500, 1700, 1900, 2100},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r2.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("label"),
|
||||
Value: []byte("bar"),
|
||||
}}
|
||||
r3 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{1200, 1400, 1600, 1800, 2000, 2200},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r3.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("label"),
|
||||
Value: []byte("v3"),
|
||||
}}
|
||||
r4 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{1300, 1500, 1700, 1900, 2100, 2300},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r4.MetricName.Tags = []storage.Tag{
|
||||
{
|
||||
Key: []byte("label"),
|
||||
Value: []byte("qwe"),
|
||||
},
|
||||
{
|
||||
Key: []byte("x"),
|
||||
Value: []byte("y"),
|
||||
},
|
||||
}
|
||||
r5 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{1400, 1600, 1800, 2000, 2200, 2400},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
resultExpected := []netstorage.Result{r1, r2, r3, r4, r5}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`label_copy(new_tag)`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `label_copy(
|
||||
@@ -5371,6 +5430,8 @@ func TestExecError(t *testing.T) {
|
||||
f(`label_transform(1)`)
|
||||
f(`label_set()`)
|
||||
f(`label_set(1, "foo")`)
|
||||
f(`label_map()`)
|
||||
f(`label_map(1)`)
|
||||
f(`label_del()`)
|
||||
f(`label_keep()`)
|
||||
f(`label_match()`)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package promql
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
@@ -14,6 +15,10 @@ import (
|
||||
"github.com/valyala/histogram"
|
||||
)
|
||||
|
||||
var maxStalenessInterval = flag.Duration("search.maxStalenessInterval", 0, "The maximum interval for staleness calculations. "+
|
||||
"By default it is automatically calculated from the median interval between samples. This flag can be useful for tuning "+
|
||||
"Prometheus data model closer to Influx-style data model. See https://prometheus.io/docs/prometheus/latest/querying/basics/#staleness for details")
|
||||
|
||||
var rollupFuncs = map[string]newRollupFunc{
|
||||
// Standard rollup funcs from PromQL.
|
||||
// See funcs accepting range-vector on https://prometheus.io/docs/prometheus/latest/querying/functions/ .
|
||||
@@ -148,6 +153,8 @@ var rollupFuncsKeepMetricGroup = map[string]bool{
|
||||
"geomean_over_time": true,
|
||||
"hoeffding_bound_lower": true,
|
||||
"hoeffding_bound_upper": true,
|
||||
"first_over_time": true,
|
||||
"last_over_time": true,
|
||||
}
|
||||
|
||||
func getRollupAggrFuncNames(expr metricsql.Expr) ([]string, error) {
|
||||
@@ -440,6 +447,11 @@ func (rc *rollupConfig) doInternal(dstValues []float64, tsm *timeseriesMap, valu
|
||||
dstValues = decimal.ExtendFloat64sCapacity(dstValues, len(rc.Timestamps))
|
||||
|
||||
scrapeInterval := getScrapeInterval(timestamps)
|
||||
if *maxStalenessInterval > 0 {
|
||||
if si := maxStalenessInterval.Milliseconds(); scrapeInterval > si {
|
||||
scrapeInterval = si
|
||||
}
|
||||
}
|
||||
maxPrevInterval := getMaxPrevInterval(scrapeInterval)
|
||||
if rc.LookbackDelta > 0 && maxPrevInterval > rc.LookbackDelta {
|
||||
maxPrevInterval = rc.LookbackDelta
|
||||
|
||||
@@ -59,6 +59,7 @@ var transformFuncs = map[string]transformFunc{
|
||||
|
||||
// New funcs
|
||||
"label_set": transformLabelSet,
|
||||
"label_map": transformLabelMap,
|
||||
"label_del": transformLabelDel,
|
||||
"label_keep": transformLabelKeep,
|
||||
"label_copy": transformLabelCopy,
|
||||
@@ -1026,6 +1027,38 @@ func transformLabelSet(tfa *transformFuncArg) ([]*timeseries, error) {
|
||||
return rvs, nil
|
||||
}
|
||||
|
||||
func transformLabelMap(tfa *transformFuncArg) ([]*timeseries, error) {
|
||||
args := tfa.args
|
||||
if len(args) < 2 {
|
||||
return nil, fmt.Errorf(`not enough args; got %d; want at least %d`, len(args), 2)
|
||||
}
|
||||
label, err := getString(args[1], 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read label name: %s", err)
|
||||
}
|
||||
srcValues, dstValues, err := getStringPairs(args[2:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[string]string, len(srcValues))
|
||||
for i, srcValue := range srcValues {
|
||||
m[srcValue] = dstValues[i]
|
||||
}
|
||||
rvs := args[0]
|
||||
for _, ts := range rvs {
|
||||
mn := &ts.MetricName
|
||||
dstValue := getDstValue(mn, label)
|
||||
value, ok := m[string(*dstValue)]
|
||||
if ok {
|
||||
*dstValue = append((*dstValue)[:0], value...)
|
||||
}
|
||||
if len(*dstValue) == 0 {
|
||||
mn.RemoveTag(label)
|
||||
}
|
||||
}
|
||||
return rvs, nil
|
||||
}
|
||||
|
||||
func transformLabelCopy(tfa *transformFuncArg) ([]*timeseries, error) {
|
||||
return transformLabelCopyExt(tfa, false)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||
@@ -247,6 +248,10 @@ func registerStorageMetrics() {
|
||||
return &sm.IndexDBMetrics
|
||||
}
|
||||
|
||||
metrics.NewGauge(fmt.Sprintf(`vm_free_disk_space_bytes{path=%q}`, *DataPath), func() float64 {
|
||||
return float64(fs.MustGetFreeSpace(*DataPath))
|
||||
})
|
||||
|
||||
metrics.NewGauge(`vm_active_merges{type="storage/big"}`, func() float64 {
|
||||
return float64(tm().ActiveBigMerges)
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "6.5.2"
|
||||
"version": "6.7.1"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
@@ -60,12 +60,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Overview for single node VictoriaMetrics v1.32.8 or higher",
|
||||
"description": "Overview for single node VictoriaMetrics v1.34.0 or higher",
|
||||
"editable": true,
|
||||
"gnetId": 10229,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"iteration": 1580634216692,
|
||||
"iteration": 1585340890561,
|
||||
"links": [
|
||||
{
|
||||
"icon": "doc",
|
||||
@@ -127,7 +127,6 @@
|
||||
}
|
||||
],
|
||||
"mode": "html",
|
||||
"options": {},
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Version",
|
||||
@@ -146,7 +145,6 @@
|
||||
},
|
||||
"id": 4,
|
||||
"links": [],
|
||||
"options": {},
|
||||
"pageSize": null,
|
||||
"scroll": true,
|
||||
"showHeader": true,
|
||||
@@ -157,6 +155,7 @@
|
||||
"styles": [
|
||||
{
|
||||
"alias": "",
|
||||
"align": "auto",
|
||||
"colorMode": null,
|
||||
"colors": [
|
||||
"rgba(245, 54, 54, 0.9)",
|
||||
@@ -171,6 +170,7 @@
|
||||
},
|
||||
{
|
||||
"alias": "",
|
||||
"align": "auto",
|
||||
"colorMode": null,
|
||||
"colors": [
|
||||
"rgba(245, 54, 54, 0.9)",
|
||||
@@ -187,6 +187,7 @@
|
||||
},
|
||||
{
|
||||
"alias": "",
|
||||
"align": "auto",
|
||||
"colorMode": null,
|
||||
"colors": [
|
||||
"rgba(245, 54, 54, 0.9)",
|
||||
@@ -196,7 +197,7 @@
|
||||
"dateFormat": "YYYY-MM-DD HH:mm:ss",
|
||||
"decimals": 2,
|
||||
"mappingType": 1,
|
||||
"pattern": ".*",
|
||||
"pattern": "/.*/",
|
||||
"thresholds": [],
|
||||
"type": "hidden",
|
||||
"unit": "short"
|
||||
@@ -259,7 +260,6 @@
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"options": {},
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
@@ -344,7 +344,6 @@
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"options": {},
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
@@ -428,7 +427,6 @@
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"options": {},
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
@@ -2878,7 +2876,7 @@
|
||||
}
|
||||
],
|
||||
"refresh": "30s",
|
||||
"schemaVersion": 21,
|
||||
"schemaVersion": 22,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
@@ -2890,6 +2888,7 @@
|
||||
"definition": "label_values(vm_app_version, job)",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"index": -1,
|
||||
"label": null,
|
||||
"multi": false,
|
||||
"name": "job",
|
||||
@@ -2912,6 +2911,7 @@
|
||||
"definition": "label_values(vm_app_version{job=\"$job\"}, version)",
|
||||
"hide": 2,
|
||||
"includeAll": false,
|
||||
"index": -1,
|
||||
"label": null,
|
||||
"multi": false,
|
||||
"name": "version",
|
||||
@@ -2961,5 +2961,8 @@
|
||||
"timezone": "",
|
||||
"title": "VictoriaMetrics",
|
||||
"uid": "wNf0q_kZk",
|
||||
"version": 2
|
||||
"variables": {
|
||||
"list": []
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
# All these commands must run from repository root.
|
||||
|
||||
DOCKER_NAMESPACE := docker.io/victoriametrics
|
||||
BUILDER_IMAGE := local/builder:go1.14.0
|
||||
CERTS_IMAGE := local/certs:1.0.3
|
||||
BUILDER_IMAGE := local/builder:go1.14.1
|
||||
BASE_IMAGE := local/base:1.1.0
|
||||
|
||||
package-certs:
|
||||
(docker image ls --format '{{.Repository}}:{{.Tag}}' | grep -q '$(CERTS_IMAGE)$$') \
|
||||
|| docker build -t $(CERTS_IMAGE) deployment/docker/certs
|
||||
package-base:
|
||||
(docker image ls --format '{{.Repository}}:{{.Tag}}' | grep -q '$(BASE_IMAGE)$$') \
|
||||
|| docker build -t $(BASE_IMAGE) deployment/docker/base
|
||||
|
||||
package-builder:
|
||||
(docker image ls --format '{{.Repository}}:{{.Tag}}' | grep -q '$(BUILDER_IMAGE)$$') \
|
||||
|| docker build -t $(BUILDER_IMAGE) deployment/docker/builder
|
||||
|
||||
app-via-docker: package-certs package-builder
|
||||
app-via-docker: package-base package-builder
|
||||
mkdir -p gocache-for-docker
|
||||
docker run --rm \
|
||||
--user $(shell id -u):$(shell id -g) \
|
||||
@@ -31,7 +31,7 @@ package-via-docker:
|
||||
$(MAKE) app-via-docker && \
|
||||
docker build \
|
||||
--build-arg src_binary=$(APP_NAME)$(APP_SUFFIX)-prod \
|
||||
--build-arg certs_image=$(CERTS_IMAGE) \
|
||||
--build-arg base_image=$(BASE_IMAGE) \
|
||||
-t $(DOCKER_NAMESPACE)/$(APP_NAME):$(PKG_TAG)$(APP_SUFFIX)$(RACE) \
|
||||
-f app/$(APP_NAME)/deployment/Dockerfile bin)
|
||||
|
||||
|
||||
8
deployment/docker/base/Dockerfile
Normal file
8
deployment/docker/base/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
# See https://medium.com/on-docker/use-multi-stage-builds-to-inject-ca-certs-ad1e8f01de1b
|
||||
FROM alpine:3.10 as base
|
||||
|
||||
RUN apk --update --no-cache add ca-certificates
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
2
deployment/docker/base/group
Normal file
2
deployment/docker/base/group
Normal file
@@ -0,0 +1,2 @@
|
||||
root:x:0:root
|
||||
victoriametrics:x:1000:victoriametrics
|
||||
2
deployment/docker/base/passwd
Normal file
2
deployment/docker/base/passwd
Normal file
@@ -0,0 +1,2 @@
|
||||
root:x:0:0:root:/root:/bin/ash
|
||||
victoriametrics:x:1000:1000::/:
|
||||
@@ -1,2 +1,2 @@
|
||||
FROM golang:1.14.0
|
||||
FROM golang:1.14.1
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# See https://medium.com/on-docker/use-multi-stage-builds-to-inject-ca-certs-ad1e8f01de1b
|
||||
FROM alpine:3.10 as certs
|
||||
RUN apk --update add ca-certificates
|
||||
@@ -2,7 +2,7 @@ version: '3.5'
|
||||
services:
|
||||
prometheus:
|
||||
container_name: prometheus
|
||||
image: prom/prometheus:v2.15.2
|
||||
image: prom/prometheus:v2.17.1
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -35,7 +35,7 @@ services:
|
||||
restart: always
|
||||
grafana:
|
||||
container_name: grafana
|
||||
image: grafana/grafana:6.5.2
|
||||
image: grafana/grafana:6.7.1
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
cd /var/lib/grafana &&
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Articles
|
||||
|
||||
|
||||
## Our articles
|
||||
|
||||
* [Open-sourcing VictoriaMetrics](https://medium.com/@valyala/open-sourcing-victoriametrics-f31e34485c2b)
|
||||
* [How we created VictoriaMetrics](https://medium.com/devopslinks/victoriametrics-creating-the-best-remote-storage-for-prometheus-5d92d66787ac)
|
||||
* [VictoriaMetrics vs TimescaleDB vs InfluxDB benchmarks on 40K unique time series](https://medium.com/@valyala/when-size-matters-benchmarking-victoriametrics-vs-timescale-and-influxdb-6035811952d4)
|
||||
@@ -21,4 +24,11 @@
|
||||
* [Improving histogram usability for Prometheus and Grafana](https://medium.com/@valyala/improving-histogram-usability-for-prometheus-and-grafana-bc7e5df0e350)
|
||||
* [Prometheus storage: tech terms for humans](https://medium.com/@valyala/prometheus-storage-technical-terms-for-humans-4ab4de6c3d48)
|
||||
* [Billy: how VictoriaMetrics deals with more than 500 billion rows](https://medium.com/@valyala/billy-how-victoriametrics-deals-with-more-than-500-billion-rows-e82ff8f725da)
|
||||
|
||||
|
||||
## Third-party articles
|
||||
|
||||
* [Better Prometheus rate() function with VictoriaMetrics](https://www.percona.com/blog/2020/02/28/better-prometheus-rate-function-with-victoriametrics/)
|
||||
* [Disk usage: VictoriaMetrics vs Prometheus](https://stas.starikevich.com/posts/disk-usage-for-vm-versus-prometheus/)
|
||||
* [Benchmarking time series workloads on Apache Kudu using TSBS](https://blog.cloudera.com/benchmarking-time-series-workloads-on-apache-kudu-using-tsbs/)
|
||||
* [What are Open Source Time Series Databases?](https://www.iunera.com/kraken/fabric/time-series-database/)
|
||||
|
||||
@@ -85,6 +85,20 @@ Numbers:
|
||||
- Churn rate - 100 new time series per hour
|
||||
|
||||
|
||||
### MHI Vestas Offshore Wind
|
||||
|
||||
The mission of [MHI Vestas Offshore Wind](http://www.mhivestasoffshore.com) is to co-develop offshore wind as an economically viable and sustainable energy resource to benefit future generations.
|
||||
|
||||
MHI Vestas Offshore Wind is using VictoriaMetrics to ingest and visualize sensor data from offshore wind turbines. The very efficient storage and ability to backfill was key in chosing VictoriaMetrics. MHI Vestas Offshore Wind is running the cluster version of VictoriaMetrics on Kubernetes using the Helm charts for deployment to be able to scale up capacity as the solution will be rolled out.
|
||||
|
||||
Numbers with current limited roll out:
|
||||
|
||||
- Active time series: 270K
|
||||
- Ingestion rate: 70K/sec
|
||||
- Total number of datapoints: 850 billions
|
||||
- Data size on disk: 800 GiB
|
||||
- Retention time: 3 years
|
||||
|
||||
|
||||
### Dreamteam
|
||||
|
||||
@@ -100,3 +114,108 @@ Numbers:
|
||||
VictoriaMetrics in production environment runs on 2 M5 EC2 instances in "HA" mode, managed by Terraform and Ansible TF module.
|
||||
2 Prometheus instances are writing to both VMs, with 2 [Promxy](https://github.com/jacksontj/promxy) replicas
|
||||
as load balancer for reads.
|
||||
|
||||
|
||||
### Brandwatch
|
||||
|
||||
[Brandwatch](https://www.brandwatch.com/) is the world's pioneering digital consumer intelligence suite,
|
||||
helping over 2,000 of the world's most admired brands and agencies to make insightful, data-driven business decisions.
|
||||
|
||||
The engineering department at Brandwatch has been using InfluxDB for storing application metrics for many years
|
||||
and when End-of-Life of InfluxDB version 1.x was announced we decided to re-evaluate our whole metrics collection and storage stack.
|
||||
|
||||
Main goals for the new metrics stack were:
|
||||
- improved performance
|
||||
- lower maintenance
|
||||
- support for native clustering in open source version
|
||||
- the less metrics shipment had to change, the better
|
||||
- achieving longer data retention would be great but not critical
|
||||
|
||||
We initially looked at CrateDB and TimescaleDB which both turned out to have limitations or requirements in the open source versions
|
||||
that made them unfit for our use case. Prometheus was also considered but push vs. pull metrics was a big change we did not want
|
||||
to include in the already significant change.
|
||||
|
||||
Once we found VictoriaMetrics it solved the following problems:
|
||||
- it is very light weight and we can now run virtual machines instead of dedicated hardware machines for metrics storage
|
||||
- very short startup time and any possible gaps in data can easily be filled in by using Promxy
|
||||
- we could continue using Telegraf as our metrics agent and ship identical metrics to both InfluxDB and VictoriaMetrics during a migration period (migration just about to start)
|
||||
- compression is really good so we can store more metrics and we can spin up new VictoriaMetrics instances
|
||||
for new data and keep read-only nodes with older data if we need to extend our retention period further
|
||||
than single virtual machine disks allow and we can aggregate all the data from VictoriaMetrics with Promxy
|
||||
|
||||
High availability is done the same way we did with InfluxDB, by running parallel single nodes of VictoriaMetrics.
|
||||
|
||||
Numbers:
|
||||
|
||||
- active time series: up to 25 million
|
||||
- ingestion rate: ~300 000
|
||||
- total number of datapoints: 380 billion and growing
|
||||
- total number of entries in inverted index: 575 million and growing
|
||||
- daily time series churn rate: ~550 000
|
||||
- data size on disk: ~660GB and growing
|
||||
- index size on disk: ~9,3GB and growing
|
||||
- average datapoint size on disk: ~1.75 bytes
|
||||
|
||||
Query rates are insignificant as we have concentrated on data ingestion so far.
|
||||
|
||||
Anders Bomberg, Monitoring and Infrastructure Team Lead, brandwatch.com
|
||||
|
||||
|
||||
### Adsterra
|
||||
|
||||
[Adsterra Network](https://adsterra.com) is a leading digital advertising company that offers
|
||||
performance-based solutions for advertisers and media partners worldwide.
|
||||
|
||||
We used to collect and store our metrics via Prometheus. Over time the amount of our servers
|
||||
and metrics increased so we were gradually reducing the retention. When retention became 7 days
|
||||
we started to look for alternative solutions. We were choosing among Thanos, VictoriaMetrics and Prometheus federation.
|
||||
|
||||
We end up with the following configuration:
|
||||
|
||||
- Local Prometheus'es with VictoriaMetrics as remote storage on our backend servers.
|
||||
- A single Prometheus on our monitoring server scrapes metrics from other servers and writes to VictoriaMetrics.
|
||||
- A separate Prometheus that federates from other Prometheus'es and processes alerts.
|
||||
|
||||
Turns out that remote write protocol generates too much traffic and connections. So after 8 months we started to look for alternatives.
|
||||
|
||||
Around the same time VictoriaMetrics released [vmagent](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/app/vmagent/README.md).
|
||||
We tried to scrape all the metrics via a single insance of vmagent. But that didn't work - vmgent wasn't able to catch up with writes
|
||||
into VictoriaMetrics. We tested different options and end up with the following scheme:
|
||||
|
||||
- We removed Prometheus from our setup.
|
||||
- VictoriaMetrics [can scrape targets](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/README.md#how-to-scrape-prometheus-exporters-such-as-node-exporter) as well,
|
||||
so we removed vmagent. Now VictoriaMetrics scrapes all the metrics from 110 jobs and 5531 targets.
|
||||
- We use [Promxy](https://github.com/jacksontj/promxy) for alerting.
|
||||
|
||||
Such a scheme has the following benefits comparing to Prometheus:
|
||||
|
||||
- We can store more metrics.
|
||||
- We need less RAM and CPU for the same workload.
|
||||
|
||||
Cons are the following:
|
||||
|
||||
- VictoriaMetrics doesn't support replication - we run extra instance of VictoriaMetrics and Promxy in front of VictoriaMetrics pair for high availability.
|
||||
- VictoriaMetrics stores 1 extra month for defined retention (if retention is set to N months, then VM stores N+1 months of data), but this is still better than other solutions.
|
||||
|
||||
Some numbers from our single-node VictoriaMetrics setup:
|
||||
|
||||
- active time series: 10M
|
||||
- ingestion rate: 800K samples/sec
|
||||
- total number of datapoints: more than 2 trillion
|
||||
- total number of entries in inverted index: more than 1 billion
|
||||
- daily time series churn rate: 2.6M
|
||||
- data size on disk: 1.5 TB
|
||||
- index size on disk: 27 GB
|
||||
- average datapoint size on disk: 0.75 bytes
|
||||
- range query rate: 16 rps
|
||||
- instant query rate: 25 rps
|
||||
- range query duration: max: 0.5s; median: 0.05s; 97th percentile: 0.29s
|
||||
- instant query duration: max: 2.1s; median: 0.04s; 97th percentile: 0.15s
|
||||
|
||||
VictoriaMetrics consumes about 50GiB of RAM.
|
||||
|
||||
Setup:
|
||||
|
||||
We have 2 single-node instances of VictoriaMetircs. The first instance collects and stores high-resolution metrics (10s scrape interval) for a month.
|
||||
The second instance collects and stores low-resolution metrics (300s scrape interval) for a month.
|
||||
We use Promxy + Alertmanager for global view and alerts evaluation.
|
||||
|
||||
@@ -51,13 +51,6 @@ Docker images for cluster version are available here:
|
||||
Source code for cluster version is available at [cluster branch](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/cluster).
|
||||
|
||||
|
||||
### Development Builds
|
||||
|
||||
1. [Install go](https://golang.org/doc/install). The minimum supported version is Go 1.12.
|
||||
2. Run `make` from the repository root. It should build `vmstorage`, `vmselect`
|
||||
and `vminsert` binaries and put them into the `bin` folder.
|
||||
|
||||
|
||||
### Production builds
|
||||
|
||||
There is no need in installing Go on a host system since binaries are built
|
||||
@@ -69,8 +62,7 @@ So [install docker](https://docs.docker.com/install/) and run the following comm
|
||||
make vminsert-prod vmselect-prod vmstorage-prod
|
||||
```
|
||||
|
||||
Production binaries are built into statically linked binaries for `GOARCH=amd64`, `GOOS=linux`.
|
||||
They are put into `bin` folder with `-prod` suffixes:
|
||||
Production binaries are built into statically linked binaries. They are put into `bin` folder with `-prod` suffixes:
|
||||
```
|
||||
$ make vminsert-prod vmselect-prod vmstorage-prod
|
||||
$ ls -1 bin
|
||||
@@ -79,6 +71,13 @@ vmselect-prod
|
||||
vmstorage-prod
|
||||
```
|
||||
|
||||
### Development Builds
|
||||
|
||||
1. [Install go](https://golang.org/doc/install). The minimum supported version is Go 1.13.
|
||||
2. Run `make` from the repository root. It should build `vmstorage`, `vmselect`
|
||||
and `vminsert` binaries and put them into the `bin` folder.
|
||||
|
||||
|
||||
### Building docker images
|
||||
|
||||
Run `make package`. It will build the following docker images locally:
|
||||
@@ -113,6 +112,15 @@ Ports may be altered by setting `-httpListenAddr` on the corresponding nodes.
|
||||
|
||||
It is recommended setting up [monitoring](#monitoring) for the cluster.
|
||||
|
||||
#### Environment variables
|
||||
|
||||
Each flag values can be set thru environment variables by following these rules:
|
||||
|
||||
- The `-envflag.enable` flag must be set
|
||||
- Each `.` in flag names must be substituted by `_` (for example `-insert.maxQueueDuration <duration>` will translate to `insert_maxQueueDuration=<duration>`)
|
||||
- For repeating flags, an alternative syntax can be used by joining the different values into one using `,` as separator (for example `-storageNode <nodeA> -storageNode <nodeB>` will translate to `storageNode=<nodeA>,<nodeB>`)
|
||||
- It is possible setting prefix for environment vars with `-envflag.prefix`. For instance, if `-envflag.prefix=VM_`, then env vars must be prepended with `VM_`
|
||||
|
||||
|
||||
### Monitoring
|
||||
|
||||
@@ -123,7 +131,8 @@ By default the following TCP ports are used:
|
||||
- `vmstorage` - 8482
|
||||
|
||||
It is recommended setting up Prometheus to scrape `/metrics` pages from all the cluster components, so they can be monitored and analyzed
|
||||
with [the official Grafana dashboard for VictoriaMetrics cluster](https://grafana.com/grafana/dashboards/11176).
|
||||
with [the official Grafana dashboard for VictoriaMetrics cluster](https://grafana.com/grafana/dashboards/11176)
|
||||
or [an alternative dashboard for VictoriaMetrics cluster](https://grafana.com/grafana/dashboards/11831).
|
||||
|
||||
|
||||
### URL format
|
||||
|
||||
@@ -54,11 +54,11 @@ Yes. Prometheus continues writing data to local storage after enabling remote st
|
||||
and new data is available for querying via Prometheus as usual.
|
||||
|
||||
|
||||
### How does VictoriaMetrics compare to other clustered TSDBs on top of Prometheus such as [M3 from Uber](https://eng.uber.com/m3/), [Thanos](https://github.com/thanos-io/thanos), [Cortex](https://github.com/cortexproject/cortex), etc.?
|
||||
### How does VictoriaMetrics compare to other remote storage solutions for Prometheus such as [M3 from Uber](https://eng.uber.com/m3/), [Thanos](https://github.com/thanos-io/thanos), [Cortex](https://github.com/cortexproject/cortex), etc.?
|
||||
|
||||
VictoriaMetrics is simpler, faster, more cost-effective and it provides [MetricsQL with useful extensions for PromQL](MetricsQL). The simplicity is twofold:
|
||||
- It is simpler to configure and operate. There is no need in configuring third-party [sidecars](https://github.com/thanos-io/thanos/blob/master/docs/components/sidecar.md)
|
||||
or fighting with [gossip protocol](https://github.com/thanos-io/thanos/blob/master/docs/proposals/completed/201809_gossip-removal.md).
|
||||
or fighting with [gossip protocol](https://github.com/improbable-eng/thanos/blob/030bc345c12c446962225221795f4973848caab5/docs/proposals/completed/201809_gossip-removal.md).
|
||||
- VictoriaMetrics has simpler architecture, which means less bugs and more useful features in the long run comparing to competing TSDBs.
|
||||
|
||||
See [comparing Thanos to VictoriaMetrics cluster](https://medium.com/@valyala/comparing-thanos-to-victoriametrics-cluster-b193bea1683)
|
||||
@@ -118,7 +118,7 @@ TimescaleDB insists on using SQL as a query language. While SQL is more powerful
|
||||
Additionally, VictoriaMetrics requires [up to 70x less storage space comparing to TimescaleDB](https://medium.com/@valyala/when-size-matters-benchmarking-victoriametrics-vs-timescale-and-influxdb-6035811952d4) for storing the same amount of time series data.
|
||||
|
||||
|
||||
### Does VictoriaMetrics use Prometheus technologies like other clustered TSDBs built on top of Prometheus such as [M3 from Uber](https://eng.uber.com/m3/), [Thanos](https://github.com/thanos-io/thanos), [Cortex](https://github.com/cortexproject/cortex)?
|
||||
### Does VictoriaMetrics use Prometheus technologies like other clustered TSDBs built on top of Prometheus such as [Thanos](https://github.com/thanos-io/thanos), [Cortex](https://github.com/cortexproject/cortex)?
|
||||
|
||||
No. VictoriaMetrics core is written in Go from scratch by [fasthttp](https://github.com/valyala/fasthttp) [author](https://github.com/valyala).
|
||||
The architecture is [optimized for storing and querying large amounts of time series data with high cardinality](https://medium.com/devopslinks/victoriametrics-creating-the-best-remote-storage-for-prometheus-5d92d66787ac). VictoriaMetrics storage uses [certain ideas from ClickHouse](https://medium.com/@valyala/how-victoriametrics-makes-instant-snapshots-for-multi-terabyte-time-series-data-e1f3fb0e0282). Special thanks to [Alexey Milovidov](https://github.com/alexey-milovidov).
|
||||
@@ -129,6 +129,8 @@ The architecture is [optimized for storing and querying large amounts of time se
|
||||
|
||||
Yes:
|
||||
|
||||
* [Benchmarking time series workloads on Apache Kudu using TSBS](https://blog.cloudera.com/benchmarking-time-series-workloads-on-apache-kudu-using-tsbs/)
|
||||
* [Billy: how VictoriaMetrics deals with more than 500 billion rows](https://medium.com/@valyala/billy-how-victoriametrics-deals-with-more-than-500-billion-rows-e82ff8f725da)
|
||||
* [Measuring vertical scalability for time series databases: VictoriaMetrics vs InfluxDB vs TimescaleDB](https://medium.com/@valyala/measuring-vertical-scalability-for-time-series-databases-in-google-cloud-92550d78d8ae).
|
||||
* [Measuring insert performance on high-cardinality time series: VictoriaMetrics vs InfluxDB](https://medium.com/@valyala/insert-benchmarks-with-inch-influxdb-vs-victoriametrics-e31a41ae2893)
|
||||
* [TSBS benchmark on high-cardinality time series: VictoriaMetrics vs InfluxDB vs TimescaleDB](https://medium.com/@valyala/high-cardinality-tsdb-benchmarks-victoriametrics-vs-timescaledb-vs-influxdb-13e6ee64dd6b)
|
||||
|
||||
@@ -46,6 +46,7 @@ This functionality can be tried at [an editable Grafana dashboard](http://play-g
|
||||
- Functions for label manipulation:
|
||||
- `alias(q, name)` for setting metric name across all the time series `q`.
|
||||
- `label_set(q, label1, value1, ... labelN, valueN)` for setting the given values for the given labels on `q`.
|
||||
- `label_map(q, label, srcValue1, dstValue1, ... srcValueN, dstValueN)` for mapping `label` values from `src*` to `dst*`.
|
||||
- `label_del(q, label1, ... labelN)` for deleting the given labels from `q`.
|
||||
- `label_keep(q, label1, ... labelN)` for deleting all the labels except the given labels from `q`.
|
||||
- `label_copy(q, src_label1, dst_label1, ... src_labelN, dst_labelN)` for copying label values from `src_*` to `dst_*`.
|
||||
|
||||
@@ -14,7 +14,9 @@ Cluster version is available [here](https://github.com/VictoriaMetrics/VictoriaM
|
||||
* [Wix.com](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#wixcom)
|
||||
* [Wedos.com](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#wedoscom)
|
||||
* [Synthesio](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#synthesio)
|
||||
* [MHI Vestas Offshore Wind](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#mhi-vestas-offshore-wind)
|
||||
* [Dreamteam](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#dreamteam)
|
||||
* [Brandwatch](https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/CaseStudies#brandwatch)
|
||||
|
||||
|
||||
## Prominent features
|
||||
@@ -103,11 +105,11 @@ Cluster version is available [here](https://github.com/VictoriaMetrics/VictoriaM
|
||||
* [Backfilling](#backfilling)
|
||||
* [Profiling](#profiling)
|
||||
* [Integrations](#integrations)
|
||||
* [Roadmap](#roadmap)
|
||||
* [Third-party contributions](#third-party-contributions)
|
||||
* [Contacts](#contacts)
|
||||
* [Community and contributions](#community-and-contributions)
|
||||
* [Third-party contributions](#third-party-contributions)
|
||||
* [Reporting bugs](#reporting-bugs)
|
||||
* [Roadmap](#roadmap)
|
||||
* [Victoria Metrics Logo](#victoria-metrics-logo)
|
||||
* [Logo Usage Guidelines](#logo-usage-guidelines)
|
||||
* [Font used](#font-used)
|
||||
@@ -136,6 +138,7 @@ Each flag values can be set thru environment variables by following these rules:
|
||||
* The `-envflag.enable` flag must be set
|
||||
* Each `.` in flag names must be substituted by `_` (for example `-insert.maxQueueDuration <duration>` will translate to `insert_maxQueueDuration=<duration>`)
|
||||
* For repeating flags, an alternative syntax can be used by joining the different values into one using `,` as separator (for example `-storageNode <nodeA> -storageNode <nodeB>` will translate to `storageNode=<nodeA>,<nodeB>`)
|
||||
* It is possible setting prefix for environment vars with `-envflag.prefix`. For instance, if `-envflag.prefix=VM_`, then env vars must be prepended with `VM_`
|
||||
|
||||
### Prometheus setup
|
||||
|
||||
@@ -464,6 +467,8 @@ The following response should be returned:
|
||||
{"metric":{"__name__":"ask","market":"NYSE","ticker":"GOOG"},"values":[1.23],"timestamps":[1583865146495]}
|
||||
```
|
||||
|
||||
Note that it could be required to flush response cache after importing historical data. See [these docs](#backfilling) for detail.
|
||||
|
||||
|
||||
### Prometheus querying API usage
|
||||
|
||||
@@ -494,11 +499,11 @@ Additionally VictoriaMetrics provides the following handlers:
|
||||
We recommend using either [binary releases](https://github.com/VictoriaMetrics/VictoriaMetrics/releases) or
|
||||
[docker images](https://hub.docker.com/r/victoriametrics/victoria-metrics/) instead of building VictoriaMetrics
|
||||
from sources. Building from sources is reasonable when developing additional features specific
|
||||
to your needs.
|
||||
to your needs or when testing bugfixes.
|
||||
|
||||
#### Development build
|
||||
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.12.
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.13.
|
||||
2. Run `make victoria-metrics` from the root folder of the repository.
|
||||
It builds `victoria-metrics` binary and puts it into the `bin` folder.
|
||||
|
||||
@@ -514,7 +519,7 @@ ARM build may run on Raspberry Pi or on [energy-efficient ARM servers](https://b
|
||||
|
||||
#### Development ARM build
|
||||
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.12.
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.13.
|
||||
2. Run `make victoria-metrics-arm` or `make victoria-metrics-arm64` from the root folder of the repository.
|
||||
It builds `victoria-metrics-arm` or `victoria-metrics-arm64` binary respectively and puts it into the `bin` folder.
|
||||
|
||||
@@ -530,7 +535,7 @@ ARM build may run on Raspberry Pi or on [energy-efficient ARM servers](https://b
|
||||
This is an experimental mode, which may result in a lower compression ratio and slower decompression performance.
|
||||
Use it with caution!
|
||||
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.12.
|
||||
1. [Install Go](https://golang.org/doc/install). The minimum supported version is Go 1.13.
|
||||
2. Run `make victoria-metrics-pure` from the root folder of the repository.
|
||||
It builds `victoria-metrics-pure` binary and puts it into the `bin` folder.
|
||||
|
||||
@@ -619,6 +624,9 @@ Each JSON line would contain data for a single time series. An example output:
|
||||
Optional `start` and `end` args may be added to the request in order to limit the time frame for the exported data. These args may contain either
|
||||
unix timestamp in seconds or [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) values.
|
||||
|
||||
Optional `max_rows_per_line` arg may be added to the request in order to limit the maximum number of rows exported per each JSON line.
|
||||
By default each JSON line contains all the rows for a single time series.
|
||||
|
||||
Pass `Accept-Encoding: gzip` HTTP header in the request to `/api/v1/export` in order to reduce network bandwidth during exporing big amounts
|
||||
of time series data. This enables gzip compression for the exported data. Example for exporting gzipped data:
|
||||
|
||||
@@ -662,6 +670,8 @@ curl -H 'Accept-Encoding: gzip' http://source-victoriametrics:8428/api/v1/export
|
||||
curl -X POST -H 'Content-Encoding: gzip' http://destination-victoriametrics:8428/api/v1/import -T exported_data.jsonl.gz
|
||||
```
|
||||
|
||||
Note that it could be required to flush response cache after importing historical data. See [these docs](#backfilling) for detail.
|
||||
|
||||
Each request to `/api/v1/import` can load up to a single vCPU core on VictoriaMetrics. Import speed can be improved by splitting the original file into smaller parts
|
||||
and importing them concurrently. Note that the original file must be split on newlines.
|
||||
|
||||
@@ -844,6 +854,7 @@ Alternatively they can be self-scraped by setting `-selfScrapeInterval` command-
|
||||
For example, `-selfScrapeInterval=10s` would enable self-scraping of `/metrics` page with 10 seconds interval.
|
||||
|
||||
There are officials Grafana dashboards for [single-node VictoriaMetrics](https://grafana.com/dashboards/10229) and [clustered VictoriaMetrics](https://grafana.com/grafana/dashboards/11176).
|
||||
There is also an [alternative dashboard for clustered VictoriaMetrics](https://grafana.com/grafana/dashboards/11831).
|
||||
|
||||
The most interesting metrics are:
|
||||
|
||||
@@ -853,10 +864,9 @@ The most interesting metrics are:
|
||||
* `vm_rows{type="indexdb"}` - the number of rows in inverted index. High value for this number usually mean high churn rate for time series.
|
||||
* Sum of `vm_rows{type="storage/big"}` and `vm_rows{type="storage/small"}` - total number of `(timestamp, value)` data points
|
||||
in the database.
|
||||
* Sum of all the `vm_cache_size_bytes` metrics - the total size of all the caches in the database.
|
||||
* `vm_allowed_memory_bytes` - the maximum allowed size for caches in the database. It is calculated as `system_memory * <-memory.allowedPercent> / 100`,
|
||||
where `system_memory` is the amount of system memory and `-memory.allowedPercent` is the corresponding flag value.
|
||||
* `vm_rows_inserted_total` - the total number of inserted rows since VictoriaMetrics start.
|
||||
* `vm_free_disk_space_bytes` - free space left at `-storageDataPath`.
|
||||
* `sum(vm_data_size_bytes)` - the total data size on disk.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
@@ -872,7 +882,9 @@ The most interesting metrics are:
|
||||
|
||||
* VictoriaMetrics requires free disk space for [merging data files to bigger ones](https://medium.com/@valyala/how-victoriametrics-makes-instant-snapshots-for-multi-terabyte-time-series-data-e1f3fb0e0282).
|
||||
It may slow down when there is no enough free space left. So make sure `-storageDataPath` directory
|
||||
has at least 20% of free space comparing to disk size.
|
||||
has at least 20% of free space comparing to disk size. The remaining amount of free space
|
||||
can be [monitored](#monitoring) via `vm_free_disk_space_bytes` metric. The total size of data
|
||||
stored on the disk can be monitored via sum of `vm_data_size_bytes` metrics.
|
||||
|
||||
* If VictoriaMetrics doesn't work because of certain parts are corrupted due to disk errors,
|
||||
then just remove directories with broken parts. This will recover VictoriaMetrics at the cost
|
||||
@@ -923,18 +935,15 @@ The collected profiles may be analyzed with [go tool pprof](https://github.com/g
|
||||
* [netdata](https://github.com/netdata/netdata) can push data into VictoriaMetrics via `Prometheus remote_write API`.
|
||||
See [these docs](https://github.com/netdata/netdata#integrations).
|
||||
* [go-graphite/carbonapi](https://github.com/go-graphite/carbonapi) can use VictoriaMetrics as time series backend.
|
||||
See [this example](/blob/master/cmd/carbonapi/carbonapi.example.prometheus.yaml).
|
||||
See [this example](https://github.com/go-graphite/carbonapi/blob/master/cmd/carbonapi/carbonapi.example.prometheus.yaml).
|
||||
* [Ansible role for installing VictoriaMetrics](https://github.com/dreamteam-gg/ansible-victoriametrics-role).
|
||||
|
||||
## Roadmap
|
||||
## Third-party contributions
|
||||
|
||||
* [ ] Replication [#118](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/118)
|
||||
* [ ] Support of Object Storages (GCS, S3, Azure Storage) [#38](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/38)
|
||||
* [ ] Data downsampling [#36](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/36)
|
||||
* [ ] Alert Manager Integration [#119](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/119)
|
||||
* [ ] CLI tool for data migration, re-balancing and adding/removing nodes [#103](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/103)
|
||||
|
||||
The discussion happens [here](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/129). Feel free to comment on any item or add you own one.
|
||||
* [Unofficial yum repository](https://copr.fedorainfracloud.org/coprs/antonpatsev/VictoriaMetrics/) ([source code](https://github.com/patsevanton/victoriametrics-rpm))
|
||||
* [Prometheus -> VictoriaMetrics exporter #1](https://github.com/ryotarai/prometheus-tsdb-dump)
|
||||
* [Prometheus -> VictoriaMetrics exporter #2](https://github.com/AnchorFree/tsdb-remote-write)
|
||||
* [Prometheus Oauth proxy](https://gitlab.com/optima_public/prometheus_oauth_proxy) - see [this article](https://medium.com/@richard.holly/powerful-saas-solution-for-detection-metrics-c67b9208d362) for details.
|
||||
|
||||
## Contacts
|
||||
|
||||
@@ -967,17 +976,21 @@ We are open to third-party pull requests provided they follow [KISS design princ
|
||||
|
||||
Adhering `KISS` principle simplifies the resulting code and architecture, so it can be reviewed, understood and verified by many people.
|
||||
|
||||
## Third-party contributions
|
||||
|
||||
* [Unofficial yum repository](https://copr.fedorainfracloud.org/coprs/antonpatsev/VictoriaMetrics/) ([source code](https://github.com/patsevanton/victoriametrics-rpm))
|
||||
* [Prometheus -> VictoriaMetrics exporter #1](https://github.com/ryotarai/prometheus-tsdb-dump)
|
||||
* [Prometheus -> VictoriaMetrics exporter #2](https://github.com/AnchorFree/tsdb-remote-write)
|
||||
* [Prometheus Oauth proxy](https://gitlab.com/optima_public/prometheus_oauth_proxy) - see [this article](https://medium.com/@richard.holly/powerful-saas-solution-for-detection-metrics-c67b9208d362) for details.
|
||||
|
||||
## Reporting bugs
|
||||
|
||||
Report bugs and propose new features [here](https://github.com/VictoriaMetrics/VictoriaMetrics/issues).
|
||||
|
||||
## Roadmap
|
||||
|
||||
* [ ] Replication [#118](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/118)
|
||||
* [ ] Support of Object Storages (GCS, S3, Azure Storage) [#38](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/38)
|
||||
* [ ] Data downsampling [#36](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/36)
|
||||
* [ ] Alert Manager Integration [#119](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/119)
|
||||
* [ ] CLI tool for data migration, re-balancing and adding/removing nodes [#103](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/103)
|
||||
|
||||
The discussion happens [here](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/129). Feel free to comment on any item or add you own one.
|
||||
|
||||
|
||||
## Victoria Metrics Logo
|
||||
|
||||
[Zip](VM_logo.zip) contains three folders with different image orientations (main color and inverted version).
|
||||
|
||||
2
docs/robots.txt
Normal file
2
docs/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
user-agent: *
|
||||
allow: /
|
||||
@@ -1,7 +1,8 @@
|
||||
## vmagent
|
||||
|
||||
`vmagent` is a tiny but brave agent, which helps you collecting metrics from various sources
|
||||
and storing them to [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics).
|
||||
and storing them to [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics)
|
||||
or any other Prometheus-compatible storage system that supports `remote_write` protocol.
|
||||
|
||||
<img alt="vmagent" src="vmagent.png">
|
||||
|
||||
@@ -18,7 +19,7 @@ to `vmagent` (like the ability to push metrics instead of pulling them). We did
|
||||
|
||||
* Can be used as drop-in replacement for Prometheus for scraping targets such as [node_exporter](https://github.com/prometheus/node_exporter).
|
||||
See [Quick Start](#quick-start) for details.
|
||||
* Can add, remove and modify labels via Prometheus relabeling. See [these docs](#relabeling) for details.
|
||||
* Can add, remove and modify labels (aka tags) via Prometheus relabeling. Can filter data before sending it to remote storage. See [these docs](#relabeling) for details.
|
||||
* Accepts data via all the ingestion protocols supported by VictoriaMetrics:
|
||||
* Influx line protocol via `http://<vmagent>:8429/write`. See [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/README.md#how-to-send-data-from-influxdb-compatible-agents-such-as-telegraf).
|
||||
* Graphite plaintext protocol if `-graphiteListenAddr` command-line flag is set. See [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/README.md#how-to-send-data-from-graphite-compatible-agents-such-as-statsd).
|
||||
@@ -105,6 +106,14 @@ data among long-term remote storage, short-term remote storage and real-time ana
|
||||
Note that each destination can receive its own subset of the collected data thanks to per-destination relabeling via `-remoteWrite.urlRelabelConfig`.
|
||||
|
||||
|
||||
#### Prometheus remote_write proxy
|
||||
|
||||
`vmagent` may be used as a proxy for Prometheus data sent via Prometheus `remote_write` protocol. It can accept data via `remote_write` API
|
||||
at `/api/v1/write` endpoint, apply relabeling and filtering and then proxy it to another `remote_write` systems.
|
||||
The `vmagent` can be configured to encrypt the incoming `remote_write` requests with `-tls*` command-line flags.
|
||||
Additionally, Basic Auth can be enabled for the incoming `remote_write` requests with `-httpAuth.*` command-line flags.
|
||||
|
||||
|
||||
|
||||
### How to collect metrics in Prometheus format
|
||||
|
||||
|
||||
20
go.mod
20
go.mod
@@ -1,25 +1,27 @@
|
||||
module github.com/VictoriaMetrics/VictoriaMetrics
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.55.0 // indirect
|
||||
cloud.google.com/go/storage v1.6.0
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7
|
||||
github.com/VictoriaMetrics/metrics v1.11.0
|
||||
github.com/aws/aws-sdk-go v1.29.10
|
||||
github.com/VictoriaMetrics/metrics v1.11.2
|
||||
github.com/aws/aws-sdk-go v1.29.34
|
||||
github.com/cespare/xxhash/v2 v2.1.1
|
||||
github.com/golang/snappy v0.0.1
|
||||
github.com/klauspost/compress v1.10.1
|
||||
github.com/jmespath/go-jmespath v0.3.0 // indirect
|
||||
github.com/klauspost/compress v1.10.3
|
||||
github.com/valyala/fasthttp v1.9.0
|
||||
github.com/valyala/fastjson v1.5.0
|
||||
github.com/valyala/fastrand v1.0.0
|
||||
github.com/valyala/gozstd v1.6.4
|
||||
github.com/valyala/histogram v1.0.1
|
||||
github.com/valyala/quicktemplate v1.4.1
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae
|
||||
golang.org/x/tools v0.0.0-20200226180945-26f6a1b6802d // indirect
|
||||
google.golang.org/api v0.19.0
|
||||
google.golang.org/genproto v0.0.0-20200225123651-fc8f55426688 // indirect
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
|
||||
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775
|
||||
golang.org/x/tools v0.0.0-20200330040139-fa3cc9eebcfe // indirect
|
||||
google.golang.org/api v0.20.0
|
||||
google.golang.org/genproto v0.0.0-20200330113809-af700f360a68 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
||||
|
||||
go 1.12
|
||||
go 1.13
|
||||
|
||||
55
go.sum
55
go.sum
@@ -9,6 +9,8 @@ cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6T
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0 h1:MZQCQQaRwOrAcuKjiHWHrgKykt4fZyuwF2dtiG3fGW8=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.55.0 h1:eoz/lYxKSL4CNAiaUJ0ZfD1J3bfMYbU5B3rwM1C1EIU=
|
||||
cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5Y=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0 h1:sAbMqjY1PEQKZBWfbu6Y6bsupJ9c4QdHnzg/VvYTLcE=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
@@ -34,12 +36,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
|
||||
github.com/VictoriaMetrics/metrics v1.11.0 h1:sfRmbgk7hGrxNXrziwyTmU8FZFLFrPNC7g2kZs0xMTQ=
|
||||
github.com/VictoriaMetrics/metrics v1.11.0/go.mod h1:LU2j9qq7xqZYXz8tF3/RQnB2z2MbZms5TDiIg9/NHiQ=
|
||||
github.com/VictoriaMetrics/metrics v1.11.2 h1:t/ceLP6SvagUqypCKU7cI7+tQn54+TIV/tGoxihHvx8=
|
||||
github.com/VictoriaMetrics/metrics v1.11.2/go.mod h1:LU2j9qq7xqZYXz8tF3/RQnB2z2MbZms5TDiIg9/NHiQ=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/aws/aws-sdk-go v1.29.10 h1:QJOQq1xNmdrY5mXUmC8CHXzZPve8134Bx/Ux0o6s38s=
|
||||
github.com/aws/aws-sdk-go v1.29.10/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
|
||||
github.com/aws/aws-sdk-go v1.29.34 h1:yrzwfDaZFe9oT4AmQeNNunSQA7c0m2chz0B43+bJ1ok=
|
||||
github.com/aws/aws-sdk-go v1.29.34/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -47,10 +49,13 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -67,12 +72,15 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -89,6 +97,7 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
@@ -98,6 +107,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
@@ -105,8 +116,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.1 h1:a/QY0o9S6wCi0XhxaMX/QmusicNUqCqFugR6WKPOSoQ=
|
||||
github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
@@ -124,6 +135,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
|
||||
@@ -140,6 +153,7 @@ github.com/valyala/histogram v1.0.1/go.mod h1:lQy0xA4wUz2+IUnf97SivorsJIp8FxsnRd
|
||||
github.com/valyala/quicktemplate v1.4.1 h1:tEtkSN6mTCJlYVT7As5x4wjtkk2hj2thsb0M+AcAVeM=
|
||||
github.com/valyala/quicktemplate v1.4.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
||||
@@ -175,6 +189,8 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNT
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
@@ -204,6 +220,10 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -217,6 +237,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -236,6 +258,10 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
|
||||
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -273,8 +299,9 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200226180945-26f6a1b6802d h1:pUSEBYeASep5mmhgY5ZgD6zz3TDJ4SWJwaepxO+tJog=
|
||||
golang.org/x/tools v0.0.0-20200226180945-26f6a1b6802d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200330040139-fa3cc9eebcfe h1:sOd+hT8wBUrIFR5Q6uQb/rg50z8NjHk96kC4adwvxjw=
|
||||
golang.org/x/tools v0.0.0-20200330040139-fa3cc9eebcfe/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
@@ -290,8 +317,8 @@ google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb
|
||||
google.golang.org/api v0.17.0 h1:0q95w+VuFtv4PAx4PZVQdBMmYbaCHbnfKaEiDIcVyag=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0 h1:GwFK8+l5/gdsOYKz5p6M4UK+QT8OvmHWZPJCnf+5DjA=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -316,17 +343,21 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200225123651-fc8f55426688 h1:1+0Z5cgv1eDXJD9z2tdQF9PSSQnJXwism490hJydMRI=
|
||||
google.golang.org/genproto v0.0.0-20200225123651-fc8f55426688/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200330113809-af700f360a68 h1:ay2fio+sR6N1ccqZQgr/bUoo6pwgbxU8imlLkQc9Nlo=
|
||||
google.golang.org/genproto v0.0.0-20200330113809-af700f360a68/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -7,9 +7,12 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var enable = flag.Bool("envflag.enable", false, "Whether to enable reading flags from environment variables additionally to command line. "+
|
||||
"Command line flag values have priority over values from environment vars. "+
|
||||
"Flags are read only from command line if this flag isn't set")
|
||||
var (
|
||||
enable = flag.Bool("envflag.enable", false, "Whether to enable reading flags from environment variables additionally to command line. "+
|
||||
"Command line flag values have priority over values from environment vars. "+
|
||||
"Flags are read only from command line if this flag isn't set")
|
||||
prefix = flag.String("envflag.prefix", "", "Prefix for environment variables if -envflag.enable is set")
|
||||
)
|
||||
|
||||
// Parse parses environment vars and command-line flags.
|
||||
//
|
||||
@@ -48,5 +51,6 @@ func Parse() {
|
||||
func getEnvFlagName(s string) string {
|
||||
// Substitute dots with underscores, since env var names cannot contain dots.
|
||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/311#issuecomment-586354129 for details.
|
||||
return strings.ReplaceAll(s, ".", "_")
|
||||
s = strings.ReplaceAll(s, ".", "_")
|
||||
return *prefix + s
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewArray returns new Array with the given name and descprition.
|
||||
// NewArray returns new Array with the given name and description.
|
||||
func NewArray(name, description string) *Array {
|
||||
var a Array
|
||||
flag.Var(&a, name, description)
|
||||
|
||||
@@ -32,7 +32,9 @@ var (
|
||||
metricsAuthKey = flag.String("metricsAuthKey", "", "Auth key for /metrics. It overrides httpAuth settings")
|
||||
pprofAuthKey = flag.String("pprofAuthKey", "", "Auth key for /debug/pprof. It overrides httpAuth settings")
|
||||
|
||||
disableResponseCompression = flag.Bool("http.disableResponseCompression", false, "Disable compression of HTTP responses for saving CPU resources. By default compression is enabled to save network bandwidth")
|
||||
disableResponseCompression = flag.Bool("http.disableResponseCompression", false, "Disable compression of HTTP responses for saving CPU resources. By default compression is enabled to save network bandwidth")
|
||||
maxGracefulShutdownDuration = flag.Duration("http.maxGracefulShutdownDuration", 7*time.Second, "The maximum duration for graceful shutdown of HTTP server. "+
|
||||
"Highly loaded server may require increased value for graceful shutdown")
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -130,16 +132,16 @@ func Stop(addr string) error {
|
||||
if s == nil {
|
||||
logger.Panicf("BUG: there is no http server at %q", addr)
|
||||
}
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), *maxGracefulShutdownDuration)
|
||||
defer cancelFunc()
|
||||
if err := s.Shutdown(ctx); err != nil {
|
||||
return fmt.Errorf("cannot gracefully shutdown http server at %q: %s", addr, err)
|
||||
return fmt.Errorf("cannot gracefully shutdown http server at %q in %.3fs: %s", addr, maxGracefulShutdownDuration.Seconds(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func gzipHandler(rh RequestHandler) http.HandlerFunc {
|
||||
hf := func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w = maybeGzipResponseWriter(w, r)
|
||||
handlerWrapper(w, r, rh)
|
||||
if zrw, ok := w.(*gzipResponseWriter); ok {
|
||||
@@ -148,7 +150,6 @@ func gzipHandler(rh RequestHandler) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
}
|
||||
return http.HandlerFunc(hf)
|
||||
}
|
||||
|
||||
var metricsHandlerDuration = metrics.NewHistogram(`vm_http_request_duration_seconds{path="/metrics"}`)
|
||||
@@ -451,3 +452,8 @@ func isTrivialNetworkError(err error) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTLS indicates is tls enabled or not
|
||||
func IsTLS() bool {
|
||||
return *tlsEnable
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ var transformFuncs = map[string]bool{
|
||||
|
||||
// New funcs from MetricsQL
|
||||
"label_set": true,
|
||||
"label_map": true,
|
||||
"label_del": true,
|
||||
"label_keep": true,
|
||||
"label_copy": true,
|
||||
|
||||
@@ -509,39 +509,26 @@ func getParamsFromLabels(labels []prompbmarshal.Label, paramsOrig map[string][]s
|
||||
|
||||
func mergeLabels(job, scheme, target, metricsPath string, labels, externalLabels, metaLabels map[string]string, params map[string][]string) ([]prompbmarshal.Label, error) {
|
||||
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config
|
||||
m := map[string]string{
|
||||
"job": job,
|
||||
"__address__": target,
|
||||
"__scheme__": scheme,
|
||||
"__metrics_path__": metricsPath,
|
||||
}
|
||||
m := make(map[string]string)
|
||||
for k, v := range externalLabels {
|
||||
if vOrig, ok := m[k]; ok {
|
||||
return nil, fmt.Errorf("external label `%q: %q` clashes with the previously set label with value %q", k, v, vOrig)
|
||||
}
|
||||
m[k] = v
|
||||
}
|
||||
for k, v := range metaLabels {
|
||||
if vOrig, ok := m[k]; ok {
|
||||
return nil, fmt.Errorf("meta label `%q: %q` clashes with the previously set label with value %q", k, v, vOrig)
|
||||
}
|
||||
m[k] = v
|
||||
}
|
||||
for k, v := range labels {
|
||||
if vOrig, ok := m[k]; ok {
|
||||
return nil, fmt.Errorf("label `%q: %q` clashes with the previously set label with value %q", k, v, vOrig)
|
||||
}
|
||||
m[k] = v
|
||||
}
|
||||
m["job"] = job
|
||||
m["__address__"] = target
|
||||
m["__scheme__"] = scheme
|
||||
m["__metrics_path__"] = metricsPath
|
||||
for k, args := range params {
|
||||
if len(args) == 0 {
|
||||
continue
|
||||
}
|
||||
k = "__param_" + k
|
||||
v := args[0]
|
||||
if vOrig, ok := m[k]; ok {
|
||||
return nil, fmt.Errorf("param `%q: %q` claches with the previously set label with value %q", k, v, vOrig)
|
||||
}
|
||||
m[k] = v
|
||||
}
|
||||
for k, v := range labels {
|
||||
m[k] = v
|
||||
}
|
||||
for k, v := range metaLabels {
|
||||
m[k] = v
|
||||
}
|
||||
result := make([]prompbmarshal.Label, 0, len(m))
|
||||
|
||||
@@ -270,43 +270,6 @@ scrape_configs:
|
||||
- targets: ["a"]
|
||||
`)
|
||||
|
||||
// Clash of external_label with job or instance
|
||||
f(`
|
||||
global:
|
||||
external_labels:
|
||||
job: foobar
|
||||
scrape_configs:
|
||||
- job_name: aaa
|
||||
static_configs:
|
||||
- targets: ["a"]
|
||||
`)
|
||||
|
||||
// Clash of external_label with static_configs label
|
||||
f(`
|
||||
global:
|
||||
external_labels:
|
||||
xxx: foobar
|
||||
scrape_configs:
|
||||
- job_name: aaa
|
||||
static_configs:
|
||||
- targets: ["a"]
|
||||
labels:
|
||||
xxx: yyy
|
||||
`)
|
||||
|
||||
// Clash of param with external_labels
|
||||
f(`
|
||||
global:
|
||||
external_labels:
|
||||
__param_xxx: foobar
|
||||
scrape_configs:
|
||||
- job_name: aaa
|
||||
params:
|
||||
xxx: [abcd]
|
||||
static_configs:
|
||||
- targets: ["a"]
|
||||
`)
|
||||
|
||||
// non-existing ca_file
|
||||
f(`
|
||||
scrape_configs:
|
||||
@@ -1091,6 +1054,62 @@ scrape_configs:
|
||||
},
|
||||
})
|
||||
f(`
|
||||
global:
|
||||
external_labels:
|
||||
job: foobar
|
||||
foo: xx
|
||||
q: qwe
|
||||
__address__: aaasdf
|
||||
__param_a: jlfd
|
||||
scrape_configs:
|
||||
- job_name: aaa
|
||||
params:
|
||||
a: [b, xy]
|
||||
static_configs:
|
||||
- targets: ["a"]
|
||||
labels:
|
||||
foo: bar
|
||||
__param_a: c
|
||||
__address__: pp
|
||||
job: yyy
|
||||
`, []ScrapeWork{
|
||||
{
|
||||
ScrapeURL: "http://pp:80/metrics?a=c&a=xy",
|
||||
ScrapeInterval: defaultScrapeInterval,
|
||||
ScrapeTimeout: defaultScrapeTimeout,
|
||||
Labels: []prompbmarshal.Label{
|
||||
{
|
||||
Name: "__address__",
|
||||
Value: "pp",
|
||||
},
|
||||
{
|
||||
Name: "__metrics_path__",
|
||||
Value: "/metrics",
|
||||
},
|
||||
{
|
||||
Name: "__param_a",
|
||||
Value: "c",
|
||||
},
|
||||
{
|
||||
Name: "__scheme__",
|
||||
Value: "http",
|
||||
},
|
||||
{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
},
|
||||
{
|
||||
Name: "job",
|
||||
Value: "yyy",
|
||||
},
|
||||
{
|
||||
Name: "q",
|
||||
Value: "qwe",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
f(`
|
||||
scrape_configs:
|
||||
- job_name: 'snmp'
|
||||
static_configs:
|
||||
|
||||
@@ -64,8 +64,6 @@ func runScraper(configFile string, pushData func(wr *prompbmarshal.WriteRequest)
|
||||
if err != nil {
|
||||
logger.Fatalf("cannot parse `file_sd_config` from %q: %s", configFile, err)
|
||||
}
|
||||
tsmGlobal.RegisterAll(swsStatic)
|
||||
tsmGlobal.RegisterAll(swsFileSD)
|
||||
|
||||
mustStop := false
|
||||
for !mustStop {
|
||||
@@ -100,13 +98,9 @@ func runScraper(configFile string, pushData func(wr *prompbmarshal.WriteRequest)
|
||||
if err != nil {
|
||||
logger.Errorf("cannot parse `file_sd_config` from %q: %s; continuing with the previous config", configFile, err)
|
||||
}
|
||||
tsmGlobal.UnregisterAll(swsStatic)
|
||||
tsmGlobal.UnregisterAll(swsFileSD)
|
||||
cfg = cfgNew
|
||||
swsStatic = swsStaticNew
|
||||
swsFileSD = swsFileSDNew
|
||||
tsmGlobal.RegisterAll(swsStatic)
|
||||
tsmGlobal.RegisterAll(swsFileSD)
|
||||
case <-globalStopCh:
|
||||
mustStop = true
|
||||
}
|
||||
@@ -166,9 +160,7 @@ func runFileSDScrapers(sws []ScrapeWork, cfg *Config, pushData func(wr *prompbma
|
||||
goto waitForChans
|
||||
}
|
||||
logger.Infof("restarting scrapers for changed `file_sd_config` targets")
|
||||
tsmGlobal.UnregisterAll(sws)
|
||||
sws = swsNew
|
||||
tsmGlobal.RegisterAll(sws)
|
||||
case <-stopCh:
|
||||
mustStop = true
|
||||
}
|
||||
@@ -233,6 +225,7 @@ func equalLabel(a, b *prompbmarshal.Label) bool {
|
||||
//
|
||||
// This function returns after closing stopCh.
|
||||
func runScrapeWorkers(sws []ScrapeWork, pushData func(wr *prompbmarshal.WriteRequest), stopCh <-chan struct{}) {
|
||||
tsmGlobal.RegisterAll(sws)
|
||||
var wg sync.WaitGroup
|
||||
for i := range sws {
|
||||
cfg := &sws[i]
|
||||
@@ -248,4 +241,5 @@ func runScrapeWorkers(sws []ScrapeWork, pushData func(wr *prompbmarshal.WriteReq
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
tsmGlobal.UnregisterAll(sws)
|
||||
}
|
||||
|
||||
@@ -88,31 +88,32 @@ type scrapeWork struct {
|
||||
func (sw *scrapeWork) run(stopCh <-chan struct{}) {
|
||||
// Randomize start time for the first scrape in order to spread load
|
||||
// when scraping many targets.
|
||||
randSleep := time.Duration(float64(sw.Config.ScrapeInterval) * rand.Float64())
|
||||
scrapeInterval := sw.Config.ScrapeInterval
|
||||
randSleep := time.Duration(float64(scrapeInterval) * rand.Float64())
|
||||
timer := time.NewTimer(randSleep)
|
||||
var timestamp int64
|
||||
var ticker *time.Ticker
|
||||
select {
|
||||
case <-stopCh:
|
||||
timer.Stop()
|
||||
return
|
||||
case t := <-timer.C:
|
||||
ticker = time.NewTicker(sw.Config.ScrapeInterval)
|
||||
timestamp := t.UnixNano() / 1e6
|
||||
case <-timer.C:
|
||||
ticker = time.NewTicker(scrapeInterval)
|
||||
timestamp = time.Now().UnixNano() / 1e6
|
||||
sw.scrapeAndLogError(timestamp)
|
||||
}
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
startTime := time.Now()
|
||||
timestamp += scrapeInterval.Milliseconds()
|
||||
select {
|
||||
case <-stopCh:
|
||||
return
|
||||
case t := <-ticker.C:
|
||||
// Adjust t if it is from the past (i.e. stale tick)
|
||||
// This can be the case if the previous scrape took longer than the scrape interval.
|
||||
if t.Sub(startTime) < 0 {
|
||||
t = startTime
|
||||
case <-ticker.C:
|
||||
t := time.Now().UnixNano() / 1e6
|
||||
if d := t - timestamp; d > 0 && float64(d)/float64(scrapeInterval.Milliseconds()) > 0.1 {
|
||||
// Too big jitter. Adjust timestamp
|
||||
timestamp = t
|
||||
}
|
||||
timestamp := t.UnixNano() / 1e6
|
||||
sw.scrapeAndLogError(timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fastjson/fastfloat"
|
||||
)
|
||||
|
||||
@@ -119,6 +120,7 @@ func parseRows(sc *scanner, dst []Row, tags []Tag, metrics []metric, cds []Colum
|
||||
}
|
||||
if sc.Error != nil {
|
||||
logger.Errorf("error when parsing csv line %q: %s; skipping this line", line, sc.Error)
|
||||
invalidLines.Inc()
|
||||
continue
|
||||
}
|
||||
if len(metrics) == 0 {
|
||||
@@ -139,3 +141,5 @@ func parseRows(sc *scanner, dst []Row, tags []Tag, metrics []metric, cds []Colum
|
||||
}
|
||||
return dst, tags, metrics
|
||||
}
|
||||
|
||||
var invalidLines = metrics.NewCounter(`vm_rows_invalid_total{type="csvimport"}`)
|
||||
|
||||
@@ -16,14 +16,14 @@ func SetMinScrapeIntervalForDeduplication(interval time.Duration) {
|
||||
var minScrapeInterval = time.Duration(0)
|
||||
|
||||
func getMinDelta() int64 {
|
||||
// Divide minScrapeInterval by 2 in order to preserve proper data points.
|
||||
// Use 7/8 of minScrapeInterval in order to preserve proper data points.
|
||||
// For instance, if minScrapeInterval=10, the following time series:
|
||||
// 10 15 19 25 30 34 41
|
||||
// Would be unexpectedly converted to:
|
||||
// Would be unexpectedly converted to if using 100% of minScrapeInterval:
|
||||
// 10 25 41
|
||||
// When dividing minScrapeInterval by 2, it will be converted to the expected:
|
||||
// When using 7/8 of minScrapeInterval, it will be converted to the expected:
|
||||
// 10 19 30 41
|
||||
return minScrapeInterval.Milliseconds() / 2
|
||||
return (minScrapeInterval.Milliseconds() / 8) * 7
|
||||
}
|
||||
|
||||
// DeduplicateSamples removes samples from src* if they are closer to each other than minScrapeInterval.
|
||||
|
||||
@@ -126,10 +126,14 @@ type indexDB struct {
|
||||
// Cache for fast MetricID -> MetricName lookup.
|
||||
metricNameCache *workingsetcache.Cache
|
||||
|
||||
// Cache holding useless TagFilters entries, which have no tag filters
|
||||
// Cache for useless TagFilters entries, which have no tag filters
|
||||
// matching low number of metrics.
|
||||
uselessTagFiltersCache *workingsetcache.Cache
|
||||
|
||||
// Cache for (date, tagFilter) -> metricIDsLen, which is used for reducing
|
||||
// the amount of work when matching a set of filters.
|
||||
metricIDsPerDateTagFilterCache *workingsetcache.Cache
|
||||
|
||||
indexSearchPool sync.Pool
|
||||
|
||||
// An inmemory set of deleted metricIDs.
|
||||
@@ -178,10 +182,11 @@ func openIndexDB(path string, metricIDCache, metricNameCache *workingsetcache.Ca
|
||||
tb: tb,
|
||||
name: name,
|
||||
|
||||
tagCache: workingsetcache.New(mem/32, time.Hour),
|
||||
metricIDCache: metricIDCache,
|
||||
metricNameCache: metricNameCache,
|
||||
uselessTagFiltersCache: workingsetcache.New(mem/128, time.Hour),
|
||||
tagCache: workingsetcache.New(mem/32, time.Hour),
|
||||
metricIDCache: metricIDCache,
|
||||
metricNameCache: metricNameCache,
|
||||
uselessTagFiltersCache: workingsetcache.New(mem/128, time.Hour),
|
||||
metricIDsPerDateTagFilterCache: workingsetcache.New(mem/128, time.Hour),
|
||||
|
||||
currHourMetricIDs: currHourMetricIDs,
|
||||
prevHourMetricIDs: prevHourMetricIDs,
|
||||
@@ -348,11 +353,13 @@ func (db *indexDB) decRef() {
|
||||
// Free space occupied by caches owned by db.
|
||||
db.tagCache.Stop()
|
||||
db.uselessTagFiltersCache.Stop()
|
||||
db.metricIDsPerDateTagFilterCache.Stop()
|
||||
|
||||
db.tagCache = nil
|
||||
db.metricIDCache = nil
|
||||
db.metricNameCache = nil
|
||||
db.uselessTagFiltersCache = nil
|
||||
db.metricIDsPerDateTagFilterCache = nil
|
||||
|
||||
if atomic.LoadUint64(&db.mustDrop) == 0 {
|
||||
return
|
||||
@@ -1014,8 +1021,7 @@ func (is *indexSearch) getStartDateForPerDayInvertedIndex() (uint64, error) {
|
||||
item := ts.Item
|
||||
if !bytes.HasPrefix(item, prefix) {
|
||||
// The databse doesn't contain per-day inverted index yet.
|
||||
// Return the next date, since the current date may contain unindexed data.
|
||||
return minDate + 1, nil
|
||||
return minDate, nil
|
||||
}
|
||||
suffix := item[len(prefix):]
|
||||
|
||||
@@ -1024,15 +1030,13 @@ func (is *indexSearch) getStartDateForPerDayInvertedIndex() (uint64, error) {
|
||||
return 0, fmt.Errorf("unexpected (date, tag)->metricIDs row len; must be at least 8 bytes; got %d bytes", len(suffix))
|
||||
}
|
||||
minDate = encoding.UnmarshalUint64(suffix)
|
||||
// The minDate can contain incomplete inverted index, so increment it.
|
||||
return minDate + 1, nil
|
||||
return minDate, nil
|
||||
}
|
||||
if err := ts.Error(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// There are no (date,tag)->metricIDs entries in the database yet.
|
||||
// Return the next date, since the current date may contain unindexed data.
|
||||
return minDate + 1, nil
|
||||
return minDate, nil
|
||||
}
|
||||
|
||||
func (is *indexSearch) loadDeletedMetricIDs() (*uint64set.Set, error) {
|
||||
@@ -1209,14 +1213,36 @@ func mergeTSIDs(a, b []TSID) []TSID {
|
||||
return tsids
|
||||
}
|
||||
|
||||
func (is *indexSearch) containsTimeRange(tr TimeRange) (bool, error) {
|
||||
ts := &is.ts
|
||||
kb := &is.kb
|
||||
|
||||
// Verify whether the maximum date in `ts` covers tr.MinTimestamp.
|
||||
minDate := uint64(tr.MinTimestamp) / msecPerDay
|
||||
kb.B = marshalCommonPrefix(kb.B[:0], nsPrefixDateToMetricID)
|
||||
prefix := kb.B
|
||||
kb.B = encoding.MarshalUint64(kb.B, minDate)
|
||||
ts.Seek(kb.B)
|
||||
if !ts.NextItem() {
|
||||
if err := ts.Error(); err != nil {
|
||||
return false, fmt.Errorf("error when searching for minDate=%d, prefix %q: %s", minDate, kb.B, err)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
if !bytes.HasPrefix(ts.Item, prefix) {
|
||||
// minDate exceeds max date from ts.
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (is *indexSearch) searchTSIDs(tfss []*TagFilters, tr TimeRange, maxMetrics int) ([]TSID, error) {
|
||||
// Verify whether `is` contains data for the given tr.
|
||||
ok, err := is.containsTimeRange(tr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in containsTimeRange(%s): %s", &tr, err)
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
// Fast path: nothing to search.
|
||||
// Fast path - the index doesn't contain data for the given tr.
|
||||
return nil, nil
|
||||
}
|
||||
metricIDs, err := is.searchMetricIDs(tfss, tr, maxMetrics)
|
||||
@@ -1682,34 +1708,21 @@ func (is *indexSearch) searchMetricIDs(tfss []*TagFilters, tr TimeRange, maxMetr
|
||||
}
|
||||
|
||||
func (is *indexSearch) updateMetricIDsForTagFilters(metricIDs *uint64set.Set, tfs *TagFilters, tr TimeRange, maxMetrics int) error {
|
||||
// Sort tag filters for faster ts.Seek below.
|
||||
sort.Slice(tfs.tfs, func(i, j int) bool {
|
||||
// Move regexp and negative filters to the end, since they require scanning
|
||||
// all the entries for the given label.
|
||||
a := &tfs.tfs[i]
|
||||
b := &tfs.tfs[j]
|
||||
if a.isRegexp != b.isRegexp {
|
||||
return !a.isRegexp
|
||||
}
|
||||
if a.isNegative != b.isNegative {
|
||||
return !a.isNegative
|
||||
}
|
||||
if len(a.orSuffixes) != len(b.orSuffixes) {
|
||||
return len(a.orSuffixes) < len(b.orSuffixes)
|
||||
}
|
||||
return bytes.Compare(a.prefix, b.prefix) < 0
|
||||
})
|
||||
|
||||
ok, err := is.tryUpdatingMetricIDsForDateRange(metricIDs, tfs, tr, maxMetrics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
err := is.tryUpdatingMetricIDsForDateRange(metricIDs, tfs, tr, maxMetrics)
|
||||
if err == nil {
|
||||
// Fast path: found metricIDs by date range.
|
||||
return nil
|
||||
}
|
||||
if err != errFallbackToMetricNameMatch {
|
||||
return err
|
||||
}
|
||||
|
||||
// Slow path - try searching over the whole inverted index.
|
||||
|
||||
// Sort tag filters for faster ts.Seek below.
|
||||
sort.Slice(tfs.tfs, func(i, j int) bool {
|
||||
return tfs.tfs[i].Less(&tfs.tfs[j])
|
||||
})
|
||||
minTf, minMetricIDs, err := is.getTagFilterWithMinMetricIDsCountOptimized(tfs, tr, maxMetrics)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -2051,30 +2064,41 @@ func (is *indexSearch) getMetricIDsForTimeRange(tr TimeRange, maxMetrics int) (*
|
||||
// Too much dates must be covered. Give up.
|
||||
return nil, errMissingMetricIDsForDate
|
||||
}
|
||||
if minDate == maxDate {
|
||||
// Fast path - query on a single day.
|
||||
metricIDs, err := is.getMetricIDsForDate(minDate, maxMetrics)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
atomic.AddUint64(&is.db.dateMetricIDsSearchHits, 1)
|
||||
return metricIDs, nil
|
||||
}
|
||||
|
||||
// Search for metricIDs for each day in parallel.
|
||||
// Slower path - query over multiple days in parallel.
|
||||
metricIDs = &uint64set.Set{}
|
||||
var wg sync.WaitGroup
|
||||
var errGlobal error
|
||||
var mu sync.Mutex // protects metricIDs + errGlobal from concurrent access below.
|
||||
for minDate <= maxDate {
|
||||
date := minDate
|
||||
isLocal := is.db.getIndexSearch()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
go func(date uint64) {
|
||||
defer wg.Done()
|
||||
isLocal := is.db.getIndexSearch()
|
||||
defer is.db.putIndexSearch(isLocal)
|
||||
var result uint64set.Set
|
||||
err := isLocal.getMetricIDsForDate(date, &result, maxMetrics)
|
||||
m, err := isLocal.getMetricIDsForDate(date, maxMetrics)
|
||||
mu.Lock()
|
||||
if metricIDs.Len() < maxMetrics {
|
||||
metricIDs.UnionMayOwn(&result)
|
||||
defer mu.Unlock()
|
||||
if errGlobal != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
errGlobal = err
|
||||
return
|
||||
}
|
||||
mu.Unlock()
|
||||
}()
|
||||
if metricIDs.Len() < maxMetrics {
|
||||
metricIDs.UnionMayOwn(m)
|
||||
}
|
||||
}(minDate)
|
||||
minDate++
|
||||
}
|
||||
wg.Wait()
|
||||
@@ -2085,126 +2109,184 @@ func (is *indexSearch) getMetricIDsForTimeRange(tr TimeRange, maxMetrics int) (*
|
||||
return metricIDs, nil
|
||||
}
|
||||
|
||||
func (is *indexSearch) tryUpdatingMetricIDsForDateRange(metricIDs *uint64set.Set, tfs *TagFilters, tr TimeRange, maxMetrics int) (bool, error) {
|
||||
func (is *indexSearch) tryUpdatingMetricIDsForDateRange(metricIDs *uint64set.Set, tfs *TagFilters, tr TimeRange, maxMetrics int) error {
|
||||
atomic.AddUint64(&is.db.dateRangeSearchCalls, 1)
|
||||
minDate := uint64(tr.MinTimestamp) / msecPerDay
|
||||
maxDate := uint64(tr.MaxTimestamp) / msecPerDay
|
||||
if minDate < is.db.startDateForPerDayInvertedIndex || maxDate < minDate {
|
||||
// Per-day inverted index doesn't cover the selected date range.
|
||||
return false, nil
|
||||
return errFallbackToMetricNameMatch
|
||||
}
|
||||
if maxDate-minDate > maxDaysForDateMetricIDs {
|
||||
// Too much dates must be covered. Give up, since it may be slow.
|
||||
return false, nil
|
||||
return errFallbackToMetricNameMatch
|
||||
}
|
||||
if minDate == maxDate {
|
||||
// Fast path - query only a single date.
|
||||
m, err := is.getMetricIDsForDateAndFilters(minDate, tfs, maxMetrics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
metricIDs.UnionMayOwn(m)
|
||||
atomic.AddUint64(&is.db.dateRangeSearchHits, 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search for metricIDs for each day in parallel.
|
||||
// Slower path - search for metricIDs for each day in parallel.
|
||||
var wg sync.WaitGroup
|
||||
var errGlobal error
|
||||
okGlobal := true
|
||||
var mu sync.Mutex // protects metricIDs + *Global vars from concurrent access below
|
||||
var mu sync.Mutex // protects metricIDs + errGlobal vars from concurrent access below
|
||||
for minDate <= maxDate {
|
||||
date := minDate
|
||||
isLocal := is.db.getIndexSearch()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
go func(date uint64) {
|
||||
defer wg.Done()
|
||||
isLocal := is.db.getIndexSearch()
|
||||
defer is.db.putIndexSearch(isLocal)
|
||||
var result uint64set.Set
|
||||
ok, err := isLocal.tryUpdatingMetricIDsForDate(date, &result, tfs, maxMetrics)
|
||||
m, err := isLocal.getMetricIDsForDateAndFilters(date, tfs, maxMetrics)
|
||||
mu.Lock()
|
||||
if metricIDs.Len() < maxMetrics {
|
||||
metricIDs.UnionMayOwn(&result)
|
||||
}
|
||||
if !ok {
|
||||
okGlobal = ok
|
||||
defer mu.Unlock()
|
||||
if errGlobal != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
if err == errFallbackToMetricNameMatch {
|
||||
// The per-date search is too expensive. Probably it is faster to perform global search
|
||||
// using metric name match.
|
||||
errGlobal = err
|
||||
return
|
||||
}
|
||||
dateStr := time.Unix(int64(date*24*3600), 0)
|
||||
errGlobal = fmt.Errorf("cannot search for metricIDs for %s: %s", dateStr, err)
|
||||
return
|
||||
}
|
||||
mu.Unlock()
|
||||
}()
|
||||
if metricIDs.Len() < maxMetrics {
|
||||
metricIDs.UnionMayOwn(m)
|
||||
}
|
||||
}(minDate)
|
||||
minDate++
|
||||
}
|
||||
wg.Wait()
|
||||
if errGlobal != nil {
|
||||
return false, errGlobal
|
||||
return errGlobal
|
||||
}
|
||||
atomic.AddUint64(&is.db.dateRangeSearchHits, 1)
|
||||
return okGlobal, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (is *indexSearch) tryUpdatingMetricIDsForDate(date uint64, metricIDs *uint64set.Set, tfs *TagFilters, maxMetrics int) (bool, error) {
|
||||
var tfFirst *tagFilter
|
||||
func (is *indexSearch) getMetricIDsForDateAndFilters(date uint64, tfs *TagFilters, maxMetrics int) (*uint64set.Set, error) {
|
||||
// Sort tfs by the number of matching filters from previous queries.
|
||||
// This way we limit the amount of work below by applying more specific filters at first.
|
||||
type tagFilterWithCount struct {
|
||||
tf *tagFilter
|
||||
count uint64
|
||||
}
|
||||
tfsWithCount := make([]tagFilterWithCount, len(tfs.tfs))
|
||||
kb := &is.kb
|
||||
var buf []byte
|
||||
for i := range tfs.tfs {
|
||||
tf := &tfs.tfs[i]
|
||||
kb.B = appendDateTagFilterCacheKey(kb.B[:0], date, tf)
|
||||
buf = is.db.metricIDsPerDateTagFilterCache.Get(buf[:0], kb.B)
|
||||
count := uint64(0)
|
||||
if len(buf) == 8 {
|
||||
count = encoding.UnmarshalUint64(buf)
|
||||
}
|
||||
tfsWithCount[i] = tagFilterWithCount{
|
||||
tf: tf,
|
||||
count: count,
|
||||
}
|
||||
}
|
||||
sort.Slice(tfsWithCount, func(i, j int) bool {
|
||||
a, b := &tfsWithCount[i], &tfsWithCount[j]
|
||||
if a.count != b.count {
|
||||
return a.count < b.count
|
||||
}
|
||||
return a.tf.Less(b.tf)
|
||||
})
|
||||
|
||||
// Populate metricIDs with the first non-negative filter.
|
||||
var tfFirst *tagFilter
|
||||
for i := range tfsWithCount {
|
||||
tf := tfsWithCount[i].tf
|
||||
if tf.isNegative {
|
||||
continue
|
||||
}
|
||||
tfFirst = tf
|
||||
break
|
||||
}
|
||||
|
||||
var result *uint64set.Set
|
||||
var metricIDs *uint64set.Set
|
||||
maxDateMetrics := maxMetrics * 50
|
||||
if tfFirst == nil {
|
||||
result = &uint64set.Set{}
|
||||
if err := is.updateMetricIDsForDateAll(result, date, maxDateMetrics); err != nil {
|
||||
// All the filters in tfs are negative. Populate all the metricIDs for the given (date),
|
||||
// so later they can be filtered out with negative filters.
|
||||
m, err := is.getMetricIDsForDate(date, maxDateMetrics)
|
||||
if err != nil {
|
||||
if err == errMissingMetricIDsForDate {
|
||||
// Zero data points were written on the given date.
|
||||
// Zero time series were written on the given date.
|
||||
// It is OK, since (date, metricID) entries must exist for the given date
|
||||
// according to startDateForPerDayInvertedIndex.
|
||||
return true, nil
|
||||
return nil, nil
|
||||
}
|
||||
return false, fmt.Errorf("cannot obtain all the metricIDs: %s", err)
|
||||
return nil, fmt.Errorf("cannot obtain all the metricIDs: %s", err)
|
||||
}
|
||||
metricIDs = m
|
||||
} else {
|
||||
// Populate metricIDs for the given tfFirst on the given (date)
|
||||
m, err := is.getMetricIDsForDateTagFilter(tfFirst, date, tfs.commonPrefix, maxDateMetrics)
|
||||
if err != nil {
|
||||
if err == errFallbackToMetricNameMatch {
|
||||
// The per-date search is too expensive. Probably it is better to perform global search
|
||||
// using metric name match.
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
return nil, err
|
||||
}
|
||||
result = m
|
||||
metricIDs = m
|
||||
}
|
||||
if result.Len() >= maxDateMetrics {
|
||||
if metricIDs.Len() >= maxDateMetrics {
|
||||
// Too many time series found by a single tag filter. Fall back to global search.
|
||||
return false, nil
|
||||
return nil, errFallbackToMetricNameMatch
|
||||
}
|
||||
|
||||
for i := range tfs.tfs {
|
||||
tf := &tfs.tfs[i]
|
||||
// Intersect metricIDs with the rest of filters.
|
||||
for i := range tfsWithCount {
|
||||
tfWithCount := &tfsWithCount[i]
|
||||
tf := tfWithCount.tf
|
||||
if tf == tfFirst {
|
||||
continue
|
||||
}
|
||||
if n := uint64(metricIDs.Len()); n < 1000 || n < tfWithCount.count/maxIndexScanLoopsPerMetric {
|
||||
// It should be faster performing metricName match on the remaining filters
|
||||
// instead of scanning big number of entries in the inverted index for these filters.
|
||||
tfsRemaining := tfsWithCount[i:]
|
||||
tfsPostponed := make([]*tagFilter, 0, len(tfsRemaining))
|
||||
for j := range tfsRemaining {
|
||||
tf := tfsRemaining[j].tf
|
||||
if tf == tfFirst {
|
||||
continue
|
||||
}
|
||||
tfsPostponed = append(tfsPostponed, tf)
|
||||
}
|
||||
var m uint64set.Set
|
||||
if err := is.updateMetricIDsByMetricNameMatch(&m, metricIDs, tfsPostponed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
m, err := is.getMetricIDsForDateTagFilter(tf, date, tfs.commonPrefix, maxDateMetrics)
|
||||
if err != nil {
|
||||
if err == errFallbackToMetricNameMatch {
|
||||
// The per-date search is too expensive. Probably it is better to perform global search
|
||||
// using metric name match.
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
return nil, err
|
||||
}
|
||||
if m.Len() >= maxDateMetrics {
|
||||
// Too many time series found by a single tag filter. Fall back to global search.
|
||||
return false, nil
|
||||
return nil, errFallbackToMetricNameMatch
|
||||
}
|
||||
if tf.isNegative {
|
||||
result.Subtract(m)
|
||||
metricIDs.Subtract(m)
|
||||
} else {
|
||||
result.Intersect(m)
|
||||
metricIDs.Intersect(m)
|
||||
}
|
||||
if result.Len() == 0 {
|
||||
return true, nil
|
||||
if metricIDs.Len() == 0 {
|
||||
// Short circuit - there is no need in applying the remaining filters to empty set.
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
metricIDs.UnionMayOwn(result)
|
||||
return true, nil
|
||||
return metricIDs, nil
|
||||
}
|
||||
|
||||
func (is *indexSearch) getMetricIDsForRecentHours(tr TimeRange, maxMetrics int) (*uint64set.Set, bool) {
|
||||
@@ -2329,70 +2411,45 @@ func (is *indexSearch) getMetricIDsForDateTagFilter(tf *tagFilter, date uint64,
|
||||
tfNew := *tf
|
||||
tfNew.isNegative = false // isNegative for the original tf is handled by the caller.
|
||||
tfNew.prefix = kb.B
|
||||
return is.getMetricIDsForTagFilter(&tfNew, maxMetrics)
|
||||
metricIDs, err := is.getMetricIDsForTagFilter(&tfNew, maxMetrics)
|
||||
|
||||
// Store the number of matching metricIDs in the cache in order to sort tag filters
|
||||
// in ascending number of matching metricIDs on the next search.
|
||||
is.kb.B = appendDateTagFilterCacheKey(is.kb.B[:0], date, tf)
|
||||
metricIDsLen := uint64(metricIDs.Len())
|
||||
if err != nil {
|
||||
// Set metricIDsLen to maxMetrics, so the given entry will be moved to the end
|
||||
// of tag filters on the next search.
|
||||
metricIDsLen = uint64(maxMetrics)
|
||||
}
|
||||
kb.B = encoding.MarshalUint64(kb.B[:0], metricIDsLen)
|
||||
is.db.metricIDsPerDateTagFilterCache.Set(is.kb.B, kb.B)
|
||||
return metricIDs, err
|
||||
}
|
||||
|
||||
func (is *indexSearch) getMetricIDsForDate(date uint64, metricIDs *uint64set.Set, maxMetrics int) error {
|
||||
ts := &is.ts
|
||||
kb := &is.kb
|
||||
kb.B = marshalCommonPrefix(kb.B[:0], nsPrefixDateToMetricID)
|
||||
kb.B = encoding.MarshalUint64(kb.B, date)
|
||||
ts.Seek(kb.B)
|
||||
items := 0
|
||||
for metricIDs.Len() < maxMetrics && ts.NextItem() {
|
||||
if !bytes.HasPrefix(ts.Item, kb.B) {
|
||||
break
|
||||
}
|
||||
// Extract MetricID from ts.Item (the last 8 bytes).
|
||||
v := ts.Item[len(kb.B):]
|
||||
if len(v) != 8 {
|
||||
return fmt.Errorf("cannot extract metricID from k; want %d bytes; got %d bytes", 8, len(v))
|
||||
}
|
||||
metricID := encoding.UnmarshalUint64(v)
|
||||
metricIDs.Add(metricID)
|
||||
items++
|
||||
}
|
||||
if err := ts.Error(); err != nil {
|
||||
return fmt.Errorf("error when searching for metricIDs for date %d: %s", date, err)
|
||||
}
|
||||
if items == 0 {
|
||||
// There are no metricIDs for the given date.
|
||||
// This may be the case for old data when Date -> MetricID wasn't available.
|
||||
return errMissingMetricIDsForDate
|
||||
}
|
||||
return nil
|
||||
func appendDateTagFilterCacheKey(dst []byte, date uint64, tf *tagFilter) []byte {
|
||||
dst = encoding.MarshalUint64(dst, date)
|
||||
dst = tf.Marshal(dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (is *indexSearch) containsTimeRange(tr TimeRange) (bool, error) {
|
||||
ts := &is.ts
|
||||
kb := &is.kb
|
||||
|
||||
// Verify whether the maximum date in `ts` covers tr.MinTimestamp.
|
||||
minDate := uint64(tr.MinTimestamp) / msecPerDay
|
||||
kb.B = marshalCommonPrefix(kb.B[:0], nsPrefixDateToMetricID)
|
||||
kb.B = encoding.MarshalUint64(kb.B, minDate)
|
||||
ts.Seek(kb.B)
|
||||
if !ts.NextItem() {
|
||||
if err := ts.Error(); err != nil {
|
||||
return false, fmt.Errorf("error when searching for minDate=%d, prefix %q: %s", minDate, kb.B, err)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
if !bytes.HasPrefix(ts.Item, kb.B[:1]) {
|
||||
// minDate exceeds max date from ts.
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (is *indexSearch) updateMetricIDsForDateAll(metricIDs *uint64set.Set, date uint64, maxMetrics int) error {
|
||||
func (is *indexSearch) getMetricIDsForDate(date uint64, maxMetrics int) (*uint64set.Set, error) {
|
||||
// Extract all the metricIDs from (date, __name__=value)->metricIDs entries.
|
||||
kb := kbPool.Get()
|
||||
defer kbPool.Put(kb)
|
||||
kb.B = marshalCommonPrefix(kb.B[:0], nsPrefixTagToMetricIDs)
|
||||
kb.B = marshalCommonPrefix(kb.B[:0], nsPrefixDateTagToMetricIDs)
|
||||
kb.B = encoding.MarshalUint64(kb.B, date)
|
||||
kb.B = marshalTagValue(kb.B, nil)
|
||||
return is.updateMetricIDsForPrefix(kb.B, metricIDs, maxMetrics)
|
||||
var metricIDs uint64set.Set
|
||||
if err := is.updateMetricIDsForPrefix(kb.B, &metricIDs, maxMetrics); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if metricIDs.Len() == 0 {
|
||||
// There are no metricIDs for the given date.
|
||||
// This may be the case for old data where (data, __name__=value)->metricIDs entries weren't available.
|
||||
return nil, errMissingMetricIDsForDate
|
||||
}
|
||||
return &metricIDs, nil
|
||||
}
|
||||
|
||||
func (is *indexSearch) updateMetricIDsAll(metricIDs *uint64set.Set, maxMetrics int) error {
|
||||
@@ -2441,7 +2498,7 @@ func (is *indexSearch) updateMetricIDsForPrefix(prefix []byte, metricIDs *uint64
|
||||
// over the found metrics.
|
||||
const maxIndexScanLoopsPerMetric = 100
|
||||
|
||||
// The maximum number of slow index scan loops per.
|
||||
// The maximum number of slow index scan loops.
|
||||
// Bigger number of loops is slower than updateMetricIDsByMetricNameMatch
|
||||
// over the found metrics.
|
||||
const maxIndexScanSlowLoopsPerMetric = 20
|
||||
|
||||
@@ -1050,6 +1050,17 @@ func TestMatchTagFilters(t *testing.T) {
|
||||
if ok {
|
||||
t.Fatalf("Shouldn't match")
|
||||
}
|
||||
tfs.Reset()
|
||||
if err := tfs.Add(nil, []byte(".+"), true, true); err != nil {
|
||||
t.Fatalf("cannot add regexp, negative filter: %s", err)
|
||||
}
|
||||
ok, err = matchTagFilters(&mn, toTFPointers(tfs.tfs), &bb)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if ok {
|
||||
t.Fatalf("Shouldn't match")
|
||||
}
|
||||
|
||||
// Positive match by MetricGroup
|
||||
tfs.Reset()
|
||||
@@ -1096,6 +1107,17 @@ func TestMatchTagFilters(t *testing.T) {
|
||||
if !ok {
|
||||
t.Fatalf("Should match")
|
||||
}
|
||||
tfs.Reset()
|
||||
if err := tfs.Add(nil, []byte(".+"), false, true); err != nil {
|
||||
t.Fatalf("cannot add regexp, positive filter: %s", err)
|
||||
}
|
||||
ok, err = matchTagFilters(&mn, toTFPointers(tfs.tfs), &bb)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("Should match")
|
||||
}
|
||||
|
||||
// Negative match by non-existing tag
|
||||
tfs.Reset()
|
||||
@@ -1153,6 +1175,17 @@ func TestMatchTagFilters(t *testing.T) {
|
||||
if !ok {
|
||||
t.Fatalf("Should match")
|
||||
}
|
||||
tfs.Reset()
|
||||
if err := tfs.Add([]byte("non-existing-tag"), []byte(".+"), false, true); err != nil {
|
||||
t.Fatalf("cannot add regexp, non-negative filter: %s", err)
|
||||
}
|
||||
ok, err = matchTagFilters(&mn, toTFPointers(tfs.tfs), &bb)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if ok {
|
||||
t.Fatalf("Shouldn't match")
|
||||
}
|
||||
|
||||
// Negative match by existing tag
|
||||
tfs.Reset()
|
||||
@@ -1199,6 +1232,17 @@ func TestMatchTagFilters(t *testing.T) {
|
||||
if ok {
|
||||
t.Fatalf("Shouldn't match")
|
||||
}
|
||||
tfs.Reset()
|
||||
if err := tfs.Add([]byte("key 3"), []byte(".+"), true, true); err != nil {
|
||||
t.Fatalf("cannot add regexp, negative filter: %s", err)
|
||||
}
|
||||
ok, err = matchTagFilters(&mn, toTFPointers(tfs.tfs), &bb)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if ok {
|
||||
t.Fatalf("Shouldn't match")
|
||||
}
|
||||
|
||||
// Positive match by existing tag
|
||||
tfs.Reset()
|
||||
@@ -1235,7 +1279,7 @@ func TestMatchTagFilters(t *testing.T) {
|
||||
t.Fatalf("Should match")
|
||||
}
|
||||
tfs.Reset()
|
||||
if err := tfs.Add([]byte("key 3"), []byte("v.+lue 2"), true, true); err != nil {
|
||||
if err := tfs.Add([]byte("key 3"), []byte("v.+lue 2|"), true, true); err != nil {
|
||||
t.Fatalf("cannot add regexp, negative filter: %s", err)
|
||||
}
|
||||
ok, err = matchTagFilters(&mn, toTFPointers(tfs.tfs), &bb)
|
||||
@@ -1256,6 +1300,17 @@ func TestMatchTagFilters(t *testing.T) {
|
||||
if !ok {
|
||||
t.Fatalf("Should match")
|
||||
}
|
||||
tfs.Reset()
|
||||
if err := tfs.Add([]byte("key 3"), []byte(".+"), false, true); err != nil {
|
||||
t.Fatalf("cannot add regexp, non-negative filter: %s", err)
|
||||
}
|
||||
ok, err = matchTagFilters(&mn, toTFPointers(tfs.tfs), &bb)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("Should match")
|
||||
}
|
||||
|
||||
// Positive match by multiple tags and MetricGroup
|
||||
tfs.Reset()
|
||||
@@ -1327,6 +1382,22 @@ func TestMatchTagFilters(t *testing.T) {
|
||||
if ok {
|
||||
t.Fatalf("Shouldn't match")
|
||||
}
|
||||
|
||||
// Negative match for multiple non-regexp positive filters
|
||||
tfs.Reset()
|
||||
if err := tfs.Add(nil, []byte("foobar_metric"), false, false); err != nil {
|
||||
t.Fatalf("cannot add non-regexp positive filter for MetricGroup: %s", err)
|
||||
}
|
||||
if err := tfs.Add([]byte("non-existing-metric"), []byte("foobar"), false, false); err != nil {
|
||||
t.Fatalf("cannot add non-regexp positive filter for non-existing tag: %s", err)
|
||||
}
|
||||
ok, err = matchTagFilters(&mn, toTFPointers(tfs.tfs), &bb)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if ok {
|
||||
t.Fatalf("Shouldn't match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchTSIDWithTimeRange(t *testing.T) {
|
||||
|
||||
@@ -80,6 +80,12 @@ type Storage struct {
|
||||
currHourMetricIDsUpdaterWG sync.WaitGroup
|
||||
retentionWatcherWG sync.WaitGroup
|
||||
prefetchedMetricIDsCleanerWG sync.WaitGroup
|
||||
|
||||
// The snapshotLock prevents from concurrent creation of snapshots,
|
||||
// since this may result in snapshots without recently added data,
|
||||
// which may be in the process of flushing to disk by concurrently running
|
||||
// snapshot process.
|
||||
snapshotLock sync.Mutex
|
||||
}
|
||||
|
||||
// OpenStorage opens storage on the given path with the given number of retention months.
|
||||
@@ -178,6 +184,9 @@ func (s *Storage) CreateSnapshot() (string, error) {
|
||||
logger.Infof("creating Storage snapshot for %q...", s.path)
|
||||
startTime := time.Now()
|
||||
|
||||
s.snapshotLock.Lock()
|
||||
defer s.snapshotLock.Unlock()
|
||||
|
||||
snapshotName := fmt.Sprintf("%s-%08X", time.Now().UTC().Format("20060102150405"), nextSnapshotIdx())
|
||||
srcDir := s.path
|
||||
dstDir := fmt.Sprintf("%s/snapshots/%s", srcDir, snapshotName)
|
||||
|
||||
@@ -675,35 +675,43 @@ func checkTagKeys(tks []string, tksExpected map[string]bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestStorageAddRows(t *testing.T) {
|
||||
path := "TestStorageAddRows"
|
||||
func TestStorageAddRowsSerial(t *testing.T) {
|
||||
path := "TestStorageAddRowsSerial"
|
||||
s, err := OpenStorage(path, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot open storage: %s", err)
|
||||
}
|
||||
t.Run("serial", func(t *testing.T) {
|
||||
if err := testStorageAddRows(s); err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
})
|
||||
t.Run("concurrent", func(t *testing.T) {
|
||||
ch := make(chan error, 3)
|
||||
for i := 0; i < cap(ch); i++ {
|
||||
go func() {
|
||||
ch <- testStorageAddRows(s)
|
||||
}()
|
||||
}
|
||||
for i := 0; i < cap(ch); i++ {
|
||||
select {
|
||||
case err := <-ch:
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatalf("timeout")
|
||||
if err := testStorageAddRows(s); err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
s.MustClose()
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
t.Fatalf("cannot remove %q: %s", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageAddRowsConcurrent(t *testing.T) {
|
||||
path := "TestStorageAddRowsConcurrent"
|
||||
s, err := OpenStorage(path, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot open storage: %s", err)
|
||||
}
|
||||
ch := make(chan error, 3)
|
||||
for i := 0; i < cap(ch); i++ {
|
||||
go func() {
|
||||
ch <- testStorageAddRows(s)
|
||||
}()
|
||||
}
|
||||
for i := 0; i < cap(ch); i++ {
|
||||
select {
|
||||
case err := <-ch:
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatalf("timeout")
|
||||
}
|
||||
})
|
||||
}
|
||||
s.MustClose()
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
t.Fatalf("cannot remove %q: %s", path, err)
|
||||
|
||||
@@ -33,6 +33,8 @@ func NewTagFilters() *TagFilters {
|
||||
// Add adds the given tag filter to tfs.
|
||||
//
|
||||
// MetricGroup must be encoded with nil key.
|
||||
//
|
||||
// Finalize must be called after tfs is constructed.
|
||||
func (tfs *TagFilters) Add(key, value []byte, isNegative, isRegexp bool) error {
|
||||
// Verify whether tag filter is empty.
|
||||
if len(value) == 0 {
|
||||
@@ -66,6 +68,38 @@ func (tfs *TagFilters) Add(key, value []byte, isNegative, isRegexp bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Finalize finalizes tfs and may return complementary TagFilters,
|
||||
// which must be added to the resulting set of tag filters.
|
||||
func (tfs *TagFilters) Finalize() []*TagFilters {
|
||||
var tfssNew []*TagFilters
|
||||
for i := range tfs.tfs {
|
||||
tf := &tfs.tfs[i]
|
||||
if tf.matchesEmptyValue {
|
||||
// tf matches empty value, so it must be accompanied with `key!~".+"` tag filter
|
||||
// in order to match time series without the given label.
|
||||
tfssNew = append(tfssNew, tfs.cloneWithNegativeFilter(tf))
|
||||
}
|
||||
}
|
||||
return tfssNew
|
||||
}
|
||||
|
||||
func (tfs *TagFilters) cloneWithNegativeFilter(tfNegative *tagFilter) *TagFilters {
|
||||
tfsNew := NewTagFilters()
|
||||
for i := range tfs.tfs {
|
||||
tf := &tfs.tfs[i]
|
||||
if tf == tfNegative {
|
||||
if err := tfsNew.Add(tf.key, []byte(".+"), true, true); err != nil {
|
||||
logger.Panicf("BUG: unexpected error when creating a tag filter key=~'.+': %s", err)
|
||||
}
|
||||
} else {
|
||||
if err := tfsNew.Add(tf.key, tf.value, tf.isNegative, tf.isRegexp); err != nil {
|
||||
logger.Panicf("BUG: unexpected error when cloning a tag filter %s: %s", tf, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return tfsNew
|
||||
}
|
||||
|
||||
// String returns human-readable value for tfs.
|
||||
func (tfs *TagFilters) String() string {
|
||||
if len(tfs.tfs) == 0 {
|
||||
@@ -102,7 +136,7 @@ type tagFilter struct {
|
||||
|
||||
// Prefix always contains {nsPrefixTagToMetricIDs, key}.
|
||||
// Additionally it contains:
|
||||
// - value ending with tagSeparatorChar if !isRegexp.
|
||||
// - value if !isRegexp.
|
||||
// - non-regexp prefix if isRegexp.
|
||||
prefix []byte
|
||||
|
||||
@@ -111,6 +145,26 @@ type tagFilter struct {
|
||||
|
||||
// Matches regexp suffix.
|
||||
reSuffixMatch func(b []byte) bool
|
||||
|
||||
// Set to true for filter that matches empty value, i.e. "", "|foo" or ".*"
|
||||
//
|
||||
// Such a filter must be applied directly to metricNames.
|
||||
matchesEmptyValue bool
|
||||
}
|
||||
|
||||
func (tf *tagFilter) Less(other *tagFilter) bool {
|
||||
// Move regexp and negative filters to the end, since they require scanning
|
||||
// all the entries for the given label.
|
||||
if tf.isRegexp != other.isRegexp {
|
||||
return !tf.isRegexp
|
||||
}
|
||||
if tf.isNegative != other.isNegative {
|
||||
return !tf.isNegative
|
||||
}
|
||||
if len(tf.orSuffixes) != len(other.orSuffixes) {
|
||||
return len(tf.orSuffixes) < len(other.orSuffixes)
|
||||
}
|
||||
return bytes.Compare(tf.prefix, other.prefix) < 0
|
||||
}
|
||||
|
||||
// String returns human-readable tf value.
|
||||
@@ -170,6 +224,7 @@ func (tf *tagFilter) Init(commonPrefix, key, value []byte, isNegative, isRegexp
|
||||
|
||||
tf.orSuffixes = tf.orSuffixes[:0]
|
||||
tf.reSuffixMatch = nil
|
||||
tf.matchesEmptyValue = false
|
||||
|
||||
tf.prefix = append(tf.prefix, commonPrefix...)
|
||||
tf.prefix = marshalTagValue(tf.prefix, key)
|
||||
@@ -196,6 +251,9 @@ func (tf *tagFilter) Init(commonPrefix, key, value []byte, isNegative, isRegexp
|
||||
}
|
||||
tf.orSuffixes = append(tf.orSuffixes[:0], rcv.orValues...)
|
||||
tf.reSuffixMatch = rcv.reMatch
|
||||
if len(prefix) == 0 && !tf.isNegative && tf.reSuffixMatch(nil) {
|
||||
tf.matchesEmptyValue = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"math/bits"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -16,6 +17,10 @@ import (
|
||||
type Set struct {
|
||||
itemsCount int
|
||||
buckets bucket32Sorter
|
||||
|
||||
// Most likely the buckets contains only a single item, so put it here for performance reasons
|
||||
// in order to improve memory locality.
|
||||
scratchBuckets [1]bucket32
|
||||
}
|
||||
|
||||
type bucket32Sorter []bucket32
|
||||
@@ -38,7 +43,11 @@ func (s *Set) Clone() *Set {
|
||||
}
|
||||
var dst Set
|
||||
dst.itemsCount = s.itemsCount
|
||||
dst.buckets = make([]bucket32, len(s.buckets))
|
||||
if len(s.buckets) == 1 {
|
||||
dst.buckets = dst.scratchBuckets[:]
|
||||
} else {
|
||||
dst.buckets = make([]bucket32, len(s.buckets))
|
||||
}
|
||||
for i := range s.buckets {
|
||||
s.buckets[i].copyTo(&dst.buckets[i])
|
||||
}
|
||||
@@ -56,6 +65,9 @@ func (s *Set) fixItemsCount() {
|
||||
func (s *Set) cloneShallow() *Set {
|
||||
var dst Set
|
||||
dst.itemsCount = s.itemsCount
|
||||
if len(s.buckets) == 1 {
|
||||
dst.buckets = dst.scratchBuckets[:]
|
||||
}
|
||||
dst.buckets = append(dst.buckets[:0], s.buckets...)
|
||||
return &dst
|
||||
}
|
||||
@@ -84,18 +96,37 @@ func (s *Set) Len() int {
|
||||
|
||||
// Add adds x to s.
|
||||
func (s *Set) Add(x uint64) {
|
||||
hi := uint32(x >> 32)
|
||||
lo := uint32(x)
|
||||
for i := range s.buckets {
|
||||
b32 := &s.buckets[i]
|
||||
if b32.hi == hi {
|
||||
if b32.add(lo) {
|
||||
hi32 := uint32(x >> 32)
|
||||
lo32 := uint32(x)
|
||||
bs := s.buckets
|
||||
if len(bs) > 0 && bs[0].hi == hi32 {
|
||||
// Manually inline bucket32.add for performance reasons.
|
||||
hi16 := uint16(lo32 >> 16)
|
||||
lo16 := uint16(lo32)
|
||||
b32 := &bs[0]
|
||||
his := b32.b16his
|
||||
if n := b32.getHint(); n < uint32(len(his)) && his[n] == hi16 {
|
||||
bs := b32.buckets
|
||||
if n < uint32(len(bs)) && bs[n].add(lo16) {
|
||||
s.itemsCount++
|
||||
}
|
||||
return
|
||||
}
|
||||
if b32.addSlow(hi16, lo16) {
|
||||
s.itemsCount++
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := range bs {
|
||||
b32 := &bs[i]
|
||||
if b32.hi == hi32 {
|
||||
if b32.add(lo32) {
|
||||
s.itemsCount++
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
s.addAlloc(hi, lo)
|
||||
s.addAlloc(hi32, lo32)
|
||||
}
|
||||
|
||||
func (s *Set) addAlloc(hi, lo uint32) {
|
||||
@@ -106,7 +137,11 @@ func (s *Set) addAlloc(hi, lo uint32) {
|
||||
}
|
||||
|
||||
func (s *Set) addBucket32() *bucket32 {
|
||||
s.buckets = append(s.buckets, bucket32{})
|
||||
if len(s.buckets) == 0 {
|
||||
s.buckets = s.scratchBuckets[:]
|
||||
} else {
|
||||
s.buckets = append(s.buckets, bucket32{})
|
||||
}
|
||||
return &s.buckets[len(s.buckets)-1]
|
||||
}
|
||||
|
||||
@@ -115,12 +150,26 @@ func (s *Set) Has(x uint64) bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
hi := uint32(x >> 32)
|
||||
lo := uint32(x)
|
||||
for i := range s.buckets {
|
||||
b32 := &s.buckets[i]
|
||||
if b32.hi == hi {
|
||||
return b32.has(lo)
|
||||
hi32 := uint32(x >> 32)
|
||||
lo32 := uint32(x)
|
||||
bs := s.buckets
|
||||
if len(bs) > 0 && bs[0].hi == hi32 {
|
||||
// Manually inline bucket32.has for performance reasons.
|
||||
hi16 := uint16(lo32 >> 16)
|
||||
lo16 := uint16(lo32)
|
||||
b32 := &bs[0]
|
||||
his := b32.b16his
|
||||
if n := b32.getHint(); n < uint32(len(his)) && his[n] == hi16 {
|
||||
// Fast path - check the previously used bucket.
|
||||
bs := b32.buckets
|
||||
return n < uint32(len(bs)) && bs[n].has(lo16)
|
||||
}
|
||||
return b32.hasSlow(hi16, lo16)
|
||||
}
|
||||
for i := range bs {
|
||||
b32 := &bs[i]
|
||||
if b32.hi == hi32 {
|
||||
return b32.has(lo32)
|
||||
}
|
||||
}
|
||||
return false
|
||||
@@ -130,8 +179,15 @@ func (s *Set) Has(x uint64) bool {
|
||||
func (s *Set) Del(x uint64) {
|
||||
hi := uint32(x >> 32)
|
||||
lo := uint32(x)
|
||||
for i := range s.buckets {
|
||||
b32 := &s.buckets[i]
|
||||
bs := s.buckets
|
||||
if len(bs) > 0 && bs[0].hi == hi {
|
||||
if bs[0].del(lo) {
|
||||
s.itemsCount--
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := range bs {
|
||||
b32 := &bs[i]
|
||||
if b32.hi == hi {
|
||||
if b32.del(lo) {
|
||||
s.itemsCount--
|
||||
@@ -205,12 +261,12 @@ func (s *Set) union(a *Set, mayOwn bool) {
|
||||
s.sort()
|
||||
i := 0
|
||||
j := 0
|
||||
sbuckets := s.buckets
|
||||
sBucketsLen := len(s.buckets)
|
||||
for {
|
||||
for i < len(sbuckets) && j < len(a.buckets) && sbuckets[i].hi < a.buckets[j].hi {
|
||||
for i < sBucketsLen && j < len(a.buckets) && s.buckets[i].hi < a.buckets[j].hi {
|
||||
i++
|
||||
}
|
||||
if i >= len(sbuckets) {
|
||||
if i >= sBucketsLen {
|
||||
for j < len(a.buckets) {
|
||||
b32 := s.addBucket32()
|
||||
a.buckets[j].copyTo(b32)
|
||||
@@ -218,7 +274,7 @@ func (s *Set) union(a *Set, mayOwn bool) {
|
||||
}
|
||||
break
|
||||
}
|
||||
for j < len(a.buckets) && a.buckets[j].hi < sbuckets[i].hi {
|
||||
for j < len(a.buckets) && a.buckets[j].hi < s.buckets[i].hi {
|
||||
b32 := s.addBucket32()
|
||||
a.buckets[j].copyTo(b32)
|
||||
j++
|
||||
@@ -226,8 +282,8 @@ func (s *Set) union(a *Set, mayOwn bool) {
|
||||
if j >= len(a.buckets) {
|
||||
break
|
||||
}
|
||||
if sbuckets[i].hi == a.buckets[j].hi {
|
||||
sbuckets[i].union(&a.buckets[j], mayOwn)
|
||||
if s.buckets[i].hi == a.buckets[j].hi {
|
||||
s.buckets[i].union(&a.buckets[j], mayOwn)
|
||||
i++
|
||||
j++
|
||||
}
|
||||
@@ -323,22 +379,19 @@ func (s *Set) ForEach(f func(part []uint64) bool) {
|
||||
}
|
||||
|
||||
type bucket32 struct {
|
||||
hi uint32
|
||||
b16his []uint16
|
||||
buckets []bucket16
|
||||
hi uint32
|
||||
|
||||
// hint may contain bucket index for the last successful add or del operation.
|
||||
// hint may contain bucket index for the last successful operation.
|
||||
// This allows saving CPU time on subsequent calls to the same bucket.
|
||||
hint int
|
||||
}
|
||||
hint uint32
|
||||
|
||||
func (b *bucket32) cloneShallow() *bucket32 {
|
||||
var dst bucket32
|
||||
dst.hi = b.hi
|
||||
dst.b16his = append(dst.b16his[:0], b.b16his...)
|
||||
dst.buckets = append(dst.buckets[:0], b.buckets...)
|
||||
dst.hint = b.hint
|
||||
return &dst
|
||||
// b16his contains high 16 bits for each bucket in buckets.
|
||||
//
|
||||
// It is always sorted.
|
||||
b16his []uint16
|
||||
|
||||
// buckets are sorted by b16his
|
||||
buckets []bucket16
|
||||
}
|
||||
|
||||
func (b *bucket32) getLen() int {
|
||||
@@ -350,49 +403,60 @@ func (b *bucket32) getLen() int {
|
||||
}
|
||||
|
||||
func (b *bucket32) union(a *bucket32, mayOwn bool) {
|
||||
if !mayOwn {
|
||||
a = a.cloneShallow() // clone a, since it is sorted below.
|
||||
}
|
||||
a.sort()
|
||||
b.sort()
|
||||
i := 0
|
||||
j := 0
|
||||
bb16his := b.b16his
|
||||
bBucketsLen := len(b.buckets)
|
||||
for {
|
||||
for i < len(bb16his) && j < len(a.b16his) && bb16his[i] < a.b16his[j] {
|
||||
for i < bBucketsLen && j < len(a.b16his) && b.b16his[i] < a.b16his[j] {
|
||||
i++
|
||||
}
|
||||
if i >= len(bb16his) {
|
||||
if i >= bBucketsLen {
|
||||
for j < len(a.b16his) {
|
||||
b.b16his = append(b.b16his, a.b16his[j])
|
||||
b16 := b.addBucket16()
|
||||
a.buckets[j].copyTo(b16)
|
||||
b16 := b.addBucket16(a.b16his[j])
|
||||
if mayOwn {
|
||||
*b16 = a.buckets[j]
|
||||
} else {
|
||||
a.buckets[j].copyTo(b16)
|
||||
}
|
||||
j++
|
||||
}
|
||||
break
|
||||
}
|
||||
for j < len(a.b16his) && a.b16his[j] < bb16his[i] {
|
||||
b.b16his = append(b.b16his, a.b16his[j])
|
||||
b16 := b.addBucket16()
|
||||
a.buckets[j].copyTo(b16)
|
||||
for j < len(a.b16his) && a.b16his[j] < b.b16his[i] {
|
||||
b16 := b.addBucket16(a.b16his[j])
|
||||
if mayOwn {
|
||||
*b16 = a.buckets[j]
|
||||
} else {
|
||||
a.buckets[j].copyTo(b16)
|
||||
}
|
||||
j++
|
||||
}
|
||||
if j >= len(a.b16his) {
|
||||
break
|
||||
}
|
||||
if bb16his[i] == a.b16his[j] {
|
||||
if b.b16his[i] == a.b16his[j] {
|
||||
b.buckets[i].union(&a.buckets[j])
|
||||
i++
|
||||
j++
|
||||
}
|
||||
}
|
||||
b.sort()
|
||||
// Restore buckets order, which could be violated during the merge above.
|
||||
if !sort.IsSorted(b) {
|
||||
sort.Sort(b)
|
||||
}
|
||||
}
|
||||
|
||||
// This is for sort.Interface used in bucket32.union
|
||||
func (b *bucket32) Len() int { return len(b.b16his) }
|
||||
func (b *bucket32) Less(i, j int) bool { return b.b16his[i] < b.b16his[j] }
|
||||
func (b *bucket32) Swap(i, j int) {
|
||||
his := b.b16his
|
||||
buckets := b.buckets
|
||||
his[i], his[j] = his[j], his[i]
|
||||
buckets[i], buckets[j] = buckets[j], buckets[i]
|
||||
}
|
||||
|
||||
func (b *bucket32) intersect(a *bucket32) {
|
||||
a = a.cloneShallow() // clone a, since it is sorted below.
|
||||
a.sort()
|
||||
b.sort()
|
||||
i := 0
|
||||
j := 0
|
||||
for {
|
||||
@@ -419,6 +483,23 @@ func (b *bucket32) intersect(a *bucket32) {
|
||||
j++
|
||||
}
|
||||
}
|
||||
// Remove zero buckets
|
||||
b16his := b.b16his[:0]
|
||||
bs := b.buckets[:0]
|
||||
for i := range b.buckets {
|
||||
b32 := &b.buckets[i]
|
||||
if b32.isZero() {
|
||||
continue
|
||||
}
|
||||
b16his = append(b16his, b.b16his[i])
|
||||
bs = append(bs, *b32)
|
||||
}
|
||||
for i := len(bs); i < len(b.buckets); i++ {
|
||||
b.buckets[i] = bucket16{}
|
||||
}
|
||||
b.hint = 0
|
||||
b.b16his = b16his
|
||||
b.buckets = bs
|
||||
}
|
||||
|
||||
func (b *bucket32) forEach(f func(part []uint64) bool) bool {
|
||||
@@ -465,73 +546,53 @@ func (b *bucket32) copyTo(dst *bucket32) {
|
||||
b.buckets[i].copyTo(&dst.buckets[i])
|
||||
}
|
||||
}
|
||||
dst.hint = b.hint
|
||||
}
|
||||
|
||||
// This is for sort.Interface
|
||||
func (b *bucket32) Len() int { return len(b.b16his) }
|
||||
func (b *bucket32) Less(i, j int) bool { return b.b16his[i] < b.b16his[j] }
|
||||
func (b *bucket32) Swap(i, j int) {
|
||||
his := b.b16his
|
||||
buckets := b.buckets
|
||||
his[i], his[j] = his[j], his[i]
|
||||
buckets[i], buckets[j] = buckets[j], buckets[i]
|
||||
func (b *bucket32) getHint() uint32 {
|
||||
return atomic.LoadUint32(&b.hint)
|
||||
}
|
||||
|
||||
const maxUnsortedBuckets = 32
|
||||
func (b *bucket32) setHint(n int) {
|
||||
atomic.StoreUint32(&b.hint, uint32(n))
|
||||
}
|
||||
|
||||
func (b *bucket32) add(x uint32) bool {
|
||||
hi := uint16(x >> 16)
|
||||
lo := uint16(x)
|
||||
if n := b.hint; n < len(b.b16his) && b.b16his[n] == hi {
|
||||
his := b.b16his
|
||||
if n := b.getHint(); n < uint32(len(his)) && his[n] == hi {
|
||||
// Fast path - add to the previously used bucket.
|
||||
return n < len(b.buckets) && b.buckets[n].add(lo)
|
||||
bs := b.buckets
|
||||
return n < uint32(len(bs)) && bs[n].add(lo)
|
||||
}
|
||||
return b.addSlow(hi, lo)
|
||||
}
|
||||
|
||||
func (b *bucket32) addSlow(hi, lo uint16) bool {
|
||||
if len(b.buckets) > maxUnsortedBuckets {
|
||||
n := binarySearch16(b.b16his, hi)
|
||||
b.hint = n
|
||||
if n < 0 || n >= len(b.b16his) || b.b16his[n] != hi {
|
||||
b.addAllocBig(hi, lo, n)
|
||||
return true
|
||||
}
|
||||
return n < len(b.buckets) && b.buckets[n].add(lo)
|
||||
his := b.b16his
|
||||
n := binarySearch16(his, hi)
|
||||
if n < 0 || n >= len(his) || his[n] != hi {
|
||||
b.addAlloc(hi, lo, n)
|
||||
return true
|
||||
}
|
||||
for i, hi16 := range b.b16his {
|
||||
if hi16 == hi {
|
||||
b.hint = i
|
||||
return i < len(b.buckets) && b.buckets[i].add(lo)
|
||||
}
|
||||
}
|
||||
b.addAllocSmall(hi, lo)
|
||||
return true
|
||||
b.setHint(n)
|
||||
bs := b.buckets
|
||||
return n < len(bs) && bs[n].add(lo)
|
||||
}
|
||||
|
||||
func (b *bucket32) addAllocSmall(hi, lo uint16) {
|
||||
func (b *bucket32) addBucket16(hi uint16) *bucket16 {
|
||||
b.b16his = append(b.b16his, hi)
|
||||
b16 := b.addBucket16()
|
||||
_ = b16.add(lo)
|
||||
if len(b.buckets) > maxUnsortedBuckets {
|
||||
sort.Sort(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bucket32) addBucket16() *bucket16 {
|
||||
b.buckets = append(b.buckets, bucket16{})
|
||||
return &b.buckets[len(b.buckets)-1]
|
||||
}
|
||||
|
||||
func (b *bucket32) addAllocBig(hi, lo uint16, n int) {
|
||||
func (b *bucket32) addAlloc(hi, lo uint16, n int) {
|
||||
if n < 0 {
|
||||
// This is a hint to Go compiler to remove automatic bounds checks below.
|
||||
return
|
||||
}
|
||||
if n >= len(b.b16his) {
|
||||
b.b16his = append(b.b16his, hi)
|
||||
b16 := b.addBucket16()
|
||||
b16 := b.addBucket16(hi)
|
||||
_ = b16.add(lo)
|
||||
return
|
||||
}
|
||||
@@ -546,57 +607,50 @@ func (b *bucket32) addAllocBig(hi, lo uint16, n int) {
|
||||
func (b *bucket32) has(x uint32) bool {
|
||||
hi := uint16(x >> 16)
|
||||
lo := uint16(x)
|
||||
if len(b.buckets) > maxUnsortedBuckets {
|
||||
return b.hasSlow(hi, lo)
|
||||
his := b.b16his
|
||||
if n := b.getHint(); n < uint32(len(his)) && his[n] == hi {
|
||||
// Fast path - check the previously used bucket.
|
||||
bs := b.buckets
|
||||
return n < uint32(len(bs)) && bs[n].has(lo)
|
||||
}
|
||||
for i, hi16 := range b.b16his {
|
||||
if hi16 == hi {
|
||||
return i < len(b.buckets) && b.buckets[i].has(lo)
|
||||
}
|
||||
}
|
||||
return false
|
||||
return b.hasSlow(hi, lo)
|
||||
}
|
||||
|
||||
func (b *bucket32) hasSlow(hi, lo uint16) bool {
|
||||
n := binarySearch16(b.b16his, hi)
|
||||
if n < 0 || n >= len(b.b16his) || b.b16his[n] != hi {
|
||||
his := b.b16his
|
||||
n := binarySearch16(his, hi)
|
||||
if n < 0 || n >= len(his) || his[n] != hi {
|
||||
return false
|
||||
}
|
||||
return n < len(b.buckets) && b.buckets[n].has(lo)
|
||||
b.setHint(n)
|
||||
bs := b.buckets
|
||||
return n < len(bs) && bs[n].has(lo)
|
||||
}
|
||||
|
||||
func (b *bucket32) del(x uint32) bool {
|
||||
hi := uint16(x >> 16)
|
||||
lo := uint16(x)
|
||||
if n := b.hint; n < len(b.b16his) && b.b16his[n] == hi {
|
||||
his := b.b16his
|
||||
if n := b.getHint(); n < uint32(len(his)) && his[n] == hi {
|
||||
// Fast path - use the bucket from the previous operation.
|
||||
return n < len(b.buckets) && b.buckets[n].del(lo)
|
||||
bs := b.buckets
|
||||
return n < uint32(len(bs)) && bs[n].del(lo)
|
||||
}
|
||||
return b.delSlow(hi, lo)
|
||||
}
|
||||
|
||||
func (b *bucket32) delSlow(hi, lo uint16) bool {
|
||||
if len(b.buckets) > maxUnsortedBuckets {
|
||||
n := binarySearch16(b.b16his, hi)
|
||||
b.hint = n
|
||||
if n < 0 || n >= len(b.b16his) || b.b16his[n] != hi {
|
||||
return false
|
||||
}
|
||||
return n < len(b.buckets) && b.buckets[n].del(lo)
|
||||
his := b.b16his
|
||||
n := binarySearch16(his, hi)
|
||||
if n < 0 || n >= len(his) || his[n] != hi {
|
||||
return false
|
||||
}
|
||||
for i, hi16 := range b.b16his {
|
||||
if hi16 == hi {
|
||||
b.hint = i
|
||||
return i < len(b.buckets) && b.buckets[i].del(lo)
|
||||
}
|
||||
}
|
||||
return false
|
||||
b.setHint(n)
|
||||
bs := b.buckets
|
||||
return n < len(bs) && bs[n].del(lo)
|
||||
}
|
||||
|
||||
func (b *bucket32) appendTo(dst []uint64) []uint64 {
|
||||
if len(b.buckets) <= maxUnsortedBuckets {
|
||||
b.sort()
|
||||
}
|
||||
for i := range b.buckets {
|
||||
hi16 := b.b16his[i]
|
||||
dst = b.buckets[i].appendTo(dst, b.hi, hi16)
|
||||
@@ -604,12 +658,6 @@ func (b *bucket32) appendTo(dst []uint64) []uint64 {
|
||||
return dst
|
||||
}
|
||||
|
||||
func (b *bucket32) sort() {
|
||||
if !sort.IsSorted(b) {
|
||||
sort.Sort(b)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
bitsPerBucket = 1 << 16
|
||||
wordsPerBucket = bitsPerBucket / 64
|
||||
@@ -621,6 +669,10 @@ type bucket16 struct {
|
||||
smallPool [56]uint16
|
||||
}
|
||||
|
||||
func (b *bucket16) isZero() bool {
|
||||
return b.bits == nil && b.smallPoolLen == 0
|
||||
}
|
||||
|
||||
func (b *bucket16) getLen() int {
|
||||
if b.bits == nil {
|
||||
return b.smallPoolLen
|
||||
@@ -637,10 +689,13 @@ func (b *bucket16) getLen() int {
|
||||
func (b *bucket16) union(a *bucket16) {
|
||||
if a.bits != nil && b.bits != nil {
|
||||
// Fast path - use bitwise ops.
|
||||
for i, ax := range a.bits {
|
||||
bx := b.bits[i]
|
||||
ab := a.bits
|
||||
bb := b.bits
|
||||
_ = bb[len(ab)-1]
|
||||
for i, ax := range ab {
|
||||
bx := bb[i]
|
||||
bx |= ax
|
||||
b.bits[i] = bx
|
||||
bb[i] = bx
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -660,10 +715,13 @@ func (b *bucket16) union(a *bucket16) {
|
||||
func (b *bucket16) intersect(a *bucket16) {
|
||||
if a.bits != nil && b.bits != nil {
|
||||
// Fast path - use bitwise ops
|
||||
for i, ax := range a.bits {
|
||||
bx := b.bits[i]
|
||||
ab := a.bits
|
||||
bb := b.bits
|
||||
_ = bb[len(ab)-1]
|
||||
for i, ax := range ab {
|
||||
bx := bb[i]
|
||||
bx &= ax
|
||||
b.bits[i] = bx
|
||||
bb[i] = bx
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,8 +9,219 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSetOps(t *testing.T) {
|
||||
f := func(a, b []uint64) {
|
||||
t.Helper()
|
||||
mUnion := make(map[uint64]bool)
|
||||
mIntersect := make(map[uint64]bool)
|
||||
ma := make(map[uint64]bool)
|
||||
sa := &Set{}
|
||||
sb := &Set{}
|
||||
for _, v := range a {
|
||||
sa.Add(v)
|
||||
ma[v] = true
|
||||
mUnion[v] = true
|
||||
}
|
||||
for _, v := range b {
|
||||
sb.Add(v)
|
||||
mUnion[v] = true
|
||||
if ma[v] {
|
||||
mIntersect[v] = true
|
||||
}
|
||||
}
|
||||
saOrig := sa.Clone()
|
||||
if !saOrig.Equal(sa) {
|
||||
t.Fatalf("saOrig must be equal to sa; got\n%v\nvs\n%v", saOrig, sa)
|
||||
}
|
||||
sbOrig := sb.Clone()
|
||||
if !sbOrig.Equal(sb) {
|
||||
t.Fatalf("sbOrig must be equal to sb; got\n%v\nvs\n%v", sbOrig, sb)
|
||||
}
|
||||
|
||||
// Verify sa.Union(sb)
|
||||
sa.Union(sb)
|
||||
if err := expectEqual(sa, mUnion); err != nil {
|
||||
t.Fatalf("ivalid sa.Union(sb): %s", err)
|
||||
}
|
||||
if !sbOrig.Equal(sb) {
|
||||
t.Fatalf("sbOrig must be equal to sb after sa.Union(sb); got\n%v\nvs\n%v", sbOrig, sb)
|
||||
}
|
||||
|
||||
// Verify sb.Union(sa)
|
||||
sa = saOrig.Clone()
|
||||
sb.Union(sa)
|
||||
if err := expectEqual(sb, mUnion); err != nil {
|
||||
t.Fatalf("invalid sb.Union(sa): %s", err)
|
||||
}
|
||||
if !saOrig.Equal(sa) {
|
||||
t.Fatalf("saOrig must be equal to sa after sb.Union(sa); got\n%v\nvs\n%v", saOrig, sa)
|
||||
}
|
||||
|
||||
// Verify sa.UnionMayOwn(sb)
|
||||
sa = saOrig.Clone()
|
||||
sb = sbOrig.Clone()
|
||||
sa.UnionMayOwn(sb)
|
||||
if err := expectEqual(sa, mUnion); err != nil {
|
||||
t.Fatalf("invalid sa.UnionMayOwn(sb): %s", err)
|
||||
}
|
||||
if !sbOrig.Equal(sb) {
|
||||
t.Fatalf("sbOrig must be equal to sb after sa.UnionMayOwn(sb); got\n%v\nvs\n%v", sbOrig, sb)
|
||||
}
|
||||
|
||||
// Verify sb.UnionMayOwn(sa)
|
||||
sa = saOrig.Clone()
|
||||
sb.UnionMayOwn(sa)
|
||||
if err := expectEqual(sb, mUnion); err != nil {
|
||||
t.Fatalf("invalid sb.UnionMayOwn(sa): %s", err)
|
||||
}
|
||||
if !saOrig.Equal(sa) {
|
||||
t.Fatalf("saOrig must be equal to sa after sb.UnionMayOwn(sa); got\n%v\nvs\n%v", saOrig, sa)
|
||||
}
|
||||
|
||||
// Verify sa.Intersect(sb)
|
||||
sa = saOrig.Clone()
|
||||
sb = sbOrig.Clone()
|
||||
sa.Intersect(sb)
|
||||
if err := expectEqual(sa, mIntersect); err != nil {
|
||||
t.Fatalf("invalid sa.Intersect(sb): %s", err)
|
||||
}
|
||||
if !sbOrig.Equal(sb) {
|
||||
t.Fatalf("sbOrig must be equal to sb after sa.Intersect(sb); got\n%v\nvs\n%v", sbOrig, sb)
|
||||
}
|
||||
|
||||
// Verify sb.Intersect(sa)
|
||||
sa = saOrig.Clone()
|
||||
sb.Intersect(sa)
|
||||
if err := expectEqual(sb, mIntersect); err != nil {
|
||||
t.Fatalf("invalid sb.Intersect(sa): %s", err)
|
||||
}
|
||||
if !saOrig.Equal(sa) {
|
||||
t.Fatalf("saOrig must be equal to sa after sb.Intersect(sa); got\n%v\nvs\n%v", saOrig, sa)
|
||||
}
|
||||
|
||||
// Verify sa.Subtract(sb)
|
||||
mSubtractAB := make(map[uint64]bool)
|
||||
for _, v := range a {
|
||||
mSubtractAB[v] = true
|
||||
}
|
||||
for _, v := range b {
|
||||
delete(mSubtractAB, v)
|
||||
}
|
||||
sa = saOrig.Clone()
|
||||
sb = sbOrig.Clone()
|
||||
sa.Subtract(sb)
|
||||
if err := expectEqual(sa, mSubtractAB); err != nil {
|
||||
t.Fatalf("invalid sa.Subtract(sb): %s", err)
|
||||
}
|
||||
if !sbOrig.Equal(sb) {
|
||||
t.Fatalf("sbOrig must be equal to sb after sa.Subtract(sb); got\n%v\nvs\n%v", sbOrig, sb)
|
||||
}
|
||||
|
||||
// Verify sb.Subtract(sa)
|
||||
mSubtractBA := make(map[uint64]bool)
|
||||
for _, v := range b {
|
||||
mSubtractBA[v] = true
|
||||
}
|
||||
for _, v := range a {
|
||||
delete(mSubtractBA, v)
|
||||
}
|
||||
sa = saOrig.Clone()
|
||||
sb.Subtract(sa)
|
||||
if err := expectEqual(sb, mSubtractBA); err != nil {
|
||||
t.Fatalf("invalid sb.Subtract(sa): %s", err)
|
||||
}
|
||||
if !saOrig.Equal(sa) {
|
||||
t.Fatalf("saOrig must be equal to sa after sb.Subtract(sa); got\n%v\nvs\n%v", saOrig, sa)
|
||||
}
|
||||
}
|
||||
|
||||
f(nil, nil)
|
||||
f([]uint64{1}, nil)
|
||||
f([]uint64{1, 2, 3}, nil)
|
||||
f([]uint64{1, 2, 3, 1 << 16, 1 << 32, 2 << 32}, nil)
|
||||
f([]uint64{1}, []uint64{1})
|
||||
f([]uint64{0}, []uint64{1 << 16})
|
||||
f([]uint64{1}, []uint64{1 << 16})
|
||||
f([]uint64{1}, []uint64{4 << 16})
|
||||
f([]uint64{1}, []uint64{1 << 32})
|
||||
f([]uint64{1}, []uint64{1 << 32, 2 << 32})
|
||||
f([]uint64{1}, []uint64{2 << 32})
|
||||
f([]uint64{1, 1<<16 - 1}, []uint64{1 << 16})
|
||||
f([]uint64{0, 1<<16 - 1}, []uint64{1 << 16, 1<<16 - 1})
|
||||
f([]uint64{0, 1<<16 - 1}, []uint64{1 << 16, 1<<16 - 1, 2 << 16, 8 << 16})
|
||||
f([]uint64{0}, []uint64{1 << 16, 1<<16 - 1, 2 << 16, 8 << 16})
|
||||
f([]uint64{0, 2 << 16}, []uint64{1 << 16})
|
||||
f([]uint64{0, 2 << 16}, []uint64{1 << 16, 3 << 16})
|
||||
f([]uint64{0, 2 << 16}, []uint64{1 << 16, 2 << 16})
|
||||
f([]uint64{0, 2 << 16}, []uint64{1 << 16, 2 << 16, 3 << 16})
|
||||
f([]uint64{0, 2 << 32}, []uint64{1 << 32})
|
||||
f([]uint64{0, 2 << 32}, []uint64{1 << 32, 3 << 32})
|
||||
f([]uint64{0, 2 << 32}, []uint64{1 << 32, 2 << 32})
|
||||
f([]uint64{0, 2 << 32}, []uint64{1 << 32, 2 << 32, 3 << 32})
|
||||
|
||||
var a []uint64
|
||||
for i := 0; i < 100; i++ {
|
||||
a = append(a, uint64(i))
|
||||
}
|
||||
var b []uint64
|
||||
for i := 1 << 16; i < 1<<16+1000; i++ {
|
||||
b = append(b, uint64(i))
|
||||
}
|
||||
f(a, b)
|
||||
|
||||
for i := 1<<16 - 100; i < 1<<16+100; i++ {
|
||||
a = append(a, uint64(i))
|
||||
}
|
||||
for i := uint64(1) << 32; i < 1<<32+1<<16+200; i++ {
|
||||
b = append(b, i)
|
||||
}
|
||||
f(a, b)
|
||||
|
||||
rng := rand.New(rand.NewSource(0))
|
||||
for i := 0; i < 10; i++ {
|
||||
a = nil
|
||||
b = nil
|
||||
for j := 0; j < 1000; j++ {
|
||||
a = append(a, uint64(rng.Intn(1e6)))
|
||||
b = append(b, uint64(rng.Intn(1e6)))
|
||||
}
|
||||
f(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
func expectEqual(s *Set, m map[uint64]bool) error {
|
||||
if s.Len() != len(m) {
|
||||
return fmt.Errorf("unexpected s.Len(); got %d; want %d\ns=%v\nm=%v", s.Len(), len(m), s.AppendTo(nil), m)
|
||||
}
|
||||
for _, v := range s.AppendTo(nil) {
|
||||
if !m[v] {
|
||||
return fmt.Errorf("missing value %d in m; s=%v\nm=%v", v, s.AppendTo(nil), m)
|
||||
}
|
||||
}
|
||||
|
||||
// Additional check via s.Has()
|
||||
for v := range m {
|
||||
if !s.Has(v) {
|
||||
return fmt.Errorf("missing value %d in s; s=%v\nm=%v", v, s.AppendTo(nil), m)
|
||||
}
|
||||
}
|
||||
|
||||
// Extra check via s.ForEach()
|
||||
var err error
|
||||
s.ForEach(func(part []uint64) bool {
|
||||
for _, v := range part {
|
||||
if !m[v] {
|
||||
err = fmt.Errorf("miising value %d in m inside s.ForEach; s=%v\nm=%v", v, s.AppendTo(nil), m)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func TestSetBasicOps(t *testing.T) {
|
||||
for _, itemsCount := range []int{1, 2, 3, 4, 5, 6, 1e2, 1e3, 1e4, 1e5, 1e6, maxUnsortedBuckets * bitsPerBucket * 2} {
|
||||
for _, itemsCount := range []int{1, 2, 3, 4, 5, 6, 1e2, 1e3, 1e4, 1e5, 1e6} {
|
||||
t.Run(fmt.Sprintf("items_%d", itemsCount), func(t *testing.T) {
|
||||
testSetBasicOps(t, itemsCount)
|
||||
})
|
||||
|
||||
16
vendor/cloud.google.com/go/CHANGES.md
generated
vendored
16
vendor/cloud.google.com/go/CHANGES.md
generated
vendored
@@ -1,5 +1,21 @@
|
||||
# Changes
|
||||
|
||||
## v0.55.0
|
||||
|
||||
- Various updates to autogenerated clients.
|
||||
|
||||
## v0.54.0
|
||||
|
||||
- all:
|
||||
- remove unused golang.org/x/exp from mod file
|
||||
- update godoc.org links to pkg.go.dev
|
||||
- compute/metadata:
|
||||
- use defaultClient when http.Client is nil
|
||||
- remove subscribeClient
|
||||
- iam:
|
||||
- add support for v3 policy and IAM conditions
|
||||
- Various updates to autogenerated clients.
|
||||
|
||||
## v0.53.0
|
||||
|
||||
- all: most clients now use transport/grpc.DialPool rather than Dial (see #1777 for outliers).
|
||||
|
||||
2
vendor/cloud.google.com/go/CONTRIBUTING.md
generated
vendored
2
vendor/cloud.google.com/go/CONTRIBUTING.md
generated
vendored
@@ -22,7 +22,7 @@ to install the code reviewing tool.
|
||||
|
||||
1. If you would like, you may want to set up aliases for `git-codereview`,
|
||||
such that `git codereview change` becomes `git change`. See the
|
||||
[godoc](https://godoc.org/golang.org/x/review/git-codereview) for details.
|
||||
[godoc](https://pkg.go.dev/golang.org/x/review/git-codereview) for details.
|
||||
|
||||
* Should you run into issues with the `git-codereview` tool, please note
|
||||
that all error messages will assume that you have set up these aliases.
|
||||
|
||||
90
vendor/cloud.google.com/go/README.md
generated
vendored
90
vendor/cloud.google.com/go/README.md
generated
vendored
@@ -1,6 +1,6 @@
|
||||
# Google Cloud Client Libraries for Go
|
||||
|
||||
[](https://godoc.org/cloud.google.com/go)
|
||||
[](https://pkg.go.dev/cloud.google.com/go)
|
||||
|
||||
Go packages for [Google Cloud Platform](https://cloud.google.com) services.
|
||||
|
||||
@@ -31,46 +31,46 @@ make backwards-incompatible changes.
|
||||
|
||||
Google API | Status | Package
|
||||
------------------------------------------------|--------------|-----------------------------------------------------------
|
||||
[Asset][cloud-asset] | alpha | [`cloud.google.com/go/asset/v1beta`](https://godoc.org/cloud.google.com/go/asset/v1beta)
|
||||
[Automl][cloud-automl] | stable | [`cloud.google.com/go/automl/apiv1`](https://godoc.org/cloud.google.com/go/automl/apiv1)
|
||||
[BigQuery][cloud-bigquery] | stable | [`cloud.google.com/go/bigquery`](https://godoc.org/cloud.google.com/go/bigquery)
|
||||
[Bigtable][cloud-bigtable] | stable | [`cloud.google.com/go/bigtable`](https://godoc.org/cloud.google.com/go/bigtable)
|
||||
[Cloudbuild][cloud-build] | stable | [`cloud.google.com/go/cloudbuild/apiv1`](https://godoc.org/cloud.google.com/go/cloudbuild/apiv1)
|
||||
[Cloudtasks][cloud-tasks] | stable | [`cloud.google.com/go/cloudtasks/apiv2`](https://godoc.org/cloud.google.com/go/cloudtasks/apiv2)
|
||||
[Container][cloud-container] | stable | [`cloud.google.com/go/container/apiv1`](https://godoc.org/cloud.google.com/go/container/apiv1)
|
||||
[ContainerAnalysis][cloud-containeranalysis] | beta | [`cloud.google.com/go/containeranalysis/apiv1`](https://godoc.org/cloud.google.com/go/containeranalysis/apiv1)
|
||||
[Dataproc][cloud-dataproc] | stable | [`cloud.google.com/go/dataproc/apiv1`](https://godoc.org/cloud.google.com/go/dataproc/apiv1)
|
||||
[Datastore][cloud-datastore] | stable | [`cloud.google.com/go/datastore`](https://godoc.org/cloud.google.com/go/datastore)
|
||||
[Debugger][cloud-debugger] | stable | [`cloud.google.com/go/debugger/apiv2`](https://godoc.org/cloud.google.com/go/debugger/apiv2)
|
||||
[Dialogflow][cloud-dialogflow] | stable | [`cloud.google.com/go/dialogflow/apiv2`](https://godoc.org/cloud.google.com/go/dialogflow/apiv2)
|
||||
[Data Loss Prevention][cloud-dlp] | stable | [`cloud.google.com/go/dlp/apiv2`](https://godoc.org/cloud.google.com/go/dlp/apiv2)
|
||||
[ErrorReporting][cloud-errors] | alpha | [`cloud.google.com/go/errorreporting`](https://godoc.org/cloud.google.com/go/errorreporting)
|
||||
[Firestore][cloud-firestore] | stable | [`cloud.google.com/go/firestore`](https://godoc.org/cloud.google.com/go/firestore)
|
||||
[IAM][cloud-iam] | stable | [`cloud.google.com/go/iam`](https://godoc.org/cloud.google.com/go/iam)
|
||||
[IoT][cloud-iot] | stable | [`cloud.google.com/go/iot/apiv1`](https://godoc.org/cloud.google.com/go/iot/apiv1)
|
||||
[IRM][cloud-irm] | alpha | [`cloud.google.com/go/irm/apiv1alpha2`](https://godoc.org/cloud.google.com/go/irm/apiv1alpha2)
|
||||
[KMS][cloud-kms] | stable | [`cloud.google.com/go/kms/apiv1`](https://godoc.org/cloud.google.com/go/kms/apiv1)
|
||||
[Natural Language][cloud-natural-language] | stable | [`cloud.google.com/go/language/apiv1`](https://godoc.org/cloud.google.com/go/language/apiv1)
|
||||
[Logging][cloud-logging] | stable | [`cloud.google.com/go/logging`](https://godoc.org/cloud.google.com/go/logging)
|
||||
[Memorystore][cloud-memorystore] | alpha | [`cloud.google.com/go/redis/apiv1`](https://godoc.org/cloud.google.com/go/redis/apiv1)
|
||||
[Monitoring][cloud-monitoring] | alpha | [`cloud.google.com/go/monitoring/apiv3`](https://godoc.org/cloud.google.com/go/monitoring/apiv3)
|
||||
[OS Login][cloud-oslogin] | alpha | [`cloud.google.com/go/oslogin/apiv1`](https://godoc.org/cloud.google.com/go/oslogin/apiv1)
|
||||
[Pub/Sub][cloud-pubsub] | stable | [`cloud.google.com/go/pubsub`](https://godoc.org/cloud.google.com/go/pubsub)
|
||||
[Phishing Protection][cloud-phishingprotection] | alpha | [`cloud.google.com/go/phishingprotection/apiv1beta1`](https://godoc.org/cloud.google.com/go/phishingprotection/apiv1beta1)
|
||||
[reCAPTCHA Enterprise][cloud-recaptcha] | alpha | [`cloud.google.com/go/recaptchaenterprise/apiv1beta1`](https://godoc.org/cloud.google.com/go/recaptchaenterprise/apiv1beta1)
|
||||
[Recommender][cloud-recommender] | beta | [`cloud.google.com/go/recommender/apiv1beta1`](https://godoc.org/cloud.google.com/go/recommender/apiv1beta1)
|
||||
[Scheduler][cloud-scheduler] | stable | [`cloud.google.com/go/scheduler/apiv1`](https://godoc.org/cloud.google.com/go/scheduler/apiv1)
|
||||
[Securitycenter][cloud-securitycenter] | alpha | [`cloud.google.com/go/securitycenter/apiv1`](https://godoc.org/cloud.google.com/go/securitycenter/apiv1)
|
||||
[Spanner][cloud-spanner] | stable | [`cloud.google.com/go/spanner`](https://godoc.org/cloud.google.com/go/spanner)
|
||||
[Speech][cloud-speech] | stable | [`cloud.google.com/go/speech/apiv1`](https://godoc.org/cloud.google.com/go/speech/apiv1)
|
||||
[Storage][cloud-storage] | stable | [`cloud.google.com/go/storage`](https://godoc.org/cloud.google.com/go/storage)
|
||||
[Talent][cloud-talent] | alpha | [`cloud.google.com/go/talent/apiv4beta1`](https://godoc.org/cloud.google.com/go/talent/apiv4beta1)
|
||||
[Text To Speech][cloud-texttospeech] | alpha | [`cloud.google.com/go/texttospeech/apiv1`](https://godoc.org/cloud.google.com/go/texttospeech/apiv1)
|
||||
[Trace][cloud-trace] | alpha | [`cloud.google.com/go/trace/apiv2`](https://godoc.org/cloud.google.com/go/trace/apiv2)
|
||||
[Translate][cloud-translate] | stable | [`cloud.google.com/go/translate`](https://godoc.org/cloud.google.com/go/translate)
|
||||
[Video Intelligence][cloud-video] | alpha | [`cloud.google.com/go/videointelligence/apiv1beta1`](https://godoc.org/cloud.google.com/go/videointelligence/apiv1beta1)
|
||||
[Vision][cloud-vision] | stable | [`cloud.google.com/go/vision/apiv1`](https://godoc.org/cloud.google.com/go/vision/apiv1)
|
||||
[Webrisk][cloud-webrisk] | alpha | [`cloud.google.com/go/webrisk/apiv1beta1`](https://godoc.org/cloud.google.com/go/webrisk/apiv1beta1)
|
||||
[Asset][cloud-asset] | stable | [`cloud.google.com/go/asset/apiv1`](https://pkg.go.dev/cloud.google.com/go/asset/v1beta)
|
||||
[Automl][cloud-automl] | stable | [`cloud.google.com/go/automl/apiv1`](https://pkg.go.dev/cloud.google.com/go/automl/apiv1)
|
||||
[BigQuery][cloud-bigquery] | stable | [`cloud.google.com/go/bigquery`](https://pkg.go.dev/cloud.google.com/go/bigquery)
|
||||
[Bigtable][cloud-bigtable] | stable | [`cloud.google.com/go/bigtable`](https://pkg.go.dev/cloud.google.com/go/bigtable)
|
||||
[Cloudbuild][cloud-build] | stable | [`cloud.google.com/go/cloudbuild/apiv1`](https://pkg.go.dev/cloud.google.com/go/cloudbuild/apiv1)
|
||||
[Cloudtasks][cloud-tasks] | stable | [`cloud.google.com/go/cloudtasks/apiv2`](https://pkg.go.dev/cloud.google.com/go/cloudtasks/apiv2)
|
||||
[Container][cloud-container] | stable | [`cloud.google.com/go/container/apiv1`](https://pkg.go.dev/cloud.google.com/go/container/apiv1)
|
||||
[ContainerAnalysis][cloud-containeranalysis] | beta | [`cloud.google.com/go/containeranalysis/apiv1`](https://pkg.go.dev/cloud.google.com/go/containeranalysis/apiv1)
|
||||
[Dataproc][cloud-dataproc] | stable | [`cloud.google.com/go/dataproc/apiv1`](https://pkg.go.dev/cloud.google.com/go/dataproc/apiv1)
|
||||
[Datastore][cloud-datastore] | stable | [`cloud.google.com/go/datastore`](https://pkg.go.dev/cloud.google.com/go/datastore)
|
||||
[Debugger][cloud-debugger] | stable | [`cloud.google.com/go/debugger/apiv2`](https://pkg.go.dev/cloud.google.com/go/debugger/apiv2)
|
||||
[Dialogflow][cloud-dialogflow] | stable | [`cloud.google.com/go/dialogflow/apiv2`](https://pkg.go.dev/cloud.google.com/go/dialogflow/apiv2)
|
||||
[Data Loss Prevention][cloud-dlp] | stable | [`cloud.google.com/go/dlp/apiv2`](https://pkg.go.dev/cloud.google.com/go/dlp/apiv2)
|
||||
[ErrorReporting][cloud-errors] | alpha | [`cloud.google.com/go/errorreporting`](https://pkg.go.dev/cloud.google.com/go/errorreporting)
|
||||
[Firestore][cloud-firestore] | stable | [`cloud.google.com/go/firestore`](https://pkg.go.dev/cloud.google.com/go/firestore)
|
||||
[IAM][cloud-iam] | stable | [`cloud.google.com/go/iam`](https://pkg.go.dev/cloud.google.com/go/iam)
|
||||
[IoT][cloud-iot] | stable | [`cloud.google.com/go/iot/apiv1`](https://pkg.go.dev/cloud.google.com/go/iot/apiv1)
|
||||
[IRM][cloud-irm] | alpha | [`cloud.google.com/go/irm/apiv1alpha2`](https://pkg.go.dev/cloud.google.com/go/irm/apiv1alpha2)
|
||||
[KMS][cloud-kms] | stable | [`cloud.google.com/go/kms/apiv1`](https://pkg.go.dev/cloud.google.com/go/kms/apiv1)
|
||||
[Natural Language][cloud-natural-language] | stable | [`cloud.google.com/go/language/apiv1`](https://pkg.go.dev/cloud.google.com/go/language/apiv1)
|
||||
[Logging][cloud-logging] | stable | [`cloud.google.com/go/logging`](https://pkg.go.dev/cloud.google.com/go/logging)
|
||||
[Memorystore][cloud-memorystore] | alpha | [`cloud.google.com/go/redis/apiv1`](https://pkg.go.dev/cloud.google.com/go/redis/apiv1)
|
||||
[Monitoring][cloud-monitoring] | stable | [`cloud.google.com/go/monitoring/apiv3`](https://pkg.go.dev/cloud.google.com/go/monitoring/apiv3)
|
||||
[OS Login][cloud-oslogin] | stable | [`cloud.google.com/go/oslogin/apiv1`](https://pkg.go.dev/cloud.google.com/go/oslogin/apiv1)
|
||||
[Pub/Sub][cloud-pubsub] | stable | [`cloud.google.com/go/pubsub`](https://pkg.go.dev/cloud.google.com/go/pubsub)
|
||||
[Phishing Protection][cloud-phishingprotection] | alpha | [`cloud.google.com/go/phishingprotection/apiv1beta1`](https://pkg.go.dev/cloud.google.com/go/phishingprotection/apiv1beta1)
|
||||
[reCAPTCHA Enterprise][cloud-recaptcha] | alpha | [`cloud.google.com/go/recaptchaenterprise/apiv1beta1`](https://pkg.go.dev/cloud.google.com/go/recaptchaenterprise/apiv1beta1)
|
||||
[Recommender][cloud-recommender] | beta | [`cloud.google.com/go/recommender/apiv1beta1`](https://pkg.go.dev/cloud.google.com/go/recommender/apiv1beta1)
|
||||
[Scheduler][cloud-scheduler] | stable | [`cloud.google.com/go/scheduler/apiv1`](https://pkg.go.dev/cloud.google.com/go/scheduler/apiv1)
|
||||
[Securitycenter][cloud-securitycenter] | stable | [`cloud.google.com/go/securitycenter/apiv1`](https://pkg.go.dev/cloud.google.com/go/securitycenter/apiv1)
|
||||
[Spanner][cloud-spanner] | stable | [`cloud.google.com/go/spanner`](https://pkg.go.dev/cloud.google.com/go/spanner)
|
||||
[Speech][cloud-speech] | stable | [`cloud.google.com/go/speech/apiv1`](https://pkg.go.dev/cloud.google.com/go/speech/apiv1)
|
||||
[Storage][cloud-storage] | stable | [`cloud.google.com/go/storage`](https://pkg.go.dev/cloud.google.com/go/storage)
|
||||
[Talent][cloud-talent] | alpha | [`cloud.google.com/go/talent/apiv4beta1`](https://pkg.go.dev/cloud.google.com/go/talent/apiv4beta1)
|
||||
[Text To Speech][cloud-texttospeech] | stable | [`cloud.google.com/go/texttospeech/apiv1`](https://pkg.go.dev/cloud.google.com/go/texttospeech/apiv1)
|
||||
[Trace][cloud-trace] | stable | [`cloud.google.com/go/trace/apiv2`](https://pkg.go.dev/cloud.google.com/go/trace/apiv2)
|
||||
[Translate][cloud-translate] | stable | [`cloud.google.com/go/translate`](https://pkg.go.dev/cloud.google.com/go/translate)
|
||||
[Video Intelligence][cloud-video] | beta | [`cloud.google.com/go/videointelligence/apiv1beta2`](https://pkg.go.dev/cloud.google.com/go/videointelligence/apiv1beta2)
|
||||
[Vision][cloud-vision] | stable | [`cloud.google.com/go/vision/apiv1`](https://pkg.go.dev/cloud.google.com/go/vision/apiv1)
|
||||
[Webrisk][cloud-webrisk] | alpha | [`cloud.google.com/go/webrisk/apiv1beta1`](https://pkg.go.dev/cloud.google.com/go/webrisk/apiv1beta1)
|
||||
|
||||
> **Alpha status**: the API is still being actively developed. As a
|
||||
> result, it might change in backward-incompatible ways and is not recommended
|
||||
@@ -83,7 +83,7 @@ Google API | Status | Package
|
||||
> **Stable status**: the API is mature and ready for production use. We will
|
||||
> continue addressing bugs and feature requests.
|
||||
|
||||
Documentation and examples are available at [godoc.org/cloud.google.com/go](https://godoc.org/cloud.google.com/go)
|
||||
Documentation and examples are available at [pkg.go.dev/cloud.google.com/go](https://pkg.go.dev/cloud.google.com/go)
|
||||
|
||||
## Go Versions Supported
|
||||
|
||||
@@ -104,7 +104,7 @@ client, err := storage.NewClient(ctx)
|
||||
To authorize using a
|
||||
[JSON key file](https://cloud.google.com/iam/docs/managing-service-account-keys),
|
||||
pass
|
||||
[`option.WithCredentialsFile`](https://godoc.org/google.golang.org/api/option#WithCredentialsFile)
|
||||
[`option.WithCredentialsFile`](https://pkg.go.dev/google.golang.org/api/option#WithCredentialsFile)
|
||||
to the `NewClient` function of the desired package. For example:
|
||||
|
||||
[snip]:# (auth-JSON)
|
||||
@@ -113,9 +113,9 @@ client, err := storage.NewClient(ctx, option.WithCredentialsFile("path/to/keyfil
|
||||
```
|
||||
|
||||
You can exert more control over authorization by using the
|
||||
[`golang.org/x/oauth2`](https://godoc.org/golang.org/x/oauth2) package to
|
||||
[`golang.org/x/oauth2`](https://pkg.go.dev/golang.org/x/oauth2) package to
|
||||
create an `oauth2.TokenSource`. Then pass
|
||||
[`option.WithTokenSource`](https://godoc.org/google.golang.org/api/option#WithTokenSource)
|
||||
[`option.WithTokenSource`](https://pkg.go.dev/google.golang.org/api/option#WithTokenSource)
|
||||
to the `NewClient` function:
|
||||
[snip]:# (auth-ts)
|
||||
```go
|
||||
|
||||
2
vendor/cloud.google.com/go/RELEASING.md
generated
vendored
2
vendor/cloud.google.com/go/RELEASING.md
generated
vendored
@@ -18,7 +18,7 @@ to install the code reviewing tool.
|
||||
|
||||
1. If you would like, you may want to set up aliases for `git-codereview`,
|
||||
such that `git codereview change` becomes `git change`. See the
|
||||
[godoc](https://godoc.org/golang.org/x/review/git-codereview) for details.
|
||||
[godoc](https://pkg.go.dev/golang.org/x/review/git-codereview) for details.
|
||||
|
||||
* Should you run into issues with the `git-codereview` tool, please note
|
||||
that all error messages will assume that you have set up these aliases.
|
||||
|
||||
40
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
40
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
@@ -61,24 +61,14 @@ var (
|
||||
instID = &cachedValue{k: "instance/id", trim: true}
|
||||
)
|
||||
|
||||
var (
|
||||
defaultClient = &Client{hc: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 2 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
},
|
||||
}}
|
||||
subscribeClient = &Client{hc: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 2 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
},
|
||||
}}
|
||||
)
|
||||
var defaultClient = &Client{hc: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 2 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
},
|
||||
}}
|
||||
|
||||
// NotDefinedError is returned when requested metadata is not defined.
|
||||
//
|
||||
@@ -205,10 +195,9 @@ func systemInfoSuggestsGCE() bool {
|
||||
return name == "Google" || name == "Google Compute Engine"
|
||||
}
|
||||
|
||||
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
|
||||
// ResponseHeaderTimeout).
|
||||
// Subscribe calls Client.Subscribe on the default client.
|
||||
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
||||
return subscribeClient.Subscribe(suffix, fn)
|
||||
return defaultClient.Subscribe(suffix, fn)
|
||||
}
|
||||
|
||||
// Get calls Client.Get on the default client.
|
||||
@@ -279,9 +268,14 @@ type Client struct {
|
||||
hc *http.Client
|
||||
}
|
||||
|
||||
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
|
||||
// will use the given http.Client instead of the default client.
|
||||
// NewClient returns a Client that can be used to fetch metadata.
|
||||
// Returns the client that uses the specified http.Client for HTTP requests.
|
||||
// If nil is specified, returns the default client.
|
||||
func NewClient(c *http.Client) *Client {
|
||||
if c == nil {
|
||||
return defaultClient
|
||||
}
|
||||
|
||||
return &Client{hc: c}
|
||||
}
|
||||
|
||||
|
||||
34
vendor/cloud.google.com/go/go.mod
generated
vendored
34
vendor/cloud.google.com/go/go.mod
generated
vendored
@@ -3,29 +3,27 @@ module cloud.google.com/go
|
||||
go 1.11
|
||||
|
||||
require (
|
||||
cloud.google.com/go/bigquery v1.3.0
|
||||
cloud.google.com/go/datastore v1.0.0
|
||||
cloud.google.com/go/pubsub v1.1.0
|
||||
cloud.google.com/go/storage v1.5.0
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/mock v1.4.0
|
||||
github.com/golang/protobuf v1.3.3
|
||||
cloud.google.com/go/bigquery v1.4.0
|
||||
cloud.google.com/go/datastore v1.1.0
|
||||
cloud.google.com/go/pubsub v1.2.0
|
||||
cloud.google.com/go/storage v1.6.0
|
||||
github.com/golang/mock v1.4.3
|
||||
github.com/golang/protobuf v1.3.5
|
||||
github.com/google/go-cmp v0.4.0
|
||||
github.com/google/martian v2.1.0+incompatible
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3
|
||||
github.com/googleapis/gax-go/v2 v2.0.5
|
||||
github.com/jstemmer/go-junit-report v0.9.1
|
||||
go.opencensus.io v0.22.3
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367
|
||||
golang.org/x/mod v0.2.0 // indirect
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect
|
||||
golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d // indirect
|
||||
golang.org/x/text v0.3.2
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56
|
||||
google.golang.org/api v0.17.0
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce
|
||||
google.golang.org/grpc v1.27.1
|
||||
honnef.co/go/tools v0.0.1-2019.2.3
|
||||
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e
|
||||
google.golang.org/api v0.20.0
|
||||
google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24
|
||||
google.golang.org/grpc v1.28.0
|
||||
honnef.co/go/tools v0.0.1-2020.1.3
|
||||
)
|
||||
|
||||
75
vendor/cloud.google.com/go/go.sum
generated
vendored
75
vendor/cloud.google.com/go/go.sum
generated
vendored
@@ -6,34 +6,49 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0 h1:sAbMqjY1PEQKZBWfbu6Y6bsupJ9c4QdHnzg/VvYTLcE=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0 h1:xE3CPsOgttP4ACBePh79zTKALtXwn/Edhcr16R5hMWU=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0 h1:9/vpR43S4aJaROxqQHQ3nH9lfyKKV0dC3vOmnw8ebQQ=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0 h1:Lpy6hKgdcl7a3WGSfJIFmxmcdjSpP6OmBEfcOv1Y680=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0 h1:RPUcBvDeYgQFMfQu1eBMq6piD1SXmLH+vK3qjewZPus=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0 h1:UDpwYIwla4jHGzZJaEJYx1tOejbgSoNqsAfHAUYe2r8=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -47,6 +62,8 @@ github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -54,6 +71,8 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
@@ -71,8 +90,10 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57 h1:eqyIo2HjKhKe/mJzTG
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12 h1:TgXhFz35pKlZuUz1pNlOKk1UCSXPpuUIc144Wd7SxCA=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 h1:SRgJV+IoxM5MKyFdlSUeNy6/ycRUF2yBAKdAQswoHUk=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
@@ -82,6 +103,7 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
@@ -98,6 +120,7 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||
@@ -119,8 +142,10 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd h1:zkO/Lhoka23X63N9OSzpSeROEUQ5ODw47tM3YWjygbs=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -136,12 +161,15 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -159,8 +187,12 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -178,6 +210,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEha
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -190,10 +224,17 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d h1:62ap6LNOjDU6uGmKXHJbSfciMoV+FeI1sRXx/pLDL44=
|
||||
golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
@@ -204,6 +245,7 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuA
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -227,12 +269,18 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56 h1:DFtSed2q3HtNuVazwVDZ4nSRS/JrZEig0gz2BY4VNrg=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e h1:8ogAbHWoJTPepnVbNRqXLOpzMkl0rtRsM7crbflc4XM=
|
||||
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
@@ -249,6 +297,9 @@ google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0 h1:0q95w+VuFtv4PAx4PZVQdBMmYbaCHbnfKaEiDIcVyag=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -275,8 +326,13 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24 h1:IGPykv426z7LZSVPlaPufOyphngM4at5uZ7x5alaFvE=
|
||||
google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
|
||||
@@ -284,11 +340,14 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
|
||||
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -303,7 +362,11 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
76
vendor/cloud.google.com/go/iam/iam.go
generated
vendored
76
vendor/cloud.google.com/go/iam/iam.go
generated
vendored
@@ -38,6 +38,7 @@ type client interface {
|
||||
Get(ctx context.Context, resource string) (*pb.Policy, error)
|
||||
Set(ctx context.Context, resource string, p *pb.Policy) error
|
||||
Test(ctx context.Context, resource string, perms []string) ([]string, error)
|
||||
GetWithVersion(ctx context.Context, resource string, requestedPolicyVersion int32) (*pb.Policy, error)
|
||||
}
|
||||
|
||||
// grpcClient implements client for the standard gRPC-based IAMPolicy service.
|
||||
@@ -57,13 +58,22 @@ var withRetry = gax.WithRetry(func() gax.Retryer {
|
||||
})
|
||||
|
||||
func (g *grpcClient) Get(ctx context.Context, resource string) (*pb.Policy, error) {
|
||||
return g.GetWithVersion(ctx, resource, 1)
|
||||
}
|
||||
|
||||
func (g *grpcClient) GetWithVersion(ctx context.Context, resource string, requestedPolicyVersion int32) (*pb.Policy, error) {
|
||||
var proto *pb.Policy
|
||||
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", resource))
|
||||
ctx = insertMetadata(ctx, md)
|
||||
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error {
|
||||
var err error
|
||||
proto, err = g.c.GetIamPolicy(ctx, &pb.GetIamPolicyRequest{Resource: resource})
|
||||
proto, err = g.c.GetIamPolicy(ctx, &pb.GetIamPolicyRequest{
|
||||
Resource: resource,
|
||||
Options: &pb.GetPolicyOptions{
|
||||
RequestedPolicyVersion: requestedPolicyVersion,
|
||||
},
|
||||
})
|
||||
return err
|
||||
}, withRetry)
|
||||
if err != nil {
|
||||
@@ -110,11 +120,18 @@ type Handle struct {
|
||||
resource string
|
||||
}
|
||||
|
||||
// A Handle3 provides IAM operations for a resource. It is similar to a Handle, but provides access to newer IAM features (e.g., conditions).
|
||||
type Handle3 struct {
|
||||
c client
|
||||
resource string
|
||||
version int32
|
||||
}
|
||||
|
||||
// InternalNewHandle is for use by the Google Cloud Libraries only.
|
||||
//
|
||||
// InternalNewHandle returns a Handle for resource.
|
||||
// The conn parameter refers to a server that must support the IAMPolicy service.
|
||||
func InternalNewHandle(conn *grpc.ClientConn, resource string) *Handle {
|
||||
func InternalNewHandle(conn grpc.ClientConnInterface, resource string) *Handle {
|
||||
return InternalNewHandleGRPCClient(pb.NewIAMPolicyClient(conn), resource)
|
||||
}
|
||||
|
||||
@@ -137,6 +154,17 @@ func InternalNewHandleClient(c client, resource string) *Handle {
|
||||
}
|
||||
}
|
||||
|
||||
// V3 returns a Handle3, which is like Handle except it sets
|
||||
// requestedPolicyVersion to 3 when retrieving a policy and policy.version to 3
|
||||
// when storing a policy.
|
||||
func (h *Handle) V3() *Handle3 {
|
||||
return &Handle3{
|
||||
c: h.c,
|
||||
resource: h.resource,
|
||||
version: 3,
|
||||
}
|
||||
}
|
||||
|
||||
// Policy retrieves the IAM policy for the resource.
|
||||
func (h *Handle) Policy(ctx context.Context) (*Policy, error) {
|
||||
proto, err := h.c.Get(ctx, h.resource)
|
||||
@@ -313,3 +341,47 @@ func insertMetadata(ctx context.Context, mds ...metadata.MD) context.Context {
|
||||
}
|
||||
return metadata.NewOutgoingContext(ctx, out)
|
||||
}
|
||||
|
||||
// A Policy3 is a list of Bindings representing roles granted to members.
|
||||
//
|
||||
// The zero Policy3 is a valid policy with no bindings.
|
||||
//
|
||||
// It is similar to a Policy, except a Policy3 provides direct access to the
|
||||
// list of Bindings.
|
||||
//
|
||||
// The policy version is always set to 3.
|
||||
type Policy3 struct {
|
||||
etag []byte
|
||||
Bindings []*pb.Binding
|
||||
}
|
||||
|
||||
// Policy retrieves the IAM policy for the resource.
|
||||
//
|
||||
// requestedPolicyVersion is always set to 3.
|
||||
func (h *Handle3) Policy(ctx context.Context) (*Policy3, error) {
|
||||
proto, err := h.c.GetWithVersion(ctx, h.resource, h.version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Policy3{
|
||||
Bindings: proto.Bindings,
|
||||
etag: proto.Etag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetPolicy replaces the resource's current policy with the supplied Policy.
|
||||
//
|
||||
// If policy was created from a prior call to Get, then the modification will
|
||||
// only succeed if the policy has not changed since the Get.
|
||||
func (h *Handle3) SetPolicy(ctx context.Context, policy *Policy3) error {
|
||||
return h.c.Set(ctx, h.resource, &pb.Policy{
|
||||
Bindings: policy.Bindings,
|
||||
Etag: policy.etag,
|
||||
Version: h.version,
|
||||
})
|
||||
}
|
||||
|
||||
// TestPermissions returns the subset of permissions that the caller has on the resource.
|
||||
func (h *Handle3) TestPermissions(ctx context.Context, permissions []string) ([]string, error) {
|
||||
return h.c.Test(ctx, h.resource, permissions)
|
||||
}
|
||||
|
||||
92
vendor/cloud.google.com/go/internal/.repo-metadata-full.json
generated
vendored
92
vendor/cloud.google.com/go/internal/.repo-metadata-full.json
generated
vendored
@@ -5,7 +5,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/asset/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/asset/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/asset/apiv1beta1",
|
||||
@@ -28,7 +28,7 @@
|
||||
"description": "Cloud AutoML API",
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/loud.google.com/go/automl/apiv1",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/automl/apiv1",
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/automl/apiv1beta1": {
|
||||
@@ -36,7 +36,7 @@
|
||||
"description": "Cloud AutoML API",
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/loud.google.com/go/automl/apiv1beta1",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/automl/apiv1beta1",
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/bigquery": {
|
||||
@@ -53,7 +53,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/bigquery/datatransfer/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/bigquery/reservation/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/bigquery/reservation/apiv1beta1",
|
||||
@@ -143,6 +143,14 @@
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/cloudtasks/apiv2beta3",
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/container/apiv1": {
|
||||
"distribution_name": "cloud.google.com/go/container/apiv1",
|
||||
"description": "Kubernetes Engine API",
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/container/apiv1",
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/containeranalysis/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/containeranalysis/apiv1beta1",
|
||||
"description": "Container Analysis API",
|
||||
@@ -165,7 +173,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/dataproc/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/dataproc/apiv1beta2": {
|
||||
"distribution_name": "cloud.google.com/go/dataproc/apiv1beta2",
|
||||
@@ -213,7 +221,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "manual",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/errorreporting",
|
||||
"release_level": "ga"
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/errorreporting/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/errorreporting/apiv1beta1",
|
||||
@@ -245,7 +253,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/firestore/apiv1",
|
||||
"release_level": "beta"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/firestore/apiv1/admin": {
|
||||
"distribution_name": "cloud.google.com/go/firestore/apiv1/admin",
|
||||
@@ -253,7 +261,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/firestore/apiv1/admin",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/firestore/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/firestore/apiv1beta1",
|
||||
@@ -277,7 +285,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/iam/credentials/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/iot/apiv1": {
|
||||
"distribution_name": "cloud.google.com/go/iot/apiv1",
|
||||
@@ -309,7 +317,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/language/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/language/apiv1beta2": {
|
||||
"distribution_name": "cloud.google.com/go/language/apiv1beta2",
|
||||
@@ -333,7 +341,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/logging/apiv2",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/longrunning/autogen": {
|
||||
"distribution_name": "cloud.google.com/go/longrunning/autogen",
|
||||
@@ -343,13 +351,21 @@
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/longrunning/autogen",
|
||||
"release_level": "alpha"
|
||||
},
|
||||
"cloud.google.com/go/memcache/apiv1beta2": {
|
||||
"distribution_name": "cloud.google.com/go/memcache/apiv1beta2",
|
||||
"description": "Cloud Memorystore for Memcached API",
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/memcache/apiv1beta2",
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/monitoring/apiv3": {
|
||||
"distribution_name": "cloud.google.com/go/monitoring/apiv3",
|
||||
"description": "Stackdriver Monitoring API",
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/monitoring/apiv3",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/osconfig/agentendpoint/apiv1beta": {
|
||||
"distribution_name": "cloud.google.com/go/osconfig/agentendpoint/apiv1beta",
|
||||
@@ -373,7 +389,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/oslogin/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/oslogin/apiv1beta": {
|
||||
"distribution_name": "cloud.google.com/go/oslogin/apiv1beta",
|
||||
@@ -399,6 +415,14 @@
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/pubsub",
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/pubsub/apiv1": {
|
||||
"distribution_name": "cloud.google.com/go/pubsub/apiv1",
|
||||
"description": "Cloud Pub/Sub API",
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/pubsub/apiv1",
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/recaptchaenterprise/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/recaptchaenterprise/apiv1beta1",
|
||||
"description": "reCAPTCHA Enterprise API",
|
||||
@@ -429,7 +453,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/redis/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/redis/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/redis/apiv1beta1",
|
||||
@@ -439,6 +463,14 @@
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/redis/apiv1beta1",
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/rpcreplay": {
|
||||
"distribution_name": "cloud.google.com/go/rpcreplay",
|
||||
"description": "RPC Replay",
|
||||
"language": "Go",
|
||||
"client_library_type": "manual",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/rpcreplay",
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/scheduler/apiv1": {
|
||||
"distribution_name": "cloud.google.com/go/scheduler/apiv1",
|
||||
"description": "Cloud Scheduler API",
|
||||
@@ -455,6 +487,14 @@
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/scheduler/apiv1beta1",
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/secretmanager/apiv1": {
|
||||
"distribution_name": "cloud.google.com/go/secretmanager/apiv1",
|
||||
"description": "Secret Manager API",
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/secretmanager/apiv1",
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/secretmanager/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/secretmanager/apiv1beta1",
|
||||
"description": "Secret Manager API",
|
||||
@@ -469,7 +509,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/securitycenter/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/securitycenter/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/securitycenter/apiv1beta1",
|
||||
@@ -487,6 +527,14 @@
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/securitycenter/apiv1p1beta1",
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/servicedirectory/apiv1beta1": {
|
||||
"distribution_name": "cloud.google.com/go/servicedirectory/apiv1beta1",
|
||||
"description": "Service Directory API",
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/servicedirectory/apiv1beta1",
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/spanner": {
|
||||
"distribution_name": "cloud.google.com/go/spanner",
|
||||
"description": "Cloud Spanner",
|
||||
@@ -501,7 +549,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/spanner/admin/database/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/spanner/admin/instance/apiv1": {
|
||||
"distribution_name": "cloud.google.com/go/spanner/admin/instance/apiv1",
|
||||
@@ -509,7 +557,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/spanner/admin/instance/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/spanner/apiv1": {
|
||||
"distribution_name": "cloud.google.com/go/spanner/apiv1",
|
||||
@@ -548,7 +596,7 @@
|
||||
"description": "Cloud Talent Solution API",
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/loud.google.com/go/talent/apiv4beta1",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/talent/apiv4beta1",
|
||||
"release_level": "beta"
|
||||
},
|
||||
"cloud.google.com/go/texttospeech/apiv1": {
|
||||
@@ -557,7 +605,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/texttospeech/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/trace": {
|
||||
"distribution_name": "cloud.google.com/go/trace",
|
||||
@@ -573,7 +621,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/trace/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/trace/apiv2": {
|
||||
"distribution_name": "cloud.google.com/go/trace/apiv2",
|
||||
@@ -581,7 +629,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/trace/apiv2",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/translate/apiv3": {
|
||||
"distribution_name": "cloud.google.com/go/translate/apiv3",
|
||||
@@ -597,7 +645,7 @@
|
||||
"language": "Go",
|
||||
"client_library_type": "generated",
|
||||
"docs_url": "https://pkg.go.dev/cloud.google.com/go/videointelligence/apiv1",
|
||||
"release_level": "alpha"
|
||||
"release_level": "ga"
|
||||
},
|
||||
"cloud.google.com/go/videointelligence/apiv1beta2": {
|
||||
"distribution_name": "cloud.google.com/go/videointelligence/apiv1beta2",
|
||||
|
||||
2
vendor/cloud.google.com/go/internal/version/version.go
generated
vendored
2
vendor/cloud.google.com/go/internal/version/version.go
generated
vendored
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
// Repo is the current version of the client libraries in this
|
||||
// repo. It should be a date in YYYYMMDD format.
|
||||
const Repo = "20200212"
|
||||
const Repo = "20200316"
|
||||
|
||||
// Go returns the Go runtime version. The returned string
|
||||
// has no whitespace.
|
||||
|
||||
1
vendor/cloud.google.com/go/tools.go
generated
vendored
1
vendor/cloud.google.com/go/tools.go
generated
vendored
@@ -26,7 +26,6 @@ package cloud
|
||||
import (
|
||||
_ "github.com/golang/protobuf/protoc-gen-go"
|
||||
_ "github.com/jstemmer/go-junit-report"
|
||||
_ "golang.org/x/exp/cmd/apidiff"
|
||||
_ "golang.org/x/lint/golint"
|
||||
_ "golang.org/x/tools/cmd/goimports"
|
||||
_ "honnef.co/go/tools/cmd/staticcheck"
|
||||
|
||||
14
vendor/github.com/VictoriaMetrics/metrics/histogram.go
generated
vendored
14
vendor/github.com/VictoriaMetrics/metrics/histogram.go
generated
vendored
@@ -246,14 +246,22 @@ func (h *Histogram) marshalTo(prefix string, w io.Writer) {
|
||||
return
|
||||
}
|
||||
name, filters := splitMetricName(prefix)
|
||||
if float64(int64(h.sum)) == h.sum {
|
||||
fmt.Fprintf(w, "%s_sum%s %d\n", name, filters, int64(h.sum))
|
||||
sum := h.getSum()
|
||||
if float64(int64(sum)) == sum {
|
||||
fmt.Fprintf(w, "%s_sum%s %d\n", name, filters, int64(sum))
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s_sum%s %g\n", name, filters, h.sum)
|
||||
fmt.Fprintf(w, "%s_sum%s %g\n", name, filters, sum)
|
||||
}
|
||||
fmt.Fprintf(w, "%s_count%s %d\n", name, filters, countTotal)
|
||||
}
|
||||
|
||||
func (h *Histogram) getSum() float64 {
|
||||
h.mu.Lock()
|
||||
sum := h.sum
|
||||
h.mu.Unlock()
|
||||
return sum
|
||||
}
|
||||
|
||||
func getBucketIdxAndOffset(v float64) (int, uint) {
|
||||
if v < 0 {
|
||||
panic(fmt.Errorf("BUG: v must be positive; got %g", v))
|
||||
|
||||
4
vendor/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go
generated
vendored
4
vendor/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go
generated
vendored
@@ -19,7 +19,9 @@ type StaticProvider struct {
|
||||
}
|
||||
|
||||
// NewStaticCredentials returns a pointer to a new Credentials object
|
||||
// wrapping a static credentials value provider.
|
||||
// wrapping a static credentials value provider. Token is only required
|
||||
// for temporary security credentials retrieved via STS, otherwise an empty
|
||||
// string can be passed for this parameter.
|
||||
func NewStaticCredentials(id, secret, token string) *Credentials {
|
||||
return NewCredentials(&StaticProvider{Value: Value{
|
||||
AccessKeyID: id,
|
||||
|
||||
1276
vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go
generated
vendored
1276
vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go
generated
vendored
File diff suppressed because it is too large
Load Diff
10
vendor/github.com/aws/aws-sdk-go/aws/session/credentials.go
generated
vendored
10
vendor/github.com/aws/aws-sdk-go/aws/session/credentials.go
generated
vendored
@@ -3,6 +3,7 @@ package session
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
@@ -206,7 +207,14 @@ func credsFromAssumeRole(cfg aws.Config,
|
||||
sharedCfg.RoleARN,
|
||||
func(opt *stscreds.AssumeRoleProvider) {
|
||||
opt.RoleSessionName = sharedCfg.RoleSessionName
|
||||
opt.Duration = sessOpts.AssumeRoleDuration
|
||||
|
||||
if sessOpts.AssumeRoleDuration == 0 &&
|
||||
sharedCfg.AssumeRoleDuration != nil &&
|
||||
*sharedCfg.AssumeRoleDuration/time.Minute > 15 {
|
||||
opt.Duration = *sharedCfg.AssumeRoleDuration
|
||||
} else if sessOpts.AssumeRoleDuration != 0 {
|
||||
opt.Duration = sessOpts.AssumeRoleDuration
|
||||
}
|
||||
|
||||
// Assume role with external ID
|
||||
if len(sharedCfg.ExternalID) > 0 {
|
||||
|
||||
28
vendor/github.com/aws/aws-sdk-go/aws/session/shared_config.go
generated
vendored
28
vendor/github.com/aws/aws-sdk-go/aws/session/shared_config.go
generated
vendored
@@ -2,6 +2,7 @@ package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
@@ -16,12 +17,13 @@ const (
|
||||
sessionTokenKey = `aws_session_token` // optional
|
||||
|
||||
// Assume Role Credentials group
|
||||
roleArnKey = `role_arn` // group required
|
||||
sourceProfileKey = `source_profile` // group required (or credential_source)
|
||||
credentialSourceKey = `credential_source` // group required (or source_profile)
|
||||
externalIDKey = `external_id` // optional
|
||||
mfaSerialKey = `mfa_serial` // optional
|
||||
roleSessionNameKey = `role_session_name` // optional
|
||||
roleArnKey = `role_arn` // group required
|
||||
sourceProfileKey = `source_profile` // group required (or credential_source)
|
||||
credentialSourceKey = `credential_source` // group required (or source_profile)
|
||||
externalIDKey = `external_id` // optional
|
||||
mfaSerialKey = `mfa_serial` // optional
|
||||
roleSessionNameKey = `role_session_name` // optional
|
||||
roleDurationSecondsKey = "duration_seconds" // optional
|
||||
|
||||
// CSM options
|
||||
csmEnabledKey = `csm_enabled`
|
||||
@@ -73,10 +75,11 @@ type sharedConfig struct {
|
||||
CredentialProcess string
|
||||
WebIdentityTokenFile string
|
||||
|
||||
RoleARN string
|
||||
RoleSessionName string
|
||||
ExternalID string
|
||||
MFASerial string
|
||||
RoleARN string
|
||||
RoleSessionName string
|
||||
ExternalID string
|
||||
MFASerial string
|
||||
AssumeRoleDuration *time.Duration
|
||||
|
||||
SourceProfileName string
|
||||
SourceProfile *sharedConfig
|
||||
@@ -274,6 +277,11 @@ func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile, e
|
||||
updateString(&cfg.CredentialSource, section, credentialSourceKey)
|
||||
updateString(&cfg.Region, section, regionKey)
|
||||
|
||||
if section.Has(roleDurationSecondsKey) {
|
||||
d := time.Duration(section.Int(roleDurationSecondsKey)) * time.Second
|
||||
cfg.AssumeRoleDuration = &d
|
||||
}
|
||||
|
||||
if v := section.String(stsRegionalEndpointSharedKey); len(v) != 0 {
|
||||
sre, err := endpoints.GetSTSRegionalEndpoint(v)
|
||||
if err != nil {
|
||||
|
||||
2
vendor/github.com/aws/aws-sdk-go/aws/version.go
generated
vendored
2
vendor/github.com/aws/aws-sdk-go/aws/version.go
generated
vendored
@@ -5,4 +5,4 @@ package aws
|
||||
const SDKName = "aws-sdk-go"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = "1.29.10"
|
||||
const SDKVersion = "1.29.34"
|
||||
|
||||
9
vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/build.go
generated
vendored
9
vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/build.go
generated
vendored
@@ -8,6 +8,7 @@ import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/private/protocol"
|
||||
@@ -60,6 +61,14 @@ func (b *xmlBuilder) buildValue(value reflect.Value, current *XMLNode, tag refle
|
||||
return nil
|
||||
}
|
||||
|
||||
xml := tag.Get("xml")
|
||||
if len(xml) != 0 {
|
||||
name := strings.SplitAfterN(xml, ",", 2)[0]
|
||||
if name == "-" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
t := tag.Get("type")
|
||||
if t == "" {
|
||||
switch value.Kind() {
|
||||
|
||||
8
vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/unmarshal.go
generated
vendored
8
vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/unmarshal.go
generated
vendored
@@ -64,6 +64,14 @@ func UnmarshalXML(v interface{}, d *xml.Decoder, wrapper string) error {
|
||||
// parse deserializes any value from the XMLNode. The type tag is used to infer the type, or reflect
|
||||
// will be used to determine the type from r.
|
||||
func parse(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||
xml := tag.Get("xml")
|
||||
if len(xml) != 0 {
|
||||
name := strings.SplitAfterN(xml, ",", 2)[0]
|
||||
if name == "-" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
rtype := r.Type()
|
||||
if rtype.Kind() == reflect.Ptr {
|
||||
rtype = rtype.Elem() // check kind of actual element type
|
||||
|
||||
244
vendor/github.com/aws/aws-sdk-go/service/s3/s3manager/pool.go
generated
vendored
Normal file
244
vendor/github.com/aws/aws-sdk-go/service/s3/s3manager/pool.go
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
package s3manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
)
|
||||
|
||||
type byteSlicePool interface {
|
||||
Get(aws.Context) (*[]byte, error)
|
||||
Put(*[]byte)
|
||||
ModifyCapacity(int)
|
||||
SliceSize() int64
|
||||
Close()
|
||||
}
|
||||
|
||||
type maxSlicePool struct {
|
||||
// allocator is defined as a function pointer to allow
|
||||
// for test cases to instrument custom tracers when allocations
|
||||
// occur.
|
||||
allocator sliceAllocator
|
||||
|
||||
slices chan *[]byte
|
||||
allocations chan struct{}
|
||||
capacityChange chan struct{}
|
||||
|
||||
max int
|
||||
sliceSize int64
|
||||
|
||||
mtx sync.RWMutex
|
||||
}
|
||||
|
||||
func newMaxSlicePool(sliceSize int64) *maxSlicePool {
|
||||
p := &maxSlicePool{sliceSize: sliceSize}
|
||||
p.allocator = p.newSlice
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
var errZeroCapacity = fmt.Errorf("get called on zero capacity pool")
|
||||
|
||||
func (p *maxSlicePool) Get(ctx aws.Context) (*[]byte, error) {
|
||||
// check if context is canceled before attempting to get a slice
|
||||
// this ensures priority is given to the cancel case first
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
p.mtx.RLock()
|
||||
|
||||
for {
|
||||
select {
|
||||
case bs, ok := <-p.slices:
|
||||
p.mtx.RUnlock()
|
||||
if !ok {
|
||||
// attempt to get on a zero capacity pool
|
||||
return nil, errZeroCapacity
|
||||
}
|
||||
return bs, nil
|
||||
case _, ok := <-p.allocations:
|
||||
p.mtx.RUnlock()
|
||||
if !ok {
|
||||
// attempt to get on a zero capacity pool
|
||||
return nil, errZeroCapacity
|
||||
}
|
||||
return p.allocator(), nil
|
||||
case <-ctx.Done():
|
||||
p.mtx.RUnlock()
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
// In the event that there are no slices or allocations available
|
||||
// This prevents some deadlock situations that can occur around sync.RWMutex
|
||||
// When a lock request occurs on ModifyCapacity, no new readers are allowed to acquire a read lock.
|
||||
// By releasing the read lock here and waiting for a notification, we prevent a deadlock situation where
|
||||
// Get could hold the read lock indefinitely waiting for capacity, ModifyCapacity is waiting for a write lock,
|
||||
// and a Put is blocked trying to get a read-lock which is blocked by ModifyCapacity.
|
||||
|
||||
// Short-circuit if the pool capacity is zero.
|
||||
if p.max == 0 {
|
||||
p.mtx.RUnlock()
|
||||
return nil, errZeroCapacity
|
||||
}
|
||||
|
||||
// Since we will be releasing the read-lock we need to take the reference to the channel.
|
||||
// Since channels are references we will still get notified if slices are added, or if
|
||||
// the channel is closed due to a capacity modification. This specifically avoids a data race condition
|
||||
// where ModifyCapacity both closes a channel and initializes a new one while we don't have a read-lock.
|
||||
c := p.capacityChange
|
||||
|
||||
p.mtx.RUnlock()
|
||||
|
||||
select {
|
||||
case _ = <-c:
|
||||
p.mtx.RLock()
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *maxSlicePool) Put(bs *[]byte) {
|
||||
p.mtx.RLock()
|
||||
defer p.mtx.RUnlock()
|
||||
|
||||
if p.max == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case p.slices <- bs:
|
||||
p.notifyCapacity()
|
||||
default:
|
||||
// If the new channel when attempting to add the slice then we drop the slice.
|
||||
// The logic here is to prevent a deadlock situation if channel is already at max capacity.
|
||||
// Allows us to reap allocations that are returned and are no longer needed.
|
||||
}
|
||||
}
|
||||
|
||||
func (p *maxSlicePool) ModifyCapacity(delta int) {
|
||||
if delta == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
p.mtx.Lock()
|
||||
defer p.mtx.Unlock()
|
||||
|
||||
p.max += delta
|
||||
|
||||
if p.max == 0 {
|
||||
p.empty()
|
||||
return
|
||||
}
|
||||
|
||||
if p.capacityChange != nil {
|
||||
close(p.capacityChange)
|
||||
}
|
||||
p.capacityChange = make(chan struct{}, p.max)
|
||||
|
||||
origAllocations := p.allocations
|
||||
p.allocations = make(chan struct{}, p.max)
|
||||
|
||||
newAllocs := len(origAllocations) + delta
|
||||
for i := 0; i < newAllocs; i++ {
|
||||
p.allocations <- struct{}{}
|
||||
}
|
||||
|
||||
if origAllocations != nil {
|
||||
close(origAllocations)
|
||||
}
|
||||
|
||||
origSlices := p.slices
|
||||
p.slices = make(chan *[]byte, p.max)
|
||||
if origSlices == nil {
|
||||
return
|
||||
}
|
||||
|
||||
close(origSlices)
|
||||
for bs := range origSlices {
|
||||
select {
|
||||
case p.slices <- bs:
|
||||
default:
|
||||
// If the new channel blocks while adding slices from the old channel
|
||||
// then we drop the slice. The logic here is to prevent a deadlock situation
|
||||
// if the new channel has a smaller capacity then the old.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *maxSlicePool) notifyCapacity() {
|
||||
select {
|
||||
case p.capacityChange <- struct{}{}:
|
||||
default:
|
||||
// This *shouldn't* happen as the channel is both buffered to the max pool capacity size and is resized
|
||||
// on capacity modifications. This is just a safety to ensure that a blocking situation can't occur.
|
||||
}
|
||||
}
|
||||
|
||||
func (p *maxSlicePool) SliceSize() int64 {
|
||||
return p.sliceSize
|
||||
}
|
||||
|
||||
func (p *maxSlicePool) Close() {
|
||||
p.mtx.Lock()
|
||||
defer p.mtx.Unlock()
|
||||
p.empty()
|
||||
}
|
||||
|
||||
func (p *maxSlicePool) empty() {
|
||||
p.max = 0
|
||||
|
||||
if p.capacityChange != nil {
|
||||
close(p.capacityChange)
|
||||
p.capacityChange = nil
|
||||
}
|
||||
|
||||
if p.allocations != nil {
|
||||
close(p.allocations)
|
||||
for range p.allocations {
|
||||
// drain channel
|
||||
}
|
||||
p.allocations = nil
|
||||
}
|
||||
|
||||
if p.slices != nil {
|
||||
close(p.slices)
|
||||
for range p.slices {
|
||||
// drain channel
|
||||
}
|
||||
p.slices = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *maxSlicePool) newSlice() *[]byte {
|
||||
bs := make([]byte, p.sliceSize)
|
||||
return &bs
|
||||
}
|
||||
|
||||
type returnCapacityPoolCloser struct {
|
||||
byteSlicePool
|
||||
returnCapacity int
|
||||
}
|
||||
|
||||
func (n *returnCapacityPoolCloser) ModifyCapacity(delta int) {
|
||||
if delta > 0 {
|
||||
n.returnCapacity = -1 * delta
|
||||
}
|
||||
n.byteSlicePool.ModifyCapacity(delta)
|
||||
}
|
||||
|
||||
func (n *returnCapacityPoolCloser) Close() {
|
||||
if n.returnCapacity < 0 {
|
||||
n.byteSlicePool.ModifyCapacity(n.returnCapacity)
|
||||
}
|
||||
}
|
||||
|
||||
type sliceAllocator func() *[]byte
|
||||
|
||||
var newByteSlicePool = func(sliceSize int64) byteSlicePool {
|
||||
return newMaxSlicePool(sliceSize)
|
||||
}
|
||||
64
vendor/github.com/aws/aws-sdk-go/service/s3/s3manager/upload.go
generated
vendored
64
vendor/github.com/aws/aws-sdk-go/service/s3/s3manager/upload.go
generated
vendored
@@ -366,6 +366,7 @@ func (u *uploader) upload() (*UploadOutput, error) {
|
||||
if err := u.init(); err != nil {
|
||||
return nil, awserr.New("ReadRequestBody", "unable to initialize upload", err)
|
||||
}
|
||||
defer u.cfg.partPool.Close()
|
||||
|
||||
if u.cfg.PartSize < MinUploadPartSize {
|
||||
msg := fmt.Sprintf("part size must be at least %d bytes", MinUploadPartSize)
|
||||
@@ -404,8 +405,13 @@ func (u *uploader) init() error {
|
||||
|
||||
// If PartSize was changed or partPool was never setup then we need to allocated a new pool
|
||||
// so that we return []byte slices of the correct size
|
||||
if u.cfg.partPool == nil || u.cfg.partPool.Size() != u.cfg.PartSize {
|
||||
poolCap := u.cfg.Concurrency + 1
|
||||
if u.cfg.partPool == nil || u.cfg.partPool.SliceSize() != u.cfg.PartSize {
|
||||
u.cfg.partPool = newByteSlicePool(u.cfg.PartSize)
|
||||
u.cfg.partPool.ModifyCapacity(poolCap)
|
||||
} else {
|
||||
u.cfg.partPool = &returnCapacityPoolCloser{byteSlicePool: u.cfg.partPool}
|
||||
u.cfg.partPool.ModifyCapacity(poolCap)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -441,10 +447,6 @@ func (u *uploader) initSize() error {
|
||||
// does not need to be wrapped in a mutex because nextReader is only called
|
||||
// from the main thread.
|
||||
func (u *uploader) nextReader() (io.ReadSeeker, int, func(), error) {
|
||||
type readerAtSeeker interface {
|
||||
io.ReaderAt
|
||||
io.ReadSeeker
|
||||
}
|
||||
switch r := u.in.Body.(type) {
|
||||
case readerAtSeeker:
|
||||
var err error
|
||||
@@ -476,15 +478,19 @@ func (u *uploader) nextReader() (io.ReadSeeker, int, func(), error) {
|
||||
return reader, int(n), cleanup, err
|
||||
|
||||
default:
|
||||
part := u.cfg.partPool.Get()
|
||||
n, err := readFillBuf(r, part)
|
||||
part, err := u.cfg.partPool.Get(u.ctx)
|
||||
if err != nil {
|
||||
return nil, 0, func() {}, err
|
||||
}
|
||||
|
||||
n, err := readFillBuf(r, *part)
|
||||
u.readerPos += int64(n)
|
||||
|
||||
cleanup := func() {
|
||||
u.cfg.partPool.Put(part)
|
||||
}
|
||||
|
||||
return bytes.NewReader(part[0:n]), n, cleanup, err
|
||||
return bytes.NewReader((*part)[0:n]), n, cleanup, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,6 +625,7 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker, cleanup func()) (*UploadO
|
||||
Key: u.in.Key,
|
||||
})
|
||||
getReq.Config.Credentials = credentials.AnonymousCredentials
|
||||
getReq.SetContext(u.ctx)
|
||||
uploadLocation, _, _ := getReq.PresignRequest(1)
|
||||
|
||||
return &UploadOutput{
|
||||
@@ -673,6 +680,8 @@ func (u *multiuploader) readChunk(ch chan chunk) {
|
||||
u.seterr(err)
|
||||
}
|
||||
}
|
||||
|
||||
data.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,7 +699,6 @@ func (u *multiuploader) send(c chunk) error {
|
||||
}
|
||||
|
||||
resp, err := u.cfg.S3.UploadPartWithContext(u.ctx, params, u.cfg.RequestOptions...)
|
||||
c.cleanup()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -763,39 +771,7 @@ func (u *multiuploader) complete() *s3.CompleteMultipartUploadOutput {
|
||||
return resp
|
||||
}
|
||||
|
||||
type byteSlicePool interface {
|
||||
Get() []byte
|
||||
Put([]byte)
|
||||
Size() int64
|
||||
}
|
||||
|
||||
type partPool struct {
|
||||
partSize int64
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
func (p *partPool) Get() []byte {
|
||||
return p.Pool.Get().([]byte)
|
||||
}
|
||||
|
||||
func (p *partPool) Put(b []byte) {
|
||||
p.Pool.Put(b)
|
||||
}
|
||||
|
||||
func (p *partPool) Size() int64 {
|
||||
return p.partSize
|
||||
}
|
||||
|
||||
func newPartPool(partSize int64) *partPool {
|
||||
p := &partPool{partSize: partSize}
|
||||
|
||||
p.New = func() interface{} {
|
||||
return make([]byte, p.partSize)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
var newByteSlicePool = func(partSize int64) byteSlicePool {
|
||||
return newPartPool(partSize)
|
||||
type readerAtSeeker interface {
|
||||
io.ReaderAt
|
||||
io.ReadSeeker
|
||||
}
|
||||
|
||||
12
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
generated
vendored
12
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
generated
vendored
@@ -1376,8 +1376,8 @@ type FileOptions struct {
|
||||
// determining the namespace.
|
||||
PhpNamespace *string `protobuf:"bytes,41,opt,name=php_namespace,json=phpNamespace" json:"php_namespace,omitempty"`
|
||||
// Use this option to change the namespace of php generated metadata classes.
|
||||
// Default is empty. When this option is empty, the proto file name will be used
|
||||
// for determining the namespace.
|
||||
// Default is empty. When this option is empty, the proto file name will be
|
||||
// used for determining the namespace.
|
||||
PhpMetadataNamespace *string `protobuf:"bytes,44,opt,name=php_metadata_namespace,json=phpMetadataNamespace" json:"php_metadata_namespace,omitempty"`
|
||||
// Use this option to change the package of ruby generated classes. Default
|
||||
// is empty. When this option is not set, the package name will be used for
|
||||
@@ -1627,7 +1627,7 @@ type MessageOptions struct {
|
||||
//
|
||||
// Implementations may choose not to generate the map_entry=true message, but
|
||||
// use a native map in the target language to hold the keys and values.
|
||||
// The reflection APIs in such implementions still need to work as
|
||||
// The reflection APIs in such implementations still need to work as
|
||||
// if the field is a repeated message field.
|
||||
//
|
||||
// NOTE: Do not set the option in .proto files. Always use the maps syntax
|
||||
@@ -2377,7 +2377,7 @@ type SourceCodeInfo struct {
|
||||
// beginning of the "extend" block and is shared by all extensions within
|
||||
// the block.
|
||||
// - Just because a location's span is a subset of some other location's span
|
||||
// does not mean that it is a descendent. For example, a "group" defines
|
||||
// does not mean that it is a descendant. For example, a "group" defines
|
||||
// both a type and a field in a single declaration. Thus, the locations
|
||||
// corresponding to the type and field and their components will overlap.
|
||||
// - Code which tries to interpret locations should probably be designed to
|
||||
@@ -2718,7 +2718,9 @@ func init() {
|
||||
proto.RegisterType((*GeneratedCodeInfo_Annotation)(nil), "google.protobuf.GeneratedCodeInfo.Annotation")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("google/protobuf/descriptor.proto", fileDescriptor_e5baabe45344a177) }
|
||||
func init() {
|
||||
proto.RegisterFile("google/protobuf/descriptor.proto", fileDescriptor_e5baabe45344a177)
|
||||
}
|
||||
|
||||
var fileDescriptor_e5baabe45344a177 = []byte{
|
||||
// 2589 bytes of a gzipped FileDescriptorProto
|
||||
|
||||
140
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto
generated
vendored
140
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto
generated
vendored
@@ -40,6 +40,7 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package google.protobuf;
|
||||
|
||||
option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor";
|
||||
option java_package = "com.google.protobuf";
|
||||
option java_outer_classname = "DescriptorProtos";
|
||||
@@ -59,8 +60,8 @@ message FileDescriptorSet {
|
||||
|
||||
// Describes a complete .proto file.
|
||||
message FileDescriptorProto {
|
||||
optional string name = 1; // file name, relative to root of source tree
|
||||
optional string package = 2; // e.g. "foo", "foo.bar", etc.
|
||||
optional string name = 1; // file name, relative to root of source tree
|
||||
optional string package = 2; // e.g. "foo", "foo.bar", etc.
|
||||
|
||||
// Names of files imported by this file.
|
||||
repeated string dependency = 3;
|
||||
@@ -100,8 +101,8 @@ message DescriptorProto {
|
||||
repeated EnumDescriptorProto enum_type = 4;
|
||||
|
||||
message ExtensionRange {
|
||||
optional int32 start = 1;
|
||||
optional int32 end = 2;
|
||||
optional int32 start = 1; // Inclusive.
|
||||
optional int32 end = 2; // Exclusive.
|
||||
|
||||
optional ExtensionRangeOptions options = 3;
|
||||
}
|
||||
@@ -115,8 +116,8 @@ message DescriptorProto {
|
||||
// fields or extension ranges in the same message. Reserved ranges may
|
||||
// not overlap.
|
||||
message ReservedRange {
|
||||
optional int32 start = 1; // Inclusive.
|
||||
optional int32 end = 2; // Exclusive.
|
||||
optional int32 start = 1; // Inclusive.
|
||||
optional int32 end = 2; // Exclusive.
|
||||
}
|
||||
repeated ReservedRange reserved_range = 9;
|
||||
// Reserved field names, which may not be used by fields in the same message.
|
||||
@@ -137,42 +138,42 @@ message FieldDescriptorProto {
|
||||
enum Type {
|
||||
// 0 is reserved for errors.
|
||||
// Order is weird for historical reasons.
|
||||
TYPE_DOUBLE = 1;
|
||||
TYPE_FLOAT = 2;
|
||||
TYPE_DOUBLE = 1;
|
||||
TYPE_FLOAT = 2;
|
||||
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
|
||||
// negative values are likely.
|
||||
TYPE_INT64 = 3;
|
||||
TYPE_UINT64 = 4;
|
||||
TYPE_INT64 = 3;
|
||||
TYPE_UINT64 = 4;
|
||||
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
|
||||
// negative values are likely.
|
||||
TYPE_INT32 = 5;
|
||||
TYPE_FIXED64 = 6;
|
||||
TYPE_FIXED32 = 7;
|
||||
TYPE_BOOL = 8;
|
||||
TYPE_STRING = 9;
|
||||
TYPE_INT32 = 5;
|
||||
TYPE_FIXED64 = 6;
|
||||
TYPE_FIXED32 = 7;
|
||||
TYPE_BOOL = 8;
|
||||
TYPE_STRING = 9;
|
||||
// Tag-delimited aggregate.
|
||||
// Group type is deprecated and not supported in proto3. However, Proto3
|
||||
// implementations should still be able to parse the group wire format and
|
||||
// treat group fields as unknown fields.
|
||||
TYPE_GROUP = 10;
|
||||
TYPE_MESSAGE = 11; // Length-delimited aggregate.
|
||||
TYPE_GROUP = 10;
|
||||
TYPE_MESSAGE = 11; // Length-delimited aggregate.
|
||||
|
||||
// New in version 2.
|
||||
TYPE_BYTES = 12;
|
||||
TYPE_UINT32 = 13;
|
||||
TYPE_ENUM = 14;
|
||||
TYPE_SFIXED32 = 15;
|
||||
TYPE_SFIXED64 = 16;
|
||||
TYPE_SINT32 = 17; // Uses ZigZag encoding.
|
||||
TYPE_SINT64 = 18; // Uses ZigZag encoding.
|
||||
};
|
||||
TYPE_BYTES = 12;
|
||||
TYPE_UINT32 = 13;
|
||||
TYPE_ENUM = 14;
|
||||
TYPE_SFIXED32 = 15;
|
||||
TYPE_SFIXED64 = 16;
|
||||
TYPE_SINT32 = 17; // Uses ZigZag encoding.
|
||||
TYPE_SINT64 = 18; // Uses ZigZag encoding.
|
||||
}
|
||||
|
||||
enum Label {
|
||||
// 0 is reserved for errors
|
||||
LABEL_OPTIONAL = 1;
|
||||
LABEL_REQUIRED = 2;
|
||||
LABEL_REPEATED = 3;
|
||||
};
|
||||
LABEL_OPTIONAL = 1;
|
||||
LABEL_REQUIRED = 2;
|
||||
LABEL_REPEATED = 3;
|
||||
}
|
||||
|
||||
optional string name = 1;
|
||||
optional int32 number = 3;
|
||||
@@ -234,8 +235,8 @@ message EnumDescriptorProto {
|
||||
// is inclusive such that it can appropriately represent the entire int32
|
||||
// domain.
|
||||
message EnumReservedRange {
|
||||
optional int32 start = 1; // Inclusive.
|
||||
optional int32 end = 2; // Inclusive.
|
||||
optional int32 start = 1; // Inclusive.
|
||||
optional int32 end = 2; // Inclusive.
|
||||
}
|
||||
|
||||
// Range of reserved numeric values. Reserved numeric values may not be used
|
||||
@@ -276,9 +277,9 @@ message MethodDescriptorProto {
|
||||
optional MethodOptions options = 4;
|
||||
|
||||
// Identifies if client streams multiple client messages
|
||||
optional bool client_streaming = 5 [default=false];
|
||||
optional bool client_streaming = 5 [default = false];
|
||||
// Identifies if server streams multiple server messages
|
||||
optional bool server_streaming = 6 [default=false];
|
||||
optional bool server_streaming = 6 [default = false];
|
||||
}
|
||||
|
||||
|
||||
@@ -314,7 +315,6 @@ message MethodDescriptorProto {
|
||||
// If this turns out to be popular, a web service will be set up
|
||||
// to automatically assign option numbers.
|
||||
|
||||
|
||||
message FileOptions {
|
||||
|
||||
// Sets the Java package where classes generated from this .proto will be
|
||||
@@ -337,7 +337,7 @@ message FileOptions {
|
||||
// named by java_outer_classname. However, the outer class will still be
|
||||
// generated to contain the file's getDescriptor() method as well as any
|
||||
// top-level extensions defined in the file.
|
||||
optional bool java_multiple_files = 10 [default=false];
|
||||
optional bool java_multiple_files = 10 [default = false];
|
||||
|
||||
// This option does nothing.
|
||||
optional bool java_generate_equals_and_hash = 20 [deprecated=true];
|
||||
@@ -348,17 +348,17 @@ message FileOptions {
|
||||
// Message reflection will do the same.
|
||||
// However, an extension field still accepts non-UTF-8 byte sequences.
|
||||
// This option has no effect on when used with the lite runtime.
|
||||
optional bool java_string_check_utf8 = 27 [default=false];
|
||||
optional bool java_string_check_utf8 = 27 [default = false];
|
||||
|
||||
|
||||
// Generated classes can be optimized for speed or code size.
|
||||
enum OptimizeMode {
|
||||
SPEED = 1; // Generate complete code for parsing, serialization,
|
||||
// etc.
|
||||
CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
|
||||
LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
|
||||
SPEED = 1; // Generate complete code for parsing, serialization,
|
||||
// etc.
|
||||
CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
|
||||
LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
|
||||
}
|
||||
optional OptimizeMode optimize_for = 9 [default=SPEED];
|
||||
optional OptimizeMode optimize_for = 9 [default = SPEED];
|
||||
|
||||
// Sets the Go package where structs generated from this .proto will be
|
||||
// placed. If omitted, the Go package will be derived from the following:
|
||||
@@ -369,6 +369,7 @@ message FileOptions {
|
||||
|
||||
|
||||
|
||||
|
||||
// Should generic services be generated in each language? "Generic" services
|
||||
// are not specific to any particular RPC system. They are generated by the
|
||||
// main code generators in each language (without additional plugins).
|
||||
@@ -379,20 +380,20 @@ message FileOptions {
|
||||
// that generate code specific to your particular RPC system. Therefore,
|
||||
// these default to false. Old code which depends on generic services should
|
||||
// explicitly set them to true.
|
||||
optional bool cc_generic_services = 16 [default=false];
|
||||
optional bool java_generic_services = 17 [default=false];
|
||||
optional bool py_generic_services = 18 [default=false];
|
||||
optional bool php_generic_services = 42 [default=false];
|
||||
optional bool cc_generic_services = 16 [default = false];
|
||||
optional bool java_generic_services = 17 [default = false];
|
||||
optional bool py_generic_services = 18 [default = false];
|
||||
optional bool php_generic_services = 42 [default = false];
|
||||
|
||||
// Is this file deprecated?
|
||||
// Depending on the target platform, this can emit Deprecated annotations
|
||||
// for everything in the file, or it will be completely ignored; in the very
|
||||
// least, this is a formalization for deprecating files.
|
||||
optional bool deprecated = 23 [default=false];
|
||||
optional bool deprecated = 23 [default = false];
|
||||
|
||||
// Enables the use of arenas for the proto messages in this file. This applies
|
||||
// only to generated classes for C++.
|
||||
optional bool cc_enable_arenas = 31 [default=false];
|
||||
optional bool cc_enable_arenas = 31 [default = false];
|
||||
|
||||
|
||||
// Sets the objective c class prefix which is prepended to all objective c
|
||||
@@ -417,10 +418,9 @@ message FileOptions {
|
||||
// determining the namespace.
|
||||
optional string php_namespace = 41;
|
||||
|
||||
|
||||
// Use this option to change the namespace of php generated metadata classes.
|
||||
// Default is empty. When this option is empty, the proto file name will be used
|
||||
// for determining the namespace.
|
||||
// Default is empty. When this option is empty, the proto file name will be
|
||||
// used for determining the namespace.
|
||||
optional string php_metadata_namespace = 44;
|
||||
|
||||
// Use this option to change the package of ruby generated classes. Default
|
||||
@@ -428,6 +428,7 @@ message FileOptions {
|
||||
// determining the ruby package.
|
||||
optional string ruby_package = 45;
|
||||
|
||||
|
||||
// The parser stores options it doesn't recognize here.
|
||||
// See the documentation for the "Options" section above.
|
||||
repeated UninterpretedOption uninterpreted_option = 999;
|
||||
@@ -458,18 +459,18 @@ message MessageOptions {
|
||||
//
|
||||
// Because this is an option, the above two restrictions are not enforced by
|
||||
// the protocol compiler.
|
||||
optional bool message_set_wire_format = 1 [default=false];
|
||||
optional bool message_set_wire_format = 1 [default = false];
|
||||
|
||||
// Disables the generation of the standard "descriptor()" accessor, which can
|
||||
// conflict with a field of the same name. This is meant to make migration
|
||||
// from proto1 easier; new code should avoid fields named "descriptor".
|
||||
optional bool no_standard_descriptor_accessor = 2 [default=false];
|
||||
optional bool no_standard_descriptor_accessor = 2 [default = false];
|
||||
|
||||
// Is this message deprecated?
|
||||
// Depending on the target platform, this can emit Deprecated annotations
|
||||
// for the message, or it will be completely ignored; in the very least,
|
||||
// this is a formalization for deprecating messages.
|
||||
optional bool deprecated = 3 [default=false];
|
||||
optional bool deprecated = 3 [default = false];
|
||||
|
||||
// Whether the message is an automatically generated map entry type for the
|
||||
// maps field.
|
||||
@@ -486,7 +487,7 @@ message MessageOptions {
|
||||
//
|
||||
// Implementations may choose not to generate the map_entry=true message, but
|
||||
// use a native map in the target language to hold the keys and values.
|
||||
// The reflection APIs in such implementions still need to work as
|
||||
// The reflection APIs in such implementations still need to work as
|
||||
// if the field is a repeated message field.
|
||||
//
|
||||
// NOTE: Do not set the option in .proto files. Always use the maps syntax
|
||||
@@ -497,6 +498,7 @@ message MessageOptions {
|
||||
reserved 8; // javalite_serializable
|
||||
reserved 9; // javanano_as_lite
|
||||
|
||||
|
||||
// The parser stores options it doesn't recognize here. See above.
|
||||
repeated UninterpretedOption uninterpreted_option = 999;
|
||||
|
||||
@@ -576,16 +578,16 @@ message FieldOptions {
|
||||
// implementation must either *always* check its required fields, or *never*
|
||||
// check its required fields, regardless of whether or not the message has
|
||||
// been parsed.
|
||||
optional bool lazy = 5 [default=false];
|
||||
optional bool lazy = 5 [default = false];
|
||||
|
||||
// Is this field deprecated?
|
||||
// Depending on the target platform, this can emit Deprecated annotations
|
||||
// for accessors, or it will be completely ignored; in the very least, this
|
||||
// is a formalization for deprecating fields.
|
||||
optional bool deprecated = 3 [default=false];
|
||||
optional bool deprecated = 3 [default = false];
|
||||
|
||||
// For Google-internal migration only. Do not use.
|
||||
optional bool weak = 10 [default=false];
|
||||
optional bool weak = 10 [default = false];
|
||||
|
||||
|
||||
// The parser stores options it doesn't recognize here. See above.
|
||||
@@ -615,7 +617,7 @@ message EnumOptions {
|
||||
// Depending on the target platform, this can emit Deprecated annotations
|
||||
// for the enum, or it will be completely ignored; in the very least, this
|
||||
// is a formalization for deprecating enums.
|
||||
optional bool deprecated = 3 [default=false];
|
||||
optional bool deprecated = 3 [default = false];
|
||||
|
||||
reserved 5; // javanano_as_lite
|
||||
|
||||
@@ -631,7 +633,7 @@ message EnumValueOptions {
|
||||
// Depending on the target platform, this can emit Deprecated annotations
|
||||
// for the enum value, or it will be completely ignored; in the very least,
|
||||
// this is a formalization for deprecating enum values.
|
||||
optional bool deprecated = 1 [default=false];
|
||||
optional bool deprecated = 1 [default = false];
|
||||
|
||||
// The parser stores options it doesn't recognize here. See above.
|
||||
repeated UninterpretedOption uninterpreted_option = 999;
|
||||
@@ -651,7 +653,7 @@ message ServiceOptions {
|
||||
// Depending on the target platform, this can emit Deprecated annotations
|
||||
// for the service, or it will be completely ignored; in the very least,
|
||||
// this is a formalization for deprecating services.
|
||||
optional bool deprecated = 33 [default=false];
|
||||
optional bool deprecated = 33 [default = false];
|
||||
|
||||
// The parser stores options it doesn't recognize here. See above.
|
||||
repeated UninterpretedOption uninterpreted_option = 999;
|
||||
@@ -671,18 +673,18 @@ message MethodOptions {
|
||||
// Depending on the target platform, this can emit Deprecated annotations
|
||||
// for the method, or it will be completely ignored; in the very least,
|
||||
// this is a formalization for deprecating methods.
|
||||
optional bool deprecated = 33 [default=false];
|
||||
optional bool deprecated = 33 [default = false];
|
||||
|
||||
// Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
|
||||
// or neither? HTTP based RPC implementation may choose GET verb for safe
|
||||
// methods, and PUT verb for idempotent methods instead of the default POST.
|
||||
enum IdempotencyLevel {
|
||||
IDEMPOTENCY_UNKNOWN = 0;
|
||||
NO_SIDE_EFFECTS = 1; // implies idempotent
|
||||
IDEMPOTENT = 2; // idempotent, but may have side effects
|
||||
NO_SIDE_EFFECTS = 1; // implies idempotent
|
||||
IDEMPOTENT = 2; // idempotent, but may have side effects
|
||||
}
|
||||
optional IdempotencyLevel idempotency_level =
|
||||
34 [default=IDEMPOTENCY_UNKNOWN];
|
||||
optional IdempotencyLevel idempotency_level = 34
|
||||
[default = IDEMPOTENCY_UNKNOWN];
|
||||
|
||||
// The parser stores options it doesn't recognize here. See above.
|
||||
repeated UninterpretedOption uninterpreted_option = 999;
|
||||
@@ -763,7 +765,7 @@ message SourceCodeInfo {
|
||||
// beginning of the "extend" block and is shared by all extensions within
|
||||
// the block.
|
||||
// - Just because a location's span is a subset of some other location's span
|
||||
// does not mean that it is a descendent. For example, a "group" defines
|
||||
// does not mean that it is a descendant. For example, a "group" defines
|
||||
// both a type and a field in a single declaration. Thus, the locations
|
||||
// corresponding to the type and field and their components will overlap.
|
||||
// - Code which tries to interpret locations should probably be designed to
|
||||
@@ -794,14 +796,14 @@ message SourceCodeInfo {
|
||||
// [ 4, 3, 2, 7 ]
|
||||
// this path refers to the whole field declaration (from the beginning
|
||||
// of the label to the terminating semicolon).
|
||||
repeated int32 path = 1 [packed=true];
|
||||
repeated int32 path = 1 [packed = true];
|
||||
|
||||
// Always has exactly three or four elements: start line, start column,
|
||||
// end line (optional, otherwise assumed same as start line), end column.
|
||||
// These are packed into a single field for efficiency. Note that line
|
||||
// and column numbers are zero-based -- typically you will want to add
|
||||
// 1 to each before displaying to a user.
|
||||
repeated int32 span = 2 [packed=true];
|
||||
repeated int32 span = 2 [packed = true];
|
||||
|
||||
// If this SourceCodeInfo represents a complete declaration, these are any
|
||||
// comments appearing before and after the declaration which appear to be
|
||||
@@ -866,7 +868,7 @@ message GeneratedCodeInfo {
|
||||
message Annotation {
|
||||
// Identifies the element in the original source .proto file. This field
|
||||
// is formatted the same as SourceCodeInfo.Location.path.
|
||||
repeated int32 path = 1 [packed=true];
|
||||
repeated int32 path = 1 [packed = true];
|
||||
|
||||
// Identifies the filesystem path to the original source .proto.
|
||||
optional string source_file = 2;
|
||||
|
||||
6
vendor/github.com/golang/protobuf/protoc-gen-go/generator/generator.go
generated
vendored
6
vendor/github.com/golang/protobuf/protoc-gen-go/generator/generator.go
generated
vendored
@@ -2264,7 +2264,7 @@ func (g *Generator) generateMessage(message *Descriptor) {
|
||||
of := oneofField{
|
||||
fieldCommon: fieldCommon{
|
||||
goName: fname,
|
||||
getterName: "Get"+fname,
|
||||
getterName: "Get" + fname,
|
||||
goType: dname,
|
||||
tags: tag,
|
||||
protoName: odp.GetName(),
|
||||
@@ -2609,7 +2609,9 @@ func (g *Generator) generateFileDescriptor(file *FileDescriptor) {
|
||||
|
||||
v := file.VarName()
|
||||
g.P()
|
||||
g.P("func init() { ", g.Pkg["proto"], ".RegisterFile(", strconv.Quote(*file.Name), ", ", v, ") }")
|
||||
g.P("func init() {")
|
||||
g.P(g.Pkg["proto"], ".RegisterFile(", strconv.Quote(*file.Name), ", ", v, ")")
|
||||
g.P("}")
|
||||
g.P("var ", v, " = []byte{")
|
||||
g.P("// ", len(b), " bytes of a gzipped FileDescriptorProto")
|
||||
for len(b) > 0 {
|
||||
|
||||
7
vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
generated
vendored
7
vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
generated
vendored
@@ -102,7 +102,8 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
//
|
||||
type Any struct {
|
||||
// A URL/resource name that uniquely identifies the type of the serialized
|
||||
// protocol buffer message. The last segment of the URL's path must represent
|
||||
// protocol buffer message. This string must contain at least
|
||||
// one "/" character. The last segment of the URL's path must represent
|
||||
// the fully qualified name of the type (as in
|
||||
// `path/google.protobuf.Duration`). The name should be in a canonical form
|
||||
// (e.g., leading "." is not accepted).
|
||||
@@ -181,7 +182,9 @@ func init() {
|
||||
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4) }
|
||||
func init() {
|
||||
proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4)
|
||||
}
|
||||
|
||||
var fileDescriptor_b53526c13ae22eb4 = []byte{
|
||||
// 185 bytes of a gzipped FileDescriptorProto
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user