mirror of
https://github.com/ccfos/nightingale.git
synced 2026-03-03 14:38:55 +00:00
Compare commits
42 Commits
report_sam
...
notify_plu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0003b33e99 | ||
|
|
c45cbd02cc | ||
|
|
04cb501ab4 | ||
|
|
ba6f089c78 | ||
|
|
ab0cb6fc47 | ||
|
|
2847a315b1 | ||
|
|
65439df7fb | ||
|
|
b6436b09ce | ||
|
|
92354d5765 | ||
|
|
05651ad744 | ||
|
|
b7ff82d722 | ||
|
|
a285966560 | ||
|
|
538880b0e0 | ||
|
|
299270f74e | ||
|
|
9c69362650 | ||
|
|
d508aef7e5 | ||
|
|
616674b643 | ||
|
|
94847d9059 | ||
|
|
cbd416495c | ||
|
|
cc32194fb6 | ||
|
|
f5e2b43526 | ||
|
|
5bc8f0b9b1 | ||
|
|
7359a69223 | ||
|
|
04d64d09d7 | ||
|
|
43343182e4 | ||
|
|
072ab98fcf | ||
|
|
35ef6b9265 | ||
|
|
eaa53f2533 | ||
|
|
de322c4daf | ||
|
|
936c751a93 | ||
|
|
796a7014a1 | ||
|
|
f4368302ea | ||
|
|
01e611a9f9 | ||
|
|
315e0ef903 | ||
|
|
98d5dfff8e | ||
|
|
6b4705608b | ||
|
|
5907817cba | ||
|
|
aa97ac54d1 | ||
|
|
8fe548aba9 | ||
|
|
18a9288b75 | ||
|
|
fe82886f09 | ||
|
|
32e6993eea |
44
.github/workflows/n9e.yml
vendored
44
.github/workflows/n9e.yml
vendored
@@ -1,26 +1,32 @@
|
||||
name: Go
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
tags:
|
||||
- 'v*'
|
||||
env:
|
||||
GO_VERSION: 1.18
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Build
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.17
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build
|
||||
run: make
|
||||
- name: Checkout Source Code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go Environment
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
88
.goreleaser.yaml
Normal file
88
.goreleaser.yaml
Normal file
@@ -0,0 +1,88 @@
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
- go mod tidy
|
||||
|
||||
snapshot:
|
||||
name_template: '{{ .Tag }}'
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
changelog:
|
||||
skip: true
|
||||
|
||||
builds:
|
||||
- id: build
|
||||
hooks:
|
||||
pre:
|
||||
- ./fe.sh
|
||||
main: ./src/
|
||||
binary: n9e
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/didi/nightingale/v5/src/pkg/version.VERSION={{ .Tag }}-{{.Commit}}
|
||||
|
||||
archives:
|
||||
- id: n9e
|
||||
builds:
|
||||
- build
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
name_template: "n9e-v{{ .Version }}-{{ .Os }}-{{ .Arch }}"
|
||||
wrap_in_directory: false
|
||||
files:
|
||||
- docker/*
|
||||
- etc/*
|
||||
- pub/*
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: ccfos
|
||||
name: nightingale
|
||||
name_template: "v{{ .Version }}"
|
||||
|
||||
dockers:
|
||||
- image_templates:
|
||||
- flashcatcloud/nightingale:{{ .Version }}-amd64
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
ids:
|
||||
- build
|
||||
dockerfile: docker/Dockerfile.goreleaser
|
||||
extra_files:
|
||||
- pub
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- image_templates:
|
||||
- flashcatcloud/nightingale:{{ .Version }}-arm64v8
|
||||
goos: linux
|
||||
goarch: arm64
|
||||
ids:
|
||||
- build
|
||||
dockerfile: docker/Dockerfile.goreleaser
|
||||
extra_files:
|
||||
- pub
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64/v8"
|
||||
|
||||
docker_manifests:
|
||||
|
||||
- name_template: flashcatcloud/nightingale:{{ .Version }}
|
||||
image_templates:
|
||||
- flashcatcloud/nightingale:{{ .Version }}-amd64
|
||||
- flashcatcloud/nightingale:{{ .Version }}-arm64v8
|
||||
|
||||
- name_template: flashcatcloud/nightingale:latest
|
||||
image_templates:
|
||||
- flashcatcloud/nightingale:{{ .Version }}-amd64
|
||||
- flashcatcloud/nightingale:{{ .Version }}-arm64v8
|
||||
10
Makefile
10
Makefile
@@ -2,10 +2,15 @@
|
||||
|
||||
NOW = $(shell date -u '+%Y%m%d%I%M%S')
|
||||
|
||||
RELEASE_VERSION = 5.9.4
|
||||
|
||||
APP = n9e
|
||||
SERVER_BIN = $(APP)
|
||||
ROOT:=$(shell pwd -P)
|
||||
GIT_COMMIT:=$(shell git --work-tree ${ROOT} rev-parse 'HEAD^{commit}')
|
||||
_GIT_VERSION:=$(shell git --work-tree ${ROOT} describe --tags --abbrev=14 "${GIT_COMMIT}^{commit}" 2>/dev/null)
|
||||
TAG=$(shell echo "${_GIT_VERSION}" | awk -F"-" '{print $$1}')
|
||||
RELEASE_VERSION:="$(TAG)-$(GIT_COMMIT)"
|
||||
|
||||
# RELEASE_ROOT = release
|
||||
# RELEASE_SERVER = release/${APP}
|
||||
# GIT_COUNT = $(shell git rev-list --all --count)
|
||||
@@ -17,6 +22,9 @@ all: build
|
||||
build:
|
||||
go build -ldflags "-w -s -X github.com/didi/nightingale/v5/src/pkg/version.VERSION=$(RELEASE_VERSION)" -o $(SERVER_BIN) ./src
|
||||
|
||||
build-linux:
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "-w -s -X github.com/didi/nightingale/v5/src/pkg/version.VERSION=$(RELEASE_VERSION)" -o $(SERVER_BIN) ./src
|
||||
|
||||
# start:
|
||||
# @go run -ldflags "-X main.VERSION=$(RELEASE_TAG)" ./cmd/${APP}/main.go web -c ./configs/config.toml -m ./configs/model.conf --menu ./configs/menu.yaml
|
||||
run_webapi:
|
||||
|
||||
118
README.md
118
README.md
@@ -1,66 +1,78 @@
|
||||
<img src="doc/img/ccf-n9e.png" width="240">
|
||||
<p align="center">
|
||||
<a href="https://github.com/ccfos/nightingale">
|
||||
<img src="doc/img/ccf-n9e.png" alt="nightingale - cloud native monitoring" width="240" /></a>
|
||||
<p align="center">夜莺是一款开源的云原生监控系统,采用 all-in-one 的设计,提供企业级的功能特性,开箱即用的产品体验。推荐升级您的 Prometheus + AlertManager + Grafana 组合方案到夜莺</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img alt="GitHub latest release" src="https://img.shields.io/github/v/release/ccfos/nightingale"/>
|
||||
<a href="https://n9e.github.io">
|
||||
<img alt="Docs" src="https://img.shields.io/badge/docs-get%20started-brightgreen"/></a>
|
||||
<a href="https://hub.docker.com/u/flashcatcloud">
|
||||
<img alt="Docker pulls" src="https://img.shields.io/docker/pulls/flashcatcloud/nightingale"/></a>
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ccfos/nightingale">
|
||||
<img alt="GitHub forks" src="https://img.shields.io/github/forks/ccfos/nightingale">
|
||||
<a href="https://github.com/ccfos/nightingale/graphs/contributors">
|
||||
<img alt="GitHub contributors" src="https://img.shields.io/github/contributors-anon/ccfos/nightingale"/></a>
|
||||
<img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-blue"/>
|
||||
</p>
|
||||
|
||||
[English](./README_EN.md) | [中文](./README.md)
|
||||
|
||||
> 夜莺是一款开源的云原生监控系统,采用 All-In-One 的设计,提供企业级的功能特性,开箱即用的产品体验。推荐升级您的 Prometheus + AlertManager + Grafana 组合方案到夜莺。
|
||||
## Highlighted Features
|
||||
|
||||
**夜莺监控具有以下特点:**
|
||||
- **开箱即用**
|
||||
- 支持 Docker、Helm Chart 等多种部署方式,内置多种监控大盘、快捷视图、告警规则模板,导入即可快速使用,活跃、专业的社区用户也在持续迭代和沉淀更多的最佳实践于产品中;
|
||||
- **兼容并包**
|
||||
- 支持 [Categraf](https://github.com/flashcatcloud/categraf)、Telegraf、Grafana-agent 等多种采集器,支持 Prometheus、VictoriaMetrics、M3DB 等各种时序数据库,支持对接 Grafana,与云原生生态无缝集成;
|
||||
- 集数据采集、可视化、监控告警、数据分析于一体,与云原生生态紧密集成,提供开箱即用的企业级监控分析和告警能力;
|
||||
- **开放社区**
|
||||
- 托管于[中国计算机学会开源发展委员会](https://www.ccf.org.cn/kyfzwyh/),有[快猫星云](https://flashcat.cloud)的持续投入,和数千名社区用户的积极参与,以及夜莺监控项目清晰明确的定位,都保证了夜莺开源社区健康、长久的发展;
|
||||
- **高性能**
|
||||
- 得益于夜莺的多数据源管理引擎,和夜莺引擎侧优秀的架构设计,借助于高性能时序库,可以满足数亿时间线的采集、存储、告警分析场景,节省大量成本;
|
||||
- **高可用**
|
||||
- 夜莺监控组件均可水平扩展,无单点,已在上千家企业部署落地,经受了严苛的生产实践检验。众多互联网头部公司,夜莺集群机器达百台,处理十亿级时间线,重度使用夜莺监控;
|
||||
- **灵活扩展**
|
||||
- 夜莺监控,可部署在1核1G的云主机,可在上百台机器部署集群,可运行在K8s中;也可将时序库、告警引擎等组件下沉到各机房、各region,兼顾边缘部署和中心化管理;
|
||||
|
||||
#### 1. 开箱即用
|
||||
支持 Docker、Helm Chart 等多种部署方式,内置多种监控大盘、快捷视图、告警规则模板,导入即可快速使用,活跃、专业的社区用户也在持续迭代和沉淀更多的最佳实践于产品中;
|
||||
|
||||
#### 2. 兼容并包
|
||||
支持 [Categraf](https://github.com/flashcatcloud/categraf)、Telegraf、Grafana-agent 等多种采集器,支持 Prometheus、VictoriaMetrics、M3DB 等各种时序数据库,支持对接 Grafana,与云原生生态无缝集成;
|
||||
|
||||
#### 3. 开放社区
|
||||
托管于[中国计算机学会开源发展委员会](https://www.ccf.org.cn/kyfzwyh/),有[快猫星云](https://flashcat.cloud)的持续投入,和数千名社区用户的积极参与,以及夜莺监控项目清晰明确的定位,都保证了夜莺开源社区健康、长久的发展;
|
||||
|
||||
#### 4. 高性能
|
||||
得益于夜莺的多数据源管理引擎,和夜莺引擎侧优秀的架构设计,借助于高性能时序库,可以满足数亿时间线的采集、存储、告警分析场景,节省大量成本;
|
||||
|
||||
#### 5. 高可用
|
||||
夜莺监控组件均可水平扩展,无单点,已在上千家企业部署落地,经受了严苛的生产实践检验。众多互联网头部公司,夜莺集群机器达百台,处理十亿级时间线,重度使用夜莺监控;
|
||||
|
||||
#### 6. 灵活扩展
|
||||
夜莺监控,可部署在1核1G的云主机,可在上百台机器部署集群,可运行在K8s中;也可将时序库、告警引擎等组件下沉到各机房、各region,兼顾边缘部署和中心化管理;
|
||||
|
||||
|
||||
#### 如果您在使用 Prometheus 过程中,有以下的一个或者多个需求场景,推荐您升级到夜莺:
|
||||
> 如果您在使用 Prometheus 过程中,有以下的一个或者多个需求场景,推荐您升级到夜莺:
|
||||
|
||||
- Prometheus、Alertmanager、Grafana 等多个系统较为割裂,缺乏统一视图,无法开箱即用;
|
||||
- 通过修改配置文件来管理 Prometheus、Alertmanager 的方式,学习曲线大,协同有难度;
|
||||
- 数据量过大而无法扩展您的 Prometheus 集群;
|
||||
- 生产环境运行多套 Prometheus 集群,面临管理和使用成本高的问题;
|
||||
|
||||
#### 如果您在使用 Zabbix,有以下的场景,推荐您升级到夜莺:
|
||||
> 如果您在使用 Zabbix,有以下的场景,推荐您升级到夜莺:
|
||||
|
||||
- 监控的数据量太大,希望有更好的扩展解决方案;
|
||||
- 学习曲线高,多人多团队模式下,希望有更好的协同使用效率;
|
||||
- 微服务和云原生架构下,监控数据的生命周期多变、监控数据维度基数高,Zabbix 数据模型不易适配;
|
||||
|
||||
#### 如果您在使用 [open-falcon](https://github.com/open-falcon/falcon-plus),我们更推荐您升级到夜莺:
|
||||
- 关于open-falcon和夜莺的详细介绍,请参考阅读[《云原生监控的十个特点和趋势》](https://mp.weixin.qq.com/s?__biz=MzkzNjI5OTM5Nw==&mid=2247483738&idx=1&sn=e8bdbb974a2cd003c1abcc2b5405dd18&chksm=c2a19fb0f5d616a63185cd79277a79a6b80118ef2185890d0683d2bb20451bd9303c78d083c5#rd)。
|
||||
> 如果您在使用 [Open-Falcon](https://github.com/open-falcon/falcon-plus),我们更推荐您升级到夜莺:
|
||||
|
||||
#### 我们推荐您使用 [Categraf](https://github.com/flashcatcloud/categraf) 作为首选的监控数据采集器:
|
||||
- Categraf 是夜莺监控的默认采集器,采用开放插件机制和 all-in-one 的设计,同时支持 metric、log、trace、event 的采集。Categraf 不仅可以采集 CPU、内存、网络等系统层面的指标,也集成了众多开源组件的采集能力,支持K8s生态。Categraf 内置了对应的仪表盘和告警规则,开箱即用。
|
||||
- 关于 Open-Falcon 和夜莺的详细介绍,请参考阅读[《云原生监控的十个特点和趋势》](https://mp.weixin.qq.com/s?__biz=MzkzNjI5OTM5Nw==&mid=2247483738&idx=1&sn=e8bdbb974a2cd003c1abcc2b5405dd18&chksm=c2a19fb0f5d616a63185cd79277a79a6b80118ef2185890d0683d2bb20451bd9303c78d083c5#rd)。
|
||||
|
||||
> 我们推荐您使用 [Categraf](https://github.com/flashcatcloud/categraf) 作为首选的监控数据采集器:
|
||||
|
||||
- [Categraf](https://github.com/flashcatcloud/categraf) 是夜莺监控的默认采集器,采用开放插件机制和 all-in-one 的设计,同时支持 metric、log、trace、event 的采集。Categraf 不仅可以采集 CPU、内存、网络等系统层面的指标,也集成了众多开源组件的采集能力,支持K8s生态。Categraf 内置了对应的仪表盘和告警规则,开箱即用。
|
||||
|
||||
|
||||
## 资料链接
|
||||
## Getting Started
|
||||
|
||||
- [快速安装](https://mp.weixin.qq.com/s/iEC4pfL1TgjMDOWYh8H-FA)
|
||||
- [详细文档](https://n9e.github.io/)
|
||||
- [社区分享](https://n9e.github.io/docs/prologue/share/)
|
||||
|
||||
## 产品演示
|
||||
## Screenshots
|
||||
|
||||
<img src="doc/img/intro.gif" width="680">
|
||||
|
||||
## 架构介绍
|
||||
|
||||
## Architecture
|
||||
|
||||
<img src="doc/img/arch-product.png" width="680">
|
||||
|
||||
Nightingale 可以接收各种采集器上报的监控数据(比如 [Categraf](https://github.com/flashcatcloud/categraf)、telegraf、grafana-agent、Prometheus),并写入多种流行的时序数据库中(可以支持Prometheus、M3DB、VictoriaMetrics、Thanos、TDEngine等),提供告警规则、屏蔽规则、订阅规则的配置能力,提供监控数据的查看能力,提供告警自愈机制(告警触发之后自动回调某个webhook地址或者执行某个脚本),提供历史告警事件的存储管理、分组查看的能力。
|
||||
|
||||
夜莺监控可以接收各种采集器上报的监控数据(比如 [Categraf](https://github.com/flashcatcloud/categraf)、telegraf、grafana-agent、Prometheus),并写入多种流行的时序数据库中(可以支持Prometheus、M3DB、VictoriaMetrics、Thanos、TDEngine等),提供告警规则、屏蔽规则、订阅规则的配置能力,提供监控数据的查看能力,提供告警自愈机制(告警触发之后自动回调某个webhook地址或者执行某个脚本),提供历史告警事件的存储管理、分组查看的能力。
|
||||
|
||||
<img src="doc/img/arch-system.png" width="680">
|
||||
|
||||
@@ -72,31 +84,39 @@ Nightingale 可以接收各种采集器上报的监控数据(比如 [Categraf]
|
||||
如果单机版本的 Prometheus 性能不够或容灾较差,我们推荐使用 [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics),VictoriaMetrics 架构较为简单,性能优异,易于部署和运维,架构图如上。VictoriaMetrics 更详尽的文档,还请参考其[官网](https://victoriametrics.com/)。
|
||||
|
||||
|
||||
## 如何参与
|
||||
## Community
|
||||
|
||||
开源项目要更有生命力,离不开开放的治理架构和源源不断的开发者和用户共同参与,我们致力于建立开放、中立的开源治理架构,吸纳更多来自企业、高校等各方面对云原生监控感兴趣、有热情的计算机专业人士,打造专业、有活力的开发者社区。关于《夜莺开源项目和社区治理架构(草案)》,请查阅 [doc/community-governance.md](./doc/community-governance.md).
|
||||
开源项目要更有生命力,离不开开放的治理架构和源源不断的开发者和用户共同参与,我们致力于建立开放、中立的开源治理架构,吸纳更多来自企业、高校等各方面对云原生监控感兴趣、有热情的开发者,一起打造有活力的夜莺开源社区。关于《夜莺开源项目和社区治理架构(草案)》,请查阅 **[COMMUNITY GOVERNANCE](./doc/community-governance.md)**.
|
||||
|
||||
**我们欢迎您以各种方式参与到夜莺开源项目和开源社区中来,工作包括不限于**:
|
||||
- 补充和完善文档 => [n9e.github.io](https://n9e.github.io/);
|
||||
- 分享您在使用夜莺监控过程中的最佳实践和经验心得 => [文章分享](https://n9e.github.io/docs/prologue/share/);
|
||||
- 提交产品建议 =》 [github issue](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Ffeature&template=enhancement.md);
|
||||
- 提交代码,让夜莺监控更快、更稳、更好用 => [github pull request](https://github.com/didi/nightingale/pulls);
|
||||
|
||||
- 补充和完善文档 => [n9e.github.io](https://n9e.github.io/)
|
||||
- 分享您在使用夜莺监控过程中的最佳实践和经验心得 => [文章分享](https://n9e.github.io/docs/prologue/share/)
|
||||
- 提交产品建议 =》 [github issue](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Ffeature&template=enhancement.md)
|
||||
- 提交代码,让夜莺监控更快、更稳、更好用 => [github pull request](https://github.com/didi/nightingale/pulls)
|
||||
|
||||
**尊重、认可和记录每一位贡献者的工作**是夜莺开源社区的第一指导原则,我们提倡**高效的提问**,这既是对开发者时间的尊重,也是对整个社区知识沉淀的贡献:
|
||||
1. 提问之前请先查阅 [FAQ](https://www.gitlink.org.cn/ccfos/nightingale/wiki/faq) ;
|
||||
2. 提问之前请先搜索 [github issue](https://github.com/ccfos/nightingale/issues);
|
||||
3. 我们优先推荐通过提交 github issue 来提问,如果[有问题点击这里](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Fbug&template=bug_report.yml) | [有需求建议点击这里](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Ffeature&template=enhancement.md);
|
||||
4. 最后,我们推荐你加入微信群,针对相关开放式问题,相互交流咨询 (请先加好友:[UlricGO](https://www.gitlink.org.cn/UlricQin/gist/tree/master/self.jpeg) 备注:夜莺加群+姓名+公司,交流群里会有开发者团队和专业、热心的群友回答问题);
|
||||
- 提问之前请先查阅 [FAQ](https://www.gitlink.org.cn/ccfos/nightingale/wiki/faq)
|
||||
- 提问之前请先搜索 [github issue](https://github.com/ccfos/nightingale/issues)
|
||||
- 我们优先推荐通过提交 github issue 来提问,如果[有问题点击这里](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Fbug&template=bug_report.yml) | [有需求建议点击这里](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Ffeature&template=enhancement.md)
|
||||
- 最后,我们推荐你加入微信群,针对相关开放式问题,相互交流咨询 (请先加好友:[UlricGO](https://www.gitlink.org.cn/UlricQin/gist/tree/master/self.jpeg) 备注:夜莺加群+姓名+公司,交流群里会有开发者团队和专业、热心的群友回答问题)
|
||||
|
||||
|
||||
## 联系我们
|
||||
- 推荐您关注夜莺监控公众号,及时获取相关产品和社区动态
|
||||
## Who is using
|
||||
|
||||
<img src="doc/img/n9e-vx-new.png" width="180">
|
||||
您可以通过在 **[Who is Using Nightingale](https://github.com/ccfos/nightingale/issues/897)** 登记您的使用情况,分享您的使用经验。
|
||||
|
||||
## Stargazers over time
|
||||
## Stargazers
|
||||
[](https://starchart.cc/ccfos/nightingale)
|
||||
|
||||
## Contributors
|
||||
<a href="https://github.com/ccfos/nightingale/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=ccfos/nightingale" />
|
||||
</a>
|
||||
|
||||
## License
|
||||
- [Apache License V2.0](https://github.com/didi/nightingale/blob/main/LICENSE)
|
||||
[Apache License V2.0](https://github.com/didi/nightingale/blob/main/LICENSE)
|
||||
|
||||
## Contact Us
|
||||
推荐您关注夜莺监控公众号,及时获取相关产品和社区动态:
|
||||
|
||||
<img src="doc/img/n9e-vx-new.png" width="180">
|
||||
0
doc/active-contributors.md
Normal file
0
doc/active-contributors.md
Normal file
0
doc/committers.md
Normal file
0
doc/committers.md
Normal file
@@ -1,52 +1,74 @@
|
||||
# 夜莺开源项目和社区治理架构(草案)
|
||||
|
||||
#### 用户(User)
|
||||
## 社区架构
|
||||
|
||||
>欢迎任何个人、公司以及组织,使用 Nightingale,并积极的反馈 bug、提交功能需求、以及相互帮助,我们推荐使用 github issue 来跟踪 bug 和管理需求。
|
||||
### 用户(User)
|
||||
|
||||
#### 贡献者(Contributer)
|
||||
> 欢迎任何个人、公司以及组织,使用夜莺监控,并积极的反馈 bug、提交功能需求、以及相互帮助,我们推荐使用 [github issue](https://github.com/ccfos/nightingale/issues) 来跟踪 bug 和管理需求。
|
||||
|
||||
>欢迎每一位用户,包括但不限于以下列方式参与到 Nightingale 开源项目并做出贡献:
|
||||
>1. 在 [github issue](https://github.com/ccfos/nightingale/issues) 中积极参与讨论;
|
||||
>2. 提交代码补丁;
|
||||
>3. 修订、补充和完善文档;
|
||||
>4. 提交建议 / 批评;
|
||||
社区用户,可以通过在 **[Who is Using Nightingale](https://github.com/ccfos/nightingale/issues/897)** 登记您的使用情况,并分享您使用夜莺监控的经验,将会自动进入 **[END USERS](./end-users.md)** 列表,并获得社区的 **VIP Support**。
|
||||
|
||||
#### 提交者(Committer)
|
||||
### 贡献者(Contributer)
|
||||
|
||||
>Committer 是指拥有 Nightingale 代码仓库写操作权限的贡献者,而且他们也签署了 Nightingale 项目贡献者许可协议(CLA),他们拥有 ccf.org.cn 为后缀的邮箱地址。原则上 Committer 能够自主决策某个代码补丁是否可以合入到 Nightingale 代码仓库,但是项目管委会拥有最终的决策权。
|
||||
> 欢迎每一位用户,包括但不限于以下列方式参与到夜莺开源社区并做出贡献:
|
||||
|
||||
#### 项目管委会成员(PMC Member)
|
||||
1. 在 [github issue](https://github.com/ccfos/nightingale/issues) 中积极参与讨论,参与社区活动;
|
||||
1. 提交代码补丁;
|
||||
1. 翻译、修订、补充和完善[文档](https://n9e.github.io);
|
||||
1. 分享夜莺监控的使用经验,积极布道;
|
||||
1. 提交建议 / 批评;
|
||||
|
||||
> 项目管委会成员,从贡献者或者 Committer 中选举产生,他们拥有 Nightingale 代码仓库的写操作权限,拥有 ccf.org.cn 为后缀的邮箱地址,拥有 Nightingale 社区相关事务的投票权、以及提名 Committer 候选人的权利。 项目管委会作为一个实体,为整个项目的发展全权负责。
|
||||
年度累计向 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale) 提交 **5** 个PR(被合并),或者因为其他贡献被**项目管委会**一致认可,将会自动进入到 **[ACTIVE CONTRIBUTORS](./active-contributors.md)** 列表,并获得 **[CCF ODC](https://www.ccf.org.cn/kyfzwyh/)** 颁发的电子证书,享有夜莺开源社区一定的权益和福利。
|
||||
|
||||
#### 项目管委会主席(PMC Chair)
|
||||
|
||||
> 项目管委会主席采用任命制,由 [CCF ODC](https://www.ccf.org.cn/kyfzwyh/) 从项目管委会成员中任命产生。项目管委会作为一个统一的实体,来管理和领导 Nightingale 项目。管委会主席是 CCF ODC 和项目管委会之间的沟通桥梁,履行特定的项目管理职责。
|
||||
### 提交者(Committer)
|
||||
|
||||
# 沟通机制(Communication)
|
||||
> Committer 是指拥有 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale) 代码仓库写操作权限的贡献者,他们拥有 ccf.org.cn 为后缀的邮箱地址(待上线)。原则上 Committer 能够自主决策某个代码补丁是否可以合入到夜莺代码仓库,但是项目管委会拥有最终的决策权。
|
||||
|
||||
Committer 承担以下一个或多个职责:
|
||||
- 积极回应 Issues;
|
||||
- Review PRs;
|
||||
- 参加开发者例行会议,积极讨论项目规划和技术方案;
|
||||
- 代表夜莺开源社区出席相关技术会议并做演讲;
|
||||
|
||||
Committer 记录并公示于 **[COMMITTERS](./committers.md)** 列表,并获得 **[CCF ODC](https://www.ccf.org.cn/kyfzwyh/)** 颁发的电子证书,以及享有夜莺开源社区的各种权益和福利。
|
||||
|
||||
|
||||
### 项目管委会成员(PMC Member)
|
||||
|
||||
> 项目管委会成员,从 Contributor 或者 Committer 中选举产生,他们拥有 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale) 代码仓库的写操作权限,拥有 ccf.org.cn 为后缀的邮箱地址(待上线),拥有 Nightingale 社区相关事务的投票权、以及提名 Committer 候选人的权利。 项目管委会作为一个实体,为整个项目的发展全权负责。项目管委会成员记录并公示于 **[PMC](./pmc.md)** 列表。
|
||||
|
||||
### 项目管委会主席(PMC Chair)
|
||||
|
||||
> 项目管委会主席采用任命制,由 **[CCF ODC](https://www.ccf.org.cn/kyfzwyh/)** 从项目管委会成员中任命产生。项目管委会作为一个统一的实体,来管理和领导夜莺项目。管委会主席是 CCF ODC 和项目管委会之间的沟通桥梁,履行特定的项目管理职责。
|
||||
|
||||
## 沟通机制(Communication)
|
||||
1. 我们推荐使用邮件列表来反馈建议(待发布);
|
||||
2. 我们推荐使用 [github issue](https://github.com/ccfos/nightingale/issues) 跟踪 bug 和管理需求;
|
||||
3. 我们推荐使用 [github milestone](https://github.com/ccfos/nightingale/milestones) 来管理项目进度和规划;
|
||||
4. 我们推荐使用腾讯会议来定期召开项目例会;
|
||||
4. 我们推荐使用腾讯会议来定期召开项目例会(会议 ID 待发布);
|
||||
|
||||
# 文档(Documentation)
|
||||
## 文档(Documentation)
|
||||
1. 我们推荐使用 [github pages](https://n9e.github.io) 来沉淀文档;
|
||||
2. 我们推荐使用 [gitlink wiki](https://www.gitlink.org.cn/ccfos/nightingale/wiki/faq) 来沉淀FAQ;
|
||||
|
||||
|
||||
# 运营机制(Operation)
|
||||
## 运营机制(Operation)
|
||||
|
||||
1. 我们定期组织用户、贡献者、项目管委会成员之间的沟通会议,讨论项目开发的目标、方案、进度,以及讨论相关需求的合理性、优先级等议题;
|
||||
2. 我们定期组织 meetup (线上&线下),创造良好的用户交流分享环境,并沉淀相关内容到文档站点;
|
||||
3. 我们定期组织 Nightingale 开发者大会,分享 best user story、同步年度开发目标和计划、讨论新技术方向等;
|
||||
3. 我们定期组织夜莺开发者大会,分享 best user story、同步年度开发目标和计划、讨论新技术方向等;
|
||||
|
||||
# 社区指导原则(Philosophy)
|
||||
- 尊重、认可和记录每一位贡献者的工作;
|
||||
## 社区指导原则(Philosophy)
|
||||
|
||||
**尊重、认可和记录每一位贡献者的工作。**
|
||||
|
||||
## 关于提问的原则
|
||||
|
||||
# 关于提问的原则
|
||||
按照**尊重、认可、记录每一位贡献者的工作**原则,我们提倡**高效的提问**,这既是对开发者时间的尊重,也是对整个社区的知识沉淀的贡献:
|
||||
|
||||
1. 提问之前请先查阅 [FAQ](https://www.gitlink.org.cn/ccfos/nightingale/wiki/faq) ;
|
||||
2. 提问之前请先搜索 [github issue](https://github.com/ccfos/nightingale/issues);
|
||||
3. 我们优先推荐通过提交 github issue 来提问,如果[有问题点击这里](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Fbug&template=bug_report.yml) | [有需求建议点击这里](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Ffeature&template=enhancement.md);
|
||||
4. 最后,我们推荐你加入微信群,针对相关开放式问题,相互交流咨询 (请先加好友:[UlricGO](https://www.gitlink.org.cn/UlricQin/gist/tree/master/self.jpeg) 备注:夜莺加群+姓名+公司,交流群里会有开发者团队和专业、热心的群友回答问题);
|
||||
|
||||
最后,我们推荐你加入微信群,针对相关开放式问题,相互交流咨询 (请先加好友:[UlricGO](https://www.gitlink.org.cn/UlricQin/gist/tree/master/self.jpeg) 备注:夜莺加群+姓名+公司,交流群里会有开发者团队和专业、热心的群友回答问题);
|
||||
0
doc/end-users.md
Normal file
0
doc/end-users.md
Normal file
0
doc/pmc.md
Normal file
0
doc/pmc.md
Normal file
234
doc/server-dash.json
Normal file
234
doc/server-dash.json
Normal file
@@ -0,0 +1,234 @@
|
||||
{
|
||||
"name": "夜莺大盘",
|
||||
"tags": "",
|
||||
"configs": {
|
||||
"var": [],
|
||||
"panels": [
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "rate(n9e_server_samples_received_total[1m])"
|
||||
}
|
||||
],
|
||||
"name": "每秒接收的数据点个数",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "none"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "off"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"i": "53fcb9dc-23f9-41e0-bc5e-121eed14c3a4",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "53fcb9dc-23f9-41e0-bc5e-121eed14c3a4"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "rate(n9e_server_alerts_total[10m])"
|
||||
}
|
||||
],
|
||||
"name": "每秒产生的告警事件个数",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "none"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "off"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0,
|
||||
"i": "47fc6252-9cc8-4b53-8e27-0c5c59a47269",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "f70dcb8b-b58b-4ef9-9e48-f230d9e17140"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "n9e_server_alert_queue_size"
|
||||
}
|
||||
],
|
||||
"name": "告警事件内存队列长度",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "none"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "off"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"i": "ad1af16c-de0c-45f4-8875-cea4e85d51d0",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "caf23e58-d907-42b0-9ed6-722c8c6f3c5f"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "n9e_server_http_request_duration_seconds_sum/n9e_server_http_request_duration_seconds_count"
|
||||
}
|
||||
],
|
||||
"name": "数据接收接口平均响应时间(单位:秒)",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "desc"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "noraml"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 4,
|
||||
"i": "64c3abc2-404c-4462-a82f-c109a21dac91",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "6b8d2db1-efca-4b9e-b429-57a9d2272bc5"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "n9e_server_sample_queue_size"
|
||||
}
|
||||
],
|
||||
"name": "内存数据队列长度",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "desc"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "off"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"i": "1c7da942-58c2-40dc-b42f-983e4a35b89b",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "bd41677d-40d3-482e-bb6e-fbd25df46d87"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "avg(n9e_server_forward_duration_seconds_sum/n9e_server_forward_duration_seconds_count)"
|
||||
}
|
||||
],
|
||||
"name": "数据发往TSDB平均耗时(单位:秒)",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "desc"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {
|
||||
"decimals": 8
|
||||
},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "noraml"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 8,
|
||||
"i": "eed94a0b-954f-48ac-82e5-a2eada1c8a3d",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "c8642e72-f384-46a5-8410-1e6be2953c3c"
|
||||
}
|
||||
],
|
||||
"version": "2.0.0"
|
||||
}
|
||||
}
|
||||
14
docker/Dockerfile.goreleaser
Normal file
14
docker/Dockerfile.goreleaser
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM --platform=$BUILDPLATFORM python:2
|
||||
|
||||
|
||||
WORKDIR /app
|
||||
ADD n9e /app
|
||||
ADD http://download.flashcat.cloud/wait /wait
|
||||
RUN mkdir -p /app/pub && chmod +x /wait
|
||||
ADD pub /app/pub/
|
||||
RUN chmod +x n9e
|
||||
|
||||
EXPOSE 19000
|
||||
EXPOSE 18000
|
||||
|
||||
CMD ["/app/n9e", "-h"]
|
||||
@@ -6,6 +6,7 @@ networks:
|
||||
|
||||
services:
|
||||
mysql:
|
||||
platform: linux/x86_64
|
||||
image: "mysql:5.7"
|
||||
container_name: mysql
|
||||
hostname: mysql
|
||||
|
||||
@@ -126,7 +126,36 @@ insert into role_operation(role_name, operation) values('Standard', '/job-tpls/d
|
||||
insert into role_operation(role_name, operation) values('Standard', '/job-tasks');
|
||||
insert into role_operation(role_name, operation) values('Standard', '/job-tasks/add');
|
||||
insert into role_operation(role_name, operation) values('Standard', '/job-tasks/put');
|
||||
insert into role_operation(role_name, operation) values('Standard', '/recording-rules');
|
||||
insert into role_operation(role_name, operation) values('Standard', '/recording-rules/add');
|
||||
insert into role_operation(role_name, operation) values('Standard', '/recording-rules/put');
|
||||
insert into role_operation(role_name, operation) values('Standard', '/recording-rules/del');
|
||||
|
||||
CREATE TABLE recording_rule (
|
||||
id bigserial NOT NULL,
|
||||
group_id bigint not null default 0,
|
||||
cluster varchar(128) not null,
|
||||
name varchar(255) not null,
|
||||
note varchar(255) not null,
|
||||
disabled smallint not null,
|
||||
prom_ql varchar(8192) not null,
|
||||
prom_eval_interval int not null,
|
||||
append_tags varchar(255) default '',
|
||||
create_at bigint default 0,
|
||||
create_by varchar(64) default '',
|
||||
update_at bigint default 0,
|
||||
update_by varchar(64) default '',
|
||||
CONSTRAINT recording_rule_pk PRIMARY KEY (id)
|
||||
) ;
|
||||
CREATE INDEX recording_rule_group_id_idx ON recording_rule (group_id);
|
||||
CREATE INDEX recording_rule_update_at_idx ON recording_rule (update_at);
|
||||
COMMENT ON COLUMN recording_rule.group_id IS 'group_id';
|
||||
COMMENT ON COLUMN recording_rule.name IS 'new metric name';
|
||||
COMMENT ON COLUMN recording_rule.note IS 'rule note';
|
||||
COMMENT ON COLUMN recording_rule.disabled IS '0:enabled 1:disabled';
|
||||
COMMENT ON COLUMN recording_rule.prom_ql IS 'promql';
|
||||
COMMENT ON COLUMN recording_rule.prom_eval_interval IS 'evaluate interval';
|
||||
COMMENT ON COLUMN recording_rule.append_tags IS 'split by space: service=n9e mod=api';
|
||||
|
||||
-- for alert_rule | collect_rule | mute | dashboard grouping
|
||||
CREATE TABLE busi_group (
|
||||
|
||||
@@ -158,12 +158,12 @@ MaxIdleConns = 100
|
||||
MaxIdleConnsPerHost = 10
|
||||
|
||||
[WriterOpt]
|
||||
# queue channel count
|
||||
QueueCount = 100
|
||||
# queue max size
|
||||
QueueMaxSize = 10000000
|
||||
QueueMaxSize = 200000
|
||||
# once pop samples number from queue
|
||||
QueuePopSize = 2000
|
||||
# unit: ms
|
||||
SleepInterval = 50
|
||||
|
||||
[[Writers]]
|
||||
Url = "http://prometheus:9090/api/v1/write"
|
||||
|
||||
232
etc/dashboards/n9e_server.json
Normal file
232
etc/dashboards/n9e_server.json
Normal file
@@ -0,0 +1,232 @@
|
||||
{
|
||||
"name": "夜莺大盘",
|
||||
"tags": "",
|
||||
"configs": {
|
||||
"var": [],
|
||||
"panels": [
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "rate(n9e_server_samples_received_total[1m])"
|
||||
}
|
||||
],
|
||||
"name": "每秒接收的数据点个数",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "none"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "off"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"i": "53fcb9dc-23f9-41e0-bc5e-121eed14c3a4",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "53fcb9dc-23f9-41e0-bc5e-121eed14c3a4"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "rate(n9e_server_alerts_total[10m])"
|
||||
}
|
||||
],
|
||||
"name": "每秒产生的告警事件个数",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "none"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "off"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0,
|
||||
"i": "47fc6252-9cc8-4b53-8e27-0c5c59a47269",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "f70dcb8b-b58b-4ef9-9e48-f230d9e17140"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "n9e_server_alert_queue_size"
|
||||
}
|
||||
],
|
||||
"name": "告警事件内存队列长度",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "none"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "off"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"i": "ad1af16c-de0c-45f4-8875-cea4e85d51d0",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "caf23e58-d907-42b0-9ed6-722c8c6f3c5f"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "n9e_server_http_request_duration_seconds_sum/n9e_server_http_request_duration_seconds_count"
|
||||
}
|
||||
],
|
||||
"name": "数据接收接口平均响应时间(单位:秒)",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "desc"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "noraml"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 4,
|
||||
"i": "64c3abc2-404c-4462-a82f-c109a21dac91",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "6b8d2db1-efca-4b9e-b429-57a9d2272bc5"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "n9e_server_sample_queue_size"
|
||||
}
|
||||
],
|
||||
"name": "内存数据队列长度",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "desc"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "off"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"i": "1c7da942-58c2-40dc-b42f-983e4a35b89b",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "bd41677d-40d3-482e-bb6e-fbd25df46d87"
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "avg(n9e_server_forward_duration_seconds_sum/n9e_server_forward_duration_seconds_count)"
|
||||
}
|
||||
],
|
||||
"name": "数据发往TSDB平均耗时(单位:秒)",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "desc"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"fillOpacity": 0.5,
|
||||
"stack": "noraml"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"type": "timeseries",
|
||||
"layout": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 8,
|
||||
"i": "eed94a0b-954f-48ac-82e5-a2eada1c8a3d",
|
||||
"isResizable": true
|
||||
},
|
||||
"id": "c8642e72-f384-46a5-8410-1e6be2953c3c"
|
||||
}
|
||||
],
|
||||
"version": "2.0.0"
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
type inter interface {
|
||||
Descript() string
|
||||
Notify([]byte)
|
||||
NotifyMaintainer([]byte)
|
||||
}
|
||||
|
||||
// N9E complete
|
||||
@@ -37,9 +38,16 @@ func (n *N9EPlugin) Notify(bs []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func (n *N9EPlugin) NotifyMaintainer(bs []byte) {
|
||||
fmt.Println("do something... begin")
|
||||
result := string(bs)
|
||||
fmt.Println(result)
|
||||
fmt.Println("do something... end")
|
||||
}
|
||||
|
||||
// will be loaded for alertingCall , The first letter must be capitalized to be exported
|
||||
var N9eCaller = N9EPlugin{
|
||||
Name: "n9e",
|
||||
Description: "演示告警通过动态链接库方式通知",
|
||||
Name: "N9EPlugin",
|
||||
Description: "Notification by lib",
|
||||
BuildAt: time.Now().Local().Format("2006/01/02 15:04:05"),
|
||||
}
|
||||
|
||||
@@ -165,7 +165,6 @@ MaxConnsPerHost = 0
|
||||
MaxIdleConns = 100
|
||||
MaxIdleConnsPerHost = 10
|
||||
|
||||
|
||||
[WriterOpt]
|
||||
# queue channel count
|
||||
QueueCount = 100
|
||||
|
||||
@@ -98,6 +98,13 @@ AccessExpired = 1500
|
||||
RefreshExpired = 10080
|
||||
RedisKeyPrefix = "/jwt/"
|
||||
|
||||
[ProxyAuth]
|
||||
# if proxy auth enabled, jwt auth is disabled
|
||||
Enable = false
|
||||
# username key in http proxy header
|
||||
HeaderUserNameKey = "X-User-Name"
|
||||
DefaultRoles = ["Standard"]
|
||||
|
||||
[BasicAuth]
|
||||
user001 = "ccc26da7b9aba533cbb263a36c07dcc5"
|
||||
|
||||
|
||||
8
fe.sh
Executable file
8
fe.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
TAG=$(curl -sX GET https://api.github.com/repos/n9e/fe-v5/releases/latest | awk '/tag_name/{print $4;exit}' FS='[""]')
|
||||
VERSION=$(echo $TAG | sed 's/v//g')
|
||||
|
||||
curl -o n9e-fe-${VERSION}.tar.gz -L https://github.com/n9e/fe-v5/releases/download/${TAG}/n9e-fe-${VERSION}.tar.gz
|
||||
|
||||
tar zxvf n9e-fe-${VERSION}.tar.gz
|
||||
14
go.mod
14
go.mod
@@ -19,13 +19,13 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.12
|
||||
github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/prometheus/common v0.26.0
|
||||
github.com/prometheus/client_golang v1.12.2
|
||||
github.com/prometheus/common v0.32.1
|
||||
github.com/prometheus/prometheus v2.5.0+incompatible
|
||||
github.com/tidwall/gjson v1.14.0
|
||||
github.com/toolkits/pkg v1.2.9
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gorm.io/driver/mysql v1.1.2
|
||||
gorm.io/driver/postgres v1.1.1
|
||||
@@ -36,7 +36,7 @@ require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fatih/camelcase v1.0.0 // indirect
|
||||
@@ -64,7 +64,7 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pquerna/cachecontrol v0.1.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
@@ -74,9 +74,9 @@ require (
|
||||
go.uber.org/automaxprocs v1.4.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/appengine v1.4.0 // indirect
|
||||
google.golang.org/appengine v1.6.6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211007155348-82e027067bd4 // indirect
|
||||
google.golang.org/grpc v1.41.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
|
||||
261
go.sum
261
go.sum
@@ -1,9 +1,41 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
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 v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
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/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
@@ -19,8 +51,12 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
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=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
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/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@@ -67,6 +103,9 @@ github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM=
|
||||
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
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/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
@@ -98,11 +137,22 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
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=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
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.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
@@ -116,23 +166,41 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
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/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
@@ -189,6 +257,8 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
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/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@@ -253,8 +323,9 @@ github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQ
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
|
||||
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@@ -262,13 +333,15 @@ github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/prometheus v2.5.0+incompatible h1:7QPitgO2kOFG8ecuRn9O/4L9+10He72rVRJvMXrE9Hg=
|
||||
github.com/prometheus/prometheus v2.5.0+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
|
||||
github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62/go.mod h1:65XQgovT59RWatovFwnwocoUxiI/eENTnOY5GK3STuY=
|
||||
@@ -315,9 +388,16 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
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/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
@@ -336,6 +416,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -346,12 +427,33 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
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-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/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=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
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/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/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -362,28 +464,51 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/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-20190813141303-74dc4d7220e7/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-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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
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-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -392,20 +517,40 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/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-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -415,11 +560,13 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/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=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -427,19 +574,55 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
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-20200103221440-774c71fcf114/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/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-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -449,19 +632,73 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
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/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
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=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
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-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-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20211007155348-82e027067bd4 h1:YXPV/eKW0ZWRdB5tyI6aPoaa2Wxb4OSlFrTREMdwn64=
|
||||
google.golang.org/genproto v0.0.0-20211007155348-82e027067bd4/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
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/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
@@ -475,6 +712,7 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
@@ -515,5 +753,12 @@ gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
gorm.io/gorm v1.21.15 h1:gAyaDoPw0lCyrSFWhBlahbUA1U4P5RViC1uIqoB+1Rk=
|
||||
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
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/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -22,8 +22,8 @@ type TagFilter struct {
|
||||
type AlertMute struct {
|
||||
Id int64 `json:"id" gorm:"primaryKey"`
|
||||
GroupId int64 `json:"group_id"`
|
||||
Prod string `json:"prod"` // product empty means n9e
|
||||
Cluster string `json:"cluster"`
|
||||
Prod string `json:"prod"` // product empty means n9e
|
||||
Cluster string `json:"cluster"` // take effect by clusters, seperated by space
|
||||
Tags ormx.JSONArr `json:"tags"`
|
||||
Cause string `json:"cause"`
|
||||
Btime int64 `json:"btime"`
|
||||
@@ -44,7 +44,7 @@ func AlertMuteGets(prods []string, bgid int64, query string) (lst []AlertMute, e
|
||||
arr := strings.Fields(query)
|
||||
for i := 0; i < len(arr); i++ {
|
||||
qarg := "%" + arr[i] + "%"
|
||||
session = session.Where("cause like ?", qarg, qarg)
|
||||
session = session.Where("cause like ?", qarg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,10 @@ func (m *AlertMute) Verify() error {
|
||||
return errors.New("cluster invalid")
|
||||
}
|
||||
|
||||
if IsClusterAll(m.Cluster) {
|
||||
m.Cluster = ClusterAll
|
||||
}
|
||||
|
||||
if m.Etime <= m.Btime {
|
||||
return fmt.Errorf("Oops... etime(%d) <= btime(%d)", m.Etime, m.Btime)
|
||||
}
|
||||
@@ -88,12 +92,12 @@ func (m *AlertMute) Parse() error {
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.ITags); i++ {
|
||||
if m.ITags[i].Func == "=~" {
|
||||
if m.ITags[i].Func == "=~" || m.ITags[i].Func == "!~" {
|
||||
m.ITags[i].Regexp, err = regexp.Compile(m.ITags[i].Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if m.ITags[i].Func == "in" {
|
||||
} else if m.ITags[i].Func == "in" || m.ITags[i].Func == "not in" {
|
||||
arr := strings.Fields(m.ITags[i].Value)
|
||||
m.ITags[i].Vset = make(map[string]struct{})
|
||||
for j := 0; j < len(arr); j++ {
|
||||
@@ -123,7 +127,7 @@ func AlertMuteDel(ids []int64) error {
|
||||
func AlertMuteStatistics(cluster string) (*Statistics, error) {
|
||||
session := DB().Model(&AlertMute{}).Select("count(*) as total", "max(create_at) as last_updated")
|
||||
if cluster != "" {
|
||||
session = session.Where("cluster = ?", cluster)
|
||||
session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll)
|
||||
}
|
||||
|
||||
var stats []*Statistics
|
||||
@@ -146,10 +150,19 @@ func AlertMuteGetsByCluster(cluster string) ([]*AlertMute, error) {
|
||||
// get my cluster's mutes
|
||||
session := DB().Model(&AlertMute{})
|
||||
if cluster != "" {
|
||||
session = session.Where("cluster = ?", cluster)
|
||||
session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll)
|
||||
}
|
||||
|
||||
var lst []*AlertMute
|
||||
var mlst []*AlertMute
|
||||
err = session.Find(&lst).Error
|
||||
return lst, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range lst {
|
||||
if MatchCluster(m.Cluster, cluster) {
|
||||
mlst = append(mlst, m)
|
||||
}
|
||||
}
|
||||
return mlst, err
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
type AlertRule struct {
|
||||
Id int64 `json:"id" gorm:"primaryKey"`
|
||||
GroupId int64 `json:"group_id"` // busi group id
|
||||
Cluster string `json:"cluster"` // take effect by cluster
|
||||
Cluster string `json:"cluster"` // take effect by clusters, seperated by space
|
||||
Name string `json:"name"` // rule name
|
||||
Note string `json:"note"` // will sent in notify
|
||||
Prod string `json:"prod"` // product empty means n9e
|
||||
@@ -67,6 +67,10 @@ func (ar *AlertRule) Verify() error {
|
||||
return errors.New("cluster is blank")
|
||||
}
|
||||
|
||||
if IsClusterAll(ar.Cluster) {
|
||||
ar.Cluster = ClusterAll
|
||||
}
|
||||
|
||||
if str.Dangerous(ar.Name) {
|
||||
return errors.New("Name has invalid characters")
|
||||
}
|
||||
@@ -124,7 +128,7 @@ func (ar *AlertRule) Add() error {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := AlertRuleExists("group_id=? and cluster=? and name=?", ar.GroupId, ar.Cluster, ar.Name)
|
||||
exists, err := AlertRuleExists(0, ar.GroupId, ar.Cluster, ar.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -142,7 +146,7 @@ func (ar *AlertRule) Add() error {
|
||||
|
||||
func (ar *AlertRule) Update(arf AlertRule) error {
|
||||
if ar.Name != arf.Name {
|
||||
exists, err := AlertRuleExists("group_id=? and cluster=? and name=? and id <> ?", ar.GroupId, ar.Cluster, arf.Name, ar.Id)
|
||||
exists, err := AlertRuleExists(ar.Id, ar.GroupId, ar.Cluster, arf.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -262,8 +266,25 @@ func AlertRuleDels(ids []int64, bgid ...int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func AlertRuleExists(where string, args ...interface{}) (bool, error) {
|
||||
return Exists(DB().Model(&AlertRule{}).Where(where, args...))
|
||||
func AlertRuleExists(id, groupId int64, cluster, name string) (bool, error) {
|
||||
session := DB().Where("id <> ? and group_id = ? and name = ?", id, groupId, name)
|
||||
|
||||
var lst []AlertRule
|
||||
err := session.Find(&lst).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(lst) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// match cluster
|
||||
for _, r := range lst {
|
||||
if MatchCluster(r.Cluster, cluster) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func AlertRuleGets(groupId int64) ([]AlertRule, error) {
|
||||
@@ -284,22 +305,39 @@ func AlertRuleGetsByCluster(cluster string) ([]*AlertRule, error) {
|
||||
session := DB().Where("disabled = ? and prod = ?", 0, "")
|
||||
|
||||
if cluster != "" {
|
||||
session = session.Where("cluster = ?", cluster)
|
||||
session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll)
|
||||
}
|
||||
|
||||
var lst []*AlertRule
|
||||
err := session.Find(&lst).Error
|
||||
if err == nil {
|
||||
if err != nil {
|
||||
return lst, err
|
||||
}
|
||||
|
||||
if len(lst) == 0 {
|
||||
return lst, nil
|
||||
}
|
||||
|
||||
if cluster == "" {
|
||||
for i := 0; i < len(lst); i++ {
|
||||
lst[i].DB2FE()
|
||||
}
|
||||
return lst, nil
|
||||
}
|
||||
|
||||
return lst, err
|
||||
lr := make([]*AlertRule, 0, len(lst))
|
||||
for _, r := range lst {
|
||||
if MatchCluster(r.Cluster, cluster) {
|
||||
r.DB2FE()
|
||||
lr = append(lr, r)
|
||||
}
|
||||
}
|
||||
|
||||
return lr, err
|
||||
}
|
||||
|
||||
func AlertRulesGetsBy(prods []string, query string) ([]*AlertRule, error) {
|
||||
session := DB().Where("disabled = ? and prod in (?)", 0, prods)
|
||||
session := DB().Where("prod in (?)", prods)
|
||||
|
||||
if query != "" {
|
||||
arr := strings.Fields(query)
|
||||
@@ -358,7 +396,8 @@ func AlertRuleStatistics(cluster string) (*Statistics, error) {
|
||||
session := DB().Model(&AlertRule{}).Select("count(*) as total", "max(update_at) as last_updated").Where("disabled = ? and prod = ?", 0, "")
|
||||
|
||||
if cluster != "" {
|
||||
session = session.Where("cluster = ?", cluster)
|
||||
// 简略的判断,当一个clustername是另一个clustername的substring的时候,会出现stats与预期不符,不影响使用
|
||||
session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll)
|
||||
}
|
||||
|
||||
var stats []*Statistics
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
type AlertSubscribe struct {
|
||||
Id int64 `json:"id" gorm:"primaryKey"`
|
||||
GroupId int64 `json:"group_id"`
|
||||
Cluster string `json:"cluster"`
|
||||
Cluster string `json:"cluster"` // take effect by clusters, seperated by space
|
||||
RuleId int64 `json:"rule_id"`
|
||||
RuleName string `json:"rule_name" gorm:"-"` // for fe
|
||||
Tags ormx.JSONArr `json:"tags"`
|
||||
@@ -59,6 +59,10 @@ func (s *AlertSubscribe) Verify() error {
|
||||
return errors.New("cluster invalid")
|
||||
}
|
||||
|
||||
if IsClusterAll(s.Cluster) {
|
||||
s.Cluster = ClusterAll
|
||||
}
|
||||
|
||||
if err := s.Parse(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -202,7 +206,7 @@ func AlertSubscribeStatistics(cluster string) (*Statistics, error) {
|
||||
session := DB().Model(&AlertSubscribe{}).Select("count(*) as total", "max(update_at) as last_updated")
|
||||
|
||||
if cluster != "" {
|
||||
session = session.Where("cluster = ?", cluster)
|
||||
session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll)
|
||||
}
|
||||
|
||||
var stats []*Statistics
|
||||
@@ -218,10 +222,19 @@ func AlertSubscribeGetsByCluster(cluster string) ([]*AlertSubscribe, error) {
|
||||
// get my cluster's subscribes
|
||||
session := DB().Model(&AlertSubscribe{})
|
||||
if cluster != "" {
|
||||
session = session.Where("cluster = ?", cluster)
|
||||
session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll)
|
||||
}
|
||||
|
||||
var lst []*AlertSubscribe
|
||||
var slst []*AlertSubscribe
|
||||
err := session.Find(&lst).Error
|
||||
return lst, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, s := range lst {
|
||||
if MatchCluster(s.Cluster, cluster) {
|
||||
slst = append(slst, s)
|
||||
}
|
||||
}
|
||||
return slst, err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/toolkits/pkg/str"
|
||||
"gorm.io/gorm"
|
||||
|
||||
@@ -9,6 +11,9 @@ import (
|
||||
|
||||
const AdminRole = "Admin"
|
||||
|
||||
// if rule's cluster field contains `ClusterAll`, means it take effect in all clusters
|
||||
const ClusterAll = "$all"
|
||||
|
||||
func DB() *gorm.DB {
|
||||
return storage.DB
|
||||
}
|
||||
@@ -42,3 +47,26 @@ type Statistics struct {
|
||||
Total int64 `gorm:"total"`
|
||||
LastUpdated int64 `gorm:"last_updated"`
|
||||
}
|
||||
|
||||
func MatchCluster(ruleCluster, targetCluster string) bool {
|
||||
if targetCluster == ClusterAll {
|
||||
return true
|
||||
}
|
||||
clusters := strings.Fields(ruleCluster)
|
||||
for _, c := range clusters {
|
||||
if c == ClusterAll || c == targetCluster {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsClusterAll(ruleCluster string) bool {
|
||||
clusters := strings.Fields(ruleCluster)
|
||||
for _, c := range clusters {
|
||||
if c == ClusterAll {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
type RecordingRule struct {
|
||||
Id int64 `json:"id" gorm:"primaryKey"`
|
||||
GroupId int64 `json:"group_id"` // busi group id
|
||||
Cluster string `json:"cluster"` // take effect by cluster
|
||||
Cluster string `json:"cluster"` // take effect by cluster, seperated by space
|
||||
Name string `json:"name"` // new metric name
|
||||
Note string `json:"note"` // note
|
||||
Disabled int `json:"disabled"` // 0: enabled, 1: disabled
|
||||
@@ -40,6 +40,7 @@ func (re *RecordingRule) DB2FE() {
|
||||
//re.ClusterJSON = strings.Fields(re.Cluster)
|
||||
re.AppendTagsJSON = strings.Fields(re.AppendTags)
|
||||
}
|
||||
|
||||
func (re *RecordingRule) Verify() error {
|
||||
if re.GroupId < 0 {
|
||||
return fmt.Errorf("GroupId(%d) invalid", re.GroupId)
|
||||
@@ -49,6 +50,10 @@ func (re *RecordingRule) Verify() error {
|
||||
return errors.New("cluster is blank")
|
||||
}
|
||||
|
||||
if IsClusterAll(re.Cluster) {
|
||||
re.Cluster = ClusterAll
|
||||
}
|
||||
|
||||
if !model.MetricNameRE.MatchString(re.Name) {
|
||||
return errors.New("Name has invalid chreacters")
|
||||
}
|
||||
@@ -78,7 +83,7 @@ func (re *RecordingRule) Add() error {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := RecordingRuleExists("group_id=? and cluster=? and name=?", re.GroupId, re.Cluster, re.Name)
|
||||
exists, err := RecordingRuleExists(0, re.GroupId, re.Cluster, re.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -96,7 +101,7 @@ func (re *RecordingRule) Add() error {
|
||||
|
||||
func (re *RecordingRule) Update(ref RecordingRule) error {
|
||||
if re.Name != ref.Name {
|
||||
exists, err := RecordingRuleExists("group_id=? and cluster=? and name=? and id <> ?", re.GroupId, re.Cluster, ref.Name, re.Id)
|
||||
exists, err := RecordingRuleExists(re.Id, re.GroupId, re.Cluster, ref.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -133,9 +138,27 @@ func RecordingRuleDels(ids []int64, groupId int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func RecordingRuleExists(where string, regs ...interface{}) (bool, error) {
|
||||
return Exists(DB().Model(&RecordingRule{}).Where(where, regs...))
|
||||
func RecordingRuleExists(id, groupId int64, cluster, name string) (bool, error) {
|
||||
session := DB().Where("id <> ? and group_id = ? and name =? ", id, groupId, name)
|
||||
|
||||
var lst []RecordingRule
|
||||
err := session.Find(&lst).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(lst) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// match cluster
|
||||
for _, r := range lst {
|
||||
if MatchCluster(r.Cluster, cluster) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func RecordingRuleGets(groupId int64) ([]RecordingRule, error) {
|
||||
session := DB().Where("group_id=?", groupId).Order("name")
|
||||
|
||||
@@ -171,26 +194,45 @@ func RecordingRuleGetById(id int64) (*RecordingRule, error) {
|
||||
}
|
||||
|
||||
func RecordingRuleGetsByCluster(cluster string) ([]*RecordingRule, error) {
|
||||
session := DB()
|
||||
session := DB().Where("disabled = ?", 0)
|
||||
|
||||
if cluster != "" {
|
||||
session = session.Where("cluster = ?", cluster)
|
||||
session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll)
|
||||
}
|
||||
|
||||
var lst []*RecordingRule
|
||||
err := session.Find(&lst).Error
|
||||
if err == nil {
|
||||
if err != nil {
|
||||
return lst, err
|
||||
}
|
||||
|
||||
if len(lst) == 0 {
|
||||
return lst, nil
|
||||
}
|
||||
|
||||
if cluster == "" {
|
||||
for i := 0; i < len(lst); i++ {
|
||||
lst[i].DB2FE()
|
||||
}
|
||||
return lst, nil
|
||||
}
|
||||
|
||||
return lst, err
|
||||
lr := make([]*RecordingRule, 0, len(lst))
|
||||
for _, r := range lst {
|
||||
if MatchCluster(r.Cluster, cluster) {
|
||||
r.DB2FE()
|
||||
lr = append(lr, r)
|
||||
}
|
||||
}
|
||||
|
||||
return lr, err
|
||||
}
|
||||
|
||||
func RecordingRuleStatistics(cluster string) (*Statistics, error) {
|
||||
session := DB().Model(&RecordingRule{}).Select("count(*) as total", "max(update_at) as last_updated")
|
||||
if cluster != "" {
|
||||
session = session.Where("cluster = ?", cluster)
|
||||
// 简略的判断,当一个clustername是另一个clustername的substring的时候,会出现stats与预期不符,不影响使用
|
||||
session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll)
|
||||
}
|
||||
|
||||
var stats []*Statistics
|
||||
|
||||
9
src/notifier/notifier.go
Normal file
9
src/notifier/notifier.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package notifier
|
||||
|
||||
type Notifier interface {
|
||||
Descript() string
|
||||
Notify([]byte)
|
||||
NotifyMaintainer([]byte)
|
||||
}
|
||||
|
||||
var Instance Notifier
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
// Package v1 provides bindings to the Prometheus HTTP API v1:
|
||||
// http://prometheus.io/docs/querying/api/
|
||||
package reader
|
||||
package prom
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -558,10 +558,11 @@ func (qr *queryResult) UnmarshalJSON(b []byte) error {
|
||||
// NewAPI returns a new API for the client.
|
||||
//
|
||||
// It is safe to use the returned API from multiple goroutines.
|
||||
func NewAPI(c api.Client) API {
|
||||
func NewAPI(c api.Client, opt ClientOptions) API {
|
||||
return &httpAPI{
|
||||
client: &apiClientImpl{
|
||||
client: c,
|
||||
opt: opt,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -891,6 +892,7 @@ type apiClient interface {
|
||||
|
||||
type apiClientImpl struct {
|
||||
client api.Client
|
||||
opt ClientOptions
|
||||
}
|
||||
|
||||
type apiResponse struct {
|
||||
@@ -921,16 +923,16 @@ func (h *apiClientImpl) URL(ep string, args map[string]string) *url.URL {
|
||||
}
|
||||
|
||||
func (h *apiClientImpl) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, Warnings, error) {
|
||||
if Reader.Opts.BasicAuthUser != "" && Reader.Opts.BasicAuthPass != "" {
|
||||
req.SetBasicAuth(Reader.Opts.BasicAuthUser, Reader.Opts.BasicAuthPass)
|
||||
if h.opt.BasicAuthUser != "" && h.opt.BasicAuthPass != "" {
|
||||
req.SetBasicAuth(h.opt.BasicAuthUser, h.opt.BasicAuthPass)
|
||||
}
|
||||
|
||||
headerCount := len(Reader.Opts.Headers)
|
||||
headerCount := len(h.opt.Headers)
|
||||
if headerCount > 0 && headerCount%2 == 0 {
|
||||
for i := 0; i < len(Reader.Opts.Headers); i += 2 {
|
||||
req.Header.Add(Reader.Opts.Headers[i], Reader.Opts.Headers[i+1])
|
||||
if Reader.Opts.Headers[i] == "Host" {
|
||||
req.Host = Reader.Opts.Headers[i+1]
|
||||
for i := 0; i < len(h.opt.Headers); i += 2 {
|
||||
req.Header.Add(h.opt.Headers[i], h.opt.Headers[i+1])
|
||||
if h.opt.Headers[i] == "Host" {
|
||||
req.Host = h.opt.Headers[i+1]
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/pkg/prom/client_option.go
Normal file
7
src/pkg/prom/client_option.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package prom
|
||||
|
||||
type ClientOptions struct {
|
||||
BasicAuthUser string
|
||||
BasicAuthPass string
|
||||
Headers []string
|
||||
}
|
||||
@@ -12,13 +12,27 @@ func AppendLabels(pt *prompb.TimeSeries, target *models.Target) {
|
||||
return
|
||||
}
|
||||
|
||||
labelKeys := make(map[string]struct{})
|
||||
for j := 0; j < len(pt.Labels); j++ {
|
||||
labelKeys[pt.Labels[j].Name] = struct{}{}
|
||||
}
|
||||
|
||||
for key, value := range target.TagsMap {
|
||||
if _, has := labelKeys[key]; has {
|
||||
continue
|
||||
}
|
||||
|
||||
pt.Labels = append(pt.Labels, &prompb.Label{
|
||||
Name: key,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
// e.g. busigroup=cloud
|
||||
if _, has := labelKeys[config.C.BusiGroupLabelKey]; has {
|
||||
return
|
||||
}
|
||||
|
||||
if target.GroupId > 0 && len(config.C.BusiGroupLabelKey) > 0 {
|
||||
bg := memsto.BusiGroupCache.GetByBusiGroupId(target.GroupId)
|
||||
if bg == nil {
|
||||
|
||||
@@ -2,8 +2,11 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"plugin"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -11,6 +14,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/koding/multiconfig"
|
||||
|
||||
"github.com/didi/nightingale/v5/src/notifier"
|
||||
"github.com/didi/nightingale/v5/src/pkg/httpx"
|
||||
"github.com/didi/nightingale/v5/src/pkg/logx"
|
||||
"github.com/didi/nightingale/v5/src/pkg/ormx"
|
||||
@@ -100,6 +104,33 @@ func MustLoad(fpaths ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
if C.Alerting.CallPlugin.Enable {
|
||||
if runtime.GOOS == "windows" {
|
||||
fmt.Println("notify plugin on unsupported os:", runtime.GOOS)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
p, err := plugin.Open(C.Alerting.CallPlugin.PluginPath)
|
||||
if err != nil {
|
||||
fmt.Println("failed to load plugin:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
caller, err := p.Lookup(C.Alerting.CallPlugin.Caller)
|
||||
if err != nil {
|
||||
fmt.Println("failed to lookup plugin Caller:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ins, ok := caller.(notifier.Notifier)
|
||||
if !ok {
|
||||
log.Println("notifier interface not implemented")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
notifier.Instance = ins
|
||||
}
|
||||
|
||||
if C.WriterOpt.QueueMaxSize <= 0 {
|
||||
C.WriterOpt.QueueMaxSize = 100000
|
||||
}
|
||||
|
||||
@@ -34,31 +34,36 @@ func matchMute(event *models.AlertCurEvent, mute *models.AlertMute, clock ...int
|
||||
return matchTags(event.TagsMap, mute.ITags)
|
||||
}
|
||||
|
||||
func matchTag(value string, filter models.TagFilter) bool {
|
||||
switch filter.Func {
|
||||
case "==":
|
||||
return filter.Value == value
|
||||
case "!=":
|
||||
return filter.Value != value
|
||||
case "in":
|
||||
_, has := filter.Vset[value]
|
||||
return has
|
||||
case "not in":
|
||||
_, has := filter.Vset[value]
|
||||
return !has
|
||||
case "=~":
|
||||
return filter.Regexp.MatchString(value)
|
||||
case "!~":
|
||||
return !filter.Regexp.MatchString(value)
|
||||
}
|
||||
// unexpect func
|
||||
return false
|
||||
}
|
||||
|
||||
func matchTags(eventTagsMap map[string]string, itags []models.TagFilter) bool {
|
||||
for i := 0; i < len(itags); i++ {
|
||||
filter := itags[i]
|
||||
value, exists := eventTagsMap[filter.Key]
|
||||
if !exists {
|
||||
for _, filter := range itags {
|
||||
value, has := eventTagsMap[filter.Key]
|
||||
if !has {
|
||||
return false
|
||||
}
|
||||
|
||||
if filter.Func == "==" {
|
||||
// ==
|
||||
if filter.Value != value {
|
||||
return false
|
||||
}
|
||||
} else if filter.Func == "in" {
|
||||
// in
|
||||
if _, has := filter.Vset[value]; !has {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// =~
|
||||
if !filter.Regexp.MatchString(value) {
|
||||
return false
|
||||
}
|
||||
if !matchTag(value, filter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"path"
|
||||
"plugin"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -22,6 +20,7 @@ import (
|
||||
"github.com/toolkits/pkg/slice"
|
||||
|
||||
"github.com/didi/nightingale/v5/src/models"
|
||||
"github.com/didi/nightingale/v5/src/notifier"
|
||||
"github.com/didi/nightingale/v5/src/pkg/sys"
|
||||
"github.com/didi/nightingale/v5/src/pkg/tplx"
|
||||
"github.com/didi/nightingale/v5/src/server/common/sender"
|
||||
@@ -103,7 +102,6 @@ func alertingRedisPub(bs []byte) {
|
||||
|
||||
func handleNotice(notice Notice, bs []byte) {
|
||||
alertingCallScript(bs)
|
||||
|
||||
alertingCallPlugin(bs)
|
||||
|
||||
if len(config.C.Alerting.NotifyBuiltinChannels) == 0 {
|
||||
@@ -398,11 +396,6 @@ func alertingCallScript(stdinBytes []byte) {
|
||||
logger.Infof("event_notify: exec %s output: %s", fpath, buf.String())
|
||||
}
|
||||
|
||||
type Notifier interface {
|
||||
Descript() string
|
||||
Notify([]byte)
|
||||
}
|
||||
|
||||
// call notify.so via golang plugin build
|
||||
// ig. etc/script/notify/notify.so
|
||||
func alertingCallPlugin(stdinBytes []byte) {
|
||||
@@ -410,26 +403,8 @@ func alertingCallPlugin(stdinBytes []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
logger.Errorf("call notify plugin on unsupported os: %s", runtime.GOOS)
|
||||
return
|
||||
}
|
||||
|
||||
p, err := plugin.Open(config.C.Alerting.CallPlugin.PluginPath)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to open notify plugin: %v", err)
|
||||
return
|
||||
}
|
||||
caller, err := p.Lookup(config.C.Alerting.CallPlugin.Caller)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to load caller: %v", err)
|
||||
return
|
||||
}
|
||||
notifier, ok := caller.(Notifier)
|
||||
if !ok {
|
||||
logger.Errorf("notifier interface not implemented): %v", err)
|
||||
return
|
||||
}
|
||||
notifier.Notify(stdinBytes)
|
||||
logger.Debugf("alertingCallPlugin done. %s", notifier.Descript())
|
||||
logger.Debugf("alertingCallPlugin begin")
|
||||
logger.Debugf("payload:", string(stdinBytes))
|
||||
notifier.Instance.Notify(stdinBytes)
|
||||
logger.Debugf("alertingCallPlugin done")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/didi/nightingale/v5/src/models"
|
||||
"github.com/didi/nightingale/v5/src/notifier"
|
||||
"github.com/didi/nightingale/v5/src/server/common/sender"
|
||||
"github.com/didi/nightingale/v5/src/server/config"
|
||||
"github.com/didi/nightingale/v5/src/server/memsto"
|
||||
@@ -10,17 +14,54 @@ import (
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
// notify to maintainer to handle the error
|
||||
func notifyToMaintainer(e error, title string) {
|
||||
type MaintainMessage struct {
|
||||
Tos []*models.User `json:"tos"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
logger.Errorf("notifyToMaintainer,title:%s, error:%v", title, e)
|
||||
|
||||
if len(config.C.Alerting.NotifyBuiltinChannels) == 0 {
|
||||
func notifyMaintainerWithPlugin(e error, title, triggerTime string, users []*models.User) {
|
||||
if !config.C.Alerting.CallPlugin.Enable {
|
||||
return
|
||||
}
|
||||
|
||||
maintainerUsers := memsto.UserCache.GetMaintainerUsers()
|
||||
if len(maintainerUsers) == 0 {
|
||||
if runtime.GOOS == "windows" {
|
||||
logger.Errorf("call notify plugin on unsupported os: %s", runtime.GOOS)
|
||||
return
|
||||
}
|
||||
|
||||
stdinBytes, err := json.Marshal(MaintainMessage{
|
||||
Tos: users,
|
||||
Title: title,
|
||||
Content: "Title: " + title + "\nContent: " + e.Error() + "\nTime: " + triggerTime,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Error("failed to marshal MaintainMessage:", err)
|
||||
return
|
||||
}
|
||||
|
||||
notifier.Instance.NotifyMaintainer(stdinBytes)
|
||||
logger.Debugf("notify maintainer with plugin done")
|
||||
}
|
||||
|
||||
// notify to maintainer to handle the error
|
||||
func notifyToMaintainer(e error, title string) {
|
||||
logger.Errorf("notifyToMaintainer, title:%s, error:%v", title, e)
|
||||
|
||||
users := memsto.UserCache.GetMaintainerUsers()
|
||||
if len(users) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
triggerTime := time.Now().Format("2006/01/02 - 15:04:05")
|
||||
|
||||
notifyMaintainerWithPlugin(e, title, triggerTime, users)
|
||||
notifyMaintainerWithBuiltin(e, title, triggerTime, users)
|
||||
}
|
||||
|
||||
func notifyMaintainerWithBuiltin(e error, title, triggerTime string, users []*models.User) {
|
||||
if len(config.C.Alerting.NotifyBuiltinChannels) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -30,7 +71,7 @@ func notifyToMaintainer(e error, title string) {
|
||||
dingtalkset := make(map[string]struct{})
|
||||
feishuset := make(map[string]struct{})
|
||||
|
||||
for _, user := range maintainerUsers {
|
||||
for _, user := range users {
|
||||
if user.Email != "" {
|
||||
emailset[user.Email] = struct{}{}
|
||||
}
|
||||
@@ -62,7 +103,6 @@ func notifyToMaintainer(e error, title string) {
|
||||
}
|
||||
|
||||
phones := StringSetKeys(phoneset)
|
||||
triggerTime := time.Now().Format("2006/01/02 - 15:04:05")
|
||||
|
||||
for _, ch := range config.C.Alerting.NotifyBuiltinChannels {
|
||||
switch ch {
|
||||
@@ -70,13 +110,13 @@ func notifyToMaintainer(e error, title string) {
|
||||
if len(emailset) == 0 {
|
||||
continue
|
||||
}
|
||||
content := "【内部处理错误】当前标题: " + title + "\n【内部处理错误】当前异常: " + e.Error() + "\n【内部处理错误】发送时间: " + triggerTime
|
||||
content := "Title: " + title + "\nContent: " + e.Error() + "\nTime: " + triggerTime
|
||||
sender.WriteEmail(title, content, StringSetKeys(emailset))
|
||||
case "dingtalk":
|
||||
if len(dingtalkset) == 0 {
|
||||
continue
|
||||
}
|
||||
content := "**【内部处理错误】当前标题: **" + title + "\n**【内部处理错误】当前异常: **" + e.Error() + "\n**【内部处理错误】发送时间: **" + triggerTime
|
||||
content := "**Title: **" + title + "\n**Content: **" + e.Error() + "\n**Time: **" + triggerTime
|
||||
sender.SendDingtalk(sender.DingtalkMessage{
|
||||
Title: title,
|
||||
Text: content,
|
||||
@@ -87,7 +127,7 @@ func notifyToMaintainer(e error, title string) {
|
||||
if len(wecomset) == 0 {
|
||||
continue
|
||||
}
|
||||
content := "**【内部处理错误】当前标题: **" + title + "\n**【内部处理错误】当前异常: **" + e.Error() + "\n**【内部处理错误】发送时间: **" + triggerTime
|
||||
content := "**Title: **" + title + "\n**Content: **" + e.Error() + "\n**Time: **" + triggerTime
|
||||
sender.SendWecom(sender.WecomMessage{
|
||||
Text: content,
|
||||
Tokens: StringSetKeys(wecomset),
|
||||
@@ -97,7 +137,7 @@ func notifyToMaintainer(e error, title string) {
|
||||
continue
|
||||
}
|
||||
|
||||
content := "【内部处理错误】当前标题: " + title + "\n【内部处理错误】当前异常: " + e.Error() + "\n【内部处理错误】发送时间: " + triggerTime
|
||||
content := "Title: " + title + "\nContent: " + e.Error() + "\nTime: " + triggerTime
|
||||
sender.SendFeishu(sender.FeishuMessage{
|
||||
Text: content,
|
||||
AtMobiles: phones,
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/toolkits/pkg/str"
|
||||
|
||||
"github.com/didi/nightingale/v5/src/models"
|
||||
"github.com/didi/nightingale/v5/src/pkg/prom"
|
||||
"github.com/didi/nightingale/v5/src/server/common/conv"
|
||||
"github.com/didi/nightingale/v5/src/server/config"
|
||||
"github.com/didi/nightingale/v5/src/server/memsto"
|
||||
@@ -111,12 +112,11 @@ func (r RuleEval) Work() {
|
||||
var value model.Value
|
||||
var err error
|
||||
if r.rule.Algorithm == "" {
|
||||
var warnings reader.Warnings
|
||||
value, warnings, err = reader.Reader.Client.Query(context.Background(), promql, time.Now())
|
||||
var warnings prom.Warnings
|
||||
value, warnings, err = reader.Client.Query(context.Background(), promql, time.Now())
|
||||
if err != nil {
|
||||
logger.Errorf("rule_eval:%d promql:%s, error:%v", r.RuleID(), promql, err)
|
||||
// 告警查询prometheus逻辑出错,发告警信息给管理员
|
||||
notifyToMaintainer(err, "查询prometheus出错")
|
||||
notifyToMaintainer(err, "failed to query prometheus")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -189,7 +189,6 @@ func (ws *WorkersType) Build(rids []int64) {
|
||||
elst, err := models.AlertCurEventGetByRule(rules[hash].Id)
|
||||
if err != nil {
|
||||
logger.Errorf("worker_build: AlertCurEventGetByRule failed: %v", err)
|
||||
notifyToMaintainer(err, "AlertCurEventGetByRule Error,ruleID="+fmt.Sprint(rules[hash].Id))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -294,6 +293,8 @@ func (r RuleEval) judge(vectors []conv.Vector) {
|
||||
tagsMap[arr[0]] = arr[1]
|
||||
}
|
||||
|
||||
tagsMap["rulename"] = r.rule.Name
|
||||
|
||||
// handle target note
|
||||
targetIdent, has := vectors[i].Labels["ident"]
|
||||
targetNote := ""
|
||||
@@ -314,6 +315,7 @@ func (r RuleEval) judge(vectors []conv.Vector) {
|
||||
TriggerTime: vectors[i].Timestamp,
|
||||
TagsMap: tagsMap,
|
||||
GroupId: r.rule.GroupId,
|
||||
RuleName: r.rule.Name,
|
||||
}
|
||||
|
||||
bg := memsto.BusiGroupCache.GetByBusiGroupId(r.rule.GroupId)
|
||||
@@ -321,7 +323,7 @@ func (r RuleEval) judge(vectors []conv.Vector) {
|
||||
event.GroupName = bg.Name
|
||||
}
|
||||
|
||||
// isMuted only need TriggerTime and TagsMap
|
||||
// isMuted only need TriggerTime RuleName and TagsMap
|
||||
if isMuted(event) {
|
||||
logger.Infof("event_muted: rule_id=%d %s", r.rule.Id, vectors[i].Key)
|
||||
continue
|
||||
@@ -554,7 +556,7 @@ func (r RecordingRuleEval) Work() {
|
||||
return
|
||||
}
|
||||
|
||||
value, warnings, err := reader.Reader.Client.Query(context.Background(), promql, time.Now())
|
||||
value, warnings, err := reader.Client.Query(context.Background(), promql, time.Now())
|
||||
if err != nil {
|
||||
logger.Errorf("recording_rule_eval:%d promql:%s, error:%v", r.RuleID(), promql, err)
|
||||
return
|
||||
|
||||
@@ -5,16 +5,12 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/didi/nightingale/v5/src/pkg/prom"
|
||||
"github.com/didi/nightingale/v5/src/server/config"
|
||||
"github.com/prometheus/client_golang/api"
|
||||
)
|
||||
|
||||
type ReaderType struct {
|
||||
Opts config.ReaderOptions
|
||||
Client API
|
||||
}
|
||||
|
||||
var Reader ReaderType
|
||||
var Client prom.API
|
||||
|
||||
func Init(opts config.ReaderOptions) error {
|
||||
cli, err := api.NewClient(api.Config{
|
||||
@@ -40,10 +36,11 @@ func Init(opts config.ReaderOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
Reader = ReaderType{
|
||||
Opts: opts,
|
||||
Client: NewAPI(cli),
|
||||
}
|
||||
Client = prom.NewAPI(cli, prom.ClientOptions{
|
||||
BasicAuthUser: opts.BasicAuthUser,
|
||||
BasicAuthPass: opts.BasicAuthPass,
|
||||
Headers: opts.Headers,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/pprof"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -13,6 +14,8 @@ import (
|
||||
"github.com/didi/nightingale/v5/src/pkg/aop"
|
||||
"github.com/didi/nightingale/v5/src/server/config"
|
||||
"github.com/didi/nightingale/v5/src/server/naming"
|
||||
|
||||
promstat "github.com/didi/nightingale/v5/src/server/stat"
|
||||
)
|
||||
|
||||
func New(version string) *gin.Engine {
|
||||
@@ -66,7 +69,7 @@ func configRoute(r *gin.Engine, version string) {
|
||||
})
|
||||
|
||||
// use apiKey not basic auth
|
||||
r.POST("/datadog/api/v1/series", datadogSeries)
|
||||
r.POST("/datadog/api/v1/series", stat(), datadogSeries)
|
||||
r.POST("/datadog/api/v1/check_run", datadogCheckRun)
|
||||
r.GET("/datadog/api/v1/validate", datadogValidate)
|
||||
r.POST("/datadog/api/v1/metadata", datadogMetadata)
|
||||
@@ -77,12 +80,13 @@ func configRoute(r *gin.Engine, version string) {
|
||||
r.Use(auth)
|
||||
}
|
||||
|
||||
r.POST("/opentsdb/put", handleOpenTSDB)
|
||||
r.POST("/openfalcon/push", falconPush)
|
||||
r.POST("/prometheus/v1/write", remoteWrite)
|
||||
r.POST("/prometheus/v1/query", queryPromql)
|
||||
r.POST("/opentsdb/put", stat(), handleOpenTSDB)
|
||||
r.POST("/openfalcon/push", stat(), falconPush)
|
||||
r.POST("/prometheus/v1/write", stat(), remoteWrite)
|
||||
r.POST("/prometheus/v1/query", stat(), queryPromql)
|
||||
|
||||
r.GET("/memory/alert-rule", alertRuleGet)
|
||||
r.GET("/memory/alert-rule-location", alertRuleLocationGet)
|
||||
r.GET("/memory/idents", identsGets)
|
||||
r.GET("/memory/alert-mutes", mutesGets)
|
||||
r.GET("/memory/alert-subscribes", subscribesGets)
|
||||
@@ -95,3 +99,16 @@ func configRoute(r *gin.Engine, version string) {
|
||||
service := r.Group("/v1/n9e")
|
||||
service.POST("/event", pushEventToQueue)
|
||||
}
|
||||
|
||||
func stat() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
start := time.Now()
|
||||
c.Next()
|
||||
|
||||
code := fmt.Sprintf("%d", c.Writer.Status())
|
||||
method := c.Request.Method
|
||||
labels := []string{code, c.FullPath(), method}
|
||||
|
||||
promstat.RequestDuration.WithLabelValues(labels...).Observe(time.Since(start).Seconds())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/didi/nightingale/v5/src/models"
|
||||
"github.com/didi/nightingale/v5/src/server/config"
|
||||
@@ -14,14 +15,46 @@ import (
|
||||
)
|
||||
|
||||
func pushEventToQueue(c *gin.Context) {
|
||||
var event models.AlertCurEvent
|
||||
var event *models.AlertCurEvent
|
||||
ginx.BindJSON(c, &event)
|
||||
if event.RuleId == 0 {
|
||||
ginx.Bomb(200, "event is illegal")
|
||||
}
|
||||
|
||||
event.TagsMap = make(map[string]string)
|
||||
for i := 0; i < len(event.TagsJSON); i++ {
|
||||
pair := strings.TrimSpace(event.TagsJSON[i])
|
||||
if pair == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
arr := strings.Split(pair, "=")
|
||||
if len(arr) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
event.TagsMap[arr[0]] = arr[1]
|
||||
}
|
||||
|
||||
if err := event.ParseRuleNote(); err != nil {
|
||||
event.RuleNote = fmt.Sprintf("failed to parse rule note: %v", err)
|
||||
}
|
||||
|
||||
// 如果 rule_note 中有 ; 前缀,则使用 rule_note 替换 tags 中的内容
|
||||
if strings.HasPrefix(event.RuleNote, ";") {
|
||||
event.RuleNote = strings.TrimPrefix(event.RuleNote, ";")
|
||||
event.Tags = strings.ReplaceAll(event.RuleNote, " ", ",,")
|
||||
event.TagsJSON = strings.Split(event.Tags, ",,")
|
||||
} else {
|
||||
event.Tags = strings.Join(event.TagsJSON, ",,")
|
||||
}
|
||||
|
||||
event.Callbacks = strings.Join(event.CallbacksJSON, " ")
|
||||
event.NotifyChannels = strings.Join(event.NotifyChannelsJSON, " ")
|
||||
event.NotifyGroups = strings.Join(event.NotifyGroupsJSON, " ")
|
||||
|
||||
promstat.CounterAlertsTotal.WithLabelValues(config.C.ClusterName).Inc()
|
||||
engine.LogEvent(&event, "http_push_queue")
|
||||
engine.LogEvent(event, "http_push_queue")
|
||||
if !engine.EventQueue.PushFront(event) {
|
||||
msg := fmt.Sprintf("event:%+v push_queue err: queue is full", event)
|
||||
ginx.Bomb(200, msg)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
|
||||
"github.com/didi/nightingale/v5/src/server/idents"
|
||||
"github.com/didi/nightingale/v5/src/server/memsto"
|
||||
"github.com/didi/nightingale/v5/src/server/naming"
|
||||
)
|
||||
|
||||
func alertRuleGet(c *gin.Context) {
|
||||
@@ -43,3 +46,14 @@ func userGroupGet(c *gin.Context) {
|
||||
ug := memsto.UserGroupCache.GetByUserGroupId(id)
|
||||
c.JSON(200, gin.H{"id": id, "user_group": ug})
|
||||
}
|
||||
|
||||
func alertRuleLocationGet(c *gin.Context) {
|
||||
id := ginx.QueryStr(c, "id")
|
||||
node, err := naming.HashRing.GetNode(id)
|
||||
if err != nil {
|
||||
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"id": id, "node": node})
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func queryPromql(c *gin.Context) {
|
||||
var f promqlForm
|
||||
ginx.BindJSON(c, &f)
|
||||
|
||||
value, warnings, err := reader.Reader.Client.Query(c.Request.Context(), f.PromQL, time.Now())
|
||||
value, warnings, err := reader.Client.Query(c.Request.Context(), f.PromQL, time.Now())
|
||||
if err != nil {
|
||||
c.String(500, "promql:%s error:%v", f.PromQL, err)
|
||||
return
|
||||
@@ -52,6 +52,24 @@ func queryPromql(c *gin.Context) {
|
||||
c.JSON(200, conv.ConvertVectors(value))
|
||||
}
|
||||
|
||||
func duplicateLabelKey(series *prompb.TimeSeries) bool {
|
||||
if series == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
labelKeys := make(map[string]struct{})
|
||||
|
||||
for j := 0; j < len(series.Labels); j++ {
|
||||
if _, has := labelKeys[series.Labels[j].Name]; has {
|
||||
return true
|
||||
} else {
|
||||
labelKeys[series.Labels[j].Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func remoteWrite(c *gin.Context) {
|
||||
req, err := DecodeWriteRequest(c.Request.Body)
|
||||
if err != nil {
|
||||
@@ -74,6 +92,10 @@ func remoteWrite(c *gin.Context) {
|
||||
)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
if duplicateLabelKey(req.Timeseries[i]) {
|
||||
continue
|
||||
}
|
||||
|
||||
ident = ""
|
||||
|
||||
// find ident label
|
||||
|
||||
@@ -49,6 +49,36 @@ var (
|
||||
Name: "alert_queue_size",
|
||||
Help: "The size of alert queue.",
|
||||
}, []string{"cluster"})
|
||||
|
||||
// 数据转发队列,各个队列的长度
|
||||
GaugeSampleQueueSize = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: "sample_queue_size",
|
||||
Help: "The size of sample queue.",
|
||||
}, []string{"cluster", "channel_number"})
|
||||
|
||||
// 一些重要的请求,比如接收数据的请求,应该统计一下延迟情况
|
||||
RequestDuration = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Buckets: []float64{.01, .1, 1},
|
||||
Name: "http_request_duration_seconds",
|
||||
Help: "HTTP request latencies in seconds.",
|
||||
}, []string{"code", "path", "method"},
|
||||
)
|
||||
|
||||
// 发往后端TSDB,延迟如何
|
||||
ForwardDuration = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Buckets: []float64{.1, 1, 10},
|
||||
Name: "forward_duration_seconds",
|
||||
Help: "Forward samples to TSDB. latencies in seconds.",
|
||||
}, []string{"cluster", "channel_number"},
|
||||
)
|
||||
)
|
||||
|
||||
func Init() {
|
||||
@@ -59,5 +89,8 @@ func Init() {
|
||||
CounterSampleTotal,
|
||||
CounterAlertsTotal,
|
||||
GaugeAlertQueueSize,
|
||||
GaugeSampleQueueSize,
|
||||
RequestDuration,
|
||||
ForwardDuration,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ type Usage struct {
|
||||
}
|
||||
|
||||
func getSamples() (float64, error) {
|
||||
value, warns, err := reader.Reader.Client.Query(context.Background(), request, time.Now())
|
||||
value, warns, err := reader.Client.Query(context.Background(), request, time.Now())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"hash/crc32"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/didi/nightingale/v5/src/server/config"
|
||||
@@ -16,6 +15,8 @@ import (
|
||||
"github.com/prometheus/client_golang/api"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
|
||||
promstat "github.com/didi/nightingale/v5/src/server/stat"
|
||||
)
|
||||
|
||||
type WriterType struct {
|
||||
@@ -23,11 +24,16 @@ type WriterType struct {
|
||||
Client api.Client
|
||||
}
|
||||
|
||||
func (w WriterType) Write(items []*prompb.TimeSeries, headers ...map[string]string) {
|
||||
func (w WriterType) Write(index int, items []*prompb.TimeSeries, headers ...map[string]string) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
promstat.ForwardDuration.WithLabelValues(config.C.ClusterName, fmt.Sprint(index)).Observe(time.Since(start).Seconds())
|
||||
}()
|
||||
|
||||
req := &prompb.WriteRequest{
|
||||
Timeseries: items,
|
||||
}
|
||||
@@ -154,23 +160,9 @@ func (ws *WritersType) StartConsumer(index int, ch chan *prompb.TimeSeries) {
|
||||
// @Author: quzhihao
|
||||
func (ws *WritersType) post(index int, series []*prompb.TimeSeries) {
|
||||
header := map[string]string{"hash": fmt.Sprintf("%s-%d", config.C.Heartbeat.Endpoint, index)}
|
||||
if len(ws.backends) == 1 {
|
||||
for key := range ws.backends {
|
||||
ws.backends[key].Write(series, header)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if len(ws.backends) > 1 {
|
||||
wg := new(sync.WaitGroup)
|
||||
for key := range ws.backends {
|
||||
wg.Add(1)
|
||||
go func(wg *sync.WaitGroup, backend WriterType, items []*prompb.TimeSeries, header map[string]string) {
|
||||
defer wg.Done()
|
||||
backend.Write(series, header)
|
||||
}(wg, ws.backends[key], series, header)
|
||||
}
|
||||
wg.Wait()
|
||||
for key := range ws.backends {
|
||||
go ws.backends[key].Write(index, series, header)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +184,8 @@ func Init(opts []config.WriterOptions, globalOpt config.WriterGlobalOpt) error {
|
||||
go Writers.StartConsumer(i, Writers.chans[i])
|
||||
}
|
||||
|
||||
go reportChanSize()
|
||||
|
||||
for i := 0; i < len(opts); i++ {
|
||||
cli, err := api.NewClient(api.Config{
|
||||
Address: opts[i].Url,
|
||||
@@ -226,3 +220,13 @@ func Init(opts []config.WriterOptions, globalOpt config.WriterGlobalOpt) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func reportChanSize() {
|
||||
for {
|
||||
time.Sleep(time.Second * 3)
|
||||
for i, c := range Writers.chans {
|
||||
size := len(c)
|
||||
promstat.GaugeSampleQueueSize.WithLabelValues(config.C.ClusterName, fmt.Sprint(i)).Set(float64(size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ type Config struct {
|
||||
Log logx.Config
|
||||
HTTP httpx.Config
|
||||
JWTAuth JWTAuth
|
||||
ProxyAuth ProxyAuth
|
||||
BasicAuth gin.Accounts
|
||||
AnonymousAccess AnonymousAccess
|
||||
LDAP ldapx.LdapSection
|
||||
@@ -135,6 +136,12 @@ type JWTAuth struct {
|
||||
RedisKeyPrefix string
|
||||
}
|
||||
|
||||
type ProxyAuth struct {
|
||||
Enable bool
|
||||
HeaderUserNameKey string
|
||||
DefaultRoles []string
|
||||
}
|
||||
|
||||
type AnonymousAccess struct {
|
||||
PromQuerier bool
|
||||
AlertDetail bool
|
||||
|
||||
@@ -11,14 +11,17 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/didi/nightingale/v5/src/pkg/prom"
|
||||
"github.com/didi/nightingale/v5/src/webapi/config"
|
||||
"github.com/prometheus/client_golang/api"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
"github.com/toolkits/pkg/net/httplib"
|
||||
)
|
||||
|
||||
type ClusterType struct {
|
||||
Opts config.ClusterOptions
|
||||
Transport *http.Transport
|
||||
Opts config.ClusterOptions
|
||||
Transport *http.Transport
|
||||
PromClient prom.API
|
||||
}
|
||||
|
||||
type ClustersType struct {
|
||||
@@ -61,18 +64,7 @@ func initClustersFromConfig() error {
|
||||
opts := config.C.Clusters
|
||||
|
||||
for i := 0; i < len(opts); i++ {
|
||||
cluster := &ClusterType{
|
||||
Opts: opts[i],
|
||||
Transport: &http.Transport{
|
||||
// TLSClientConfig: tlsConfig,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: time.Duration(opts[i].DialTimeout) * time.Millisecond,
|
||||
}).DialContext,
|
||||
ResponseHeaderTimeout: time.Duration(opts[i].Timeout) * time.Millisecond,
|
||||
MaxIdleConnsPerHost: opts[i].MaxIdleConnsPerHost,
|
||||
},
|
||||
}
|
||||
cluster := newClusterByOption(opts[i])
|
||||
Clusters.Put(opts[i].Name, cluster)
|
||||
}
|
||||
|
||||
@@ -87,9 +79,9 @@ type DSReply struct {
|
||||
Settings struct {
|
||||
PrometheusAddr string `json:"prometheus.addr"`
|
||||
PrometheusBasic struct {
|
||||
PrometheusUser string `json:"promethues.user"`
|
||||
PrometheusPass string `json:"promethues.password"`
|
||||
} `json:"promethues.basic"`
|
||||
PrometheusUser string `json:"prometheus.user"`
|
||||
PrometheusPass string `json:"prometheus.password"`
|
||||
} `json:"prometheus.basic"`
|
||||
PrometheusTimeout int64 `json:"prometheus.timeout"`
|
||||
} `json:"settings,omitempty"`
|
||||
} `json:"items"`
|
||||
@@ -137,6 +129,7 @@ func loadClustersFromAPI() {
|
||||
logger.Errorf("read response body of %s fail: %v", url, err)
|
||||
continue
|
||||
}
|
||||
logger.Debugf("curl %s success, response: %s", url, string(jsonBytes))
|
||||
|
||||
err = json.Unmarshal(jsonBytes, &reply)
|
||||
if err != nil {
|
||||
@@ -172,21 +165,41 @@ func loadClustersFromAPI() {
|
||||
MaxIdleConnsPerHost: 32,
|
||||
}
|
||||
|
||||
cluster := &ClusterType{
|
||||
Opts: opt,
|
||||
Transport: &http.Transport{
|
||||
// TLSClientConfig: tlsConfig,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: time.Duration(opt.DialTimeout) * time.Millisecond,
|
||||
}).DialContext,
|
||||
ResponseHeaderTimeout: time.Duration(opt.Timeout) * time.Millisecond,
|
||||
MaxIdleConnsPerHost: opt.MaxIdleConnsPerHost,
|
||||
},
|
||||
}
|
||||
|
||||
Clusters.Put(item.Name, cluster)
|
||||
Clusters.Put(item.Name, newClusterByOption(opt))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newClusterByOption(opt config.ClusterOptions) *ClusterType {
|
||||
transport := &http.Transport{
|
||||
// TLSClientConfig: tlsConfig,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: time.Duration(opt.DialTimeout) * time.Millisecond,
|
||||
}).DialContext,
|
||||
ResponseHeaderTimeout: time.Duration(opt.Timeout) * time.Millisecond,
|
||||
MaxIdleConnsPerHost: opt.MaxIdleConnsPerHost,
|
||||
}
|
||||
|
||||
cli, err := api.NewClient(api.Config{
|
||||
Address: opt.Prom,
|
||||
RoundTripper: transport,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("new client fail: %v", err)
|
||||
}
|
||||
|
||||
cluster := &ClusterType{
|
||||
Opts: opt,
|
||||
Transport: transport,
|
||||
PromClient: prom.NewAPI(cli, prom.ClientOptions{
|
||||
BasicAuthUser: opt.BasicAuthUser,
|
||||
BasicAuthPass: opt.BasicAuthPass,
|
||||
Headers: opt.Headers,
|
||||
}),
|
||||
}
|
||||
|
||||
return cluster
|
||||
}
|
||||
|
||||
@@ -98,19 +98,24 @@ func configRoute(r *gin.Engine, version string) {
|
||||
|
||||
pages := r.Group(pagesPrefix)
|
||||
{
|
||||
|
||||
if config.C.AnonymousAccess.PromQuerier {
|
||||
pages.Any("/prometheus/*url", prometheusProxy)
|
||||
|
||||
pages.POST("/query-range-batch", promBatchQueryRange)
|
||||
} else {
|
||||
pages.Any("/prometheus/*url", jwtAuth(), prometheusProxy)
|
||||
pages.Any("/prometheus/*url", auth(), prometheusProxy)
|
||||
|
||||
pages.POST("/query-range-batch", auth(), promBatchQueryRange)
|
||||
}
|
||||
|
||||
pages.GET("/version", func(c *gin.Context) {
|
||||
c.String(200, version)
|
||||
})
|
||||
|
||||
pages.POST("/auth/login", loginPost)
|
||||
pages.POST("/auth/logout", logoutPost)
|
||||
pages.POST("/auth/refresh", refreshPost)
|
||||
pages.POST("/auth/login", jwtMock(), loginPost)
|
||||
pages.POST("/auth/logout", jwtMock(), logoutPost)
|
||||
pages.POST("/auth/refresh", jwtMock(), refreshPost)
|
||||
|
||||
pages.GET("/auth/redirect", loginRedirect)
|
||||
pages.GET("/auth/callback", loginCallback)
|
||||
@@ -123,150 +128,150 @@ func configRoute(r *gin.Engine, version string) {
|
||||
pages.GET("/contact-keys", contactKeysGets)
|
||||
pages.GET("/clusters", clustersGets)
|
||||
|
||||
pages.GET("/self/perms", jwtAuth(), user(), permsGets)
|
||||
pages.GET("/self/profile", jwtAuth(), user(), selfProfileGet)
|
||||
pages.PUT("/self/profile", jwtAuth(), user(), selfProfilePut)
|
||||
pages.PUT("/self/password", jwtAuth(), user(), selfPasswordPut)
|
||||
pages.GET("/self/perms", auth(), user(), permsGets)
|
||||
pages.GET("/self/profile", auth(), user(), selfProfileGet)
|
||||
pages.PUT("/self/profile", auth(), user(), selfProfilePut)
|
||||
pages.PUT("/self/password", auth(), user(), selfPasswordPut)
|
||||
|
||||
pages.GET("/users", jwtAuth(), user(), perm("/users"), userGets)
|
||||
pages.POST("/users", jwtAuth(), admin(), userAddPost)
|
||||
pages.GET("/user/:id/profile", jwtAuth(), userProfileGet)
|
||||
pages.PUT("/user/:id/profile", jwtAuth(), admin(), userProfilePut)
|
||||
pages.PUT("/user/:id/password", jwtAuth(), admin(), userPasswordPut)
|
||||
pages.DELETE("/user/:id", jwtAuth(), admin(), userDel)
|
||||
pages.GET("/users", auth(), user(), perm("/users"), userGets)
|
||||
pages.POST("/users", auth(), admin(), userAddPost)
|
||||
pages.GET("/user/:id/profile", auth(), userProfileGet)
|
||||
pages.PUT("/user/:id/profile", auth(), admin(), userProfilePut)
|
||||
pages.PUT("/user/:id/password", auth(), admin(), userPasswordPut)
|
||||
pages.DELETE("/user/:id", auth(), admin(), userDel)
|
||||
|
||||
pages.GET("/metric-views", jwtAuth(), metricViewGets)
|
||||
pages.DELETE("/metric-views", jwtAuth(), user(), metricViewDel)
|
||||
pages.POST("/metric-views", jwtAuth(), user(), metricViewAdd)
|
||||
pages.PUT("/metric-views", jwtAuth(), user(), metricViewPut)
|
||||
pages.GET("/metric-views", auth(), metricViewGets)
|
||||
pages.DELETE("/metric-views", auth(), user(), metricViewDel)
|
||||
pages.POST("/metric-views", auth(), user(), metricViewAdd)
|
||||
pages.PUT("/metric-views", auth(), user(), metricViewPut)
|
||||
|
||||
pages.GET("/user-groups", jwtAuth(), user(), userGroupGets)
|
||||
pages.POST("/user-groups", jwtAuth(), user(), perm("/user-groups/add"), userGroupAdd)
|
||||
pages.GET("/user-group/:id", jwtAuth(), user(), userGroupGet)
|
||||
pages.PUT("/user-group/:id", jwtAuth(), user(), perm("/user-groups/put"), userGroupWrite(), userGroupPut)
|
||||
pages.DELETE("/user-group/:id", jwtAuth(), user(), perm("/user-groups/del"), userGroupWrite(), userGroupDel)
|
||||
pages.POST("/user-group/:id/members", jwtAuth(), user(), perm("/user-groups/put"), userGroupWrite(), userGroupMemberAdd)
|
||||
pages.DELETE("/user-group/:id/members", jwtAuth(), user(), perm("/user-groups/put"), userGroupWrite(), userGroupMemberDel)
|
||||
pages.GET("/user-groups", auth(), user(), userGroupGets)
|
||||
pages.POST("/user-groups", auth(), user(), perm("/user-groups/add"), userGroupAdd)
|
||||
pages.GET("/user-group/:id", auth(), user(), userGroupGet)
|
||||
pages.PUT("/user-group/:id", auth(), user(), perm("/user-groups/put"), userGroupWrite(), userGroupPut)
|
||||
pages.DELETE("/user-group/:id", auth(), user(), perm("/user-groups/del"), userGroupWrite(), userGroupDel)
|
||||
pages.POST("/user-group/:id/members", auth(), user(), perm("/user-groups/put"), userGroupWrite(), userGroupMemberAdd)
|
||||
pages.DELETE("/user-group/:id/members", auth(), user(), perm("/user-groups/put"), userGroupWrite(), userGroupMemberDel)
|
||||
|
||||
pages.GET("/busi-groups", jwtAuth(), user(), busiGroupGets)
|
||||
pages.POST("/busi-groups", jwtAuth(), user(), perm("/busi-groups/add"), busiGroupAdd)
|
||||
pages.GET("/busi-groups/alertings", jwtAuth(), busiGroupAlertingsGets)
|
||||
pages.GET("/busi-group/:id", jwtAuth(), user(), bgro(), busiGroupGet)
|
||||
pages.PUT("/busi-group/:id", jwtAuth(), user(), perm("/busi-groups/put"), bgrw(), busiGroupPut)
|
||||
pages.POST("/busi-group/:id/members", jwtAuth(), user(), perm("/busi-groups/put"), bgrw(), busiGroupMemberAdd)
|
||||
pages.DELETE("/busi-group/:id/members", jwtAuth(), user(), perm("/busi-groups/put"), bgrw(), busiGroupMemberDel)
|
||||
pages.DELETE("/busi-group/:id", jwtAuth(), user(), perm("/busi-groups/del"), bgrw(), busiGroupDel)
|
||||
pages.GET("/busi-group/:id/perm/:perm", jwtAuth(), user(), checkBusiGroupPerm)
|
||||
pages.GET("/busi-groups", auth(), user(), busiGroupGets)
|
||||
pages.POST("/busi-groups", auth(), user(), perm("/busi-groups/add"), busiGroupAdd)
|
||||
pages.GET("/busi-groups/alertings", auth(), busiGroupAlertingsGets)
|
||||
pages.GET("/busi-group/:id", auth(), user(), bgro(), busiGroupGet)
|
||||
pages.PUT("/busi-group/:id", auth(), user(), perm("/busi-groups/put"), bgrw(), busiGroupPut)
|
||||
pages.POST("/busi-group/:id/members", auth(), user(), perm("/busi-groups/put"), bgrw(), busiGroupMemberAdd)
|
||||
pages.DELETE("/busi-group/:id/members", auth(), user(), perm("/busi-groups/put"), bgrw(), busiGroupMemberDel)
|
||||
pages.DELETE("/busi-group/:id", auth(), user(), perm("/busi-groups/del"), bgrw(), busiGroupDel)
|
||||
pages.GET("/busi-group/:id/perm/:perm", auth(), user(), checkBusiGroupPerm)
|
||||
|
||||
pages.GET("/targets", jwtAuth(), user(), targetGets)
|
||||
pages.DELETE("/targets", jwtAuth(), user(), perm("/targets/del"), targetDel)
|
||||
pages.GET("/targets/tags", jwtAuth(), user(), targetGetTags)
|
||||
pages.POST("/targets/tags", jwtAuth(), user(), perm("/targets/put"), targetBindTagsByFE)
|
||||
pages.DELETE("/targets/tags", jwtAuth(), user(), perm("/targets/put"), targetUnbindTagsByFE)
|
||||
pages.PUT("/targets/note", jwtAuth(), user(), perm("/targets/put"), targetUpdateNote)
|
||||
pages.PUT("/targets/bgid", jwtAuth(), user(), perm("/targets/put"), targetUpdateBgid)
|
||||
pages.GET("/targets", auth(), user(), targetGets)
|
||||
pages.DELETE("/targets", auth(), user(), perm("/targets/del"), targetDel)
|
||||
pages.GET("/targets/tags", auth(), user(), targetGetTags)
|
||||
pages.POST("/targets/tags", auth(), user(), perm("/targets/put"), targetBindTagsByFE)
|
||||
pages.DELETE("/targets/tags", auth(), user(), perm("/targets/put"), targetUnbindTagsByFE)
|
||||
pages.PUT("/targets/note", auth(), user(), perm("/targets/put"), targetUpdateNote)
|
||||
pages.PUT("/targets/bgid", auth(), user(), perm("/targets/put"), targetUpdateBgid)
|
||||
|
||||
pages.GET("/builtin-boards", builtinBoardGets)
|
||||
pages.GET("/builtin-board/:name", builtinBoardGet)
|
||||
|
||||
pages.GET("/busi-group/:id/boards", jwtAuth(), user(), perm("/dashboards"), bgro(), boardGets)
|
||||
pages.POST("/busi-group/:id/boards", jwtAuth(), user(), perm("/dashboards/add"), bgrw(), boardAdd)
|
||||
pages.POST("/busi-group/:id/board/:bid/clone", jwtAuth(), user(), perm("/dashboards/add"), bgrw(), boardClone)
|
||||
pages.GET("/busi-group/:id/boards", auth(), user(), perm("/dashboards"), bgro(), boardGets)
|
||||
pages.POST("/busi-group/:id/boards", auth(), user(), perm("/dashboards/add"), bgrw(), boardAdd)
|
||||
pages.POST("/busi-group/:id/board/:bid/clone", auth(), user(), perm("/dashboards/add"), bgrw(), boardClone)
|
||||
|
||||
pages.GET("/board/:bid", jwtAuth(), user(), boardGet)
|
||||
pages.PUT("/board/:bid", jwtAuth(), user(), perm("/dashboards/put"), boardPut)
|
||||
pages.PUT("/board/:bid/configs", jwtAuth(), user(), perm("/dashboards/put"), boardPutConfigs)
|
||||
pages.DELETE("/boards", jwtAuth(), user(), perm("/dashboards/del"), boardDel)
|
||||
pages.GET("/board/:bid", auth(), user(), boardGet)
|
||||
pages.PUT("/board/:bid", auth(), user(), perm("/dashboards/put"), boardPut)
|
||||
pages.PUT("/board/:bid/configs", auth(), user(), perm("/dashboards/put"), boardPutConfigs)
|
||||
pages.DELETE("/boards", auth(), user(), perm("/dashboards/del"), boardDel)
|
||||
|
||||
// migrate v5.8.0
|
||||
pages.GET("/dashboards", jwtAuth(), admin(), migrateDashboards)
|
||||
pages.GET("/dashboard/:id", jwtAuth(), admin(), migrateDashboardGet)
|
||||
pages.PUT("/dashboard/:id/migrate", jwtAuth(), admin(), migrateDashboard)
|
||||
pages.GET("/dashboards", auth(), admin(), migrateDashboards)
|
||||
pages.GET("/dashboard/:id", auth(), admin(), migrateDashboardGet)
|
||||
pages.PUT("/dashboard/:id/migrate", auth(), admin(), migrateDashboard)
|
||||
|
||||
// deprecated ↓
|
||||
pages.GET("/dashboards/builtin/list", builtinBoardGets)
|
||||
pages.POST("/busi-group/:id/dashboards/builtin", jwtAuth(), user(), perm("/dashboards/add"), bgrw(), dashboardBuiltinImport)
|
||||
pages.GET("/busi-group/:id/dashboards", jwtAuth(), user(), perm("/dashboards"), bgro(), dashboardGets)
|
||||
pages.POST("/busi-group/:id/dashboards", jwtAuth(), user(), perm("/dashboards/add"), bgrw(), dashboardAdd)
|
||||
pages.POST("/busi-group/:id/dashboards/export", jwtAuth(), user(), perm("/dashboards"), bgro(), dashboardExport)
|
||||
pages.POST("/busi-group/:id/dashboards/import", jwtAuth(), user(), perm("/dashboards/add"), bgrw(), dashboardImport)
|
||||
pages.POST("/busi-group/:id/dashboard/:did/clone", jwtAuth(), user(), perm("/dashboards/add"), bgrw(), dashboardClone)
|
||||
pages.GET("/busi-group/:id/dashboard/:did", jwtAuth(), user(), perm("/dashboards"), bgro(), dashboardGet)
|
||||
pages.PUT("/busi-group/:id/dashboard/:did", jwtAuth(), user(), perm("/dashboards/put"), bgrw(), dashboardPut)
|
||||
pages.DELETE("/busi-group/:id/dashboard/:did", jwtAuth(), user(), perm("/dashboards/del"), bgrw(), dashboardDel)
|
||||
pages.POST("/busi-group/:id/dashboards/builtin", auth(), user(), perm("/dashboards/add"), bgrw(), dashboardBuiltinImport)
|
||||
pages.GET("/busi-group/:id/dashboards", auth(), user(), perm("/dashboards"), bgro(), dashboardGets)
|
||||
pages.POST("/busi-group/:id/dashboards", auth(), user(), perm("/dashboards/add"), bgrw(), dashboardAdd)
|
||||
pages.POST("/busi-group/:id/dashboards/export", auth(), user(), perm("/dashboards"), bgro(), dashboardExport)
|
||||
pages.POST("/busi-group/:id/dashboards/import", auth(), user(), perm("/dashboards/add"), bgrw(), dashboardImport)
|
||||
pages.POST("/busi-group/:id/dashboard/:did/clone", auth(), user(), perm("/dashboards/add"), bgrw(), dashboardClone)
|
||||
pages.GET("/busi-group/:id/dashboard/:did", auth(), user(), perm("/dashboards"), bgro(), dashboardGet)
|
||||
pages.PUT("/busi-group/:id/dashboard/:did", auth(), user(), perm("/dashboards/put"), bgrw(), dashboardPut)
|
||||
pages.DELETE("/busi-group/:id/dashboard/:did", auth(), user(), perm("/dashboards/del"), bgrw(), dashboardDel)
|
||||
|
||||
pages.GET("/busi-group/:id/chart-groups", jwtAuth(), user(), bgro(), chartGroupGets)
|
||||
pages.POST("/busi-group/:id/chart-groups", jwtAuth(), user(), bgrw(), chartGroupAdd)
|
||||
pages.PUT("/busi-group/:id/chart-groups", jwtAuth(), user(), bgrw(), chartGroupPut)
|
||||
pages.DELETE("/busi-group/:id/chart-groups", jwtAuth(), user(), bgrw(), chartGroupDel)
|
||||
pages.GET("/busi-group/:id/chart-groups", auth(), user(), bgro(), chartGroupGets)
|
||||
pages.POST("/busi-group/:id/chart-groups", auth(), user(), bgrw(), chartGroupAdd)
|
||||
pages.PUT("/busi-group/:id/chart-groups", auth(), user(), bgrw(), chartGroupPut)
|
||||
pages.DELETE("/busi-group/:id/chart-groups", auth(), user(), bgrw(), chartGroupDel)
|
||||
|
||||
pages.GET("/busi-group/:id/charts", jwtAuth(), user(), bgro(), chartGets)
|
||||
pages.POST("/busi-group/:id/charts", jwtAuth(), user(), bgrw(), chartAdd)
|
||||
pages.PUT("/busi-group/:id/charts", jwtAuth(), user(), bgrw(), chartPut)
|
||||
pages.DELETE("/busi-group/:id/charts", jwtAuth(), user(), bgrw(), chartDel)
|
||||
pages.GET("/busi-group/:id/charts", auth(), user(), bgro(), chartGets)
|
||||
pages.POST("/busi-group/:id/charts", auth(), user(), bgrw(), chartAdd)
|
||||
pages.PUT("/busi-group/:id/charts", auth(), user(), bgrw(), chartPut)
|
||||
pages.DELETE("/busi-group/:id/charts", auth(), user(), bgrw(), chartDel)
|
||||
// deprecated ↑
|
||||
|
||||
pages.GET("/share-charts", chartShareGets)
|
||||
pages.POST("/share-charts", jwtAuth(), chartShareAdd)
|
||||
pages.POST("/share-charts", auth(), chartShareAdd)
|
||||
|
||||
pages.GET("/alert-rules/builtin/list", alertRuleBuiltinList)
|
||||
pages.POST("/busi-group/:id/alert-rules/builtin", jwtAuth(), user(), perm("/alert-rules/add"), bgrw(), alertRuleBuiltinImport)
|
||||
pages.GET("/busi-group/:id/alert-rules", jwtAuth(), user(), perm("/alert-rules"), alertRuleGets)
|
||||
pages.POST("/busi-group/:id/alert-rules", jwtAuth(), user(), perm("/alert-rules/add"), bgrw(), alertRuleAddByFE)
|
||||
pages.DELETE("/busi-group/:id/alert-rules", jwtAuth(), user(), perm("/alert-rules/del"), bgrw(), alertRuleDel)
|
||||
pages.PUT("/busi-group/:id/alert-rules/fields", jwtAuth(), user(), perm("/alert-rules/put"), bgrw(), alertRulePutFields)
|
||||
pages.PUT("/busi-group/:id/alert-rule/:arid", jwtAuth(), user(), perm("/alert-rules/put"), alertRulePutByFE)
|
||||
pages.GET("/alert-rule/:arid", jwtAuth(), user(), perm("/alert-rules"), alertRuleGet)
|
||||
pages.POST("/busi-group/:id/alert-rules/builtin", auth(), user(), perm("/alert-rules/add"), bgrw(), alertRuleBuiltinImport)
|
||||
pages.GET("/busi-group/:id/alert-rules", auth(), user(), perm("/alert-rules"), alertRuleGets)
|
||||
pages.POST("/busi-group/:id/alert-rules", auth(), user(), perm("/alert-rules/add"), bgrw(), alertRuleAddByFE)
|
||||
pages.DELETE("/busi-group/:id/alert-rules", auth(), user(), perm("/alert-rules/del"), bgrw(), alertRuleDel)
|
||||
pages.PUT("/busi-group/:id/alert-rules/fields", auth(), user(), perm("/alert-rules/put"), bgrw(), alertRulePutFields)
|
||||
pages.PUT("/busi-group/:id/alert-rule/:arid", auth(), user(), perm("/alert-rules/put"), alertRulePutByFE)
|
||||
pages.GET("/alert-rule/:arid", auth(), user(), perm("/alert-rules"), alertRuleGet)
|
||||
|
||||
pages.GET("/busi-group/:id/recording-rules", jwtAuth(), user(), perm("/recording-rules"), recordingRuleGets)
|
||||
pages.POST("/busi-group/:id/recording-rules", jwtAuth(), user(), perm("/recording-rules/add"), bgrw(), recordingRuleAddByFE)
|
||||
pages.DELETE("/busi-group/:id/recording-rules", jwtAuth(), user(), perm("/recording-rules/del"), bgrw(), recordingRuleDel)
|
||||
pages.PUT("/busi-group/:id/recording-rule/:rrid", jwtAuth(), user(), perm("/recording-rules/put"), bgrw(), recordingRulePutByFE)
|
||||
pages.GET("/recording-rule/:rrid", jwtAuth(), user(), perm("/recording-rules"), recordingRuleGet)
|
||||
pages.PUT("/busi-group/:id/recording-rules/fields", jwtAuth(), user(), perm("/recording-rules/put"), recordingRulePutFields)
|
||||
pages.GET("/busi-group/:id/recording-rules", auth(), user(), perm("/recording-rules"), recordingRuleGets)
|
||||
pages.POST("/busi-group/:id/recording-rules", auth(), user(), perm("/recording-rules/add"), bgrw(), recordingRuleAddByFE)
|
||||
pages.DELETE("/busi-group/:id/recording-rules", auth(), user(), perm("/recording-rules/del"), bgrw(), recordingRuleDel)
|
||||
pages.PUT("/busi-group/:id/recording-rule/:rrid", auth(), user(), perm("/recording-rules/put"), bgrw(), recordingRulePutByFE)
|
||||
pages.GET("/recording-rule/:rrid", auth(), user(), perm("/recording-rules"), recordingRuleGet)
|
||||
pages.PUT("/busi-group/:id/recording-rules/fields", auth(), user(), perm("/recording-rules/put"), recordingRulePutFields)
|
||||
|
||||
pages.GET("/busi-group/:id/alert-mutes", jwtAuth(), user(), perm("/alert-mutes"), bgro(), alertMuteGetsByBG)
|
||||
pages.POST("/busi-group/:id/alert-mutes", jwtAuth(), user(), perm("/alert-mutes/add"), bgrw(), alertMuteAdd)
|
||||
pages.DELETE("/busi-group/:id/alert-mutes", jwtAuth(), user(), perm("/alert-mutes/del"), bgrw(), alertMuteDel)
|
||||
pages.GET("/busi-group/:id/alert-mutes", auth(), user(), perm("/alert-mutes"), bgro(), alertMuteGetsByBG)
|
||||
pages.POST("/busi-group/:id/alert-mutes", auth(), user(), perm("/alert-mutes/add"), bgrw(), alertMuteAdd)
|
||||
pages.DELETE("/busi-group/:id/alert-mutes", auth(), user(), perm("/alert-mutes/del"), bgrw(), alertMuteDel)
|
||||
|
||||
pages.GET("/busi-group/:id/alert-subscribes", jwtAuth(), user(), perm("/alert-subscribes"), bgro(), alertSubscribeGets)
|
||||
pages.GET("/alert-subscribe/:sid", jwtAuth(), user(), perm("/alert-subscribes"), alertSubscribeGet)
|
||||
pages.POST("/busi-group/:id/alert-subscribes", jwtAuth(), user(), perm("/alert-subscribes/add"), bgrw(), alertSubscribeAdd)
|
||||
pages.PUT("/busi-group/:id/alert-subscribes", jwtAuth(), user(), perm("/alert-subscribes/put"), bgrw(), alertSubscribePut)
|
||||
pages.DELETE("/busi-group/:id/alert-subscribes", jwtAuth(), user(), perm("/alert-subscribes/del"), bgrw(), alertSubscribeDel)
|
||||
pages.GET("/busi-group/:id/alert-subscribes", auth(), user(), perm("/alert-subscribes"), bgro(), alertSubscribeGets)
|
||||
pages.GET("/alert-subscribe/:sid", auth(), user(), perm("/alert-subscribes"), alertSubscribeGet)
|
||||
pages.POST("/busi-group/:id/alert-subscribes", auth(), user(), perm("/alert-subscribes/add"), bgrw(), alertSubscribeAdd)
|
||||
pages.PUT("/busi-group/:id/alert-subscribes", auth(), user(), perm("/alert-subscribes/put"), bgrw(), alertSubscribePut)
|
||||
pages.DELETE("/busi-group/:id/alert-subscribes", auth(), user(), perm("/alert-subscribes/del"), bgrw(), alertSubscribeDel)
|
||||
|
||||
if config.C.AnonymousAccess.AlertDetail {
|
||||
pages.GET("/alert-cur-event/:eid", alertCurEventGet)
|
||||
pages.GET("/alert-his-event/:eid", alertHisEventGet)
|
||||
} else {
|
||||
pages.GET("/alert-cur-event/:eid", jwtAuth(), alertCurEventGet)
|
||||
pages.GET("/alert-his-event/:eid", jwtAuth(), alertHisEventGet)
|
||||
pages.GET("/alert-cur-event/:eid", auth(), alertCurEventGet)
|
||||
pages.GET("/alert-his-event/:eid", auth(), alertHisEventGet)
|
||||
}
|
||||
|
||||
// card logic
|
||||
pages.GET("/alert-cur-events/list", jwtAuth(), alertCurEventsList)
|
||||
pages.GET("/alert-cur-events/card", jwtAuth(), alertCurEventsCard)
|
||||
pages.POST("/alert-cur-events/card/details", jwtAuth(), alertCurEventsCardDetails)
|
||||
pages.GET("/alert-his-events/list", jwtAuth(), alertHisEventsList)
|
||||
pages.DELETE("/alert-cur-events", jwtAuth(), user(), perm("/alert-cur-events/del"), alertCurEventDel)
|
||||
pages.GET("/alert-cur-events/list", auth(), alertCurEventsList)
|
||||
pages.GET("/alert-cur-events/card", auth(), alertCurEventsCard)
|
||||
pages.POST("/alert-cur-events/card/details", auth(), alertCurEventsCardDetails)
|
||||
pages.GET("/alert-his-events/list", auth(), alertHisEventsList)
|
||||
pages.DELETE("/alert-cur-events", auth(), user(), perm("/alert-cur-events/del"), alertCurEventDel)
|
||||
|
||||
pages.GET("/alert-aggr-views", jwtAuth(), alertAggrViewGets)
|
||||
pages.DELETE("/alert-aggr-views", jwtAuth(), user(), alertAggrViewDel)
|
||||
pages.POST("/alert-aggr-views", jwtAuth(), user(), alertAggrViewAdd)
|
||||
pages.PUT("/alert-aggr-views", jwtAuth(), user(), alertAggrViewPut)
|
||||
pages.GET("/alert-aggr-views", auth(), alertAggrViewGets)
|
||||
pages.DELETE("/alert-aggr-views", auth(), user(), alertAggrViewDel)
|
||||
pages.POST("/alert-aggr-views", auth(), user(), alertAggrViewAdd)
|
||||
pages.PUT("/alert-aggr-views", auth(), user(), alertAggrViewPut)
|
||||
|
||||
pages.GET("/busi-group/:id/task-tpls", jwtAuth(), user(), perm("/job-tpls"), bgro(), taskTplGets)
|
||||
pages.POST("/busi-group/:id/task-tpls", jwtAuth(), user(), perm("/job-tpls/add"), bgrw(), taskTplAdd)
|
||||
pages.DELETE("/busi-group/:id/task-tpl/:tid", jwtAuth(), user(), perm("/job-tpls/del"), bgrw(), taskTplDel)
|
||||
pages.POST("/busi-group/:id/task-tpls/tags", jwtAuth(), user(), perm("/job-tpls/put"), bgrw(), taskTplBindTags)
|
||||
pages.DELETE("/busi-group/:id/task-tpls/tags", jwtAuth(), user(), perm("/job-tpls/put"), bgrw(), taskTplUnbindTags)
|
||||
pages.GET("/busi-group/:id/task-tpl/:tid", jwtAuth(), user(), perm("/job-tpls"), bgro(), taskTplGet)
|
||||
pages.PUT("/busi-group/:id/task-tpl/:tid", jwtAuth(), user(), perm("/job-tpls/put"), bgrw(), taskTplPut)
|
||||
pages.GET("/busi-group/:id/task-tpls", auth(), user(), perm("/job-tpls"), bgro(), taskTplGets)
|
||||
pages.POST("/busi-group/:id/task-tpls", auth(), user(), perm("/job-tpls/add"), bgrw(), taskTplAdd)
|
||||
pages.DELETE("/busi-group/:id/task-tpl/:tid", auth(), user(), perm("/job-tpls/del"), bgrw(), taskTplDel)
|
||||
pages.POST("/busi-group/:id/task-tpls/tags", auth(), user(), perm("/job-tpls/put"), bgrw(), taskTplBindTags)
|
||||
pages.DELETE("/busi-group/:id/task-tpls/tags", auth(), user(), perm("/job-tpls/put"), bgrw(), taskTplUnbindTags)
|
||||
pages.GET("/busi-group/:id/task-tpl/:tid", auth(), user(), perm("/job-tpls"), bgro(), taskTplGet)
|
||||
pages.PUT("/busi-group/:id/task-tpl/:tid", auth(), user(), perm("/job-tpls/put"), bgrw(), taskTplPut)
|
||||
|
||||
pages.GET("/busi-group/:id/tasks", jwtAuth(), user(), perm("/job-tasks"), bgro(), taskGets)
|
||||
pages.POST("/busi-group/:id/tasks", jwtAuth(), user(), perm("/job-tasks/add"), bgrw(), taskAdd)
|
||||
pages.GET("/busi-group/:id/task/*url", jwtAuth(), user(), perm("/job-tasks"), taskProxy)
|
||||
pages.PUT("/busi-group/:id/task/*url", jwtAuth(), user(), perm("/job-tasks/put"), bgrw(), taskProxy)
|
||||
pages.GET("/busi-group/:id/tasks", auth(), user(), perm("/job-tasks"), bgro(), taskGets)
|
||||
pages.POST("/busi-group/:id/tasks", auth(), user(), perm("/job-tasks/add"), bgrw(), taskAdd)
|
||||
pages.GET("/busi-group/:id/task/*url", auth(), user(), perm("/job-tasks"), taskProxy)
|
||||
pages.PUT("/busi-group/:id/task/*url", auth(), user(), perm("/job-tasks/put"), bgrw(), taskProxy)
|
||||
}
|
||||
|
||||
service := r.Group("/v1/n9e")
|
||||
|
||||
@@ -74,6 +74,7 @@ func alertSubscribePut(c *gin.Context) {
|
||||
fs[i].UpdateBy = username
|
||||
fs[i].UpdateAt = timestamp
|
||||
ginx.Dangerous(fs[i].Update(
|
||||
"cluster",
|
||||
"rule_id",
|
||||
"tags",
|
||||
"redefine_severity",
|
||||
|
||||
@@ -24,6 +24,46 @@ type AccessDetails struct {
|
||||
UserIdentity string
|
||||
}
|
||||
|
||||
func handleProxyUser(c *gin.Context) *models.User {
|
||||
headerUserNameKey := config.C.ProxyAuth.HeaderUserNameKey
|
||||
username := c.GetHeader(headerUserNameKey)
|
||||
if username == "" {
|
||||
ginx.Bomb(http.StatusUnauthorized, "unauthorized")
|
||||
}
|
||||
|
||||
user, err := models.UserGetByUsername(username)
|
||||
if err != nil {
|
||||
ginx.Bomb(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
now := time.Now().Unix()
|
||||
user = &models.User{
|
||||
Username: username,
|
||||
Nickname: username,
|
||||
Roles: strings.Join(config.C.ProxyAuth.DefaultRoles, " "),
|
||||
CreateAt: now,
|
||||
UpdateAt: now,
|
||||
CreateBy: "system",
|
||||
UpdateBy: "system",
|
||||
}
|
||||
err = user.Add()
|
||||
if err != nil {
|
||||
ginx.Bomb(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func proxyAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
user := handleProxyUser(c)
|
||||
c.Set("userid", user.Id)
|
||||
c.Set("username", user)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func jwtAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
metadata, err := extractTokenMetadata(c.Request)
|
||||
@@ -54,6 +94,35 @@ func jwtAuth() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func auth() gin.HandlerFunc {
|
||||
if config.C.ProxyAuth.Enable {
|
||||
return proxyAuth()
|
||||
} else {
|
||||
return jwtAuth()
|
||||
}
|
||||
}
|
||||
|
||||
// if proxy auth is enabled, mock jwt login/logout/refresh request
|
||||
func jwtMock() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if !config.C.ProxyAuth.Enable {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if strings.Contains(c.FullPath(), "logout") {
|
||||
ginx.Bomb(http.StatusBadRequest, "logout is not supported when proxy auth is enabled")
|
||||
}
|
||||
user := handleProxyUser(c)
|
||||
ginx.NewRender(c).Data(gin.H{
|
||||
"user": user,
|
||||
"access_token": "",
|
||||
"refresh_token": "",
|
||||
}, nil)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func user() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userid := c.MustGet("userid").(int64)
|
||||
|
||||
@@ -1,18 +1,75 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
|
||||
pkgprom "github.com/didi/nightingale/v5/src/pkg/prom"
|
||||
"github.com/didi/nightingale/v5/src/webapi/config"
|
||||
"github.com/didi/nightingale/v5/src/webapi/prom"
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
type queryFormItem struct {
|
||||
Start int64 `json:"start" binding:"required"`
|
||||
End int64 `json:"end" binding:"required"`
|
||||
Step int64 `json:"step" binding:"required"`
|
||||
Query string `json:"query" binding:"required"`
|
||||
}
|
||||
|
||||
type batchQueryForm struct {
|
||||
Queries []queryFormItem `json:"queries" binding:"required"`
|
||||
}
|
||||
|
||||
func promBatchQueryRange(c *gin.Context) {
|
||||
xcluster := c.GetHeader("X-Cluster")
|
||||
if xcluster == "" {
|
||||
c.String(500, "X-Cluster is blank")
|
||||
return
|
||||
}
|
||||
|
||||
var f batchQueryForm
|
||||
err := c.BindJSON(&f)
|
||||
if err != nil {
|
||||
c.String(500, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
cluster, exist := prom.Clusters.Get(xcluster)
|
||||
if !exist {
|
||||
c.String(http.StatusBadRequest, "cluster(%s) not found", xcluster)
|
||||
return
|
||||
}
|
||||
|
||||
var lst []model.Value
|
||||
|
||||
for _, item := range f.Queries {
|
||||
r := pkgprom.Range{
|
||||
Start: time.Unix(item.Start, 0),
|
||||
End: time.Unix(item.End, 0),
|
||||
Step: time.Duration(item.Step) * time.Second,
|
||||
}
|
||||
|
||||
resp, _, err := cluster.PromClient.QueryRange(context.Background(), item.Query, r)
|
||||
if err != nil {
|
||||
c.String(500, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
lst = append(lst, resp)
|
||||
}
|
||||
|
||||
c.JSON(200, lst)
|
||||
}
|
||||
|
||||
func prometheusProxy(c *gin.Context) {
|
||||
xcluster := c.GetHeader("X-Cluster")
|
||||
if xcluster == "" {
|
||||
|
||||
Reference in New Issue
Block a user