Compare commits

..

1 Commits

Author SHA1 Message Date
Ulric Qin
92163f44e2 add configuration ForceUseServerTS 2022-08-22 23:22:37 +08:00
75 changed files with 251 additions and 3064 deletions

View File

@@ -96,8 +96,9 @@
**尊重、认可和记录每一位贡献者的工作**是夜莺开源社区的第一指导原则,我们提倡**高效的提问**,这既是对开发者时间的尊重,也是对整个社区知识沉淀的贡献:
- 提问之前请先查阅 [FAQ](https://www.gitlink.org.cn/ccfos/nightingale/wiki/faq)
- 我们使用[GitHub Discussions](https://github.com/ccfos/nightingale/discussions)作为交流论坛,有问题可以到这里搜索、提问
- 我们也推荐你加入微信群,和其他夜莺用户交流经验 (请先加好友:[UlricGO](https://www.gitlink.org.cn/UlricQin/gist/tree/master/self.jpeg) 备注:夜莺加群+姓名+公司)
- 提问之前请先搜索 [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

View File

@@ -3,5 +3,3 @@
- [xiaoziv](https://github.com/xiaoziv)
- [tanxiao1990](https://github.com/tanxiao1990)
- [bbaobelief](https://github.com/bbaobelief)
- [freedomkk-qfeng](https://github.com/freedomkk-qfeng)
- [lsy1990](https://github.com/lsy1990)

View File

@@ -1,36 +1,29 @@
[夜莺监控](https://github.com/ccfos/nightingale "夜莺监控")是一款开源云原生监控系统由滴滴设计开发2020 年 3 月份开源之后,凭借其优秀的产品设计、灵活性架构和明确清晰的定位,夜莺监控快速发展为国内最活跃的企业级云原生监控方案。[截止当前](具体指2022年8月 "截止当前"),在 [Github](https://github.com/ccfos/nightingale "Github") 上已经迭代发布了 **70** 多个版本,获得了 **5K** 多个 Star**80** 多位代码贡献者。快速的迭代,也让夜莺监控的用户群越来越大,涉及各行各业。
# 夜莺开源项目和社区治理架构(草案)
更进一步,夜莺监控于 2022 年 5 月 11 日,正式捐赠予中国计算机学会开源发展委员会 [CCF ODC](https://www.ccf.org.cn/kyfzwyh/ "CCF ODC"),为 CCF ODC 成立后接受捐赠的第一个开源项目。
## 社区架构
开源项目要更有生命力,离不开开放的治理架构和源源不断的开发者共同参与。夜莺监控项目加入 CCF 开源大家庭后,能在计算机学会的支持和带动下,进一步结合云原生、可观测、国产化等多个技术发展的需求,建立开放、中立的开源治理架构,打造更专业、有活力的开发者社区。
### 用户(User)
**今天,我们郑重发布夜莺监控开源社区治理架构,并公示相关的任命和社区荣誉,期待开源的道路上,一起同行。**
> 欢迎任何个人、公司以及组织,使用夜莺监控,并积极的反馈 bug、提交功能需求、以及相互帮助我们推荐使用 [github issue](https://github.com/ccfos/nightingale/issues) 来跟踪 bug 和管理需求。
# 夜莺监控开源社区架构
社区用户,可以通过在 **[Who is Using Nightingale](https://github.com/ccfos/nightingale/issues/897)** 登记您的使用情况,并分享您使用夜莺监控的经验,将会自动进入 **[END USERS](./end-users.md)** 列表,并获得社区的 **VIP Support**
### User|用户
### 贡献者(Contributer)
> 欢迎任何个人、公司以及组织,使用夜莺监控,并积极的反馈 bug、提交功能需求、以及相互帮助我们推荐使用 [Github Issue](https://github.com/ccfos/nightingale/issues "Github Issue") 来跟踪 bug 和管理需求。
> 欢迎每一位用户,包括但不限于以下列方式参与到夜莺开源社区并做出贡献:
社区用户,可以通过在 **[Who is Using Nightingale](https://github.com/ccfos/nightingale/issues/897 "Who is Using Nightingale")** 登记您的使用情况,并分享您使用夜莺监控的经验,将会自动进入 **[END USERS](https://github.com/ccfos/nightingale/blob/main/doc/end-users.md "END USERS")** 文件列表,并获得社区的 **VIP Support**
### Contributor|贡献者
> 欢迎每一位用户,包括但不限于以下方式参与到夜莺开源社区并做出贡献:
1. 在 [Github Issue](https://github.com/ccfos/nightingale/issues "Github Issue") 中积极参与讨论,参与社区活动;
1. 在 [github issue](https://github.com/ccfos/nightingale/issues) 中积极参与讨论,参与社区活动;
1. 提交代码补丁;
1. 翻译、修订、补充和完善[文档](https://n9e.github.io "文档")
1. 翻译、修订、补充和完善[文档](https://n9e.github.io)
1. 分享夜莺监控的使用经验,积极布道;
1. 提交建议 / 批评;
年度累计向 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale "CCFOS/NIGHTINGALE") 提交 **5** 个PR被合并或者因为其他贡献被**项目管委会**一致认可,将会自动进入到 **[ACTIVE CONTRIBUTORS](https://github.com/ccfos/nightingale/blob/main/doc/active-contributors.md "ACTIVE CONTRIBUTORS")** 列表,并获得夜莺开源社区颁发的证书,享有夜莺开源社区一定的权益和福利。
年度累计向 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale) 提交 **5** 个PR被合并或者因为其他贡献被**项目管委会**一致认可,将会自动进入到 **[ACTIVE CONTRIBUTORS](./active-contributors.md)** 列表,并获得 **[CCF ODC](https://www.ccf.org.cn/kyfzwyh/)** 颁发的电子证书,享有夜莺开源社区一定的权益和福利。
所有向 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale "CCFOS/NIGHTINGALE") 提交过PR被合并或者做出过重要贡献的 Contributor都会被永久记载于 [CONTRIBUTORS](https://github.com/ccfos/nightingale/blob/main/doc/contributors.md "CONTRIBUTORS") 列表。
### Committer|提交者
### 提交者(Committer)
> Committer 是指拥有 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale "CCFOS/NIGHTINGALE") 代码仓库写操作权限的贡献者。原则上 Committer 能够自主决策某个代码补丁是否可以合入到夜莺代码仓库,但是项目管委会拥有最终的决策权。
> Committer 是指拥有 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale) 代码仓库写操作权限的贡献者,他们拥有 ccf.org.cn 为后缀的邮箱地址(待上线)。原则上 Committer 能够自主决策某个代码补丁是否可以合入到夜莺代码仓库,但是项目管委会拥有最终的决策权。
Committer 承担以下一个或多个职责:
- 积极回应 Issues
@@ -38,43 +31,43 @@ Committer 承担以下一个或多个职责:
- 参加开发者例行会议,积极讨论项目规划和技术方案;
- 代表夜莺开源社区出席相关技术会议并做演讲;
Committer 记录并公示于 **[COMMITTERS](https://github.com/ccfos/nightingale/blob/main/doc/committers.md "COMMITTERS")** 列表,并获得夜莺开源社区颁发的证书,以及享有夜莺开源社区的各种权益和福利。
Committer 记录并公示于 **[COMMITTERS](./committers.md)** 列表,并获得 **[CCF ODC](https://www.ccf.org.cn/kyfzwyh/)** 颁发的电子证书,以及享有夜莺开源社区的各种权益和福利。
### PMC|项目管委会
### 项目管委会(PMC)
> PMC项目管委会作为一个实体,来管理和领导夜莺项目,为整个项目的发展全权负责。项目管委会相关内容记录并公示于文件[PMC](https://github.com/ccfos/nightingale/blob/main/doc/pmc.md "PMC").
> 项目管委会作为一个实体,来管理和领导夜莺项目,为整个项目的发展全权负责。项目管委会相关内容记录并公示于文件[PMC](./pmc.md).
- 项目管委会成员(PMC Member),从 Contributor 或者 Committer 中选举产生,他们拥有 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale "CCFOS/NIGHTINGALE") 代码仓库的写操作权限,拥有 Nightingale 社区相关事务的投票权、以及提名 Committer 候选人的权利。
- 项目管委会主席(PMC Chair)从项目管委会成员中投票产生。管委会主席是 **[CCF ODC](https://www.ccf.org.cn/kyfzwyh/ "CCF ODC")** 和项目管委会之间的沟通桥梁,履行特定的项目管理职责。
- 项目管委会成员(PMC Member),从 Contributor 或者 Committer 中选举产生,他们拥有 [CCFOS/NIGHTINGALE](https://github.com/ccfos/nightingale) 代码仓库的写操作权限,拥有 ccf.org.cn 为后缀的邮箱地址(待上线),拥有 Nightingale 社区相关事务的投票权、以及提名 Committer 候选人的权利。
- 项目管委会主席(PMC Chair) **[CCF ODC](https://www.ccf.org.cn/kyfzwyh/)** 从项目管委会成员中任命产生。管委会主席是 CCF ODC 和项目管委会之间的沟通桥梁,履行特定的项目管理职责。
## Communication|沟通机制
## 沟通机制(Communication)
1. 我们推荐使用邮件列表来反馈建议(待发布);
2. 我们推荐使用 [Github Issue](https://github.com/ccfos/nightingale/issues "Github Issue") 跟踪 bug 和管理需求;
3. 我们推荐使用 [Github Milestone](https://github.com/ccfos/nightingale/milestones "Github Milestone") 来管理项目进度和规划;
2. 我们推荐使用 [github issue](https://github.com/ccfos/nightingale/issues) 跟踪 bug 和管理需求;
3. 我们推荐使用 [github milestone](https://github.com/ccfos/nightingale/milestones) 来管理项目进度和规划;
4. 我们推荐使用腾讯会议来定期召开项目例会(会议 ID 待发布);
## Documentation|文档
1. 我们推荐使用 [Github Pages](https://n9e.github.io "Github Pages") 来沉淀文档;
2. 我们推荐使用 [Gitlink Wiki](https://www.gitlink.org.cn/ccfos/nightingale/wiki/faq "Gitlink Wiki") 来沉淀 FAQ
## 文档(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. 我们定期组织夜莺开发者大会,分享 [best user story](https://n9e.github.io/docs/prologue/share/ "best user story")、同步年度开发目标和计划、讨论新技术方向等;
3. 我们定期组织夜莺开发者大会,分享 best user story、同步年度开发目标和计划、讨论新技术方向等
## Philosophy|社区指导原则
## 社区指导原则(Philosophy)
>尊重、认可和记录每一位贡献者的工作。
**尊重、认可和记录每一位贡献者的工作。**
## 关于提问的原则
按照**尊重、认可、记录每一位贡献者的工作**原则,我们提倡**高效的提问**,这既是对开发者时间的尊重,也是对整个社区的知识沉淀的贡献:
1. 提问之前请先查阅 [FAQ](https://www.gitlink.org.cn/ccfos/nightingale/wiki/faq "FAQ")
2. 提问之前请先搜索 [Github Issues](https://github.com/ccfos/nightingale/issues "Github Issue")
3. 我们优先推荐通过提交 [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 "有需求建议点击这里")
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)
最后,我们推荐你加入微信群,针对相关开放式问题,相互交流咨询 (请先加好友:[UlricGO](https://www.gitlink.org.cn/UlricQin/gist/tree/master/self.jpeg "UlricGO") 备注:夜莺加群+姓名+公司,交流群里会有开发者团队和专业、热心的群友回答问题)
最后,我们推荐你加入微信群,针对相关开放式问题,相互交流咨询 (请先加好友:[UlricGO](https://www.gitlink.org.cn/UlricQin/gist/tree/master/self.jpeg) 备注:夜莺加群+姓名+公司,交流群里会有开发者团队和专业、热心的群友回答问题)

View File

@@ -1,7 +1,7 @@
### PMC Chair
## PMC Chair
- [laiwei](https://github.com/laiwei)
### PMC Co-Chair
- [UlricQin](https://github.com/UlricQin)
## PMC Member
### PMC Member
- [UlricQin](https://github.com/UlricQin)

View File

@@ -48,4 +48,4 @@ max_idle_conns_per_host = 100
enable = false
address = ":9100"
print_access = false
run_mode = "release"
run_mode = "release"

View File

@@ -41,12 +41,10 @@ CREATE TABLE `user_group` (
insert into user_group(id, name, create_at, create_by, update_at, update_by) values(1, 'demo-root-group', unix_timestamp(now()), 'root', unix_timestamp(now()), 'root');
CREATE TABLE `user_group_member` (
`id` bigint unsigned not null auto_increment,
`group_id` bigint unsigned not null,
`user_id` bigint unsigned not null,
KEY (`group_id`),
KEY (`user_id`),
PRIMARY KEY(`id`)
KEY (`user_id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
insert into user_group_member(group_id, user_id) values(1, 1);
@@ -72,12 +70,10 @@ insert into `role`(name, note) values('Standard', 'Ordinary user role');
insert into `role`(name, note) values('Guest', 'Readonly user role');
CREATE TABLE `role_operation`(
`id` bigint unsigned not null auto_increment,
`role_name` varchar(128) not null,
`operation` varchar(191) not null,
KEY (`role_name`),
KEY (`operation`),
PRIMARY KEY(`id`)
KEY (`operation`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- Admin is special, who has no concrete operation but can do anything.
@@ -165,16 +161,13 @@ CREATE TABLE `board` (
`id` bigint unsigned not null auto_increment,
`group_id` bigint not null default 0 comment 'busi group id',
`name` varchar(191) not null,
`ident` varchar(200) not null default '',
`tags` varchar(255) not null comment 'split by space',
`public` tinyint(1) not null default 0 comment '0:false 1:true',
`create_at` bigint not null default 0,
`create_by` varchar(64) not null default '',
`update_at` bigint not null default 0,
`update_by` varchar(64) not null default '',
PRIMARY KEY (`id`),
UNIQUE KEY (`group_id`, `name`),
KEY(`ident`)
UNIQUE KEY (`group_id`, `name`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- for dashboard new version
@@ -272,18 +265,14 @@ CREATE TABLE `alert_mute` (
`id` bigint unsigned not null auto_increment,
`group_id` bigint not null default 0 comment 'busi group id',
`prod` varchar(255) not null default '',
`note` varchar(1024) not null default '',
`cate` varchar(128) not null,
`cluster` varchar(128) not null,
`tags` varchar(4096) not null default '' comment 'json,map,tagkey->regexp|value',
`cause` varchar(255) not null default '',
`btime` bigint not null default 0 comment 'begin time',
`etime` bigint not null default 0 comment 'end time',
`disabled` tinyint(1) not null default 0 comment '0:enabled 1:disabled',
`create_at` bigint not null default 0,
`create_by` varchar(64) not null default '',
`update_at` bigint not null default 0,
`update_by` varchar(64) not null default '',
PRIMARY KEY (`id`),
KEY (`create_at`),
KEY (`group_id`)
@@ -291,8 +280,6 @@ CREATE TABLE `alert_mute` (
CREATE TABLE `alert_subscribe` (
`id` bigint unsigned not null auto_increment,
`name` varchar(255) not null default '',
`disabled` tinyint(1) not null default 0 comment '0:enabled 1:disabled',
`group_id` bigint not null default 0 comment 'busi group id',
`cate` varchar(128) not null,
`cluster` varchar(128) not null,
@@ -366,7 +353,7 @@ CREATE TABLE `recording_rule` (
`cluster` varchar(128) not null,
`name` varchar(255) not null comment 'new metric name',
`note` varchar(255) not null comment 'rule note',
`disabled` tinyint(1) not null default 0 comment '0:enabled 1:disabled',
`disabled` tinyint(1) not null comment '0:enabled 1:disabled',
`prom_ql` varchar(8192) not null comment 'promql',
`prom_eval_interval` int not null comment 'evaluate interval',
`append_tags` varchar(255) default '' comment 'split by space: service=n9e mod=api',

View File

@@ -43,11 +43,9 @@ CREATE INDEX user_group_update_at_idx ON user_group (update_at);
insert into user_group(id, name, create_at, create_by, update_at, update_by) values(1, 'demo-root-group', date_part('epoch',current_timestamp)::int, 'root', date_part('epoch',current_timestamp)::int, 'root');
CREATE TABLE user_group_member (
id bigserial,
group_id bigint not null,
user_id bigint not null
) ;
ALTER TABLE user_group_member ADD CONSTRAINT user_group_member_pk PRIMARY KEY (id);
CREATE INDEX user_group_member_group_id_idx ON user_group_member (group_id);
CREATE INDEX user_group_member_user_id_idx ON user_group_member (user_id);
@@ -74,11 +72,9 @@ insert into role(name, note) values('Standard', 'Ordinary user role');
insert into role(name, note) values('Guest', 'Readonly user role');
CREATE TABLE role_operation(
id bigserial,
role_name varchar(128) not null,
operation varchar(191) not null
) ;
ALTER TABLE role_operation ADD CONSTRAINT role_operation_pk PRIMARY KEY (id);
CREATE INDEX role_operation_role_name_idx ON role_operation (role_name);
CREATE INDEX role_operation_operation_idx ON role_operation (operation);
@@ -198,9 +194,7 @@ CREATE TABLE board (
id bigserial not null ,
group_id bigint not null default 0 ,
name varchar(191) not null,
ident varchar(200) not null default '',
tags varchar(255) not null ,
public smallint not null default 0,
create_at bigint not null default 0,
create_by varchar(64) not null default '',
update_at bigint not null default 0,
@@ -210,8 +204,6 @@ ALTER TABLE board ADD CONSTRAINT board_pk PRIMARY KEY (id);
ALTER TABLE board ADD CONSTRAINT board_un UNIQUE (group_id,"name");
COMMENT ON COLUMN board.group_id IS 'busi group id';
COMMENT ON COLUMN board.tags IS 'split by space';
COMMENT ON COLUMN board.public IS '0:false 1:true';
CREATE INDEX board_ident_idx ON board (ident);
-- for dashboard new version
CREATE TABLE board_payload (
@@ -267,7 +259,6 @@ CREATE INDEX chart_share_create_at_idx ON chart_share (create_at);
CREATE TABLE alert_rule (
id bigserial NOT NULL,
group_id int8 NOT NULL DEFAULT 0,
cate varchar(128) not null default '' ,
"cluster" varchar(128) NOT NULL,
"name" varchar(255) NOT NULL,
note varchar(1024) NOT NULL,
@@ -323,19 +314,14 @@ COMMENT ON COLUMN alert_rule.append_tags IS 'split by space: service=n9e mod=api
CREATE TABLE alert_mute (
id bigserial,
group_id bigint not null default 0 ,
cate varchar(128) not null default '' ,
prod varchar(255) NOT NULL DEFAULT '' ,
note varchar(1024) not null default '',
cluster varchar(128) not null,
tags varchar(4096) not null default '' ,
cause varchar(255) not null default '',
btime bigint not null default 0 ,
etime bigint not null default 0 ,
disabled smallint not null default 0 ,
create_at bigint not null default 0,
create_by varchar(64) not null default '',
update_at bigint not null default 0,
update_by varchar(64) not null default ''
create_by varchar(64) not null default ''
) ;
ALTER TABLE alert_mute ADD CONSTRAINT alert_mute_pk PRIMARY KEY (id);
CREATE INDEX alert_mute_create_at_idx ON alert_mute (create_at);
@@ -344,14 +330,10 @@ COMMENT ON COLUMN alert_mute.group_id IS 'busi group id';
COMMENT ON COLUMN alert_mute.tags IS 'json,map,tagkey->regexp|value';
COMMENT ON COLUMN alert_mute.btime IS 'begin time';
COMMENT ON COLUMN alert_mute.etime IS 'end time';
COMMENT ON COLUMN alert_mute.disabled IS '0:enabled 1:disabled';
CREATE TABLE alert_subscribe (
id bigserial,
"name" varchar(255) NOT NULL default '',
disabled int2 NOT NULL default 0 ,
group_id bigint not null default 0 ,
cate varchar(128) not null default '' ,
cluster varchar(128) not null,
rule_id bigint not null default 0,
tags jsonb not null ,
@@ -368,7 +350,6 @@ CREATE TABLE alert_subscribe (
ALTER TABLE alert_subscribe ADD CONSTRAINT alert_subscribe_pk PRIMARY KEY (id);
CREATE INDEX alert_subscribe_group_id_idx ON alert_subscribe (group_id);
CREATE INDEX alert_subscribe_update_at_idx ON alert_subscribe (update_at);
COMMENT ON COLUMN alert_subscribe.disabled IS '0:enabled 1:disabled';
COMMENT ON COLUMN alert_subscribe.group_id IS 'busi group id';
COMMENT ON COLUMN alert_subscribe.tags IS 'json,map,tagkey->regexp|value';
COMMENT ON COLUMN alert_subscribe.redefine_severity IS 'is redefine severity?';
@@ -435,7 +416,6 @@ insert into alert_aggr_view(name, rule, cate) values('By RuleName', 'field:rule_
CREATE TABLE alert_cur_event (
id bigserial NOT NULL,
cate varchar(128) not null default '' ,
"cluster" varchar(128) NOT NULL,
group_id int8 NOT NULL,
group_name varchar(255) NOT NULL DEFAULT ''::character varying,
@@ -489,7 +469,6 @@ COMMENT ON COLUMN alert_cur_event.tags IS 'merge data_tags rule_tags, split by ,
CREATE TABLE alert_his_event (
id bigserial NOT NULL,
is_recovered int2 NOT NULL,
cate varchar(128) not null default '' ,
"cluster" varchar(128) NOT NULL,
group_id int8 NOT NULL,
group_name varchar(255) NOT NULL DEFAULT ''::character varying,

View File

@@ -23,11 +23,6 @@ class Sender(object):
def send_feishu(cls, payload):
# already done in go code
pass
@classmethod
def send_mm(cls, payload):
# already done in go code
pass
@classmethod
def send_sms(cls, payload):

View File

@@ -9,12 +9,7 @@ ClusterName = "Default"
BusiGroupLabelKey = "busigroup"
# sleep x seconds, then start judge engine
EngineDelay = 60
DisableUsageReport = false
# config | database
ReaderFrom = "config"
EngineDelay = 120
[Log]
# log write dir
@@ -73,12 +68,10 @@ InsecureSkipVerify = true
Batch = 5
[Alerting]
# timeout settings, unit: ms, default: 30000ms
Timeout=30000
TemplatesDir = "./etc/template"
NotifyConcurrency = 10
# use builtin go code notify
NotifyBuiltinChannels = ["email", "dingtalk", "wecom", "feishu", "mm"]
NotifyBuiltinChannels = ["email", "dingtalk", "wecom", "feishu"]
[Alerting.CallScript]
# built in sending capability in go code
@@ -90,8 +83,7 @@ ScriptPath = "./etc/script/notify.py"
Enable = false
# use a plugin via `go build -buildmode=plugin -o notify.so`
PluginPath = "./etc/script/notify.so"
# The first letter must be capitalized to be exported
Caller = "N9eCaller"
Caller = "n9eCaller"
[Alerting.RedisPub]
Enable = false
@@ -109,7 +101,7 @@ Headers = ["Content-Type", "application/json", "X-From", "N9E"]
[NoData]
Metric = "target_up"
# unit: second
Interval = 120
Interval = 15
[Ibex]
# callback: ${ibex}/${tplid}/${host}
@@ -144,7 +136,7 @@ MaxIdleConns = 50
# table prefix
TablePrefix = ""
# enable auto migrate or not
# EnableAutoMigrate = false
EnableAutoMigrate = false
[Reader]
# prometheus base url
@@ -155,18 +147,23 @@ BasicAuthUser = ""
BasicAuthPass = ""
# timeout settings, unit: ms
Timeout = 30000
DialTimeout = 3000
MaxIdleConnsPerHost = 100
DialTimeout = 10000
TLSHandshakeTimeout = 30000
ExpectContinueTimeout = 1000
IdleConnTimeout = 90000
# time duration, unit: ms
KeepAlive = 30000
MaxConnsPerHost = 0
MaxIdleConns = 100
MaxIdleConnsPerHost = 10
[WriterOpt]
# queue channel count
QueueCount = 1000
QueueCount = 100
# queue max size
QueueMaxSize = 1000000
QueueMaxSize = 200000
# once pop samples number from queue
QueuePopSize = 1000
# metric or ident
ShardingKey = "ident"
QueuePopSize = 2000
[[Writers]]
Url = "http://prometheus:9090/api/v1/write"
@@ -175,8 +172,8 @@ BasicAuthUser = ""
# Basic auth password
BasicAuthPass = ""
# timeout settings, unit: ms
Timeout = 10000
DialTimeout = 3000
Timeout = 30000
DialTimeout = 10000
TLSHandshakeTimeout = 30000
ExpectContinueTimeout = 1000
IdleConnTimeout = 90000
@@ -185,12 +182,6 @@ KeepAlive = 30000
MaxConnsPerHost = 0
MaxIdleConns = 100
MaxIdleConnsPerHost = 100
# [[Writers.WriteRelabels]]
# Action = "replace"
# SourceLabels = ["__address__"]
# Regex = "([^:]+)(?::\\d+)?"
# Replacement = "$1:80"
# TargetLabel = "__address__"
# [[Writers]]
# Url = "http://m3db:7201/api/v1/prom/remote/write"

View File

@@ -4,21 +4,12 @@ RunMode = "release"
# # custom i18n dict config
# I18N = "./etc/i18n.json"
# # custom i18n request header key
# I18NHeaderKey = "X-Language"
# metrics descriptions
MetricsYamlFile = "./etc/metrics.yaml"
BuiltinAlertsDir = "./etc/alerts"
BuiltinDashboardsDir = "./etc/dashboards"
# config | api
ClustersFrom = "config"
# using when ClustersFrom = "api"
ClustersFromAPIs = []
[[NotifyChannels]]
Label = "邮箱"
# do not change Key
@@ -39,11 +30,6 @@ Label = "飞书机器人"
# do not change Key
Key = "feishu"
[[NotifyChannels]]
Label = "mm bot"
# do not change Key
Key = "mm"
[[ContactKeys]]
Label = "Wecom Robot Token"
# do not change Key
@@ -59,11 +45,6 @@ Label = "Feishu Robot Token"
# do not change Key
Key = "feishu_robot_token"
[[ContactKeys]]
Label = "MatterMost Webhook URL"
# do not change Key
Key = "mm_webhook_url"
[Log]
# log write dir
Dir = "logs"
@@ -111,13 +92,6 @@ 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"
@@ -147,20 +121,6 @@ Nickname = "cn"
Phone = "mobile"
Email = "mail"
[OIDC]
Enable = false
RedirectURL = "http://n9e.com/callback"
SsoAddr = "http://sso.example.org"
ClientId = ""
ClientSecret = ""
CoverAttributes = true
DefaultRoles = ["Standard"]
[OIDC.Attributes]
Nickname = "nickname"
Phone = "phone_number"
Email = "email"
[Redis]
# address, ip:port
Address = "redis:6379"
@@ -185,7 +145,7 @@ MaxIdleConns = 50
# table prefix
TablePrefix = ""
# enable auto migrate or not
# EnableAutoMigrate = false
EnableAutoMigrate = false
[[Clusters]]
# Prometheus cluster name
@@ -198,7 +158,14 @@ BasicAuthUser = ""
BasicAuthPass = ""
# timeout settings, unit: ms
Timeout = 30000
DialTimeout = 3000
DialTimeout = 10000
TLSHandshakeTimeout = 30000
ExpectContinueTimeout = 1000
IdleConnTimeout = 90000
# time duration, unit: ms
KeepAlive = 30000
MaxConnsPerHost = 0
MaxIdleConns = 100
MaxIdleConnsPerHost = 100
[Ibex]
@@ -213,4 +180,4 @@ Timeout = 3000
TargetUp = '''max(max_over_time(target_up{ident=~"(%s)"}[%dm])) by (ident)'''
LoadPerCore = '''max(max_over_time(system_load_norm_1{ident=~"(%s)"}[%dm])) by (ident)'''
MemUtil = '''100-max(max_over_time(mem_available_percent{ident=~"(%s)"}[%dm])) by (ident)'''
DiskUtil = '''max(max_over_time(disk_used_percent{ident=~"(%s)", path="/"}[%dm])) by (ident)'''
DiskUtil = '''max(max_over_time(disk_used_percent{ident=~"(%s)", path="/"}[%dm])) by (ident)'''

View File

@@ -24,11 +24,6 @@ class Sender(object):
# already done in go code
pass
@classmethod
def send_mm(cls, payload):
# already done in go code
pass
@classmethod
def send_sms(cls, payload):
users = payload.get('event').get("notify_users_obj")

View File

@@ -9,7 +9,7 @@ ClusterName = "Default"
BusiGroupLabelKey = "busigroup"
# sleep x seconds, then start judge engine
EngineDelay = 60
EngineDelay = 120
DisableUsageReport = false
@@ -73,12 +73,10 @@ InsecureSkipVerify = true
Batch = 5
[Alerting]
# timeout settings, unit: ms, default: 30000ms
Timeout=30000
TemplatesDir = "./etc/template"
NotifyConcurrency = 10
# use builtin go code notify
NotifyBuiltinChannels = ["email", "dingtalk", "wecom", "feishu", "mm"]
NotifyBuiltinChannels = ["email", "dingtalk", "wecom", "feishu"]
[Alerting.CallScript]
# built in sending capability in go code
@@ -109,7 +107,7 @@ Headers = ["Content-Type", "application/json", "X-From", "N9E"]
[NoData]
Metric = "target_up"
# unit: second
Interval = 120
Interval = 15
[Ibex]
# callback: ${ibex}/${tplid}/${host}
@@ -165,13 +163,11 @@ MaxIdleConnsPerHost = 100
[WriterOpt]
# queue channel count
QueueCount = 1000
QueueCount = 100
# queue max size
QueueMaxSize = 1000000
QueueMaxSize = 200000
# once pop samples number from queue
QueuePopSize = 1000
# metric or ident
ShardingKey = "ident"
QueuePopSize = 2000
[[Writers]]
Url = "http://127.0.0.1:9090/api/v1/write"
@@ -180,7 +176,6 @@ BasicAuthUser = ""
# Basic auth password
BasicAuthPass = ""
# timeout settings, unit: ms
Headers = ["X-From", "n9e"]
Timeout = 10000
DialTimeout = 3000
TLSHandshakeTimeout = 30000

View File

@@ -1,7 +0,0 @@
级别状态: S{{.Severity}} {{if .IsRecovered}}Recovered{{else}}Triggered{{end}}
规则名称: {{.RuleName}}{{if .RuleNote}}
规则备注: {{.RuleNote}}{{end}}
监控指标: {{.TagsJSON}}
{{if .IsRecovered}}恢复时间:{{timeformat .LastEvalTime}}{{else}}触发时间: {{timeformat .TriggerTime}}
触发时值: {{.TriggerValue}}{{end}}
发送时间: {{timestamp}}

View File

@@ -1,9 +1,7 @@
**级别状态**: {{if .IsRecovered}}<font color="info">S{{.Severity}} Recovered</font>{{else}}<font color="warning">S{{.Severity}} Triggered</font>{{end}}
**规则标题**: {{.RuleName}}{{if .RuleNote}}
**规则备注**: {{.RuleNote}}{{end}}{{if .TargetIdent}}
**监控对象**: {{.TargetIdent}}{{end}}
**监控指标**: {{.TagsJSON}}{{if not .IsRecovered}}
**规则备注**: {{.RuleNote}}{{end}}
**监控指标**: {{.TagsJSON}}
{{if .IsRecovered}}**恢复时间**{{timeformat .LastEvalTime}}{{else}}**触发时间**: {{timeformat .TriggerTime}}
**触发时值**: {{.TriggerValue}}{{end}}
{{if .IsRecovered}}**恢复时间**: {{timeformat .LastEvalTime}}{{else}}**首次触发时间**: {{timeformat .FirstTriggerTime}}{{end}}
{{$time_duration := sub now.Unix .FirstTriggerTime }}{{if .IsRecovered}}{{$time_duration = sub .LastEvalTime .FirstTriggerTime }}{{end}}**持续时长**: {{humanizeDurationInterface $time_duration}}
**发送时间**: {{timestamp}}

View File

@@ -39,11 +39,6 @@ Label = "飞书机器人"
# do not change Key
Key = "feishu"
[[NotifyChannels]]
Label = "mm bot"
# do not change Key
Key = "mm"
[[ContactKeys]]
Label = "Wecom Robot Token"
# do not change Key
@@ -59,11 +54,6 @@ Label = "Feishu Robot Token"
# do not change Key
Key = "feishu_robot_token"
[[ContactKeys]]
Label = "MatterMost Webhook URL"
# do not change Key
Key = "mm_webhook_url"
[Log]
# log write dir
Dir = "logs"
@@ -149,7 +139,6 @@ Email = "mail"
[OIDC]
Enable = false
DiaplayName = "OIDC登录"
RedirectURL = "http://n9e.com/callback"
SsoAddr = "http://sso.example.org"
ClientId = ""
@@ -162,52 +151,6 @@ Nickname = "nickname"
Phone = "phone_number"
Email = "email"
[CAS]
Enable = false
DiaplayName = "CAS登录"
SsoAddr = "https://cas.example.com/cas/"
RedirectURL = "http://127.0.0.1:18000/callback/cas"
CoverAttributes = false
# cas user default roles
DefaultRoles = ["Standard"]
[CAS.Attributes]
Nickname = "nickname"
Phone = "phone_number"
Email = "email"
[OAuth]
Enable = false
DisplayName = "OAuth2登录"
RedirectURL = "http://127.0.0.1:18000/callback/oauth"
SsoAddr = "https://sso.example.com/oauth2/authorize"
TokenAddr = "https://sso.example.com/oauth2/token"
UserInfoAddr = "https://api.example.com/api/v1/user/info"
ClientId = ""
ClientSecret = ""
CoverAttributes = true
DefaultRoles = ["Standard"]
UserinfoIsArray = false
UserinfoPrefix = "data"
Scopes = ["profile", "email", "phone"]
[OAuth.Attributes]
# Username must be defined
Username = "username"
Nickname = "nickname"
Phone = "phone_number"
Email = "email"
# example
# # nested : UserinfoIsArray=false, UserinfoPrefix="data"
# # {"data":{"username":"123456","nickname":"姓名"},"code":0,"message":"ok"}
# # nested and array : UserinfoIsArray=true, UserinfoPrefix="data"
# # {"data":[{"username":"123456","nickname":"姓名"}],"code":0,"message":"ok"}
# # flat : UserinfoIsArray=false, UserinfoPrefix=""
# # {"username":"123456","nickname":"姓名"}
# # flat and array : UserinfoIsArray=true, UserinfoPrefix=""
# # [{"username":"123456","nickname":"姓名"}]
[Redis]
# address, ip:port or ip1:port,ip2:port for cluster and sentinel(SentinelAddrs)
Address = "127.0.0.1:6379"
@@ -251,7 +194,6 @@ BasicAuthPass = ""
Timeout = 30000
DialTimeout = 3000
MaxIdleConnsPerHost = 100
Headers = ["X-From", "n9e"]
[Ibex]
Address = "http://127.0.0.1:10090"
@@ -265,4 +207,4 @@ Timeout = 3000
TargetUp = '''max(max_over_time(target_up{ident=~"(%s)"}[%dm])) by (ident)'''
LoadPerCore = '''max(max_over_time(system_load_norm_1{ident=~"(%s)"}[%dm])) by (ident)'''
MemUtil = '''100-max(max_over_time(mem_available_percent{ident=~"(%s)"}[%dm])) by (ident)'''
DiskUtil = '''max(max_over_time(disk_used_percent{ident=~"(%s)", path="/"}[%dm])) by (ident)'''
DiskUtil = '''max(max_over_time(disk_used_percent{ident=~"(%s)", path="/"}[%dm])) by (ident)'''

10
go.mod
View File

@@ -8,7 +8,7 @@ require (
github.com/gin-contrib/pprof v1.3.0
github.com/gin-gonic/gin v1.7.4
github.com/go-ldap/ldap/v3 v3.4.1
github.com/go-redis/redis/v9 v9.0.0-rc.1
github.com/go-redis/redis/v8 v8.11.3
github.com/gogo/protobuf v1.3.2
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang/protobuf v1.5.2
@@ -16,7 +16,6 @@ require (
github.com/google/uuid v1.3.0
github.com/json-iterator/go v1.1.12
github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7
github.com/mailru/easyjson v0.7.7
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
@@ -24,7 +23,7 @@ require (
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.3.1-0.20220824084030-9f9f830a05d5
github.com/toolkits/pkg v1.2.9
github.com/urfave/cli/v2 v2.3.0
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -59,7 +58,6 @@ require (
github.com/jackc/pgx/v4 v4.13.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -75,8 +73,8 @@ require (
github.com/ugorji/go/codec v1.1.7 // indirect
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-20220722155237-a158d28d115b // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.6 // indirect
google.golang.org/genproto v0.0.0-20211007155348-82e027067bd4 // indirect

54
go.sum
View File

@@ -89,7 +89,9 @@ github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=
@@ -121,11 +123,12 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-redis/redis/v9 v9.0.0-rc.1 h1:/+bS+yeUnanqAbuD3QwlejzQZ+4eqgfUtFTG4b+QnXs=
github.com/go-redis/redis/v9 v9.0.0-rc.1/go.mod h1:8et+z03j0l8N+DvsVnclzjf3Dl/pFHgRk+2Ct1qw66A=
github.com/go-redis/redis/v8 v8.11.3 h1:GCjoYp8c+yQTJfc0n69iwSiHjvuAdruxl7elnZCxgt8=
github.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -174,7 +177,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
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=
@@ -195,6 +199,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4
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=
@@ -245,8 +250,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -279,8 +282,6 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -298,9 +299,17 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.21.1 h1:OB/euWYIExnPBohllTicTHmGTrMaqJ67nIu80j0/uEM=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc h1:Ak86L+yDSOzKFa7WM5bf5itSOo1e3Xh8bm5YCMUXIjQ=
github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -364,16 +373,16 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w=
github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/toolkits/pkg v1.3.1-0.20220824084030-9f9f830a05d5 h1:kMCwr2gNHjHEVgw+uNVdiPbGadj4TekbIfrTXElZeI0=
github.com/toolkits/pkg v1.3.1-0.20220824084030-9f9f830a05d5/go.mod h1:PvTBg/UxazPgBz6VaCM7FM7kJldjfVrsuN6k4HT/VuY=
github.com/toolkits/pkg v1.2.9 h1:zGlrJDl+2sMBoxBRIoMtAwvKmW5wctuji2+qHCecMKk=
github.com/toolkits/pkg v1.2.9/go.mod h1:ZUsQAOoaR99PSbes+RXSirvwmtd6+XIUvizCmrjfUYc=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
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=
@@ -449,6 +458,7 @@ 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=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -472,6 +482,7 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
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=
@@ -479,9 +490,10 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
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-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -501,6 +513,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
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=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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=
@@ -514,8 +527,11 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
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=
@@ -537,15 +553,15 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
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=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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=
@@ -607,12 +623,14 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
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=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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=
@@ -708,12 +726,14 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -723,8 +743,8 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M=
gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM=
gorm.io/driver/postgres v1.1.1 h1:tWLmqYCyaoh89fi7DhM6QggujrOnmfo3H98AzgNAAu0=

View File

@@ -34,11 +34,6 @@ func newWebapiCmd() *cli.Command {
Aliases: []string{"c"},
Usage: "specify configuration file(.json,.yaml,.toml)",
},
&cli.StringFlag{
Name: "key",
Aliases: []string{"k"},
Usage: "specify the secret key for configuration file field encryption",
},
},
Action: func(c *cli.Context) error {
printEnv()
@@ -48,9 +43,6 @@ func newWebapiCmd() *cli.Command {
opts = append(opts, webapi.SetConfigFile(c.String("conf")))
}
opts = append(opts, webapi.SetVersion(version.VERSION))
if c.String("key") != "" {
opts = append(opts, webapi.SetKey(c.String("key")))
}
webapi.Run(opts...)
return nil
@@ -68,11 +60,6 @@ func newServerCmd() *cli.Command {
Aliases: []string{"c"},
Usage: "specify configuration file(.json,.yaml,.toml)",
},
&cli.StringFlag{
Name: "key",
Aliases: []string{"k"},
Usage: "specify the secret key for configuration file field encryption",
},
},
Action: func(c *cli.Context) error {
printEnv()
@@ -82,9 +69,6 @@ func newServerCmd() *cli.Command {
opts = append(opts, server.SetConfigFile(c.String("conf")))
}
opts = append(opts, server.SetVersion(version.VERSION))
if c.String("key") != "" {
opts = append(opts, server.SetKey(c.String("key")))
}
server.Run(opts...)
return nil

View File

@@ -63,11 +63,10 @@ type AggrRule struct {
Value string
}
func (e *AlertCurEvent) ParseRule(field string) error {
f := e.GetField(field)
f = strings.TrimSpace(f)
func (e *AlertCurEvent) ParseRuleNote() error {
e.RuleNote = strings.TrimSpace(e.RuleNote)
if f == "" {
if e.RuleNote == "" {
return nil
}
@@ -76,7 +75,7 @@ func (e *AlertCurEvent) ParseRule(field string) error {
"{{$value := .TriggerValue}}",
}
text := strings.Join(append(defs, f), "")
text := strings.Join(append(defs, e.RuleNote), "")
t, err := template.New(fmt.Sprint(e.RuleId)).Funcs(tplx.TemplateFuncMap).Parse(text)
if err != nil {
return err
@@ -88,13 +87,7 @@ func (e *AlertCurEvent) ParseRule(field string) error {
return err
}
if field == "rule_name" {
e.RuleName = body.String()
}
if field == "rule_note" {
e.RuleNote = body.String()
}
e.RuleNote = body.String()
return nil
}
@@ -140,8 +133,6 @@ func (e *AlertCurEvent) GetField(field string) string {
return fmt.Sprint(e.RuleId)
case "rule_name":
return e.RuleName
case "rule_note":
return e.RuleNote
case "severity":
return fmt.Sprint(e.Severity)
case "runbook_url":

View File

@@ -22,7 +22,6 @@ type TagFilter struct {
type AlertMute struct {
Id int64 `json:"id" gorm:"primaryKey"`
GroupId int64 `json:"group_id"`
Note string `json:"note"`
Cate string `json:"cate"`
Prod string `json:"prod"` // product empty means n9e
Cluster string `json:"cluster"` // take effect by clusters, seperated by space
@@ -30,11 +29,8 @@ type AlertMute struct {
Cause string `json:"cause"`
Btime int64 `json:"btime"`
Etime int64 `json:"etime"`
Disabled int `json:"disabled"` // 0: enabled, 1: disabled
CreateBy string `json:"create_by"`
UpdateBy string `json:"update_by"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
ITags []TagFilter `json:"-" gorm:"-"` // inner tags
}
@@ -42,24 +38,6 @@ func (m *AlertMute) TableName() string {
return "alert_mute"
}
func AlertMuteGetById(id int64) (*AlertMute, error) {
return AlertMuteGet("id=?", id)
}
func AlertMuteGet(where string, args ...interface{}) (*AlertMute, error) {
var lst []*AlertMute
err := DB().Where(where, args...).Find(&lst).Error
if err != nil {
return nil, err
}
if len(lst) == 0 {
return nil, nil
}
return lst[0], nil
}
func AlertMuteGets(prods []string, bgid int64, query string) (lst []AlertMute, err error) {
session := DB().Where("group_id = ? and prod in (?)", bgid, prods)
@@ -140,25 +118,6 @@ func (m *AlertMute) Add() error {
return Insert(m)
}
func (m *AlertMute) Update(arm AlertMute) error {
arm.Id = m.Id
arm.GroupId = m.GroupId
arm.CreateAt = m.CreateAt
arm.CreateBy = m.CreateBy
arm.UpdateAt = time.Now().Unix()
err := arm.Verify()
if err != nil {
return err
}
return DB().Model(m).Select("*").Updates(arm).Error
}
func (m *AlertMute) UpdateFieldsMap(fields map[string]interface{}) error {
return DB().Model(m).Updates(fields).Error
}
func AlertMuteDel(ids []int64) error {
if len(ids) == 0 {
return nil
@@ -167,20 +126,13 @@ func AlertMuteDel(ids []int64) error {
}
func AlertMuteStatistics(cluster string) (*Statistics, error) {
// clean expired first
buf := int64(30)
err := DB().Where("etime < ?", time.Now().Unix()-buf).Delete(new(AlertMute)).Error
if err != nil {
return nil, err
}
session := DB().Model(&AlertMute{}).Select("count(*) as total", "max(update_at) as last_updated")
session := DB().Model(&AlertMute{}).Select("count(*) as total", "max(create_at) as last_updated")
if cluster != "" {
session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll)
}
var stats []*Statistics
err = session.Find(&stats).Error
err := session.Find(&stats).Error
if err != nil {
return nil, err
}
@@ -189,6 +141,13 @@ func AlertMuteStatistics(cluster string) (*Statistics, error) {
}
func AlertMuteGetsByCluster(cluster string) ([]*AlertMute, error) {
// clean expired first
buf := int64(30)
err := DB().Where("etime < ?", time.Now().Unix()+buf).Delete(new(AlertMute)).Error
if err != nil {
return nil, err
}
// get my cluster's mutes
session := DB().Model(&AlertMute{})
if cluster != "" {
@@ -197,7 +156,7 @@ func AlertMuteGetsByCluster(cluster string) ([]*AlertMute, error) {
var lst []*AlertMute
var mlst []*AlertMute
err := session.Find(&lst).Error
err = session.Find(&lst).Error
if err != nil {
return nil, err
}

View File

@@ -13,8 +13,6 @@ import (
type AlertSubscribe struct {
Id int64 `json:"id" gorm:"primaryKey"`
Name string `json:"name"` // AlertSubscribe name
Disabled int `json:"disabled"` // 0: enabled, 1: disabled
GroupId int64 `json:"group_id"`
Cate string `json:"cate"`
Cluster string `json:"cluster"` // take effect by clusters, seperated by space
@@ -57,10 +55,6 @@ func AlertSubscribeGet(where string, args ...interface{}) (*AlertSubscribe, erro
return lst[0], nil
}
func (s *AlertSubscribe) IsDisabled() bool {
return s.Disabled == 1
}
func (s *AlertSubscribe) Verify() error {
if s.Cluster == "" {
return errors.New("cluster invalid")

View File

@@ -72,7 +72,7 @@ func AlertingEngineGetsInstances(where string, args ...interface{}) ([]string, e
return arr, err
}
func AlertingEngineHeartbeat(instance, cluster string) error {
func AlertingEngineHeartbeat(instance string) error {
var total int64
err := DB().Model(new(AlertingEngines)).Where("instance=?", instance).Count(&total).Error
if err != nil {
@@ -83,13 +83,11 @@ func AlertingEngineHeartbeat(instance, cluster string) error {
// insert
err = DB().Create(&AlertingEngines{
Instance: instance,
Cluster: cluster,
Clock: time.Now().Unix(),
}).Error
} else {
// updates
fields := map[string]interface{}{"clock": time.Now().Unix(), "cluster": cluster}
err = DB().Model(new(AlertingEngines)).Where("instance=?", instance).Updates(fields).Error
// update
err = DB().Model(new(AlertingEngines)).Where("instance=?", instance).Update("clock", time.Now().Unix()).Error
}
return err

View File

@@ -13,14 +13,12 @@ type Board struct {
Id int64 `json:"id" gorm:"primaryKey"`
GroupId int64 `json:"group_id"`
Name string `json:"name"`
Ident string `json:"ident"`
Tags string `json:"tags"`
CreateAt int64 `json:"create_at"`
CreateBy string `json:"create_by"`
UpdateAt int64 `json:"update_at"`
UpdateBy string `json:"update_by"`
Configs string `json:"configs" gorm:"-"`
Public int `json:"public"` // 0: false, 1: true
}
func (b *Board) TableName() string {
@@ -39,36 +37,11 @@ func (b *Board) Verify() error {
return nil
}
func (b *Board) CanRenameIdent(ident string) (bool, error) {
if ident == "" {
return true, nil
}
cnt, err := Count(DB().Model(b).Where("ident=? and id <> ?", ident, b.Id))
if err != nil {
return false, err
}
return cnt == 0, nil
}
func (b *Board) Add() error {
if err := b.Verify(); err != nil {
return err
}
if b.Ident != "" {
// ident duplicate check
cnt, err := Count(DB().Model(b).Where("ident=?", b.Ident))
if err != nil {
return err
}
if cnt > 0 {
return errors.New("Ident duplicate")
}
}
now := time.Now().Unix()
b.CreateAt = now
b.UpdateAt = now

View File

@@ -74,62 +74,7 @@ func ConfigsSet(ckey, cval string) error {
return err
}
func ConfigGet(id int64) (*Configs, error) {
var objs []*Configs
err := DB().Where("id=?", id).Find(&objs).Error
if len(objs) == 0 {
return nil, nil
}
return objs[0], err
}
func ConfigsGets(prefix string, limit, offset int) ([]*Configs, error) {
var objs []*Configs
session := DB()
if prefix != "" {
session = session.Where("ckey like ?", prefix+"%")
}
err := session.Order("id desc").Limit(limit).Offset(offset).Find(&objs).Error
return objs, err
}
func (c *Configs) Add() error {
num, err := Count(DB().Model(&Configs{}).Where("ckey=?", c.Ckey))
if err != nil {
return errors.WithMessage(err, "failed to count configs")
}
if num > 0 {
return errors.WithMessage(err, "key is exists")
}
// insert
err = DB().Create(&Configs{
Ckey: c.Ckey,
Cval: c.Cval,
}).Error
return err
}
func (c *Configs) Update() error {
num, err := Count(DB().Model(&Configs{}).Where("id<>? and ckey=?", c.Id, c.Ckey))
if err != nil {
return errors.WithMessage(err, "failed to count configs")
}
if num > 0 {
return errors.WithMessage(err, "key is exists")
}
err = DB().Model(&Configs{}).Where("id=?", c.Id).Updates(c).Error
return err
}
func ConfigsDel(ids []int64) error {
return DB().Where("id in ?", ids).Delete(&Configs{}).Error
}
func ConfigsGetsByKey(ckeys []string) (map[string]string, error) {
func ConfigsGets(ckeys []string) (map[string]string, error) {
var objs []Configs
err := DB().Where("ckey in ?", ckeys).Find(&objs).Error
if err != nil {

View File

@@ -95,7 +95,7 @@ func (u *User) Update(selectField interface{}, selectFields ...interface{}) erro
return err
}
return DB().Model(u).Select(selectField, selectFields...).Updates(u).Error
return DB().Model(u).Select(selectField, selectFields).Updates(u).Error
}
func (u *User) UpdateAllFields() error {
@@ -462,10 +462,6 @@ func (u *User) BusiGroups(limit int, query string, all ...bool) ([]BusiGroup, er
return lst, err
}
if t == nil {
return lst, nil
}
err = DB().Order("name").Limit(limit).Where("id=?", t.GroupId).Find(&lst).Error
}

View File

@@ -1,150 +0,0 @@
package cas
import (
"bytes"
"context"
"net/url"
"strings"
"time"
"github.com/didi/nightingale/v5/src/storage"
"github.com/google/uuid"
"github.com/toolkits/pkg/cas"
"github.com/toolkits/pkg/logger"
)
type Config struct {
Enable bool
SsoAddr string
RedirectURL string
DisplayName string
CoverAttributes bool
Attributes struct {
Nickname string
Phone string
Email string
}
DefaultRoles []string
}
type ssoClient struct {
config Config
ssoAddr string
callbackAddr string
displayName string
attributes struct {
nickname string
phone string
email string
}
}
var (
cli ssoClient
)
func Init(cf Config) {
if !cf.Enable {
return
}
cli = ssoClient{}
cli.config = cf
cli.ssoAddr = cf.SsoAddr
cli.callbackAddr = cf.RedirectURL
cli.displayName = cf.DisplayName
cli.attributes.nickname = cf.Attributes.Nickname
cli.attributes.phone = cf.Attributes.Phone
cli.attributes.email = cf.Attributes.Email
}
func GetDisplayName() string {
return cli.displayName
}
// Authorize return the cas authorize location and state
func Authorize(redirect string) (string, string, error) {
state := uuid.New().String()
ctx := context.Background()
err := storage.Redis.Set(ctx, wrapStateKey(state), redirect, time.Duration(300*time.Second)).Err()
if err != nil {
return "", "", err
}
return cli.genRedirectURL(state), state, nil
}
func fetchRedirect(ctx context.Context, state string) (string, error) {
return storage.Redis.Get(ctx, wrapStateKey(state)).Result()
}
func deleteRedirect(ctx context.Context, state string) error {
return storage.Redis.Del(ctx, wrapStateKey(state)).Err()
}
func wrapStateKey(key string) string {
return "n9e_cas_" + key
}
func (cli *ssoClient) genRedirectURL(state string) string {
var buf bytes.Buffer
buf.WriteString(cli.ssoAddr + "login")
v := url.Values{
"service": {cli.callbackAddr},
}
if strings.Contains(cli.ssoAddr, "?") {
buf.WriteByte('&')
} else {
buf.WriteByte('?')
}
buf.WriteString(v.Encode())
return buf.String()
}
type CallbackOutput struct {
Redirect string `json:"redirect"`
Msg string `json:"msg"`
AccessToken string `json:"accessToken"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Phone string `yaml:"phone"`
Email string `yaml:"email"`
}
func ValidateServiceTicket(ctx context.Context, ticket, state string) (ret *CallbackOutput, err error) {
casUrl, err := url.Parse(cli.config.SsoAddr)
if err != nil {
logger.Error(err)
return
}
serviceUrl, err := url.Parse(cli.callbackAddr)
if err != nil {
logger.Error(err)
return
}
resOptions := &cas.RestOptions{
CasURL: casUrl,
ServiceURL: serviceUrl,
}
resCli := cas.NewRestClient(resOptions)
authRet, err := resCli.ValidateServiceTicket(cas.ServiceTicket(ticket))
if err != nil {
logger.Errorf("Ticket Validating Failed: %s", err)
return
}
ret = &CallbackOutput{}
ret.Username = authRet.User
ret.Nickname = authRet.Attributes.Get(cli.attributes.nickname)
logger.Debugf("CAS Authentication Response's Attributes--[Nickname]: %s", ret.Nickname)
ret.Email = authRet.Attributes.Get(cli.attributes.email)
logger.Debugf("CAS Authentication Response's Attributes--[Email]: %s", ret.Email)
ret.Phone = authRet.Attributes.Get(cli.attributes.phone)
logger.Debugf("CAS Authentication Response's Attributes--[Phone]: %s", ret.Phone)
ret.Redirect, err = fetchRedirect(ctx, state)
if err != nil {
logger.Debugf("get redirect err:%s state:%s", state, err)
}
err = deleteRedirect(ctx, state)
if err != nil {
logger.Debugf("delete redirect err:%s state:%s", state, err)
}
return
}

View File

@@ -1,205 +0,0 @@
package oauth2x
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/didi/nightingale/v5/src/storage"
"github.com/toolkits/pkg/logger"
"github.com/google/uuid"
jsoniter "github.com/json-iterator/go"
"golang.org/x/oauth2"
)
type ssoClient struct {
config oauth2.Config
ssoAddr string
userInfoAddr string
callbackAddr string
displayName string
coverAttributes bool
attributes struct {
username string
nickname string
phone string
email string
}
userinfoIsArray bool
userinfoPrefix string
}
type Config struct {
Enable bool
DisplayName string
RedirectURL string
SsoAddr string
TokenAddr string
UserInfoAddr string
ClientId string
ClientSecret string
CoverAttributes bool
Attributes struct {
Username string
Nickname string
Phone string
Email string
}
DefaultRoles []string
UserinfoIsArray bool
UserinfoPrefix string
Scopes []string
}
var (
cli ssoClient
)
func Init(cf Config) {
if !cf.Enable {
return
}
cli.ssoAddr = cf.SsoAddr
cli.userInfoAddr = cf.UserInfoAddr
cli.callbackAddr = cf.RedirectURL
cli.displayName = cf.DisplayName
cli.coverAttributes = cf.CoverAttributes
cli.attributes.username = cf.Attributes.Username
cli.attributes.nickname = cf.Attributes.Nickname
cli.attributes.phone = cf.Attributes.Phone
cli.attributes.email = cf.Attributes.Email
cli.userinfoIsArray = cf.UserinfoIsArray
cli.userinfoPrefix = cf.UserinfoPrefix
cli.config = oauth2.Config{
ClientID: cf.ClientId,
ClientSecret: cf.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: cf.SsoAddr,
TokenURL: cf.TokenAddr,
},
RedirectURL: cf.RedirectURL,
Scopes: cf.Scopes,
}
}
func GetDisplayName() string {
return cli.displayName
}
func wrapStateKey(key string) string {
return "n9e_oauth_" + key
}
// Authorize return the sso authorize location with state
func Authorize(redirect string) (string, error) {
state := uuid.New().String()
ctx := context.Background()
err := storage.Redis.Set(ctx, wrapStateKey(state), redirect, time.Duration(300*time.Second)).Err()
if err != nil {
return "", err
}
return cli.config.AuthCodeURL(state), nil
}
func fetchRedirect(ctx context.Context, state string) (string, error) {
return storage.Redis.Get(ctx, wrapStateKey(state)).Result()
}
func deleteRedirect(ctx context.Context, state string) error {
return storage.Redis.Del(ctx, wrapStateKey(state)).Err()
}
// Callback 用 code 兑换 accessToken 以及 用户信息
func Callback(ctx context.Context, code, state string) (*CallbackOutput, error) {
ret, err := exchangeUser(code)
if err != nil {
return nil, fmt.Errorf("ilegal user:%v", err)
}
ret.Redirect, err = fetchRedirect(ctx, state)
if err != nil {
logger.Errorf("get redirect err:%v code:%s state:%s", code, state, err)
}
err = deleteRedirect(ctx, state)
if err != nil {
logger.Errorf("delete redirect err:%v code:%s state:%s", code, state, err)
}
return ret, nil
}
type CallbackOutput struct {
Redirect string `json:"redirect"`
Msg string `json:"msg"`
AccessToken string `json:"accessToken"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Phone string `yaml:"phone"`
Email string `yaml:"email"`
}
func exchangeUser(code string) (*CallbackOutput, error) {
ctx := context.Background()
oauth2Token, err := cli.config.Exchange(ctx, code)
if err != nil {
return nil, fmt.Errorf("failed to exchange token: %s", err)
}
userInfo, err := getUserInfo(cli.userInfoAddr, oauth2Token.AccessToken)
if err != nil {
logger.Errorf("failed to get user info: %s", err)
return nil, fmt.Errorf("failed to get user info: %s", err)
}
return &CallbackOutput{
AccessToken: oauth2Token.AccessToken,
Username: getUserinfoField(userInfo, cli.userinfoIsArray, cli.userinfoPrefix, cli.attributes.username),
Nickname: getUserinfoField(userInfo, cli.userinfoIsArray, cli.userinfoPrefix, cli.attributes.nickname),
Phone: getUserinfoField(userInfo, cli.userinfoIsArray, cli.userinfoPrefix, cli.attributes.phone),
Email: getUserinfoField(userInfo, cli.userinfoIsArray, cli.userinfoPrefix, cli.attributes.email),
}, nil
}
func getUserInfo(userInfoAddr, accessToken string) ([]byte, error) {
r, err := http.NewRequest("GET", userInfoAddr, nil)
if err != nil {
return nil, err
}
r.Header.Add("Authorization", "Bearer "+accessToken)
resp, err := http.DefaultClient.Do(r)
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, nil
}
return body, err
}
func getUserinfoField(input []byte, isArray bool, prefix, field string) string {
if prefix == "" {
if isArray {
return jsoniter.Get(input, 0).Get(field).ToString()
} else {
return jsoniter.Get(input, field).ToString()
}
} else {
if isArray {
return jsoniter.Get(input, prefix, 0).Get(field).ToString()
} else {
return jsoniter.Get(input, prefix).Get(field).ToString()
}
}
}

View File

@@ -20,7 +20,6 @@ type ssoClient struct {
ssoAddr string
callbackAddr string
coverAttributes bool
displayName string
attributes struct {
username string
nickname string
@@ -31,7 +30,6 @@ type ssoClient struct {
type Config struct {
Enable bool
DisplayName string
RedirectURL string
SsoAddr string
ClientId string
@@ -61,7 +59,6 @@ func Init(cf Config) {
cli.attributes.nickname = cf.Attributes.Nickname
cli.attributes.phone = cf.Attributes.Phone
cli.attributes.email = cf.Attributes.Email
cli.displayName = cf.DisplayName
provider, err := oidc.NewProvider(context.Background(), cf.SsoAddr)
if err != nil {
log.Fatal(err)
@@ -80,10 +77,6 @@ func Init(cf Config) {
}
}
func GetDisplayName() string {
return cli.displayName
}
func wrapStateKey(key string) string {
return "n9e_oidc_" + key
}

View File

@@ -1,100 +0,0 @@
package secu
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"strings"
)
// BASE64StdEncode base64编码
func BASE64StdEncode(src []byte) string {
return base64.StdEncoding.EncodeToString(src)
}
// BASE64StdDecode base64解码
func BASE64StdDecode(src string) ([]byte, error) {
dst, err := base64.StdEncoding.DecodeString(src)
if err != nil {
return nil, err
}
return dst, nil
}
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func PKCS7UnPadding(originData []byte) []byte {
length := len(originData)
unpadding := int(originData[length-1])
return originData[:(length - unpadding)]
}
//AES加密
func AesEncrypt(origData, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
//加密块填充
blockSize := block.BlockSize()
padOrigData := PKCS7Padding(origData, blockSize)
//初始化CBC加密
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
crypted := make([]byte, len(padOrigData))
//加密
blockMode.CryptBlocks(crypted, padOrigData)
return crypted, nil
}
//AES解密
func AesDecrypt(crypted, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
origData := make([]byte, len(crypted))
//解密
blockMode.CryptBlocks(origData, crypted)
//去除填充
origData = PKCS7UnPadding(origData)
return origData, nil
}
// 针对配置文件属性进行解密处理
func DealWithDecrypt(src string, key string) (string, error) {
//如果是{{cipher}}前缀,则代表是加密过的属性,先解密
if strings.HasPrefix(src, "{{cipher}}") {
data := src[10:]
decodeData, err := BASE64StdDecode(data)
if err != nil {
return src, err
}
//解密
origin, err := AesDecrypt(decodeData, []byte(key))
if err != nil {
return src, err
}
//返回明文
return string(origin), nil
} else {
return src, nil
}
}
// 针对配置文件属性进行加密处理
func DealWithEncrypt(src string, key string) (string, error) {
encrypted, err := AesEncrypt([]byte(src), []byte(key))
if err != nil {
return src, err
}
data := BASE64StdEncode(encrypted)
return "{{cipher}}" + data, nil
}

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"html/template"
"math"
"reflect"
"regexp"
"strconv"
"time"
@@ -34,10 +33,6 @@ func Timestamp(pattern ...string) string {
return time.Now().Format(defp)
}
func Now() time.Time {
return time.Now()
}
func Args(args ...interface{}) map[string]interface{} {
result := make(map[string]interface{})
for i, a := range args {
@@ -100,27 +95,11 @@ func Humanize1024(s string) string {
return fmt.Sprintf("%.4g%s", v, prefix)
}
func ToString(v interface{}) string {
return fmt.Sprint(v)
}
func HumanizeDuration(s string) string {
v, err := strconv.ParseFloat(s, 64)
if err != nil {
return s
}
return HumanizeDurationFloat64(v)
}
func HumanizeDurationInterface(i interface{}) string {
f, err := ToFloat64(i)
if err != nil {
return ToString(i)
}
return HumanizeDurationFloat64(f)
}
func HumanizeDurationFloat64(v float64) string {
if math.IsNaN(v) || math.IsInf(v, 0) {
return fmt.Sprintf("%.4g", v)
}
@@ -176,179 +155,3 @@ func HumanizePercentageH(s string) string {
}
return fmt.Sprintf("%.2f%%", v)
}
// Add returns the sum of a and b.
func Add(a, b interface{}) (interface{}, error) {
av := reflect.ValueOf(a)
bv := reflect.ValueOf(b)
switch av.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return av.Int() + bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Int() + int64(bv.Uint()), nil
case reflect.Float32, reflect.Float64:
return float64(av.Int()) + bv.Float(), nil
default:
return nil, fmt.Errorf("add: unknown type for %q (%T)", bv, b)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return int64(av.Uint()) + bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Uint() + bv.Uint(), nil
case reflect.Float32, reflect.Float64:
return float64(av.Uint()) + bv.Float(), nil
default:
return nil, fmt.Errorf("add: unknown type for %q (%T)", bv, b)
}
case reflect.Float32, reflect.Float64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return av.Float() + float64(bv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Float() + float64(bv.Uint()), nil
case reflect.Float32, reflect.Float64:
return av.Float() + bv.Float(), nil
default:
return nil, fmt.Errorf("add: unknown type for %q (%T)", bv, b)
}
default:
return nil, fmt.Errorf("add: unknown type for %q (%T)", av, a)
}
}
// Subtract returns the difference of b from a.
func Subtract(a, b interface{}) (interface{}, error) {
av := reflect.ValueOf(a)
bv := reflect.ValueOf(b)
switch av.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return av.Int() - bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Int() - int64(bv.Uint()), nil
case reflect.Float32, reflect.Float64:
return float64(av.Int()) - bv.Float(), nil
default:
return nil, fmt.Errorf("subtract: unknown type for %q (%T)", bv, b)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return int64(av.Uint()) - bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Uint() - bv.Uint(), nil
case reflect.Float32, reflect.Float64:
return float64(av.Uint()) - bv.Float(), nil
default:
return nil, fmt.Errorf("subtract: unknown type for %q (%T)", bv, b)
}
case reflect.Float32, reflect.Float64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return av.Float() - float64(bv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Float() - float64(bv.Uint()), nil
case reflect.Float32, reflect.Float64:
return av.Float() - bv.Float(), nil
default:
return nil, fmt.Errorf("subtract: unknown type for %q (%T)", bv, b)
}
default:
return nil, fmt.Errorf("subtract: unknown type for %q (%T)", av, a)
}
}
// Multiply returns the product of a and b.
func Multiply(a, b interface{}) (interface{}, error) {
av := reflect.ValueOf(a)
bv := reflect.ValueOf(b)
switch av.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return av.Int() * bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Int() * int64(bv.Uint()), nil
case reflect.Float32, reflect.Float64:
return float64(av.Int()) * bv.Float(), nil
default:
return nil, fmt.Errorf("multiply: unknown type for %q (%T)", bv, b)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return int64(av.Uint()) * bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Uint() * bv.Uint(), nil
case reflect.Float32, reflect.Float64:
return float64(av.Uint()) * bv.Float(), nil
default:
return nil, fmt.Errorf("multiply: unknown type for %q (%T)", bv, b)
}
case reflect.Float32, reflect.Float64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return av.Float() * float64(bv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Float() * float64(bv.Uint()), nil
case reflect.Float32, reflect.Float64:
return av.Float() * bv.Float(), nil
default:
return nil, fmt.Errorf("multiply: unknown type for %q (%T)", bv, b)
}
default:
return nil, fmt.Errorf("multiply: unknown type for %q (%T)", av, a)
}
}
// Divide returns the division of b from a.
func Divide(a, b interface{}) (interface{}, error) {
av := reflect.ValueOf(a)
bv := reflect.ValueOf(b)
switch av.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return av.Int() / bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Int() / int64(bv.Uint()), nil
case reflect.Float32, reflect.Float64:
return float64(av.Int()) / bv.Float(), nil
default:
return nil, fmt.Errorf("divide: unknown type for %q (%T)", bv, b)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return int64(av.Uint()) / bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Uint() / bv.Uint(), nil
case reflect.Float32, reflect.Float64:
return float64(av.Uint()) / bv.Float(), nil
default:
return nil, fmt.Errorf("divide: unknown type for %q (%T)", bv, b)
}
case reflect.Float32, reflect.Float64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return av.Float() / float64(bv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return av.Float() / float64(bv.Uint()), nil
case reflect.Float32, reflect.Float64:
return av.Float() / bv.Float(), nil
default:
return nil, fmt.Errorf("divide: unknown type for %q (%T)", bv, b)
}
default:
return nil, fmt.Errorf("divide: unknown type for %q (%T)", av, a)
}
}

View File

@@ -1,73 +0,0 @@
package tplx
import (
"fmt"
"strconv"
)
// ToFloat64 convert interface to float64
func ToFloat64(val interface{}) (float64, error) {
switch v := val.(type) {
case string:
if f, err := strconv.ParseFloat(v, 64); err == nil {
return f, nil
}
// try int
if i, err := strconv.ParseInt(v, 0, 64); err == nil {
return float64(i), nil
}
// try bool
b, err := strconv.ParseBool(v)
if err == nil {
if b {
return 1, nil
} else {
return 0, nil
}
}
if v == "Yes" || v == "yes" || v == "YES" || v == "Y" || v == "ON" || v == "on" || v == "On" || v == "ok" || v == "up" {
return 1, nil
}
if v == "No" || v == "no" || v == "NO" || v == "N" || v == "OFF" || v == "off" || v == "Off" || v == "fail" || v == "err" || v == "down" {
return 0, nil
}
return 0, fmt.Errorf("unparseable value %v", v)
case float64:
return v, nil
case uint64:
return float64(v), nil
case uint32:
return float64(v), nil
case uint16:
return float64(v), nil
case uint8:
return float64(v), nil
case uint:
return float64(v), nil
case int64:
return float64(v), nil
case int32:
return float64(v), nil
case int16:
return float64(v), nil
case int8:
return float64(v), nil
case bool:
if v {
return 1, nil
} else {
return 0, nil
}
case int:
return float64(v), nil
case float32:
return float64(v), nil
default:
return strconv.ParseFloat(fmt.Sprint(v), 64)
}
}

View File

@@ -8,27 +8,20 @@ import (
)
var TemplateFuncMap = template.FuncMap{
"escape": url.PathEscape,
"unescaped": Unescaped,
"urlconvert": Urlconvert,
"timeformat": Timeformat,
"timestamp": Timestamp,
"args": Args,
"reReplaceAll": ReReplaceAll,
"match": regexp.MatchString,
"toUpper": strings.ToUpper,
"toLower": strings.ToLower,
"contains": strings.Contains,
"humanize": Humanize,
"humanize1024": Humanize1024,
"humanizeDuration": HumanizeDuration,
"humanizeDurationInterface": HumanizeDurationInterface,
"humanizePercentage": HumanizePercentage,
"humanizePercentageH": HumanizePercentageH,
"add": Add,
"sub": Subtract,
"mul": Multiply,
"div": Divide,
"now": Now,
"toString": ToString,
"escape": url.PathEscape,
"unescaped": Unescaped,
"urlconvert": Urlconvert,
"timeformat": Timeformat,
"timestamp": Timestamp,
"args": Args,
"reReplaceAll": ReReplaceAll,
"match": regexp.MatchString,
"toUpper": strings.ToUpper,
"toLower": strings.ToLower,
"contains": strings.Contains,
"humanize": Humanize,
"humanize1024": Humanize1024,
"humanizeDuration": HumanizeDuration,
"humanizePercentage": HumanizePercentage,
"humanizePercentageH": HumanizePercentageH,
}

View File

@@ -96,9 +96,6 @@ func labelsToLabelsProto(labels model.Metric, rule *models.RecordingRule) (resul
}
result = append(result, nameLs)
for k, v := range labels {
if k == LabelName {
continue
}
if model.LabelNameRE.MatchString(string(k)) {
result = append(result, &prompb.Label{
Name: string(k),

View File

@@ -50,9 +50,6 @@ func SendDingtalk(message DingtalkMessage) {
}
ur := "https://oapi.dingtalk.com/robot/send?access_token=" + u.Path
if strings.HasPrefix(message.Tokens[i], "https://") {
ur = message.Tokens[i]
}
body := dingtalk{
Msgtype: "markdown",
Markdown: dingtalkMarkdown{

View File

@@ -1,7 +1,6 @@
package sender
import (
"strings"
"time"
"github.com/didi/nightingale/v5/src/pkg/poster"
@@ -32,9 +31,6 @@ type feishu struct {
func SendFeishu(message FeishuMessage) {
for i := 0; i < len(message.Tokens); i++ {
url := "https://open.feishu.cn/open-apis/bot/v2/hook/" + message.Tokens[i]
if strings.HasPrefix(message.Tokens[i], "https://") {
url = message.Tokens[i]
}
body := feishu{
Msgtype: "text",
Content: feishuContent{

View File

@@ -1,73 +0,0 @@
package sender
import (
"net/url"
"strings"
"time"
"github.com/didi/nightingale/v5/src/pkg/poster"
"github.com/toolkits/pkg/logger"
)
type MatterMostMessage struct {
Text string
Tokens []string
}
type mm struct {
Channel string `json:"channel"`
Username string `json:"username"`
Text string `json:"text"`
}
func MapStrToStr(arr []string, fn func(s string) string) []string {
var newArray = []string{}
for _, it := range arr {
newArray = append(newArray, fn(it))
}
return newArray
}
func SendMM(message MatterMostMessage) {
for i := 0; i < len(message.Tokens); i++ {
u, err := url.Parse(message.Tokens[i])
if err != nil {
logger.Errorf("mm_sender: failed to parse error=%v", err)
}
v, err := url.ParseQuery(u.RawQuery)
if err != nil {
logger.Errorf("mm_sender: failed to parse query error=%v", err)
}
channels := v["channel"] // do not get
txt := ""
atuser := v["atuser"]
if len(atuser) != 0 {
txt = strings.Join(MapStrToStr(atuser, func(u string) string {
return "@" + u
}), ",") + "\n"
}
username := v.Get("username")
if err != nil {
logger.Errorf("mm_sender: failed to parse error=%v", err)
}
// simple concatenating
ur := u.Scheme + "://" + u.Host + u.Path
for _, channel := range channels {
body := mm{
Channel: channel,
Username: username,
Text: txt + message.Text,
}
res, code, err := poster.PostJSON(ur, time.Second*5, body, 3)
if err != nil {
logger.Errorf("mm_sender: result=fail url=%s code=%d error=%v response=%s", ur, code, err, string(res))
} else {
logger.Infof("mm_sender: result=succ url=%s code=%d response=%s", ur, code, string(res))
}
}
}
}

View File

@@ -1,7 +1,6 @@
package sender
import (
"strings"
"time"
"github.com/didi/nightingale/v5/src/pkg/poster"
@@ -25,9 +24,6 @@ type wecom struct {
func SendWecom(message WecomMessage) {
for i := 0; i < len(message.Tokens); i++ {
url := "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=" + message.Tokens[i]
if strings.HasPrefix(message.Tokens[i], "https://") {
url = message.Tokens[i]
}
body := wecom{
Msgtype: "markdown",
Markdown: wecomMarkdown{

View File

@@ -19,7 +19,6 @@ import (
"github.com/didi/nightingale/v5/src/pkg/httpx"
"github.com/didi/nightingale/v5/src/pkg/logx"
"github.com/didi/nightingale/v5/src/pkg/ormx"
"github.com/didi/nightingale/v5/src/pkg/secu"
"github.com/didi/nightingale/v5/src/storage"
)
@@ -28,61 +27,7 @@ var (
once sync.Once
)
func DealConfigCrypto(key string) {
decryptDsn, err := secu.DealWithDecrypt(C.DB.DSN, key)
if err != nil {
fmt.Println("failed to decrypt the db dsn", err)
os.Exit(1)
}
C.DB.DSN = decryptDsn
decryptRedisPwd, err := secu.DealWithDecrypt(C.Redis.Password, key)
if err != nil {
fmt.Println("failed to decrypt the redis password", err)
os.Exit(1)
}
C.Redis.Password = decryptRedisPwd
decryptSmtpPwd, err := secu.DealWithDecrypt(C.SMTP.Pass, key)
if err != nil {
fmt.Println("failed to decrypt the smtp password", err)
os.Exit(1)
}
C.SMTP.Pass = decryptSmtpPwd
decryptHookPwd, err := secu.DealWithDecrypt(C.Alerting.Webhook.BasicAuthPass, key)
if err != nil {
fmt.Println("failed to decrypt the alert webhook password", err)
os.Exit(1)
}
C.Alerting.Webhook.BasicAuthPass = decryptHookPwd
decryptIbexPwd, err := secu.DealWithDecrypt(C.Ibex.BasicAuthPass, key)
if err != nil {
fmt.Println("failed to decrypt the ibex password", err)
os.Exit(1)
}
C.Ibex.BasicAuthPass = decryptIbexPwd
decryptReaderPwd, err := secu.DealWithDecrypt(C.Reader.BasicAuthPass, key)
if err != nil {
fmt.Println("failed to decrypt the reader password", err)
os.Exit(1)
}
C.Reader.BasicAuthPass = decryptReaderPwd
for index, v := range C.Writers {
decryptWriterPwd, err := secu.DealWithDecrypt(v.BasicAuthPass, key)
if err != nil {
fmt.Printf("failed to decrypt the writer password: %s , error: %s", v.BasicAuthPass, err.Error())
os.Exit(1)
}
C.Writers[index].BasicAuthPass = decryptWriterPwd
}
}
func MustLoad(key string, fpaths ...string) {
func MustLoad(fpaths ...string) {
once.Do(func() {
loaders := []multiconfig.Loader{
&multiconfig.TagLoader{},
@@ -121,8 +66,6 @@ func MustLoad(key string, fpaths ...string) {
}
m.MustLoad(C)
DealConfigCrypto(key)
if C.EngineDelay == 0 {
C.EngineDelay = 120
}
@@ -131,11 +74,6 @@ func MustLoad(key string, fpaths ...string) {
C.ReaderFrom = "config"
}
if C.ReaderFrom == "config" && C.ClusterName == "" {
fmt.Println("configuration ClusterName is blank")
os.Exit(1)
}
if C.Heartbeat.IP == "" {
// auto detect
// C.Heartbeat.IP = fmt.Sprint(GetOutboundIP())
@@ -202,7 +140,7 @@ func MustLoad(key string, fpaths ...string) {
}
if C.WriterOpt.QueueMaxSize <= 0 {
C.WriterOpt.QueueMaxSize = 10000000
C.WriterOpt.QueueMaxSize = 100000
}
if C.WriterOpt.QueuePopSize <= 0 {
@@ -210,11 +148,7 @@ func MustLoad(key string, fpaths ...string) {
}
if C.WriterOpt.QueueCount <= 0 {
C.WriterOpt.QueueCount = 1000
}
if C.WriterOpt.ShardingKey == "" {
C.WriterOpt.ShardingKey = "ident"
C.WriterOpt.QueueCount = 100
}
for _, write := range C.Writers {
@@ -297,7 +231,6 @@ type WriterGlobalOpt struct {
QueueCount int
QueueMaxSize int
QueuePopSize int
ShardingKey string
}
type HeartbeatConfig struct {
@@ -317,7 +250,6 @@ type SMTPConfig struct {
}
type Alerting struct {
Timeout int64
TemplatesDir string
NotifyConcurrency int
NotifyBuiltinChannels []string

View File

@@ -45,11 +45,7 @@ func consume(events []interface{}, sema *semaphore.Semaphore) {
func consumeOne(event *models.AlertCurEvent) {
LogEvent(event, "consume")
if err := event.ParseRule("rule_name"); err != nil {
event.RuleName = fmt.Sprintf("failed to parse rule name: %v", err)
}
if err := event.ParseRule("rule_note"); err != nil {
if err := event.ParseRuleNote(); err != nil {
event.RuleNote = fmt.Sprintf("failed to parse rule note: %v", err)
}

View File

@@ -22,10 +22,6 @@ func IsMuted(event *models.AlertCurEvent, clock ...int64) bool {
}
func matchMute(event *models.AlertCurEvent, mute *models.AlertMute, clock ...int64) bool {
if mute.Disabled == 1 {
return false
}
ts := event.TriggerTime
if len(clock) > 0 {
ts = clock[0]

View File

@@ -125,7 +125,6 @@ func handleNotice(notice Notice, bs []byte) {
wecomset := make(map[string]struct{})
dingtalkset := make(map[string]struct{})
feishuset := make(map[string]struct{})
mmset := make(map[string]struct{})
for _, user := range notice.Event.NotifyUsersObj {
if user.Email != "" {
@@ -156,11 +155,6 @@ func handleNotice(notice Notice, bs []byte) {
if ret.Exists() {
feishuset[ret.String()] = struct{}{}
}
ret = gjson.GetBytes(bs, "mm_webhook_url")
if ret.Exists() {
mmset[ret.String()] = struct{}{}
}
}
phones := StringSetKeys(phoneset)
@@ -242,23 +236,6 @@ func handleNotice(notice Notice, bs []byte) {
AtMobiles: phones,
Tokens: StringSetKeys(feishuset),
})
case "mm":
if len(mmset) == 0 {
continue
}
if !slice.ContainsString(config.C.Alerting.NotifyBuiltinChannels, "mm") {
continue
}
content, has := notice.Tpls["mm.tpl"]
if !has {
content = "mm.tpl not found"
}
sender.SendMM(sender.MatterMostMessage{
Text: content,
Tokens: StringSetKeys(mmset),
})
}
}
}
@@ -351,10 +328,6 @@ func handleSubscribes(event models.AlertCurEvent, subs []*models.AlertSubscribe)
}
func handleSubscribe(event models.AlertCurEvent, sub *models.AlertSubscribe) {
if sub.IsDisabled() {
return
}
if !matchTags(event.TagsMap, sub.ITags) {
return
}
@@ -398,10 +371,6 @@ func alertingCallScript(stdinBytes []byte) {
return
}
if config.C.Alerting.Timeout == 0 {
config.C.Alerting.Timeout = 30000
}
fpath := config.C.Alerting.CallScript.ScriptPath
cmd := exec.Command(fpath)
cmd.Stdin = bytes.NewReader(stdinBytes)
@@ -417,7 +386,7 @@ func alertingCallScript(stdinBytes []byte) {
return
}
err, isTimeout := sys.WrapTimeout(cmd, time.Duration(config.C.Alerting.Timeout)*time.Millisecond)
err, isTimeout := sys.WrapTimeout(cmd, time.Duration(30)*time.Second)
if isTimeout {
if err == nil {

View File

@@ -64,7 +64,6 @@ func notifyMaintainerWithBuiltin(title, msg, triggerTime string, users []*models
wecomset := make(map[string]struct{})
dingtalkset := make(map[string]struct{})
feishuset := make(map[string]struct{})
mmset := make(map[string]struct{})
for _, user := range users {
if user.Email != "" {
@@ -95,11 +94,6 @@ func notifyMaintainerWithBuiltin(title, msg, triggerTime string, users []*models
if ret.Exists() {
feishuset[ret.String()] = struct{}{}
}
ret = gjson.GetBytes(bs, "mm_webhook_url")
if ret.Exists() {
mmset[ret.String()] = struct{}{}
}
}
phones := StringSetKeys(phoneset)
@@ -143,15 +137,6 @@ func notifyMaintainerWithBuiltin(title, msg, triggerTime string, users []*models
AtMobiles: phones,
Tokens: StringSetKeys(feishuset),
})
case "mm":
if len(mmset) == 0 {
continue
}
content := "**Title: **" + title + "\n**Content: **" + msg + "\n**Time: **" + triggerTime
sender.SendMM(sender.MatterMostMessage{
Text: content,
Tokens: StringSetKeys(mmset),
})
}
}
}

View File

@@ -175,7 +175,7 @@ func (r *RuleEval) Work() {
var value model.Value
var err error
if r.rule.Algorithm == "" && (r.rule.Cate == "" || strings.ToLower(r.rule.Cate) == "prometheus") {
if r.rule.Algorithm == "" && (r.rule.Cate == "" || r.rule.Cate == "prometheus") {
var warnings prom.Warnings
value, warnings, err = readerClient.Query(context.Background(), promql, time.Now())
if err != nil {
@@ -358,7 +358,7 @@ func (r *RuleEval) MakeNewEvent(from string, now int64, clusterName string, vect
tagsMap["rulename"] = r.rule.Name
// handle target note
targetIdent, has := tagsMap["ident"]
targetIdent, has := vectors[i].Labels["ident"]
targetNote := ""
if has {
target, exists := memsto.TargetCache.Get(string(targetIdent))
@@ -371,9 +371,6 @@ func (r *RuleEval) MakeNewEvent(from string, now int64, clusterName string, vect
logger.Debugf("event_enable_in_bg: rule_eval:%d", r.rule.Id)
continue
}
} else if strings.Contains(r.rule.PromQl, "target_up") {
// target 已经不存在了,可能是被删除了
continue
}
}

View File

@@ -49,7 +49,7 @@ func toRedis() {
// clean old idents
for key, at := range items {
if at.(int64) < now-config.C.NoData.Interval {
if at.(int64) < now-10 {
Idents.Remove(key)
} else {
// use now as timestamp to redis
@@ -184,9 +184,6 @@ func pushMetrics() {
// 把actives传给TargetCache看看除了active的部分还有别的target么有的话返回设置target_up = 0
deads := memsto.TargetCache.GetDeads(actives)
for ident, dead := range deads {
if ident == "" {
continue
}
// build metrics
pt := &prompb.TimeSeries{}
pt.Samples = append(pt.Samples, prompb.Sample{

View File

@@ -1,44 +0,0 @@
package memsto
import (
"sync"
)
type LogSampleCacheType struct {
sync.RWMutex
m map[string]map[string]struct{} // map[labelName]map[labelValue]struct{}
}
var LogSampleCache = LogSampleCacheType{
m: make(map[string]map[string]struct{}),
}
func (l *LogSampleCacheType) Set(m map[string][]string) {
l.Lock()
for k, v := range m {
l.m[k] = make(map[string]struct{})
for _, vv := range v {
l.m[k][vv] = struct{}{}
}
}
l.Unlock()
}
func (l *LogSampleCacheType) Get() map[string]map[string]struct{} {
l.RLock()
defer l.RUnlock()
return l.m
}
func (l *LogSampleCacheType) Clean() {
l.Lock()
l.m = make(map[string]map[string]struct{})
l.Unlock()
}
func (l *LogSampleCacheType) Len() int {
l.RLock()
defer l.RUnlock()
return len(l.m)
}

View File

@@ -37,12 +37,7 @@ func loopHeartbeat() {
}
func heartbeat() error {
cluster := ""
if config.C.ReaderFrom == "config" {
cluster = config.C.ClusterName
}
err := models.AlertingEngineHeartbeat(config.C.Heartbeat.Endpoint, cluster)
err := models.AlertingEngineHeartbeat(config.C.Heartbeat.Endpoint)
if err != nil {
return err
}

View File

@@ -101,10 +101,6 @@ func configRoute(r *gin.Engine, version string, reloadFunc func()) {
r.GET("/metrics", gin.WrapH(promhttp.Handler()))
r.GET("/log-sample-filter", logSampleFilterGet)
r.POST("/log-sample-filter", logSampleFilterAdd)
r.DELETE("/log-sample-filter", logSampleFilterDel)
service := r.Group("/v1/n9e")
service.POST("/event", pushEventToQueue)
service.POST("/make-event", makeEvent)

View File

@@ -3,6 +3,7 @@ package router
import (
"compress/gzip"
"compress/zlib"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
@@ -16,17 +17,14 @@ import (
promstat "github.com/didi/nightingale/v5/src/server/stat"
"github.com/didi/nightingale/v5/src/server/writer"
"github.com/gin-gonic/gin"
"github.com/mailru/easyjson"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/prompb"
)
//easyjson:json
type TimeSeries struct {
Series []*DatadogMetric `json:"series"`
}
//easyjson:json
type DatadogMetric struct {
Metric string `json:"metric"`
Points []DatadogPoint `json:"points"`
@@ -34,7 +32,6 @@ type DatadogMetric struct {
Tags []string `json:"tags,omitempty"`
}
//easyjson:json
type DatadogPoint [2]float64
func (m *DatadogMetric) Clean() error {
@@ -217,7 +214,7 @@ func datadogSeries(c *gin.Context) {
}
var series TimeSeries
err = easyjson.Unmarshal(bs, &series)
err = json.Unmarshal(bs, &series)
if err != nil {
c.String(400, err.Error())
return
@@ -266,16 +263,7 @@ func datadogSeries(c *gin.Context) {
}
}
LogSample(c.Request.RemoteAddr, pt)
if config.C.WriterOpt.ShardingKey == "ident" {
if ident == "" {
writer.Writers.PushSample("-", pt)
} else {
writer.Writers.PushSample(ident, pt)
}
} else {
writer.Writers.PushSample(item.Metric, pt)
}
writer.Writers.PushSample(item.Metric, pt)
succ++
}

View File

@@ -1,334 +0,0 @@
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package router
import (
json "encoding/json"
easyjson "github.com/mailru/easyjson"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
)
// suppress unused package warning
var (
_ *json.RawMessage
_ *jlexer.Lexer
_ *jwriter.Writer
_ easyjson.Marshaler
)
func easyjsonF301f710DecodeGithubComDidiNightingaleV5SrcServerRouter(in *jlexer.Lexer, out *TimeSeries) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "series":
if in.IsNull() {
in.Skip()
out.Series = nil
} else {
in.Delim('[')
if out.Series == nil {
if !in.IsDelim(']') {
out.Series = make([]*DatadogMetric, 0, 8)
} else {
out.Series = []*DatadogMetric{}
}
} else {
out.Series = (out.Series)[:0]
}
for !in.IsDelim(']') {
var v1 *DatadogMetric
if in.IsNull() {
in.Skip()
v1 = nil
} else {
if v1 == nil {
v1 = new(DatadogMetric)
}
(*v1).UnmarshalEasyJSON(in)
}
out.Series = append(out.Series, v1)
in.WantComma()
}
in.Delim(']')
}
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjsonF301f710EncodeGithubComDidiNightingaleV5SrcServerRouter(out *jwriter.Writer, in TimeSeries) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"series\":"
out.RawString(prefix[1:])
if in.Series == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v2, v3 := range in.Series {
if v2 > 0 {
out.RawByte(',')
}
if v3 == nil {
out.RawString("null")
} else {
(*v3).MarshalEasyJSON(out)
}
}
out.RawByte(']')
}
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v TimeSeries) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjsonF301f710EncodeGithubComDidiNightingaleV5SrcServerRouter(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v TimeSeries) MarshalEasyJSON(w *jwriter.Writer) {
easyjsonF301f710EncodeGithubComDidiNightingaleV5SrcServerRouter(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *TimeSeries) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjsonF301f710DecodeGithubComDidiNightingaleV5SrcServerRouter(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *TimeSeries) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjsonF301f710DecodeGithubComDidiNightingaleV5SrcServerRouter(l, v)
}
func easyjsonF301f710DecodeGithubComDidiNightingaleV5SrcServerRouter1(in *jlexer.Lexer, out *DatadogPoint) {
isTopLevel := in.IsStart()
if in.IsNull() {
in.Skip()
} else {
in.Delim('[')
v4 := 0
for !in.IsDelim(']') {
if v4 < 2 {
(*out)[v4] = float64(in.Float64())
v4++
} else {
in.SkipRecursive()
}
in.WantComma()
}
in.Delim(']')
}
if isTopLevel {
in.Consumed()
}
}
func easyjsonF301f710EncodeGithubComDidiNightingaleV5SrcServerRouter1(out *jwriter.Writer, in DatadogPoint) {
out.RawByte('[')
for v5 := range in {
if v5 > 0 {
out.RawByte(',')
}
out.Float64(float64((in)[v5]))
}
out.RawByte(']')
}
// MarshalJSON supports json.Marshaler interface
func (v DatadogPoint) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjsonF301f710EncodeGithubComDidiNightingaleV5SrcServerRouter1(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v DatadogPoint) MarshalEasyJSON(w *jwriter.Writer) {
easyjsonF301f710EncodeGithubComDidiNightingaleV5SrcServerRouter1(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *DatadogPoint) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjsonF301f710DecodeGithubComDidiNightingaleV5SrcServerRouter1(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *DatadogPoint) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjsonF301f710DecodeGithubComDidiNightingaleV5SrcServerRouter1(l, v)
}
func easyjsonF301f710DecodeGithubComDidiNightingaleV5SrcServerRouter2(in *jlexer.Lexer, out *DatadogMetric) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "metric":
out.Metric = string(in.String())
case "points":
if in.IsNull() {
in.Skip()
out.Points = nil
} else {
in.Delim('[')
if out.Points == nil {
if !in.IsDelim(']') {
out.Points = make([]DatadogPoint, 0, 4)
} else {
out.Points = []DatadogPoint{}
}
} else {
out.Points = (out.Points)[:0]
}
for !in.IsDelim(']') {
var v6 DatadogPoint
(v6).UnmarshalEasyJSON(in)
out.Points = append(out.Points, v6)
in.WantComma()
}
in.Delim(']')
}
case "host":
out.Host = string(in.String())
case "tags":
if in.IsNull() {
in.Skip()
out.Tags = nil
} else {
in.Delim('[')
if out.Tags == nil {
if !in.IsDelim(']') {
out.Tags = make([]string, 0, 4)
} else {
out.Tags = []string{}
}
} else {
out.Tags = (out.Tags)[:0]
}
for !in.IsDelim(']') {
var v7 string
v7 = string(in.String())
out.Tags = append(out.Tags, v7)
in.WantComma()
}
in.Delim(']')
}
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjsonF301f710EncodeGithubComDidiNightingaleV5SrcServerRouter2(out *jwriter.Writer, in DatadogMetric) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"metric\":"
out.RawString(prefix[1:])
out.String(string(in.Metric))
}
{
const prefix string = ",\"points\":"
out.RawString(prefix)
if in.Points == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v8, v9 := range in.Points {
if v8 > 0 {
out.RawByte(',')
}
(v9).MarshalEasyJSON(out)
}
out.RawByte(']')
}
}
{
const prefix string = ",\"host\":"
out.RawString(prefix)
out.String(string(in.Host))
}
if len(in.Tags) != 0 {
const prefix string = ",\"tags\":"
out.RawString(prefix)
{
out.RawByte('[')
for v10, v11 := range in.Tags {
if v10 > 0 {
out.RawByte(',')
}
out.String(string(v11))
}
out.RawByte(']')
}
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v DatadogMetric) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjsonF301f710EncodeGithubComDidiNightingaleV5SrcServerRouter2(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v DatadogMetric) MarshalEasyJSON(w *jwriter.Writer) {
easyjsonF301f710EncodeGithubComDidiNightingaleV5SrcServerRouter2(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *DatadogMetric) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjsonF301f710DecodeGithubComDidiNightingaleV5SrcServerRouter2(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *DatadogMetric) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjsonF301f710DecodeGithubComDidiNightingaleV5SrcServerRouter2(l, v)
}

View File

@@ -1,18 +0,0 @@
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package router
import (
json "encoding/json"
easyjson "github.com/mailru/easyjson"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
)
// suppress unused package warning
var (
_ *json.RawMessage
_ *jlexer.Lexer
_ *jwriter.Writer
_ easyjson.Marshaler
)

View File

@@ -46,11 +46,7 @@ func pushEventToQueue(c *gin.Context) {
return
}
if err := event.ParseRule("rule_name"); err != nil {
event.RuleName = fmt.Sprintf("failed to parse rule name: %v", err)
}
if err := event.ParseRule("rule_note"); err != nil {
if err := event.ParseRuleNote(); err != nil {
event.RuleNote = fmt.Sprintf("failed to parse rule note: %v", err)
}

View File

@@ -1,55 +0,0 @@
package router
import (
"github.com/didi/nightingale/v5/src/server/memsto"
"github.com/gin-gonic/gin"
"github.com/prometheus/prometheus/prompb"
"github.com/toolkits/pkg/ginx"
"github.com/toolkits/pkg/logger"
)
func logSampleFilterAdd(c *gin.Context) {
var f map[string][]string
ginx.BindJSON(c, &f)
memsto.LogSampleCache.Set(f)
c.JSON(200, "ok")
}
func logSampleFilterGet(c *gin.Context) {
c.JSON(200, memsto.LogSampleCache.Get())
}
func logSampleFilterDel(c *gin.Context) {
memsto.LogSampleCache.Clean()
c.JSON(200, "ok")
}
func LogSample(remoteAddr string, v *prompb.TimeSeries) {
if memsto.LogSampleCache.Len() == 0 {
return
}
labelMap := make(map[string]string)
for i := 0; i < len(v.Labels); i++ {
labelMap[v.Labels[i].Name] = v.Labels[i].Value
}
filterMap := memsto.LogSampleCache.Get()
for k, v := range filterMap {
// 在指标 labels 中找过滤的 label key ,如果找不到,直接返回
lableValue, exists := labelMap[k]
if !exists {
return
}
// key 存在,在过滤条件中找指标的 label value如果找不到直接返回
_, exists = v[lableValue]
if !exists {
return
}
}
// 每个过滤条件都在 指标的 labels 中找到了
logger.Debugf("recv sample from:%s sample:%s", remoteAddr, v.String())
}

View File

@@ -2,6 +2,7 @@ package router
import (
"compress/gzip"
"encoding/json"
"fmt"
"io/ioutil"
"strconv"
@@ -15,12 +16,10 @@ import (
promstat "github.com/didi/nightingale/v5/src/server/stat"
"github.com/didi/nightingale/v5/src/server/writer"
"github.com/gin-gonic/gin"
"github.com/mailru/easyjson"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/prompb"
)
//easyjson:json
type FalconMetric struct {
Metric string `json:"metric"`
Endpoint string `json:"endpoint"`
@@ -30,9 +29,6 @@ type FalconMetric struct {
Tags string `json:"tags"`
}
//easyjson:json
type FalconMetricArr []FalconMetric
func (m *FalconMetric) Clean(ts int64) error {
if m.Metric == "" {
return fmt.Errorf("metric is blank")
@@ -144,7 +140,6 @@ func (m *FalconMetric) ToProm() (*prompb.TimeSeries, string, error) {
}
func falconPush(c *gin.Context) {
var bs []byte
var err error
var r *gzip.Reader
@@ -167,13 +162,13 @@ func falconPush(c *gin.Context) {
return
}
var arr FalconMetricArr
var arr []FalconMetric
if bs[0] == '[' {
err = easyjson.Unmarshal(bs, &arr)
err = json.Unmarshal(bs, &arr)
} else {
var one FalconMetric
err = easyjson.Unmarshal(bs, &one)
err = json.Unmarshal(bs, &one)
arr = []FalconMetric{one}
}
@@ -213,16 +208,7 @@ func falconPush(c *gin.Context) {
}
}
LogSample(c.Request.RemoteAddr, pt)
if config.C.WriterOpt.ShardingKey == "ident" {
if ident == "" {
writer.Writers.PushSample("-", pt)
} else {
writer.Writers.PushSample(ident, pt)
}
} else {
writer.Writers.PushSample(arr[i].Metric, pt)
}
writer.Writers.PushSample(arr[i].Metric, pt)
succ++
}

View File

@@ -1,191 +0,0 @@
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package router
import (
json "encoding/json"
easyjson "github.com/mailru/easyjson"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
)
// suppress unused package warning
var (
_ *json.RawMessage
_ *jlexer.Lexer
_ *jwriter.Writer
_ easyjson.Marshaler
)
func easyjson61ba9b47DecodeGithubComDidiNightingaleV5SrcServerRouter(in *jlexer.Lexer, out *FalconMetricArr) {
isTopLevel := in.IsStart()
if in.IsNull() {
in.Skip()
*out = nil
} else {
in.Delim('[')
if *out == nil {
if !in.IsDelim(']') {
*out = make(FalconMetricArr, 0, 0)
} else {
*out = FalconMetricArr{}
}
} else {
*out = (*out)[:0]
}
for !in.IsDelim(']') {
var v1 FalconMetric
(v1).UnmarshalEasyJSON(in)
*out = append(*out, v1)
in.WantComma()
}
in.Delim(']')
}
if isTopLevel {
in.Consumed()
}
}
func easyjson61ba9b47EncodeGithubComDidiNightingaleV5SrcServerRouter(out *jwriter.Writer, in FalconMetricArr) {
if in == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v2, v3 := range in {
if v2 > 0 {
out.RawByte(',')
}
(v3).MarshalEasyJSON(out)
}
out.RawByte(']')
}
}
// MarshalJSON supports json.Marshaler interface
func (v FalconMetricArr) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson61ba9b47EncodeGithubComDidiNightingaleV5SrcServerRouter(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v FalconMetricArr) MarshalEasyJSON(w *jwriter.Writer) {
easyjson61ba9b47EncodeGithubComDidiNightingaleV5SrcServerRouter(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *FalconMetricArr) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson61ba9b47DecodeGithubComDidiNightingaleV5SrcServerRouter(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *FalconMetricArr) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson61ba9b47DecodeGithubComDidiNightingaleV5SrcServerRouter(l, v)
}
func easyjson61ba9b47DecodeGithubComDidiNightingaleV5SrcServerRouter1(in *jlexer.Lexer, out *FalconMetric) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "metric":
out.Metric = string(in.String())
case "endpoint":
out.Endpoint = string(in.String())
case "timestamp":
out.Timestamp = int64(in.Int64())
case "value":
if m, ok := out.ValueUnTyped.(easyjson.Unmarshaler); ok {
m.UnmarshalEasyJSON(in)
} else if m, ok := out.ValueUnTyped.(json.Unmarshaler); ok {
_ = m.UnmarshalJSON(in.Raw())
} else {
out.ValueUnTyped = in.Interface()
}
case "tags":
out.Tags = string(in.String())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson61ba9b47EncodeGithubComDidiNightingaleV5SrcServerRouter1(out *jwriter.Writer, in FalconMetric) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"metric\":"
out.RawString(prefix[1:])
out.String(string(in.Metric))
}
{
const prefix string = ",\"endpoint\":"
out.RawString(prefix)
out.String(string(in.Endpoint))
}
{
const prefix string = ",\"timestamp\":"
out.RawString(prefix)
out.Int64(int64(in.Timestamp))
}
{
const prefix string = ",\"value\":"
out.RawString(prefix)
if m, ok := in.ValueUnTyped.(easyjson.Marshaler); ok {
m.MarshalEasyJSON(out)
} else if m, ok := in.ValueUnTyped.(json.Marshaler); ok {
out.Raw(m.MarshalJSON())
} else {
out.Raw(json.Marshal(in.ValueUnTyped))
}
}
{
const prefix string = ",\"tags\":"
out.RawString(prefix)
out.String(string(in.Tags))
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v FalconMetric) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson61ba9b47EncodeGithubComDidiNightingaleV5SrcServerRouter1(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v FalconMetric) MarshalEasyJSON(w *jwriter.Writer) {
easyjson61ba9b47EncodeGithubComDidiNightingaleV5SrcServerRouter1(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *FalconMetric) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson61ba9b47DecodeGithubComDidiNightingaleV5SrcServerRouter1(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *FalconMetric) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson61ba9b47DecodeGithubComDidiNightingaleV5SrcServerRouter1(l, v)
}

View File

@@ -2,6 +2,7 @@ package router
import (
"compress/gzip"
"encoding/json"
"fmt"
"io/ioutil"
"strconv"
@@ -19,11 +20,8 @@ import (
"github.com/didi/nightingale/v5/src/server/memsto"
promstat "github.com/didi/nightingale/v5/src/server/stat"
"github.com/didi/nightingale/v5/src/server/writer"
"github.com/mailru/easyjson"
_ "github.com/mailru/easyjson/gen"
)
// easyjson:json
type HTTPMetric struct {
Metric string `json:"metric"`
Timestamp int64 `json:"timestamp"`
@@ -32,9 +30,6 @@ type HTTPMetric struct {
Tags map[string]string `json:"tags"`
}
//easyjson:json
type HTTPMetricArr []HTTPMetric
func (m *HTTPMetric) Clean(ts int64) error {
if m.Metric == "" {
return fmt.Errorf("metric is blank")
@@ -151,13 +146,13 @@ func handleOpenTSDB(c *gin.Context) {
return
}
var arr HTTPMetricArr
var arr []HTTPMetric
if bs[0] == '[' {
err = easyjson.Unmarshal(bs, &arr)
err = json.Unmarshal(bs, &arr)
} else {
var one HTTPMetric
err = easyjson.Unmarshal(bs, &one)
err = json.Unmarshal(bs, &one)
arr = []HTTPMetric{one}
}
@@ -207,16 +202,7 @@ func handleOpenTSDB(c *gin.Context) {
}
}
LogSample(c.Request.RemoteAddr, pt)
if config.C.WriterOpt.ShardingKey == "ident" {
if host == "" {
writer.Writers.PushSample("-", pt)
} else {
writer.Writers.PushSample(host, pt)
}
} else {
writer.Writers.PushSample(arr[i].Metric, pt)
}
writer.Writers.PushSample(arr[i].Metric, pt)
succ++
}

View File

@@ -1,214 +0,0 @@
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package router
import (
json "encoding/json"
easyjson "github.com/mailru/easyjson"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
)
// suppress unused package warning
var (
_ *json.RawMessage
_ *jlexer.Lexer
_ *jwriter.Writer
_ easyjson.Marshaler
)
func easyjson30864de9DecodeGithubComDidiNightingaleV5SrcServerRouter(in *jlexer.Lexer, out *HTTPMetricArr) {
isTopLevel := in.IsStart()
if in.IsNull() {
in.Skip()
*out = nil
} else {
in.Delim('[')
if *out == nil {
if !in.IsDelim(']') {
*out = make(HTTPMetricArr, 0, 1)
} else {
*out = HTTPMetricArr{}
}
} else {
*out = (*out)[:0]
}
for !in.IsDelim(']') {
var v1 HTTPMetric
(v1).UnmarshalEasyJSON(in)
*out = append(*out, v1)
in.WantComma()
}
in.Delim(']')
}
if isTopLevel {
in.Consumed()
}
}
func easyjson30864de9EncodeGithubComDidiNightingaleV5SrcServerRouter(out *jwriter.Writer, in HTTPMetricArr) {
if in == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v2, v3 := range in {
if v2 > 0 {
out.RawByte(',')
}
(v3).MarshalEasyJSON(out)
}
out.RawByte(']')
}
}
// MarshalJSON supports json.Marshaler interface
func (v HTTPMetricArr) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson30864de9EncodeGithubComDidiNightingaleV5SrcServerRouter(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v HTTPMetricArr) MarshalEasyJSON(w *jwriter.Writer) {
easyjson30864de9EncodeGithubComDidiNightingaleV5SrcServerRouter(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *HTTPMetricArr) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson30864de9DecodeGithubComDidiNightingaleV5SrcServerRouter(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *HTTPMetricArr) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson30864de9DecodeGithubComDidiNightingaleV5SrcServerRouter(l, v)
}
func easyjson30864de9DecodeGithubComDidiNightingaleV5SrcServerRouter1(in *jlexer.Lexer, out *HTTPMetric) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "metric":
out.Metric = string(in.String())
case "timestamp":
out.Timestamp = int64(in.Int64())
case "value":
if m, ok := out.ValueUnTyped.(easyjson.Unmarshaler); ok {
m.UnmarshalEasyJSON(in)
} else if m, ok := out.ValueUnTyped.(json.Unmarshaler); ok {
_ = m.UnmarshalJSON(in.Raw())
} else {
out.ValueUnTyped = in.Interface()
}
case "tags":
if in.IsNull() {
in.Skip()
} else {
in.Delim('{')
out.Tags = make(map[string]string)
for !in.IsDelim('}') {
key := string(in.String())
in.WantColon()
var v4 string
v4 = string(in.String())
(out.Tags)[key] = v4
in.WantComma()
}
in.Delim('}')
}
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson30864de9EncodeGithubComDidiNightingaleV5SrcServerRouter1(out *jwriter.Writer, in HTTPMetric) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"metric\":"
out.RawString(prefix[1:])
out.String(string(in.Metric))
}
{
const prefix string = ",\"timestamp\":"
out.RawString(prefix)
out.Int64(int64(in.Timestamp))
}
{
const prefix string = ",\"value\":"
out.RawString(prefix)
if m, ok := in.ValueUnTyped.(easyjson.Marshaler); ok {
m.MarshalEasyJSON(out)
} else if m, ok := in.ValueUnTyped.(json.Marshaler); ok {
out.Raw(m.MarshalJSON())
} else {
out.Raw(json.Marshal(in.ValueUnTyped))
}
}
{
const prefix string = ",\"tags\":"
out.RawString(prefix)
if in.Tags == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 {
out.RawString(`null`)
} else {
out.RawByte('{')
v5First := true
for v5Name, v5Value := range in.Tags {
if v5First {
v5First = false
} else {
out.RawByte(',')
}
out.String(string(v5Name))
out.RawByte(':')
out.String(string(v5Value))
}
out.RawByte('}')
}
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v HTTPMetric) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson30864de9EncodeGithubComDidiNightingaleV5SrcServerRouter1(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v HTTPMetric) MarshalEasyJSON(w *jwriter.Writer) {
easyjson30864de9EncodeGithubComDidiNightingaleV5SrcServerRouter1(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *HTTPMetric) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson30864de9DecodeGithubComDidiNightingaleV5SrcServerRouter1(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *HTTPMetric) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson30864de9DecodeGithubComDidiNightingaleV5SrcServerRouter1(l, v)
}

View File

@@ -143,17 +143,7 @@ func remoteWrite(c *gin.Context) {
}
}
LogSample(c.Request.RemoteAddr, req.Timeseries[i])
if config.C.WriterOpt.ShardingKey == "ident" {
if ident == "" {
writer.Writers.PushSample("-", req.Timeseries[i])
} else {
writer.Writers.PushSample(ident, req.Timeseries[i])
}
} else {
writer.Writers.PushSample(metric, req.Timeseries[i])
}
writer.Writers.PushSample(metric, req.Timeseries[i])
}
cn := config.ReaderClient.GetClusterName()

View File

@@ -28,7 +28,6 @@ import (
type Server struct {
ConfigFile string
Version string
Key string
}
type ServerOption func(*Server)
@@ -45,12 +44,6 @@ func SetVersion(v string) ServerOption {
}
}
func SetKey(k string) ServerOption {
return func(s *Server) {
s.Key = k
}
}
// Run run server
func Run(opts ...ServerOption) {
code := 1
@@ -99,7 +92,7 @@ func (s Server) initialize() (func(), error) {
fns.Add(cancel)
// parse config file
config.MustLoad(s.Key, s.ConfigFile)
config.MustLoad(s.ConfigFile)
// init i18n
i18n.Init()

View File

@@ -1,111 +0,0 @@
package writer
import (
"container/list"
"sync"
"github.com/prometheus/prometheus/prompb"
)
type SafeList struct {
sync.RWMutex
L *list.List
}
func NewSafeList() *SafeList {
return &SafeList{L: list.New()}
}
func (sl *SafeList) PushFront(v interface{}) *list.Element {
sl.Lock()
e := sl.L.PushFront(v)
sl.Unlock()
return e
}
func (sl *SafeList) PushFrontBatch(vs []interface{}) {
sl.Lock()
for _, item := range vs {
sl.L.PushFront(item)
}
sl.Unlock()
}
func (sl *SafeList) PopBack(max int) []*prompb.TimeSeries {
sl.Lock()
count := sl.L.Len()
if count == 0 {
sl.Unlock()
return []*prompb.TimeSeries{}
}
if count > max {
count = max
}
items := make([]*prompb.TimeSeries, 0, count)
for i := 0; i < count; i++ {
item := sl.L.Remove(sl.L.Back())
sample, ok := item.(*prompb.TimeSeries)
if ok {
items = append(items, sample)
}
}
sl.Unlock()
return items
}
func (sl *SafeList) RemoveAll() {
sl.Lock()
sl.L.Init()
sl.Unlock()
}
func (sl *SafeList) Len() int {
sl.RLock()
size := sl.L.Len()
sl.RUnlock()
return size
}
// SafeList with Limited Size
type SafeListLimited struct {
maxSize int
SL *SafeList
}
func NewSafeListLimited(maxSize int) *SafeListLimited {
return &SafeListLimited{SL: NewSafeList(), maxSize: maxSize}
}
func (sll *SafeListLimited) PopBack(max int) []*prompb.TimeSeries {
return sll.SL.PopBack(max)
}
func (sll *SafeListLimited) PushFront(v interface{}) bool {
if sll.SL.Len() >= sll.maxSize {
return false
}
sll.SL.PushFront(v)
return true
}
func (sll *SafeListLimited) PushFrontBatch(vs []interface{}) bool {
if sll.SL.Len() >= sll.maxSize {
return false
}
sll.SL.PushFrontBatch(vs)
return true
}
func (sll *SafeListLimited) RemoveAll() {
sll.SL.RemoveAll()
}
func (sll *SafeListLimited) Len() int {
return sll.SL.Len()
}

View File

@@ -130,39 +130,73 @@ func (w WriterType) Post(req []byte, headers ...map[string]string) error {
type WritersType struct {
globalOpt config.WriterGlobalOpt
backends map[string]WriterType
queues map[int]*SafeListLimited
chans map[int]chan *prompb.TimeSeries
}
func (ws *WritersType) Put(name string, writer WriterType) {
ws.backends[name] = writer
}
// PushSample Push one sample to chan, hash by ident
// @Author: quzhihao
func (ws *WritersType) PushSample(ident string, v interface{}) {
hashkey := crc32.ChecksumIEEE([]byte(ident)) % uint32(ws.globalOpt.QueueCount)
c, ok := ws.queues[int(hashkey)]
c, ok := ws.chans[int(hashkey)]
if ok {
succ := c.PushFront(v)
if !succ {
logger.Warningf("Write channel(%s) full, current channel size: %d", ident, c.Len())
select {
case c <- v.(*prompb.TimeSeries):
default:
logger.Warningf("Write channel(%s) full, current channel size: %d", ident, len(c))
}
}
}
func (ws *WritersType) StartConsumer(index int, ch *SafeListLimited) {
for {
series := ch.PopBack(ws.globalOpt.QueuePopSize)
if len(series) == 0 {
time.Sleep(time.Millisecond * 400)
continue
}
// StartConsumer every ident channel has a consumer, start it
// @Author: quzhihao
func (ws *WritersType) StartConsumer(index int, ch chan *prompb.TimeSeries) {
var (
batch = ws.globalOpt.QueuePopSize
series = make([]*prompb.TimeSeries, 0, batch)
batchCounter int
)
for key := range ws.backends {
go ws.backends[key].Write(index, series)
for {
select {
case item := <-ch:
// has data, no need to close
series = append(series, item)
batchCounter++
if batchCounter >= ws.globalOpt.QueuePopSize {
ws.post(index, series)
// reset
batchCounter = 0
series = make([]*prompb.TimeSeries, 0, batch)
}
case <-time.After(time.Second):
if len(series) > 0 {
ws.post(index, series)
// reset
batchCounter = 0
series = make([]*prompb.TimeSeries, 0, batch)
}
}
}
}
// post post series to TSDB
// @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)}
for key := range ws.backends {
go ws.backends[key].Write(index, series, header)
}
}
func NewWriters() WritersType {
return WritersType{
backends: make(map[string]WriterType),
@@ -173,11 +207,12 @@ var Writers = NewWriters()
func Init(opts []config.WriterOptions, globalOpt config.WriterGlobalOpt) error {
Writers.globalOpt = globalOpt
Writers.queues = make(map[int]*SafeListLimited)
Writers.chans = make(map[int]chan *prompb.TimeSeries)
// init channels
for i := 0; i < globalOpt.QueueCount; i++ {
Writers.queues[i] = NewSafeListLimited(Writers.globalOpt.QueueMaxSize)
go Writers.StartConsumer(i, Writers.queues[i])
Writers.chans[i] = make(chan *prompb.TimeSeries, Writers.globalOpt.QueueMaxSize)
go Writers.StartConsumer(i, Writers.chans[i])
}
go reportChanSize()
@@ -225,8 +260,8 @@ func reportChanSize() {
for {
time.Sleep(time.Second * 3)
for i, c := range Writers.queues {
size := c.Len()
for i, c := range Writers.chans {
size := len(c)
promstat.GaugeSampleQueueSize.WithLabelValues(clusterName, fmt.Sprint(i)).Set(float64(size))
}
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/didi/nightingale/v5/src/pkg/ormx"
"github.com/didi/nightingale/v5/src/pkg/tls"
"github.com/go-redis/redis/v9"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
)
@@ -38,7 +38,7 @@ var Redis interface {
Del(ctx context.Context, keys ...string) *redis.IntCmd
Get(ctx context.Context, key string) *redis.StringCmd
Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.StatusCmd
HGetAll(ctx context.Context, key string) *redis.MapStringStringCmd
HGetAll(ctx context.Context, key string) *redis.StringStringMapCmd
HSet(ctx context.Context, key string, values ...interface{}) *redis.IntCmd
HDel(ctx context.Context, key string, fields ...string) *redis.IntCmd
Close() error

View File

@@ -9,14 +9,11 @@ import (
"github.com/gin-gonic/gin"
"github.com/koding/multiconfig"
"github.com/didi/nightingale/v5/src/pkg/cas"
"github.com/didi/nightingale/v5/src/pkg/httpx"
"github.com/didi/nightingale/v5/src/pkg/ldapx"
"github.com/didi/nightingale/v5/src/pkg/logx"
"github.com/didi/nightingale/v5/src/pkg/oauth2x"
"github.com/didi/nightingale/v5/src/pkg/oidcc"
"github.com/didi/nightingale/v5/src/pkg/ormx"
"github.com/didi/nightingale/v5/src/pkg/secu"
"github.com/didi/nightingale/v5/src/pkg/tls"
"github.com/didi/nightingale/v5/src/storage"
)
@@ -26,40 +23,7 @@ var (
once sync.Once
)
func DealConfigCrypto(key string) {
decryptDsn, err := secu.DealWithDecrypt(C.DB.DSN, key)
if err != nil {
fmt.Println("failed to decrypt the db dsn", err)
os.Exit(1)
}
C.DB.DSN = decryptDsn
decryptRedisPwd, err := secu.DealWithDecrypt(C.Redis.Password, key)
if err != nil {
fmt.Println("failed to decrypt the redis password", err)
os.Exit(1)
}
C.Redis.Password = decryptRedisPwd
decryptIbexPwd, err := secu.DealWithDecrypt(C.Ibex.BasicAuthPass, key)
if err != nil {
fmt.Println("failed to decrypt the ibex password", err)
os.Exit(1)
}
C.Ibex.BasicAuthPass = decryptIbexPwd
for index, v := range C.Clusters {
decryptClusterPwd, err := secu.DealWithDecrypt(v.BasicAuthPass, key)
if err != nil {
fmt.Printf("failed to decrypt the clusters password: %s , error: %s", v.BasicAuthPass, err.Error())
os.Exit(1)
}
C.Clusters[index].BasicAuthPass = decryptClusterPwd
}
}
func MustLoad(key string, fpaths ...string) {
func MustLoad(fpaths ...string) {
once.Do(func() {
loaders := []multiconfig.Loader{
&multiconfig.TagLoader{},
@@ -99,8 +63,6 @@ func MustLoad(key string, fpaths ...string) {
m.MustLoad(C)
DealConfigCrypto(key)
if !strings.HasPrefix(C.Ibex.Address, "http") {
C.Ibex.Address = "http://" + C.Ibex.Address
}
@@ -137,8 +99,6 @@ type Config struct {
Clusters []ClusterOptions
Ibex Ibex
OIDC oidcc.Config
CAS cas.Config
OAuth oauth2x.Config
TargetMetrics map[string]string
}

View File

@@ -30,7 +30,6 @@ var (
"Name is blank": "名称不能为空",
"Name has invalid characters": "名称含有非法字符",
"Dashboard already exists": "监控大盘已存在",
"Ident duplicate": "英文标识已存在",
"No such dashboard": "监控大盘不存在",
"AlertRule already exists": "告警规则已存在,不能重复创建",
"No such AlertRule": "告警规则不存在",

View File

@@ -7,7 +7,6 @@ import (
"math/rand"
"net"
"net/http"
"sort"
"strings"
"sync"
"time"
@@ -121,8 +120,7 @@ type DSReply struct {
PrometheusUser string `json:"prometheus.user"`
PrometheusPass string `json:"prometheus.password"`
} `json:"prometheus.basic"`
Headers map[string]string `json:"prometheus.headers"`
PrometheusTimeout int64 `json:"prometheus.timeout"`
PrometheusTimeout int64 `json:"prometheus.timeout"`
} `json:"settings,omitempty"`
} `json:"items"`
} `json:"data"`
@@ -194,8 +192,7 @@ func loadClustersFromAPI() {
old.Opts.BasicAuthUser != item.Settings.PrometheusBasic.PrometheusUser ||
old.Opts.BasicAuthPass != item.Settings.PrometheusBasic.PrometheusPass ||
old.Opts.Timeout != item.Settings.PrometheusTimeout ||
old.Opts.Prom != item.Settings.PrometheusAddr ||
!equalHeader(old.Opts.Headers, transformHeader(item.Settings.Headers)) {
old.Opts.Prom != item.Settings.PrometheusAddr {
opt := config.ClusterOptions{
Name: item.Name,
Prom: item.Settings.PrometheusAddr,
@@ -204,7 +201,6 @@ func loadClustersFromAPI() {
Timeout: item.Settings.PrometheusTimeout,
DialTimeout: 5000,
MaxIdleConnsPerHost: 32,
Headers: transformHeader(item.Settings.Headers),
}
if strings.HasPrefix(opt.Prom, "https") {
@@ -264,29 +260,3 @@ func newClusterByOption(opt config.ClusterOptions) *ClusterType {
return cluster
}
func equalHeader(a, b []string) bool {
sort.Strings(a)
sort.Strings(b)
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func transformHeader(header map[string]string) []string {
var headers []string
for k, v := range header {
headers = append(headers, k)
headers = append(headers, v)
}
return headers
}

View File

@@ -134,13 +134,8 @@ func configRoute(r *gin.Engine, version string) {
pages.POST("/auth/logout", jwtMock(), logoutPost)
pages.POST("/auth/refresh", jwtMock(), refreshPost)
pages.GET("/auth/sso-config", ssoConfigGet)
pages.GET("/auth/redirect", loginRedirect)
pages.GET("/auth/redirect/cas", loginRedirectCas)
pages.GET("/auth/redirect/oauth", loginRedirectOAuth)
pages.GET("/auth/callback", loginCallback)
pages.GET("/auth/callback/cas", loginCallbackCas)
pages.GET("/auth/callback/oauth", loginCallbackOAuth)
pages.GET("/metrics/desc", metricsDescGetFile)
pages.POST("/metrics/desc", metricsDescGetMap)
@@ -200,11 +195,10 @@ func configRoute(r *gin.Engine, version string) {
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", boardGet)
pages.GET("/board/:bid", auth(), user(), boardGet)
pages.GET("/board/:bid/pure", boardPureGet)
pages.PUT("/board/:bid", auth(), user(), perm("/dashboards/put"), boardPut)
pages.PUT("/board/:bid/configs", auth(), user(), perm("/dashboards/put"), boardPutConfigs)
pages.PUT("/board/:bid/public", auth(), user(), perm("/dashboards/put"), boardPutPublic)
pages.DELETE("/boards", auth(), user(), perm("/dashboards/del"), boardDel)
// migrate v5.8.0
@@ -257,8 +251,6 @@ func configRoute(r *gin.Engine, version string) {
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.PUT("/busi-group/:id/alert-mute/:amid", auth(), user(), perm("/alert-mutes/put"), alertMutePutByFE)
pages.PUT("/busi-group/:id/alert-mutes/fields", auth(), user(), perm("/alert-mutes/put"), bgrw(), alertMutePutFields)
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)
@@ -330,14 +322,5 @@ func configRoute(r *gin.Engine, version string) {
service.GET("/alert-cur-events", alertCurEventsList)
service.GET("/alert-his-events", alertHisEventsList)
service.GET("/alert-his-event/:eid", alertHisEventGet)
service.GET("/config/:id", configGet)
service.GET("/configs", configsGet)
service.PUT("/configs", configsPut)
service.POST("/configs", configsPost)
service.DELETE("/configs", configsDel)
service.POST("/conf-prop/encrypt", confPropEncrypt)
service.POST("/conf-prop/decrypt", confPropDecrypt)
}
}

View File

@@ -181,7 +181,6 @@ func alertRulePutByService(c *gin.Context) {
type alertRuleFieldForm struct {
Ids []int64 `json:"ids"`
Fields map[string]interface{} `json:"fields"`
Action string `json:"action"`
}
// update one field: cluster note severity disabled prom_eval_interval prom_for_duration notify_channels notify_groups notify_recovered notify_repeat_step callbacks runbook_url append_tags
@@ -204,26 +203,6 @@ func alertRulePutFields(c *gin.Context) {
continue
}
if f.Action == "callback_add" {
// 增加一个 callback 地址
if callbacks, has := f.Fields["callbacks"]; has {
callback := callbacks.(string)
if !strings.Contains(ar.Callbacks, callback) {
ginx.Dangerous(ar.UpdateFieldsMap(map[string]interface{}{"callbacks": ar.Callbacks + " " + callback}))
continue
}
}
}
if f.Action == "callback_del" {
// 删除一个 callback 地址
if callbacks, has := f.Fields["callbacks"]; has {
callback := callbacks.(string)
ginx.Dangerous(ar.UpdateFieldsMap(map[string]interface{}{"callbacks": strings.ReplaceAll(ar.Callbacks, callback, "")}))
continue
}
}
ginx.Dangerous(ar.UpdateFieldsMap(f.Fields))
}

View File

@@ -74,8 +74,6 @@ func alertSubscribePut(c *gin.Context) {
fs[i].UpdateBy = username
fs[i].UpdateAt = timestamp
ginx.Dangerous(fs[i].Update(
"name",
"disabled",
"cluster",
"rule_id",
"tags",

View File

@@ -6,16 +6,13 @@ import (
"github.com/didi/nightingale/v5/src/models"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/toolkits/pkg/ginx"
)
type boardForm struct {
Name string `json:"name"`
Ident string `json:"ident"`
Tags string `json:"tags"`
Configs string `json:"configs"`
Public int `json:"public"`
}
func boardAdd(c *gin.Context) {
@@ -27,7 +24,6 @@ func boardAdd(c *gin.Context) {
board := &models.Board{
GroupId: ginx.UrlParamInt64(c, "id"),
Name: f.Name,
Ident: f.Ident,
Tags: f.Tags,
Configs: f.Configs,
CreateBy: me.Username,
@@ -45,21 +41,13 @@ func boardAdd(c *gin.Context) {
}
func boardGet(c *gin.Context) {
bid := ginx.UrlParamStr(c, "bid")
board, err := models.BoardGet("id = ? or ident = ?", bid, bid)
board, err := models.BoardGet("id = ?", ginx.UrlParamInt64(c, "bid"))
ginx.Dangerous(err)
if board == nil {
ginx.Bomb(http.StatusNotFound, "No such dashboard")
}
if board.Public == 0 {
auth()(c)
user()(c)
bgroCheck(c, board.GroupId)
}
ginx.NewRender(c).Data(board, nil)
}
@@ -121,20 +109,12 @@ func boardPut(c *gin.Context) {
// check permission
bgrwCheck(c, bo.GroupId)
can, err := bo.CanRenameIdent(f.Ident)
ginx.Dangerous(err)
if !can {
ginx.Bomb(http.StatusOK, "Ident duplicate")
}
bo.Name = f.Name
bo.Ident = f.Ident
bo.Tags = f.Tags
bo.UpdateBy = me.Username
bo.UpdateAt = time.Now().Unix()
err = bo.Update("name", "ident", "tags", "update_by", "update_at")
err := bo.Update("name", "tags", "update_by", "update_at")
ginx.NewRender(c).Data(bo, err)
}
@@ -144,14 +124,7 @@ func boardPutConfigs(c *gin.Context) {
ginx.BindJSON(c, &f)
me := c.MustGet("user").(*models.User)
bid := ginx.UrlParamStr(c, "bid")
bo, err := models.BoardGet("id = ? or ident = ?", bid, bid)
ginx.Dangerous(err)
if bo == nil {
ginx.Bomb(http.StatusNotFound, "No such dashboard")
}
bo := Board(ginx.UrlParamInt64(c, "bid"))
// check permission
bgrwCheck(c, bo.GroupId)
@@ -166,25 +139,6 @@ func boardPutConfigs(c *gin.Context) {
ginx.NewRender(c).Data(bo, nil)
}
// bgrwCheck
func boardPutPublic(c *gin.Context) {
var f boardForm
ginx.BindJSON(c, &f)
me := c.MustGet("user").(*models.User)
bo := Board(ginx.UrlParamInt64(c, "bid"))
// check permission
bgrwCheck(c, bo.GroupId)
bo.Public = f.Public
bo.UpdateBy = me.Username
bo.UpdateAt = time.Now().Unix()
err := bo.Update("public", "update_by", "update_at")
ginx.NewRender(c).Data(bo, err)
}
func boardGets(c *gin.Context) {
bgid := ginx.UrlParamInt64(c, "id")
query := ginx.QueryStr(c, "query", "")
@@ -205,10 +159,6 @@ func boardClone(c *gin.Context) {
UpdateBy: me.Username,
}
if bo.Ident != "" {
newBoard.Ident = uuid.NewString()
}
ginx.Dangerous(newBoard.Add())
// clone payload

View File

@@ -1,49 +0,0 @@
package router
import (
"github.com/didi/nightingale/v5/src/models"
"github.com/gin-gonic/gin"
"github.com/toolkits/pkg/ginx"
)
func configsGet(c *gin.Context) {
prefix := ginx.QueryStr(c, "prefix", "")
limit := ginx.QueryInt(c, "limit", 10)
configs, err := models.ConfigsGets(prefix, limit, ginx.Offset(c, limit))
ginx.NewRender(c).Data(configs, err)
}
func configGet(c *gin.Context) {
id := ginx.UrlParamInt64(c, "id")
configs, err := models.ConfigGet(id)
ginx.NewRender(c).Data(configs, err)
}
func configsDel(c *gin.Context) {
var f idsForm
ginx.BindJSON(c, &f)
ginx.NewRender(c).Message(models.ConfigsDel(f.Ids))
}
func configsPut(c *gin.Context) {
var arr []models.Configs
ginx.BindJSON(c, &arr)
for i := 0; i < len(arr); i++ {
ginx.Dangerous(arr[i].Update())
}
ginx.NewRender(c).Message(nil)
}
func configsPost(c *gin.Context) {
var arr []models.Configs
ginx.BindJSON(c, &arr)
for i := 0; i < len(arr); i++ {
ginx.Dangerous(arr[i].Add())
}
ginx.NewRender(c).Message(nil)
}

View File

@@ -1,62 +0,0 @@
package router
import (
"github.com/didi/nightingale/v5/src/pkg/secu"
"github.com/gin-gonic/gin"
"github.com/toolkits/pkg/ginx"
)
type confPropCrypto struct {
Data string `json:"data" binding:"required"`
Key string `json:"key" binding:"required"`
}
func confPropEncrypt(c *gin.Context) {
var f confPropCrypto
ginx.BindJSON(c, &f)
k := len(f.Key)
switch k {
default:
c.String(400, "The key length should be 16, 24 or 32")
return
case 16, 24, 32:
break
}
s, err := secu.DealWithEncrypt(f.Data, f.Key)
if err != nil {
c.String(500, err.Error())
}
c.JSON(200, gin.H{
"src": f.Data,
"key": f.Key,
"encrypt": s,
})
}
func confPropDecrypt(c *gin.Context) {
var f confPropCrypto
ginx.BindJSON(c, &f)
k := len(f.Key)
switch k {
default:
c.String(400, "The key length should be 16, 24 or 32")
return
case 16, 24, 32:
break
}
s, err := secu.DealWithDecrypt(f.Data, f.Key)
if err != nil {
c.String(500, err.Error())
}
c.JSON(200, gin.H{
"src": f.Data,
"key": f.Key,
"decrypt": s,
})
}

View File

@@ -13,8 +13,6 @@ import (
"github.com/toolkits/pkg/logger"
"github.com/didi/nightingale/v5/src/models"
"github.com/didi/nightingale/v5/src/pkg/cas"
"github.com/didi/nightingale/v5/src/pkg/oauth2x"
"github.com/didi/nightingale/v5/src/pkg/oidcc"
"github.com/didi/nightingale/v5/src/webapi/config"
)
@@ -261,212 +259,3 @@ func loginCallback(c *gin.Context) {
RefreshToken: ts.RefreshToken,
}, nil)
}
type RedirectOutput struct {
Redirect string `json:"redirect"`
State string `json:"state"`
}
func loginRedirectCas(c *gin.Context) {
redirect := ginx.QueryStr(c, "redirect", "/")
v, exists := c.Get("userid")
if exists {
userid := v.(int64)
user, err := models.UserGetById(userid)
ginx.Dangerous(err)
if user == nil {
ginx.Bomb(200, "user not found")
}
if user.Username != "" { // already login
ginx.NewRender(c).Data(redirect, nil)
return
}
}
if !config.C.CAS.Enable {
logger.Error("cas is not enable")
ginx.NewRender(c).Data("", nil)
return
}
redirect, state, err := cas.Authorize(redirect)
ginx.Dangerous(err)
ginx.NewRender(c).Data(RedirectOutput{
Redirect: redirect,
State: state,
}, err)
}
func loginCallbackCas(c *gin.Context) {
ticket := ginx.QueryStr(c, "ticket", "")
state := ginx.QueryStr(c, "state", "")
ret, err := cas.ValidateServiceTicket(c.Request.Context(), ticket, state)
if err != nil {
logger.Errorf("ValidateServiceTicket: %s", err)
ginx.NewRender(c).Data("", err)
return
}
user, err := models.UserGet("username=?", ret.Username)
if err != nil {
logger.Errorf("UserGet: %s", err)
}
ginx.Dangerous(err)
if user != nil {
if config.C.CAS.CoverAttributes {
user.Nickname = ret.Nickname
user.Email = ret.Email
user.Phone = ret.Phone
user.UpdateAt = time.Now().Unix()
ginx.Dangerous(user.Update("email", "nickname", "phone", "update_at"))
}
} else {
now := time.Now().Unix()
user = &models.User{
Username: ret.Username,
Password: "******",
Nickname: ret.Nickname,
Portrait: "",
Roles: strings.Join(config.C.CAS.DefaultRoles, " "),
RolesLst: config.C.CAS.DefaultRoles,
Contacts: []byte("{}"),
Phone: ret.Phone,
Email: ret.Email,
CreateAt: now,
UpdateAt: now,
CreateBy: "CAS",
UpdateBy: "CAS",
}
// create user from cas
ginx.Dangerous(user.Add())
}
// set user login state
userIdentity := fmt.Sprintf("%d-%s", user.Id, user.Username)
ts, err := createTokens(config.C.JWTAuth.SigningKey, userIdentity)
if err != nil {
logger.Errorf("createTokens: %s", err)
}
ginx.Dangerous(err)
ginx.Dangerous(createAuth(c.Request.Context(), userIdentity, ts))
redirect := "/"
if ret.Redirect != "/login" {
redirect = ret.Redirect
}
ginx.NewRender(c).Data(CallbackOutput{
Redirect: redirect,
User: user,
AccessToken: ts.AccessToken,
RefreshToken: ts.RefreshToken,
}, nil)
}
func loginRedirectOAuth(c *gin.Context) {
redirect := ginx.QueryStr(c, "redirect", "/")
v, exists := c.Get("userid")
if exists {
userid := v.(int64)
user, err := models.UserGetById(userid)
ginx.Dangerous(err)
if user == nil {
ginx.Bomb(200, "user not found")
}
if user.Username != "" { // already login
ginx.NewRender(c).Data(redirect, nil)
return
}
}
if !config.C.OAuth.Enable {
ginx.NewRender(c).Data("", nil)
return
}
redirect, err := oauth2x.Authorize(redirect)
ginx.Dangerous(err)
ginx.NewRender(c).Data(redirect, err)
}
func loginCallbackOAuth(c *gin.Context) {
code := ginx.QueryStr(c, "code", "")
state := ginx.QueryStr(c, "state", "")
ret, err := oauth2x.Callback(c.Request.Context(), code, state)
if err != nil {
logger.Debugf("sso.callback() get ret %+v error %v", ret, err)
ginx.NewRender(c).Data(CallbackOutput{}, err)
return
}
user, err := models.UserGet("username=?", ret.Username)
ginx.Dangerous(err)
if user != nil {
if config.C.OAuth.CoverAttributes {
user.Nickname = ret.Nickname
user.Email = ret.Email
user.Phone = ret.Phone
user.UpdateAt = time.Now().Unix()
user.Update("email", "nickname", "phone", "update_at")
}
} else {
now := time.Now().Unix()
user = &models.User{
Username: ret.Username,
Password: "******",
Nickname: ret.Nickname,
Phone: ret.Phone,
Email: ret.Email,
Portrait: "",
Roles: strings.Join(config.C.OAuth.DefaultRoles, " "),
RolesLst: config.C.OAuth.DefaultRoles,
Contacts: []byte("{}"),
CreateAt: now,
UpdateAt: now,
CreateBy: "oauth2",
UpdateBy: "oauth2",
}
// create user from oidc
ginx.Dangerous(user.Add())
}
// set user login state
userIdentity := fmt.Sprintf("%d-%s", user.Id, user.Username)
ts, err := createTokens(config.C.JWTAuth.SigningKey, userIdentity)
ginx.Dangerous(err)
ginx.Dangerous(createAuth(c.Request.Context(), userIdentity, ts))
redirect := "/"
if ret.Redirect != "/login" {
redirect = ret.Redirect
}
ginx.NewRender(c).Data(CallbackOutput{
Redirect: redirect,
User: user,
AccessToken: ts.AccessToken,
RefreshToken: ts.RefreshToken,
}, nil)
}
type SsoConfigOutput struct {
OidcDisplayName string `json:"oidcDisplayName"`
CasDisplayName string `json:"casDisplayName"`
OauthDisplayName string `json:"oauthDisplayName"`
}
func ssoConfigGet(c *gin.Context) {
ginx.NewRender(c).Data(SsoConfigOutput{
OidcDisplayName: oidcc.GetDisplayName(),
CasDisplayName: cas.GetDisplayName(),
OauthDisplayName: oauth2x.GetDisplayName(),
}, nil)
}

View File

@@ -1,9 +1,7 @@
package router
import (
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/toolkits/pkg/ginx"
@@ -52,52 +50,3 @@ func alertMuteDel(c *gin.Context) {
ginx.NewRender(c).Message(models.AlertMuteDel(f.Ids))
}
func alertMutePutByFE(c *gin.Context) {
var f models.AlertMute
ginx.BindJSON(c, &f)
amid := ginx.UrlParamInt64(c, "amid")
am, err := models.AlertMuteGetById(amid)
ginx.Dangerous(err)
if am == nil {
ginx.NewRender(c, http.StatusNotFound).Message("No such AlertMute")
return
}
bgrwCheck(c, am.GroupId)
f.UpdateBy = c.MustGet("username").(string)
ginx.NewRender(c).Message(am.Update(f))
}
type alertMuteFieldForm struct {
Ids []int64 `json:"ids"`
Fields map[string]interface{} `json:"fields"`
}
func alertMutePutFields(c *gin.Context) {
var f alertMuteFieldForm
ginx.BindJSON(c, &f)
if len(f.Fields) == 0 {
ginx.Bomb(http.StatusBadRequest, "fields empty")
}
f.Fields["update_by"] = c.MustGet("username").(string)
f.Fields["update_at"] = time.Now().Unix()
for i := 0; i < len(f.Ids); i++ {
am, err := models.AlertMuteGetById(f.Ids[i])
ginx.Dangerous(err)
if am == nil {
continue
}
ginx.Dangerous(am.UpdateFieldsMap(f.Fields))
}
ginx.NewRender(c).Message(nil)
}

View File

@@ -225,7 +225,7 @@ func bgroCheck(c *gin.Context, bgid int64) {
me := c.MustGet("user").(*models.User)
bg := BusiGroup(bgid)
can, err := me.CanDoBusiGroup(bg)
can, err := me.CanDoBusiGroup(bg, "ro")
ginx.Dangerous(err)
if !can {

View File

@@ -112,7 +112,7 @@ func prometheusProxy(c *gin.Context) {
headerCount := len(cluster.Opts.Headers)
if headerCount > 0 && headerCount%2 == 0 {
for i := 0; i < len(cluster.Opts.Headers); i += 2 {
req.Header.Set(cluster.Opts.Headers[i], cluster.Opts.Headers[i+1])
req.Header.Add(cluster.Opts.Headers[i], cluster.Opts.Headers[i+1])
if cluster.Opts.Headers[i] == "Host" {
req.Host = cluster.Opts.Headers[i+1]
}

View File

@@ -10,11 +10,9 @@ import (
"github.com/toolkits/pkg/i18n"
"github.com/didi/nightingale/v5/src/models"
"github.com/didi/nightingale/v5/src/pkg/cas"
"github.com/didi/nightingale/v5/src/pkg/httpx"
"github.com/didi/nightingale/v5/src/pkg/ldapx"
"github.com/didi/nightingale/v5/src/pkg/logx"
"github.com/didi/nightingale/v5/src/pkg/oauth2x"
"github.com/didi/nightingale/v5/src/pkg/oidcc"
"github.com/didi/nightingale/v5/src/storage"
"github.com/didi/nightingale/v5/src/webapi/config"
@@ -26,7 +24,6 @@ import (
type Webapi struct {
ConfigFile string
Version string
Key string
}
type WebapiOption func(*Webapi)
@@ -43,12 +40,6 @@ func SetVersion(v string) WebapiOption {
}
}
func SetKey(k string) WebapiOption {
return func(s *Webapi) {
s.Key = k
}
}
// Run run webapi
func Run(opts ...WebapiOption) {
code := 1
@@ -92,7 +83,7 @@ EXIT:
func (a Webapi) initialize() (func(), error) {
// parse config file
config.MustLoad(a.Key, a.ConfigFile)
config.MustLoad(a.ConfigFile)
// init i18n
i18n.Init(config.C.I18N)
@@ -103,12 +94,6 @@ func (a Webapi) initialize() (func(), error) {
// init oidc
oidcc.Init(config.C.OIDC)
// init cas
cas.Init(config.C.CAS)
// init oauth
oauth2x.Init(config.C.OAuth)
// init logger
loggerClean, err := logx.Init(config.C.Log)
if err != nil {